diff --git a/CHANGELOG b/CHANGELOG index 19082af9e..3fe2323dd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +02-02-2022 (v3.5.1) + * dia.Paper - fix `mvc.View` import + 01-02-2022 (v3.5.0) * Add Curves Demo * Add Fills Demo diff --git a/dist/geometry.js b/dist/geometry.js index 6287bece0..0fa959517 100644 --- a/dist/geometry.js +++ b/dist/geometry.js @@ -1,4 +1,4 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public diff --git a/dist/geometry.min.js b/dist/geometry.min.js index 99b006dc5..1f5915855 100644 --- a/dist/geometry.min.js +++ b/dist/geometry.min.js @@ -1,4 +1,4 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public diff --git a/dist/joint.core.css b/dist/joint.core.css index 89250abd3..e0a59bf73 100644 --- a/dist/joint.core.css +++ b/dist/joint.core.css @@ -1,4 +1,4 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public diff --git a/dist/joint.core.js b/dist/joint.core.js index d1fd0e0d2..818978fc1 100644 --- a/dist/joint.core.js +++ b/dist/joint.core.js @@ -1,4 +1,4 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public @@ -31569,7 +31569,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. Control: Control }); - var version = "3.5.0"; + var version = "3.5.1"; var Vectorizer = V; var layout = { PortLabel: PortLabel, Port: Port }; diff --git a/dist/joint.core.min.css b/dist/joint.core.min.css index eac51af0e..cc92a1dcd 100644 --- a/dist/joint.core.min.css +++ b/dist/joint.core.min.css @@ -1,4 +1,4 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public diff --git a/dist/joint.core.min.js b/dist/joint.core.min.js index ab9465771..2a4536799 100644 --- a/dist/joint.core.min.js +++ b/dist/joint.core.min.js @@ -1,8 +1,8 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -if(function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("backbone"),require("lodash"),require("jquery")):"function"==typeof define&&define.amd?define(["exports","backbone","lodash","jquery"],e):e((t=t||self).joint={},t.Backbone,t._,t.$)}(this,function(t,a,n,T){"use strict";a=a&&a.hasOwnProperty("default")?a.default:a,n=n&&n.hasOwnProperty("default")?n.default:n,T=T&&T.hasOwnProperty("default")?T.default:T;var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function i(t,e){return t(e={exports:{}},e.exports),e.exports}var r=function(t){return t&&t.Math==Math&&t},l=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof e&&e)||function(){return this}()||Function("return this")(),s=function(t){try{return!!t()}catch(t){return!0}},u=!s(function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}),o={}.propertyIsEnumerable,h=Object.getOwnPropertyDescriptor,c={f:h&&!o.call({1:2},1)?function(t){var e=h(this,t);return!!e&&e.enumerable}:o},b=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},d={}.toString,f=function(t){return d.call(t).slice(8,-1)},p="".split,x=s(function(){return!Object("z").propertyIsEnumerable(0)})?function(t){return"String"==f(t)?p.call(t,""):Object(t)}:Object,g=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},v=function(t){return x(g(t))},m=function(t){return"object"==typeof t?null!==t:"function"==typeof t},y=function(t,e){if(!m(t))return t;var n,i;if(e&&"function"==typeof(n=t.toString)&&!m(i=n.call(t)))return i;if("function"==typeof(n=t.valueOf)&&!m(i=n.call(t)))return i;if(!e&&"function"==typeof(n=t.toString)&&!m(i=n.call(t)))return i;throw TypeError("Can't convert object to primitive value")},w={}.hasOwnProperty,A=function(t,e){return w.call(t,e)},P=l.document,S=m(P)&&m(P.createElement),E=function(t){return S?P.createElement(t):{}},k=!u&&!s(function(){return 7!=Object.defineProperty(E("div"),"a",{get:function(){return 7}}).a}),C=Object.getOwnPropertyDescriptor,L={f:u?C:function(t,e){if(t=v(t),e=y(e,!0),k)try{return C(t,e)}catch(t){}if(A(t,e))return b(!c.f.call(t,e),t[e])}},M=function(t){if(!m(t))throw TypeError(String(t)+" is not an object");return t},N=Object.defineProperty,R={f:u?N:function(t,e,n){if(M(t),e=y(e,!0),M(n),k)try{return N(t,e,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported");return"value"in n&&(t[e]=n.value),t}},O=u?function(t,e,n){return R.f(t,e,b(1,n))}:function(t,e,n){return t[e]=n,t},_=function(e,n){try{O(l,e,n)}catch(t){l[e]=n}return n},V="__core-js_shared__",F=l[V]||_(V,{}),B=Function.toString;"function"!=typeof F.inspectSource&&(F.inspectSource=function(t){return B.call(t)});var D,I,z,j=F.inspectSource,G=l.WeakMap,U="function"==typeof G&&/native code/.test(j(G)),q=i(function(t){(t.exports=function(t,e){return F[t]||(F[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.8.3",mode:"global",copyright:"\xa9 2021 Denis Pushkarev (zloirock.ru)"})}),H=0,Y=Math.random(),W=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++H+Y).toString(36)},X=q("keys"),$=function(t){return X[t]||(X[t]=W(t))},Z={},J=l.WeakMap;if(U){var K=F.state||(F.state=new J),Q=K.get,tt=K.has,et=K.set;D=function(t,e){return e.facade=t,et.call(K,t,e),e},I=function(t){return Q.call(K,t)||{}},z=function(t){return tt.call(K,t)}}else{var nt=$("state");Z[nt]=!0,D=function(t,e){return e.facade=t,O(t,nt,e),e},I=function(t){return A(t,nt)?t[nt]:{}},z=function(t){return A(t,nt)}}var it,rt={set:D,get:I,has:z,enforce:function(t){return z(t)?I(t):D(t,{})},getterFor:function(n){return function(t){var e;if(!m(t)||(e=I(t)).type!==n)throw TypeError("Incompatible receiver, "+n+" required");return e}}},ot=i(function(t){var e=rt.get,u=rt.enforce,h=String(String).split("String");(t.exports=function(t,e,n,i){var r,o=!!i&&!!i.unsafe,s=!!i&&!!i.enumerable,a=!!i&&!!i.noTargetGet;"function"==typeof n&&("string"!=typeof e||A(n,"name")||O(n,"name",e),(r=u(n)).source||(r.source=h.join("string"==typeof e?e:""))),t!==l?(o?!a&&t[e]&&(s=!0):delete t[e],s?t[e]=n:O(t,e,n)):s?t[e]=n:_(e,n)})(Function.prototype,"toString",function(){return"function"==typeof this&&e(this).source||j(this)})}),st=l,at=function(t){return"function"==typeof t?t:void 0},ut=function(t,e){return arguments.length<2?at(st[t])||at(l[t]):st[t]&&st[t][e]||l[t]&&l[t][e]},ht=Math.ceil,lt=Math.floor,ct=function(t){return isNaN(t=+t)?0:(0r;)A(i,n=e[r++])&&(~yt(o,n)||o.push(n));return o},bt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],xt=bt.concat("length","prototype"),At={f:Object.getOwnPropertyNames||function(t){return wt(t,xt)}},Pt={f:Object.getOwnPropertySymbols},St=ut("Reflect","ownKeys")||function(t){var e=At.f(M(t)),n=Pt.f;return n?e.concat(n(t)):e},Et=function(t,e){for(var n=St(e),i=R.f,r=L.f,o=0;o"+t+""},$t=function(){try{it=document.domain&&new ActiveXObject("htmlfile")}catch(t){}var t,e;$t=it?function(t){t.write(Xt("")),t.close();var e=t.parentWindow.Object;return t=null,e}(it):((e=E("iframe")).style.display="none",Ut.appendChild(e),e.src=String("javascript:"),(t=e.contentWindow.document).open(),t.write(Xt("document.F=Object")),t.close(),t.F);for(var n=bt.length;n--;)delete $t[qt][bt[n]];return $t()};Z[Yt]=!0;var Zt=Object.create||function(t,e){var n;return null!==t?(Wt[qt]=M(t),n=new Wt,Wt[qt]=null,n[Yt]=t):n=$t(),void 0===e?n:Gt(n,e)},Jt=zt("unscopables"),Kt=Array.prototype;null==Kt[Jt]&&R.f(Kt,Jt,{configurable:!0,value:Zt(null)});var Qt=function(t){Kt[Jt][t]=!0},te=Object.defineProperty,ee={},ne=function(t){throw t},ie=function(t,e){if(A(ee,t))return ee[t];e||(e={});var n=[][t],i=!!A(e,"ACCESSORS")&&e.ACCESSORS,r=A(e,0)?e[0]:ne,o=A(e,1)?e[1]:void 0;return ee[t]=!!n&&!s(function(){if(i&&!u)return!0;var t={length:-1};i?te(t,1,{enumerable:!0,get:ne}):t[1]=1,n.call(t,r,o)})},re=mt.includes,oe=ie("indexOf",{ACCESSORS:!0,1:0});_t({target:"Array",proto:!0,forced:!oe},{includes:function(t){return re(this,t,1=n.length?{value:void 0,done:!0}:(t=He(n,i),e.index+=t.length,{value:t,done:!1})});var $e=function(e,t,n,i){try{return i?t(M(n)[0],n[1]):t(n)}catch(t){throw function(t){var e=t.return;if(void 0!==e)M(e.call(t)).value}(e),t}},Ze=zt("iterator"),Je=Array.prototype,Ke=function(t,e,n){var i=y(e);i in t?R.f(t,i,b(0,n)):t[i]=n},Qe={};Qe[zt("toStringTag")]="z";var tn="[object z]"===String(Qe),en=zt("toStringTag"),nn="Arguments"==f(function(){return arguments}()),rn=tn?f:function(t){var e,n,i;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),en))?n:nn?f(e):"Object"==(i=f(e))&&"function"==typeof e.callee?"Arguments":i},on=zt("iterator"),sn=zt("iterator"),an=!1;try{var un=0,hn={next:function(){return{done:!!un++}},return:function(){an=!0}};hn[sn]=function(){return this},Array.from(hn,function(){throw 2})}catch(t){}var ln=!function(t,e){if(!e&&!an)return!1;var n=!1;try{var i={};i[sn]=function(){return{next:function(){return{done:n=!0}}}},t(i)}catch(t){}return n}(function(t){Array.from(t)});_t({target:"Array",stat:!0,forced:ln},{from:function(t){var e,n,i,r,o,s,a,u=he(t),h="function"==typeof this?this:Array,l=arguments.length,c=1>8-r%1*8)){if(255<(n=i.charCodeAt(r+=.75)))throw new u("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");e=e<<8|n}return s}),t.atob||(t.atob=function(t){var e=String(t).replace(/=+$/,"");if(e.length%4==1)throw new u("'atob' failed: The string to be decoded is not correctly encoded.");for(var n,i,r=0,o=0,s="";i=e.charAt(o++);~i&&(n=r%4?64*n+i:i,r++%4)?s+=String.fromCharCode(255&n>>(-2*r&6)):0)i=a.indexOf(i);return s})}()});_t({target:"Number",stat:!0},{isNaN:function(t){return t!=t}});st.Number.isNaN;var gn=l.isFinite,vn=Number.isFinite||function(t){return"number"==typeof t&&gn(t)};_t({target:"Number",stat:!0},{isFinite:vn});st.Number.isFinite;var mn=zt("match"),yn=function(t){if(m(e=t)&&(void 0!==(n=e[mn])?n:"RegExp"==f(e)))throw TypeError("The method doesn't accept regular expressions");var e,n;return t},wn=zt("match"),bn=function(e){var n=/./;try{"/./"[e](n)}catch(t){try{return n[wn]=!1,"/./"[e](n)}catch(t){}}return!1};_t({target:"String",proto:!0,forced:!bn("includes")},{includes:function(t){return!!~String(g(this)).indexOf(yn(t),1i)&&!(new ni(t,n).length()>i)},divideAt:function(t){var e=this.pointAt(t);return[new ni(this.start,e),new ni(e,this.end)]},divideAtLength:function(t){var e=this.pointAtLength(t);return[new ni(this.start,e),new ni(e,this.end)]},equals:function(t){return!!t&&this.start.x===t.start.x&&this.start.y===t.start.y&&this.end.x===t.end.x&&this.end.y===t.end.y},intersect:function(t,e){if(t&&t.intersectionWithLine){var n=t.intersectionWithLine(this,e);return n&&t instanceof ni&&(n=n[0]),n}return null},intersectionWithLine:function(t){var e=new Kn(this.end.x-this.start.x,this.end.y-this.start.y),n=new Kn(t.end.x-t.start.x,t.end.y-t.start.y),i=e.x*n.y-e.y*n.x,r=new Kn(t.start.x-this.start.x,t.start.y-this.start.y),o=r.x*n.y-r.y*n.x,s=r.x*e.y-r.y*e.x;if(0===i||o*i<0||s*i<0)return null;if(0a.x+o/2,c=ia.x?r-30:r+30)-h)/(s*s*(i-u))+u:e=s*s/(r-h)-s*s*(i-u)*((n=r>a.y?i+30:i-30)-u)/(o*o*(r-h))+h,new Kn(n,e).theta(t)},toString:function(){return new Kn(this.x,this.y).toString()+" "+this.a+" "+this.b}};var ui=ai,hi=Math.abs,li=Math.cos,ci=Math.sin,di=Math.min,fi=Math.max,pi=Math.round,gi=Math.pow,vi=function(t,e,n,i){if(!(this instanceof vi))return new vi(t,e,n,i);Object(t)===t&&(e=t.y,n=t.width,i=t.height,t=t.x),this.x=void 0===t?0:t,this.y=void 0===e?0:e,this.width=void 0===n?0:n,this.height=void 0===i?0:i};vi.fromEllipse=function(t){return t=new ai(t),new vi(t.x-t.a,t.y-t.b,2*t.a,2*t.b)},vi.fromPointUnion=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(0===t.length)return null;var n,i,r,o,s=new Kn;n=i=1/0,r=o=-1/0;for(var a=0;a=this.x&&t.x<=this.x+this.width&&t.y>=this.y&&t.y<=this.y+this.height},containsRect:function(t){var e=new vi(this).normalize(),n=new vi(t).normalize(),i=e.width,r=e.height,o=n.width,s=n.height;if(!(i&&r&&o&&s))return!1;var a=e.x,u=e.y,h=n.x,l=n.y;return o+=h,i+=a,s+=l,r+=u,a<=h&&o<=i&&u<=l&&s<=r},corner:function(){return new Kn(this.x+this.width,this.y+this.height)},equals:function(t){var e=new vi(this).normalize(),n=new vi(t).normalize();return e.x===n.x&&e.y===n.y&&e.width===n.width&&e.height===n.height},inflate:function(t,e){return void 0===t&&(t=0),void 0===e&&(e=t),this.x-=t,this.y-=e,this.width+=2*t,this.height+=2*e,this},intersect:function(t){var e=this.origin(),n=this.corner(),i=t.origin(),r=t.corner();if(r.x<=e.x||r.y<=e.y||i.x>=n.x||i.y>=n.y)return null;var o=fi(e.x,i.x),s=fi(e.y,i.y);return new vi(o,s,di(n.x,r.x)-o,di(n.y,r.y)-s)},intersectionWithLine:function(t){var e,n,i=[this.topLine(),this.rightLine(),this.bottomLine(),this.leftLine()],r=[],o=[],s=i.length;for(n=0;nl&&(i=(this.x+this.width-l)/(f.x-l)),f.y>c&&(a=(this.y+this.height-c)/(f.y-c));var p=t.topRight();p.x>l&&(r=(this.x+this.width-l)/(p.x-l)),p.yc&&(h=(this.y+this.height-c)/(g.y-c)),{sx:di(n,i,r,o),sy:di(s,a,u,h)}},maxRectUniformScaleToFit:function(t,e){var n=this.maxRectScaleToFit(t,e);return di(n.sx,n.sy)},moveAndExpand:function(t){return this.x+=t.x||0,this.y+=t.y||0,this.width+=t.width||0,this.height+=t.height||0,this},normalize:function(){var t=this.x,e=this.y,n=this.width,i=this.height;return this.width<0&&(t=this.x+this.width,n=-this.width),this.height<0&&(e=this.y+this.height,i=-this.height),this.x=t,this.y=e,this.width=n,this.height=i,this},offset:function(t,e){return Kn.prototype.offset.call(this,t,e)},origin:function(){return new Kn(this.x,this.y)},pointNearestToPoint:function(t){if(t=new Kn(t),this.containsPoint(t))switch(this.sideNearestToPoint(t)){case"right":return new Kn(this.x+this.width,t.y);case"left":return new Kn(this.x,t.y);case"bottom":return new Kn(t.x,this.y+this.height);case"top":return new Kn(t.x,this.y)}return t.adhereToRect(this)},rightLine:function(){return new ni(this.topRight(),this.bottomRight())},rightMiddle:function(){return new Kn(this.x+this.width,this.y+this.height/2)},round:function(t){var e=1;if(t)switch(t){case 1:e=10;break;case 2:e=100;break;case 3:e=1e3;break;default:e=gi(10,t)}return this.x=pi(this.x*e)/e,this.y=pi(this.y*e)/e,this.width=pi(this.width*e)/e,this.height=pi(this.height*e)/e,this},scale:function(t,e,n){return n=this.origin().scale(t,e,n),this.x=n.x,this.y=n.y,this.width*=t,this.height*=e,this},sideNearestToPoint:function(t){var e=(t=new Kn(t)).x-this.x,n=this.x+this.width-t.x,i=t.y-this.y,r=e,o="left";return ni.x&&(i=t[e]);var s=[];for(e=0;ed.y||r>c.y&&r<=d.y){var f=c.x-i>d.x-i?c.x-i:d.x-i;0<=f&&(l.x=i+f,l.y=r,h.start=t,h.end=l,u.intersect(h)&&a++)}o=s}return a%2==1},close:function(){var t=this.start,e=this.end,n=this.points;return t&&e&&!t.equals(e)&&n.push(t.clone()),this},lengthPoints:function(){return this.points},convexHull:function(){return new xi(bi(this.points))},equals:function(t){if(!t)return!1;var e=this.points,n=t.points,i=e.length;if(n.length!==i)return!1;for(var r=0;rr+s&&i.x>r+s||n.xo+a&&i.y>o+a||n.ye.x&&t.ye.y}function $i(t,e){return lr(t,e,{interior:!1})}function Zi(t,e){return cr(t,e,{interior:!1})}function Ji(t,e){return dr(t,e,{interior:!1})}function Ki(t,e){return pr(t,e,{interior:!1})}function Qi(t,e){return lr(t,e,{interior:!0})}function tr(t,e){return cr(t,e,{interior:!0})}function er(t,e){return dr(t,e,{interior:!0})}function nr(t,e){return pr(t,e,{interior:!0})}function ir(t,e){return gr(t,e,{interior:!0})}function rr(t,n,i){return t.getSubpaths().some(function(t){var e=t.toPolylines(i)[0];return"Z"===t.getSegment(-1).type?Qi(e,n):$i(e,n)})}function or(t,n,i){return t.getSubpaths().some(function(t){var e=t.toPolylines(i)[0];return"Z"===t.getSegment(-1).type?tr(e,n):Zi(e,n)})}function sr(t,e,n){return ur(t,Gi.fromRect(e),n)}function ar(t,e,n){return fr(t,e,n,{interior:!1})}function ur(t,e,n){return fr(t,e,n,{interior:!0})}function hr(t,n,i,r){return t.getSubpaths().some(function(t){var e=t.toPolylines(i)[0];return"Z"===t.getSegment(-1).type?ur(n,e,r):ar(n,e,r)})}function lr(t,e,n){void 0===n&&(n={});var i,r=n.interior;if(void 0===r&&(r=!1),r){if(t.containsPoint(e.start))return!0;var o=t.start,s=t.end,a=t.points;i=s.equals(o)?a:a.concat([o])}else i=t.points;for(var u=i.length,h=new ni,l=0;l'+t+"";return N.parseXML(e,{async:!1}).documentElement}var n=document.createElementNS(M.svg,"svg");return n.setAttributeNS(M.xmlns,"xmlns:xlink",M.xlink),n.setAttribute("version","1.1"),n},N.createSVGStyle=function(t){return N("style",{type:"text/css"},[N.createCDATASection(t)]).node},N.createCDATASection=function(t){return void 0===t&&(t=""),document.implementation.createDocument(null,"xml",null).createCDATASection(t)},N.idCounter=0,N.uniqueId=function(){return"v-"+ ++N.idCounter},N.toNode=function(t){return N.isV(t)?t.node:t.nodeName&&t||t[0]},N.ensureId=function(t){return(t=N.toNode(t)).id||(t.id=N.uniqueId())},N.sanitizeText=function(t){return(t||"").replace(/ /g,"\xa0")},N.isUndefined=function(t){return void 0===t},N.isString=function(t){return"string"==typeof t},N.isObject=function(t){return t&&"object"==typeof t},N.isArray=Array.isArray,N.parseXML=function(t,e){var n;e=e||{};try{var i=new DOMParser;N.isUndefined(e.async)||(i.async=e.async),n=i.parseFromString(t,"text/xml")}catch(t){n=void 0}if(!n||n.getElementsByTagName("parsererror").length)throw new Error("Invalid XML: "+t);return n},N.qualifyAttr=function(t){if(-1===t.indexOf(":"))return{ns:null,local:t};var e=t.split(":");return{ns:M[e[0]],local:e[1]}},N.transformRegex=/(\w+)\(([^,)]+),?([^)]+)?\)/gi,N.transformSeparatorRegex=/[ ,]+/,N.transformationListRegex=/^(\w+)\((.*)\)/,N.transformStringToMatrix=function(t){var e=N.createSVGMatrix(),n=t&&t.match(N.transformRegex);if(!n)return e;for(var i=0,r=n.length;i=t.start&&et.start&&n<=t.end||t.start>=e&&t.end=e?t.end+=n:t.start>=e&&(t.start+=n,t.end+=n)}),t},N.convertLineToPathData=function(t){return["M",(t=N(t)).attr("x1"),t.attr("y1"),"L",t.attr("x2"),t.attr("y2")].join(" ")},N.convertPolygonToPathData=function(t){var e=N.getPointsFromSvgNode(t);return 0===e.length?null:N.svgPointsToPath(e)+" Z"},N.convertPolylineToPathData=function(t){var e=N.getPointsFromSvgNode(t);return 0===e.length?null:N.svgPointsToPath(e)},N.svgPointsToPath=function(t){for(var e=0,n=t.length;e=o[r]&&(s.push([e].concat(i.splice(0,o[r]))),o[r]););}),s}(t)),!t||!t.length)return[["M",0,0]];for(var e,n=[],i=0,r=0,o=0,s=0,a=t.length,u=0;ud){var k=S,T=u,C=h;p=t(u=x+i*H(S=P+d*(a&&Pf)M=f-1;else if(void 0!==o){var N;if(void 0===v)v=(N="auto"===n.lineHeight?{value:1.5,unit:"em"}:Ir(n.lineHeight,["em"])||{value:1,unit:"em"}).value,"em"===N.unit&&(v*=u.getBBox().height);v*w.length>o&&(M=Math.floor(o/v)-1)}if(null!==M){w.splice(M+1);var R=i.ellipsis;if(!R||M<0)break;"string"!=typeof R&&(R="\u2026");var O=w[M];if(!O&&!S)break;var _,V,F=O.length;do{if(V=O[F],_=O.substring(0,F),V?V.match(l)&&(_+=V):_+="string"==typeof l?l:" ",_+=R,h.data=_,a.getComputedTextLength()<=r){w[M]=_;break}F--}while(0<=F);break}}}return i.svgDocument?s.removeChild(u):document.body.removeChild(s),w.join(c)},jr=function(t){var e=T(T.parseHTML("
"+t+"
",null,!1));return e.find("*").each(function(){var n=this;T.each(n.attributes,function(){var t=this.name,e=this.value;(t.startsWith("on")||e.startsWith("javascript:")||e.startsWith("data:")||e.startsWith("vbscript:"))&&T(n).removeAttr(t)})}),e.html()},Gr=function(t,e){if(window.navigator.msSaveBlob)window.navigator.msSaveBlob(t,e);else{var n=window.URL.createObjectURL(t),i=document.createElement("a");i.href=n,i.download=e,document.body.appendChild(i),i.click(),document.body.removeChild(i),window.URL.revokeObjectURL(n)}},Ur=function(t){t=t.replace(/\s/g,"");var e,n=(t=decodeURIComponent(t)).indexOf(","),i=t.slice(0,n),r=i.split(":")[1].split(";")[0],o=t.slice(n+1);e=0<=i.indexOf("base64")?atob(o):unescape(encodeURIComponent(o));for(var s=new Uint8Array(e.length),a=0;a')({color:t.color||"blue",opacity:Number.isFinite(t.opacity)?t.opacity:1,outerRadius:e+n,innerRadius:e})},highlight:function(t){return Zr('')({color:t.color||"red",width:Number.isFinite(t.width)?t.width:1,blur:Number.isFinite(t.blur)?t.blur:0,opacity:Number.isFinite(t.opacity)?t.opacity:1})},blur:function(t){var e=Number.isFinite(t.x)?t.x:2;return Zr('')({stdDeviation:Number.isFinite(t.y)?[e,t.y]:e})},dropShadow:function(t){var e="SVGFEDropShadowElement"in window?'':'';return Zr(e)({dx:t.dx||0,dy:t.dy||0,opacity:Number.isFinite(t.opacity)?t.opacity:1,color:t.color||"black",blur:Number.isFinite(t.blur)?t.blur:4})},grayscale:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Zr('')({a:.2126+.7874*(1-e),b:.7152-.7152*(1-e),c:.0722-.0722*(1-e),d:.2126-.2126*(1-e),e:.7152+.2848*(1-e),f:.0722-.0722*(1-e),g:.2126-.2126*(1-e),h:.0722+.9278*(1-e)})},sepia:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Zr('')({a:.393+.607*(1-e),b:.769-.769*(1-e),c:.189-.189*(1-e),d:.349-.349*(1-e),e:.686+.314*(1-e),f:.168-.168*(1-e),g:.272-.272*(1-e),h:.534-.534*(1-e),i:.131+.869*(1-e)})},saturate:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Zr('')({amount:1-e})},hueRotate:function(t){return Zr('')({angle:t.angle||0})},invert:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Zr('')({amount:e,amount2:1-e})},brightness:function(t){return Zr('')({amount:Number.isFinite(t.amount)?t.amount:1})},contrast:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Zr('')({amount:e,amount2:.5-e/2})}},$r={number:function(t,e,o){o=o||{currency:["$",""],decimal:".",thousands:",",grouping:[3]};var n=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i.exec(t),i=n[1]||" ",r=n[2]||">",s=n[3]||"",a=n[4]||"",u=n[5],h=+n[6],l=n[7],c=n[8],d=n[9],f=1,p="",g="",v=!1;switch(c&&(c=+c.substring(1)),(u||"0"===i&&"="===r)&&(u=i="0",r="=",l&&(h-=Math.floor((h-1)/4))),d){case"n":l=!0,d="g";break;case"%":f=100,g="%",d="f";break;case"p":f=100,g="%",d="r";break;case"b":case"o":case"x":case"X":"#"===a&&(p="0"+d.toLowerCase());break;case"c":case"d":v=!0,c=0;break;case"s":f=-1,d="r"}"$"===a&&(p=o.currency[0],g=o.currency[1]),"r"!=d||c||(d="g"),null!=c&&("g"==d?c=Math.max(1,Math.min(21,c)):"e"!=d&&"f"!=d||(c=Math.max(0,Math.min(20,c))));var m=u&&l;if(v&&e%1)return"";var y=e<0||0===e&&1/e<0?(e=-e,"-"):s,w=g;if(f<0){var b=this.prefix(e,c);e=b.scale(e),w=b.symbol+g}else e*=f;var x=(e=this.convert(d,e,c)).lastIndexOf("."),A=x<0?e:e.substring(0,x),P=x<0?"":o.decimal+e.substring(x+1);function S(t){for(var e=t.length,n=[],i=0,r=o.grouping[0];0"===r?k+y+e:"^"===r?k.substring(0,E>>=1)+y+e+k.substring(E):y+(m?e:k+e))+w},string:function(t,e){for(var n,i="{",r=!1,o=[];-1!==(n=t.indexOf(i));){var s,a,u;if(s=t.slice(0,n),r){u=(a=s.split(":")).shift().split("."),s=e;for(var h=0;h|\$\{ ?([^{} ]+) ?\}|\{\{([^{} ]+)\}\}/g;return function(i){return i=i||{},t.replace(e,function(t){for(var e=Array.from(arguments).slice(1,4).find(function(t){return!!t}).split("."),n=i[e.shift()];void 0!==n&&e.length;)n=n[e.shift()];return void 0!==n?n:""})}},Jr=n.assign,Kr=n.defaults,Qr=Jr,to=n.defaultsDeep,eo=n.assign,no=n.defaults,io=n.defaultsDeep,ro=n.invokeMap||n.invoke,oo=n.sortedIndexBy||n.sortedIndex,so=n.uniqBy||n.uniq,ao=n.clone,uo=n.cloneDeep,ho=n.isEmpty,lo=n.isEqual,co=n.isFunction,fo=n.isPlainObject,po=n.toArray,go=n.debounce,vo=n.groupBy,mo=n.sortBy,yo=n.flattenDeep,wo=n.without,bo=n.difference,xo=n.intersection,Ao=n.union,Po=n.has,So=n.result,Eo=n.omit,ko=n.pick,To=n.bindAll,Co=n.forIn,Lo=n.camelCase,Mo=n.uniqueId,No=function(){if(n.mergeWith){var t=Array.from(arguments),e=t[t.length-1],i=co(e)?e:Fo;return t.push(function(t,e){var n=i(t,e);return void 0!==n?n:Array.isArray(t)&&!Array.isArray(e)?e:void 0}),n.mergeWith.apply(this,t)}return n.merge.apply(this,arguments)},Ro=function(t){var e=Object.prototype.toString;return!0===t||!1===t||!!t&&"object"==typeof t&&"[object Boolean]"===e.call(t)},Oo=function(t){return!!t&&("object"==typeof t||"function"==typeof t)},_o=function(t){var e=Object.prototype.toString;return"number"==typeof t||!!t&&"object"==typeof t&&"[object Number]"===e.call(t)},Vo=function(t){var e=Object.prototype.toString;return"string"==typeof t||!!t&&"object"==typeof t&&"[object String]"===e.call(t)},Fo=function(){};function Bo(t){t=so(t);var s=po(t).reduce(function(t,e){return t[e.id]=e.clone(),t},{});return po(t).forEach(function(t){var e=s[t.id];if(e.isLink()){var n=e.source(),i=e.target();n.id&&s[n.id]&&e.prop("source/id",s[n.id].id),i.id&&s[i.id]&&e.prop("target/id",s[i.id].id)}var r=t.get("parent");r&&s[r]&&e.set("parent",s[r].id);var o=po(t.get("embeds")).reduce(function(t,e){return s[e]&&t.push(s[e].id),t},[]);ho(o)||e.set("embeds",o)}),s}var Do={x:"x",y:"y",width:"w",height:"h",minimum:"s",maximum:"l",diagonal:"d"},Io=Object.keys(Do).map(function(t){return Do[t]}).join(""),zo="[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?",jo=/\s/g,Go=new RegExp("^("+zo+"\\*)?(["+Io+"])([-+]{1,2}"+zo+")?$","g");function Uo(t){throw new Error("Invalid calc() expression: "+t)}function qo(t,e){var n=Go.exec(t.replace(jo,""));n||Uo(t),Go.lastIndex=0;var i=n[1];void 0===i&&(i=1);var r=n[2],o=n[3];void 0===o&&(o=0);var s=e.x,a=e.y,u=e.width,h=e.height,l=0;switch(r){case Do.width:l=u;break;case Do.height:l=h;break;case Do.x:l=s;break;case Do.y:l=a;break;case Do.minimum:l=Math.min(h,u);break;case Do.maximum:l=Math.max(h,u);break;case Do.diagonal:l=Math.sqrt(h*h+u*u)}return parseFloat(i)*l+function(t){if(!t)return 0;switch(t[0]){case"+":return parseFloat(t.substr(1));case"-":return-parseFloat(t.substr(1))}return parseFloat(t)}(o)}function Ho(t){return"string"==typeof t&&t.includes("calc")}var Yo="calc(",Wo=Yo.length;function Xo(t,e){for(var n=t,i=0;;){var r=n.indexOf(Yo,i);if(-1===r)return n;var o=r+Wo,s=1;t:for(;;){switch(n[o]){case"(":s++;break;case")":if(0===--s)break t;break;case void 0:Uo(n)}o++}var a=n.slice(r+Wo,o);Ho(a)&&(a=Xo(a,e));var u=String(qo(a,e));n=n.slice(0,r)+u+n.slice(o+1),i=r+u.length}}function $o(o,s){return function(t,e){var n=Dr(t);t=parseFloat(t),n&&(t/=100);var i={};if(isFinite(t)){var r=n||0<=t&&t<=1?t*e[s]:Math.max(t+e[s],0);i[o]=r}return i}}function Zo(s,a,u){return function(t,e){var n,i=Dr(t);if(t=parseFloat(t),i&&(t/=100),isFinite(t)){var r=e[u]();n=i||0e.width?ss:as)(t,e)})},refRCircumscribed:{set:function(t,e){var n=Dr(t);t=parseFloat(t),n&&(t/=100);var i,r=Math.sqrt(e.height*e.height+e.width*e.width);return isFinite(t)&&(i=n||0<=t&&t<=1?t*r:Math.max(t+r,0)),{r:i}}},refCx:{set:$o("cx","width")},refCy:{set:$o("cy","height")},xAlignment:{offset:Jo("x","width","right")},yAlignment:{offset:Jo("y","height","bottom")},resetOffset:{offset:function(t,e){return t?{x:-e.x,y:-e.y}:{x:0,y:0}}},refDResetOffset:{set:Qo({resetOffset:!0})},refDKeepOffset:{set:Qo({resetOffset:!1})},refPointsResetOffset:{set:ts({resetOffset:!0})},refPointsKeepOffset:{set:ts({resetOffset:!1})},connection:{qualify:is,set:function(t){var e,n=t.stubs;if(void 0===n&&(n=0),isFinite(n)&&0!==n){var i;i=n<0?(this.getConnectionLength()+n)/2:n;var r=this.getConnection(),o=r.divideAtLength(i),s=r.divideAtLength(-i);o&&s&&(e=o[0].serialize()+" "+s[1].serialize())}return{d:e||this.getSerializedConnection()}}},atConnectionLengthKeepGradient:{qualify:is,set:es("getTangentAtLength",{rotate:!0})},atConnectionLengthIgnoreGradient:{qualify:is,set:es("getTangentAtLength",{rotate:!1})},atConnectionRatioKeepGradient:{qualify:is,set:es("getTangentAtRatio",{rotate:!0})},atConnectionRatioIgnoreGradient:{qualify:is,set:es("getTangentAtRatio",{rotate:!1})}};["transform","d","points","width","height","cx","cy","r","rx","ry","x1","x2","y1","y2","x","y","dx","dy"].forEach(function(i){us[i]={qualify:Ho,set:function(t,e){var n;return(n={})[i]=Xo(t,e),n}}}),us.refR=us.refRInscribed,us.refD=us.refDResetOffset,us.refPoints=us.refPointsResetOffset,us.atConnectionLength=us.atConnectionLengthKeepGradient,us.atConnectionRatio=us.atConnectionRatioKeepGradient,us.refX2=us.refX,us.refY2=us.refY,us.refWidth2=us.refWidth,us.refHeight2=us.refHeight,us["ref-x"]=us.refX,us["ref-y"]=us.refY,us["ref-dy"]=us.refDy,us["ref-dx"]=us.refDx,us["ref-width"]=us.refWidth,us["ref-height"]=us.refHeight,us["x-alignment"]=us.xAlignment,us["y-alignment"]=us.yAlignment;var hs=us,ls=a.Model.extend({constructor:function(t,e){var n,i=t||{};"function"==typeof this.preinitialize&&this.preinitialize.apply(this,arguments),this.cid=Mo("c"),this.attributes={},e&&e.collection&&(this.collection=e.collection),e&&e.parse&&(i=this.parse(i,e)||{}),(n=So(this,"defaults"))&&(i=No({},n,i)),this.set(i,e),this.changed={},this.initialize.apply(this,arguments)},translate:function(t,e,n){throw new Error("Must define a translate() method.")},toJSON:function(){var e=So(this.constructor.prototype,"defaults").attrs||{},t=this.attributes.attrs,o={};Co(t,function(t,i){var r=e[i];Co(t,function(t,n){Oo(t)&&!Array.isArray(t)?Co(t,function(t,e){r&&r[n]&&lo(r[n][e],t)||(o[i]=o[i]||{},(o[i][n]||(o[i][n]={}))[e]=t)}):r&&lo(r[n],t)||(o[i]=o[i]||{},o[i][n]=t)})});var n=uo(Eo(this.attributes,"attrs"));return n.attrs=o,n},initialize:function(t){t&&t.id||this.set("id",this.generateId(),{silent:!0}),this._transitionIds={},this.processPorts(),this.on("change:attrs",this.processPorts,this)},generateId:function(){return Rr()},processPorts:function(){var t=this.ports,n={};Co(this.get("attrs"),function(t,e){t&&t.port&&(void 0!==t.port.id?n[t.port.id]=t.port:n[t.port]={id:t.port})});var i={};(Co(t,function(t,e){n[e]||(i[e]=!0)}),this.graph&&!ho(i))&&(this.graph.getConnectedLinks(this,{inbound:!0}).forEach(function(t){i[t.get("target").port]&&t.remove()}),this.graph.getConnectedLinks(this,{outbound:!0}).forEach(function(t){i[t.get("source").port]&&t.remove()}));this.ports=n},remove:function(t){void 0===t&&(t={});var e=this.graph,n=this.collection;if(!e)return n&&n.remove(this,t),this;e.startBatch("remove");var i=this.getParentCell();i&&i.unembed(this,t);for(var r=this.getEmbeddedCells(),o=0,s=r.length;oh[2]?(s=".3em",r=l,o=0,"start"):uc[2]?(s=".3em",r=-h,o=0,"end"):u "+e),n=this.getSelector(t.parentNode,n)}return n},addLinkFromMagnet:function(t,e,n){var i=this.paper,r=i.model,o=i.getDefaultLink(this,t);return o.set({source:this.getLinkEnd(t,e,n,o,"source"),target:{x:e,y:n}}).addTo(r,{async:!1,ui:!0}),o.findView(i)},getLinkEnd:function(t){for(var e,n=[],i=arguments.length-1;0r.options.clickThreshold||this.notify("element:magnet:pointerclick",t,e,n,i)}},{Flags:Bs});eo(Ds.prototype,Ts);var Is={_results:{},_tests:{svgforeignobject:function(){return!!document.createElementNS&&/SVGForeignObject/.test({}.toString.call(document.createElementNS("http://www.w3.org/2000/svg","foreignObject")))}},addTest:function(t,e){return this._tests[t]=e},test:function(t){var e=this._tests[t];if(!e)throw new Error('Test not defined ("'+t+'"). Use `joint.env.addTest(name, fn) to add a new test.`');var n=this._results[t];if(void 0!==n)return n;try{n=e()}catch(t){n=!1}return this._results[t]=n}},zs=Cs.define("basic.Generic",{attrs:{".":{fill:"#ffffff",stroke:"none"}}}),js=zs.define("basic.Rect",{attrs:{rect:{fill:"#ffffff",stroke:"#000000",width:100,height:60},text:{fill:"#000000",text:"","font-size":14,"ref-x":.5,"ref-y":.5,"text-anchor":"middle","y-alignment":"middle","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Gs=Ds.extend({presentationAttributes:Ds.addPresentationAttributes({attrs:["SCALE"]}),confirmUpdate:function(){var t=Ds.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"SCALE")&&(this.resize(),t=this.removeFlag(t,"SCALE")),t}}),Us=zs.define("basic.Text",{attrs:{text:{"font-size":18,fill:"#000000"}}},{markup:''}),qs=zs.define("basic.Circle",{size:{width:60,height:60},attrs:{circle:{fill:"#ffffff",stroke:"#000000",r:30,cx:30,cy:30},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Hs=zs.define("basic.Ellipse",{size:{width:60,height:40},attrs:{ellipse:{fill:"#ffffff",stroke:"#000000",rx:30,ry:20,cx:30,cy:20},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Ys=zs.define("basic.Polygon",{size:{width:60,height:40},attrs:{polygon:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Ws=zs.define("basic.Polyline",{size:{width:60,height:40},attrs:{polyline:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Xs=zs.define("basic.Image",{attrs:{text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),$s=zs.define("basic.Path",{size:{width:60,height:60},attrs:{path:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle",ref:"path","ref-x":.5,"ref-dy":10,fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Zs=$s.define("basic.Rhombus",{attrs:{path:{d:"M 30 0 L 60 30 30 60 0 30 z"},text:{"ref-y":.5,"ref-dy":null,"y-alignment":"middle"}}}),Js=Is.test("svgforeignobject"),Ks=zs.define("basic.TextBlock",{attrs:{rect:{fill:"#ffffff",stroke:"#000000",width:80,height:100},text:{fill:"#000000","font-size":14,"font-family":"Arial, helvetica, sans-serif"},".content":{text:"","ref-x":.5,"ref-y":.5,"y-alignment":"middle","x-alignment":"middle"}},content:""},{markup:['','',Js?'
':'',""].join(""),initialize:function(){this.listenTo(this,"change:size",this.updateSize),this.listenTo(this,"change:content",this.updateContent),this.updateSize(this,this.get("size")),this.updateContent(this,this.get("content")),zs.prototype.initialize.apply(this,arguments)},updateSize:function(t,e){this.attr({".fobj":eo({},e),div:{style:eo({},e)}})},updateContent:function(t,e){Js?this.attr({".content":{html:jr(e)}}):this.attr({".content":{text:e}})},setForeignObjectSize:function(){this.updateSize.apply(this,arguments)},setDivContent:function(){this.updateContent.apply(this,arguments)}}),Qs=Ds.extend({presentationAttributes:Js?Ds.prototype.presentationAttributes:Ds.addPresentationAttributes({content:["CONTENT"],size:["CONTENT"]}),initFlag:["RENDER","CONTENT"],confirmUpdate:function(){var t=Ds.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"CONTENT")&&(this.updateContent(this.model),t=this.removeFlag(t,"CONTENT")),t},update:function(t,e){var n=this.model;if(Js)Ds.prototype.update.call(this,n,e);else{var i=Eo(e||n.get("attrs"),".content");Ds.prototype.update.call(this,n,i),e&&!Po(e,".content")||this.updateContent(n,e)}},updateContent:function(t,e){var n=No({},(e||t.get("attrs"))[".content"]);n=Eo(n,"text");var i=zr(t.get("content"),t.get("size"),n,{svgDocument:this.paper.svg}),r=Lr({},".content",n,"/");r[".content"].text=i,Ds.prototype.update.call(this,t,r)}}),ta={Generic:zs,Rect:js,TextView:Gs,Text:Us,Circle:qs,Ellipse:Hs,Polygon:Ys,Polyline:Ws,Image:Xs,Path:$s,Rhombus:Zs,TextBlock:Ks,TextBlockView:Qs},ea=ls.extend({markup:['','','','','','','',''].join(""),toolMarkup:['','','','',"Remove link.","",'','','',"Link options.","",""].join(""),doubleToolMarkup:void 0,vertexMarkup:['','','','',"Remove vertex.","",""].join(""),arrowheadMarkup:['','',""].join(""),defaultLabel:void 0,labelMarkup:void 0,_builtins:{defaultLabel:{markup:[{tagName:"rect",selector:"rect"},{tagName:"text",selector:"text"}],attrs:{text:{fill:"#000000",fontSize:14,textAnchor:"middle",yAlignment:"middle",pointerEvents:"none"},rect:{ref:"text",fill:"#ffffff",rx:3,ry:3,refWidth:1,refHeight:1,refX:0,refY:0}},position:{distance:.5}}},defaults:{type:"link",source:{},target:{}},isLink:function(){return!0},disconnect:function(t){return this.set({source:{x:0,y:0},target:{x:0,y:0}},t)},source:function(t,e,n){return void 0===t?ao(this.get("source")):(r=t instanceof ls?((i=ao(e)||{}).id=t.id,n):fo(t)?(i=t,e):((i=ao(e)||{}).x=t.x,i.y=t.y,n),this.set("source",i,r));var i,r},target:function(t,e,n){return void 0===t?ao(this.get("target")):(r=t instanceof ls?((i=ao(e)||{}).id=t.id,n):fo(t)?(i=t,e):((i=ao(e)||{}).x=t.x,i.y=t.y,n),this.set("target",i,r));var i,r},router:function(t,e,n){if(void 0===t){var i=this.get("router");return i?"object"==typeof i?ao(i):i:this.get("manhattan")?{name:"orthogonal"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},s=r?e:n;return this.set("router",o,s)},connector:function(t,e,n){if(void 0===t){var i=this.get("connector");return i?"object"==typeof i?ao(i):i:this.get("smooth")?{name:"smooth"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},s=r?e:n;return this.set("connector",o,s)},label:function(t,e,n){var i=this.labels();return(t=isFinite(t)&&null!==t?0|t:0)<0&&(t=i.length+t),arguments.length<=1?this.prop(["labels",t]):this.prop(["labels",t],e,n)},labels:function(t,e){return 0===arguments.length?(t=this.get("labels"),Array.isArray(t)?t.slice():[]):(Array.isArray(t)||(t=[]),this.set("labels",t,e))},hasLabels:function(){var t=this.attributes.labels;return Array.isArray(t)&&0e[1].x?"right":"left";switch(i){case"left":return new Kn(-1,0);case"right":default:return new Kn(1,0)}}(t,e);case Pa.VERTICAL:return function(t,e,n){var i,r=t.sourceBBox;i=r.width&&r.height?r.sideNearestToPoint(e[0]):r.y>e[1].y?"bottom":"top";switch(i){case"top":return new Kn(0,-1);case"bottom":default:return new Kn(0,1)}}(t,e);case Pa.CLOSEST_POINT:return Ca(t,e);case Pa.OUTWARDS:return Ma(t,e);case Pa.AUTO:default:return ka(t,e)}}(r,c,s,u),f=c[0].distance(c[1])*u.coeff,p=Oa(d,c[1].difference(c[0]).normalize());if(p>Math.PI/4){var g=f+(p-Math.PI/4)*u.angleTangentCoefficient;h=d.clone().scale(g,g)}else h=d.clone().scale(f,f)}if(u.targetTangent)l=u.targetTangent;else{var v=function(t,e,n,i){if(i.targetDirection)switch(i.targetDirection){case Sa.UP:return new Kn(0,-1);case Sa.DOWN:return new Kn(0,1);case Sa.LEFT:return new Kn(-1,0);case Sa.RIGHT:return new Kn(0,1);case Sa.AUTO:return Ta(t,e);case Sa.CLOSEST_POINT:return La(t,e);case Sa.OUTWARDS:return Na(t,e);default:return i.targetDirection}switch(n){case Pa.HORIZONTAL:return function(t,e,n){var i,r=t.targetBBox;i=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.x>e[e.length-2].x?"left":"right";switch(i){case"left":return new Kn(-1,0);case"right":default:return new Kn(1,0)}}(t,e);case Pa.VERTICAL:return function(t,e,n){var i,r=t.targetBBox;i=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.y>e[e.length-2].y?"top":"bottom";switch(i){case"top":return new Kn(0,-1);case"bottom":default:return new Kn(0,1)}}(t,e);case Pa.CLOSEST_POINT:return La(t,e);case Pa.OUTWARDS:return Na(t,e);case Pa.AUTO:default:return Ta(t,e)}}(r,c,s,u),m=c.length-1,y=c[m-1].distance(c[m])*u.coeff,w=Oa(v,c[m-1].difference(c[m]).normalize());if(w>Math.PI/4){var b=y+(w-Math.PI/4)*u.angleTangentCoefficient;l=v.clone().scale(b,b)}else l=v.clone().scale(y,y)}var x=function(t,e,n,i){for(var r=i.tau,o=i.coeff,s=[],a=[],u=[],h=t.length-1,l=0;l=Math.abs(t.y-e.y)){var h=(t.x+e.x)/2;u=Li.createSegment("C",h,t.y,h,e.y,e.x,e.y),r.appendSegment(u)}else{var l=(t.y+e.y)/2;u=Li.createSegment("C",t.x,l,e.x,l,e.x,e.y),r.appendSegment(u)}}return o?r:r.serialize()},curve:Ea},Fa=_s.extend({tagName:"path",className:"highlight-stroke",attributes:{"pointer-events":"none","vector-effect":"non-scaling-stroke",fill:"none"},options:{padding:3,rx:0,ry:0,useFirstSubpath:!1,attrs:{"stroke-width":3,stroke:"#FEB663"}},getPathData:function(e,n){var i,r=this.options,t=r.useFirstSubpath;try{var o=xr(n);if(i=o.convertToPathData().trim(),"PATH"===o.tagName()&&t){var s=i.search(/.M/i)+1;0e.y?"N":"S":t.y===e.y?t.x>e.x?"W":"E":null}function su(t){return new vi(t.x,t.y,0,0)}function au(t){var e=Hr(t.padding||t.elementPadding||20);return{x:-e.left,y:-e.top,width:e.left+e.right,height:e.top+e.bottom}}function uu(t,e){return t.sourceBBox.clone().moveAndExpand(au(e))}function hu(t,e){return t.targetBBox.clone().moveAndExpand(au(e))}function lu(t,e,n){var i=new Kn(t.x,e.y),r=new Kn(e.x,t.y),o=ou(t,i),s=ou(t,r),a=eu[n],u=o===n||o!==a&&(s===a||s!==n)?i:r;return{points:[u],direction:ou(u,e)}}function cu(t,e,n){var i=iu(t,e,n);return{points:[i],direction:ou(i,e)}}function du(e,t,n,i){var r,o={},s=[new Kn(e.x,t.y),new Kn(t.x,e.y)],a=s.filter(function(t){return!n.containsPoint(t)}),u=a.filter(function(t){return ou(t,e)!==i});if(0h.center().distance(t),c=l?e:t,d=l?t:e;return s=iu(o=r?(o=Kn.fromPolar(h.width+h.height,nu[r],c),h.pointNearestToPoint(o).move(o,-1)):h.pointNearestToPoint(c).move(c,1),d,h),o.round().equals(s.round())?(s=Kn.fromPolar(h.width+h.height,On(o.theta(c))+Math.PI/2,d),a=iu(o,s=h.pointNearestToPoint(s).move(d,1).round(),h),u.points=l?[s,a,o]:[o,a,s]):u.points=l?[s,o]:[o,s],u.direction=ou(l?o:s,e),u}function gu(t,e,n){var i,r,o,s,a,u=uu(n,e),h=hu(n,e),l=(r=e,(i=n).sourceAnchor?i.sourceAnchor:uu(i,r).center()),c=(s=e,(o=n).targetAnchor?o.targetAnchor:hu(o,s).center());u=u.union(su(l)),h=h.union(su(c)),(t=po(t).map(Kn)).unshift(l),t.push(c);for(var d=[],f=0,p=t.length-1;fi.maxAllowedDirectionChange)){var Y=ku(D.clone().offset(C.gridOffsetX,C.gridOffsetY),w,u),W=Tu(Y);if(!b.isClose(W)&&!n(Y)){if(0<=_.indexOf(W))if(!Y.equals(f))if(Pu(H,Au(Y,f,O,w,i))>i.maxAllowedDirectionChange)continue;var X=z+C.cost+(G?0:i.penalties[L]);(!b.isOpen(W)||X=this.options.longLinkLength){var r=this.options.doubleLinkToolsOffset||e;i=this.getPointAtLength(n-r),this._tool2Cache.attr("transform","translate("+i.x+", "+i.y+") "+t),this._tool2Cache.attr("visibility","visible")}else this.options.doubleLinkTools&&this._tool2Cache.attr("visibility","hidden")}return this},updateArrowheadMarkers:function(){if(!this._V.markerArrowheads)return this;if("none"===T.css(this._V.markerArrowheads.node,"display"))return this;var t=this.getConnectionLength()r;)A(i,n=e[r++])&&(~yt(o,n)||o.push(n));return o},bt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],xt=bt.concat("length","prototype"),At={f:Object.getOwnPropertyNames||function(t){return wt(t,xt)}},Pt={f:Object.getOwnPropertySymbols},St=ut("Reflect","ownKeys")||function(t){var e=At.f(M(t)),n=Pt.f;return n?e.concat(n(t)):e},Et=function(t,e){for(var n=St(e),i=R.f,r=L.f,o=0;o"+t+""},$t=function(){try{it=document.domain&&new ActiveXObject("htmlfile")}catch(t){}var t,e;$t=it?function(t){t.write(Xt("")),t.close();var e=t.parentWindow.Object;return t=null,e}(it):((e=E("iframe")).style.display="none",Ut.appendChild(e),e.src=String("javascript:"),(t=e.contentWindow.document).open(),t.write(Xt("document.F=Object")),t.close(),t.F);for(var n=bt.length;n--;)delete $t[qt][bt[n]];return $t()};Z[Yt]=!0;var Zt=Object.create||function(t,e){var n;return null!==t?(Wt[qt]=M(t),n=new Wt,Wt[qt]=null,n[Yt]=t):n=$t(),void 0===e?n:Gt(n,e)},Jt=zt("unscopables"),Kt=Array.prototype;null==Kt[Jt]&&R.f(Kt,Jt,{configurable:!0,value:Zt(null)});var Qt=function(t){Kt[Jt][t]=!0},te=Object.defineProperty,ee={},ne=function(t){throw t},ie=function(t,e){if(A(ee,t))return ee[t];e||(e={});var n=[][t],i=!!A(e,"ACCESSORS")&&e.ACCESSORS,r=A(e,0)?e[0]:ne,o=A(e,1)?e[1]:void 0;return ee[t]=!!n&&!s(function(){if(i&&!u)return!0;var t={length:-1};i?te(t,1,{enumerable:!0,get:ne}):t[1]=1,n.call(t,r,o)})},re=mt.includes,oe=ie("indexOf",{ACCESSORS:!0,1:0});_t({target:"Array",proto:!0,forced:!oe},{includes:function(t){return re(this,t,1=n.length?{value:void 0,done:!0}:(t=He(n,i),e.index+=t.length,{value:t,done:!1})});var $e=function(e,t,n,i){try{return i?t(M(n)[0],n[1]):t(n)}catch(t){throw function(t){var e=t.return;if(void 0!==e)M(e.call(t)).value}(e),t}},Ze=zt("iterator"),Je=Array.prototype,Ke=function(t,e,n){var i=y(e);i in t?R.f(t,i,b(0,n)):t[i]=n},Qe={};Qe[zt("toStringTag")]="z";var tn="[object z]"===String(Qe),en=zt("toStringTag"),nn="Arguments"==f(function(){return arguments}()),rn=tn?f:function(t){var e,n,i;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),en))?n:nn?f(e):"Object"==(i=f(e))&&"function"==typeof e.callee?"Arguments":i},on=zt("iterator"),sn=zt("iterator"),an=!1;try{var un=0,hn={next:function(){return{done:!!un++}},return:function(){an=!0}};hn[sn]=function(){return this},Array.from(hn,function(){throw 2})}catch(t){}var ln=!function(t,e){if(!e&&!an)return!1;var n=!1;try{var i={};i[sn]=function(){return{next:function(){return{done:n=!0}}}},t(i)}catch(t){}return n}(function(t){Array.from(t)});_t({target:"Array",stat:!0,forced:ln},{from:function(t){var e,n,i,r,o,s,a,u=he(t),h="function"==typeof this?this:Array,l=arguments.length,c=1>8-r%1*8)){if(255<(n=i.charCodeAt(r+=.75)))throw new u("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");e=e<<8|n}return s}),t.atob||(t.atob=function(t){var e=String(t).replace(/=+$/,"");if(e.length%4==1)throw new u("'atob' failed: The string to be decoded is not correctly encoded.");for(var n,i,r=0,o=0,s="";i=e.charAt(o++);~i&&(n=r%4?64*n+i:i,r++%4)?s+=String.fromCharCode(255&n>>(-2*r&6)):0)i=a.indexOf(i);return s})}()});_t({target:"Number",stat:!0},{isNaN:function(t){return t!=t}});st.Number.isNaN;var gn=l.isFinite,vn=Number.isFinite||function(t){return"number"==typeof t&&gn(t)};_t({target:"Number",stat:!0},{isFinite:vn});st.Number.isFinite;var mn=zt("match"),yn=function(t){if(m(e=t)&&(void 0!==(n=e[mn])?n:"RegExp"==f(e)))throw TypeError("The method doesn't accept regular expressions");var e,n;return t},wn=zt("match"),bn=function(e){var n=/./;try{"/./"[e](n)}catch(t){try{return n[wn]=!1,"/./"[e](n)}catch(t){}}return!1};_t({target:"String",proto:!0,forced:!bn("includes")},{includes:function(t){return!!~String(g(this)).indexOf(yn(t),1i)&&!(new ni(t,n).length()>i)},divideAt:function(t){var e=this.pointAt(t);return[new ni(this.start,e),new ni(e,this.end)]},divideAtLength:function(t){var e=this.pointAtLength(t);return[new ni(this.start,e),new ni(e,this.end)]},equals:function(t){return!!t&&this.start.x===t.start.x&&this.start.y===t.start.y&&this.end.x===t.end.x&&this.end.y===t.end.y},intersect:function(t,e){if(t&&t.intersectionWithLine){var n=t.intersectionWithLine(this,e);return n&&t instanceof ni&&(n=n[0]),n}return null},intersectionWithLine:function(t){var e=new Kn(this.end.x-this.start.x,this.end.y-this.start.y),n=new Kn(t.end.x-t.start.x,t.end.y-t.start.y),i=e.x*n.y-e.y*n.x,r=new Kn(t.start.x-this.start.x,t.start.y-this.start.y),o=r.x*n.y-r.y*n.x,s=r.x*e.y-r.y*e.x;if(0===i||o*i<0||s*i<0)return null;if(0a.x+o/2,c=ia.x?r-30:r+30)-h)/(s*s*(i-u))+u:e=s*s/(r-h)-s*s*(i-u)*((n=r>a.y?i+30:i-30)-u)/(o*o*(r-h))+h,new Kn(n,e).theta(t)},toString:function(){return new Kn(this.x,this.y).toString()+" "+this.a+" "+this.b}};var ui=ai,hi=Math.abs,li=Math.cos,ci=Math.sin,di=Math.min,fi=Math.max,pi=Math.round,gi=Math.pow,vi=function(t,e,n,i){if(!(this instanceof vi))return new vi(t,e,n,i);Object(t)===t&&(e=t.y,n=t.width,i=t.height,t=t.x),this.x=void 0===t?0:t,this.y=void 0===e?0:e,this.width=void 0===n?0:n,this.height=void 0===i?0:i};vi.fromEllipse=function(t){return t=new ai(t),new vi(t.x-t.a,t.y-t.b,2*t.a,2*t.b)},vi.fromPointUnion=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(0===t.length)return null;var n,i,r,o,s=new Kn;n=i=1/0,r=o=-1/0;for(var a=0;a=this.x&&t.x<=this.x+this.width&&t.y>=this.y&&t.y<=this.y+this.height},containsRect:function(t){var e=new vi(this).normalize(),n=new vi(t).normalize(),i=e.width,r=e.height,o=n.width,s=n.height;if(!(i&&r&&o&&s))return!1;var a=e.x,u=e.y,h=n.x,l=n.y;return o+=h,i+=a,s+=l,r+=u,a<=h&&o<=i&&u<=l&&s<=r},corner:function(){return new Kn(this.x+this.width,this.y+this.height)},equals:function(t){var e=new vi(this).normalize(),n=new vi(t).normalize();return e.x===n.x&&e.y===n.y&&e.width===n.width&&e.height===n.height},inflate:function(t,e){return void 0===t&&(t=0),void 0===e&&(e=t),this.x-=t,this.y-=e,this.width+=2*t,this.height+=2*e,this},intersect:function(t){var e=this.origin(),n=this.corner(),i=t.origin(),r=t.corner();if(r.x<=e.x||r.y<=e.y||i.x>=n.x||i.y>=n.y)return null;var o=fi(e.x,i.x),s=fi(e.y,i.y);return new vi(o,s,di(n.x,r.x)-o,di(n.y,r.y)-s)},intersectionWithLine:function(t){var e,n,i=[this.topLine(),this.rightLine(),this.bottomLine(),this.leftLine()],r=[],o=[],s=i.length;for(n=0;nl&&(i=(this.x+this.width-l)/(f.x-l)),f.y>c&&(a=(this.y+this.height-c)/(f.y-c));var p=t.topRight();p.x>l&&(r=(this.x+this.width-l)/(p.x-l)),p.yc&&(h=(this.y+this.height-c)/(g.y-c)),{sx:di(n,i,r,o),sy:di(s,a,u,h)}},maxRectUniformScaleToFit:function(t,e){var n=this.maxRectScaleToFit(t,e);return di(n.sx,n.sy)},moveAndExpand:function(t){return this.x+=t.x||0,this.y+=t.y||0,this.width+=t.width||0,this.height+=t.height||0,this},normalize:function(){var t=this.x,e=this.y,n=this.width,i=this.height;return this.width<0&&(t=this.x+this.width,n=-this.width),this.height<0&&(e=this.y+this.height,i=-this.height),this.x=t,this.y=e,this.width=n,this.height=i,this},offset:function(t,e){return Kn.prototype.offset.call(this,t,e)},origin:function(){return new Kn(this.x,this.y)},pointNearestToPoint:function(t){if(t=new Kn(t),this.containsPoint(t))switch(this.sideNearestToPoint(t)){case"right":return new Kn(this.x+this.width,t.y);case"left":return new Kn(this.x,t.y);case"bottom":return new Kn(t.x,this.y+this.height);case"top":return new Kn(t.x,this.y)}return t.adhereToRect(this)},rightLine:function(){return new ni(this.topRight(),this.bottomRight())},rightMiddle:function(){return new Kn(this.x+this.width,this.y+this.height/2)},round:function(t){var e=1;if(t)switch(t){case 1:e=10;break;case 2:e=100;break;case 3:e=1e3;break;default:e=gi(10,t)}return this.x=pi(this.x*e)/e,this.y=pi(this.y*e)/e,this.width=pi(this.width*e)/e,this.height=pi(this.height*e)/e,this},scale:function(t,e,n){return n=this.origin().scale(t,e,n),this.x=n.x,this.y=n.y,this.width*=t,this.height*=e,this},sideNearestToPoint:function(t){var e=(t=new Kn(t)).x-this.x,n=this.x+this.width-t.x,i=t.y-this.y,r=e,o="left";return ni.x&&(i=t[e]);var s=[];for(e=0;ed.y||r>c.y&&r<=d.y){var f=c.x-i>d.x-i?c.x-i:d.x-i;0<=f&&(l.x=i+f,l.y=r,h.start=t,h.end=l,u.intersect(h)&&a++)}o=s}return a%2==1},close:function(){var t=this.start,e=this.end,n=this.points;return t&&e&&!t.equals(e)&&n.push(t.clone()),this},lengthPoints:function(){return this.points},convexHull:function(){return new xi(bi(this.points))},equals:function(t){if(!t)return!1;var e=this.points,n=t.points,i=e.length;if(n.length!==i)return!1;for(var r=0;rr+s&&i.x>r+s||n.xo+a&&i.y>o+a||n.ye.x&&t.ye.y}function $i(t,e){return lr(t,e,{interior:!1})}function Zi(t,e){return cr(t,e,{interior:!1})}function Ji(t,e){return dr(t,e,{interior:!1})}function Ki(t,e){return pr(t,e,{interior:!1})}function Qi(t,e){return lr(t,e,{interior:!0})}function tr(t,e){return cr(t,e,{interior:!0})}function er(t,e){return dr(t,e,{interior:!0})}function nr(t,e){return pr(t,e,{interior:!0})}function ir(t,e){return gr(t,e,{interior:!0})}function rr(t,n,i){return t.getSubpaths().some(function(t){var e=t.toPolylines(i)[0];return"Z"===t.getSegment(-1).type?Qi(e,n):$i(e,n)})}function or(t,n,i){return t.getSubpaths().some(function(t){var e=t.toPolylines(i)[0];return"Z"===t.getSegment(-1).type?tr(e,n):Zi(e,n)})}function sr(t,e,n){return ur(t,Gi.fromRect(e),n)}function ar(t,e,n){return fr(t,e,n,{interior:!1})}function ur(t,e,n){return fr(t,e,n,{interior:!0})}function hr(t,n,i,r){return t.getSubpaths().some(function(t){var e=t.toPolylines(i)[0];return"Z"===t.getSegment(-1).type?ur(n,e,r):ar(n,e,r)})}function lr(t,e,n){void 0===n&&(n={});var i,r=n.interior;if(void 0===r&&(r=!1),r){if(t.containsPoint(e.start))return!0;var o=t.start,s=t.end,a=t.points;i=s.equals(o)?a:a.concat([o])}else i=t.points;for(var u=i.length,h=new ni,l=0;l'+t+"";return N.parseXML(e,{async:!1}).documentElement}var n=document.createElementNS(M.svg,"svg");return n.setAttributeNS(M.xmlns,"xmlns:xlink",M.xlink),n.setAttribute("version","1.1"),n},N.createSVGStyle=function(t){return N("style",{type:"text/css"},[N.createCDATASection(t)]).node},N.createCDATASection=function(t){return void 0===t&&(t=""),document.implementation.createDocument(null,"xml",null).createCDATASection(t)},N.idCounter=0,N.uniqueId=function(){return"v-"+ ++N.idCounter},N.toNode=function(t){return N.isV(t)?t.node:t.nodeName&&t||t[0]},N.ensureId=function(t){return(t=N.toNode(t)).id||(t.id=N.uniqueId())},N.sanitizeText=function(t){return(t||"").replace(/ /g,"\xa0")},N.isUndefined=function(t){return void 0===t},N.isString=function(t){return"string"==typeof t},N.isObject=function(t){return t&&"object"==typeof t},N.isArray=Array.isArray,N.parseXML=function(t,e){var n;e=e||{};try{var i=new DOMParser;N.isUndefined(e.async)||(i.async=e.async),n=i.parseFromString(t,"text/xml")}catch(t){n=void 0}if(!n||n.getElementsByTagName("parsererror").length)throw new Error("Invalid XML: "+t);return n},N.qualifyAttr=function(t){if(-1===t.indexOf(":"))return{ns:null,local:t};var e=t.split(":");return{ns:M[e[0]],local:e[1]}},N.transformRegex=/(\w+)\(([^,)]+),?([^)]+)?\)/gi,N.transformSeparatorRegex=/[ ,]+/,N.transformationListRegex=/^(\w+)\((.*)\)/,N.transformStringToMatrix=function(t){var e=N.createSVGMatrix(),n=t&&t.match(N.transformRegex);if(!n)return e;for(var i=0,r=n.length;i=t.start&&et.start&&n<=t.end||t.start>=e&&t.end=e?t.end+=n:t.start>=e&&(t.start+=n,t.end+=n)}),t},N.convertLineToPathData=function(t){return["M",(t=N(t)).attr("x1"),t.attr("y1"),"L",t.attr("x2"),t.attr("y2")].join(" ")},N.convertPolygonToPathData=function(t){var e=N.getPointsFromSvgNode(t);return 0===e.length?null:N.svgPointsToPath(e)+" Z"},N.convertPolylineToPathData=function(t){var e=N.getPointsFromSvgNode(t);return 0===e.length?null:N.svgPointsToPath(e)},N.svgPointsToPath=function(t){for(var e=0,n=t.length;e=o[r]&&(s.push([e].concat(i.splice(0,o[r]))),o[r]););}),s}(t)),!t||!t.length)return[["M",0,0]];for(var e,n=[],i=0,r=0,o=0,s=0,a=t.length,u=0;ud){var k=S,T=u,C=h;p=t(u=x+i*H(S=P+d*(a&&Pf)M=f-1;else if(void 0!==o){var N;if(void 0===v)v=(N="auto"===n.lineHeight?{value:1.5,unit:"em"}:Ir(n.lineHeight,["em"])||{value:1,unit:"em"}).value,"em"===N.unit&&(v*=u.getBBox().height);v*w.length>o&&(M=Math.floor(o/v)-1)}if(null!==M){w.splice(M+1);var R=i.ellipsis;if(!R||M<0)break;"string"!=typeof R&&(R="\u2026");var O=w[M];if(!O&&!S)break;var _,V,F=O.length;do{if(V=O[F],_=O.substring(0,F),V?V.match(l)&&(_+=V):_+="string"==typeof l?l:" ",_+=R,h.data=_,a.getComputedTextLength()<=r){w[M]=_;break}F--}while(0<=F);break}}}return i.svgDocument?s.removeChild(u):document.body.removeChild(s),w.join(c)},jr=function(t){var e=T(T.parseHTML("
"+t+"
",null,!1));return e.find("*").each(function(){var n=this;T.each(n.attributes,function(){var t=this.name,e=this.value;(t.startsWith("on")||e.startsWith("javascript:")||e.startsWith("data:")||e.startsWith("vbscript:"))&&T(n).removeAttr(t)})}),e.html()},Gr=function(t,e){if(window.navigator.msSaveBlob)window.navigator.msSaveBlob(t,e);else{var n=window.URL.createObjectURL(t),i=document.createElement("a");i.href=n,i.download=e,document.body.appendChild(i),i.click(),document.body.removeChild(i),window.URL.revokeObjectURL(n)}},Ur=function(t){t=t.replace(/\s/g,"");var e,n=(t=decodeURIComponent(t)).indexOf(","),i=t.slice(0,n),r=i.split(":")[1].split(";")[0],o=t.slice(n+1);e=0<=i.indexOf("base64")?atob(o):unescape(encodeURIComponent(o));for(var s=new Uint8Array(e.length),a=0;a')({color:t.color||"blue",opacity:Number.isFinite(t.opacity)?t.opacity:1,outerRadius:e+n,innerRadius:e})},highlight:function(t){return Zr('')({color:t.color||"red",width:Number.isFinite(t.width)?t.width:1,blur:Number.isFinite(t.blur)?t.blur:0,opacity:Number.isFinite(t.opacity)?t.opacity:1})},blur:function(t){var e=Number.isFinite(t.x)?t.x:2;return Zr('')({stdDeviation:Number.isFinite(t.y)?[e,t.y]:e})},dropShadow:function(t){var e="SVGFEDropShadowElement"in window?'':'';return Zr(e)({dx:t.dx||0,dy:t.dy||0,opacity:Number.isFinite(t.opacity)?t.opacity:1,color:t.color||"black",blur:Number.isFinite(t.blur)?t.blur:4})},grayscale:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Zr('')({a:.2126+.7874*(1-e),b:.7152-.7152*(1-e),c:.0722-.0722*(1-e),d:.2126-.2126*(1-e),e:.7152+.2848*(1-e),f:.0722-.0722*(1-e),g:.2126-.2126*(1-e),h:.0722+.9278*(1-e)})},sepia:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Zr('')({a:.393+.607*(1-e),b:.769-.769*(1-e),c:.189-.189*(1-e),d:.349-.349*(1-e),e:.686+.314*(1-e),f:.168-.168*(1-e),g:.272-.272*(1-e),h:.534-.534*(1-e),i:.131+.869*(1-e)})},saturate:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Zr('')({amount:1-e})},hueRotate:function(t){return Zr('')({angle:t.angle||0})},invert:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Zr('')({amount:e,amount2:1-e})},brightness:function(t){return Zr('')({amount:Number.isFinite(t.amount)?t.amount:1})},contrast:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Zr('')({amount:e,amount2:.5-e/2})}},$r={number:function(t,e,o){o=o||{currency:["$",""],decimal:".",thousands:",",grouping:[3]};var n=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i.exec(t),i=n[1]||" ",r=n[2]||">",s=n[3]||"",a=n[4]||"",u=n[5],h=+n[6],l=n[7],c=n[8],d=n[9],f=1,p="",g="",v=!1;switch(c&&(c=+c.substring(1)),(u||"0"===i&&"="===r)&&(u=i="0",r="=",l&&(h-=Math.floor((h-1)/4))),d){case"n":l=!0,d="g";break;case"%":f=100,g="%",d="f";break;case"p":f=100,g="%",d="r";break;case"b":case"o":case"x":case"X":"#"===a&&(p="0"+d.toLowerCase());break;case"c":case"d":v=!0,c=0;break;case"s":f=-1,d="r"}"$"===a&&(p=o.currency[0],g=o.currency[1]),"r"!=d||c||(d="g"),null!=c&&("g"==d?c=Math.max(1,Math.min(21,c)):"e"!=d&&"f"!=d||(c=Math.max(0,Math.min(20,c))));var m=u&&l;if(v&&e%1)return"";var y=e<0||0===e&&1/e<0?(e=-e,"-"):s,w=g;if(f<0){var b=this.prefix(e,c);e=b.scale(e),w=b.symbol+g}else e*=f;var x=(e=this.convert(d,e,c)).lastIndexOf("."),A=x<0?e:e.substring(0,x),P=x<0?"":o.decimal+e.substring(x+1);function S(t){for(var e=t.length,n=[],i=0,r=o.grouping[0];0"===r?k+y+e:"^"===r?k.substring(0,E>>=1)+y+e+k.substring(E):y+(m?e:k+e))+w},string:function(t,e){for(var n,i="{",r=!1,o=[];-1!==(n=t.indexOf(i));){var s,a,u;if(s=t.slice(0,n),r){u=(a=s.split(":")).shift().split("."),s=e;for(var h=0;h|\$\{ ?([^{} ]+) ?\}|\{\{([^{} ]+)\}\}/g;return function(i){return i=i||{},t.replace(e,function(t){for(var e=Array.from(arguments).slice(1,4).find(function(t){return!!t}).split("."),n=i[e.shift()];void 0!==n&&e.length;)n=n[e.shift()];return void 0!==n?n:""})}},Jr=n.assign,Kr=n.defaults,Qr=Jr,to=n.defaultsDeep,eo=n.assign,no=n.defaults,io=n.defaultsDeep,ro=n.invokeMap||n.invoke,oo=n.sortedIndexBy||n.sortedIndex,so=n.uniqBy||n.uniq,ao=n.clone,uo=n.cloneDeep,ho=n.isEmpty,lo=n.isEqual,co=n.isFunction,fo=n.isPlainObject,po=n.toArray,go=n.debounce,vo=n.groupBy,mo=n.sortBy,yo=n.flattenDeep,wo=n.without,bo=n.difference,xo=n.intersection,Ao=n.union,Po=n.has,So=n.result,Eo=n.omit,ko=n.pick,To=n.bindAll,Co=n.forIn,Lo=n.camelCase,Mo=n.uniqueId,No=function(){if(n.mergeWith){var t=Array.from(arguments),e=t[t.length-1],i=co(e)?e:Fo;return t.push(function(t,e){var n=i(t,e);return void 0!==n?n:Array.isArray(t)&&!Array.isArray(e)?e:void 0}),n.mergeWith.apply(this,t)}return n.merge.apply(this,arguments)},Ro=function(t){var e=Object.prototype.toString;return!0===t||!1===t||!!t&&"object"==typeof t&&"[object Boolean]"===e.call(t)},Oo=function(t){return!!t&&("object"==typeof t||"function"==typeof t)},_o=function(t){var e=Object.prototype.toString;return"number"==typeof t||!!t&&"object"==typeof t&&"[object Number]"===e.call(t)},Vo=function(t){var e=Object.prototype.toString;return"string"==typeof t||!!t&&"object"==typeof t&&"[object String]"===e.call(t)},Fo=function(){};function Bo(t){t=so(t);var s=po(t).reduce(function(t,e){return t[e.id]=e.clone(),t},{});return po(t).forEach(function(t){var e=s[t.id];if(e.isLink()){var n=e.source(),i=e.target();n.id&&s[n.id]&&e.prop("source/id",s[n.id].id),i.id&&s[i.id]&&e.prop("target/id",s[i.id].id)}var r=t.get("parent");r&&s[r]&&e.set("parent",s[r].id);var o=po(t.get("embeds")).reduce(function(t,e){return s[e]&&t.push(s[e].id),t},[]);ho(o)||e.set("embeds",o)}),s}var Do={x:"x",y:"y",width:"w",height:"h",minimum:"s",maximum:"l",diagonal:"d"},Io=Object.keys(Do).map(function(t){return Do[t]}).join(""),zo="[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?",jo=/\s/g,Go=new RegExp("^("+zo+"\\*)?(["+Io+"])([-+]{1,2}"+zo+")?$","g");function Uo(t){throw new Error("Invalid calc() expression: "+t)}function qo(t,e){var n=Go.exec(t.replace(jo,""));n||Uo(t),Go.lastIndex=0;var i=n[1];void 0===i&&(i=1);var r=n[2],o=n[3];void 0===o&&(o=0);var s=e.x,a=e.y,u=e.width,h=e.height,l=0;switch(r){case Do.width:l=u;break;case Do.height:l=h;break;case Do.x:l=s;break;case Do.y:l=a;break;case Do.minimum:l=Math.min(h,u);break;case Do.maximum:l=Math.max(h,u);break;case Do.diagonal:l=Math.sqrt(h*h+u*u)}return parseFloat(i)*l+function(t){if(!t)return 0;switch(t[0]){case"+":return parseFloat(t.substr(1));case"-":return-parseFloat(t.substr(1))}return parseFloat(t)}(o)}function Ho(t){return"string"==typeof t&&t.includes("calc")}var Yo="calc(",Wo=Yo.length;function Xo(t,e){for(var n=t,i=0;;){var r=n.indexOf(Yo,i);if(-1===r)return n;var o=r+Wo,s=1;t:for(;;){switch(n[o]){case"(":s++;break;case")":if(0===--s)break t;break;case void 0:Uo(n)}o++}var a=n.slice(r+Wo,o);Ho(a)&&(a=Xo(a,e));var u=String(qo(a,e));n=n.slice(0,r)+u+n.slice(o+1),i=r+u.length}}function $o(o,s){return function(t,e){var n=Dr(t);t=parseFloat(t),n&&(t/=100);var i={};if(isFinite(t)){var r=n||0<=t&&t<=1?t*e[s]:Math.max(t+e[s],0);i[o]=r}return i}}function Zo(s,a,u){return function(t,e){var n,i=Dr(t);if(t=parseFloat(t),i&&(t/=100),isFinite(t)){var r=e[u]();n=i||0e.width?ss:as)(t,e)})},refRCircumscribed:{set:function(t,e){var n=Dr(t);t=parseFloat(t),n&&(t/=100);var i,r=Math.sqrt(e.height*e.height+e.width*e.width);return isFinite(t)&&(i=n||0<=t&&t<=1?t*r:Math.max(t+r,0)),{r:i}}},refCx:{set:$o("cx","width")},refCy:{set:$o("cy","height")},xAlignment:{offset:Jo("x","width","right")},yAlignment:{offset:Jo("y","height","bottom")},resetOffset:{offset:function(t,e){return t?{x:-e.x,y:-e.y}:{x:0,y:0}}},refDResetOffset:{set:Qo({resetOffset:!0})},refDKeepOffset:{set:Qo({resetOffset:!1})},refPointsResetOffset:{set:ts({resetOffset:!0})},refPointsKeepOffset:{set:ts({resetOffset:!1})},connection:{qualify:is,set:function(t){var e,n=t.stubs;if(void 0===n&&(n=0),isFinite(n)&&0!==n){var i;i=n<0?(this.getConnectionLength()+n)/2:n;var r=this.getConnection(),o=r.divideAtLength(i),s=r.divideAtLength(-i);o&&s&&(e=o[0].serialize()+" "+s[1].serialize())}return{d:e||this.getSerializedConnection()}}},atConnectionLengthKeepGradient:{qualify:is,set:es("getTangentAtLength",{rotate:!0})},atConnectionLengthIgnoreGradient:{qualify:is,set:es("getTangentAtLength",{rotate:!1})},atConnectionRatioKeepGradient:{qualify:is,set:es("getTangentAtRatio",{rotate:!0})},atConnectionRatioIgnoreGradient:{qualify:is,set:es("getTangentAtRatio",{rotate:!1})}};["transform","d","points","width","height","cx","cy","r","rx","ry","x1","x2","y1","y2","x","y","dx","dy"].forEach(function(i){us[i]={qualify:Ho,set:function(t,e){var n;return(n={})[i]=Xo(t,e),n}}}),us.refR=us.refRInscribed,us.refD=us.refDResetOffset,us.refPoints=us.refPointsResetOffset,us.atConnectionLength=us.atConnectionLengthKeepGradient,us.atConnectionRatio=us.atConnectionRatioKeepGradient,us.refX2=us.refX,us.refY2=us.refY,us.refWidth2=us.refWidth,us.refHeight2=us.refHeight,us["ref-x"]=us.refX,us["ref-y"]=us.refY,us["ref-dy"]=us.refDy,us["ref-dx"]=us.refDx,us["ref-width"]=us.refWidth,us["ref-height"]=us.refHeight,us["x-alignment"]=us.xAlignment,us["y-alignment"]=us.yAlignment;var hs=us,ls=a.Model.extend({constructor:function(t,e){var n,i=t||{};"function"==typeof this.preinitialize&&this.preinitialize.apply(this,arguments),this.cid=Mo("c"),this.attributes={},e&&e.collection&&(this.collection=e.collection),e&&e.parse&&(i=this.parse(i,e)||{}),(n=So(this,"defaults"))&&(i=No({},n,i)),this.set(i,e),this.changed={},this.initialize.apply(this,arguments)},translate:function(t,e,n){throw new Error("Must define a translate() method.")},toJSON:function(){var e=So(this.constructor.prototype,"defaults").attrs||{},t=this.attributes.attrs,o={};Co(t,function(t,i){var r=e[i];Co(t,function(t,n){Oo(t)&&!Array.isArray(t)?Co(t,function(t,e){r&&r[n]&&lo(r[n][e],t)||(o[i]=o[i]||{},(o[i][n]||(o[i][n]={}))[e]=t)}):r&&lo(r[n],t)||(o[i]=o[i]||{},o[i][n]=t)})});var n=uo(Eo(this.attributes,"attrs"));return n.attrs=o,n},initialize:function(t){t&&t.id||this.set("id",this.generateId(),{silent:!0}),this._transitionIds={},this.processPorts(),this.on("change:attrs",this.processPorts,this)},generateId:function(){return Rr()},processPorts:function(){var t=this.ports,n={};Co(this.get("attrs"),function(t,e){t&&t.port&&(void 0!==t.port.id?n[t.port.id]=t.port:n[t.port]={id:t.port})});var i={};(Co(t,function(t,e){n[e]||(i[e]=!0)}),this.graph&&!ho(i))&&(this.graph.getConnectedLinks(this,{inbound:!0}).forEach(function(t){i[t.get("target").port]&&t.remove()}),this.graph.getConnectedLinks(this,{outbound:!0}).forEach(function(t){i[t.get("source").port]&&t.remove()}));this.ports=n},remove:function(t){void 0===t&&(t={});var e=this.graph,n=this.collection;if(!e)return n&&n.remove(this,t),this;e.startBatch("remove");var i=this.getParentCell();i&&i.unembed(this,t);for(var r=this.getEmbeddedCells(),o=0,s=r.length;oh[2]?(s=".3em",r=l,o=0,"start"):uc[2]?(s=".3em",r=-h,o=0,"end"):u "+e),n=this.getSelector(t.parentNode,n)}return n},addLinkFromMagnet:function(t,e,n){var i=this.paper,r=i.model,o=i.getDefaultLink(this,t);return o.set({source:this.getLinkEnd(t,e,n,o,"source"),target:{x:e,y:n}}).addTo(r,{async:!1,ui:!0}),o.findView(i)},getLinkEnd:function(t){for(var e,n=[],i=arguments.length-1;0r.options.clickThreshold||this.notify("element:magnet:pointerclick",t,e,n,i)}},{Flags:Bs});eo(Ds.prototype,Ts);var Is={_results:{},_tests:{svgforeignobject:function(){return!!document.createElementNS&&/SVGForeignObject/.test({}.toString.call(document.createElementNS("http://www.w3.org/2000/svg","foreignObject")))}},addTest:function(t,e){return this._tests[t]=e},test:function(t){var e=this._tests[t];if(!e)throw new Error('Test not defined ("'+t+'"). Use `joint.env.addTest(name, fn) to add a new test.`');var n=this._results[t];if(void 0!==n)return n;try{n=e()}catch(t){n=!1}return this._results[t]=n}},zs=Cs.define("basic.Generic",{attrs:{".":{fill:"#ffffff",stroke:"none"}}}),js=zs.define("basic.Rect",{attrs:{rect:{fill:"#ffffff",stroke:"#000000",width:100,height:60},text:{fill:"#000000",text:"","font-size":14,"ref-x":.5,"ref-y":.5,"text-anchor":"middle","y-alignment":"middle","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Gs=Ds.extend({presentationAttributes:Ds.addPresentationAttributes({attrs:["SCALE"]}),confirmUpdate:function(){var t=Ds.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"SCALE")&&(this.resize(),t=this.removeFlag(t,"SCALE")),t}}),Us=zs.define("basic.Text",{attrs:{text:{"font-size":18,fill:"#000000"}}},{markup:''}),qs=zs.define("basic.Circle",{size:{width:60,height:60},attrs:{circle:{fill:"#ffffff",stroke:"#000000",r:30,cx:30,cy:30},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Hs=zs.define("basic.Ellipse",{size:{width:60,height:40},attrs:{ellipse:{fill:"#ffffff",stroke:"#000000",rx:30,ry:20,cx:30,cy:20},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Ys=zs.define("basic.Polygon",{size:{width:60,height:40},attrs:{polygon:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Ws=zs.define("basic.Polyline",{size:{width:60,height:40},attrs:{polyline:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Xs=zs.define("basic.Image",{attrs:{text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),$s=zs.define("basic.Path",{size:{width:60,height:60},attrs:{path:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle",ref:"path","ref-x":.5,"ref-dy":10,fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Zs=$s.define("basic.Rhombus",{attrs:{path:{d:"M 30 0 L 60 30 30 60 0 30 z"},text:{"ref-y":.5,"ref-dy":null,"y-alignment":"middle"}}}),Js=Is.test("svgforeignobject"),Ks=zs.define("basic.TextBlock",{attrs:{rect:{fill:"#ffffff",stroke:"#000000",width:80,height:100},text:{fill:"#000000","font-size":14,"font-family":"Arial, helvetica, sans-serif"},".content":{text:"","ref-x":.5,"ref-y":.5,"y-alignment":"middle","x-alignment":"middle"}},content:""},{markup:['','',Js?'
':'',""].join(""),initialize:function(){this.listenTo(this,"change:size",this.updateSize),this.listenTo(this,"change:content",this.updateContent),this.updateSize(this,this.get("size")),this.updateContent(this,this.get("content")),zs.prototype.initialize.apply(this,arguments)},updateSize:function(t,e){this.attr({".fobj":eo({},e),div:{style:eo({},e)}})},updateContent:function(t,e){Js?this.attr({".content":{html:jr(e)}}):this.attr({".content":{text:e}})},setForeignObjectSize:function(){this.updateSize.apply(this,arguments)},setDivContent:function(){this.updateContent.apply(this,arguments)}}),Qs=Ds.extend({presentationAttributes:Js?Ds.prototype.presentationAttributes:Ds.addPresentationAttributes({content:["CONTENT"],size:["CONTENT"]}),initFlag:["RENDER","CONTENT"],confirmUpdate:function(){var t=Ds.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"CONTENT")&&(this.updateContent(this.model),t=this.removeFlag(t,"CONTENT")),t},update:function(t,e){var n=this.model;if(Js)Ds.prototype.update.call(this,n,e);else{var i=Eo(e||n.get("attrs"),".content");Ds.prototype.update.call(this,n,i),e&&!Po(e,".content")||this.updateContent(n,e)}},updateContent:function(t,e){var n=No({},(e||t.get("attrs"))[".content"]);n=Eo(n,"text");var i=zr(t.get("content"),t.get("size"),n,{svgDocument:this.paper.svg}),r=Lr({},".content",n,"/");r[".content"].text=i,Ds.prototype.update.call(this,t,r)}}),ta={Generic:zs,Rect:js,TextView:Gs,Text:Us,Circle:qs,Ellipse:Hs,Polygon:Ys,Polyline:Ws,Image:Xs,Path:$s,Rhombus:Zs,TextBlock:Ks,TextBlockView:Qs},ea=ls.extend({markup:['','','','','','','',''].join(""),toolMarkup:['','','','',"Remove link.","",'','','',"Link options.","",""].join(""),doubleToolMarkup:void 0,vertexMarkup:['','','','',"Remove vertex.","",""].join(""),arrowheadMarkup:['','',""].join(""),defaultLabel:void 0,labelMarkup:void 0,_builtins:{defaultLabel:{markup:[{tagName:"rect",selector:"rect"},{tagName:"text",selector:"text"}],attrs:{text:{fill:"#000000",fontSize:14,textAnchor:"middle",yAlignment:"middle",pointerEvents:"none"},rect:{ref:"text",fill:"#ffffff",rx:3,ry:3,refWidth:1,refHeight:1,refX:0,refY:0}},position:{distance:.5}}},defaults:{type:"link",source:{},target:{}},isLink:function(){return!0},disconnect:function(t){return this.set({source:{x:0,y:0},target:{x:0,y:0}},t)},source:function(t,e,n){return void 0===t?ao(this.get("source")):(r=t instanceof ls?((i=ao(e)||{}).id=t.id,n):fo(t)?(i=t,e):((i=ao(e)||{}).x=t.x,i.y=t.y,n),this.set("source",i,r));var i,r},target:function(t,e,n){return void 0===t?ao(this.get("target")):(r=t instanceof ls?((i=ao(e)||{}).id=t.id,n):fo(t)?(i=t,e):((i=ao(e)||{}).x=t.x,i.y=t.y,n),this.set("target",i,r));var i,r},router:function(t,e,n){if(void 0===t){var i=this.get("router");return i?"object"==typeof i?ao(i):i:this.get("manhattan")?{name:"orthogonal"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},s=r?e:n;return this.set("router",o,s)},connector:function(t,e,n){if(void 0===t){var i=this.get("connector");return i?"object"==typeof i?ao(i):i:this.get("smooth")?{name:"smooth"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},s=r?e:n;return this.set("connector",o,s)},label:function(t,e,n){var i=this.labels();return(t=isFinite(t)&&null!==t?0|t:0)<0&&(t=i.length+t),arguments.length<=1?this.prop(["labels",t]):this.prop(["labels",t],e,n)},labels:function(t,e){return 0===arguments.length?(t=this.get("labels"),Array.isArray(t)?t.slice():[]):(Array.isArray(t)||(t=[]),this.set("labels",t,e))},hasLabels:function(){var t=this.attributes.labels;return Array.isArray(t)&&0e[1].x?"right":"left";switch(i){case"left":return new Kn(-1,0);case"right":default:return new Kn(1,0)}}(t,e);case Pa.VERTICAL:return function(t,e,n){var i,r=t.sourceBBox;i=r.width&&r.height?r.sideNearestToPoint(e[0]):r.y>e[1].y?"bottom":"top";switch(i){case"top":return new Kn(0,-1);case"bottom":default:return new Kn(0,1)}}(t,e);case Pa.CLOSEST_POINT:return Ca(t,e);case Pa.OUTWARDS:return Ma(t,e);case Pa.AUTO:default:return ka(t,e)}}(r,c,s,u),f=c[0].distance(c[1])*u.coeff,p=Oa(d,c[1].difference(c[0]).normalize());if(p>Math.PI/4){var g=f+(p-Math.PI/4)*u.angleTangentCoefficient;h=d.clone().scale(g,g)}else h=d.clone().scale(f,f)}if(u.targetTangent)l=u.targetTangent;else{var v=function(t,e,n,i){if(i.targetDirection)switch(i.targetDirection){case Sa.UP:return new Kn(0,-1);case Sa.DOWN:return new Kn(0,1);case Sa.LEFT:return new Kn(-1,0);case Sa.RIGHT:return new Kn(0,1);case Sa.AUTO:return Ta(t,e);case Sa.CLOSEST_POINT:return La(t,e);case Sa.OUTWARDS:return Na(t,e);default:return i.targetDirection}switch(n){case Pa.HORIZONTAL:return function(t,e,n){var i,r=t.targetBBox;i=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.x>e[e.length-2].x?"left":"right";switch(i){case"left":return new Kn(-1,0);case"right":default:return new Kn(1,0)}}(t,e);case Pa.VERTICAL:return function(t,e,n){var i,r=t.targetBBox;i=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.y>e[e.length-2].y?"top":"bottom";switch(i){case"top":return new Kn(0,-1);case"bottom":default:return new Kn(0,1)}}(t,e);case Pa.CLOSEST_POINT:return La(t,e);case Pa.OUTWARDS:return Na(t,e);case Pa.AUTO:default:return Ta(t,e)}}(r,c,s,u),m=c.length-1,y=c[m-1].distance(c[m])*u.coeff,w=Oa(v,c[m-1].difference(c[m]).normalize());if(w>Math.PI/4){var b=y+(w-Math.PI/4)*u.angleTangentCoefficient;l=v.clone().scale(b,b)}else l=v.clone().scale(y,y)}var x=function(t,e,n,i){for(var r=i.tau,o=i.coeff,s=[],a=[],u=[],h=t.length-1,l=0;l=Math.abs(t.y-e.y)){var h=(t.x+e.x)/2;u=Li.createSegment("C",h,t.y,h,e.y,e.x,e.y),r.appendSegment(u)}else{var l=(t.y+e.y)/2;u=Li.createSegment("C",t.x,l,e.x,l,e.x,e.y),r.appendSegment(u)}}return o?r:r.serialize()},curve:Ea},Fa=_s.extend({tagName:"path",className:"highlight-stroke",attributes:{"pointer-events":"none","vector-effect":"non-scaling-stroke",fill:"none"},options:{padding:3,rx:0,ry:0,useFirstSubpath:!1,attrs:{"stroke-width":3,stroke:"#FEB663"}},getPathData:function(e,n){var i,r=this.options,t=r.useFirstSubpath;try{var o=xr(n);if(i=o.convertToPathData().trim(),"PATH"===o.tagName()&&t){var s=i.search(/.M/i)+1;0e.y?"N":"S":t.y===e.y?t.x>e.x?"W":"E":null}function su(t){return new vi(t.x,t.y,0,0)}function au(t){var e=Hr(t.padding||t.elementPadding||20);return{x:-e.left,y:-e.top,width:e.left+e.right,height:e.top+e.bottom}}function uu(t,e){return t.sourceBBox.clone().moveAndExpand(au(e))}function hu(t,e){return t.targetBBox.clone().moveAndExpand(au(e))}function lu(t,e,n){var i=new Kn(t.x,e.y),r=new Kn(e.x,t.y),o=ou(t,i),s=ou(t,r),a=eu[n],u=o===n||o!==a&&(s===a||s!==n)?i:r;return{points:[u],direction:ou(u,e)}}function cu(t,e,n){var i=iu(t,e,n);return{points:[i],direction:ou(i,e)}}function du(e,t,n,i){var r,o={},s=[new Kn(e.x,t.y),new Kn(t.x,e.y)],a=s.filter(function(t){return!n.containsPoint(t)}),u=a.filter(function(t){return ou(t,e)!==i});if(0h.center().distance(t),c=l?e:t,d=l?t:e;return s=iu(o=r?(o=Kn.fromPolar(h.width+h.height,nu[r],c),h.pointNearestToPoint(o).move(o,-1)):h.pointNearestToPoint(c).move(c,1),d,h),o.round().equals(s.round())?(s=Kn.fromPolar(h.width+h.height,On(o.theta(c))+Math.PI/2,d),a=iu(o,s=h.pointNearestToPoint(s).move(d,1).round(),h),u.points=l?[s,a,o]:[o,a,s]):u.points=l?[s,o]:[o,s],u.direction=ou(l?o:s,e),u}function gu(t,e,n){var i,r,o,s,a,u=uu(n,e),h=hu(n,e),l=(r=e,(i=n).sourceAnchor?i.sourceAnchor:uu(i,r).center()),c=(s=e,(o=n).targetAnchor?o.targetAnchor:hu(o,s).center());u=u.union(su(l)),h=h.union(su(c)),(t=po(t).map(Kn)).unshift(l),t.push(c);for(var d=[],f=0,p=t.length-1;fi.maxAllowedDirectionChange)){var Y=ku(D.clone().offset(C.gridOffsetX,C.gridOffsetY),w,u),W=Tu(Y);if(!b.isClose(W)&&!n(Y)){if(0<=_.indexOf(W))if(!Y.equals(f))if(Pu(H,Au(Y,f,O,w,i))>i.maxAllowedDirectionChange)continue;var X=z+C.cost+(G?0:i.penalties[L]);(!b.isOpen(W)||X=this.options.longLinkLength){var r=this.options.doubleLinkToolsOffset||e;i=this.getPointAtLength(n-r),this._tool2Cache.attr("transform","translate("+i.x+", "+i.y+") "+t),this._tool2Cache.attr("visibility","visible")}else this.options.doubleLinkTools&&this._tool2Cache.attr("visibility","hidden")}return this},updateArrowheadMarkers:function(){if(!this._V.markerArrowheads)return this;if("none"===T.css(this._V.markerArrowheads.node,"display"))return this;var t=this.getConnectionLength()`) node that wraps all the nodes of the Cell view. + // Expose class name setter as a separate method. + _ensureElement: function() { + if (!this.el) { + var tagName = result(this, 'tagName'); + var attrs = assign({}, result(this, 'attributes')); + var style = assign({}, result(this, 'style')); + if (this.id) { attrs.id = result(this, 'id'); } + this.setElement(this._createElement(tagName)); + this._setAttributes(attrs); + this._setStyle(style); } else { - // this block is inside of `else` as an optimization so the distance is - // not calculated when we know there are no other intersection points - var endDistance = jumpStart.distance(lastLine.end); - // if the end is too close to possible jump, draw remaining line instead of a jump - if (endDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) { - resultLines.push(lastLine); - return resultLines; - } + this.setElement(result(this, 'el')); } + this._ensureElClassName(); + }, - var startDistance = jumpEnd.distance(lastLine.start); - if (startDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) { - // if the start of line is too close to jump, draw that line instead of a jump - resultLines.push(lastLine); - return resultLines; + _setAttributes: function(attrs) { + if (this.svgElement) { + this.vel.attr(attrs); + } else { + this.$el.attr(attrs); } + }, - // finally create a jump line - var jumpLine = line(jumpStart, jumpEnd); - // it's just simple line but with a `isJump` property - jumpLine.isJump = true; + _setStyle: function(style) { + this.$el.css(style); + }, - resultLines.push( - line(lastLine.start, jumpStart), - jumpLine, - line(jumpEnd, lastLine.end) - ); - return resultLines; - }, []); - } + _createElement: function(tagName) { + if (this.svgElement) { + return document.createElementNS(V.namespace.svg, tagName); + } else { + return document.createElement(tagName); + } + }, - /** - * Assemble `D` attribute of a SVG path by iterating given lines. - * @param {g.line[]} lines source lines to use - * @param {number} jumpSize the size of jump arc (length empty spot on a line) - * @param {number} radius the radius - * @return {string} - */ - function buildPath(lines, jumpSize, jumpType, radius) { + // Utilize an alternative DOM manipulation API by + // adding an element reference wrapped in Vectorizer. + _setElement: function(el) { + this.$el = el instanceof Backbone.$ ? el : Backbone.$(el); + this.el = this.$el[0]; + if (this.svgElement) { this.vel = V(this.el); } + }, - var path = new Path(); - var segment; + _ensureElClassName: function() { + var className = result(this, 'className'); + if (!className) { return; } + var prefixedClassName = addClassNamePrefix(className); + // Note: className removal here kept for backwards compatibility only + if (this.svgElement) { + this.vel.removeClass(className).addClass(prefixedClassName); + } else { + this.$el.removeClass(className).addClass(prefixedClassName); + } + }, - // first move to the start of a first line - segment = Path.createSegment('M', lines[0].start); - path.appendSegment(segment); + init: function() { + // Intentionally empty. + // This method is meant to be overridden. + }, - // make a paths from lines - toArray(lines).forEach(function(line, index) { + onRender: function() { + // Intentionally empty. + // This method is meant to be overridden. + }, - if (line.isJump) { - var angle, diff; + confirmUpdate: function() { + // Intentionally empty. + // This method is meant to be overridden. + return 0; + }, - var control1, control2; + setTheme: function(theme, opt) { - if (jumpType === 'arc') { // approximates semicircle with 2 curves - angle = -90; - // determine rotation of arc based on difference between points - diff = line.start.difference(line.end); - // make sure the arc always points up (or right) - var xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0)); - if (xAxisRotate) { angle += 180; } + opt = opt || {}; - var midpoint = line.midpoint(); - var centerLine = new Line(midpoint, line.end).rotate(midpoint, angle); + // Theme is already set, override is required, and override has not been set. + // Don't set the theme. + if (this.theme && this.requireSetThemeOverride && !opt.override) { + return this; + } - var halfLine; + this.removeThemeClassName(); + this.addThemeClassName(theme); + this.onSetTheme(this.theme/* oldTheme */, theme/* newTheme */); + this.theme = theme; - // first half - halfLine = new Line(line.start, midpoint); + return this; + }, - control1 = halfLine.pointAt(2 / 3).rotate(line.start, angle); - control2 = centerLine.pointAt(1 / 3).rotate(centerLine.end, -angle); + addThemeClassName: function(theme) { - segment = Path.createSegment('C', control1, control2, centerLine.end); - path.appendSegment(segment); + theme = theme || this.theme; + if (!theme) { return this; } - // second half - halfLine = new Line(midpoint, line.end); + var className = this.themeClassNamePrefix + theme; - control1 = centerLine.pointAt(1 / 3).rotate(centerLine.end, angle); - control2 = halfLine.pointAt(1 / 3).rotate(line.end, -angle); + if (this.svgElement) { + this.vel.addClass(className); + } else { + this.$el.addClass(className); + } - segment = Path.createSegment('C', control1, control2, line.end); - path.appendSegment(segment); + return this; + }, - } else if (jumpType === 'gap') { - segment = Path.createSegment('M', line.end); - path.appendSegment(segment); + removeThemeClassName: function(theme) { - } else if (jumpType === 'cubic') { // approximates semicircle with 1 curve - angle = line.start.theta(line.end); + theme = theme || this.theme; - var xOffset = jumpSize * 0.6; - var yOffset = jumpSize * 1.35; + var className = this.themeClassNamePrefix + theme; - // determine rotation of arc based on difference between points - diff = line.start.difference(line.end); - // make sure the arc always points up (or right) - xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0)); - if (xAxisRotate) { yOffset *= -1; } + if (this.svgElement) { + this.vel.removeClass(className); + } else { + this.$el.removeClass(className); + } - control1 = Point(line.start.x + xOffset, line.start.y + yOffset).rotate(line.start, angle); - control2 = Point(line.end.x - xOffset, line.end.y + yOffset).rotate(line.end, angle); + return this; + }, - segment = Path.createSegment('C', control1, control2, line.end); - path.appendSegment(segment); - } + onSetTheme: function(oldTheme, newTheme) { + // Intentionally empty. + // This method is meant to be overridden. + }, - } else { - var nextLine = lines[index + 1]; - if (radius == 0 || !nextLine || nextLine.isJump) { - segment = Path.createSegment('L', line.end); - path.appendSegment(segment); - } else { - buildRoundedSegment(radius, path, line.end, line.start, nextLine.end); - } - } - }); + remove: function() { - return path; - } + this.onRemove(); + this.undelegateDocumentEvents(); - function buildRoundedSegment(offset, path, curr, prev, next) { - var prevDistance = curr.distance(prev) / 2; - var nextDistance = curr.distance(next) / 2; + views[this.cid] = null; - var startMove = -Math.min(offset, prevDistance); - var endMove = -Math.min(offset, nextDistance); + Backbone.View.prototype.remove.apply(this, arguments); - var roundedStart = curr.clone().move(prev, startMove).round(); - var roundedEnd = curr.clone().move(next, endMove).round(); + return this; + }, - var control1 = new Point((_13 * roundedStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedStart.y)); - var control2 = new Point((_13 * roundedEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedEnd.y)); + onRemove: function() { + // Intentionally empty. + // This method is meant to be overridden. + }, - var segment; - segment = Path.createSegment('L', roundedStart); - path.appendSegment(segment); + getEventNamespace: function() { + // Returns a per-session unique namespace + return '.joint-event-ns-' + this.cid; + }, - segment = Path.createSegment('C', control1, control2, roundedEnd); - path.appendSegment(segment); - } + delegateElementEvents: function(element, events, data) { + if (!events) { return this; } + data || (data = {}); + var eventNS = this.getEventNamespace(); + for (var eventName in events) { + var method = events[eventName]; + if (typeof method !== 'function') { method = this[method]; } + if (!method) { continue; } + $(element).on(eventName + eventNS, data, method.bind(this)); + } + return this; + }, - /** - * Actual connector function that will be run on every update. - * @param {g.point} sourcePoint start point of this link - * @param {g.point} targetPoint end point of this link - * @param {g.point[]} route of this link - * @param {object} opt options - * @property {number} size optional size of a jump arc - * @return {string} created `D` attribute of SVG path - */ - var jumpover = function(sourcePoint, targetPoint, route, opt) { // eslint-disable-line max-params + undelegateElementEvents: function(element) { + $(element).off(this.getEventNamespace()); + return this; + }, - setupUpdating(this); + delegateDocumentEvents: function(events, data) { + events || (events = result(this, 'documentEvents')); + return this.delegateElementEvents(document, events, data); + }, - var raw = opt.raw; - var jumpSize = opt.size || JUMP_SIZE; - var jumpType = opt.jump && ('' + opt.jump).toLowerCase(); - var radius = opt.radius || RADIUS; - var ignoreConnectors = opt.ignoreConnectors || IGNORED_CONNECTORS; + undelegateDocumentEvents: function() { + return this.undelegateElementEvents(document); + }, - // grab the first jump type as a default if specified one is invalid - if (JUMP_TYPES.indexOf(jumpType) === -1) { - jumpType = JUMP_TYPES[0]; - } + eventData: function(evt, data) { + if (!evt) { throw new Error('eventData(): event object required.'); } + var currentData = evt.data; + var key = '__' + this.cid + '__'; + if (data === undefined) { + if (!currentData) { return {}; } + return currentData[key] || {}; + } + currentData || (currentData = evt.data = {}); + currentData[key] || (currentData[key] = {}); + assign(currentData[key], data); + return this; + }, - var paper = this.paper; - var graph = paper.model; - var allLinks = graph.getLinks(); + stopPropagation: function(evt) { + this.eventData(evt, { propagationStopped: true }); + return this; + }, - // there is just one link, draw it directly - if (allLinks.length === 1) { - return buildPath( - createLines(sourcePoint, targetPoint, route), - jumpSize, jumpType, radius - ); + isPropagationStopped: function(evt) { + return !!this.eventData(evt).propagationStopped; } - var thisModel = this.model; - var thisIndex = allLinks.indexOf(thisModel); - var defaultConnector = paper.options.defaultConnector || {}; - - // not all links are meant to be jumped over. - var links = allLinks.filter(function(link, idx) { + }, { - var connector = link.get('connector') || defaultConnector; + extend: function() { - // avoid jumping over links with connector type listed in `ignored connectors`. - if (toArray(ignoreConnectors).includes(connector.name)) { - return false; - } - // filter out links that are above this one and have the same connector type - // otherwise there would double hoops for each intersection - if (idx > thisIndex) { - return connector.name !== 'jumpover'; - } - return true; - }); + var args = Array.from(arguments); - // find views for all links - var linkViews = links.map(function(link) { - return paper.findViewByModel(link); - }); + // Deep clone the prototype and static properties objects. + // This prevents unexpected behavior where some properties are overwritten outside of this function. + var protoProps = args[0] && assign({}, args[0]) || {}; + var staticProps = args[1] && assign({}, args[1]) || {}; - // create lines for this link - var thisLines = createLines( - sourcePoint, - targetPoint, - route - ); + // Need the real render method so that we can wrap it and call it later. + var renderFn = protoProps.render || (this.prototype && this.prototype.render) || null; - // create lines for all other links - var linkLines = linkViews.map(function(linkView) { - if (linkView == null) { - return []; - } - if (linkView === this) { - return thisLines; - } - return createLines( - linkView.sourcePoint, - linkView.targetPoint, - linkView.route - ); - }, this); - - // transform lines for this link by splitting with jump lines at - // points of intersection with other links - var jumpingLines = thisLines.reduce(function(resultLines, thisLine) { - // iterate all links and grab the intersections with this line - // these are then sorted by distance so the line can be split more easily + /* + Wrap the real render method so that: + .. `onRender` is always called. + .. `this` is always returned. + */ + protoProps.render = function() { - var intersections = links.reduce(function(res, link, i) { - // don't intersection with itself - if (link !== thisModel) { + if (typeof renderFn === 'function') { + // Call the original render method. + renderFn.apply(this, arguments); + } - var lineIntersections = findLineIntersections(thisLine, linkLines[i]); - res.push.apply(res, lineIntersections); + if (this.render.__render__ === renderFn) { + // Should always call onRender() method. + // Should call it only once when renderFn is actual prototype method i.e. not the wrapper + this.onRender(); } - return res; - }, []).sort(function(a, b) { - return sortPoints(thisLine.start, a) - sortPoints(thisLine.start, b); - }); - if (intersections.length > 0) { - // split the line based on found intersection points - resultLines.push.apply(resultLines, createJumps(thisLine, intersections, jumpSize)); - } else { - // without any intersection the line goes uninterrupted - resultLines.push(thisLine); - } - return resultLines; - }, []); + // Should always return itself. + return this; + }; - var path = buildPath(jumpingLines, jumpSize, jumpType, radius); - return (raw) ? path : path.serialize(); - }; + protoProps.render.__render__ = renderFn; - var normal = function(sourcePoint, targetPoint, route, opt) { + return Backbone.View.extend.call(this, protoProps, staticProps); + } + }); - var raw = opt && opt.raw; - var points = [sourcePoint].concat(route).concat([targetPoint]); + var DoubleTapEventName = 'dbltap'; + if ($.event && !(DoubleTapEventName in $.event.special)) { + var maxDelay = config.doubleTapInterval; + var minDelay = 30; + $.event.special[DoubleTapEventName] = { + bindType: 'touchend', + delegateType: 'touchend', + handle: function(event) { + var ref; - var polyline = new Polyline(points); - var path = new Path(polyline); + var args = [], len = arguments.length - 1; + while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; + var handleObj = event.handleObj; + var target = event.target; + var targetData = $.data(target); + var now = new Date().getTime(); + var delta = 'lastTouch' in targetData ? now - targetData.lastTouch : 0; + if (delta < maxDelay && delta > minDelay) { + targetData.lastTouch = null; + event.type = handleObj.origType; + // let jQuery handle the triggering of "dbltap" event handlers + (ref = handleObj.handler).call.apply(ref, [ this, event ].concat( args )); + } else { + targetData.lastTouch = now; + } + } + }; + } - return (raw) ? path : path.serialize(); + var index$1 = ({ + views: views, + View: View + }); + + var LayersNames = { + CELLS: 'cells', + BACK: 'back', + FRONT: 'front', + TOOLS: 'tools', + LABELS: 'labels' }; - var rounded = function(sourcePoint, targetPoint, route, opt) { + var PaperLayer = View.extend({ - opt || (opt = {}); + tagName: 'g', + svgElement: true, + pivotNodes: null, + defaultTheme: null, - var offset = opt.radius || 10; - var raw = opt.raw; - var path = new Path(); - var segment; + options: { + name: '' + }, - segment = Path.createSegment('M', sourcePoint); - path.appendSegment(segment); + className: function() { + return addClassNamePrefix(((this.options.name) + "-layer")); + }, - var _13 = 1 / 3; - var _23 = 2 / 3; + init: function() { + this.pivotNodes = {}; + }, - var curr; - var prev, next; - var prevDistance, nextDistance; - var startMove, endMove; - var roundedStart, roundedEnd; - var control1, control2; + insertSortedNode: function(node, z) { + this.el.insertBefore(node, this.insertPivot(z)); + }, - for (var index = 0, n = route.length; index < n; index++) { + insertNode: function(node) { + var ref = this; + var el = ref.el; + if (node.parentNode !== el) { + el.appendChild(node); + } + }, - curr = new Point(route[index]); + insertPivot: function(z) { + var ref = this; + var el = ref.el; + var pivotNodes = ref.pivotNodes; + z = +z; + z || (z = 0); + var pivotNode = pivotNodes[z]; + if (pivotNode) { return pivotNode; } + pivotNode = pivotNodes[z] = document.createComment('z-index:' + (z + 1)); + var neighborZ = -Infinity; + for (var currentZ in pivotNodes) { + currentZ = +currentZ; + if (currentZ < z && currentZ > neighborZ) { + neighborZ = currentZ; + if (neighborZ === z - 1) { continue; } + } + } + if (neighborZ !== -Infinity) { + var neighborPivot = pivotNodes[neighborZ]; + // Insert After + el.insertBefore(pivotNode, neighborPivot.nextSibling); + } else { + // First Child + el.insertBefore(pivotNode, el.firstChild); + } + return pivotNode; + }, - prev = route[index - 1] || sourcePoint; - next = route[index + 1] || targetPoint; + removePivots: function() { + var ref = this; + var el = ref.el; + var pivotNodes = ref.pivotNodes; + for (var z in pivotNodes) { el.removeChild(pivotNodes[z]); } + this.pivotNodes = {}; + } - prevDistance = nextDistance || (curr.distance(prev) / 2); - nextDistance = curr.distance(next) / 2; + }); - startMove = -Math.min(offset, prevDistance); - endMove = -Math.min(offset, nextDistance); + function toArray$1(obj) { + if (!obj) { return []; } + if (Array.isArray(obj)) { return obj; } + return [obj]; + } - roundedStart = curr.clone().move(prev, startMove).round(); - roundedEnd = curr.clone().move(next, endMove).round(); + var HighlighterView = View.extend({ - control1 = new Point((_13 * roundedStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedStart.y)); - control2 = new Point((_13 * roundedEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedEnd.y)); + tagName: 'g', + svgElement: true, + className: 'highlight', - segment = Path.createSegment('L', roundedStart); - path.appendSegment(segment); + HIGHLIGHT_FLAG: 1, + UPDATE_PRIORITY: 3, + DETACHABLE: false, + UPDATABLE: true, + MOUNTABLE: true, - segment = Path.createSegment('C', control1, control2, roundedEnd); - path.appendSegment(segment); - } + cellView: null, + nodeSelector: null, + node: null, + updateRequested: false, + transformGroup: null, - segment = Path.createSegment('L', targetPoint); - path.appendSegment(segment); + requestUpdate: function requestUpdate(cellView, nodeSelector) { + var paper = cellView.paper; + this.cellView = cellView; + this.nodeSelector = nodeSelector; + if (paper) { + this.updateRequested = true; + paper.requestViewUpdate(this, this.HIGHLIGHT_FLAG, this.UPDATE_PRIORITY); + } + }, - return (raw) ? path : path.serialize(); - }; + confirmUpdate: function confirmUpdate() { + // The cellView is now rendered/updated since it has a higher update priority. + this.updateRequested = false; + var ref = this; + var cellView = ref.cellView; + var nodeSelector = ref.nodeSelector; + this.update(cellView, nodeSelector); + this.mount(); + this.transform(); + return 0; + }, - var smooth = function(sourcePoint, targetPoint, route, opt) { + findNode: function findNode(cellView, nodeSelector) { + var assign, assign$1; - var raw = opt && opt.raw; - var path; + if ( nodeSelector === void 0 ) nodeSelector = null; + var el; + if (typeof nodeSelector === 'string') { + (assign = cellView.findBySelector(nodeSelector), el = assign[0]); + } else if (isPlainObject(nodeSelector)) { + var isLink = cellView.model.isLink(); + var label = nodeSelector.label; if ( label === void 0 ) label = null; + var port = nodeSelector.port; + var selector = nodeSelector.selector; + if (isLink && label !== null) { + // Link Label Selector + el = cellView.findLabelNode(label, selector); + } else if (!isLink && port) { + // Element Port Selector + el = cellView.findPortNode(port, selector); + } else { + // Cell Selector + (assign$1 = cellView.findBySelector(selector), el = assign$1[0]); + } + } else if (nodeSelector) { + el = V.toNode(nodeSelector); + if (!(el instanceof SVGElement)) { el = null; } + } + return el ? el : null; + }, - if (route && route.length !== 0) { + mount: function mount() { + var ref = this; + var MOUNTABLE = ref.MOUNTABLE; + var cellView = ref.cellView; + var el = ref.el; + var options = ref.options; + var transformGroup = ref.transformGroup; + if (!MOUNTABLE || transformGroup) { return; } + var cellViewRoot = cellView.vel; + var paper = cellView.paper; + var layerName = options.layer; + if (layerName) { + var vGroup = this.transformGroup = V('g').addClass('highlight-transform').append(el); + paper.getLayerView(layerName).insertSortedNode(vGroup.node, options.z); + } else { + // TODO: prepend vs append + if (!el.parentNode || el.nextSibling) { + // Not appended yet or not the last child + cellViewRoot.append(el); + } + } + }, - var points = [sourcePoint].concat(route).concat([targetPoint]); - var curves = Curve.throughPoints(points); + unmount: function unmount() { + var ref = this; + var MOUNTABLE = ref.MOUNTABLE; + var transformGroup = ref.transformGroup; + var vel = ref.vel; + if (!MOUNTABLE) { return; } + if (transformGroup) { + this.transformGroup = null; + transformGroup.remove(); + } else { + vel.remove(); + } + }, - path = new Path(curves); + transform: function transform() { + var ref = this; + var transformGroup = ref.transformGroup; + var cellView = ref.cellView; + var updateRequested = ref.updateRequested; + if (!transformGroup || cellView.model.isLink() || updateRequested) { return; } + var translateMatrix = cellView.getRootTranslateMatrix(); + var rotateMatrix = cellView.getRootRotateMatrix(); + var transformMatrix = translateMatrix.multiply(rotateMatrix); + transformGroup.attr('transform', V.matrixToTransformString(transformMatrix)); + }, - } else { - // if we have no route, use a default cubic bezier curve - // cubic bezier requires two control points - // the control points have `x` midway between source and target - // this produces an S-like curve + update: function update() { + var ref = this; + var prevNode = ref.node; + var cellView = ref.cellView; + var nodeSelector = ref.nodeSelector; + var updateRequested = ref.updateRequested; + var id = ref.id; + if (updateRequested) { return; } + var node = this.node = this.findNode(cellView, nodeSelector); + if (prevNode) { + this.unhighlight(cellView, prevNode); + } + if (node) { + this.highlight(cellView, node); + this.mount(); + } else { + this.unmount(); + cellView.notify('cell:highlight:invalid', id, this); + } + }, - path = new Path(); + onRemove: function onRemove() { + var ref = this; + var node = ref.node; + var cellView = ref.cellView; + var id = ref.id; + var constructor = ref.constructor; + if (node) { + this.unhighlight(cellView, node); + } + this.unmount(); + constructor._removeRef(cellView, id); + }, - var segment; + highlight: function highlight(_cellView, _node) { + // to be overridden + }, - segment = Path.createSegment('M', sourcePoint); - path.appendSegment(segment); + unhighlight: function unhighlight(_cellView, _node) { + // to be overridden + } - if ((Math.abs(sourcePoint.x - targetPoint.x)) >= (Math.abs(sourcePoint.y - targetPoint.y))) { - var controlPointX = (sourcePoint.x + targetPoint.x) / 2; + }, { - segment = Path.createSegment('C', controlPointX, sourcePoint.y, controlPointX, targetPoint.y, targetPoint.x, targetPoint.y); - path.appendSegment(segment); + _views: {}, - } else { - var controlPointY = (sourcePoint.y + targetPoint.y) / 2; + // Used internally by CellView highlight() + highlight: function(cellView, node, opt) { + var id = this.uniqueId(node, opt); + this.add(cellView, node, id, opt); + }, - segment = Path.createSegment('C', sourcePoint.x, controlPointY, targetPoint.x, controlPointY, targetPoint.x, targetPoint.y); - path.appendSegment(segment); + // Used internally by CellView unhighlight() + unhighlight: function(cellView, node, opt) { + var id = this.uniqueId(node, opt); + this.remove(cellView, id); + }, + + get: function get(cellView, id) { + if ( id === void 0 ) id = null; + var cid = cellView.cid; + var ref$2 = this; + var _views = ref$2._views; + var refs = _views[cid]; + if (id === null) { + // all highlighters + var views = []; + if (!refs) { return views; } + for (var hid in refs) { + var ref = refs[hid]; + if (ref instanceof this) { + views.push(ref); + } + } + return views; + } else { + // single highlighter + if (!refs) { return null; } + if (id in refs) { + var ref$1 = refs[id]; + if (ref$1 instanceof this) { return ref$1; } + } + return null; } - } + }, - return (raw) ? path : path.serialize(); - }; + add: function add(cellView, nodeSelector, id, opt) { + if ( opt === void 0 ) opt = {}; - var Directions = { - AUTO: 'auto', - HORIZONTAL: 'horizontal', - VERTICAL: 'vertical', - CLOSEST_POINT: 'closest-point', - OUTWARDS: 'outwards' - }; + if (!id) { throw new Error('dia.HighlighterView: An ID required.'); } + // Search the existing view amongst all the highlighters + var previousView = HighlighterView.get(cellView, id); + if (previousView) { previousView.remove(); } + var view = new this(opt); + view.id = id; + this._addRef(cellView, id, view); + view.requestUpdate(cellView, nodeSelector); + return view; + }, - var TangentDirections = { - UP: 'up', - DOWN: 'down', - LEFT: 'left', - RIGHT: 'right', - AUTO: 'auto', - CLOSEST_POINT: 'closest-point', - OUTWARDS: 'outwards' - }; + _addRef: function _addRef(cellView, id, view) { + var cid = cellView.cid; + var ref = this; + var _views = ref._views; + var refs = _views[cid]; + if (!refs) { refs = _views[cid] = {}; } + refs[id] = view; + }, - var curve = function(sourcePoint, targetPoint, route, opt, linkView) { - if ( route === void 0 ) route = []; - if ( opt === void 0 ) opt = {}; + _removeRef: function _removeRef(cellView, id) { + var cid = cellView.cid; + var ref = this; + var _views = ref._views; + var refs = _views[cid]; + if (!refs) { return; } + if (id) { delete refs[id]; } + for (var _ in refs) { return; } + delete _views[cid]; + }, - var raw = Boolean(opt.raw); - // distanceCoefficient - a coefficient of the tangent vector length relative to the distance between points. - // angleTangentCoefficient - a coefficient of the end tangents length in the case of angles larger than 45 degrees. - // tension - a Catmull-Rom curve tension parameter. - // sourceTangent - a tangent vector along the curve at the sourcePoint. - // sourceDirection - a unit direction vector along the curve at the sourcePoint. - // targetTangent - a tangent vector along the curve at the targetPoint. - // targetDirection - a unit direction vector along the curve at the targetPoint. - // precision - a rounding precision for path values. - var direction = opt.direction; if ( direction === void 0 ) direction = Directions.AUTO; - var precision = opt.precision; if ( precision === void 0 ) precision = 3; - var options = { - coeff: opt.distanceCoefficient || 0.6, - angleTangentCoefficient: opt.angleTangentCoefficient || 80, - tau: opt.tension || 0.5, - sourceTangent: opt.sourceTangent ? new Point(opt.sourceTangent) : null, - targetTangent: opt.targetTangent ? new Point(opt.targetTangent) : null - }; - if (typeof opt.sourceDirection === 'string') - { options.sourceDirection = opt.sourceDirection; } - else if (typeof opt.sourceDirection === 'number') - { options.sourceDirection = new Point(1, 0).rotate(null, opt.sourceDirection); } - else - { options.sourceDirection = opt.sourceDirection ? new Point(opt.sourceDirection).normalize() : null; } + remove: function remove(cellView, id) { + if ( id === void 0 ) id = null; - if (typeof opt.targetDirection === 'string') - { options.targetDirection = opt.targetDirection; } - else if (typeof opt.targetDirection === 'number') - { options.targetDirection = new Point(1, 0).rotate(null, opt.targetDirection); } - else - { options.targetDirection = opt.targetDirection ? new Point(opt.targetDirection).normalize() : null; } + toArray$1(this.get(cellView, id)).forEach(function (view) { + view.remove(); + }); + }, - var completeRoute = [sourcePoint ].concat( route.map(function (p) { return new Point(p); }), [targetPoint]); + update: function update(cellView, id, dirty) { + if ( id === void 0 ) id = null; + if ( dirty === void 0 ) dirty = false; - // The calculation of a sourceTangent - var sourceTangent; - if (options.sourceTangent) { - sourceTangent = options.sourceTangent; - } else { - var sourceDirection = getSourceTangentDirection(linkView, completeRoute, direction, options); - var tangentLength = completeRoute[0].distance(completeRoute[1]) * options.coeff; - var pointsVector = completeRoute[1].difference(completeRoute[0]).normalize(); - var angle = angleBetweenVectors(sourceDirection, pointsVector); - if (angle > Math.PI / 4) { - var updatedLength = tangentLength + (angle - Math.PI / 4) * options.angleTangentCoefficient; - sourceTangent = sourceDirection.clone().scale(updatedLength, updatedLength); - } else { - sourceTangent = sourceDirection.clone().scale(tangentLength, tangentLength); - } - } + toArray$1(this.get(cellView, id)).forEach(function (view) { + if (dirty || view.UPDATABLE) { view.update(); } + }); + }, - // The calculation of a targetTangent - var targetTangent; - if (options.targetTangent) { - targetTangent = options.targetTangent; - } else { - var targetDirection = getTargetTangentDirection(linkView, completeRoute, direction, options); - var last = completeRoute.length - 1; - var tangentLength$1 = completeRoute[last - 1].distance(completeRoute[last]) * options.coeff; - var pointsVector$1 = completeRoute[last - 1].difference(completeRoute[last]).normalize(); - var angle$1 = angleBetweenVectors(targetDirection, pointsVector$1); - if (angle$1 > Math.PI / 4) { - var updatedLength$1 = tangentLength$1 + (angle$1 - Math.PI / 4) * options.angleTangentCoefficient; - targetTangent = targetDirection.clone().scale(updatedLength$1, updatedLength$1); - } else { - targetTangent = targetDirection.clone().scale(tangentLength$1, tangentLength$1); - } + transform: function transform(cellView, id) { + if ( id === void 0 ) id = null; + + toArray$1(this.get(cellView, id)).forEach(function (view) { + if (view.UPDATABLE) { view.transform(); } + }); + }, + + uniqueId: function uniqueId(node, opt) { + if ( opt === void 0 ) opt = ''; + + return V.ensureId(node) + JSON.stringify(opt); } - - var catmullRomCurves = createCatmullRomCurves(completeRoute, sourceTangent, targetTangent, options); - var bezierCurves = catmullRomCurves.map(function (curve) { return catmullRomToBezier(curve, options); }); - var path = new Path(bezierCurves).round(precision); - return (raw) ? path : path.serialize(); + }); + + var HighlightingTypes = { + DEFAULT: 'default', + EMBEDDING: 'embedding', + CONNECTING: 'connecting', + MAGNET_AVAILABILITY: 'magnetAvailability', + ELEMENT_AVAILABILITY: 'elementAvailability' }; - curve.Directions = Directions; - curve.TangentDirections = TangentDirections; - function getHorizontalSourceDirection(linkView, route, options) { - var sourceBBox = linkView.sourceBBox; + // CellView base view and controller. + // -------------------------------------------- - var sourceSide; - if (!sourceBBox.width || !sourceBBox.height) { - if (sourceBBox.x > route[1].x) - { sourceSide = 'right'; } - else - { sourceSide = 'left'; } - } else { - sourceSide = sourceBBox.sideNearestToPoint(route[0]); - } + // This is the base view and controller for `ElementView` and `LinkView`. + var CellView = View.extend({ - switch (sourceSide) { - case 'left': { - return new Point(-1, 0); - } - case 'right': - default: { - return new Point(1, 0); - } - } - } + tagName: 'g', - function getHorizontalTargetDirection(linkView, route, options) { - var targetBBox = linkView.targetBBox; + svgElement: true, - var targetSide; - if (!targetBBox.width || !targetBBox.height) { - if (targetBBox.x > route[route.length - 2].x) - { targetSide = 'left'; } - else - { targetSide = 'right'; } - } else { - targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); - } + selector: 'root', - switch (targetSide) { - case 'left': { - return new Point(-1, 0); - } - case 'right': - default: { - return new Point(1, 0); - } - } - } + metrics: null, - function getVerticalSourceDirection(linkView, route, options) { - var sourceBBox = linkView.sourceBBox; + className: function() { - var sourceSide; - if (!sourceBBox.width || !sourceBBox.height) { - if (sourceBBox.y > route[1].y) - { sourceSide = 'bottom'; } - else - { sourceSide = 'top'; } - } else { - sourceSide = sourceBBox.sideNearestToPoint(route[0]); - } + var classNames = ['cell']; + var type = this.model.get('type'); - switch (sourceSide) { - case 'top': { - return new Point(0, -1); - } - case 'bottom': - default: { - return new Point(0, 1); + if (type) { + + type.toLowerCase().split('.').forEach(function(value, index, list) { + classNames.push('type-' + list.slice(0, index + 1).join('-')); + }); } - } - } - function getVerticalTargetDirection(linkView, route, options) { - var targetBBox = linkView.targetBBox; + return classNames.join(' '); + }, - var targetSide; - if (!targetBBox.width || !targetBBox.height) { - if (targetBBox.y > route[route.length - 2].y) - { targetSide = 'top'; } - else - { targetSide = 'bottom'; } - } else { - targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); - } + _presentationAttributes: null, + _flags: null, - switch (targetSide) { - case 'top': { - return new Point(0, -1); - } - case 'bottom': - default: { - return new Point(0, 1); + setFlags: function() { + var flags = {}; + var attributes = {}; + var shift = 0; + var i, n, label; + var presentationAttributes = result(this, 'presentationAttributes'); + for (var attribute in presentationAttributes) { + if (!presentationAttributes.hasOwnProperty(attribute)) { continue; } + var labels = presentationAttributes[attribute]; + if (!Array.isArray(labels)) { labels = [labels]; } + for (i = 0, n = labels.length; i < n; i++) { + label = labels[i]; + var flag = flags[label]; + if (!flag) { + flag = flags[label] = 1<<(shift++); + } + attributes[attribute] |= flag; + } + } + var initFlag = result(this, 'initFlag'); + if (!Array.isArray(initFlag)) { initFlag = [initFlag]; } + for (i = 0, n = initFlag.length; i < n; i++) { + label = initFlag[i]; + if (!flags[label]) { flags[label] = 1<<(shift++); } } - } - } - function getAutoSourceDirection(linkView, route, options) { - var sourceBBox = linkView.sourceBBox; + // 26 - 30 are reserved for paper flags + // 31+ overflows maximal number + if (shift > 25) { throw new Error('dia.CellView: Maximum number of flags exceeded.'); } - var sourceSide; - if (!sourceBBox.width || !sourceBBox.height) { - sourceSide = sourceBBox.sideNearestToPoint(route[1]); - } else { - sourceSide = sourceBBox.sideNearestToPoint(route[0]); - } + this._flags = flags; + this._presentationAttributes = attributes; + }, - switch (sourceSide) { - case 'top': - return new Point(0, -1); - case 'bottom': - return new Point(0, 1); - case 'right': - return new Point(1, 0); - case 'left': - return new Point(-1, 0); - } - } + hasFlag: function(flag, label) { + return flag & this.getFlag(label); + }, - function getAutoTargetDirection(linkView, route, options) { - var targetBBox = linkView.targetBBox; - - var targetSide; - if (!targetBBox.width || !targetBBox.height) { - targetSide = targetBBox.sideNearestToPoint(route[route.length - 2]); - } else { - targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); - } + removeFlag: function(flag, label) { + return flag ^ (flag & this.getFlag(label)); + }, - switch (targetSide) { - case 'top': - return new Point(0, -1); - case 'bottom': - return new Point(0, 1); - case 'right': - return new Point(1, 0); - case 'left': - return new Point(-1, 0); - } - } + getFlag: function(label) { + var flags = this._flags; + if (!flags) { return 0; } + var flag = 0; + if (Array.isArray(label)) { + for (var i = 0, n = label.length; i < n; i++) { flag |= flags[label[i]]; } + } else { + flag |= flags[label]; + } + return flag; + }, - function getClosestPointSourceDirection(linkView, route, options) { - return route[1].difference(route[0]).normalize(); - } + attributes: function() { + var cell = this.model; + return { + 'model-id': cell.id, + 'data-type': cell.attributes.type + }; + }, - function getClosestPointTargetDirection(linkView, route, options) { - var last = route.length - 1; - return route[last - 1].difference(route[last]).normalize(); - } + constructor: function(options) { - function getOutwardsSourceDirection(linkView, route, options) { - var sourceBBox = linkView.sourceBBox; - var sourceCenter = sourceBBox.center(); - return route[0].difference(sourceCenter).normalize(); - } + // Make sure a global unique id is assigned to this view. Store this id also to the properties object. + // The global unique id makes sure that the same view can be rendered on e.g. different machines and + // still be associated to the same object among all those clients. This is necessary for real-time + // collaboration mechanism. + options.id = options.id || guid(this); - function getOutwardsTargetDirection(linkView, route, options) { - var targetBBox = linkView.targetBBox; - var targetCenter = targetBBox.center(); - return route[route.length - 1].difference(targetCenter).normalize(); - } + View.call(this, options); + }, - function getSourceTangentDirection(linkView, route, direction, options) { - if (options.sourceDirection) { - switch(options.sourceDirection) { - case TangentDirections.UP: - return new Point(0, -1); - case TangentDirections.DOWN: - return new Point(0, 1); - case TangentDirections.LEFT: - return new Point(-1, 0); - case TangentDirections.RIGHT: - return new Point(0, 1); - case TangentDirections.AUTO: - return getAutoSourceDirection(linkView, route, options); - case TangentDirections.CLOSEST_POINT: - return getClosestPointSourceDirection(linkView, route, options); - case TangentDirections.OUTWARDS: { - return getOutwardsSourceDirection(linkView, route, options); - } - default: - return options.sourceDirection; - } - } - - switch (direction) { - case Directions.HORIZONTAL: - return getHorizontalSourceDirection(linkView, route, options); - case Directions.VERTICAL: - return getVerticalSourceDirection(linkView, route, options); - case Directions.CLOSEST_POINT: - return getClosestPointSourceDirection(linkView, route, options); - case Directions.OUTWARDS: - return getOutwardsSourceDirection(linkView, route, options); - case Directions.AUTO: - default: - return getAutoSourceDirection(linkView, route, options); - } - } + initialize: function() { - function getTargetTangentDirection(linkView, route, direction, options) { - if (options.targetDirection) { - switch(options.targetDirection) { - case TangentDirections.UP: - return new Point(0, -1); - case TangentDirections.DOWN: - return new Point(0, 1); - case TangentDirections.LEFT: - return new Point(-1, 0); - case TangentDirections.RIGHT: - return new Point(0, 1); - case TangentDirections.AUTO: - return getAutoTargetDirection(linkView, route, options); - case TangentDirections.CLOSEST_POINT: - return getClosestPointTargetDirection(linkView, route, options); - case TangentDirections.OUTWARDS: { - return getOutwardsTargetDirection(linkView, route, options); - } - default: - return options.targetDirection; - } - } - - switch (direction) { - case Directions.HORIZONTAL: - return getHorizontalTargetDirection(linkView, route, options); - case Directions.VERTICAL: - return getVerticalTargetDirection(linkView, route, options); - case Directions.CLOSEST_POINT: - return getClosestPointTargetDirection(linkView, route, options); - case Directions.OUTWARDS: - return getOutwardsTargetDirection(linkView, route, options); - case Directions.AUTO: - default: - return getAutoTargetDirection(linkView, route, options); - } - } + this.setFlags(); - function rotateVector(vector, angle) { - var cos = Math.cos(angle); - var sin = Math.sin(angle); - var x = cos * vector.x - sin * vector.y; - var y = sin * vector.x + cos * vector.y; - vector.x = x; - vector.y = y; - } + View.prototype.initialize.apply(this, arguments); - function angleBetweenVectors(v1, v2) { - var cos = v1.dot(v2) / (v1.magnitude() * v2.magnitude()); - if (cos < -1) { cos = -1; } - if (cos > 1) { cos = 1; } - return Math.acos(cos); - } + this.cleanNodesCache(); - function determinant(v1, v2) { - return v1.x * v2.y - v1.y * v2.x; - } + // Store reference to this to the DOM element so that the view is accessible through the DOM tree. + this.$el.data('view', this); - function createCatmullRomCurves(points, sourceTangent, targetTangent, options) { - var tau = options.tau; - var coeff = options.coeff; - var distances = []; - var tangents = []; - var catmullRomCurves = []; - var n = points.length - 1; + this.startListening(); + }, - for (var i = 0; i < n; i++) { - distances[i] = points[i].distance(points[i + 1]); - } + onMount: function onMount() { + // To be overridden + }, - tangents[0] = sourceTangent; - tangents[n] = targetTangent; + startListening: function() { + this.listenTo(this.model, 'change', this.onAttributesChange); + }, - // The calculation of tangents of vertices - for (var i$1 = 1; i$1 < n; i$1++) { - var tpPrev = (void 0); - var tpNext = (void 0); - if (i$1 === 1) { - tpPrev = points[i$1 - 1].clone().offset(tangents[i$1 - 1].x, tangents[i$1 - 1].y); - } else { - tpPrev = points[i$1 - 1].clone(); - } - if (i$1 === n - 1) { - tpNext = points[i$1 + 1].clone().offset(tangents[i$1 + 1].x, tangents[i$1 + 1].y); - } else { - tpNext = points[i$1 + 1].clone(); - } - var v1 = tpPrev.difference(points[i$1]).normalize(); - var v2 = tpNext.difference(points[i$1]).normalize(); - var vAngle = angleBetweenVectors(v1, v2); + onAttributesChange: function(model, opt) { + var flag = model.getChangeFlag(this._presentationAttributes); + if (opt.updateHandled || !flag) { return; } + if (opt.dirty && this.hasFlag(flag, 'UPDATE')) { flag |= this.getFlag('RENDER'); } + // TODO: tool changes does not need to be sync + // Fix Segments tools + if (opt.tool) { opt.async = false; } + this.requestUpdate(flag, opt); + }, - var rot = (Math.PI - vAngle) / 2; - var t = (void 0); - var vectorDeterminant = determinant(v1, v2); - var pointsDeterminant = (void 0); - pointsDeterminant = determinant(points[i$1].difference(points[i$1 + 1]), points[i$1].difference(points[i$1 - 1])); - if (vectorDeterminant < 0) { - rot = -rot; - } - if ((vAngle < Math.PI / 2) && ((rot < 0 && pointsDeterminant < 0) || (rot > 0 && pointsDeterminant > 0))) { - rot = rot - Math.PI; + requestUpdate: function(flags, opt) { + var ref = this; + var paper = ref.paper; + if (paper && flags > 0) { + paper.requestViewUpdate(this, flags, this.UPDATE_PRIORITY, opt); } - t = v2.clone(); - rotateVector(t, rot); - - var t1 = t.clone(); - var t2 = t.clone(); - var scaleFactor1 = distances[i$1 - 1] * coeff; - var scaleFactor2 = distances[i$1] * coeff; - t1.scale(scaleFactor1, scaleFactor1); - t2.scale(scaleFactor2, scaleFactor2); + }, - tangents[i$1] = [t1, t2]; - } + parseDOMJSON: function(markup, root) { - // The building of a Catmull-Rom curve based of tangents of points - for (var i$2 = 0; i$2 < n; i$2++) { - var p0 = (void 0); - var p3 = (void 0); - if (i$2 === 0) { - p0 = points[i$2 + 1].difference(tangents[i$2].x / tau, tangents[i$2].y / tau); - } else { - p0 = points[i$2 + 1].difference(tangents[i$2][1].x / tau, tangents[i$2][1].y / tau); + var doc = parseDOMJSON(markup); + var selectors = doc.selectors; + var groups = doc.groupSelectors; + for (var group in groups) { + if (selectors[group]) { throw new Error('dia.CellView: ambiguous group selector'); } + selectors[group] = groups[group]; } - if (i$2 === n - 1) { - p3 = points[i$2].clone().offset(tangents[i$2 + 1].x / tau, tangents[i$2 + 1].y / tau); - } else { - p3 = points[i$2].difference(tangents[i$2 + 1][0].x / tau, tangents[i$2 + 1][0].y / tau); + if (root) { + var rootSelector = this.selector; + if (selectors[rootSelector]) { throw new Error('dia.CellView: ambiguous root selector.'); } + selectors[rootSelector] = root; } + return { fragment: doc.fragment, selectors: selectors }; + }, - catmullRomCurves[i$2] = [p0, points[i$2], points[i$2 + 1], p3]; - } - return catmullRomCurves; - } + // Return `true` if cell link is allowed to perform a certain UI `feature`. + // Example: `can('vertexMove')`, `can('labelMove')`. + can: function(feature) { - // The function to convert Catmull-Rom curve to Bezier curve using the tension (tau) - function catmullRomToBezier(points, options) { - var tau = options.tau; + var interactive = isFunction(this.options.interactive) + ? this.options.interactive(this) + : this.options.interactive; - var bcp1 = new Point(); - bcp1.x = points[1].x + (points[2].x - points[0].x) / (6 * tau); - bcp1.y = points[1].y + (points[2].y - points[0].y) / (6 * tau); + return (isObject$1(interactive) && interactive[feature] !== false) || + (isBoolean(interactive) && interactive !== false); + }, - var bcp2 = new Point(); - bcp2.x = points[2].x + (points[3].x - points[1].x) / (6 * tau); - bcp2.y = points[2].y + (points[3].y - points[1].y) / (6 * tau); - return new Curve( - points[1], - bcp1, - bcp2, - points[2] - ); - } + findBySelector: function(selector, root, selectors) { + root || (root = this.el); + selectors || (selectors = this.selectors); + // These are either descendants of `this.$el` of `this.$el` itself. + // `.` is a special selector used to select the wrapping `` element. + if (!selector || selector === '.') { return [root]; } + if (selectors) { + var nodes = selectors[selector]; + if (nodes) { + if (Array.isArray(nodes)) { return nodes; } + return [nodes]; + } + } - var connectors = ({ - jumpover: jumpover, - normal: normal, - rounded: rounded, - smooth: smooth, - curve: curve - }); - - var views = {}; + // Maintaining backwards compatibility + // e.g. `circle:first` would fail with querySelector() call + if (config.useCSSSelectors) { return $(root).find(selector).toArray(); } - var View = Backbone.View.extend({ + return []; + }, - options: {}, - theme: null, - themeClassNamePrefix: addClassNamePrefix('theme-'), - requireSetThemeOverride: false, - defaultTheme: config.defaultTheme, - children: null, - childNodes: null, + notify: function(eventName) { - DETACHABLE: true, - UPDATE_PRIORITY: 2, - FLAG_INSERT: 1<<30, - FLAG_REMOVE: 1<<29, + if (this.paper) { - constructor: function(options) { + var args = Array.prototype.slice.call(arguments, 1); - this.requireSetThemeOverride = options && !!options.theme; - this.options = assign({}, this.options, options); + // Trigger the event on both the element itself and also on the paper. + this.trigger.apply(this, [eventName].concat(args)); - Backbone.View.call(this, options); + // Paper event handlers receive the view object as the first argument. + this.paper.trigger.apply(this.paper, [eventName, this].concat(args)); + } }, - initialize: function() { - - views[this.cid] = this; - - this.setTheme(this.options.theme || this.defaultTheme); - this.init(); - }, + getBBox: function(opt) { - unmount: function() { - if (this.svgElement) { - this.vel.remove(); + var bbox; + if (opt && opt.useModelGeometry) { + var model = this.model; + bbox = model.getBBox().bbox(model.angle()); } else { - this.$el.remove(); + bbox = this.getNodeBBox(this.el); } - }, - renderChildren: function(children) { - children || (children = result(this, 'children')); - if (children) { - var isSVG = this.svgElement; - var namespace = V.namespace[isSVG ? 'svg' : 'xhtml']; - var doc = parseDOMJSON(children, namespace); - (isSVG ? this.vel : this.$el).empty().append(doc.fragment); - this.childNodes = doc.selectors; - } - return this; + return this.paper.localToPaperRect(bbox); }, - findAttribute: function(attributeName, node) { + getNodeBBox: function(magnet) { - var currentNode = node; + var rect = this.getNodeBoundingRect(magnet); + var magnetMatrix = this.getNodeMatrix(magnet); + var translateMatrix = this.getRootTranslateMatrix(); + var rotateMatrix = this.getRootRotateMatrix(); + return V.transformRect(rect, translateMatrix.multiply(rotateMatrix).multiply(magnetMatrix)); + }, - while (currentNode && currentNode.nodeType === 1) { - var attributeValue = currentNode.getAttribute(attributeName); - // attribute found - if (attributeValue) { return attributeValue; } - // do not climb up the DOM - if (currentNode === this.el) { return null; } - // try parent node - currentNode = currentNode.parentNode; - } + getNodeUnrotatedBBox: function(magnet) { - return null; + var rect = this.getNodeBoundingRect(magnet); + var magnetMatrix = this.getNodeMatrix(magnet); + var translateMatrix = this.getRootTranslateMatrix(); + return V.transformRect(rect, translateMatrix.multiply(magnetMatrix)); }, - // Override the Backbone `_ensureElement()` method in order to create an - // svg element (e.g., ``) node that wraps all the nodes of the Cell view. - // Expose class name setter as a separate method. - _ensureElement: function() { - if (!this.el) { - var tagName = result(this, 'tagName'); - var attrs = assign({}, result(this, 'attributes')); - var style = assign({}, result(this, 'style')); - if (this.id) { attrs.id = result(this, 'id'); } - this.setElement(this._createElement(tagName)); - this._setAttributes(attrs); - this._setStyle(style); - } else { - this.setElement(result(this, 'el')); - } - this._ensureElClassName(); - }, + getRootTranslateMatrix: function() { - _setAttributes: function(attrs) { - if (this.svgElement) { - this.vel.attr(attrs); - } else { - this.$el.attr(attrs); - } + var model = this.model; + var position = model.position(); + var mt = V.createSVGMatrix().translate(position.x, position.y); + return mt; }, - _setStyle: function(style) { - this.$el.css(style); - }, + getRootRotateMatrix: function() { - _createElement: function(tagName) { - if (this.svgElement) { - return document.createElementNS(V.namespace.svg, tagName); - } else { - return document.createElement(tagName); + var mr = V.createSVGMatrix(); + var model = this.model; + var angle = model.angle(); + if (angle) { + var bbox = model.getBBox(); + var cx = bbox.width / 2; + var cy = bbox.height / 2; + mr = mr.translate(cx, cy).rotate(angle).translate(-cx, -cy); } + return mr; }, - // Utilize an alternative DOM manipulation API by - // adding an element reference wrapped in Vectorizer. - _setElement: function(el) { - this.$el = el instanceof Backbone.$ ? el : Backbone.$(el); - this.el = this.$el[0]; - if (this.svgElement) { this.vel = V(this.el); } - }, + _notifyHighlight: function(eventName, el, opt) { + var assign, assign$1; - _ensureElClassName: function() { - var className = result(this, 'className'); - if (!className) { return; } - var prefixedClassName = addClassNamePrefix(className); - // Note: className removal here kept for backwards compatibility only - if (this.svgElement) { - this.vel.removeClass(className).addClass(prefixedClassName); + if ( opt === void 0 ) opt = {}; + var ref = this; + var rootNode = ref.el; + var node; + if (typeof el === 'string') { + (assign = this.findBySelector(el), node = assign[0], node = node === void 0 ? rootNode : node); } else { - this.$el.removeClass(className).addClass(prefixedClassName); + (assign$1 = this.$(el), node = assign$1[0], node = node === void 0 ? rootNode : node); + } + // set partial flag if the highlighted element is not the entire view. + opt.partial = (node !== rootNode); + // translate type flag into a type string + if (opt.type === undefined) { + var type; + switch (true) { + case opt.embedding: + type = HighlightingTypes.EMBEDDING; + break; + case opt.connecting: + type = HighlightingTypes.CONNECTING; + break; + case opt.magnetAvailability: + type = HighlightingTypes.MAGNET_AVAILABILITY; + break; + case opt.elementAvailability: + type = HighlightingTypes.ELEMENT_AVAILABILITY; + break; + default: + type = HighlightingTypes.DEFAULT; + break; + } + opt.type = type; } + this.notify(eventName, node, opt); + return this; }, - init: function() { - // Intentionally empty. - // This method is meant to be overridden. + highlight: function(el, opt) { + return this._notifyHighlight('cell:highlight', el, opt); }, - onRender: function() { - // Intentionally empty. - // This method is meant to be overridden. - }, + unhighlight: function(el, opt) { + if ( opt === void 0 ) opt = {}; - confirmUpdate: function() { - // Intentionally empty. - // This method is meant to be overridden. - return 0; + return this._notifyHighlight('cell:unhighlight', el, opt); }, - setTheme: function(theme, opt) { - - opt = opt || {}; + // Find the closest element that has the `magnet` attribute set to `true`. If there was not such + // an element found, return the root element of the cell view. + findMagnet: function(el) { - // Theme is already set, override is required, and override has not been set. - // Don't set the theme. - if (this.theme && this.requireSetThemeOverride && !opt.override) { - return this; + var root = this.el; + var magnet = this.$(el)[0]; + if (!magnet) { + magnet = root; } - this.removeThemeClassName(); - this.addThemeClassName(theme); - this.onSetTheme(this.theme/* oldTheme */, theme/* newTheme */); - this.theme = theme; + do { + var magnetAttribute = magnet.getAttribute('magnet'); + var isMagnetRoot = (magnet === root); + if ((magnetAttribute || isMagnetRoot) && magnetAttribute !== 'false') { + return magnet; + } + if (isMagnetRoot) { + // If the overall cell has set `magnet === false`, then return `undefined` to + // announce there is no magnet found for this cell. + // This is especially useful to set on cells that have 'ports'. In this case, + // only the ports have set `magnet === true` and the overall element has `magnet === false`. + return undefined; + } + magnet = magnet.parentNode; + } while (magnet); - return this; + return undefined; }, - addThemeClassName: function(theme) { + findProxyNode: function(el, type) { + el || (el = this.el); + var nodeSelector = el.getAttribute((type + "-selector")); + if (nodeSelector) { + var ref = this.findBySelector(nodeSelector); + var proxyNode = ref[0]; + if (proxyNode) { return proxyNode; } + } + return el; + }, - theme = theme || this.theme; - if (!theme) { return this; } + // Construct a unique selector for the `el` element within this view. + // `prevSelector` is being collected through the recursive call. + // No value for `prevSelector` is expected when using this method. + getSelector: function(el, prevSelector) { - var className = this.themeClassNamePrefix + theme; + var selector; - if (this.svgElement) { - this.vel.addClass(className); - } else { - this.$el.addClass(className); + if (el === this.el) { + if (typeof prevSelector === 'string') { selector = '> ' + prevSelector; } + return selector; } - return this; - }, - - removeThemeClassName: function(theme) { + if (el) { - theme = theme || this.theme; + var nthChild = V(el).index() + 1; + selector = el.tagName + ':nth-child(' + nthChild + ')'; - var className = this.themeClassNamePrefix + theme; + if (prevSelector) { + selector += ' > ' + prevSelector; + } - if (this.svgElement) { - this.vel.removeClass(className); - } else { - this.$el.removeClass(className); + selector = this.getSelector(el.parentNode, selector); } - return this; + return selector; }, - onSetTheme: function(oldTheme, newTheme) { - // Intentionally empty. - // This method is meant to be overridden. - }, + addLinkFromMagnet: function(magnet, x, y) { - remove: function() { + var paper = this.paper; + var graph = paper.model; - this.onRemove(); - this.undelegateDocumentEvents(); + var link = paper.getDefaultLink(this, magnet); + link.set({ + source: this.getLinkEnd(magnet, x, y, link, 'source'), + target: { x: x, y: y } + }).addTo(graph, { + async: false, + ui: true + }); - views[this.cid] = null; + return link.findView(paper); + }, - Backbone.View.prototype.remove.apply(this, arguments); + getLinkEnd: function(magnet) { + var ref; - return this; - }, + var args = [], len = arguments.length - 1; + while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; - onRemove: function() { - // Intentionally empty. - // This method is meant to be overridden. - }, + var model = this.model; + var id = model.id; + var port = this.findAttribute('port', magnet); + // Find a unique `selector` of the element under pointer that is a magnet. + var selector = magnet.getAttribute('joint-selector'); - getEventNamespace: function() { - // Returns a per-session unique namespace - return '.joint-event-ns-' + this.cid; + var end = { id: id }; + if (selector != null) { end.magnet = selector; } + if (port != null) { + end.port = port; + if (!model.hasPort(port) && !selector) { + // port created via the `port` attribute (not API) + end.selector = this.getSelector(magnet); + } + } else if (selector == null && this.el !== magnet) { + end.selector = this.getSelector(magnet); + } + + return (ref = this).customizeLinkEnd.apply(ref, [ end, magnet ].concat( args )); }, - delegateElementEvents: function(element, events, data) { - if (!events) { return this; } - data || (data = {}); - var eventNS = this.getEventNamespace(); - for (var eventName in events) { - var method = events[eventName]; - if (typeof method !== 'function') { method = this[method]; } - if (!method) { continue; } - $(element).on(eventName + eventNS, data, method.bind(this)); + customizeLinkEnd: function(end, magnet, x, y, link, endType) { + var ref = this; + var paper = ref.paper; + var ref$1 = paper.options; + var connectionStrategy = ref$1.connectionStrategy; + if (typeof connectionStrategy === 'function') { + var strategy = connectionStrategy.call(paper, end, this, magnet, new Point(x, y), link, endType, paper); + if (strategy) { return strategy; } } - return this; + return end; }, - undelegateElementEvents: function(element) { - $(element).off(this.getEventNamespace()); - return this; - }, + getMagnetFromLinkEnd: function(end) { - delegateDocumentEvents: function(events, data) { - events || (events = result(this, 'documentEvents')); - return this.delegateElementEvents(document, events, data); + var root = this.el; + var port = end.port; + var selector = end.magnet; + var model = this.model; + var magnet; + if (port != null && model.isElement() && model.hasPort(port)) { + magnet = this.findPortNode(port, selector) || root; + } else { + if (!selector) { selector = end.selector; } + if (!selector && port != null) { + // link end has only `id` and `port` property referencing + // a port created via the `port` attribute (not API). + selector = '[port="' + port + '"]'; + } + magnet = this.findBySelector(selector, root, this.selectors)[0]; + } + + return this.findProxyNode(magnet, 'magnet'); }, - undelegateDocumentEvents: function() { - return this.undelegateElementEvents(document); + dragLinkStart: function(evt, magnet, x, y) { + this.model.startBatch('add-link'); + var linkView = this.addLinkFromMagnet(magnet, x, y); + // backwards compatibility events + linkView.notifyPointerdown(evt, x, y); + linkView.eventData(evt, linkView.startArrowheadMove('target', { whenNotAllowed: 'remove' })); + this.eventData(evt, { linkView: linkView }); }, - eventData: function(evt, data) { - if (!evt) { throw new Error('eventData(): event object required.'); } - var currentData = evt.data; - var key = '__' + this.cid + '__'; - if (data === undefined) { - if (!currentData) { return {}; } - return currentData[key] || {}; + dragLink: function(evt, x, y) { + var data = this.eventData(evt); + var linkView = data.linkView; + if (linkView) { + linkView.pointermove(evt, x, y); + } else { + var paper = this.paper; + var magnetThreshold = paper.options.magnetThreshold; + var currentTarget = this.getEventTarget(evt); + var targetMagnet = data.targetMagnet; + if (magnetThreshold === 'onleave') { + // magnetThreshold when the pointer leaves the magnet + if (targetMagnet === currentTarget || V(targetMagnet).contains(currentTarget)) { return; } + } else { + // magnetThreshold defined as a number of movements + if (paper.eventData(evt).mousemoved <= magnetThreshold) { return; } + } + this.dragLinkStart(evt, targetMagnet, x, y); } - currentData || (currentData = evt.data = {}); - currentData[key] || (currentData[key] = {}); - assign(currentData[key], data); - return this; }, - stopPropagation: function(evt) { - this.eventData(evt, { propagationStopped: true }); - return this; + dragLinkEnd: function(evt, x, y) { + var data = this.eventData(evt); + var linkView = data.linkView; + if (!linkView) { return; } + linkView.pointerup(evt, x, y); + this.model.stopBatch('add-link'); }, - isPropagationStopped: function(evt) { - return !!this.eventData(evt).propagationStopped; - } - - }, { - - extend: function() { + getAttributeDefinition: function(attrName) { - var args = Array.from(arguments); + return this.model.constructor.getAttributeDefinition(attrName); + }, - // Deep clone the prototype and static properties objects. - // This prevents unexpected behavior where some properties are overwritten outside of this function. - var protoProps = args[0] && assign({}, args[0]) || {}; - var staticProps = args[1] && assign({}, args[1]) || {}; + setNodeAttributes: function(node, attrs) { - // Need the real render method so that we can wrap it and call it later. - var renderFn = protoProps.render || (this.prototype && this.prototype.render) || null; + if (!isEmpty(attrs)) { + if (node instanceof SVGElement) { + V(node).attr(attrs); + } else { + $(node).attr(attrs); + } + } + }, - /* - Wrap the real render method so that: - .. `onRender` is always called. - .. `this` is always returned. - */ - protoProps.render = function() { + processNodeAttributes: function(node, attrs) { - if (typeof renderFn === 'function') { - // Call the original render method. - renderFn.apply(this, arguments); + var attrName, attrVal, def, i, n; + var normalAttrs, setAttrs, positionAttrs, offsetAttrs; + var relatives = []; + // divide the attributes between normal and special + for (attrName in attrs) { + if (!attrs.hasOwnProperty(attrName)) { continue; } + attrVal = attrs[attrName]; + def = this.getAttributeDefinition(attrName); + if (def && (!isFunction(def.qualify) || def.qualify.call(this, attrVal, node, attrs))) { + if (isString(def.set)) { + normalAttrs || (normalAttrs = {}); + normalAttrs[def.set] = attrVal; + } + if (attrVal !== null) { + relatives.push(attrName, def); + } + } else { + normalAttrs || (normalAttrs = {}); + normalAttrs[toKebabCase(attrName)] = attrVal; } + } - if (this.render.__render__ === renderFn) { - // Should always call onRender() method. - // Should call it only once when renderFn is actual prototype method i.e. not the wrapper - this.onRender(); + // handle the rest of attributes via related method + // from the special attributes namespace. + for (i = 0, n = relatives.length; i < n; i+=2) { + attrName = relatives[i]; + def = relatives[i+1]; + attrVal = attrs[attrName]; + if (isFunction(def.set)) { + setAttrs || (setAttrs = {}); + setAttrs[attrName] = attrVal; } + if (isFunction(def.position)) { + positionAttrs || (positionAttrs = {}); + positionAttrs[attrName] = attrVal; + } + if (isFunction(def.offset)) { + offsetAttrs || (offsetAttrs = {}); + offsetAttrs[attrName] = attrVal; + } + } - // Should always return itself. - return this; - }; - - protoProps.render.__render__ = renderFn; - - return Backbone.View.extend.call(this, protoProps, staticProps); - } - }); - - var DoubleTapEventName = 'dbltap'; - if ($.event && !(DoubleTapEventName in $.event.special)) { - var maxDelay = config.doubleTapInterval; - var minDelay = 30; - $.event.special[DoubleTapEventName] = { - bindType: 'touchend', - delegateType: 'touchend', - handle: function(event) { - var ref; - - var args = [], len = arguments.length - 1; - while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; - var handleObj = event.handleObj; - var target = event.target; - var targetData = $.data(target); - var now = new Date().getTime(); - var delta = 'lastTouch' in targetData ? now - targetData.lastTouch : 0; - if (delta < maxDelay && delta > minDelay) { - targetData.lastTouch = null; - event.type = handleObj.origType; - // let jQuery handle the triggering of "dbltap" event handlers - (ref = handleObj.handler).call.apply(ref, [ this, event ].concat( args )); - } else { - targetData.lastTouch = now; - } - } - }; - } - - var index$1 = ({ - views: views, - View: View - }); - - function toArray$1(obj) { - if (!obj) { return []; } - if (Array.isArray(obj)) { return obj; } - return [obj]; - } + return { + raw: attrs, + normal: normalAttrs, + set: setAttrs, + position: positionAttrs, + offset: offsetAttrs + }; + }, - var HighlighterView = View.extend({ + updateRelativeAttributes: function(node, attrs, refBBox, opt) { - tagName: 'g', - svgElement: true, - className: 'highlight', + opt || (opt = {}); - HIGHLIGHT_FLAG: 1, - UPDATE_PRIORITY: 3, - DETACHABLE: false, - UPDATABLE: true, - MOUNTABLE: true, + var attrName, attrVal, def; + var rawAttrs = attrs.raw || {}; + var nodeAttrs = attrs.normal || {}; + var setAttrs = attrs.set; + var positionAttrs = attrs.position; + var offsetAttrs = attrs.offset; - cellView: null, - nodeSelector: null, - node: null, - updateRequested: false, - transformGroup: null, + for (attrName in setAttrs) { + attrVal = setAttrs[attrName]; + def = this.getAttributeDefinition(attrName); + // SET - set function should return attributes to be set on the node, + // which will affect the node dimensions based on the reference bounding + // box. e.g. `width`, `height`, `d`, `rx`, `ry`, `points + var setResult = def.set.call(this, attrVal, refBBox.clone(), node, rawAttrs); + if (isObject$1(setResult)) { + assign(nodeAttrs, setResult); + } else if (setResult !== undefined) { + nodeAttrs[attrName] = setResult; + } + } - requestUpdate: function requestUpdate(cellView, nodeSelector) { - var paper = cellView.paper; - this.cellView = cellView; - this.nodeSelector = nodeSelector; - if (paper) { - this.updateRequested = true; - paper.requestViewUpdate(this, this.HIGHLIGHT_FLAG, this.UPDATE_PRIORITY); + if (node instanceof HTMLElement) { + // TODO: setting the `transform` attribute on HTMLElements + // via `node.style.transform = 'matrix(...)';` would introduce + // a breaking change (e.g. basic.TextBlock). + this.setNodeAttributes(node, nodeAttrs); + return; } - }, - confirmUpdate: function confirmUpdate() { - // The cellView is now rendered/updated since it has a higher update priority. - this.updateRequested = false; - var ref = this; - var cellView = ref.cellView; - var nodeSelector = ref.nodeSelector; - this.update(cellView, nodeSelector); - this.mount(); - this.transform(); - return 0; - }, + // The final translation of the subelement. + var nodeTransform = nodeAttrs.transform; + var nodeMatrix = V.transformStringToMatrix(nodeTransform); + var nodePosition = Point(nodeMatrix.e, nodeMatrix.f); + if (nodeTransform) { + nodeAttrs = omit(nodeAttrs, 'transform'); + nodeMatrix.e = nodeMatrix.f = 0; + } - findNode: function findNode(cellView, nodeSelector) { - var assign, assign$1; + // Calculate node scale determined by the scalable group + // only if later needed. + var sx, sy, translation; + if (positionAttrs || offsetAttrs) { + var nodeScale = this.getNodeScale(node, opt.scalableNode); + sx = nodeScale.sx; + sy = nodeScale.sy; + } - if ( nodeSelector === void 0 ) nodeSelector = null; - var el; - if (typeof nodeSelector === 'string') { - (assign = cellView.findBySelector(nodeSelector), el = assign[0]); - } else if (isPlainObject(nodeSelector)) { - var isLink = cellView.model.isLink(); - var label = nodeSelector.label; if ( label === void 0 ) label = null; - var port = nodeSelector.port; - var selector = nodeSelector.selector; - if (isLink && label !== null) { - // Link Label Selector - el = cellView.findLabelNode(label, selector); - } else if (!isLink && port) { - // Element Port Selector - el = cellView.findPortNode(port, selector); - } else { - // Cell Selector - (assign$1 = cellView.findBySelector(selector), el = assign$1[0]); + var positioned = false; + for (attrName in positionAttrs) { + attrVal = positionAttrs[attrName]; + def = this.getAttributeDefinition(attrName); + // POSITION - position function should return a point from the + // reference bounding box. The default position of the node is x:0, y:0 of + // the reference bounding box or could be further specify by some + // SVG attributes e.g. `x`, `y` + translation = def.position.call(this, attrVal, refBBox.clone(), node, rawAttrs); + if (translation) { + nodePosition.offset(Point(translation).scale(sx, sy)); + positioned || (positioned = true); } - } else if (nodeSelector) { - el = V.toNode(nodeSelector); - if (!(el instanceof SVGElement)) { el = null; } } - return el ? el : null; - }, - mount: function mount() { - var ref = this; - var MOUNTABLE = ref.MOUNTABLE; - var cellView = ref.cellView; - var el = ref.el; - var options = ref.options; - var transformGroup = ref.transformGroup; - if (!MOUNTABLE || transformGroup) { return; } - var cellViewRoot = cellView.vel; - var paper = cellView.paper; - var layerName = options.layer; - if (layerName) { - var vGroup = this.transformGroup = V('g').addClass('highlight-transform').append(el); - paper.getLayerView(layerName).insertSortedNode(vGroup.node, options.z); - } else { - // TODO: prepend vs append - if (!el.parentNode || el.nextSibling) { - // Not appended yet or not the last child - cellViewRoot.append(el); + // The node bounding box could depend on the `size` set from the previous loop. + // Here we know, that all the size attributes have been already set. + this.setNodeAttributes(node, nodeAttrs); + + var offseted = false; + if (offsetAttrs) { + // Check if the node is visible + var nodeBoundingRect = this.getNodeBoundingRect(node); + if (nodeBoundingRect.width > 0 && nodeBoundingRect.height > 0) { + var nodeBBox = V.transformRect(nodeBoundingRect, nodeMatrix).scale(1 / sx, 1 / sy); + for (attrName in offsetAttrs) { + attrVal = offsetAttrs[attrName]; + def = this.getAttributeDefinition(attrName); + // OFFSET - offset function should return a point from the element + // bounding box. The default offset point is x:0, y:0 (origin) or could be further + // specify with some SVG attributes e.g. `text-anchor`, `cx`, `cy` + translation = def.offset.call(this, attrVal, nodeBBox, node, rawAttrs); + if (translation) { + nodePosition.offset(Point(translation).scale(sx, sy)); + offseted || (offseted = true); + } + } } } - }, - unmount: function unmount() { - var ref = this; - var MOUNTABLE = ref.MOUNTABLE; - var transformGroup = ref.transformGroup; - var vel = ref.vel; - if (!MOUNTABLE) { return; } - if (transformGroup) { - this.transformGroup = null; - transformGroup.remove(); - } else { - vel.remove(); + // Do not touch node's transform attribute if there is no transformation applied. + if (nodeTransform !== undefined || positioned || offseted) { + // Round the coordinates to 1 decimal point. + nodePosition.round(1); + nodeMatrix.e = nodePosition.x; + nodeMatrix.f = nodePosition.y; + node.setAttribute('transform', V.matrixToTransformString(nodeMatrix)); + // TODO: store nodeMatrix metrics? } }, - transform: function transform() { - var ref = this; - var transformGroup = ref.transformGroup; - var cellView = ref.cellView; - var updateRequested = ref.updateRequested; - if (!transformGroup || cellView.model.isLink() || updateRequested) { return; } - var translateMatrix = cellView.getRootTranslateMatrix(); - var rotateMatrix = cellView.getRootRotateMatrix(); - var transformMatrix = translateMatrix.multiply(rotateMatrix); - transformGroup.attr('transform', V.matrixToTransformString(transformMatrix)); - }, + getNodeScale: function(node, scalableNode) { - update: function update() { - var ref = this; - var prevNode = ref.node; - var cellView = ref.cellView; - var nodeSelector = ref.nodeSelector; - var updateRequested = ref.updateRequested; - var id = ref.id; - if (updateRequested) { return; } - var node = this.node = this.findNode(cellView, nodeSelector); - if (prevNode) { - this.unhighlight(cellView, prevNode); - } - if (node) { - this.highlight(cellView, node); - this.mount(); + // Check if the node is a descendant of the scalable group. + var sx, sy; + if (scalableNode && scalableNode.contains(node)) { + var scale = scalableNode.scale(); + sx = 1 / scale.sx; + sy = 1 / scale.sy; } else { - this.unmount(); - cellView.notify('cell:highlight:invalid', id, this); + sx = 1; + sy = 1; } - }, - onRemove: function onRemove() { - var ref = this; - var node = ref.node; - var cellView = ref.cellView; - var id = ref.id; - var constructor = ref.constructor; - if (node) { - this.unhighlight(cellView, node); - } - this.unmount(); - constructor._removeRef(cellView, id); + return { sx: sx, sy: sy }; }, - highlight: function highlight(_cellView, _node) { - // to be overridden + cleanNodesCache: function() { + this.metrics = {}; }, - unhighlight: function unhighlight(_cellView, _node) { - // to be overridden - } + nodeCache: function(magnet) { - }, { + var metrics = this.metrics; + // Don't use cache? It most likely a custom view with overridden update. + if (!metrics) { return {}; } + var id = V.ensureId(magnet); + var value = metrics[id]; + if (!value) { value = metrics[id] = {}; } + return value; + }, - _views: {}, + getNodeData: function(magnet) { - // Used internally by CellView highlight() - highlight: function(cellView, node, opt) { - var id = this.uniqueId(node, opt); - this.add(cellView, node, id, opt); + var metrics = this.nodeCache(magnet); + if (!metrics.data) { metrics.data = {}; } + return metrics.data; }, - // Used internally by CellView unhighlight() - unhighlight: function(cellView, node, opt) { - var id = this.uniqueId(node, opt); - this.remove(cellView, id); + getNodeBoundingRect: function(magnet) { + + var metrics = this.nodeCache(magnet); + if (metrics.boundingRect === undefined) { metrics.boundingRect = V(magnet).getBBox(); } + return new Rect(metrics.boundingRect); }, - get: function get(cellView, id) { - if ( id === void 0 ) id = null; + getNodeMatrix: function(magnet) { - var cid = cellView.cid; - var ref$2 = this; - var _views = ref$2._views; - var refs = _views[cid]; - if (id === null) { - // all highlighters - var views = []; - if (!refs) { return views; } - for (var hid in refs) { - var ref = refs[hid]; - if (ref instanceof this) { - views.push(ref); - } - } - return views; - } else { - // single highlighter - if (!refs) { return null; } - if (id in refs) { - var ref$1 = refs[id]; - if (ref$1 instanceof this) { return ref$1; } - } - return null; + var metrics = this.nodeCache(magnet); + if (metrics.magnetMatrix === undefined) { + var target = this.rotatableNode || this.el; + metrics.magnetMatrix = V(magnet).getTransformToElement(target); } + return V.createSVGMatrix(metrics.magnetMatrix); }, - add: function add(cellView, nodeSelector, id, opt) { - if ( opt === void 0 ) opt = {}; + getNodeShape: function(magnet) { - if (!id) { throw new Error('dia.HighlighterView: An ID required.'); } - // Search the existing view amongst all the highlighters - var previousView = HighlighterView.get(cellView, id); - if (previousView) { previousView.remove(); } - var view = new this(opt); - view.id = id; - this._addRef(cellView, id, view); - view.requestUpdate(cellView, nodeSelector); - return view; + var metrics = this.nodeCache(magnet); + if (metrics.geometryShape === undefined) { metrics.geometryShape = V(magnet).toGeometryShape(); } + return metrics.geometryShape.clone(); }, - _addRef: function _addRef(cellView, id, view) { - var cid = cellView.cid; - var ref = this; - var _views = ref._views; - var refs = _views[cid]; - if (!refs) { refs = _views[cid] = {}; } - refs[id] = view; + isNodeConnection: function(node) { + return this.model.isLink() && (!node || node === this.el); }, - _removeRef: function _removeRef(cellView, id) { - var cid = cellView.cid; - var ref = this; - var _views = ref._views; - var refs = _views[cid]; - if (!refs) { return; } - if (id) { delete refs[id]; } - for (var _ in refs) { return; } - delete _views[cid]; - }, + findNodesAttributes: function(attrs, root, selectorCache, selectors) { - remove: function remove(cellView, id) { - if ( id === void 0 ) id = null; + var i, n, nodeAttrs, nodeId; + var nodesAttrs = {}; + var mergeIds = []; + for (var selector in attrs) { + if (!attrs.hasOwnProperty(selector)) { continue; } + nodeAttrs = attrs[selector]; + if (!isPlainObject(nodeAttrs)) { continue; } // Not a valid selector-attributes pair + var selected = selectorCache[selector] = this.findBySelector(selector, root, selectors); + for (i = 0, n = selected.length; i < n; i++) { + var node = selected[i]; + nodeId = V.ensureId(node); + // "unique" selectors are selectors that referencing a single node (defined by `selector`) + // groupSelector referencing a single node is not "unique" + var unique = (selectors && selectors[selector] === node); + var prevNodeAttrs = nodesAttrs[nodeId]; + if (prevNodeAttrs) { + // Note, that nodes referenced by deprecated `CSS selectors` are not taken into account. + // e.g. css:`.circle` and selector:`circle` can be applied in a random order + if (!prevNodeAttrs.array) { + mergeIds.push(nodeId); + prevNodeAttrs.array = true; + prevNodeAttrs.attributes = [prevNodeAttrs.attributes]; + prevNodeAttrs.selectedLength = [prevNodeAttrs.selectedLength]; + } + var attributes = prevNodeAttrs.attributes; + var selectedLength = prevNodeAttrs.selectedLength; + if (unique) { + // node referenced by `selector` + attributes.unshift(nodeAttrs); + selectedLength.unshift(-1); + } else { + // node referenced by `groupSelector` + var sortIndex = sortedIndex(selectedLength, n); + attributes.splice(sortIndex, 0, nodeAttrs); + selectedLength.splice(sortIndex, 0, n); + } + } else { + nodesAttrs[nodeId] = { + attributes: nodeAttrs, + selectedLength: unique ? -1 : n, + node: node, + array: false + }; + } + } + } - toArray$1(this.get(cellView, id)).forEach(function (view) { - view.remove(); - }); + for (i = 0, n = mergeIds.length; i < n; i++) { + nodeId = mergeIds[i]; + nodeAttrs = nodesAttrs[nodeId]; + nodeAttrs.attributes = merge.apply(void 0, [ {} ].concat( nodeAttrs.attributes.reverse() )); + } + + return nodesAttrs; }, - update: function update(cellView, id, dirty) { - if ( id === void 0 ) id = null; - if ( dirty === void 0 ) dirty = false; + getEventTarget: function(evt, opt) { + if ( opt === void 0 ) opt = {}; - toArray$1(this.get(cellView, id)).forEach(function (view) { - if (dirty || view.UPDATABLE) { view.update(); } - }); + // Touchmove/Touchend event's target is not reflecting the element under the coordinates as mousemove does. + // It holds the element when a touchstart triggered. + var target = evt.target; + var type = evt.type; + var clientX = evt.clientX; if ( clientX === void 0 ) clientX = 0; + var clientY = evt.clientY; if ( clientY === void 0 ) clientY = 0; + if (opt.fromPoint || type === 'touchmove' || type === 'touchend') { + return document.elementFromPoint(clientX, clientY); + } + + return target; }, - transform: function transform(cellView, id) { - if ( id === void 0 ) id = null; + // Default is to process the `model.attributes.attrs` object and set attributes on subelements based on the selectors, + // unless `attrs` parameter was passed. + updateDOMSubtreeAttributes: function(rootNode, attrs, opt) { - toArray$1(this.get(cellView, id)).forEach(function (view) { - if (view.UPDATABLE) { view.transform(); } - }); - }, + opt || (opt = {}); + opt.rootBBox || (opt.rootBBox = Rect()); + opt.selectors || (opt.selectors = this.selectors); // selector collection to use - uniqueId: function uniqueId(node, opt) { - if ( opt === void 0 ) opt = ''; + // Cache table for query results and bounding box calculation. + // Note that `selectorCache` needs to be invalidated for all + // `updateAttributes` calls, as the selectors might pointing + // to nodes designated by an attribute or elements dynamically + // created. + var selectorCache = {}; + var bboxCache = {}; + var relativeItems = []; + var relativeRefItems = []; + var item, node, nodeAttrs, nodeData, processedAttrs; - return V.ensureId(node) + JSON.stringify(opt); - } + var roAttrs = opt.roAttributes; + var nodesAttrs = this.findNodesAttributes(roAttrs || attrs, rootNode, selectorCache, opt.selectors); + // `nodesAttrs` are different from all attributes, when + // rendering only attributes sent to this method. + var nodesAllAttrs = (roAttrs) + ? this.findNodesAttributes(attrs, rootNode, selectorCache, opt.selectors) + : nodesAttrs; - }); + for (var nodeId in nodesAttrs) { + nodeData = nodesAttrs[nodeId]; + nodeAttrs = nodeData.attributes; + node = nodeData.node; + processedAttrs = this.processNodeAttributes(node, nodeAttrs); - var stroke = HighlighterView.extend({ + if (!processedAttrs.set && !processedAttrs.position && !processedAttrs.offset) { + // Set all the normal attributes right on the SVG/HTML element. + this.setNodeAttributes(node, processedAttrs.normal); - tagName: 'path', - className: 'highlight-stroke', - attributes: { - 'pointer-events': 'none', - 'vector-effect': 'non-scaling-stroke', - 'fill': 'none' - }, + } else { - options: { - padding: 3, - rx: 0, - ry: 0, - useFirstSubpath: false, - attrs: { - 'stroke-width': 3, - 'stroke': '#FEB663' - } - }, + var nodeAllAttrs = nodesAllAttrs[nodeId] && nodesAllAttrs[nodeId].attributes; + var refSelector = (nodeAllAttrs && (nodeAttrs.ref === undefined)) + ? nodeAllAttrs.ref + : nodeAttrs.ref; - getPathData: function getPathData(cellView, node) { - var ref = this; - var options = ref.options; - var useFirstSubpath = options.useFirstSubpath; - var d; - try { - var vNode = V(node); - d = vNode.convertToPathData().trim(); - if (vNode.tagName() === 'PATH' && useFirstSubpath) { - var secondSubpathIndex = d.search(/.M/i) + 1; - if (secondSubpathIndex > 0) { - d = d.substr(0, secondSubpathIndex); + var refNode; + if (refSelector) { + refNode = (selectorCache[refSelector] || this.findBySelector(refSelector, rootNode, opt.selectors))[0]; + if (!refNode) { + throw new Error('dia.CellView: "' + refSelector + '" reference does not exist.'); + } + } else { + refNode = null; + } + + item = { + node: node, + refNode: refNode, + processedAttributes: processedAttrs, + allAttributes: nodeAllAttrs + }; + + if (refNode) { + // If an element in the list is positioned relative to this one, then + // we want to insert this one before it in the list. + var itemIndex = relativeRefItems.findIndex(function(item) { + return item.refNode === node; + }); + + if (itemIndex > -1) { + relativeRefItems.splice(itemIndex, 0, item); + } else { + relativeRefItems.push(item); + } + } else { + // A node with no ref attribute. To be updated before the nodes referencing other nodes. + // The order of no-ref-items is not specified/important. + relativeItems.push(item); } } - } catch (error) { - // Failed to get path data from magnet element. - // Draw a rectangle around the node instead. - var nodeBBox = cellView.getNodeBoundingRect(node); - d = V.rectToPath(assign({}, options, nodeBBox.toJSON())); } - return d; - }, - highlightConnection: function highlightConnection(cellView) { - this.vel.attr('d', cellView.getSerializedConnection()); - }, + relativeItems.push.apply(relativeItems, relativeRefItems); - highlightNode: function highlightNode(cellView, node) { - var ref = this; - var vel = ref.vel; - var options = ref.options; - var padding = options.padding; - var layer = options.layer; - var highlightMatrix = cellView.getNodeMatrix(node); - // Add padding to the highlight element. - if (padding) { - if (!layer && node === cellView.el) { - // If the highlighter is appended to the cellView - // and we measure the size of the cellView wrapping group - // it's necessary to remove the highlighter first - vel.remove(); + var rotatableMatrix; + for (var i = 0, n = relativeItems.length; i < n; i++) { + item = relativeItems[i]; + node = item.node; + refNode = item.refNode; + + // Find the reference element bounding box. If no reference was provided, we + // use the optional bounding box. + var vRotatable = V(opt.rotatableNode); + var refNodeId = refNode ? V.ensureId(refNode) : ''; + var isRefNodeRotatable = !!vRotatable && !!refNode && vRotatable.contains(refNode); + var unrotatedRefBBox = bboxCache[refNodeId]; + if (!unrotatedRefBBox) { + // Get the bounding box of the reference element relative to the `rotatable` `` (without rotation) + // or to the root `` element if no rotatable group present if reference node present. + // Uses the bounding box provided. + var transformationTarget = (isRefNodeRotatable) ? vRotatable : rootNode; + unrotatedRefBBox = bboxCache[refNodeId] = (refNode) + ? V(refNode).getBBox({ target: transformationTarget }) + : opt.rootBBox; } - var nodeBBox = cellView.getNodeBoundingRect(node); - var cx = nodeBBox.x + (nodeBBox.width / 2); - var cy = nodeBBox.y + (nodeBBox.height / 2); - nodeBBox = V.transformRect(nodeBBox, highlightMatrix); - var width = Math.max(nodeBBox.width, 1); - var height = Math.max(nodeBBox.height, 1); - var sx = (width + padding) / width; - var sy = (height + padding) / height; - var paddingMatrix = V.createSVGMatrix({ - a: sx, - b: 0, - c: 0, - d: sy, - e: cx - sx * cx, - f: cy - sy * cy - }); - highlightMatrix = highlightMatrix.multiply(paddingMatrix); + + if (roAttrs) { + // if there was a special attribute affecting the position amongst passed-in attributes + // we have to merge it with the rest of the element's attributes as they are necessary + // to update the position relatively (i.e `ref-x` && 'ref-dx') + processedAttrs = this.processNodeAttributes(node, item.allAttributes); + this.mergeProcessedAttributes(processedAttrs, item.processedAttributes); + + } else { + processedAttrs = item.processedAttributes; + } + + var refBBox = unrotatedRefBBox; + if (isRefNodeRotatable && !vRotatable.contains(node)) { + // if the referenced node is inside the rotatable group while the updated node is outside, + // we need to take the rotatable node transformation into account + if (!rotatableMatrix) { rotatableMatrix = V.transformStringToMatrix(vRotatable.attr('transform')); } + refBBox = V.transformRect(unrotatedRefBBox, rotatableMatrix); + } + + this.updateRelativeAttributes(node, processedAttrs, refBBox, opt); } - vel.attr({ - 'd': this.getPathData(cellView, node), - 'transform': V.matrixToTransformString(highlightMatrix) - }); }, - highlight: function highlight(cellView, node) { - var ref = this; - var vel = ref.vel; - var options = ref.options; - vel.attr(options.attrs); - if (cellView.isNodeConnection(node)) { - this.highlightConnection(cellView); - } else { - this.highlightNode(cellView, node); - } - } + mergeProcessedAttributes: function(processedAttrs, roProcessedAttrs) { - }); + processedAttrs.set || (processedAttrs.set = {}); + processedAttrs.position || (processedAttrs.position = {}); + processedAttrs.offset || (processedAttrs.offset = {}); - var MASK_CLIP = 20; + assign(processedAttrs.set, roProcessedAttrs.set); + assign(processedAttrs.position, roProcessedAttrs.position); + assign(processedAttrs.offset, roProcessedAttrs.offset); - function forEachDescendant(vel, fn) { - var descendants = vel.children(); - while (descendants.length > 0) { - var descendant = descendants.shift(); - if (fn(descendant)) { - descendants.push.apply(descendants, descendant.children()); + // Handle also the special transform property. + var transform = processedAttrs.normal && processedAttrs.normal.transform; + if (transform !== undefined && roProcessedAttrs.normal) { + roProcessedAttrs.normal.transform = transform; } - } - } + processedAttrs.normal = roProcessedAttrs.normal; + }, - var mask = HighlighterView.extend({ + onRemove: function() { + this.removeTools(); + this.removeHighlighters(); + }, - tagName: 'rect', - className: 'highlight-mask', - attributes: { - 'pointer-events': 'none' + _toolsView: null, + + hasTools: function(name) { + var toolsView = this._toolsView; + if (!toolsView) { return false; } + if (!name) { return true; } + return (toolsView.getName() === name); }, - options: { - padding: 3, - maskClip: MASK_CLIP, - deep: false, - attrs: { - 'stroke': '#FEB663', - 'stroke-width': 3, - 'stroke-linecap': 'butt', - 'stroke-linejoin': 'miter', + addTools: function(toolsView) { + + this.removeTools(); + + if (toolsView) { + this._toolsView = toolsView; + toolsView.configure({ relatedView: this }); + toolsView.listenTo(this.paper, 'tools:event', this.onToolEvent.bind(this)); } + return this; }, - VISIBLE: 'white', - INVISIBLE: 'black', + updateTools: function(opt) { - MASK_ROOT_ATTRIBUTE_BLACKLIST: [ - 'marker-start', - 'marker-end', - 'marker-mid', - 'transform', - 'stroke-dasharray' - ], + var toolsView = this._toolsView; + if (toolsView) { toolsView.update(opt); } + return this; + }, - MASK_CHILD_ATTRIBUTE_BLACKLIST: [ - 'stroke', - 'fill', - 'stroke-width', - 'stroke-opacity', - 'stroke-dasharray', - 'fill-opacity', - 'marker-start', - 'marker-end', - 'marker-mid' - ], + removeTools: function() { - // TODO: change the list to a function callback - MASK_REPLACE_TAGS: [ - 'FOREIGNOBJECT', - 'IMAGE', - 'USE', - 'TEXT', - 'TSPAN', - 'TEXTPATH' - ], + var toolsView = this._toolsView; + if (toolsView) { + toolsView.remove(); + this._toolsView = null; + } + return this; + }, - // TODO: change the list to a function callback - MASK_REMOVE_TAGS: [ - 'TEXT', - 'TSPAN', - 'TEXTPATH' - ], + hideTools: function() { - transformMaskChild: function transformMaskChild(cellView, childEl) { - var ref = this; - var MASK_CHILD_ATTRIBUTE_BLACKLIST = ref.MASK_CHILD_ATTRIBUTE_BLACKLIST; - var MASK_REPLACE_TAGS = ref.MASK_REPLACE_TAGS; - var MASK_REMOVE_TAGS = ref.MASK_REMOVE_TAGS; - var childTagName = childEl.tagName(); - // Do not include the element in the mask's image - if (!V.isSVGGraphicsElement(childEl) || MASK_REMOVE_TAGS.includes(childTagName)) { - childEl.remove(); - return false; - } - // Replace the element with a rectangle - if (MASK_REPLACE_TAGS.includes(childTagName)) { - // Note: clone() method does not change the children ids - var originalChild = cellView.vel.findOne(("#" + (childEl.id))); - if (originalChild) { - var originalNode = originalChild.node; - var childBBox = cellView.getNodeBoundingRect(originalNode); - if (cellView.model.isElement()) { - childBBox = V.transformRect(childBBox, cellView.getNodeMatrix(originalNode)); - } - var replacement = V('rect', childBBox.toJSON()); - var ref$1 = childBBox.center(); - var ox = ref$1.x; - var oy = ref$1.y; - var ref$2 = originalChild.rotate(); - var angle = ref$2.angle; - var cx = ref$2.cx; if ( cx === void 0 ) cx = ox; - var cy = ref$2.cy; if ( cy === void 0 ) cy = oy; - if (angle) { replacement.rotate(angle, cx, cy); } - // Note: it's not important to keep the same sibling index since all subnodes are filled - childEl.parent().append(replacement); - } - childEl.remove(); - return false; + var toolsView = this._toolsView; + if (toolsView) { toolsView.hide(); } + return this; + }, + + showTools: function() { + + var toolsView = this._toolsView; + if (toolsView) { toolsView.show(); } + return this; + }, + + onToolEvent: function(event) { + switch (event) { + case 'remove': + this.removeTools(); + break; + case 'hide': + this.hideTools(); + break; + case 'show': + this.showTools(); + break; } - // Keep the element, but clean it from certain attributes - MASK_CHILD_ATTRIBUTE_BLACKLIST.forEach(function (attrName) { - if (attrName === 'fill' && childEl.attr('fill') === 'none') { return; } - childEl.removeAttr(attrName); - }); - return true; }, - transformMaskRoot: function transformMaskRoot(_cellView, rootEl) { - var ref = this; - var MASK_ROOT_ATTRIBUTE_BLACKLIST = ref.MASK_ROOT_ATTRIBUTE_BLACKLIST; - MASK_ROOT_ATTRIBUTE_BLACKLIST.forEach(function (attrName) { - rootEl.removeAttr(attrName); - }); + removeHighlighters: function() { + HighlighterView.remove(this); }, - getMaskShape: function getMaskShape(cellView, vel) { - var this$1 = this; + updateHighlighters: function(dirty) { + if ( dirty === void 0 ) dirty = false; - var ref = this; - var options = ref.options; - var MASK_REPLACE_TAGS = ref.MASK_REPLACE_TAGS; - var deep = options.deep; - var tagName = vel.tagName(); - var maskRoot; - if (tagName === 'G') { - if (!deep) { return null; } - maskRoot = vel.clone(); - forEachDescendant(maskRoot, function (maskChild) { return this$1.transformMaskChild(cellView, maskChild); }); - } else { - if (MASK_REPLACE_TAGS.includes(tagName)) { return null; } - maskRoot = vel.clone(); - } - this.transformMaskRoot(cellView, maskRoot); - return maskRoot; + HighlighterView.update(this, null, dirty); }, - getMaskId: function getMaskId() { - return ("highlight-mask-" + (this.cid)); + transformHighlighters: function() { + HighlighterView.transform(this); }, - getMask: function getMask(cellView, vNode) { + // Interaction. The controller part. + // --------------------------------- - var ref = this; - var VISIBLE = ref.VISIBLE; - var INVISIBLE = ref.INVISIBLE; - var options = ref.options; - var padding = options.padding; - var attrs = options.attrs; + // Interaction is handled by the paper and delegated to the view in interest. + // `x` & `y` parameters passed to these functions represent the coordinates already snapped to the paper grid. + // If necessary, real coordinates can be obtained from the `evt` event object. - var strokeWidth = ('stroke-width' in attrs) ? attrs['stroke-width'] : 1; - var hasNodeFill = vNode.attr('fill') !== 'none'; - var magnetStrokeWidth = parseFloat(vNode.attr('stroke-width')); - if (isNaN(magnetStrokeWidth)) { magnetStrokeWidth = 1; } - // stroke of the invisible shape - var minStrokeWidth = magnetStrokeWidth + padding * 2; - // stroke of the visible shape - var maxStrokeWidth = minStrokeWidth + strokeWidth * 2; - var maskEl = this.getMaskShape(cellView, vNode); - if (!maskEl) { - var nodeBBox = cellView.getNodeBoundingRect(vNode.node); - // Make sure the rect is visible - nodeBBox.inflate(nodeBBox.width ? 0 : 0.5, nodeBBox.height ? 0 : 0.5); - maskEl = V('rect', nodeBBox.toJSON()); - } - maskEl.attr(attrs); - return V('mask', { - 'id': this.getMaskId() - }).append([ - maskEl.clone().attr({ - 'fill': hasNodeFill ? VISIBLE : 'none', - 'stroke': VISIBLE, - 'stroke-width': maxStrokeWidth - }), - maskEl.clone().attr({ - 'fill': hasNodeFill ? INVISIBLE : 'none', - 'stroke': INVISIBLE, - 'stroke-width': minStrokeWidth - }) - ]); + // These functions are supposed to be overriden by the views that inherit from `joint.dia.Cell`, + // i.e. `joint.dia.Element` and `joint.dia.Link`. + + pointerdblclick: function(evt, x, y) { + + this.notify('cell:pointerdblclick', evt, x, y); }, - removeMask: function removeMask(paper) { - var maskNode = paper.svg.getElementById(this.getMaskId()); - if (maskNode) { - paper.defs.removeChild(maskNode); - } + pointerclick: function(evt, x, y) { + + this.notify('cell:pointerclick', evt, x, y); }, - addMask: function addMask(paper, maskEl) { - paper.defs.appendChild(maskEl.node); + contextmenu: function(evt, x, y) { + + this.notify('cell:contextmenu', evt, x, y); }, - highlight: function highlight(cellView, node) { + pointerdown: function(evt, x, y) { + var ref = this; - var options = ref.options; - var vel = ref.vel; - var padding = options.padding; - var attrs = options.attrs; - var maskClip = options.maskClip; if ( maskClip === void 0 ) maskClip = MASK_CLIP; - var layer = options.layer; - var color = ('stroke' in attrs) ? attrs['stroke'] : '#000000'; - if (!layer && node === cellView.el) { - // If the highlighter is appended to the cellView - // and we measure the size of the cellView wrapping group - // it's necessary to remove the highlighter first - vel.remove(); + var model = ref.model; + var graph = model.graph; + if (graph) { + model.startBatch('pointer'); + this.eventData(evt, { graph: graph }); } - var highlighterBBox = cellView.getNodeBoundingRect(node).inflate(padding + maskClip); - var maskEl = this.getMask(cellView, V(node)); - this.addMask(cellView.paper, maskEl); - vel.attr(highlighterBBox.toJSON()); - vel.attr({ - 'transform': V.matrixToTransformString(cellView.getNodeMatrix(node)), - 'mask': ("url(#" + (maskEl.id) + ")"), - 'fill': color - }); + + this.notify('cell:pointerdown', evt, x, y); }, - unhighlight: function unhighlight(cellView) { - this.removeMask(cellView.paper); - } + pointermove: function(evt, x, y) { - }); + this.notify('cell:pointermove', evt, x, y); + }, - var opacity = HighlighterView.extend({ + pointerup: function(evt, x, y) { - UPDATABLE: false, - MOUNTABLE: false, + var ref = this.eventData(evt); + var graph = ref.graph; - opacityClassName: addClassNamePrefix('highlight-opacity'), + this.notify('cell:pointerup', evt, x, y); - highlight: function(_cellView, node) { - V(node).addClass(this.opacityClassName); + if (graph) { + // we don't want to trigger event on model as model doesn't + // need to be member of collection anymore (remove) + graph.stopBatch('pointer', { cell: this.model }); + } }, - unhighlight: function(_cellView, node) { - V(node).removeClass(this.opacityClassName); - } + mouseover: function(evt) { - }); + this.notify('cell:mouseover', evt); + }, - var className = addClassNamePrefix('highlighted'); + mouseout: function(evt) { - var addClass = HighlighterView.extend({ + this.notify('cell:mouseout', evt); + }, - UPDATABLE: false, - MOUNTABLE: false, + mouseenter: function(evt) { - options: { - className: className + this.notify('cell:mouseenter', evt); }, - highlight: function(_cellView, node) { - V(node).addClass(this.options.className); + mouseleave: function(evt) { + + this.notify('cell:mouseleave', evt); }, - unhighlight: function(_cellView, node) { - V(node).removeClass(this.options.className); - } + mousewheel: function(evt, x, y, delta) { - }, { - // Backwards Compatibility - className: className - }); + this.notify('cell:mousewheel', evt, x, y, delta); + }, + onevent: function(evt, eventName, x, y) { + this.notify(eventName, evt, x, y); + }, - var highlighters = ({ - stroke: stroke, - mask: mask, - opacity: opacity, - addClass: addClass - }); + onmagnet: function() { - function offsetPoint(p1, p2, offset) { - if (isPlainObject(offset)) { - var x = offset.x; - var y = offset.y; - if (isFinite(y)) { - var line = new Line(p2, p1); - var ref = line.parallel(y); - var start = ref.start; - var end = ref.end; - p2 = start; - p1 = end; - } - offset = x; - } - if (!isFinite(offset)) { return p1; } - var length = p1.distance(p2); - if (offset === 0 && length > 0) { return p1; } - return p1.move(p2, -Math.min(offset, length - 1)); - } + // noop + }, - function stroke$1(magnet) { + magnetpointerdblclick: function() { - var stroke = magnet.getAttribute('stroke-width'); - if (stroke === null) { return 0; } - return parseFloat(stroke) || 0; - } + // noop + }, - function alignLine(line, type, offset) { - if ( offset === void 0 ) offset = 0; + magnetcontextmenu: function() { - var coordinate, a, b, direction; - var start = line.start; - var end = line.end; - switch (type) { - case 'left': - coordinate = 'x'; - a = end; - b = start; - direction = -1; - break; - case 'right': - coordinate = 'x'; - a = start; - b = end; - direction = 1; - break; - case 'top': - coordinate = 'y'; - a = end; - b = start; - direction = -1; - break; - case 'bottom': - coordinate = 'y'; - a = start; - b = end; - direction = 1; - break; - default: - return; - } - if (start[coordinate] < end[coordinate]) { - a[coordinate] = b[coordinate]; - } else { - b[coordinate] = a[coordinate]; - } - if (isFinite(offset)) { - a[coordinate] += direction * offset; - b[coordinate] += direction * offset; + // noop + }, + + checkMouseleave: function checkMouseleave(evt) { + var ref = this; + var paper = ref.paper; + if (paper.isAsync()) { + // Do the updates of the current view synchronously now + paper.dumpView(this); + } + var target = this.getEventTarget(evt, { fromPoint: true }); + var view = paper.findView(target); + if (view === this) { return; } + // Leaving the current view + this.mouseleave(evt); + if (!view) { return; } + // Entering another view + view.mouseenter(evt); + }, + + setInteractivity: function(value) { + + this.options.interactive = value; } - } + }, { - // Connection Points + Highlighting: HighlightingTypes, - function anchorConnectionPoint(line, _view, _magnet, opt) { - var offset = opt.offset; - var alignOffset = opt.alignOffset; - var align = opt.align; - if (align) { alignLine(line, align, alignOffset); } - return offsetPoint(line.end, line.start, offset); - } + addPresentationAttributes: function(presentationAttributes) { + return merge({}, result(this.prototype, 'presentationAttributes'), presentationAttributes, function(a, b) { + if (!a || !b) { return; } + if (typeof a === 'string') { a = [a]; } + if (typeof b === 'string') { b = [b]; } + if (Array.isArray(a) && Array.isArray(b)) { return uniq(a.concat(b)); } + }); + } + }); - function bboxIntersection(line, view, magnet, opt) { + var Flags = { + UPDATE: 'UPDATE', + TRANSLATE: 'TRANSLATE', + TOOLS: 'TOOLS', + RESIZE: 'RESIZE', + PORTS: 'PORTS', + ROTATE: 'ROTATE', + RENDER: 'RENDER' + }; - var bbox = view.getNodeBBox(magnet); - if (opt.stroke) { bbox.inflate(stroke$1(magnet) / 2); } - var intersections = line.intersect(bbox); - var cp = (intersections) - ? line.start.chooseClosest(intersections) - : line.end; - return offsetPoint(cp, line.start, opt.offset); - } + // Element base view and controller. + // ------------------------------------------- - function rectangleIntersection(line, view, magnet, opt) { + var ElementView = CellView.extend({ - var angle = view.model.angle(); - if (angle === 0) { - return bboxIntersection(line, view, magnet, opt); - } + /** + * @abstract + */ + _removePorts: function() { + // implemented in ports.js + }, - var bboxWORotation = view.getNodeUnrotatedBBox(magnet); - if (opt.stroke) { bboxWORotation.inflate(stroke$1(magnet) / 2); } - var center = bboxWORotation.center(); - var lineWORotation = line.clone().rotate(center, angle); - var intersections = lineWORotation.setLength(1e6).intersect(bboxWORotation); - var cp = (intersections) - ? lineWORotation.start.chooseClosest(intersections).rotate(center, -angle) - : line.end; - return offsetPoint(cp, line.start, opt.offset); - } + /** + * + * @abstract + */ + _renderPorts: function() { + // implemented in ports.js + }, - function findShapeNode(magnet) { - if (!magnet) { return null; } - var node = magnet; - do { - var tagName = node.tagName; - if (typeof tagName !== 'string') { return null; } - tagName = tagName.toUpperCase(); - if (tagName === 'G') { - node = node.firstElementChild; - } else if (tagName === 'TITLE') { - node = node.nextElementSibling; - } else { break; } - } while (node); - return node; - } + className: function() { - var BNDR_SUBDIVISIONS = 'segmentSubdivisons'; - var BNDR_SHAPE_BBOX = 'shapeBBox'; + var classNames = CellView.prototype.className.apply(this).split(' '); - function boundaryIntersection(line, view, magnet, opt) { + classNames.push('element'); - var node, intersection; - var selector = opt.selector; - var anchor = line.end; + return classNames.join(' '); + }, - if (typeof selector === 'string') { - node = view.findBySelector(selector)[0]; - } else if (Array.isArray(selector)) { - node = getByPath(magnet, selector); - } else { - node = findShapeNode(magnet); - } + initialize: function() { - if (!V.isSVGGraphicsElement(node)) { - if (node === magnet || !V.isSVGGraphicsElement(magnet)) { return anchor; } - node = magnet; - } + CellView.prototype.initialize.apply(this, arguments); - var localShape = view.getNodeShape(node); - var magnetMatrix = view.getNodeMatrix(node); - var translateMatrix = view.getRootTranslateMatrix(); - var rotateMatrix = view.getRootRotateMatrix(); - var targetMatrix = translateMatrix.multiply(rotateMatrix).multiply(magnetMatrix); - var localMatrix = targetMatrix.inverse(); - var localLine = V.transformLine(line, localMatrix); - var localRef = localLine.start.clone(); - var data = view.getNodeData(node); + this._initializePorts(); + }, - if (opt.insideout === false) { - if (!data[BNDR_SHAPE_BBOX]) { data[BNDR_SHAPE_BBOX] = localShape.bbox(); } - var localBBox = data[BNDR_SHAPE_BBOX]; - if (localBBox.containsPoint(localRef)) { return anchor; } - } + presentationAttributes: { + 'attrs': [Flags.UPDATE], + 'position': [Flags.TRANSLATE, Flags.TOOLS], + 'size': [Flags.RESIZE, Flags.PORTS, Flags.TOOLS], + 'angle': [Flags.ROTATE, Flags.TOOLS], + 'markup': [Flags.RENDER], + 'ports': [Flags.PORTS], + }, - // Caching segment subdivisions for paths - var pathOpt; - if (localShape instanceof Path) { - var precision = opt.precision || 2; - if (!data[BNDR_SUBDIVISIONS]) { data[BNDR_SUBDIVISIONS] = localShape.getSegmentSubdivisions({ precision: precision }); } - pathOpt = { - precision: precision, - segmentSubdivisions: data[BNDR_SUBDIVISIONS] - }; - } + initFlag: [Flags.RENDER], - if (opt.extrapolate === true) { localLine.setLength(1e6); } + UPDATE_PRIORITY: 0, - intersection = localLine.intersect(localShape, pathOpt); - if (intersection) { - // More than one intersection - if (V.isArray(intersection)) { intersection = localRef.chooseClosest(intersection); } - } else if (opt.sticky === true) { - // No intersection, find the closest point instead - if (localShape instanceof Rect) { - intersection = localShape.pointNearestToPoint(localRef); - } else if (localShape instanceof Ellipse) { - intersection = localShape.intersectionWithLineFromCenterToPoint(localRef); - } else { - intersection = localShape.closestPoint(localRef, pathOpt); + confirmUpdate: function(flag, opt) { + + var useCSSSelectors = config.useCSSSelectors; + if (this.hasFlag(flag, Flags.PORTS)) { + this._removePorts(); + this._cleanPortsCache(); } - } + var transformHighlighters = false; + if (this.hasFlag(flag, Flags.RENDER)) { + this.render(); + this.updateTools(opt); + this.updateHighlighters(true); + transformHighlighters = true; + flag = this.removeFlag(flag, [Flags.RENDER, Flags.UPDATE, Flags.RESIZE, Flags.TRANSLATE, Flags.ROTATE, Flags.PORTS, Flags.TOOLS]); + } else { + var updateHighlighters = false; - var cp = (intersection) ? V.transformPoint(intersection, targetMatrix) : anchor; - var cpOffset = opt.offset || 0; - if (opt.stroke) { cpOffset += stroke$1(node) / 2; } + // Skip this branch if render is required + if (this.hasFlag(flag, Flags.RESIZE)) { + this.resize(opt); + updateHighlighters = true; + // Resize method is calling `update()` internally + flag = this.removeFlag(flag, [Flags.RESIZE, Flags.UPDATE]); + } + if (this.hasFlag(flag, Flags.UPDATE)) { + this.update(this.model, null, opt); + flag = this.removeFlag(flag, Flags.UPDATE); + updateHighlighters = true; + if (useCSSSelectors) { + // `update()` will render ports when useCSSSelectors are enabled + flag = this.removeFlag(flag, Flags.PORTS); + } + } + if (this.hasFlag(flag, Flags.TRANSLATE)) { + this.translate(); + flag = this.removeFlag(flag, Flags.TRANSLATE); + transformHighlighters = true; + } + if (this.hasFlag(flag, Flags.ROTATE)) { + this.rotate(); + flag = this.removeFlag(flag, Flags.ROTATE); + transformHighlighters = true; + } + if (this.hasFlag(flag, Flags.PORTS)) { + this._renderPorts(); + updateHighlighters = true; + flag = this.removeFlag(flag, Flags.PORTS); + } - return offsetPoint(cp, line.start, cpOffset); - } + if (updateHighlighters) { + this.updateHighlighters(false); + } + } - var anchor = anchorConnectionPoint; - var bbox = bboxIntersection; - var rectangle = rectangleIntersection; - var boundary = boundaryIntersection; + if (transformHighlighters) { + this.transformHighlighters(); + } - var connectionPoints = ({ - anchor: anchor, - bbox: bbox, - rectangle: rectangle, - boundary: boundary - }); + if (this.hasFlag(flag, Flags.TOOLS)) { + this.updateTools(opt); + flag = this.removeFlag(flag, Flags.TOOLS); + } - function abs2rel(value, max) { + return flag; + }, - if (max === 0) { return '0%'; } - return Math.round(value / max * 100) + '%'; - } + /** + * @abstract + */ + _initializePorts: function() { - function pin(relative) { + }, - return function(end, view, magnet, coords) { - var fn = (view.isNodeConnection(magnet)) ? pinnedLinkEnd : pinnedElementEnd; - return fn(relative, end, view, magnet, coords); - }; - } + update: function(_, renderingOnlyAttrs) { - function pinnedElementEnd(relative, end, view, magnet, coords) { + this.cleanNodesCache(); - var angle = view.model.angle(); - var bbox = view.getNodeUnrotatedBBox(magnet); - var origin = view.model.getBBox().center(); - coords.rotate(origin, angle); - var dx = coords.x - bbox.x; - var dy = coords.y - bbox.y; + // When CSS selector strings are used, make sure no rule matches port nodes. + var useCSSSelectors = config.useCSSSelectors; + if (useCSSSelectors) { this._removePorts(); } - if (relative) { - dx = abs2rel(dx, bbox.width); - dy = abs2rel(dy, bbox.height); - } + var model = this.model; + var modelAttrs = model.attr(); + this.updateDOMSubtreeAttributes(this.el, modelAttrs, { + rootBBox: new Rect(model.size()), + selectors: this.selectors, + scalableNode: this.scalableNode, + rotatableNode: this.rotatableNode, + // Use rendering only attributes if they differs from the model attributes + roAttributes: (renderingOnlyAttrs === modelAttrs) ? null : renderingOnlyAttrs + }); - end.anchor = { - name: 'topLeft', - args: { - dx: dx, - dy: dy, - rotate: true + if (useCSSSelectors) { + this._renderPorts(); } - }; + }, - return end; - } + rotatableSelector: 'rotatable', + scalableSelector: 'scalable', + scalableNode: null, + rotatableNode: null, - function pinnedLinkEnd(relative, end, view, _magnet, coords) { + // `prototype.markup` is rendered by default. Set the `markup` attribute on the model if the + // default markup is not desirable. + renderMarkup: function() { - var connection = view.getConnection(); - if (!connection) { return end; } - var length = connection.closestPointLength(coords); - if (relative) { - var totalLength = connection.length(); - end.anchor = { - name: 'connectionRatio', - args: { - ratio: length / totalLength - } - }; - } else { - end.anchor = { - name: 'connectionLength', - args: { - length: length - } - }; - } - return end; - } + var element = this.model; + var markup = element.get('markup') || element.markup; + if (!markup) { throw new Error('dia.ElementView: markup required'); } + if (Array.isArray(markup)) { return this.renderJSONMarkup(markup); } + if (typeof markup === 'string') { return this.renderStringMarkup(markup); } + throw new Error('dia.ElementView: invalid markup'); + }, - var useDefaults = noop; - var pinAbsolute = pin(false); - var pinRelative = pin(true); + renderJSONMarkup: function(markup) { - var index$2 = ({ - useDefaults: useDefaults, - pinAbsolute: pinAbsolute, - pinRelative: pinRelative - }); + var doc = this.parseDOMJSON(markup, this.el); + var selectors = this.selectors = doc.selectors; + this.rotatableNode = V(selectors[this.rotatableSelector]) || null; + this.scalableNode = V(selectors[this.scalableSelector]) || null; + // Fragment + this.vel.append(doc.fragment); + }, - // Does not make any changes to vertices. - // Returns the arguments that are passed to it, unchanged. - var normal$1 = function(vertices, opt, linkView) { + renderStringMarkup: function(markup) { - return vertices; - }; + var vel = this.vel; + vel.append(V(markup)); + // Cache transformation groups + this.rotatableNode = vel.findOne('.rotatable'); + this.scalableNode = vel.findOne('.scalable'); - // Routes the link always to/from a certain side - // - // Arguments: - // padding ... gap between the element and the first vertex. :: Default 40. - // side ... 'left' | 'right' | 'top' | 'bottom' :: Default 'bottom'. - // - var oneSide = function(vertices, opt, linkView) { + var selectors = this.selectors = {}; + selectors[this.selector] = this.el; + }, - var side = opt.side || 'bottom'; - var padding = normalizeSides(opt.padding || 40); + render: function() { - // LinkView contains cached source an target bboxes. - // Note that those are Geometry rectangle objects. - var sourceBBox = linkView.sourceBBox; - var targetBBox = linkView.targetBBox; - var sourcePoint = sourceBBox.center(); - var targetPoint = targetBBox.center(); + this.vel.empty(); + this.renderMarkup(); + if (this.scalableNode) { + // Double update is necessary for elements with the scalable group only + // Note the resize() triggers the other `update`. + this.update(); + } + this.resize(); + if (this.rotatableNode) { + // Translate transformation is applied on `this.el` while the rotation transformation + // on `this.rotatableNode` + this.rotate(); + this.translate(); + } else { + this.updateTransformation(); + } + if (!config.useCSSSelectors) { this._renderPorts(); } + return this; + }, - var coordinate, dimension, direction; + resize: function(opt) { - switch (side) { - case 'bottom': - direction = 1; - coordinate = 'y'; - dimension = 'height'; - break; - case 'top': - direction = -1; - coordinate = 'y'; - dimension = 'height'; - break; - case 'left': - direction = -1; - coordinate = 'x'; - dimension = 'width'; - break; - case 'right': - direction = 1; - coordinate = 'x'; - dimension = 'width'; - break; - default: - throw new Error('Router: invalid side'); - } + if (this.scalableNode) { return this.sgResize(opt); } + if (this.model.attributes.angle) { this.rotate(); } + this.update(); + }, - // move the points from the center of the element to outside of it. - sourcePoint[coordinate] += direction * (sourceBBox[dimension] / 2 + padding[side]); - targetPoint[coordinate] += direction * (targetBBox[dimension] / 2 + padding[side]); + translate: function() { - // make link orthogonal (at least the first and last vertex). - if ((direction * (sourcePoint[coordinate] - targetPoint[coordinate])) > 0) { - targetPoint[coordinate] = sourcePoint[coordinate]; - } else { - sourcePoint[coordinate] = targetPoint[coordinate]; - } + if (this.rotatableNode) { return this.rgTranslate(); } + this.updateTransformation(); + }, - return [sourcePoint].concat(vertices, targetPoint); - }; + rotate: function() { - // bearing -> opposite bearing - var opposites = { - N: 'S', - S: 'N', - E: 'W', - W: 'E' - }; + if (this.rotatableNode) { + this.rgRotate(); + // It's necessary to call the update for the nodes outside + // the rotatable group referencing nodes inside the group + this.update(); + return; + } + this.updateTransformation(); + }, - // bearing -> radians - var radians = { - N: -Math.PI / 2 * 3, - S: -Math.PI / 2, - E: 0, - W: Math.PI - }; + updateTransformation: function() { - // HELPERS // + var transformation = this.getTranslateString(); + var rotateString = this.getRotateString(); + if (rotateString) { transformation += ' ' + rotateString; } + this.vel.attr('transform', transformation); + }, - // returns a point `p` where lines p,p1 and p,p2 are perpendicular and p is not contained - // in the given box - function freeJoin(p1, p2, bbox) { + getTranslateString: function() { - var p = new Point(p1.x, p2.y); - if (bbox.containsPoint(p)) { p = new Point(p2.x, p1.y); } - // kept for reference - // if (bbox.containsPoint(p)) p = null; + var position = this.model.attributes.position; + return 'translate(' + position.x + ',' + position.y + ')'; + }, - return p; - } + getRotateString: function() { + var attributes = this.model.attributes; + var angle = attributes.angle; + if (!angle) { return null; } + var size = attributes.size; + return 'rotate(' + angle + ',' + (size.width / 2) + ',' + (size.height / 2) + ')'; + }, - // returns either width or height of a bbox based on the given bearing - function getBBoxSize(bbox, bearing) { + // Rotatable & Scalable Group + // always slower, kept mainly for backwards compatibility - return bbox[(bearing === 'W' || bearing === 'E') ? 'width' : 'height']; - } + rgRotate: function() { - // simple bearing method (calculates only orthogonal cardinals) - function getBearing(from, to) { + this.rotatableNode.attr('transform', this.getRotateString()); + }, - if (from.x === to.x) { return (from.y > to.y) ? 'N' : 'S'; } - if (from.y === to.y) { return (from.x > to.x) ? 'W' : 'E'; } - return null; - } + rgTranslate: function() { - // transform point to a rect - function getPointBox(p) { + this.vel.attr('transform', this.getTranslateString()); + }, - return new Rect(p.x, p.y, 0, 0); - } + sgResize: function(opt) { - function getPaddingBox(opt) { + var model = this.model; + var angle = model.angle(); + var size = model.size(); + var scalable = this.scalableNode; - // if both provided, opt.padding wins over opt.elementPadding - var sides = normalizeSides(opt.padding || opt.elementPadding || 20); + // Getting scalable group's bbox. + // Due to a bug in webkit's native SVG .getBBox implementation, the bbox of groups with path children includes the paths' control points. + // To work around the issue, we need to check whether there are any path elements inside the scalable group. + var recursive = false; + if (scalable.node.getElementsByTagName('path').length > 0) { + // If scalable has at least one descendant that is a path, we need to switch to recursive bbox calculation. + // If there are no path descendants, group bbox calculation works and so we can use the (faster) native function directly. + recursive = true; + } + var scalableBBox = scalable.getBBox({ recursive: recursive }); - return { - x: -sides.left, - y: -sides.top, - width: sides.left + sides.right, - height: sides.top + sides.bottom - }; - } + // Make sure `scalableBbox.width` and `scalableBbox.height` are not zero which can happen if the element does not have any content. By making + // the width/height 1, we prevent HTML errors of the type `scale(Infinity, Infinity)`. + var sx = (size.width / (scalableBBox.width || 1)); + var sy = (size.height / (scalableBBox.height || 1)); + scalable.attr('transform', 'scale(' + sx + ',' + sy + ')'); - // return source bbox - function getSourceBBox(linkView, opt) { + // Now the interesting part. The goal is to be able to store the object geometry via just `x`, `y`, `angle`, `width` and `height` + // Order of transformations is significant but we want to reconstruct the object always in the order: + // resize(), rotate(), translate() no matter of how the object was transformed. For that to work, + // we must adjust the `x` and `y` coordinates of the object whenever we resize it (because the origin of the + // rotation changes). The new `x` and `y` coordinates are computed by canceling the previous rotation + // around the center of the resized object (which is a different origin then the origin of the previous rotation) + // and getting the top-left corner of the resulting object. Then we clean up the rotation back to what it originally was. - return linkView.sourceBBox.clone().moveAndExpand(getPaddingBox(opt)); - } + // Cancel the rotation but now around a different origin, which is the center of the scaled object. + var rotatable = this.rotatableNode; + var rotation = rotatable && rotatable.attr('transform'); + if (rotation) { - // return target bbox - function getTargetBBox(linkView, opt) { + rotatable.attr('transform', rotation + ' rotate(' + (-angle) + ',' + (size.width / 2) + ',' + (size.height / 2) + ')'); + var rotatableBBox = scalable.getBBox({ target: this.paper.cells }); - return linkView.targetBBox.clone().moveAndExpand(getPaddingBox(opt)); - } + // Store new x, y and perform rotate() again against the new rotation origin. + model.set('position', { x: rotatableBBox.x, y: rotatableBBox.y }, assign({ updateHandled: true }, opt)); + this.translate(); + this.rotate(); + } - // return source anchor - function getSourceAnchor(linkView, opt) { + // Update must always be called on non-rotated element. Otherwise, relative positioning + // would work with wrong (rotated) bounding boxes. + this.update(); + }, - if (linkView.sourceAnchor) { return linkView.sourceAnchor; } + // Embedding mode methods. + // ----------------------- - // fallback: center of bbox - var sourceBBox = getSourceBBox(linkView, opt); - return sourceBBox.center(); - } + prepareEmbedding: function(data) { + if ( data === void 0 ) data = {}; - // return target anchor - function getTargetAnchor(linkView, opt) { - if (linkView.targetAnchor) { return linkView.targetAnchor; } + var element = data.model || this.model; + var paper = data.paper || this.paper; + var graph = paper.model; - // fallback: center of bbox - var targetBBox = getTargetBBox(linkView, opt); - return targetBBox.center(); // default - } + var initialZIndices = data.initialZIndices = {}; + var embeddedCells = element.getEmbeddedCells({ deep: true }); + var connectedLinks = graph.getConnectedLinks(element, { deep: true, includeEnclosed: true }); - // PARTIAL ROUTERS // + // Note: an embedded cell can be a connect link, but it's fine + // to iterate over the cell twice. + [ + element ].concat( embeddedCells, + connectedLinks + ).forEach(function (cell) { return initialZIndices[cell.id] = cell.attributes.z; }); - function vertexVertex(from, to, bearing) { + element.startBatch('to-front'); - var p1 = new Point(from.x, to.y); - var p2 = new Point(to.x, from.y); - var d1 = getBearing(from, p1); - var d2 = getBearing(from, p2); - var opposite = opposites[bearing]; + // Bring the model to the front with all his embeds. + element.toFront({ deep: true, ui: true }); - var p = (d1 === bearing || (d1 !== opposite && (d2 === opposite || d2 !== bearing))) ? p1 : p2; + // Note that at this point cells in the collection are not sorted by z index (it's running in the batch, see + // the dia.Graph._sortOnChangeZ), so we can't assume that the last cell in the collection has the highest z. + var maxZ = graph.getElements().reduce(function (max, cell) { return Math.max(max, cell.attributes.z || 0); }, 0); - return { points: [p], direction: getBearing(p, to) }; - } + // Move to front also all the inbound and outbound links that are connected + // to any of the element descendant. If we bring to front only embedded elements, + // links connected to them would stay in the background. + connectedLinks.forEach(function (link) { + if (link.attributes.z <= maxZ) { + link.set('z', maxZ + 1, { ui: true }); + } + }); - function elementVertex(from, to, fromBBox) { + element.stopBatch('to-front'); - var p = freeJoin(from, to, fromBBox); + // Before we start looking for suitable parent we remove the current one. + var parentId = element.parent(); + if (parentId) { + var parent = graph.getCell(parentId); + parent.unembed(element, { ui: true }); + data.initialParentId = parentId; + } else { + data.initialParentId = null; + } + }, - return { points: [p], direction: getBearing(p, to) }; - } + processEmbedding: function(data, evt, x, y) { + if ( data === void 0 ) data = {}; - function vertexElement(from, to, toBBox, bearing) { - var route = {}; + var model = data.model || this.model; + var paper = data.paper || this.paper; + var graph = paper.model; + var ref = paper.options; + var findParentBy = ref.findParentBy; + var frontParentOnly = ref.frontParentOnly; + var validateEmbedding = ref.validateEmbedding; - var points = [new Point(from.x, to.y), new Point(to.x, from.y)]; - var freePoints = points.filter(function(pt) { - return !toBBox.containsPoint(pt); - }); - var freeBearingPoints = freePoints.filter(function(pt) { - return getBearing(pt, from) !== bearing; - }); + var candidates; + if (isFunction(findParentBy)) { + candidates = toArray(findParentBy.call(graph, this, evt, x, y)); + } else if (findParentBy === 'pointer') { + candidates = toArray(graph.findModelsFromPoint({ x: x, y: y })); + } else { + candidates = graph.findModelsUnderElement(model, { searchBy: findParentBy }); + } - var p; + candidates = candidates.filter(function (el) { + return (el instanceof Cell) && (model.id !== el.id) && !el.isEmbeddedIn(model); + }); - if (freeBearingPoints.length > 0) { - // Try to pick a point which bears the same direction as the previous segment. + if (frontParentOnly) { + // pick the element with the highest `z` index + candidates = candidates.slice(-1); + } - p = freeBearingPoints.filter(function(pt) { - return getBearing(from, pt) === bearing; - }).pop(); - p = p || freeBearingPoints[0]; + var newCandidateView = null; + var prevCandidateView = data.candidateEmbedView; - route.points = [p]; - route.direction = getBearing(p, to); + // iterate over all candidates starting from the last one (has the highest z-index). + for (var i = candidates.length - 1; i >= 0; i--) { + var candidate = candidates[i]; + if (prevCandidateView && prevCandidateView.model.id == candidate.id) { + // candidate remains the same + newCandidateView = prevCandidateView; + break; + } else { + var view = candidate.findView(paper); + if (!isFunction(validateEmbedding) || validateEmbedding.call(paper, this, view)) { + // flip to the new candidate + newCandidateView = view; + break; + } + } + } - } else { - // Here we found only points which are either contained in the element or they would create - // a link segment going in opposite direction from the previous one. - // We take the point inside element and move it outside the element in the direction the - // route is going. Now we can join this point with the current end (using freeJoin). + if (newCandidateView && newCandidateView != prevCandidateView) { + // A new candidate view found. Highlight the new one. + this.clearEmbedding(data); + data.candidateEmbedView = newCandidateView.highlight( + newCandidateView.findProxyNode(null, 'container'), + { embedding: true } + ); + } - p = difference(points, freePoints)[0]; + if (!newCandidateView && prevCandidateView) { + // No candidate view found. Unhighlight the previous candidate. + this.clearEmbedding(data); + } + }, - var p2 = (new Point(to)).move(p, -getBBoxSize(toBBox, bearing) / 2); - var p1 = freeJoin(p2, from, toBBox); + clearEmbedding: function(data) { - route.points = [p1, p2]; - route.direction = getBearing(p2, to); - } + data || (data = {}); - return route; - } + var candidateView = data.candidateEmbedView; + if (candidateView) { + // No candidate view found. Unhighlight the previous candidate. + candidateView.unhighlight( + candidateView.findProxyNode(null, 'container'), + { embedding: true } + ); + data.candidateEmbedView = null; + } + }, - function elementElement(from, to, fromBBox, toBBox) { + finalizeEmbedding: function(data) { + if ( data === void 0 ) data = {}; - var route = elementVertex(to, from, toBBox); - var p1 = route.points[0]; - if (fromBBox.containsPoint(p1)) { + var candidateView = data.candidateEmbedView; + var element = data.model || this.model; + var paper = data.paper || this.paper; - route = elementVertex(from, to, fromBBox); - var p2 = route.points[0]; + if (candidateView) { - if (toBBox.containsPoint(p2)) { + // We finished embedding. Candidate view is chosen to become the parent of the model. + candidateView.model.embed(element, { ui: true }); + candidateView.unhighlight(candidateView.findProxyNode(null, 'container'), { embedding: true }); - var fromBorder = (new Point(from)).move(p2, -getBBoxSize(fromBBox, getBearing(from, p2)) / 2); - var toBorder = (new Point(to)).move(p1, -getBBoxSize(toBBox, getBearing(to, p1)) / 2); - var mid = (new Line(fromBorder, toBorder)).midpoint(); + data.candidateEmbedView = null; - var startRoute = elementVertex(from, mid, fromBBox); - var endRoute = vertexVertex(mid, to, startRoute.direction); + } else { - route.points = [startRoute.points[0], endRoute.points[0]]; - route.direction = endRoute.direction; + var ref = paper.options; + var validateUnembedding = ref.validateUnembedding; + var initialParentId = data.initialParentId; + // The element was originally embedded into another element. + // The interaction would unembed the element. Let's validate + // if the element can be unembedded. + if ( + initialParentId && + typeof validateUnembedding === 'function' && + !validateUnembedding.call(paper, this) + ) { + this._disallowUnembed(data); + return; + } } - } - - return route; - } - // Finds route for situations where one element is inside the other. - // Typically the route is directed outside the outer element first and - // then back towards the inner element. - function insideElement(from, to, fromBBox, toBBox, bearing) { - - var route = {}; - var boundary = fromBBox.union(toBBox).inflate(1); - - // start from the point which is closer to the boundary - var reversed = boundary.center().distance(to) > boundary.center().distance(from); - var start = reversed ? to : from; - var end = reversed ? from : to; + paper.model.getConnectedLinks(element, { deep: true }).forEach(function (link) { + link.reparent({ ui: true }); + }); + }, - var p1, p2, p3; + _disallowUnembed: function(data) { + var model = data.model; + var whenNotAllowed = data.whenNotAllowed; if ( whenNotAllowed === void 0 ) whenNotAllowed = 'revert'; + var element = model || this.model; + var paper = data.paper || this.paper; + var graph = paper.model; + switch (whenNotAllowed) { + case 'remove': { + element.remove({ ui: true }); + break; + } + case 'revert': { + var initialParentId = data.initialParentId; + var initialPosition = data.initialPosition; + var initialZIndices = data.initialZIndices; + // Revert the element's position (and the position of its embedded cells if any) + if (initialPosition) { + var x = initialPosition.x; + var y = initialPosition.y; + element.position(x, y, { deep: true, ui: true }); + } + // Revert all the z-indices changed during the embedding + if (initialZIndices) { + Object.keys(initialZIndices).forEach(function (id) { + var cell = graph.getCell(id); + if (cell) { + cell.set('z', initialZIndices[id], { ui: true }); + } + }); + } + // Revert the original parent + var parent = graph.getCell(initialParentId); + if (parent) { + parent.embed(element, { ui: true }); + } + break; + } + } + }, - if (bearing) { - // Points on circle with radius equals 'W + H` are always outside the rectangle - // with width W and height H if the center of that circle is the center of that rectangle. - p1 = Point.fromPolar(boundary.width + boundary.height, radians[bearing], start); - p1 = boundary.pointNearestToPoint(p1).move(p1, -1); + getDelegatedView: function() { - } else { - p1 = boundary.pointNearestToPoint(start).move(start, 1); - } + var view = this; + var model = view.model; + var paper = view.paper; - p2 = freeJoin(p1, end, boundary); + while (view) { + if (model.isLink()) { break; } + if (!model.isEmbedded() || view.can('stopDelegation')) { return view; } + model = model.getParentCell(); + view = paper.findViewByModel(model); + } - if (p1.round().equals(p2.round())) { - p2 = Point.fromPolar(boundary.width + boundary.height, toRad(p1.theta(start)) + Math.PI / 2, end); - p2 = boundary.pointNearestToPoint(p2).move(end, 1).round(); - p3 = freeJoin(p1, p2, boundary); - route.points = reversed ? [p2, p3, p1] : [p1, p3, p2]; + return null; + }, - } else { - route.points = reversed ? [p2, p1] : [p1, p2]; - } + findProxyNode: function(el, type) { + el || (el = this.el); + var nodeSelector = el.getAttribute((type + "-selector")); + if (nodeSelector) { + var port = this.findAttribute('port', el); + if (port) { + var proxyPortNode = this.findPortNode(port, nodeSelector); + if (proxyPortNode) { return proxyPortNode; } + } else { + var ref = this.findBySelector(nodeSelector); + var proxyNode = ref[0]; + if (proxyNode) { return proxyNode; } + } + } + return el; + }, - route.direction = reversed ? getBearing(p1, to) : getBearing(p2, to); + // Interaction. The controller part. + // --------------------------------- - return route; - } + notifyPointerdown: function notifyPointerdown(evt, x, y) { + CellView.prototype.pointerdown.call(this, evt, x, y); + this.notify('element:pointerdown', evt, x, y); + }, - // MAIN ROUTER // + notifyPointermove: function notifyPointermove(evt, x, y) { + CellView.prototype.pointermove.call(this, evt, x, y); + this.notify('element:pointermove', evt, x, y); + }, - // Return points through which a connection needs to be drawn in order to obtain an orthogonal link - // routing from source to target going through `vertices`. - function orthogonal(vertices, opt, linkView) { + notifyPointerup: function notifyPointerup(evt, x, y) { + this.notify('element:pointerup', evt, x, y); + CellView.prototype.pointerup.call(this, evt, x, y); + }, - var sourceBBox = getSourceBBox(linkView, opt); - var targetBBox = getTargetBBox(linkView, opt); + pointerdblclick: function(evt, x, y) { - var sourceAnchor = getSourceAnchor(linkView, opt); - var targetAnchor = getTargetAnchor(linkView, opt); + CellView.prototype.pointerdblclick.apply(this, arguments); + this.notify('element:pointerdblclick', evt, x, y); + }, - // if anchor lies outside of bbox, the bbox expands to include it - sourceBBox = sourceBBox.union(getPointBox(sourceAnchor)); - targetBBox = targetBBox.union(getPointBox(targetAnchor)); + pointerclick: function(evt, x, y) { - vertices = toArray(vertices).map(Point); - vertices.unshift(sourceAnchor); - vertices.push(targetAnchor); + CellView.prototype.pointerclick.apply(this, arguments); + this.notify('element:pointerclick', evt, x, y); + }, - var bearing; // bearing of previous route segment + contextmenu: function(evt, x, y) { - var orthogonalVertices = []; // the array of found orthogonal vertices to be returned - for (var i = 0, max = vertices.length - 1; i < max; i++) { + CellView.prototype.contextmenu.apply(this, arguments); + this.notify('element:contextmenu', evt, x, y); + }, - var route = null; + pointerdown: function(evt, x, y) { - var from = vertices[i]; - var to = vertices[i + 1]; + if (this.isPropagationStopped(evt)) { return; } - var isOrthogonal = !!getBearing(from, to); + this.notifyPointerdown(evt, x, y); + this.dragStart(evt, x, y); + }, - if (i === 0) { // source + pointermove: function(evt, x, y) { - if (i + 1 === max) { // route source -> target + var data = this.eventData(evt); - // Expand one of the elements by 1px to detect situations when the two - // elements are positioned next to each other with no gap in between. - if (sourceBBox.intersect(targetBBox.clone().inflate(1))) { - route = insideElement(from, to, sourceBBox, targetBBox); - - } else if (!isOrthogonal) { - route = elementElement(from, to, sourceBBox, targetBBox); - } - - } else { // route source -> vertex - - if (sourceBBox.containsPoint(to)) { - route = insideElement(from, to, sourceBBox, getPointBox(to).moveAndExpand(getPaddingBox(opt))); - - } else if (!isOrthogonal) { - route = elementVertex(from, to, sourceBBox); - } - } - - } else if (i + 1 === max) { // route vertex -> target - - // prevent overlaps with previous line segment - var isOrthogonalLoop = isOrthogonal && getBearing(to, from) === bearing; - - if (targetBBox.containsPoint(from) || isOrthogonalLoop) { - route = insideElement(from, to, getPointBox(from).moveAndExpand(getPaddingBox(opt)), targetBBox, bearing); - - } else if (!isOrthogonal) { - route = vertexElement(from, to, targetBBox, bearing); - } - - } else if (!isOrthogonal) { // route vertex -> vertex - route = vertexVertex(from, to, bearing); + switch (data.action) { + case 'magnet': + this.dragMagnet(evt, x, y); + break; + case 'move': + (data.delegatedView || this).drag(evt, x, y); + // eslint: no-fallthrough=false + default: + this.notifyPointermove(evt, x, y); + break; } - // applicable to all routes: - - // set bearing for next iteration - if (route) { - Array.prototype.push.apply(orthogonalVertices, route.points); - bearing = route.direction; + // Make sure the element view data is passed along. + // It could have been wiped out in the handlers above. + this.eventData(evt, data); + }, - } else { - // orthogonal route and not looped - bearing = getBearing(from, to); - } + pointerup: function(evt, x, y) { - // push `to` point to identified orthogonal vertices array - if (i + 1 < max) { - orthogonalVertices.push(to); + var data = this.eventData(evt); + switch (data.action) { + case 'magnet': + this.dragMagnetEnd(evt, x, y); + break; + case 'move': + (data.delegatedView || this).dragEnd(evt, x, y); + // eslint: no-fallthrough=false + default: + this.notifyPointerup(evt, x, y); } - } - return orthogonalVertices; - } + var magnet = data.targetMagnet; + if (magnet) { this.magnetpointerclick(evt, magnet, x, y); } - var config$1 = { + this.checkMouseleave(evt); + }, - // size of the step to find a route (the grid of the manhattan pathfinder) - step: 10, + mouseover: function(evt) { - // the number of route finding loops that cause the router to abort - // returns fallback route instead - maximumLoops: 2000, + CellView.prototype.mouseover.apply(this, arguments); + this.notify('element:mouseover', evt); + }, - // the number of decimal places to round floating point coordinates - precision: 1, + mouseout: function(evt) { - // maximum change of direction - maxAllowedDirectionChange: 90, + CellView.prototype.mouseout.apply(this, arguments); + this.notify('element:mouseout', evt); + }, - // should the router use perpendicular linkView option? - // does not connect anchor of element but rather a point close-by that is orthogonal - // this looks much better - perpendicular: true, + mouseenter: function(evt) { - // should the source and/or target not be considered as obstacles? - excludeEnds: [], // 'source', 'target' + CellView.prototype.mouseenter.apply(this, arguments); + this.notify('element:mouseenter', evt); + }, - // should certain types of elements not be considered as obstacles? - excludeTypes: ['basic.Text'], + mouseleave: function(evt) { - // possible starting directions from an element - startDirections: ['top', 'right', 'bottom', 'left'], + CellView.prototype.mouseleave.apply(this, arguments); + this.notify('element:mouseleave', evt); + }, - // possible ending directions to an element - endDirections: ['top', 'right', 'bottom', 'left'], + mousewheel: function(evt, x, y, delta) { - // specify the directions used above and what they mean - directionMap: { - top: { x: 0, y: -1 }, - right: { x: 1, y: 0 }, - bottom: { x: 0, y: 1 }, - left: { x: -1, y: 0 } + CellView.prototype.mousewheel.apply(this, arguments); + this.notify('element:mousewheel', evt, x, y, delta); }, - // cost of an orthogonal step - cost: function() { + onmagnet: function(evt, x, y) { - return this.step; + this.dragMagnetStart(evt, x, y); }, - // an array of directions to find next points on the route - // different from start/end directions - directions: function() { - - var step = this.step; - var cost = this.cost(); + magnetpointerdblclick: function(evt, magnet, x, y) { - return [ - { offsetX: step, offsetY: 0, cost: cost }, - { offsetX: -step, offsetY: 0, cost: cost }, - { offsetX: 0, offsetY: step, cost: cost }, - { offsetX: 0, offsetY: -step, cost: cost } - ]; + this.notify('element:magnet:pointerdblclick', evt, magnet, x, y); }, - // a penalty received for direction change - penalties: function() { + magnetcontextmenu: function(evt, magnet, x, y) { - return { - 0: 0, - 45: this.step / 2, - 90: this.step / 2 - }; + this.notify('element:magnet:contextmenu', evt, magnet, x, y); }, - // padding applied on the element bounding boxes - paddingBox: function() { + // Drag Start Handlers - var step = this.step; + dragStart: function(evt, x, y) { - return { - x: -step, - y: -step, - width: 2 * step, - height: 2 * step - }; - }, + var view = this.getDelegatedView(); + if (!view || !view.can('elementMove')) { return; } - // A function that determines whether a given point is an obstacle or not. - // If used, the `padding`, `excludeEnds`and `excludeTypes` options are ignored. - // (point: dia.Point) => boolean; - isPointObstacle: null, + this.eventData(evt, { + action: 'move', + delegatedView: view + }); - // a router to use when the manhattan router fails - // (one of the partial routes returns null) - fallbackRouter: function(vertices, opt, linkView) { + var position = view.model.position(); + view.eventData(evt, { + initialPosition: position, + pointerOffset: position.difference(x, y), + restrictedArea: this.paper.getRestrictedArea(view, x, y) + }); + }, - if (!isFunction(orthogonal)) { - throw new Error('Manhattan requires the orthogonal router as default fallback.'); - } + dragMagnetStart: function(evt, x, y) { - return orthogonal(vertices, assign({}, config$1, opt), linkView); - }, + if (!this.can('addLinkFromMagnet')) { return; } - /* Deprecated */ - // a simple route used in situations when main routing method fails - // (exceed max number of loop iterations, inaccessible) - fallbackRoute: function(from, to, opt) { + var magnet = evt.currentTarget; + var paper = this.paper; + this.eventData(evt, { targetMagnet: magnet }); + evt.stopPropagation(); - return null; // null result will trigger the fallbackRouter + if (paper.options.validateMagnet(this, magnet, evt)) { - // left for reference: - /*// Find an orthogonal route ignoring obstacles. + if (paper.options.magnetThreshold <= 0) { + this.dragLinkStart(evt, magnet, x, y); + } - var point = ((opt.previousDirAngle || 0) % 180 === 0) - ? new g.Point(from.x, to.y) - : new g.Point(to.x, from.y); + this.eventData(evt, { action: 'magnet' }); + this.stopPropagation(evt); - return [point];*/ - }, + } else { - // if a function is provided, it's used to route the link while dragging an end - // i.e. function(from, to, opt) { return []; } - draggingRoute: null - }; + this.pointerdown(evt, x, y); + } - // HELPER CLASSES // + paper.delegateDragEvents(this, evt.data); + }, - // Map of obstacles - // Helper structure to identify whether a point lies inside an obstacle. - function ObstacleMap(opt) { + // Drag Handlers - this.map = {}; - this.options = opt; - // tells how to divide the paper when creating the elements map - this.mapGridSize = 100; - } + drag: function(evt, x, y) { - ObstacleMap.prototype.build = function(graph, link) { + var paper = this.paper; + var grid = paper.options.gridSize; + var element = this.model; + var data = this.eventData(evt); + var pointerOffset = data.pointerOffset; + var restrictedArea = data.restrictedArea; + var embedding = data.embedding; - var opt = this.options; + // Make sure the new element's position always snaps to the current grid + var elX = snapToGrid(x + pointerOffset.x, grid); + var elY = snapToGrid(y + pointerOffset.y, grid); - // source or target element could be excluded from set of obstacles - var excludedEnds = toArray(opt.excludeEnds).reduce(function(res, item) { + element.position(elX, elY, { restrictedArea: restrictedArea, deep: true, ui: true }); - var end = link.get(item); - if (end) { - var cell = graph.getCell(end.id); - if (cell) { - res.push(cell); + if (paper.options.embeddingMode) { + if (!embedding) { + // Prepare the element for embedding only if the pointer moves. + // We don't want to do unnecessary action with the element + // if an user only clicks/dblclicks on it. + this.prepareEmbedding(data); + embedding = true; } + this.processEmbedding(data, evt, x, y); } - return res; - }, []); + this.eventData(evt, { + embedding: embedding + }); + }, - // Exclude any embedded elements from the source and the target element. - var excludedAncestors = []; + dragMagnet: function(evt, x, y) { + this.dragLink(evt, x, y); + }, - var source = graph.getCell(link.get('source').id); - if (source) { - excludedAncestors = union(excludedAncestors, source.getAncestors().map(function(cell) { - return cell.id; - })); - } + // Drag End Handlers - var target = graph.getCell(link.get('target').id); - if (target) { - excludedAncestors = union(excludedAncestors, target.getAncestors().map(function(cell) { - return cell.id; - })); - } + dragEnd: function(evt, x, y) { - // Builds a map of all elements for quicker obstacle queries (i.e. is a point contained - // in any obstacle?) (a simplified grid search). - // The paper is divided into smaller cells, where each holds information about which - // elements belong to it. When we query whether a point lies inside an obstacle we - // don't need to go through all obstacles, we check only those in a particular cell. - var mapGridSize = this.mapGridSize; + var data = this.eventData(evt); + if (data.embedding) { this.finalizeEmbedding(data); } + }, - graph.getElements().reduce(function(map, element) { + dragMagnetEnd: function(evt, x, y) { + this.dragLinkEnd(evt, x, y); + }, - var isExcludedType = toArray(opt.excludeTypes).includes(element.get('type')); - var isExcludedEnd = excludedEnds.find(function(excluded) { - return excluded.id === element.id; - }); - var isExcludedAncestor = excludedAncestors.includes(element.id); + magnetpointerclick: function(evt, magnet, x, y) { + var paper = this.paper; + if (paper.eventData(evt).mousemoved > paper.options.clickThreshold) { return; } + this.notify('element:magnet:pointerclick', evt, magnet, x, y); + } - var isExcluded = isExcludedType || isExcludedEnd || isExcludedAncestor; - if (!isExcluded) { - var bbox = element.getBBox().moveAndExpand(opt.paddingBox); + }, { - var origin = bbox.origin().snapToGrid(mapGridSize); - var corner = bbox.corner().snapToGrid(mapGridSize); + Flags: Flags, + }); - for (var x = origin.x; x <= corner.x; x += mapGridSize) { - for (var y = origin.y; y <= corner.y; y += mapGridSize) { - var gridKey = x + '@' + y; - map[gridKey] = map[gridKey] || []; - map[gridKey].push(bbox); - } - } - } + assign(ElementView.prototype, elementViewPortPrototype); - return map; - }, this.map); + // Does not make any changes to vertices. + // Returns the arguments that are passed to it, unchanged. + var normal = function(vertices, opt, linkView) { - return this; + return vertices; }; - ObstacleMap.prototype.isPointAccessible = function(point) { + // Routes the link always to/from a certain side + // + // Arguments: + // padding ... gap between the element and the first vertex. :: Default 40. + // side ... 'left' | 'right' | 'top' | 'bottom' :: Default 'bottom'. + // + var oneSide = function(vertices, opt, linkView) { - var mapKey = point.clone().snapToGrid(this.mapGridSize).toString(); + var side = opt.side || 'bottom'; + var padding = normalizeSides(opt.padding || 40); - return toArray(this.map[mapKey]).every(function(obstacle) { - return !obstacle.containsPoint(point); - }); - }; + // LinkView contains cached source an target bboxes. + // Note that those are Geometry rectangle objects. + var sourceBBox = linkView.sourceBBox; + var targetBBox = linkView.targetBBox; + var sourcePoint = sourceBBox.center(); + var targetPoint = targetBBox.center(); - // Sorted Set - // Set of items sorted by given value. - function SortedSet() { - this.items = []; - this.hash = {}; - this.values = {}; - this.OPEN = 1; - this.CLOSE = 2; - } + var coordinate, dimension, direction; - SortedSet.prototype.add = function(item, value) { + switch (side) { + case 'bottom': + direction = 1; + coordinate = 'y'; + dimension = 'height'; + break; + case 'top': + direction = -1; + coordinate = 'y'; + dimension = 'height'; + break; + case 'left': + direction = -1; + coordinate = 'x'; + dimension = 'width'; + break; + case 'right': + direction = 1; + coordinate = 'x'; + dimension = 'width'; + break; + default: + throw new Error('Router: invalid side'); + } - if (this.hash[item]) { - // item removal - this.items.splice(this.items.indexOf(item), 1); + // move the points from the center of the element to outside of it. + sourcePoint[coordinate] += direction * (sourceBBox[dimension] / 2 + padding[side]); + targetPoint[coordinate] += direction * (targetBBox[dimension] / 2 + padding[side]); + + // make link orthogonal (at least the first and last vertex). + if ((direction * (sourcePoint[coordinate] - targetPoint[coordinate])) > 0) { + targetPoint[coordinate] = sourcePoint[coordinate]; } else { - this.hash[item] = this.OPEN; + sourcePoint[coordinate] = targetPoint[coordinate]; } - this.values[item] = value; + return [sourcePoint].concat(vertices, targetPoint); + }; - var index$1 = sortedIndex(this.items, item, function(i) { - return this.values[i]; - }.bind(this)); + // bearing -> opposite bearing + var opposites = { + N: 'S', + S: 'N', + E: 'W', + W: 'E' + }; - this.items.splice(index$1, 0, item); + // bearing -> radians + var radians = { + N: -Math.PI / 2 * 3, + S: -Math.PI / 2, + E: 0, + W: Math.PI }; - SortedSet.prototype.remove = function(item) { + // HELPERS // - this.hash[item] = this.CLOSE; - }; + // returns a point `p` where lines p,p1 and p,p2 are perpendicular and p is not contained + // in the given box + function freeJoin(p1, p2, bbox) { - SortedSet.prototype.isOpen = function(item) { + var p = new Point(p1.x, p2.y); + if (bbox.containsPoint(p)) { p = new Point(p2.x, p1.y); } + // kept for reference + // if (bbox.containsPoint(p)) p = null; - return this.hash[item] === this.OPEN; - }; + return p; + } - SortedSet.prototype.isClose = function(item) { + // returns either width or height of a bbox based on the given bearing + function getBBoxSize(bbox, bearing) { - return this.hash[item] === this.CLOSE; - }; + return bbox[(bearing === 'W' || bearing === 'E') ? 'width' : 'height']; + } - SortedSet.prototype.isEmpty = function() { + // simple bearing method (calculates only orthogonal cardinals) + function getBearing(from, to) { - return this.items.length === 0; - }; + if (from.x === to.x) { return (from.y > to.y) ? 'N' : 'S'; } + if (from.y === to.y) { return (from.x > to.x) ? 'W' : 'E'; } + return null; + } - SortedSet.prototype.pop = function() { + // transform point to a rect + function getPointBox(p) { - var item = this.items.shift(); - this.remove(item); - return item; - }; + return new Rect(p.x, p.y, 0, 0); + } - // HELPERS // + function getPaddingBox(opt) { - // return source bbox - function getSourceBBox$1(linkView, opt) { + // if both provided, opt.padding wins over opt.elementPadding + var sides = normalizeSides(opt.padding || opt.elementPadding || 20); - // expand by padding box - if (opt && opt.paddingBox) { return linkView.sourceBBox.clone().moveAndExpand(opt.paddingBox); } + return { + x: -sides.left, + y: -sides.top, + width: sides.left + sides.right, + height: sides.top + sides.bottom + }; + } - return linkView.sourceBBox.clone(); + // return source bbox + function getSourceBBox(linkView, opt) { + + return linkView.sourceBBox.clone().moveAndExpand(getPaddingBox(opt)); } // return target bbox - function getTargetBBox$1(linkView, opt) { - - // expand by padding box - if (opt && opt.paddingBox) { return linkView.targetBBox.clone().moveAndExpand(opt.paddingBox); } + function getTargetBBox(linkView, opt) { - return linkView.targetBBox.clone(); + return linkView.targetBBox.clone().moveAndExpand(getPaddingBox(opt)); } // return source anchor - function getSourceAnchor$1(linkView, opt) { + function getSourceAnchor(linkView, opt) { if (linkView.sourceAnchor) { return linkView.sourceAnchor; } // fallback: center of bbox - var sourceBBox = getSourceBBox$1(linkView, opt); + var sourceBBox = getSourceBBox(linkView, opt); return sourceBBox.center(); } // return target anchor - function getTargetAnchor$1(linkView, opt) { + function getTargetAnchor(linkView, opt) { if (linkView.targetAnchor) { return linkView.targetAnchor; } // fallback: center of bbox - var targetBBox = getTargetBBox$1(linkView, opt); + var targetBBox = getTargetBBox(linkView, opt); return targetBBox.center(); // default } - // returns a direction index from start point to end point - // corrects for grid deformation between start and end - function getDirectionAngle(start, end, numDirections, grid, opt) { + // PARTIAL ROUTERS // - var quadrant = 360 / numDirections; - var angleTheta = start.theta(fixAngleEnd(start, end, grid, opt)); - var normalizedAngle = normalizeAngle(angleTheta + (quadrant / 2)); - return quadrant * Math.floor(normalizedAngle / quadrant); - } + function vertexVertex(from, to, bearing) { - // helper function for getDirectionAngle() - // corrects for grid deformation - // (if a point is one grid steps away from another in both dimensions, - // it is considered to be 45 degrees away, even if the real angle is different) - // this causes visible angle discrepancies if `opt.step` is much larger than `paper.gridSize` - function fixAngleEnd(start, end, grid, opt) { + var p1 = new Point(from.x, to.y); + var p2 = new Point(to.x, from.y); + var d1 = getBearing(from, p1); + var d2 = getBearing(from, p2); + var opposite = opposites[bearing]; - var step = opt.step; + var p = (d1 === bearing || (d1 !== opposite && (d2 === opposite || d2 !== bearing))) ? p1 : p2; - var diffX = end.x - start.x; - var diffY = end.y - start.y; + return { points: [p], direction: getBearing(p, to) }; + } - var gridStepsX = diffX / grid.x; - var gridStepsY = diffY / grid.y; + function elementVertex(from, to, fromBBox) { - var distanceX = gridStepsX * step; - var distanceY = gridStepsY * step; + var p = freeJoin(from, to, fromBBox); - return new Point(start.x + distanceX, start.y + distanceY); + return { points: [p], direction: getBearing(p, to) }; } - // return the change in direction between two direction angles - function getDirectionChange(angle1, angle2) { + function vertexElement(from, to, toBBox, bearing) { - var directionChange = Math.abs(angle1 - angle2); - return (directionChange > 180) ? (360 - directionChange) : directionChange; - } + var route = {}; - // fix direction offsets according to current grid - function getGridOffsets(directions, grid, opt) { + var points = [new Point(from.x, to.y), new Point(to.x, from.y)]; + var freePoints = points.filter(function(pt) { + return !toBBox.containsPoint(pt); + }); + var freeBearingPoints = freePoints.filter(function(pt) { + return getBearing(pt, from) !== bearing; + }); - var step = opt.step; + var p; - toArray(opt.directions).forEach(function(direction) { + if (freeBearingPoints.length > 0) { + // Try to pick a point which bears the same direction as the previous segment. - direction.gridOffsetX = (direction.offsetX / step) * grid.x; - direction.gridOffsetY = (direction.offsetY / step) * grid.y; - }); - } + p = freeBearingPoints.filter(function(pt) { + return getBearing(from, pt) === bearing; + }).pop(); + p = p || freeBearingPoints[0]; - // get grid size in x and y dimensions, adapted to source and target positions - function getGrid(step, source, target) { + route.points = [p]; + route.direction = getBearing(p, to); - return { - source: source.clone(), - x: getGridDimension(target.x - source.x, step), - y: getGridDimension(target.y - source.y, step) - }; - } + } else { + // Here we found only points which are either contained in the element or they would create + // a link segment going in opposite direction from the previous one. + // We take the point inside element and move it outside the element in the direction the + // route is going. Now we can join this point with the current end (using freeJoin). - // helper function for getGrid() - function getGridDimension(diff, step) { + p = difference(points, freePoints)[0]; - // return step if diff = 0 - if (!diff) { return step; } + var p2 = (new Point(to)).move(p, -getBBoxSize(toBBox, bearing) / 2); + var p1 = freeJoin(p2, from, toBBox); - var absDiff = Math.abs(diff); - var numSteps = Math.round(absDiff / step); + route.points = [p1, p2]; + route.direction = getBearing(p2, to); + } - // return absDiff if less than one step apart - if (!numSteps) { return absDiff; } + return route; + } - // otherwise, return corrected step - var roundedDiff = numSteps * step; - var remainder = absDiff - roundedDiff; - var stepCorrection = remainder / numSteps; + function elementElement(from, to, fromBBox, toBBox) { - return step + stepCorrection; - } + var route = elementVertex(to, from, toBBox); + var p1 = route.points[0]; - // return a clone of point snapped to grid - function snapToGrid$1(point, grid) { + if (fromBBox.containsPoint(p1)) { - var source = grid.source; + route = elementVertex(from, to, fromBBox); + var p2 = route.points[0]; - var snappedX = snapToGrid(point.x - source.x, grid.x) + source.x; - var snappedY = snapToGrid(point.y - source.y, grid.y) + source.y; + if (toBBox.containsPoint(p2)) { - return new Point(snappedX, snappedY); - } + var fromBorder = (new Point(from)).move(p2, -getBBoxSize(fromBBox, getBearing(from, p2)) / 2); + var toBorder = (new Point(to)).move(p1, -getBBoxSize(toBBox, getBearing(to, p1)) / 2); + var mid = (new Line(fromBorder, toBorder)).midpoint(); - // round the point to opt.precision - function round$4(point, precision) { + var startRoute = elementVertex(from, mid, fromBBox); + var endRoute = vertexVertex(mid, to, startRoute.direction); - return point.round(precision); + route.points = [startRoute.points[0], endRoute.points[0]]; + route.direction = endRoute.direction; + } + } + + return route; } - // snap to grid and then round the point - function align(point, grid, precision) { + // Finds route for situations where one element is inside the other. + // Typically the route is directed outside the outer element first and + // then back towards the inner element. + function insideElement(from, to, fromBBox, toBBox, bearing) { - return round$4(snapToGrid$1(point.clone(), grid), precision); - } + var route = {}; + var boundary = fromBBox.union(toBBox).inflate(1); - // return a string representing the point - // string is rounded in both dimensions - function getKey(point) { + // start from the point which is closer to the boundary + var reversed = boundary.center().distance(to) > boundary.center().distance(from); + var start = reversed ? to : from; + var end = reversed ? from : to; - return point.clone().toString(); - } + var p1, p2, p3; - // return a normalized vector from given point - // used to determine the direction of a difference of two points - function normalizePoint(point) { + if (bearing) { + // Points on circle with radius equals 'W + H` are always outside the rectangle + // with width W and height H if the center of that circle is the center of that rectangle. + p1 = Point.fromPolar(boundary.width + boundary.height, radians[bearing], start); + p1 = boundary.pointNearestToPoint(p1).move(p1, -1); - return new Point( - point.x === 0 ? 0 : Math.abs(point.x) / point.x, - point.y === 0 ? 0 : Math.abs(point.y) / point.y - ); - } + } else { + p1 = boundary.pointNearestToPoint(start).move(start, 1); + } - // PATHFINDING // + p2 = freeJoin(p1, end, boundary); - // reconstructs a route by concatenating points with their parents - function reconstructRoute(parents, points, tailPoint, from, to, grid, opt) { + if (p1.round().equals(p2.round())) { + p2 = Point.fromPolar(boundary.width + boundary.height, toRad(p1.theta(start)) + Math.PI / 2, end); + p2 = boundary.pointNearestToPoint(p2).move(end, 1).round(); + p3 = freeJoin(p1, p2, boundary); + route.points = reversed ? [p2, p3, p1] : [p1, p3, p2]; - var route = []; + } else { + route.points = reversed ? [p2, p1] : [p1, p2]; + } - var prevDiff = normalizePoint(to.difference(tailPoint)); + route.direction = reversed ? getBearing(p1, to) : getBearing(p2, to); - // tailPoint is assumed to be aligned already - var currentKey = getKey(tailPoint); - var parent = parents[currentKey]; + return route; + } - var point; - while (parent) { + // MAIN ROUTER // - // point is assumed to be aligned already - point = points[currentKey]; + // Return points through which a connection needs to be drawn in order to obtain an orthogonal link + // routing from source to target going through `vertices`. + function orthogonal(vertices, opt, linkView) { - var diff = normalizePoint(point.difference(parent)); - if (!diff.equals(prevDiff)) { - route.unshift(point); - prevDiff = diff; - } + var sourceBBox = getSourceBBox(linkView, opt); + var targetBBox = getTargetBBox(linkView, opt); - // parent is assumed to be aligned already - currentKey = getKey(parent); - parent = parents[currentKey]; - } + var sourceAnchor = getSourceAnchor(linkView, opt); + var targetAnchor = getTargetAnchor(linkView, opt); - // leadPoint is assumed to be aligned already - var leadPoint = points[currentKey]; + // if anchor lies outside of bbox, the bbox expands to include it + sourceBBox = sourceBBox.union(getPointBox(sourceAnchor)); + targetBBox = targetBBox.union(getPointBox(targetAnchor)); - var fromDiff = normalizePoint(leadPoint.difference(from)); - if (!fromDiff.equals(prevDiff)) { - route.unshift(leadPoint); - } + vertices = toArray(vertices).map(Point); + vertices.unshift(sourceAnchor); + vertices.push(targetAnchor); - return route; - } + var bearing; // bearing of previous route segment - // heuristic method to determine the distance between two points - function estimateCost(from, endPoints) { + var orthogonalVertices = []; // the array of found orthogonal vertices to be returned + for (var i = 0, max = vertices.length - 1; i < max; i++) { - var min = Infinity; + var route = null; - for (var i = 0, len = endPoints.length; i < len; i++) { - var cost = from.manhattanDistance(endPoints[i]); - if (cost < min) { min = cost; } - } + var from = vertices[i]; + var to = vertices[i + 1]; - return min; - } + var isOrthogonal = !!getBearing(from, to); - // find points around the bbox taking given directions into account - // lines are drawn from anchor in given directions, intersections recorded - // if anchor is outside bbox, only those directions that intersect get a rect point - // the anchor itself is returned as rect point (representing some directions) - // (since those directions are unobstructed by the bbox) - function getRectPoints(anchor, bbox, directionList, grid, opt) { + if (i === 0) { // source - var precision = opt.precision; - var directionMap = opt.directionMap; + if (i + 1 === max) { // route source -> target - var anchorCenterVector = anchor.difference(bbox.center()); + // Expand one of the elements by 1px to detect situations when the two + // elements are positioned next to each other with no gap in between. + if (sourceBBox.intersect(targetBBox.clone().inflate(1))) { + route = insideElement(from, to, sourceBBox, targetBBox); - var keys = isObject$1(directionMap) ? Object.keys(directionMap) : []; - var dirList = toArray(directionList); - var rectPoints = keys.reduce(function(res, key) { + } else if (!isOrthogonal) { + route = elementElement(from, to, sourceBBox, targetBBox); + } - if (dirList.includes(key)) { - var direction = directionMap[key]; + } else { // route source -> vertex - // create a line that is guaranteed to intersect the bbox if bbox is in the direction - // even if anchor lies outside of bbox - var endpoint = new Point( - anchor.x + direction.x * (Math.abs(anchorCenterVector.x) + bbox.width), - anchor.y + direction.y * (Math.abs(anchorCenterVector.y) + bbox.height) - ); - var intersectionLine = new Line(anchor, endpoint); + if (sourceBBox.containsPoint(to)) { + route = insideElement(from, to, sourceBBox, getPointBox(to).moveAndExpand(getPaddingBox(opt))); - // get the farther intersection, in case there are two - // (that happens if anchor lies next to bbox) - var intersections = intersectionLine.intersect(bbox) || []; - var numIntersections = intersections.length; - var farthestIntersectionDistance; - var farthestIntersection = null; - for (var i = 0; i < numIntersections; i++) { - var currentIntersection = intersections[i]; - var distance = anchor.squaredDistance(currentIntersection); - if ((farthestIntersectionDistance === undefined) || (distance > farthestIntersectionDistance)) { - farthestIntersectionDistance = distance; - farthestIntersection = currentIntersection; + } else if (!isOrthogonal) { + route = elementVertex(from, to, sourceBBox); } } - // if an intersection was found in this direction, it is our rectPoint - if (farthestIntersection) { - var point = align(farthestIntersection, grid, precision); + } else if (i + 1 === max) { // route vertex -> target - // if the rectPoint lies inside the bbox, offset it by one more step - if (bbox.containsPoint(point)) { - point = align(point.offset(direction.x * grid.x, direction.y * grid.y), grid, precision); - } + // prevent overlaps with previous line segment + var isOrthogonalLoop = isOrthogonal && getBearing(to, from) === bearing; - // then add the point to the result array - // aligned - res.push(point); + if (targetBBox.containsPoint(from) || isOrthogonalLoop) { + route = insideElement(from, to, getPointBox(from).moveAndExpand(getPaddingBox(opt)), targetBBox, bearing); + + } else if (!isOrthogonal) { + route = vertexElement(from, to, targetBBox, bearing); } + + } else if (!isOrthogonal) { // route vertex -> vertex + route = vertexVertex(from, to, bearing); } - return res; - }, []); + // applicable to all routes: - // if anchor lies outside of bbox, add it to the array of points - if (!bbox.containsPoint(anchor)) { - // aligned - rectPoints.push(align(anchor, grid, precision)); + // set bearing for next iteration + if (route) { + Array.prototype.push.apply(orthogonalVertices, route.points); + bearing = route.direction; + + } else { + // orthogonal route and not looped + bearing = getBearing(from, to); + } + + // push `to` point to identified orthogonal vertices array + if (i + 1 < max) { + orthogonalVertices.push(to); + } } - return rectPoints; + return orthogonalVertices; } - // finds the route between two points/rectangles (`from`, `to`) implementing A* algorithm - // rectangles get rect points assigned by getRectPoints() - function findRoute(from, to, isPointObstacle, opt) { + var config$1 = { - var precision = opt.precision; + // size of the step to find a route (the grid of the manhattan pathfinder) + step: 10, - // Get grid for this route. + // the number of route finding loops that cause the router to abort + // returns fallback route instead + maximumLoops: 2000, - var sourceAnchor, targetAnchor; + // the number of decimal places to round floating point coordinates + precision: 1, - if (from instanceof Rect) { // `from` is sourceBBox - sourceAnchor = round$4(getSourceAnchor$1(this, opt).clone(), precision); - } else { - sourceAnchor = round$4(from.clone(), precision); - } + // maximum change of direction + maxAllowedDirectionChange: 90, - if (to instanceof Rect) { // `to` is targetBBox - targetAnchor = round$4(getTargetAnchor$1(this, opt).clone(), precision); - } else { - targetAnchor = round$4(to.clone(), precision); - } + // should the router use perpendicular linkView option? + // does not connect anchor of element but rather a point close-by that is orthogonal + // this looks much better + perpendicular: true, - var grid = getGrid(opt.step, sourceAnchor, targetAnchor); + // should the source and/or target not be considered as obstacles? + excludeEnds: [], // 'source', 'target' - // Get pathfinding points. + // should certain types of elements not be considered as obstacles? + excludeTypes: ['basic.Text'], - var start, end; // aligned with grid by definition - var startPoints, endPoints; // assumed to be aligned with grid already + // possible starting directions from an element + startDirections: ['top', 'right', 'bottom', 'left'], - // set of points we start pathfinding from - if (from instanceof Rect) { // `from` is sourceBBox - start = sourceAnchor; - startPoints = getRectPoints(start, from, opt.startDirections, grid, opt); + // possible ending directions to an element + endDirections: ['top', 'right', 'bottom', 'left'], - } else { - start = sourceAnchor; - startPoints = [start]; - } + // specify the directions used above and what they mean + directionMap: { + top: { x: 0, y: -1 }, + right: { x: 1, y: 0 }, + bottom: { x: 0, y: 1 }, + left: { x: -1, y: 0 } + }, - // set of points we want the pathfinding to finish at - if (to instanceof Rect) { // `to` is targetBBox - end = targetAnchor; - endPoints = getRectPoints(targetAnchor, to, opt.endDirections, grid, opt); + // cost of an orthogonal step + cost: function() { - } else { - end = targetAnchor; - endPoints = [end]; - } + return this.step; + }, - // take into account only accessible rect points (those not under obstacles) - startPoints = startPoints.filter(function (p) { return !isPointObstacle(p); }); - endPoints = endPoints.filter(function (p) { return !isPointObstacle(p); }); + // an array of directions to find next points on the route + // different from start/end directions + directions: function() { - // Check that there is an accessible route point on both sides. - // Otherwise, use fallbackRoute(). - if (startPoints.length > 0 && endPoints.length > 0) { + var step = this.step; + var cost = this.cost(); - // The set of tentative points to be evaluated, initially containing the start points. - // Rounded to nearest integer for simplicity. - var openSet = new SortedSet(); - // Keeps reference to actual points for given elements of the open set. - var points = {}; - // Keeps reference to a point that is immediate predecessor of given element. - var parents = {}; - // Cost from start to a point along best known path. - var costs = {}; + return [ + { offsetX: step, offsetY: 0, cost: cost }, + { offsetX: -step, offsetY: 0, cost: cost }, + { offsetX: 0, offsetY: step, cost: cost }, + { offsetX: 0, offsetY: -step, cost: cost } + ]; + }, - for (var i = 0, n = startPoints.length; i < n; i++) { - // startPoint is assumed to be aligned already - var startPoint = startPoints[i]; + // a penalty received for direction change + penalties: function() { - var key = getKey(startPoint); + return { + 0: 0, + 45: this.step / 2, + 90: this.step / 2 + }; + }, - openSet.add(key, estimateCost(startPoint, endPoints)); - points[key] = startPoint; - costs[key] = 0; - } + // padding applied on the element bounding boxes + paddingBox: function() { - var previousRouteDirectionAngle = opt.previousDirectionAngle; // undefined for first route - var isPathBeginning = (previousRouteDirectionAngle === undefined); + var step = this.step; - // directions - var direction, directionChange; - var directions = opt.directions; - getGridOffsets(directions, grid, opt); + return { + x: -step, + y: -step, + width: 2 * step, + height: 2 * step + }; + }, - var numDirections = directions.length; + // A function that determines whether a given point is an obstacle or not. + // If used, the `padding`, `excludeEnds`and `excludeTypes` options are ignored. + // (point: dia.Point) => boolean; + isPointObstacle: null, - var endPointsKeys = toArray(endPoints).reduce(function(res, endPoint) { - // endPoint is assumed to be aligned already + // a router to use when the manhattan router fails + // (one of the partial routes returns null) + fallbackRouter: function(vertices, opt, linkView) { - var key = getKey(endPoint); - res.push(key); - return res; - }, []); + if (!isFunction(orthogonal)) { + throw new Error('Manhattan requires the orthogonal router as default fallback.'); + } - // main route finding loop - var loopsRemaining = opt.maximumLoops; - while (!openSet.isEmpty() && loopsRemaining > 0) { + return orthogonal(vertices, assign({}, config$1, opt), linkView); + }, - // remove current from the open list - var currentKey = openSet.pop(); - var currentPoint = points[currentKey]; - var currentParent = parents[currentKey]; - var currentCost = costs[currentKey]; + /* Deprecated */ + // a simple route used in situations when main routing method fails + // (exceed max number of loop iterations, inaccessible) + fallbackRoute: function(from, to, opt) { - var isRouteBeginning = (currentParent === undefined); // undefined for route starts - var isStart = currentPoint.equals(start); // (is source anchor or `from` point) = can leave in any direction + return null; // null result will trigger the fallbackRouter - var previousDirectionAngle; - if (!isRouteBeginning) { previousDirectionAngle = getDirectionAngle(currentParent, currentPoint, numDirections, grid, opt); } // a vertex on the route - else if (!isPathBeginning) { previousDirectionAngle = previousRouteDirectionAngle; } // beginning of route on the path - else if (!isStart) { previousDirectionAngle = getDirectionAngle(start, currentPoint, numDirections, grid, opt); } // beginning of path, start rect point - else { previousDirectionAngle = null; } // beginning of path, source anchor or `from` point + // left for reference: + /*// Find an orthogonal route ignoring obstacles. - // check if we reached any endpoint - var samePoints = startPoints.length === endPoints.length; - if (samePoints) { - for (var j = 0; j < startPoints.length; j++) { - if (!startPoints[j].equals(endPoints[j])) { - samePoints = false; - break; - } - } - } - var skipEndCheck = (isRouteBeginning && samePoints); - if (!skipEndCheck && (endPointsKeys.indexOf(currentKey) >= 0)) { - opt.previousDirectionAngle = previousDirectionAngle; - return reconstructRoute(parents, points, currentPoint, start, end, grid, opt); - } + var point = ((opt.previousDirAngle || 0) % 180 === 0) + ? new g.Point(from.x, to.y) + : new g.Point(to.x, from.y); - // go over all possible directions and find neighbors - for (i = 0; i < numDirections; i++) { - direction = directions[i]; + return [point];*/ + }, - var directionAngle = direction.angle; - directionChange = getDirectionChange(previousDirectionAngle, directionAngle); + // if a function is provided, it's used to route the link while dragging an end + // i.e. function(from, to, opt) { return []; } + draggingRoute: null + }; - // if the direction changed rapidly, don't use this point - // any direction is allowed for starting points - if (!(isPathBeginning && isStart) && directionChange > opt.maxAllowedDirectionChange) { continue; } - - var neighborPoint = align(currentPoint.clone().offset(direction.gridOffsetX, direction.gridOffsetY), grid, precision); - var neighborKey = getKey(neighborPoint); + // HELPER CLASSES // - // Closed points from the openSet were already evaluated. - if (openSet.isClose(neighborKey) || isPointObstacle(neighborPoint)) { continue; } + // Map of obstacles + // Helper structure to identify whether a point lies inside an obstacle. + function ObstacleMap(opt) { - // We can only enter end points at an acceptable angle. - if (endPointsKeys.indexOf(neighborKey) >= 0) { // neighbor is an end point + this.map = {}; + this.options = opt; + // tells how to divide the paper when creating the elements map + this.mapGridSize = 100; + } - var isNeighborEnd = neighborPoint.equals(end); // (is target anchor or `to` point) = can be entered in any direction + ObstacleMap.prototype.build = function(graph, link) { - if (!isNeighborEnd) { - var endDirectionAngle = getDirectionAngle(neighborPoint, end, numDirections, grid, opt); - var endDirectionChange = getDirectionChange(directionAngle, endDirectionAngle); + var opt = this.options; - if (endDirectionChange > opt.maxAllowedDirectionChange) { continue; } - } - } + // source or target element could be excluded from set of obstacles + var excludedEnds = toArray(opt.excludeEnds).reduce(function(res, item) { - // The current direction is ok. + var end = link.get(item); + if (end) { + var cell = graph.getCell(end.id); + if (cell) { + res.push(cell); + } + } - var neighborCost = direction.cost; - var neighborPenalty = isStart ? 0 : opt.penalties[directionChange]; // no penalties for start point - var costFromStart = currentCost + neighborCost + neighborPenalty; + return res; + }, []); - if (!openSet.isOpen(neighborKey) || (costFromStart < costs[neighborKey])) { - // neighbor point has not been processed yet - // or the cost of the path from start is lower than previously calculated + // Exclude any embedded elements from the source and the target element. + var excludedAncestors = []; - points[neighborKey] = neighborPoint; - parents[neighborKey] = currentPoint; - costs[neighborKey] = costFromStart; - openSet.add(neighborKey, costFromStart + estimateCost(neighborPoint, endPoints)); - } - } + var source = graph.getCell(link.get('source').id); + if (source) { + excludedAncestors = union(excludedAncestors, source.getAncestors().map(function(cell) { + return cell.id; + })); + } - loopsRemaining--; - } + var target = graph.getCell(link.get('target').id); + if (target) { + excludedAncestors = union(excludedAncestors, target.getAncestors().map(function(cell) { + return cell.id; + })); } - // no route found (`to` point either wasn't accessible or finding route took - // way too much calculation) - return opt.fallbackRoute.call(this, start, end, opt); - } + // Builds a map of all elements for quicker obstacle queries (i.e. is a point contained + // in any obstacle?) (a simplified grid search). + // The paper is divided into smaller cells, where each holds information about which + // elements belong to it. When we query whether a point lies inside an obstacle we + // don't need to go through all obstacles, we check only those in a particular cell. + var mapGridSize = this.mapGridSize; - // resolve some of the options - function resolveOptions(opt) { + graph.getElements().reduce(function(map, element) { - opt.directions = result(opt, 'directions'); - opt.penalties = result(opt, 'penalties'); - opt.paddingBox = result(opt, 'paddingBox'); - opt.padding = result(opt, 'padding'); + var isExcludedType = toArray(opt.excludeTypes).includes(element.get('type')); + var isExcludedEnd = excludedEnds.find(function(excluded) { + return excluded.id === element.id; + }); + var isExcludedAncestor = excludedAncestors.includes(element.id); - if (opt.padding) { - // if both provided, opt.padding wins over opt.paddingBox - var sides = normalizeSides(opt.padding); - opt.paddingBox = { - x: -sides.left, - y: -sides.top, - width: sides.left + sides.right, - height: sides.top + sides.bottom - }; - } + var isExcluded = isExcludedType || isExcludedEnd || isExcludedAncestor; + if (!isExcluded) { + var bbox = element.getBBox().moveAndExpand(opt.paddingBox); - toArray(opt.directions).forEach(function(direction) { + var origin = bbox.origin().snapToGrid(mapGridSize); + var corner = bbox.corner().snapToGrid(mapGridSize); - var point1 = new Point(0, 0); - var point2 = new Point(direction.offsetX, direction.offsetY); + for (var x = origin.x; x <= corner.x; x += mapGridSize) { + for (var y = origin.y; y <= corner.y; y += mapGridSize) { + var gridKey = x + '@' + y; + map[gridKey] = map[gridKey] || []; + map[gridKey].push(bbox); + } + } + } - direction.angle = normalizeAngle(point1.theta(point2)); - }); - } + return map; + }, this.map); - // initialization of the route finding - function router(vertices, opt, linkView) { + return this; + }; - resolveOptions(opt); + ObstacleMap.prototype.isPointAccessible = function(point) { - // enable/disable linkView perpendicular option - linkView.options.perpendicular = !!opt.perpendicular; + var mapKey = point.clone().snapToGrid(this.mapGridSize).toString(); - var sourceBBox = getSourceBBox$1(linkView, opt); - var targetBBox = getTargetBBox$1(linkView, opt); + return toArray(this.map[mapKey]).every(function(obstacle) { + return !obstacle.containsPoint(point); + }); + }; - var sourceAnchor = getSourceAnchor$1(linkView, opt); - //var targetAnchor = getTargetAnchor(linkView, opt); + // Sorted Set + // Set of items sorted by given value. + function SortedSet() { + this.items = []; + this.hash = {}; + this.values = {}; + this.OPEN = 1; + this.CLOSE = 2; + } - // pathfinding - var isPointObstacle; - if (typeof opt.isPointObstacle === 'function') { - isPointObstacle = opt.isPointObstacle; + SortedSet.prototype.add = function(item, value) { + + if (this.hash[item]) { + // item removal + this.items.splice(this.items.indexOf(item), 1); } else { - var map = new ObstacleMap(opt); - map.build(linkView.paper.model, linkView.model); - isPointObstacle = function (point) { return !map.isPointAccessible(point); }; + this.hash[item] = this.OPEN; } - var oldVertices = toArray(vertices).map(Point); - var newVertices = []; - var tailPoint = sourceAnchor; // the origin of first route's grid, does not need snapping + this.values[item] = value; - // find a route by concatenating all partial routes (routes need to pass through vertices) - // source -> vertex[1] -> ... -> vertex[n] -> target - var to, from; + var index$1 = sortedIndex(this.items, item, function(i) { + return this.values[i]; + }.bind(this)); - for (var i = 0, len = oldVertices.length; i <= len; i++) { + this.items.splice(index$1, 0, item); + }; - var partialRoute = null; + SortedSet.prototype.remove = function(item) { - from = to || sourceBBox; - to = oldVertices[i]; + this.hash[item] = this.CLOSE; + }; - if (!to) { - // this is the last iteration - // we ran through all vertices in oldVertices - // 'to' is not a vertex. + SortedSet.prototype.isOpen = function(item) { - to = targetBBox; + return this.hash[item] === this.OPEN; + }; - // If the target is a point (i.e. it's not an element), we - // should use dragging route instead of main routing method if it has been provided. - var isEndingAtPoint = !linkView.model.get('source').id || !linkView.model.get('target').id; + SortedSet.prototype.isClose = function(item) { - if (isEndingAtPoint && isFunction(opt.draggingRoute)) { - // Make sure we are passing points only (not rects). - var dragFrom = (from === sourceBBox) ? sourceAnchor : from; - var dragTo = to.origin(); + return this.hash[item] === this.CLOSE; + }; - partialRoute = opt.draggingRoute.call(linkView, dragFrom, dragTo, opt); - } - } + SortedSet.prototype.isEmpty = function() { - // if partial route has not been calculated yet use the main routing method to find one - partialRoute = partialRoute || findRoute.call(linkView, from, to, isPointObstacle, opt); + return this.items.length === 0; + }; - if (partialRoute === null) { // the partial route cannot be found - return opt.fallbackRouter(vertices, opt, linkView); - } + SortedSet.prototype.pop = function() { - var leadPoint = partialRoute[0]; + var item = this.items.shift(); + this.remove(item); + return item; + }; - // remove the first point if the previous partial route had the same point as last - if (leadPoint && leadPoint.equals(tailPoint)) { partialRoute.shift(); } + // HELPERS // - // save tailPoint for next iteration - tailPoint = partialRoute[partialRoute.length - 1] || tailPoint; + // return source bbox + function getSourceBBox$1(linkView, opt) { - Array.prototype.push.apply(newVertices, partialRoute); - } + // expand by padding box + if (opt && opt.paddingBox) { return linkView.sourceBBox.clone().moveAndExpand(opt.paddingBox); } - return newVertices; + return linkView.sourceBBox.clone(); } - // public function - var manhattan = function(vertices, opt, linkView) { - return router(vertices, assign({}, config$1, opt), linkView); - }; + // return target bbox + function getTargetBBox$1(linkView, opt) { - var config$2 = { + // expand by padding box + if (opt && opt.paddingBox) { return linkView.targetBBox.clone().moveAndExpand(opt.paddingBox); } - maxAllowedDirectionChange: 45, + return linkView.targetBBox.clone(); + } - // cost of a diagonal step - diagonalCost: function() { + // return source anchor + function getSourceAnchor$1(linkView, opt) { - var step = this.step; - return Math.ceil(Math.sqrt(step * step << 1)); - }, + if (linkView.sourceAnchor) { return linkView.sourceAnchor; } - // an array of directions to find next points on the route - // different from start/end directions - directions: function() { + // fallback: center of bbox + var sourceBBox = getSourceBBox$1(linkView, opt); + return sourceBBox.center(); + } - var step = this.step; - var cost = this.cost(); - var diagonalCost = this.diagonalCost(); + // return target anchor + function getTargetAnchor$1(linkView, opt) { - return [ - { offsetX: step, offsetY: 0, cost: cost }, - { offsetX: step, offsetY: step, cost: diagonalCost }, - { offsetX: 0, offsetY: step, cost: cost }, - { offsetX: -step, offsetY: step, cost: diagonalCost }, - { offsetX: -step, offsetY: 0, cost: cost }, - { offsetX: -step, offsetY: -step, cost: diagonalCost }, - { offsetX: 0, offsetY: -step, cost: cost }, - { offsetX: step, offsetY: -step, cost: diagonalCost } - ]; - }, + if (linkView.targetAnchor) { return linkView.targetAnchor; } - // a simple route used in situations when main routing method fails - // (exceed max number of loop iterations, inaccessible) - fallbackRoute: function(from, to, opt) { + // fallback: center of bbox + var targetBBox = getTargetBBox$1(linkView, opt); + return targetBBox.center(); // default + } - // Find a route which breaks by 45 degrees ignoring all obstacles. + // returns a direction index from start point to end point + // corrects for grid deformation between start and end + function getDirectionAngle(start, end, numDirections, grid, opt) { - var theta = from.theta(to); + var quadrant = 360 / numDirections; + var angleTheta = start.theta(fixAngleEnd(start, end, grid, opt)); + var normalizedAngle = normalizeAngle(angleTheta + (quadrant / 2)); + return quadrant * Math.floor(normalizedAngle / quadrant); + } - var route = []; + // helper function for getDirectionAngle() + // corrects for grid deformation + // (if a point is one grid steps away from another in both dimensions, + // it is considered to be 45 degrees away, even if the real angle is different) + // this causes visible angle discrepancies if `opt.step` is much larger than `paper.gridSize` + function fixAngleEnd(start, end, grid, opt) { - var a = { x: to.x, y: from.y }; - var b = { x: from.x, y: to.y }; + var step = opt.step; - if (theta % 180 > 90) { - var t = a; - a = b; - b = t; - } + var diffX = end.x - start.x; + var diffY = end.y - start.y; - var p1 = (theta % 90) < 45 ? a : b; - var l1 = new Line(from, p1); + var gridStepsX = diffX / grid.x; + var gridStepsY = diffY / grid.y; - var alpha = 90 * Math.ceil(theta / 90); + var distanceX = gridStepsX * step; + var distanceY = gridStepsY * step; - var p2 = Point.fromPolar(l1.squaredLength(), toRad(alpha + 135), p1); - var l2 = new Line(to, p2); + return new Point(start.x + distanceX, start.y + distanceY); + } - var intersectionPoint = l1.intersection(l2); - var point = intersectionPoint ? intersectionPoint : to; + // return the change in direction between two direction angles + function getDirectionChange(angle1, angle2) { - var directionFrom = intersectionPoint ? point : from; + var directionChange = Math.abs(angle1 - angle2); + return (directionChange > 180) ? (360 - directionChange) : directionChange; + } - var quadrant = 360 / opt.directions.length; - var angleTheta = directionFrom.theta(to); - var normalizedAngle = normalizeAngle(angleTheta + (quadrant / 2)); - var directionAngle = quadrant * Math.floor(normalizedAngle / quadrant); + // fix direction offsets according to current grid + function getGridOffsets(directions, grid, opt) { - opt.previousDirectionAngle = directionAngle; + var step = opt.step; - if (point) { route.push(point.round()); } - route.push(to); + toArray(opt.directions).forEach(function(direction) { - return route; - } - }; + direction.gridOffsetX = (direction.offsetX / step) * grid.x; + direction.gridOffsetY = (direction.offsetY / step) * grid.y; + }); + } - // public function - var metro = function(vertices, opt, linkView) { + // get grid size in x and y dimensions, adapted to source and target positions + function getGrid(step, source, target) { - if (!isFunction(manhattan)) { - throw new Error('Metro requires the manhattan router.'); - } + return { + source: source.clone(), + x: getGridDimension(target.x - source.x, step), + y: getGridDimension(target.y - source.y, step) + }; + } - return manhattan(vertices, assign({}, config$2, opt), linkView); - }; + // helper function for getGrid() + function getGridDimension(diff, step) { + // return step if diff = 0 + if (!diff) { return step; } + var absDiff = Math.abs(diff); + var numSteps = Math.round(absDiff / step); - var routers = ({ - normal: normal$1, - oneSide: oneSide, - orthogonal: orthogonal, - manhattan: manhattan, - metro: metro - }); + // return absDiff if less than one step apart + if (!numSteps) { return absDiff; } - function connectionRatio(view, _magnet, _refPoint, opt) { + // otherwise, return corrected step + var roundedDiff = numSteps * step; + var remainder = absDiff - roundedDiff; + var stepCorrection = remainder / numSteps; - var ratio = ('ratio' in opt) ? opt.ratio : 0.5; - return view.getPointAtRatio(ratio); + return step + stepCorrection; } - function connectionLength(view, _magnet, _refPoint, opt) { + // return a clone of point snapped to grid + function snapToGrid$1(point, grid) { - var length = ('length' in opt) ? opt.length : 20; - return view.getPointAtLength(length); - } + var source = grid.source; - function _connectionPerpendicular(view, _magnet, refPoint, opt) { + var snappedX = snapToGrid(point.x - source.x, grid.x) + source.x; + var snappedY = snapToGrid(point.y - source.y, grid.y) + source.y; - var OFFSET = 1e6; - var path = view.getConnection(); - var segmentSubdivisions = view.getConnectionSubdivisions(); - var verticalLine = new Line(refPoint.clone().offset(0, OFFSET), refPoint.clone().offset(0, -OFFSET)); - var horizontalLine = new Line(refPoint.clone().offset(OFFSET, 0), refPoint.clone().offset(-OFFSET, 0)); - var verticalIntersections = verticalLine.intersect(path, { segmentSubdivisions: segmentSubdivisions }); - var horizontalIntersections = horizontalLine.intersect(path, { segmentSubdivisions: segmentSubdivisions }); - var intersections = []; - if (verticalIntersections) { Array.prototype.push.apply(intersections, verticalIntersections); } - if (horizontalIntersections) { Array.prototype.push.apply(intersections, horizontalIntersections); } - if (intersections.length > 0) { return refPoint.chooseClosest(intersections); } - if ('fallbackAt' in opt) { - return getPointAtLink(view, opt.fallbackAt); - } - return connectionClosest(view, _magnet, refPoint, opt); + return new Point(snappedX, snappedY); } - function _connectionClosest(view, _magnet, refPoint, _opt) { + // round the point to opt.precision + function round$4(point, precision) { - var closestPoint = view.getClosestPoint(refPoint); - if (!closestPoint) { return new Point(); } - return closestPoint; + return point.round(precision); } - function resolveRef(fn) { - return function(view, magnet, ref, opt) { - if (ref instanceof Element) { - var refView = this.paper.findView(ref); - var refPoint; - if (refView) { - if (refView.isNodeConnection(ref)) { - var distance = ('fixedAt' in opt) ? opt.fixedAt : '50%'; - refPoint = getPointAtLink(refView, distance); - } else { - refPoint = refView.getNodeBBox(ref).center(); - } - } else { - // Something went wrong - refPoint = new Point(); - } - return fn.call(this, view, magnet, refPoint, opt); - } - return fn.apply(this, arguments); - }; - } + // snap to grid and then round the point + function align(point, grid, precision) { - function getPointAtLink(view, value) { - var parsedValue = parseFloat(value); - if (isPercentage(value)) { - return view.getPointAtRatio(parsedValue / 100); - } else { - return view.getPointAtLength(parsedValue); - } + return round$4(snapToGrid$1(point.clone(), grid), precision); } - var connectionPerpendicular = resolveRef(_connectionPerpendicular); - var connectionClosest = resolveRef(_connectionClosest); - - var linkAnchors = ({ - resolveRef: resolveRef, - connectionRatio: connectionRatio, - connectionLength: connectionLength, - connectionPerpendicular: connectionPerpendicular, - connectionClosest: connectionClosest - }); - function bboxWrapper(method) { + // return a string representing the point + // string is rounded in both dimensions + function getKey(point) { - return function(view, magnet, ref, opt) { + return point.clone().toString(); + } - var rotate = !!opt.rotate; - var bbox = (rotate) ? view.getNodeUnrotatedBBox(magnet) : view.getNodeBBox(magnet); - var anchor = bbox[method](); + // return a normalized vector from given point + // used to determine the direction of a difference of two points + function normalizePoint(point) { - var dx = opt.dx; - if (dx) { - var dxPercentage = isPercentage(dx); - dx = parseFloat(dx); - if (isFinite(dx)) { - if (dxPercentage) { - dx /= 100; - dx *= bbox.width; - } - anchor.x += dx; - } - } + return new Point( + point.x === 0 ? 0 : Math.abs(point.x) / point.x, + point.y === 0 ? 0 : Math.abs(point.y) / point.y + ); + } - var dy = opt.dy; - if (dy) { - var dyPercentage = isPercentage(dy); - dy = parseFloat(dy); - if (isFinite(dy)) { - if (dyPercentage) { - dy /= 100; - dy *= bbox.height; - } - anchor.y += dy; - } - } + // PATHFINDING // - return (rotate) ? anchor.rotate(view.model.getBBox().center(), -view.model.angle()) : anchor; - }; - } + // reconstructs a route by concatenating points with their parents + function reconstructRoute(parents, points, tailPoint, from, to, grid, opt) { - function _perpendicular(view, magnet, refPoint, opt) { + var route = []; - var angle = view.model.angle(); - var bbox = view.getNodeBBox(magnet); - var anchor = bbox.center(); - var topLeft = bbox.origin(); - var bottomRight = bbox.corner(); + var prevDiff = normalizePoint(to.difference(tailPoint)); - var padding = opt.padding; - if (!isFinite(padding)) { padding = 0; } + // tailPoint is assumed to be aligned already + var currentKey = getKey(tailPoint); + var parent = parents[currentKey]; - if ((topLeft.y + padding) <= refPoint.y && refPoint.y <= (bottomRight.y - padding)) { - var dy = (refPoint.y - anchor.y); - anchor.x += (angle === 0 || angle === 180) ? 0 : dy * 1 / Math.tan(toRad(angle)); - anchor.y += dy; - } else if ((topLeft.x + padding) <= refPoint.x && refPoint.x <= (bottomRight.x - padding)) { - var dx = (refPoint.x - anchor.x); - anchor.y += (angle === 90 || angle === 270) ? 0 : dx * Math.tan(toRad(angle)); - anchor.x += dx; + var point; + while (parent) { + + // point is assumed to be aligned already + point = points[currentKey]; + + var diff = normalizePoint(point.difference(parent)); + if (!diff.equals(prevDiff)) { + route.unshift(point); + prevDiff = diff; + } + + // parent is assumed to be aligned already + currentKey = getKey(parent); + parent = parents[currentKey]; } - return anchor; + // leadPoint is assumed to be aligned already + var leadPoint = points[currentKey]; + + var fromDiff = normalizePoint(leadPoint.difference(from)); + if (!fromDiff.equals(prevDiff)) { + route.unshift(leadPoint); + } + + return route; } - function _midSide(view, magnet, refPoint, opt) { + // heuristic method to determine the distance between two points + function estimateCost(from, endPoints) { - var rotate = !!opt.rotate; - var bbox, angle, center; - if (rotate) { - bbox = view.getNodeUnrotatedBBox(magnet); - center = view.model.getBBox().center(); - angle = view.model.angle(); - } else { - bbox = view.getNodeBBox(magnet); + var min = Infinity; + + for (var i = 0, len = endPoints.length; i < len; i++) { + var cost = from.manhattanDistance(endPoints[i]); + if (cost < min) { min = cost; } } - var padding = opt.padding; - if (isFinite(padding)) { bbox.inflate(padding); } + return min; + } - if (rotate) { refPoint.rotate(center, angle); } + // find points around the bbox taking given directions into account + // lines are drawn from anchor in given directions, intersections recorded + // if anchor is outside bbox, only those directions that intersect get a rect point + // the anchor itself is returned as rect point (representing some directions) + // (since those directions are unobstructed by the bbox) + function getRectPoints(anchor, bbox, directionList, grid, opt) { - var side = bbox.sideNearestToPoint(refPoint); - var anchor; - switch (side) { - case 'left': - anchor = bbox.leftMiddle(); - break; - case 'right': - anchor = bbox.rightMiddle(); - break; - case 'top': - anchor = bbox.topMiddle(); - break; - case 'bottom': - anchor = bbox.bottomMiddle(); - break; + var precision = opt.precision; + var directionMap = opt.directionMap; + + var anchorCenterVector = anchor.difference(bbox.center()); + + var keys = isObject$1(directionMap) ? Object.keys(directionMap) : []; + var dirList = toArray(directionList); + var rectPoints = keys.reduce(function(res, key) { + + if (dirList.includes(key)) { + var direction = directionMap[key]; + + // create a line that is guaranteed to intersect the bbox if bbox is in the direction + // even if anchor lies outside of bbox + var endpoint = new Point( + anchor.x + direction.x * (Math.abs(anchorCenterVector.x) + bbox.width), + anchor.y + direction.y * (Math.abs(anchorCenterVector.y) + bbox.height) + ); + var intersectionLine = new Line(anchor, endpoint); + + // get the farther intersection, in case there are two + // (that happens if anchor lies next to bbox) + var intersections = intersectionLine.intersect(bbox) || []; + var numIntersections = intersections.length; + var farthestIntersectionDistance; + var farthestIntersection = null; + for (var i = 0; i < numIntersections; i++) { + var currentIntersection = intersections[i]; + var distance = anchor.squaredDistance(currentIntersection); + if ((farthestIntersectionDistance === undefined) || (distance > farthestIntersectionDistance)) { + farthestIntersectionDistance = distance; + farthestIntersection = currentIntersection; + } + } + + // if an intersection was found in this direction, it is our rectPoint + if (farthestIntersection) { + var point = align(farthestIntersection, grid, precision); + + // if the rectPoint lies inside the bbox, offset it by one more step + if (bbox.containsPoint(point)) { + point = align(point.offset(direction.x * grid.x, direction.y * grid.y), grid, precision); + } + + // then add the point to the result array + // aligned + res.push(point); + } + } + + return res; + }, []); + + // if anchor lies outside of bbox, add it to the array of points + if (!bbox.containsPoint(anchor)) { + // aligned + rectPoints.push(align(anchor, grid, precision)); } - return (rotate) ? anchor.rotate(center, -angle) : anchor; + return rectPoints; } - // Can find anchor from model, when there is no selector or the link end - // is connected to a port - function _modelCenter(view, _magnet, _refPoint, opt, endType) { - return view.model.getPointFromConnectedLink(this.model, endType).offset(opt.dx, opt.dy); - } + // finds the route between two points/rectangles (`from`, `to`) implementing A* algorithm + // rectangles get rect points assigned by getRectPoints() + function findRoute(from, to, isPointObstacle, opt) { - //joint.anchors - var center = bboxWrapper('center'); - var top$2 = bboxWrapper('topMiddle'); - var bottom$2 = bboxWrapper('bottomMiddle'); - var left$2 = bboxWrapper('leftMiddle'); - var right$2 = bboxWrapper('rightMiddle'); - var topLeft = bboxWrapper('origin'); - var topRight = bboxWrapper('topRight'); - var bottomLeft = bboxWrapper('bottomLeft'); - var bottomRight = bboxWrapper('corner'); - var perpendicular = resolveRef(_perpendicular); - var midSide = resolveRef(_midSide); - var modelCenter = _modelCenter; + var precision = opt.precision; - var anchors = ({ - center: center, - top: top$2, - bottom: bottom$2, - left: left$2, - right: right$2, - topLeft: topLeft, - topRight: topRight, - bottomLeft: bottomLeft, - bottomRight: bottomRight, - perpendicular: perpendicular, - midSide: midSide, - modelCenter: modelCenter - }); + // Get grid for this route. - var ToolView = View.extend({ - name: null, - tagName: 'g', - className: 'tool', - svgElement: true, - _visible: true, + var sourceAnchor, targetAnchor; - init: function() { - var name = this.name; - if (name) { this.vel.attr('data-tool-name', name); } - }, + if (from instanceof Rect) { // `from` is sourceBBox + sourceAnchor = round$4(getSourceAnchor$1(this, opt).clone(), precision); + } else { + sourceAnchor = round$4(from.clone(), precision); + } - configure: function(view, toolsView) { - this.relatedView = view; - this.paper = view.paper; - this.parentView = toolsView; - this.simulateRelatedView(this.el); - // Delegate events in case the ToolView was removed from the DOM and reused. - this.delegateEvents(); - return this; - }, + if (to instanceof Rect) { // `to` is targetBBox + targetAnchor = round$4(getTargetAnchor$1(this, opt).clone(), precision); + } else { + targetAnchor = round$4(to.clone(), precision); + } - simulateRelatedView: function(el) { - if (el) { el.setAttribute('model-id', this.relatedView.model.id); } - }, + var grid = getGrid(opt.step, sourceAnchor, targetAnchor); - getName: function() { - return this.name; - }, + // Get pathfinding points. - show: function() { - this.el.style.display = ''; - this._visible = true; - }, + var start, end; // aligned with grid by definition + var startPoints, endPoints; // assumed to be aligned with grid already - hide: function() { - this.el.style.display = 'none'; - this._visible = false; - }, + // set of points we start pathfinding from + if (from instanceof Rect) { // `from` is sourceBBox + start = sourceAnchor; + startPoints = getRectPoints(start, from, opt.startDirections, grid, opt); - isVisible: function() { - return !!this._visible; - }, + } else { + start = sourceAnchor; + startPoints = [start]; + } - focus: function() { - var opacity = this.options.focusOpacity; - if (isFinite(opacity)) { this.el.style.opacity = opacity; } - this.parentView.focusTool(this); - }, + // set of points we want the pathfinding to finish at + if (to instanceof Rect) { // `to` is targetBBox + end = targetAnchor; + endPoints = getRectPoints(targetAnchor, to, opt.endDirections, grid, opt); - blur: function() { - this.el.style.opacity = ''; - this.parentView.blurTool(this); - }, + } else { + end = targetAnchor; + endPoints = [end]; + } - update: function() { - // to be overridden - }, + // take into account only accessible rect points (those not under obstacles) + startPoints = startPoints.filter(function (p) { return !isPointObstacle(p); }); + endPoints = endPoints.filter(function (p) { return !isPointObstacle(p); }); - guard: function(evt) { - // Let the context-menu event bubble up to the relatedView - var ref = this; - var paper = ref.paper; - var relatedView = ref.relatedView; - if (!paper || !relatedView) { return true; } - return paper.guard(evt, relatedView); + // Check that there is an accessible route point on both sides. + // Otherwise, use fallbackRoute(). + if (startPoints.length > 0 && endPoints.length > 0) { + + // The set of tentative points to be evaluated, initially containing the start points. + // Rounded to nearest integer for simplicity. + var openSet = new SortedSet(); + // Keeps reference to actual points for given elements of the open set. + var points = {}; + // Keeps reference to a point that is immediate predecessor of given element. + var parents = {}; + // Cost from start to a point along best known path. + var costs = {}; + + for (var i = 0, n = startPoints.length; i < n; i++) { + // startPoint is assumed to be aligned already + var startPoint = startPoints[i]; + + var key = getKey(startPoint); + + openSet.add(key, estimateCost(startPoint, endPoints)); + points[key] = startPoint; + costs[key] = 0; + } + + var previousRouteDirectionAngle = opt.previousDirectionAngle; // undefined for first route + var isPathBeginning = (previousRouteDirectionAngle === undefined); + + // directions + var direction, directionChange; + var directions = opt.directions; + getGridOffsets(directions, grid, opt); + + var numDirections = directions.length; + + var endPointsKeys = toArray(endPoints).reduce(function(res, endPoint) { + // endPoint is assumed to be aligned already + + var key = getKey(endPoint); + res.push(key); + return res; + }, []); + + // main route finding loop + var loopsRemaining = opt.maximumLoops; + while (!openSet.isEmpty() && loopsRemaining > 0) { + + // remove current from the open list + var currentKey = openSet.pop(); + var currentPoint = points[currentKey]; + var currentParent = parents[currentKey]; + var currentCost = costs[currentKey]; + + var isRouteBeginning = (currentParent === undefined); // undefined for route starts + var isStart = currentPoint.equals(start); // (is source anchor or `from` point) = can leave in any direction + + var previousDirectionAngle; + if (!isRouteBeginning) { previousDirectionAngle = getDirectionAngle(currentParent, currentPoint, numDirections, grid, opt); } // a vertex on the route + else if (!isPathBeginning) { previousDirectionAngle = previousRouteDirectionAngle; } // beginning of route on the path + else if (!isStart) { previousDirectionAngle = getDirectionAngle(start, currentPoint, numDirections, grid, opt); } // beginning of path, start rect point + else { previousDirectionAngle = null; } // beginning of path, source anchor or `from` point + + // check if we reached any endpoint + var samePoints = startPoints.length === endPoints.length; + if (samePoints) { + for (var j = 0; j < startPoints.length; j++) { + if (!startPoints[j].equals(endPoints[j])) { + samePoints = false; + break; + } + } + } + var skipEndCheck = (isRouteBeginning && samePoints); + if (!skipEndCheck && (endPointsKeys.indexOf(currentKey) >= 0)) { + opt.previousDirectionAngle = previousDirectionAngle; + return reconstructRoute(parents, points, currentPoint, start, end, grid, opt); + } + + // go over all possible directions and find neighbors + for (i = 0; i < numDirections; i++) { + direction = directions[i]; + + var directionAngle = direction.angle; + directionChange = getDirectionChange(previousDirectionAngle, directionAngle); + + // if the direction changed rapidly, don't use this point + // any direction is allowed for starting points + if (!(isPathBeginning && isStart) && directionChange > opt.maxAllowedDirectionChange) { continue; } + + var neighborPoint = align(currentPoint.clone().offset(direction.gridOffsetX, direction.gridOffsetY), grid, precision); + var neighborKey = getKey(neighborPoint); + + // Closed points from the openSet were already evaluated. + if (openSet.isClose(neighborKey) || isPointObstacle(neighborPoint)) { continue; } + + // We can only enter end points at an acceptable angle. + if (endPointsKeys.indexOf(neighborKey) >= 0) { // neighbor is an end point + + var isNeighborEnd = neighborPoint.equals(end); // (is target anchor or `to` point) = can be entered in any direction + + if (!isNeighborEnd) { + var endDirectionAngle = getDirectionAngle(neighborPoint, end, numDirections, grid, opt); + var endDirectionChange = getDirectionChange(directionAngle, endDirectionAngle); + + if (endDirectionChange > opt.maxAllowedDirectionChange) { continue; } + } + } + + // The current direction is ok. + + var neighborCost = direction.cost; + var neighborPenalty = isStart ? 0 : opt.penalties[directionChange]; // no penalties for start point + var costFromStart = currentCost + neighborCost + neighborPenalty; + + if (!openSet.isOpen(neighborKey) || (costFromStart < costs[neighborKey])) { + // neighbor point has not been processed yet + // or the cost of the path from start is lower than previously calculated + + points[neighborKey] = neighborPoint; + parents[neighborKey] = currentPoint; + costs[neighborKey] = costFromStart; + openSet.add(neighborKey, costFromStart + estimateCost(neighborPoint, endPoints)); + } + } + + loopsRemaining--; + } } - }); - function getAnchor(coords, view, magnet) { - // take advantage of an existing logic inside of the - // pin relative connection strategy - var end = pinRelative.call( - this.paper, - {}, - view, - magnet, - coords, - this.model - ); - return end.anchor; + // no route found (`to` point either wasn't accessible or finding route took + // way too much calculation) + return opt.fallbackRoute.call(this, start, end, opt); } - function snapAnchor(coords, view, magnet, type, relatedView, toolView) { - var snapRadius = toolView.options.snapRadius; - var isSource = (type === 'source'); - var refIndex = (isSource ? 0 : -1); - var ref = this.model.vertex(refIndex) || this.getEndAnchor(isSource ? 'target' : 'source'); - if (ref) { - if (Math.abs(ref.x - coords.x) < snapRadius) { coords.x = ref.x; } - if (Math.abs(ref.y - coords.y) < snapRadius) { coords.y = ref.y; } + // resolve some of the options + function resolveOptions(opt) { + + opt.directions = result(opt, 'directions'); + opt.penalties = result(opt, 'penalties'); + opt.paddingBox = result(opt, 'paddingBox'); + opt.padding = result(opt, 'padding'); + + if (opt.padding) { + // if both provided, opt.padding wins over opt.paddingBox + var sides = normalizeSides(opt.padding); + opt.paddingBox = { + x: -sides.left, + y: -sides.top, + width: sides.left + sides.right, + height: sides.top + sides.bottom + }; } - return coords; - } - function getViewBBox(view, useModelGeometry) { - var model = view.model; - if (useModelGeometry) { return model.getBBox(); } - return (model.isLink()) ? view.getConnection().bbox() : view.getNodeUnrotatedBBox(view.el); + toArray(opt.directions).forEach(function(direction) { + + var point1 = new Point(0, 0); + var point2 = new Point(direction.offsetX, direction.offsetY); + + direction.angle = normalizeAngle(point1.theta(point2)); + }); } - // Vertex Handles - var VertexHandle = View.extend({ - tagName: 'circle', - svgElement: true, - className: 'marker-vertex', - events: { - mousedown: 'onPointerDown', - touchstart: 'onPointerDown', - dblclick: 'onDoubleClick', - dbltap: 'onDoubleClick' - }, - documentEvents: { - mousemove: 'onPointerMove', - touchmove: 'onPointerMove', - mouseup: 'onPointerUp', - touchend: 'onPointerUp', - touchcancel: 'onPointerUp' - }, - attributes: { - 'r': 6, - 'fill': '#33334F', - 'stroke': '#FFFFFF', - 'stroke-width': 2, - 'cursor': 'move' - }, - position: function(x, y) { - this.vel.attr({ cx: x, cy: y }); - }, - onPointerDown: function(evt) { - if (this.options.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - this.options.paper.undelegateEvents(); - this.delegateDocumentEvents(null, evt.data); - this.trigger('will-change', this, evt); - }, - onPointerMove: function(evt) { - this.trigger('changing', this, evt); - }, - onDoubleClick: function(evt) { - this.trigger('remove', this, evt); - }, - onPointerUp: function(evt) { - this.trigger('changed', this, evt); - this.undelegateDocumentEvents(); - this.options.paper.delegateEvents(); + // initialization of the route finding + function router(vertices, opt, linkView) { + + resolveOptions(opt); + + // enable/disable linkView perpendicular option + linkView.options.perpendicular = !!opt.perpendicular; + + var sourceBBox = getSourceBBox$1(linkView, opt); + var targetBBox = getTargetBBox$1(linkView, opt); + + var sourceAnchor = getSourceAnchor$1(linkView, opt); + //var targetAnchor = getTargetAnchor(linkView, opt); + + // pathfinding + var isPointObstacle; + if (typeof opt.isPointObstacle === 'function') { + isPointObstacle = opt.isPointObstacle; + } else { + var map = new ObstacleMap(opt); + map.build(linkView.paper.model, linkView.model); + isPointObstacle = function (point) { return !map.isPointAccessible(point); }; } - }); - var Vertices = ToolView.extend({ - name: 'vertices', - options: { - handleClass: VertexHandle, - snapRadius: 20, - redundancyRemoval: true, - vertexAdding: true, - stopPropagation: true - }, - children: [{ - tagName: 'path', - selector: 'connection', - className: 'joint-vertices-path', - attributes: { - 'fill': 'none', - 'stroke': 'transparent', - 'stroke-width': 10, - 'cursor': 'cell' - } - }], - handles: null, - events: { - 'mousedown .joint-vertices-path': 'onPathPointerDown', - 'touchstart .joint-vertices-path': 'onPathPointerDown' - }, - onRender: function() { - if (this.options.vertexAdding) { - this.renderChildren(); - this.updatePath(); - } - this.resetHandles(); - this.renderHandles(); - return this; - }, - update: function() { - var relatedView = this.relatedView; - var vertices = relatedView.model.vertices(); - if (vertices.length === this.handles.length) { - this.updateHandles(); - } else { - this.resetHandles(); - this.renderHandles(); - } - if (this.options.vertexAdding) { - this.updatePath(); - } - return this; - }, - resetHandles: function() { - var handles = this.handles; - this.handles = []; - this.stopListening(); - if (!Array.isArray(handles)) { return; } - for (var i = 0, n = handles.length; i < n; i++) { - handles[i].remove(); - } - }, - renderHandles: function() { - var this$1 = this; - - var relatedView = this.relatedView; - var vertices = relatedView.model.vertices(); - for (var i = 0, n = vertices.length; i < n; i++) { - var vertex = vertices[i]; - var handle = new (this.options.handleClass)({ - index: i, - paper: this.paper, - guard: function (evt) { return this$1.guard(evt); } - }); - handle.render(); - handle.position(vertex.x, vertex.y); - this.simulateRelatedView(handle.el); - handle.vel.appendTo(this.el); - this.handles.push(handle); - this.startHandleListening(handle); - } - }, - updateHandles: function() { - var relatedView = this.relatedView; - var vertices = relatedView.model.vertices(); - for (var i = 0, n = vertices.length; i < n; i++) { - var vertex = vertices[i]; - var handle = this.handles[i]; - if (!handle) { return; } - handle.position(vertex.x, vertex.y); - } - }, - updatePath: function() { - var connection = this.childNodes.connection; - if (connection) { connection.setAttribute('d', this.relatedView.getSerializedConnection()); } - }, - startHandleListening: function(handle) { - var relatedView = this.relatedView; - if (relatedView.can('vertexMove')) { - this.listenTo(handle, 'will-change', this.onHandleWillChange); - this.listenTo(handle, 'changing', this.onHandleChanging); - this.listenTo(handle, 'changed', this.onHandleChanged); - } - if (relatedView.can('vertexRemove')) { - this.listenTo(handle, 'remove', this.onHandleRemove); - } - }, - getNeighborPoints: function(index) { - var linkView = this.relatedView; - var vertices = linkView.model.vertices(); - var prev = (index > 0) ? vertices[index - 1] : linkView.sourceAnchor; - var next = (index < vertices.length - 1) ? vertices[index + 1] : linkView.targetAnchor; - return { - prev: new Point(prev), - next: new Point(next) - }; - }, - onHandleWillChange: function(_handle, evt) { - this.focus(); - var ref = this; - var relatedView = ref.relatedView; - var options = ref.options; - relatedView.model.startBatch('vertex-move', { ui: true, tool: this.cid }); - if (!options.stopPropagation) { relatedView.notifyPointerdown.apply(relatedView, relatedView.paper.getPointerArgs(evt)); } - }, - onHandleChanging: function(handle, evt) { - var ref = this; - var options = ref.options; - var linkView = ref.relatedView; - var index = handle.options.index; - var ref$1 = linkView.paper.getPointerArgs(evt); - var normalizedEvent = ref$1[0]; - var x = ref$1[1]; - var y = ref$1[2]; - var vertex = { x: x, y: y }; - this.snapVertex(vertex, index); - linkView.model.vertex(index, vertex, { ui: true, tool: this.cid }); - handle.position(vertex.x, vertex.y); - if (!options.stopPropagation) { linkView.notifyPointermove(normalizedEvent, x, y); } - }, - onHandleChanged: function(_handle, evt) { - var ref = this; - var options = ref.options; - var linkView = ref.relatedView; - if (options.vertexAdding) { this.updatePath(); } - if (!options.redundancyRemoval) { return; } - var verticesRemoved = linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); - if (verticesRemoved) { this.render(); } - this.blur(); - linkView.model.stopBatch('vertex-move', { ui: true, tool: this.cid }); - if (this.eventData(evt).vertexAdded) { - linkView.model.stopBatch('vertex-add', { ui: true, tool: this.cid }); - } - var ref$1 = linkView.paper.getPointerArgs(evt); - var normalizedEvt = ref$1[0]; - var x = ref$1[1]; - var y = ref$1[2]; - if (!options.stopPropagation) { linkView.notifyPointerup(normalizedEvt, x, y); } - linkView.checkMouseleave(normalizedEvt); - }, - snapVertex: function(vertex, index) { - var snapRadius = this.options.snapRadius; - if (snapRadius > 0) { - var neighbors = this.getNeighborPoints(index); - var prev = neighbors.prev; - var next = neighbors.next; - if (Math.abs(vertex.x - prev.x) < snapRadius) { - vertex.x = prev.x; - } else if (Math.abs(vertex.x - next.x) < snapRadius) { - vertex.x = next.x; - } - if (Math.abs(vertex.y - prev.y) < snapRadius) { - vertex.y = neighbors.prev.y; - } else if (Math.abs(vertex.y - next.y) < snapRadius) { - vertex.y = next.y; - } - } - }, - onHandleRemove: function(handle, evt) { - var index$1 = handle.options.index; - var linkView = this.relatedView; - linkView.model.removeVertex(index$1, { ui: true }); - if (this.options.vertexAdding) { this.updatePath(); } - linkView.checkMouseleave(normalizeEvent(evt)); - }, - onPathPointerDown: function(evt) { - if (this.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - var normalizedEvent = normalizeEvent(evt); - var vertex = this.paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY).toJSON(); - var relatedView = this.relatedView; - relatedView.model.startBatch('vertex-add', { ui: true, tool: this.cid }); - var index$1 = relatedView.getVertexIndex(vertex.x, vertex.y); - this.snapVertex(vertex, index$1); - relatedView.model.insertVertex(index$1, vertex, { ui: true, tool: this.cid }); - this.update(); - var handle = this.handles[index$1]; - this.eventData(normalizedEvent, { vertexAdded: true }); - handle.onPointerDown(normalizedEvent); - }, - onRemove: function() { - this.resetHandles(); - } - }, { - VertexHandle: VertexHandle // keep as class property - }); - - var SegmentHandle = View.extend({ - tagName: 'g', - svgElement: true, - className: 'marker-segment', - events: { - mousedown: 'onPointerDown', - touchstart: 'onPointerDown' - }, - documentEvents: { - mousemove: 'onPointerMove', - touchmove: 'onPointerMove', - mouseup: 'onPointerUp', - touchend: 'onPointerUp', - touchcancel: 'onPointerUp' - }, - children: [{ - tagName: 'line', - selector: 'line', - attributes: { - 'stroke': '#33334F', - 'stroke-width': 2, - 'fill': 'none', - 'pointer-events': 'none' - } - }, { - tagName: 'rect', - selector: 'handle', - attributes: { - 'width': 20, - 'height': 8, - 'x': -10, - 'y': -4, - 'rx': 4, - 'ry': 4, - 'fill': '#33334F', - 'stroke': '#FFFFFF', - 'stroke-width': 2 - } - }], - onRender: function() { - this.renderChildren(); - }, - position: function(x, y, angle, view) { - - var matrix = V.createSVGMatrix().translate(x, y).rotate(angle); - var handle = this.childNodes.handle; - handle.setAttribute('transform', V.matrixToTransformString(matrix)); - handle.setAttribute('cursor', (angle % 180 === 0) ? 'row-resize' : 'col-resize'); - - var viewPoint = view.getClosestPoint(new Point(x, y)); - var line = this.childNodes.line; - line.setAttribute('x1', x); - line.setAttribute('y1', y); - line.setAttribute('x2', viewPoint.x); - line.setAttribute('y2', viewPoint.y); - }, - onPointerDown: function(evt) { - if (this.options.guard(evt)) { return; } - this.trigger('change:start', this, evt); - evt.stopPropagation(); - evt.preventDefault(); - this.options.paper.undelegateEvents(); - this.delegateDocumentEvents(null, evt.data); - }, - onPointerMove: function(evt) { - this.trigger('changing', this, evt); - }, - onPointerUp: function(evt) { - this.undelegateDocumentEvents(); - this.options.paper.delegateEvents(); - this.trigger('change:end', this, evt); - }, - show: function() { - this.el.style.display = ''; - }, - hide: function() { - this.el.style.display = 'none'; - } - }); - - var Segments = ToolView.extend({ - name: 'segments', - precision: .5, - options: { - handleClass: SegmentHandle, - segmentLengthThreshold: 40, - redundancyRemoval: true, - anchor: getAnchor, - snapRadius: 10, - snapHandle: true, - stopPropagation: true - }, - handles: null, - onRender: function() { - this.resetHandles(); - var relatedView = this.relatedView; - var vertices = relatedView.model.vertices(); - vertices.unshift(relatedView.sourcePoint); - vertices.push(relatedView.targetPoint); - for (var i = 0, n = vertices.length; i < n - 1; i++) { - var vertex = vertices[i]; - var nextVertex = vertices[i + 1]; - var handle = this.renderHandle(vertex, nextVertex); - this.simulateRelatedView(handle.el); - this.handles.push(handle); - handle.options.index = i; - } - return this; - }, - renderHandle: function(vertex, nextVertex) { - var this$1 = this; - - var handle = new (this.options.handleClass)({ - paper: this.paper, - guard: function (evt) { return this$1.guard(evt); } - }); - handle.render(); - this.updateHandle(handle, vertex, nextVertex); - handle.vel.appendTo(this.el); - this.startHandleListening(handle); - return handle; - }, - update: function() { - this.render(); - return this; - }, - startHandleListening: function(handle) { - this.listenTo(handle, 'change:start', this.onHandleChangeStart); - this.listenTo(handle, 'changing', this.onHandleChanging); - this.listenTo(handle, 'change:end', this.onHandleChangeEnd); - }, - resetHandles: function() { - var handles = this.handles; - this.handles = []; - this.stopListening(); - if (!Array.isArray(handles)) { return; } - for (var i = 0, n = handles.length; i < n; i++) { - handles[i].remove(); - } - }, - shiftHandleIndexes: function(value) { - var handles = this.handles; - for (var i = 0, n = handles.length; i < n; i++) { handles[i].options.index += value; } - }, - resetAnchor: function(type, anchor) { - var relatedModel = this.relatedView.model; - if (anchor) { - relatedModel.prop([type, 'anchor'], anchor, { - rewrite: true, - ui: true, - tool: this.cid - }); - } else { - relatedModel.removeProp([type, 'anchor'], { - ui: true, - tool: this.cid - }); - } - }, - snapHandle: function(handle, position, data) { - - var index = handle.options.index; - var linkView = this.relatedView; - var link = linkView.model; - var vertices = link.vertices(); - var axis = handle.options.axis; - var prev = vertices[index - 2] || data.sourceAnchor; - var next = vertices[index + 1] || data.targetAnchor; - var snapRadius = this.options.snapRadius; - if (Math.abs(position[axis] - prev[axis]) < snapRadius) { - position[axis] = prev[axis]; - } else if (Math.abs(position[axis] - next[axis]) < snapRadius) { - position[axis] = next[axis]; - } - return position; - }, - - onHandleChanging: function(handle, evt) { - - var ref = this; - var options = ref.options; - var data = this.eventData(evt); - var relatedView = this.relatedView; - var paper = relatedView.paper; - var index$1 = handle.options.index - 1; - var normalizedEvent = normalizeEvent(evt); - var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - var position = this.snapHandle(handle, coords.clone(), data); - var axis = handle.options.axis; - var offset = (this.options.snapHandle) ? 0 : (coords[axis] - position[axis]); - var link = relatedView.model; - var vertices = cloneDeep(link.vertices()); - var vertex = vertices[index$1]; - var nextVertex = vertices[index$1 + 1]; - var anchorFn = this.options.anchor; - if (typeof anchorFn !== 'function') { anchorFn = null; } - - // First Segment - var sourceView = relatedView.sourceView; - var sourceBBox = relatedView.sourceBBox; - var changeSourceAnchor = false; - var deleteSourceAnchor = false; - if (!vertex) { - vertex = relatedView.sourceAnchor.toJSON(); - vertex[axis] = position[axis]; - if (sourceBBox.containsPoint(vertex)) { - vertex[axis] = position[axis]; - changeSourceAnchor = true; - } else { - // we left the area of the source magnet for the first time - vertices.unshift(vertex); - this.shiftHandleIndexes(1); - deleteSourceAnchor = true; - } - } else if (index$1 === 0) { - if (sourceBBox.containsPoint(vertex)) { - vertices.shift(); - this.shiftHandleIndexes(-1); - changeSourceAnchor = true; - } else { - vertex[axis] = position[axis]; - deleteSourceAnchor = true; - } - } else { - vertex[axis] = position[axis]; - } - - if (anchorFn && sourceView) { - if (changeSourceAnchor) { - var sourceAnchorPosition = data.sourceAnchor.clone(); - sourceAnchorPosition[axis] = position[axis]; - var sourceAnchor = anchorFn.call(relatedView, sourceAnchorPosition, sourceView, relatedView.sourceMagnet || sourceView.el, 'source', relatedView); - this.resetAnchor('source', sourceAnchor); - } - if (deleteSourceAnchor) { - this.resetAnchor('source', data.sourceAnchorDef); - } - } - - // Last segment - var targetView = relatedView.targetView; - var targetBBox = relatedView.targetBBox; - var changeTargetAnchor = false; - var deleteTargetAnchor = false; - if (!nextVertex) { - nextVertex = relatedView.targetAnchor.toJSON(); - nextVertex[axis] = position[axis]; - if (targetBBox.containsPoint(nextVertex)) { - changeTargetAnchor = true; - } else { - // we left the area of the target magnet for the first time - vertices.push(nextVertex); - deleteTargetAnchor = true; - } - } else if (index$1 === vertices.length - 2) { - if (targetBBox.containsPoint(nextVertex)) { - vertices.pop(); - changeTargetAnchor = true; - } else { - nextVertex[axis] = position[axis]; - deleteTargetAnchor = true; - } - } else { - nextVertex[axis] = position[axis]; - } - - if (anchorFn && targetView) { - if (changeTargetAnchor) { - var targetAnchorPosition = data.targetAnchor.clone(); - targetAnchorPosition[axis] = position[axis]; - var targetAnchor = anchorFn.call(relatedView, targetAnchorPosition, targetView, relatedView.targetMagnet || targetView.el, 'target', relatedView); - this.resetAnchor('target', targetAnchor); - } - if (deleteTargetAnchor) { - this.resetAnchor('target', data.targetAnchorDef); - } - } - - link.vertices(vertices, { ui: true, tool: this.cid }); - this.updateHandle(handle, vertex, nextVertex, offset); - if (!options.stopPropagation) { relatedView.notifyPointermove(normalizedEvent, coords.x, coords.y); } - }, - onHandleChangeStart: function(handle, evt) { - var ref = this; - var options = ref.options; - var handles = ref.handles; - var linkView = ref.relatedView; - var model = linkView.model; - var paper = linkView.paper; - var index$1 = handle.options.index; - if (!Array.isArray(handles)) { return; } - for (var i = 0, n = handles.length; i < n; i++) { - if (i !== index$1) { handles[i].hide(); } - } - this.focus(); - this.eventData(evt, { - sourceAnchor: linkView.sourceAnchor.clone(), - targetAnchor: linkView.targetAnchor.clone(), - sourceAnchorDef: clone(model.prop(['source', 'anchor'])), - targetAnchorDef: clone(model.prop(['target', 'anchor'])) - }); - model.startBatch('segment-move', { ui: true, tool: this.cid }); - if (!options.stopPropagation) { linkView.notifyPointerdown.apply(linkView, paper.getPointerArgs(evt)); } - }, - onHandleChangeEnd: function(_handle, evt) { - var ref= this; - var options = ref.options; - var linkView = ref.relatedView; - var paper = linkView.paper; - var model = linkView.model; - if (options.redundancyRemoval) { - linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); - } - var normalizedEvent = normalizeEvent(evt); - var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - this.render(); - this.blur(); - model.stopBatch('segment-move', { ui: true, tool: this.cid }); - if (!options.stopPropagation) { linkView.notifyPointerup(normalizedEvent, coords.x, coords.y); } - linkView.checkMouseleave(normalizedEvent); - }, - updateHandle: function(handle, vertex, nextVertex, offset) { - var vertical = Math.abs(vertex.x - nextVertex.x) < this.precision; - var horizontal = Math.abs(vertex.y - nextVertex.y) < this.precision; - if (vertical || horizontal) { - var segmentLine = new Line(vertex, nextVertex); - var length = segmentLine.length(); - if (length < this.options.segmentLengthThreshold) { - handle.hide(); - } else { - var position = segmentLine.midpoint(); - var axis = (vertical) ? 'x' : 'y'; - position[axis] += offset || 0; - var angle = segmentLine.vector().vectorAngle(new Point(1, 0)); - handle.position(position.x, position.y, angle, this.relatedView); - handle.show(); - handle.options.axis = axis; - } - } else { - handle.hide(); - } - }, - onRemove: function() { - this.resetHandles(); - } - }, { - SegmentHandle: SegmentHandle // keep as class property - }); - - // End Markers - var Arrowhead = ToolView.extend({ - tagName: 'path', - xAxisVector: new Point(1, 0), - events: { - mousedown: 'onPointerDown', - touchstart: 'onPointerDown' - }, - documentEvents: { - mousemove: 'onPointerMove', - touchmove: 'onPointerMove', - mouseup: 'onPointerUp', - touchend: 'onPointerUp', - touchcancel: 'onPointerUp' - }, - onRender: function() { - this.update(); - }, - update: function() { - var ratio = this.ratio; - var view = this.relatedView; - var tangent = view.getTangentAtRatio(ratio); - var position, angle; - if (tangent) { - position = tangent.start; - angle = tangent.vector().vectorAngle(this.xAxisVector) || 0; - } else { - position = view.getPointAtRatio(ratio); - angle = 0; - } - if (!position) { return this; } - var matrix = V.createSVGMatrix().translate(position.x, position.y).rotate(angle); - this.vel.transform(matrix, { absolute: true }); - return this; - }, - onPointerDown: function(evt) { - if (this.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - var relatedView = this.relatedView; - relatedView.model.startBatch('arrowhead-move', { ui: true, tool: this.cid }); - if (relatedView.can('arrowheadMove')) { - relatedView.startArrowheadMove(this.arrowheadType); - this.delegateDocumentEvents(); - relatedView.paper.undelegateEvents(); - } - this.focus(); - this.el.style.pointerEvents = 'none'; - }, - onPointerMove: function(evt) { - var normalizedEvent = normalizeEvent(evt); - var coords = this.paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - this.relatedView.pointermove(normalizedEvent, coords.x, coords.y); - }, - onPointerUp: function(evt) { - this.undelegateDocumentEvents(); - var relatedView = this.relatedView; - var paper = relatedView.paper; - var normalizedEvent = normalizeEvent(evt); - var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - relatedView.pointerup(normalizedEvent, coords.x, coords.y); - paper.delegateEvents(); - this.blur(); - this.el.style.pointerEvents = ''; - relatedView.model.stopBatch('arrowhead-move', { ui: true, tool: this.cid }); - } - }); - - var TargetArrowhead = Arrowhead.extend({ - name: 'target-arrowhead', - ratio: 1, - arrowheadType: 'target', - attributes: { - 'd': 'M -10 -8 10 0 -10 8 Z', - 'fill': '#33334F', - 'stroke': '#FFFFFF', - 'stroke-width': 2, - 'cursor': 'move', - 'class': 'target-arrowhead' - } - }); - - var SourceArrowhead = Arrowhead.extend({ - name: 'source-arrowhead', - ratio: 0, - arrowheadType: 'source', - attributes: { - 'd': 'M 10 -8 -10 0 10 8 Z', - 'fill': '#33334F', - 'stroke': '#FFFFFF', - 'stroke-width': 2, - 'cursor': 'move', - 'class': 'source-arrowhead' - } - }); - - var Button = ToolView.extend({ - name: 'button', - events: { - 'mousedown': 'onPointerDown', - 'touchstart': 'onPointerDown' - }, - options: { - distance: 0, - offset: 0, - rotate: false - }, - onRender: function() { - this.renderChildren(this.options.markup); - this.update(); - }, - update: function() { - this.position(); - return this; - }, - position: function() { - var ref = this; - var view = ref.relatedView; - var vel = ref.vel; - var matrix = view.model.isLink() ? this.getLinkMatrix() : this.getElementMatrix(); - vel.transform(matrix, { absolute: true }); - }, - getElementMatrix: function getElementMatrix() { - var ref = this; - var view = ref.relatedView; - var options = ref.options; - var x = options.x; if ( x === void 0 ) x = 0; - var y = options.y; if ( y === void 0 ) y = 0; - var offset = options.offset; if ( offset === void 0 ) offset = {}; - var useModelGeometry = options.useModelGeometry; - var rotate = options.rotate; - var bbox = getViewBBox(view, useModelGeometry); - var angle = view.model.angle(); - if (!rotate) { bbox = bbox.bbox(angle); } - var offsetX = offset.x; if ( offsetX === void 0 ) offsetX = 0; - var offsetY = offset.y; if ( offsetY === void 0 ) offsetY = 0; - if (isPercentage(x)) { - x = parseFloat(x) / 100 * bbox.width; - } - if (isPercentage(y)) { - y = parseFloat(y) / 100 * bbox.height; - } - var matrix = V.createSVGMatrix().translate(bbox.x + bbox.width / 2, bbox.y + bbox.height / 2); - if (rotate) { matrix = matrix.rotate(angle); } - matrix = matrix.translate(x + offsetX - bbox.width / 2, y + offsetY - bbox.height / 2); - return matrix; - }, - getLinkMatrix: function getLinkMatrix() { - var ref = this; - var view = ref.relatedView; - var options = ref.options; - var offset = options.offset; if ( offset === void 0 ) offset = 0; - var distance = options.distance; if ( distance === void 0 ) distance = 0; - var rotate = options.rotate; - var tangent, position, angle; - if (isPercentage(distance)) { - tangent = view.getTangentAtRatio(parseFloat(distance) / 100); - } else { - tangent = view.getTangentAtLength(distance); - } - if (tangent) { - position = tangent.start; - angle = tangent.vector().vectorAngle(new Point(1, 0)) || 0; - } else { - position = view.getConnection().start; - angle = 0; - } - var matrix = V.createSVGMatrix() - .translate(position.x, position.y) - .rotate(angle) - .translate(0, offset); - if (!rotate) { matrix = matrix.rotate(-angle); } - return matrix; - }, - onPointerDown: function(evt) { - if (this.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - var actionFn = this.options.action; - if (typeof actionFn === 'function') { - actionFn.call(this.relatedView, evt, this.relatedView, this); - } - } - }); - - - var Remove = Button.extend({ - children: [{ - tagName: 'circle', - selector: 'button', - attributes: { - 'r': 7, - 'fill': '#FF1D00', - 'cursor': 'pointer' - } - }, { - tagName: 'path', - selector: 'icon', - attributes: { - 'd': 'M -3 -3 3 3 M -3 3 3 -3', - 'fill': 'none', - 'stroke': '#FFFFFF', - 'stroke-width': 2, - 'pointer-events': 'none' - } - }], - options: { - distance: 60, - offset: 0, - action: function(evt, view, tool) { - view.model.remove({ ui: true, tool: tool.cid }); - } - } - }); - - var Connect = Button.extend({ - name: 'connect', - documentEvents: { - mousemove: 'drag', - touchmove: 'drag', - mouseup: 'dragend', - touchend: 'dragend', - touchcancel: 'dragend' - }, - children: [{ - tagName: 'circle', - selector: 'button', - attributes: { - 'r': 7, - 'fill': '#333333', - 'cursor': 'pointer' - } - }, { - tagName: 'path', - selector: 'icon', - attributes: { - 'd': 'M -4 -1 L 0 -1 L 0 -4 L 4 0 L 0 4 0 1 -4 1 z', - 'fill': '#FFFFFF', - 'stroke': 'none', - 'stroke-width': 2, - 'pointer-events': 'none' - } - }], - options: { - distance: 80, - offset: 0, - magnet: function (view) { return view.el; }, - action: function (evt, _view, tool) { return tool.dragstart(evt); }, - }, - getMagnetNode: function() { - var assign; - - var ref = this; - var options = ref.options; - var relatedView = ref.relatedView; - var magnet = options.magnet; - var magnetNode; - switch (typeof magnet) { - case 'function': { - magnetNode = magnet.call(this, relatedView, this); - break; - } - case 'string': { - (assign = relatedView.findBySelector(magnet), magnetNode = assign[0]); - break; - } - default: { - magnetNode = magnet; - break; - } - } - if (!magnetNode) { magnetNode = relatedView.el; } - if (magnetNode instanceof SVGElement) { return magnetNode; } - throw new Error('Connect: magnet must be an SVGElement'); - }, - dragstart: function(evt) { - var ref = this; - var paper = ref.paper; - var relatedView = ref.relatedView; - var normalizedEvent = normalizeEvent(evt); - var ref$1 = paper.clientToLocalPoint(normalizedEvent.clientX, normalizedEvent.clientY); - var x = ref$1.x; - var y = ref$1.y; - relatedView.dragLinkStart(normalizedEvent, this.getMagnetNode(), x, y); - paper.undelegateEvents(); - this.delegateDocumentEvents(null, evt.data); - this.focus(); - }, - drag: function(evt) { - var ref = this; - var paper = ref.paper; - var relatedView = ref.relatedView; - var normalizedEvent = normalizeEvent(evt); - var ref$1 = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - var x = ref$1.x; - var y = ref$1.y; - relatedView.dragLink(normalizedEvent, x, y); - }, - dragend: function(evt) { - var ref = this; - var paper = ref.paper; - var relatedView = ref.relatedView; - var normalizedEvent = normalizeEvent(evt); - var ref$1 = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - var x = ref$1.x; - var y = ref$1.y; - relatedView.dragLinkEnd(normalizedEvent, x, y); - this.undelegateDocumentEvents(); - paper.delegateEvents(); - this.blur(); - relatedView.checkMouseleave(normalizedEvent); - } - }); - - - var Boundary = ToolView.extend({ - name: 'boundary', - tagName: 'rect', - options: { - padding: 10, - useModelGeometry: false, - }, - attributes: { - 'fill': 'none', - 'stroke': '#33334F', - 'stroke-width': .5, - 'stroke-dasharray': '5, 5', - 'pointer-events': 'none' - }, - onRender: function() { - this.update(); - }, - update: function() { - var ref = this; - var view = ref.relatedView; - var options = ref.options; - var vel = ref.vel; - var useModelGeometry = options.useModelGeometry; - var rotate = options.rotate; - var padding = normalizeSides(options.padding); - var bbox = getViewBBox(view, useModelGeometry).moveAndExpand({ - x: -padding.left, - y: -padding.top, - width: padding.left + padding.right, - height: padding.top + padding.bottom - }); - var model = view.model; - if (model.isElement()) { - var angle = model.angle(); - if (angle) { - if (rotate) { - var origin = model.getBBox().center(); - vel.rotate(angle, origin.x, origin.y, { absolute: true }); - } else { - bbox = bbox.bbox(angle); - } - } - } - vel.attr(bbox.toJSON()); - return this; - } - }); - - var Anchor = ToolView.extend({ - tagName: 'g', - type: null, - children: [{ - tagName: 'circle', - selector: 'anchor', - attributes: { - 'cursor': 'pointer' - } - }, { - tagName: 'rect', - selector: 'area', - attributes: { - 'pointer-events': 'none', - 'fill': 'none', - 'stroke': '#33334F', - 'stroke-dasharray': '2,4', - 'rx': 5, - 'ry': 5 - } - }], - events: { - mousedown: 'onPointerDown', - touchstart: 'onPointerDown', - dblclick: 'onPointerDblClick', - dbltap: 'onPointerDblClick' - }, - documentEvents: { - mousemove: 'onPointerMove', - touchmove: 'onPointerMove', - mouseup: 'onPointerUp', - touchend: 'onPointerUp', - touchcancel: 'onPointerUp' - }, - options: { - snap: snapAnchor, - anchor: getAnchor, - resetAnchor: true, - customAnchorAttributes: { - 'stroke-width': 4, - 'stroke': '#33334F', - 'fill': '#FFFFFF', - 'r': 5 - }, - defaultAnchorAttributes: { - 'stroke-width': 2, - 'stroke': '#FFFFFF', - 'fill': '#33334F', - 'r': 6 - }, - areaPadding: 6, - snapRadius: 10, - restrictArea: true, - redundancyRemoval: true - }, - onRender: function() { - this.renderChildren(); - this.toggleArea(false); - this.update(); - }, - update: function() { - var type = this.type; - var relatedView = this.relatedView; - var view = relatedView.getEndView(type); - if (view) { - this.updateAnchor(); - this.updateArea(); - this.el.style.display = ''; - } else { - this.el.style.display = 'none'; - } - return this; - }, - updateAnchor: function() { - var childNodes = this.childNodes; - if (!childNodes) { return; } - var anchorNode = childNodes.anchor; - if (!anchorNode) { return; } - var relatedView = this.relatedView; - var type = this.type; - var position = relatedView.getEndAnchor(type); - var options = this.options; - var customAnchor = relatedView.model.prop([type, 'anchor']); - anchorNode.setAttribute('transform', 'translate(' + position.x + ',' + position.y + ')'); - var anchorAttributes = (customAnchor) ? options.customAnchorAttributes : options.defaultAnchorAttributes; - for (var attrName in anchorAttributes) { - anchorNode.setAttribute(attrName, anchorAttributes[attrName]); - } - }, - updateArea: function() { - var childNodes = this.childNodes; - if (!childNodes) { return; } - var areaNode = childNodes.area; - if (!areaNode) { return; } - var relatedView = this.relatedView; - var type = this.type; - var view = relatedView.getEndView(type); - var model = view.model; - var magnet = relatedView.getEndMagnet(type); - var padding = this.options.areaPadding; - if (!isFinite(padding)) { padding = 0; } - var bbox, angle, center; - if (view.isNodeConnection(magnet)) { - bbox = view.getBBox(); - angle = 0; - center = bbox.center(); - } else { - bbox = view.getNodeUnrotatedBBox(magnet); - angle = model.angle(); - center = bbox.center(); - if (angle) { center.rotate(model.getBBox().center(), -angle); } - // TODO: get the link's magnet rotation into account - } - bbox.inflate(padding); - areaNode.setAttribute('x', -bbox.width / 2); - areaNode.setAttribute('y', -bbox.height / 2); - areaNode.setAttribute('width', bbox.width); - areaNode.setAttribute('height', bbox.height); - areaNode.setAttribute('transform', 'translate(' + center.x + ',' + center.y + ') rotate(' + angle + ')'); - }, - toggleArea: function(visible) { - this.childNodes.area.style.display = (visible) ? '' : 'none'; - }, - onPointerDown: function(evt) { - if (this.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - this.paper.undelegateEvents(); - this.delegateDocumentEvents(); - this.focus(); - this.toggleArea(this.options.restrictArea); - this.relatedView.model.startBatch('anchor-move', { ui: true, tool: this.cid }); - }, - resetAnchor: function(anchor) { - var type = this.type; - var relatedModel = this.relatedView.model; - if (anchor) { - relatedModel.prop([type, 'anchor'], anchor, { - rewrite: true, - ui: true, - tool: this.cid - }); - } else { - relatedModel.removeProp([type, 'anchor'], { - ui: true, - tool: this.cid - }); - } - }, - onPointerMove: function(evt) { - - var relatedView = this.relatedView; - var type = this.type; - var view = relatedView.getEndView(type); - var model = view.model; - var magnet = relatedView.getEndMagnet(type); - var normalizedEvent = normalizeEvent(evt); - var coords = this.paper.clientToLocalPoint(normalizedEvent.clientX, normalizedEvent.clientY); - var snapFn = this.options.snap; - if (typeof snapFn === 'function') { - coords = snapFn.call(relatedView, coords, view, magnet, type, relatedView, this); - coords = new Point(coords); - } - - if (this.options.restrictArea) { - if (view.isNodeConnection(magnet)) { - // snap coords to the link's connection - var pointAtConnection = view.getClosestPoint(coords); - if (pointAtConnection) { coords = pointAtConnection; } - } else { - // snap coords within node bbox - var bbox = view.getNodeUnrotatedBBox(magnet); - var angle = model.angle(); - var origin = model.getBBox().center(); - var rotatedCoords = coords.clone().rotate(origin, angle); - if (!bbox.containsPoint(rotatedCoords)) { - coords = bbox.pointNearestToPoint(rotatedCoords).rotate(origin, -angle); - } - } - } - - var anchor; - var anchorFn = this.options.anchor; - if (typeof anchorFn === 'function') { - anchor = anchorFn.call(relatedView, coords, view, magnet, type, relatedView); - } - - this.resetAnchor(anchor); - this.update(); - }, - - onPointerUp: function(evt) { - this.paper.delegateEvents(); - this.undelegateDocumentEvents(); - this.blur(); - this.toggleArea(false); - var linkView = this.relatedView; - if (this.options.redundancyRemoval) { linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); } - linkView.model.stopBatch('anchor-move', { ui: true, tool: this.cid }); - }, - - onPointerDblClick: function() { - var anchor = this.options.resetAnchor; - if (anchor === false) { return; } // reset anchor disabled - if (anchor === true) { anchor = null; } // remove the current anchor - this.resetAnchor(cloneDeep(anchor)); - this.update(); - } - }); - - var SourceAnchor = Anchor.extend({ - name: 'source-anchor', - type: 'source' - }); - - var TargetAnchor = Anchor.extend({ - name: 'target-anchor', - type: 'target' - }); - - var index$3 = ({ - Vertices: Vertices, - Segments: Segments, - SourceArrowhead: SourceArrowhead, - TargetArrowhead: TargetArrowhead, - SourceAnchor: SourceAnchor, - TargetAnchor: TargetAnchor, - Button: Button, - Remove: Remove, - Connect: Connect, - Boundary: Boundary - }); - - var Control = ToolView.extend({ - tagName: 'g', - children: [{ - tagName: 'circle', - selector: 'handle', - attributes: { - 'cursor': 'pointer', - 'stroke-width': 2, - 'stroke': '#FFFFFF', - 'fill': '#33334F', - 'r': 6 - } - }, { - tagName: 'rect', - selector: 'extras', - attributes: { - 'pointer-events': 'none', - 'fill': 'none', - 'stroke': '#33334F', - 'stroke-dasharray': '2,4', - 'rx': 5, - 'ry': 5 - } - }], - events: { - mousedown: 'onPointerDown', - touchstart: 'onPointerDown', - dblclick: 'onPointerDblClick', - dbltap: 'onPointerDblClick' - }, - documentEvents: { - mousemove: 'onPointerMove', - touchmove: 'onPointerMove', - mouseup: 'onPointerUp', - touchend: 'onPointerUp', - touchcancel: 'onPointerUp' - }, - options: { - handleAttributes: null, - selector: 'root', - padding: 6, - }, - - getPosition: function() { - // To be overridden - }, - setPosition: function() { - // To be overridden - }, - resetPosition: function() { - // To be overridden - }, - onRender: function() { - this.renderChildren(); - this.toggleExtras(false); - this.update(); - }, - update: function() { - var ref = this.childNodes; - var handle = ref.handle; - var extras = ref.extras; - if (handle) { - this.updateHandle(handle); - } else { - throw new Error('Control: markup selector `handle` is required'); - } - if (extras) { - this.updateExtras(extras); - } - return this; - }, - updateHandle: function(handleNode) { - var ref = this; - var relatedView = ref.relatedView; - var options = ref.options; - var model = relatedView.model; - var relativePos = this.getPosition(relatedView, this); - var absolutePos = model.getAbsolutePointFromRelative(relativePos); - handleNode.setAttribute('transform', ("translate(" + (absolutePos.x) + "," + (absolutePos.y) + ")")); - var handleAttributes = options.handleAttributes; - if (handleAttributes) { - for (var attrName in handleAttributes) { - handleNode.setAttribute(attrName, handleAttributes[attrName]); - } - } - }, - updateExtras: function(extrasNode) { - var ref = this; - var relatedView = ref.relatedView; - var options = ref.options; - var ref$1 = this.options; - var selector = ref$1.selector; - if (!selector) { - this.toggleExtras(false); - return; - } - var ref$2 = relatedView.findBySelector(selector); - var magnet = ref$2[0]; - if (!magnet) { throw new Error('Control: invalid selector.'); } - var padding = options.padding; - if (!isFinite(padding)) { padding = 0; } - var bbox = relatedView.getNodeUnrotatedBBox(magnet); - var model = relatedView.model; - var angle = model.angle(); - var center = bbox.center(); - if (angle) { center.rotate(model.getBBox().center(), -angle); } - bbox.inflate(padding); - extrasNode.setAttribute('x', -bbox.width / 2); - extrasNode.setAttribute('y', -bbox.height / 2); - extrasNode.setAttribute('width', bbox.width); - extrasNode.setAttribute('height', bbox.height); - extrasNode.setAttribute('transform', ("translate(" + (center.x) + "," + (center.y) + ") rotate(" + angle + ")")); - }, - toggleExtras: function(visible) { - var ref = this.childNodes; - var extras = ref.extras; - if (!extras) { return; } - extras.style.display = (visible) ? '' : 'none'; - }, - onPointerDown: function(evt) { - var ref = this; - var relatedView = ref.relatedView; - var paper = ref.paper; - if (this.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - paper.undelegateEvents(); - this.delegateDocumentEvents(); - this.focus(); - this.toggleExtras(true); - relatedView.model.startBatch('control-move', { ui: true, tool: this.cid }); - }, - onPointerMove: function(evt) { - var ref = this; - var relatedView = ref.relatedView; - var paper = ref.paper; - var model = relatedView.model; - var ref$1 = normalizeEvent(evt); - var clientX = ref$1.clientX; - var clientY = ref$1.clientY; - var coords = paper.clientToLocalPoint(clientX, clientY); - var relativeCoords = model.getRelativePointFromAbsolute(coords); - this.setPosition(relatedView, relativeCoords, this); - this.update(); - }, - onPointerUp: function(_evt) { - var ref = this; - var relatedView = ref.relatedView; - var paper = ref.paper; - paper.delegateEvents(); - this.undelegateDocumentEvents(); - this.blur(); - this.toggleExtras(false); - relatedView.model.stopBatch('control-move', { ui: true, tool: this.cid }); - }, - onPointerDblClick: function() { - var ref = this; - var relatedView = ref.relatedView; - this.resetPosition(relatedView, this); - this.update(); - } - - }); - - - - var index$4 = ({ - Button: Button, - Remove: Remove, - Connect: Connect, - Boundary: Boundary, - Control: Control - }); - - var version = "3.5.0"; - - var env = { - - _results: {}, - - _tests: { - - svgforeignobject: function() { - return !!document.createElementNS && - /SVGForeignObject/.test(({}).toString.call(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'))); - } - }, - - addTest: function(name, fn) { - - return this._tests[name] = fn; - }, - - test: function(name) { - - var fn = this._tests[name]; - - if (!fn) { - throw new Error('Test not defined ("' + name + '"). Use `joint.env.addTest(name, fn) to add a new test.`'); - } - - var result = this._results[name]; - - if (typeof result !== 'undefined') { - return result; - } - - try { - result = fn(); - } catch (error) { - result = false; - } - - // Cache the test result. - this._results[name] = result; - - return result; - } - }; - - var Vectorizer = V; - var layout = { PortLabel: PortLabel, Port: Port }; - var setTheme = function(theme, opt) { - - opt = opt || {}; - - invoke(views, 'setTheme', theme, opt); - - // Update the default theme on the view prototype. - View.prototype.defaultTheme = theme; - }; - - var LayersNames = { - CELLS: 'cells', - BACK: 'back', - FRONT: 'front', - TOOLS: 'tools', - LABELS: 'labels' - }; - - var PaperLayer = View.extend({ - - tagName: 'g', - svgElement: true, - pivotNodes: null, - defaultTheme: null, - - options: { - name: '' - }, - - className: function() { - return addClassNamePrefix(((this.options.name) + "-layer")); - }, - - init: function() { - this.pivotNodes = {}; - }, - - insertSortedNode: function(node, z) { - this.el.insertBefore(node, this.insertPivot(z)); - }, - - insertNode: function(node) { - var ref = this; - var el = ref.el; - if (node.parentNode !== el) { - el.appendChild(node); - } - }, - - insertPivot: function(z) { - var ref = this; - var el = ref.el; - var pivotNodes = ref.pivotNodes; - z = +z; - z || (z = 0); - var pivotNode = pivotNodes[z]; - if (pivotNode) { return pivotNode; } - pivotNode = pivotNodes[z] = document.createComment('z-index:' + (z + 1)); - var neighborZ = -Infinity; - for (var currentZ in pivotNodes) { - currentZ = +currentZ; - if (currentZ < z && currentZ > neighborZ) { - neighborZ = currentZ; - if (neighborZ === z - 1) { continue; } - } - } - if (neighborZ !== -Infinity) { - var neighborPivot = pivotNodes[neighborZ]; - // Insert After - el.insertBefore(pivotNode, neighborPivot.nextSibling); - } else { - // First Child - el.insertBefore(pivotNode, el.firstChild); - } - return pivotNode; - }, - - removePivots: function() { - var ref = this; - var el = ref.el; - var pivotNodes = ref.pivotNodes; - for (var z in pivotNodes) { el.removeChild(pivotNodes[z]); } - this.pivotNodes = {}; - } - - }); - - var HighlightingTypes = { - DEFAULT: 'default', - EMBEDDING: 'embedding', - CONNECTING: 'connecting', - MAGNET_AVAILABILITY: 'magnetAvailability', - ELEMENT_AVAILABILITY: 'elementAvailability' - }; - - // CellView base view and controller. - // -------------------------------------------- - - // This is the base view and controller for `ElementView` and `LinkView`. - var CellView = View.extend({ - - tagName: 'g', - - svgElement: true, - - selector: 'root', - - metrics: null, - - className: function() { - - var classNames = ['cell']; - var type = this.model.get('type'); - - if (type) { - - type.toLowerCase().split('.').forEach(function(value, index, list) { - classNames.push('type-' + list.slice(0, index + 1).join('-')); - }); - } - - return classNames.join(' '); - }, - - _presentationAttributes: null, - _flags: null, - - setFlags: function() { - var flags = {}; - var attributes = {}; - var shift = 0; - var i, n, label; - var presentationAttributes = result(this, 'presentationAttributes'); - for (var attribute in presentationAttributes) { - if (!presentationAttributes.hasOwnProperty(attribute)) { continue; } - var labels = presentationAttributes[attribute]; - if (!Array.isArray(labels)) { labels = [labels]; } - for (i = 0, n = labels.length; i < n; i++) { - label = labels[i]; - var flag = flags[label]; - if (!flag) { - flag = flags[label] = 1<<(shift++); - } - attributes[attribute] |= flag; - } - } - var initFlag = result(this, 'initFlag'); - if (!Array.isArray(initFlag)) { initFlag = [initFlag]; } - for (i = 0, n = initFlag.length; i < n; i++) { - label = initFlag[i]; - if (!flags[label]) { flags[label] = 1<<(shift++); } - } - - // 26 - 30 are reserved for paper flags - // 31+ overflows maximal number - if (shift > 25) { throw new Error('dia.CellView: Maximum number of flags exceeded.'); } - - this._flags = flags; - this._presentationAttributes = attributes; - }, - - hasFlag: function(flag, label) { - return flag & this.getFlag(label); - }, - - removeFlag: function(flag, label) { - return flag ^ (flag & this.getFlag(label)); - }, - - getFlag: function(label) { - var flags = this._flags; - if (!flags) { return 0; } - var flag = 0; - if (Array.isArray(label)) { - for (var i = 0, n = label.length; i < n; i++) { flag |= flags[label[i]]; } - } else { - flag |= flags[label]; - } - return flag; - }, - - attributes: function() { - var cell = this.model; - return { - 'model-id': cell.id, - 'data-type': cell.attributes.type - }; - }, - - constructor: function(options) { - - // Make sure a global unique id is assigned to this view. Store this id also to the properties object. - // The global unique id makes sure that the same view can be rendered on e.g. different machines and - // still be associated to the same object among all those clients. This is necessary for real-time - // collaboration mechanism. - options.id = options.id || guid(this); - - View.call(this, options); - }, - - initialize: function() { - - this.setFlags(); - - View.prototype.initialize.apply(this, arguments); - - this.cleanNodesCache(); - - // Store reference to this to the DOM element so that the view is accessible through the DOM tree. - this.$el.data('view', this); - - this.startListening(); - }, - - onMount: function onMount() { - // To be overridden - }, - - startListening: function() { - this.listenTo(this.model, 'change', this.onAttributesChange); - }, - - onAttributesChange: function(model, opt) { - var flag = model.getChangeFlag(this._presentationAttributes); - if (opt.updateHandled || !flag) { return; } - if (opt.dirty && this.hasFlag(flag, 'UPDATE')) { flag |= this.getFlag('RENDER'); } - // TODO: tool changes does not need to be sync - // Fix Segments tools - if (opt.tool) { opt.async = false; } - this.requestUpdate(flag, opt); - }, - - requestUpdate: function(flags, opt) { - var ref = this; - var paper = ref.paper; - if (paper && flags > 0) { - paper.requestViewUpdate(this, flags, this.UPDATE_PRIORITY, opt); - } - }, - - parseDOMJSON: function(markup, root) { - - var doc = parseDOMJSON(markup); - var selectors = doc.selectors; - var groups = doc.groupSelectors; - for (var group in groups) { - if (selectors[group]) { throw new Error('dia.CellView: ambiguous group selector'); } - selectors[group] = groups[group]; - } - if (root) { - var rootSelector = this.selector; - if (selectors[rootSelector]) { throw new Error('dia.CellView: ambiguous root selector.'); } - selectors[rootSelector] = root; - } - return { fragment: doc.fragment, selectors: selectors }; - }, - - // Return `true` if cell link is allowed to perform a certain UI `feature`. - // Example: `can('vertexMove')`, `can('labelMove')`. - can: function(feature) { - - var interactive = isFunction(this.options.interactive) - ? this.options.interactive(this) - : this.options.interactive; - - return (isObject$1(interactive) && interactive[feature] !== false) || - (isBoolean(interactive) && interactive !== false); - }, - - findBySelector: function(selector, root, selectors) { - - root || (root = this.el); - selectors || (selectors = this.selectors); - - // These are either descendants of `this.$el` of `this.$el` itself. - // `.` is a special selector used to select the wrapping `` element. - if (!selector || selector === '.') { return [root]; } - if (selectors) { - var nodes = selectors[selector]; - if (nodes) { - if (Array.isArray(nodes)) { return nodes; } - return [nodes]; - } - } - - // Maintaining backwards compatibility - // e.g. `circle:first` would fail with querySelector() call - if (config.useCSSSelectors) { return $(root).find(selector).toArray(); } - - return []; - }, - - notify: function(eventName) { - - if (this.paper) { - - var args = Array.prototype.slice.call(arguments, 1); - - // Trigger the event on both the element itself and also on the paper. - this.trigger.apply(this, [eventName].concat(args)); - - // Paper event handlers receive the view object as the first argument. - this.paper.trigger.apply(this.paper, [eventName, this].concat(args)); - } - }, - - getBBox: function(opt) { - - var bbox; - if (opt && opt.useModelGeometry) { - var model = this.model; - bbox = model.getBBox().bbox(model.angle()); - } else { - bbox = this.getNodeBBox(this.el); - } - - return this.paper.localToPaperRect(bbox); - }, - - getNodeBBox: function(magnet) { - - var rect = this.getNodeBoundingRect(magnet); - var magnetMatrix = this.getNodeMatrix(magnet); - var translateMatrix = this.getRootTranslateMatrix(); - var rotateMatrix = this.getRootRotateMatrix(); - return V.transformRect(rect, translateMatrix.multiply(rotateMatrix).multiply(magnetMatrix)); - }, - - getNodeUnrotatedBBox: function(magnet) { - - var rect = this.getNodeBoundingRect(magnet); - var magnetMatrix = this.getNodeMatrix(magnet); - var translateMatrix = this.getRootTranslateMatrix(); - return V.transformRect(rect, translateMatrix.multiply(magnetMatrix)); - }, - - getRootTranslateMatrix: function() { - - var model = this.model; - var position = model.position(); - var mt = V.createSVGMatrix().translate(position.x, position.y); - return mt; - }, - - getRootRotateMatrix: function() { - - var mr = V.createSVGMatrix(); - var model = this.model; - var angle = model.angle(); - if (angle) { - var bbox = model.getBBox(); - var cx = bbox.width / 2; - var cy = bbox.height / 2; - mr = mr.translate(cx, cy).rotate(angle).translate(-cx, -cy); - } - return mr; - }, - - _notifyHighlight: function(eventName, el, opt) { - var assign, assign$1; - - if ( opt === void 0 ) opt = {}; - var ref = this; - var rootNode = ref.el; - var node; - if (typeof el === 'string') { - (assign = this.findBySelector(el), node = assign[0], node = node === void 0 ? rootNode : node); - } else { - (assign$1 = this.$(el), node = assign$1[0], node = node === void 0 ? rootNode : node); - } - // set partial flag if the highlighted element is not the entire view. - opt.partial = (node !== rootNode); - // translate type flag into a type string - if (opt.type === undefined) { - var type; - switch (true) { - case opt.embedding: - type = HighlightingTypes.EMBEDDING; - break; - case opt.connecting: - type = HighlightingTypes.CONNECTING; - break; - case opt.magnetAvailability: - type = HighlightingTypes.MAGNET_AVAILABILITY; - break; - case opt.elementAvailability: - type = HighlightingTypes.ELEMENT_AVAILABILITY; - break; - default: - type = HighlightingTypes.DEFAULT; - break; - } - opt.type = type; - } - this.notify(eventName, node, opt); - return this; - }, - - highlight: function(el, opt) { - return this._notifyHighlight('cell:highlight', el, opt); - }, - - unhighlight: function(el, opt) { - if ( opt === void 0 ) opt = {}; - - return this._notifyHighlight('cell:unhighlight', el, opt); - }, - - // Find the closest element that has the `magnet` attribute set to `true`. If there was not such - // an element found, return the root element of the cell view. - findMagnet: function(el) { - - var root = this.el; - var magnet = this.$(el)[0]; - if (!magnet) { - magnet = root; - } - - do { - var magnetAttribute = magnet.getAttribute('magnet'); - var isMagnetRoot = (magnet === root); - if ((magnetAttribute || isMagnetRoot) && magnetAttribute !== 'false') { - return magnet; - } - if (isMagnetRoot) { - // If the overall cell has set `magnet === false`, then return `undefined` to - // announce there is no magnet found for this cell. - // This is especially useful to set on cells that have 'ports'. In this case, - // only the ports have set `magnet === true` and the overall element has `magnet === false`. - return undefined; - } - magnet = magnet.parentNode; - } while (magnet); - - return undefined; - }, - - findProxyNode: function(el, type) { - el || (el = this.el); - var nodeSelector = el.getAttribute((type + "-selector")); - if (nodeSelector) { - var ref = this.findBySelector(nodeSelector); - var proxyNode = ref[0]; - if (proxyNode) { return proxyNode; } - } - return el; - }, - - // Construct a unique selector for the `el` element within this view. - // `prevSelector` is being collected through the recursive call. - // No value for `prevSelector` is expected when using this method. - getSelector: function(el, prevSelector) { - - var selector; - - if (el === this.el) { - if (typeof prevSelector === 'string') { selector = '> ' + prevSelector; } - return selector; - } - - if (el) { - - var nthChild = V(el).index() + 1; - selector = el.tagName + ':nth-child(' + nthChild + ')'; - - if (prevSelector) { - selector += ' > ' + prevSelector; - } - - selector = this.getSelector(el.parentNode, selector); - } - - return selector; - }, - - addLinkFromMagnet: function(magnet, x, y) { - - var paper = this.paper; - var graph = paper.model; - - var link = paper.getDefaultLink(this, magnet); - link.set({ - source: this.getLinkEnd(magnet, x, y, link, 'source'), - target: { x: x, y: y } - }).addTo(graph, { - async: false, - ui: true - }); - - return link.findView(paper); - }, - - getLinkEnd: function(magnet) { - var ref; - - var args = [], len = arguments.length - 1; - while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; - - var model = this.model; - var id = model.id; - var port = this.findAttribute('port', magnet); - // Find a unique `selector` of the element under pointer that is a magnet. - var selector = magnet.getAttribute('joint-selector'); - - var end = { id: id }; - if (selector != null) { end.magnet = selector; } - if (port != null) { - end.port = port; - if (!model.hasPort(port) && !selector) { - // port created via the `port` attribute (not API) - end.selector = this.getSelector(magnet); - } - } else if (selector == null && this.el !== magnet) { - end.selector = this.getSelector(magnet); - } - - return (ref = this).customizeLinkEnd.apply(ref, [ end, magnet ].concat( args )); - }, - - customizeLinkEnd: function(end, magnet, x, y, link, endType) { - var ref = this; - var paper = ref.paper; - var ref$1 = paper.options; - var connectionStrategy = ref$1.connectionStrategy; - if (typeof connectionStrategy === 'function') { - var strategy = connectionStrategy.call(paper, end, this, magnet, new Point(x, y), link, endType, paper); - if (strategy) { return strategy; } - } - return end; - }, - - getMagnetFromLinkEnd: function(end) { - - var root = this.el; - var port = end.port; - var selector = end.magnet; - var model = this.model; - var magnet; - if (port != null && model.isElement() && model.hasPort(port)) { - magnet = this.findPortNode(port, selector) || root; - } else { - if (!selector) { selector = end.selector; } - if (!selector && port != null) { - // link end has only `id` and `port` property referencing - // a port created via the `port` attribute (not API). - selector = '[port="' + port + '"]'; - } - magnet = this.findBySelector(selector, root, this.selectors)[0]; - } - - return this.findProxyNode(magnet, 'magnet'); - }, - - dragLinkStart: function(evt, magnet, x, y) { - this.model.startBatch('add-link'); - var linkView = this.addLinkFromMagnet(magnet, x, y); - // backwards compatibility events - linkView.notifyPointerdown(evt, x, y); - linkView.eventData(evt, linkView.startArrowheadMove('target', { whenNotAllowed: 'remove' })); - this.eventData(evt, { linkView: linkView }); - }, - - dragLink: function(evt, x, y) { - var data = this.eventData(evt); - var linkView = data.linkView; - if (linkView) { - linkView.pointermove(evt, x, y); - } else { - var paper = this.paper; - var magnetThreshold = paper.options.magnetThreshold; - var currentTarget = this.getEventTarget(evt); - var targetMagnet = data.targetMagnet; - if (magnetThreshold === 'onleave') { - // magnetThreshold when the pointer leaves the magnet - if (targetMagnet === currentTarget || V(targetMagnet).contains(currentTarget)) { return; } - } else { - // magnetThreshold defined as a number of movements - if (paper.eventData(evt).mousemoved <= magnetThreshold) { return; } - } - this.dragLinkStart(evt, targetMagnet, x, y); - } - }, - - dragLinkEnd: function(evt, x, y) { - var data = this.eventData(evt); - var linkView = data.linkView; - if (!linkView) { return; } - linkView.pointerup(evt, x, y); - this.model.stopBatch('add-link'); - }, - - getAttributeDefinition: function(attrName) { - - return this.model.constructor.getAttributeDefinition(attrName); - }, - - setNodeAttributes: function(node, attrs) { - - if (!isEmpty(attrs)) { - if (node instanceof SVGElement) { - V(node).attr(attrs); - } else { - $(node).attr(attrs); - } - } - }, - - processNodeAttributes: function(node, attrs) { - - var attrName, attrVal, def, i, n; - var normalAttrs, setAttrs, positionAttrs, offsetAttrs; - var relatives = []; - // divide the attributes between normal and special - for (attrName in attrs) { - if (!attrs.hasOwnProperty(attrName)) { continue; } - attrVal = attrs[attrName]; - def = this.getAttributeDefinition(attrName); - if (def && (!isFunction(def.qualify) || def.qualify.call(this, attrVal, node, attrs))) { - if (isString(def.set)) { - normalAttrs || (normalAttrs = {}); - normalAttrs[def.set] = attrVal; - } - if (attrVal !== null) { - relatives.push(attrName, def); - } - } else { - normalAttrs || (normalAttrs = {}); - normalAttrs[toKebabCase(attrName)] = attrVal; - } - } - - // handle the rest of attributes via related method - // from the special attributes namespace. - for (i = 0, n = relatives.length; i < n; i+=2) { - attrName = relatives[i]; - def = relatives[i+1]; - attrVal = attrs[attrName]; - if (isFunction(def.set)) { - setAttrs || (setAttrs = {}); - setAttrs[attrName] = attrVal; - } - if (isFunction(def.position)) { - positionAttrs || (positionAttrs = {}); - positionAttrs[attrName] = attrVal; - } - if (isFunction(def.offset)) { - offsetAttrs || (offsetAttrs = {}); - offsetAttrs[attrName] = attrVal; - } - } - - return { - raw: attrs, - normal: normalAttrs, - set: setAttrs, - position: positionAttrs, - offset: offsetAttrs - }; - }, - - updateRelativeAttributes: function(node, attrs, refBBox, opt) { - - opt || (opt = {}); - - var attrName, attrVal, def; - var rawAttrs = attrs.raw || {}; - var nodeAttrs = attrs.normal || {}; - var setAttrs = attrs.set; - var positionAttrs = attrs.position; - var offsetAttrs = attrs.offset; - - for (attrName in setAttrs) { - attrVal = setAttrs[attrName]; - def = this.getAttributeDefinition(attrName); - // SET - set function should return attributes to be set on the node, - // which will affect the node dimensions based on the reference bounding - // box. e.g. `width`, `height`, `d`, `rx`, `ry`, `points - var setResult = def.set.call(this, attrVal, refBBox.clone(), node, rawAttrs); - if (isObject$1(setResult)) { - assign(nodeAttrs, setResult); - } else if (setResult !== undefined) { - nodeAttrs[attrName] = setResult; - } - } - - if (node instanceof HTMLElement) { - // TODO: setting the `transform` attribute on HTMLElements - // via `node.style.transform = 'matrix(...)';` would introduce - // a breaking change (e.g. basic.TextBlock). - this.setNodeAttributes(node, nodeAttrs); - return; - } - - // The final translation of the subelement. - var nodeTransform = nodeAttrs.transform; - var nodeMatrix = V.transformStringToMatrix(nodeTransform); - var nodePosition = Point(nodeMatrix.e, nodeMatrix.f); - if (nodeTransform) { - nodeAttrs = omit(nodeAttrs, 'transform'); - nodeMatrix.e = nodeMatrix.f = 0; - } - - // Calculate node scale determined by the scalable group - // only if later needed. - var sx, sy, translation; - if (positionAttrs || offsetAttrs) { - var nodeScale = this.getNodeScale(node, opt.scalableNode); - sx = nodeScale.sx; - sy = nodeScale.sy; - } - - var positioned = false; - for (attrName in positionAttrs) { - attrVal = positionAttrs[attrName]; - def = this.getAttributeDefinition(attrName); - // POSITION - position function should return a point from the - // reference bounding box. The default position of the node is x:0, y:0 of - // the reference bounding box or could be further specify by some - // SVG attributes e.g. `x`, `y` - translation = def.position.call(this, attrVal, refBBox.clone(), node, rawAttrs); - if (translation) { - nodePosition.offset(Point(translation).scale(sx, sy)); - positioned || (positioned = true); - } - } - - // The node bounding box could depend on the `size` set from the previous loop. - // Here we know, that all the size attributes have been already set. - this.setNodeAttributes(node, nodeAttrs); - - var offseted = false; - if (offsetAttrs) { - // Check if the node is visible - var nodeBoundingRect = this.getNodeBoundingRect(node); - if (nodeBoundingRect.width > 0 && nodeBoundingRect.height > 0) { - var nodeBBox = V.transformRect(nodeBoundingRect, nodeMatrix).scale(1 / sx, 1 / sy); - for (attrName in offsetAttrs) { - attrVal = offsetAttrs[attrName]; - def = this.getAttributeDefinition(attrName); - // OFFSET - offset function should return a point from the element - // bounding box. The default offset point is x:0, y:0 (origin) or could be further - // specify with some SVG attributes e.g. `text-anchor`, `cx`, `cy` - translation = def.offset.call(this, attrVal, nodeBBox, node, rawAttrs); - if (translation) { - nodePosition.offset(Point(translation).scale(sx, sy)); - offseted || (offseted = true); - } - } - } - } - - // Do not touch node's transform attribute if there is no transformation applied. - if (nodeTransform !== undefined || positioned || offseted) { - // Round the coordinates to 1 decimal point. - nodePosition.round(1); - nodeMatrix.e = nodePosition.x; - nodeMatrix.f = nodePosition.y; - node.setAttribute('transform', V.matrixToTransformString(nodeMatrix)); - // TODO: store nodeMatrix metrics? - } - }, - - getNodeScale: function(node, scalableNode) { - - // Check if the node is a descendant of the scalable group. - var sx, sy; - if (scalableNode && scalableNode.contains(node)) { - var scale = scalableNode.scale(); - sx = 1 / scale.sx; - sy = 1 / scale.sy; - } else { - sx = 1; - sy = 1; - } - - return { sx: sx, sy: sy }; - }, - - cleanNodesCache: function() { - this.metrics = {}; - }, - - nodeCache: function(magnet) { - - var metrics = this.metrics; - // Don't use cache? It most likely a custom view with overridden update. - if (!metrics) { return {}; } - var id = V.ensureId(magnet); - var value = metrics[id]; - if (!value) { value = metrics[id] = {}; } - return value; - }, - - getNodeData: function(magnet) { - - var metrics = this.nodeCache(magnet); - if (!metrics.data) { metrics.data = {}; } - return metrics.data; - }, - - getNodeBoundingRect: function(magnet) { - - var metrics = this.nodeCache(magnet); - if (metrics.boundingRect === undefined) { metrics.boundingRect = V(magnet).getBBox(); } - return new Rect(metrics.boundingRect); - }, - - getNodeMatrix: function(magnet) { - - var metrics = this.nodeCache(magnet); - if (metrics.magnetMatrix === undefined) { - var target = this.rotatableNode || this.el; - metrics.magnetMatrix = V(magnet).getTransformToElement(target); - } - return V.createSVGMatrix(metrics.magnetMatrix); - }, - - getNodeShape: function(magnet) { - - var metrics = this.nodeCache(magnet); - if (metrics.geometryShape === undefined) { metrics.geometryShape = V(magnet).toGeometryShape(); } - return metrics.geometryShape.clone(); - }, - - isNodeConnection: function(node) { - return this.model.isLink() && (!node || node === this.el); - }, - - findNodesAttributes: function(attrs, root, selectorCache, selectors) { - - var i, n, nodeAttrs, nodeId; - var nodesAttrs = {}; - var mergeIds = []; - for (var selector in attrs) { - if (!attrs.hasOwnProperty(selector)) { continue; } - nodeAttrs = attrs[selector]; - if (!isPlainObject(nodeAttrs)) { continue; } // Not a valid selector-attributes pair - var selected = selectorCache[selector] = this.findBySelector(selector, root, selectors); - for (i = 0, n = selected.length; i < n; i++) { - var node = selected[i]; - nodeId = V.ensureId(node); - // "unique" selectors are selectors that referencing a single node (defined by `selector`) - // groupSelector referencing a single node is not "unique" - var unique = (selectors && selectors[selector] === node); - var prevNodeAttrs = nodesAttrs[nodeId]; - if (prevNodeAttrs) { - // Note, that nodes referenced by deprecated `CSS selectors` are not taken into account. - // e.g. css:`.circle` and selector:`circle` can be applied in a random order - if (!prevNodeAttrs.array) { - mergeIds.push(nodeId); - prevNodeAttrs.array = true; - prevNodeAttrs.attributes = [prevNodeAttrs.attributes]; - prevNodeAttrs.selectedLength = [prevNodeAttrs.selectedLength]; - } - var attributes = prevNodeAttrs.attributes; - var selectedLength = prevNodeAttrs.selectedLength; - if (unique) { - // node referenced by `selector` - attributes.unshift(nodeAttrs); - selectedLength.unshift(-1); - } else { - // node referenced by `groupSelector` - var sortIndex = sortedIndex(selectedLength, n); - attributes.splice(sortIndex, 0, nodeAttrs); - selectedLength.splice(sortIndex, 0, n); - } - } else { - nodesAttrs[nodeId] = { - attributes: nodeAttrs, - selectedLength: unique ? -1 : n, - node: node, - array: false - }; - } - } - } - - for (i = 0, n = mergeIds.length; i < n; i++) { - nodeId = mergeIds[i]; - nodeAttrs = nodesAttrs[nodeId]; - nodeAttrs.attributes = merge.apply(void 0, [ {} ].concat( nodeAttrs.attributes.reverse() )); - } - - return nodesAttrs; - }, - - getEventTarget: function(evt, opt) { - if ( opt === void 0 ) opt = {}; - - // Touchmove/Touchend event's target is not reflecting the element under the coordinates as mousemove does. - // It holds the element when a touchstart triggered. - var target = evt.target; - var type = evt.type; - var clientX = evt.clientX; if ( clientX === void 0 ) clientX = 0; - var clientY = evt.clientY; if ( clientY === void 0 ) clientY = 0; - if (opt.fromPoint || type === 'touchmove' || type === 'touchend') { - return document.elementFromPoint(clientX, clientY); - } - - return target; - }, - - // Default is to process the `model.attributes.attrs` object and set attributes on subelements based on the selectors, - // unless `attrs` parameter was passed. - updateDOMSubtreeAttributes: function(rootNode, attrs, opt) { - - opt || (opt = {}); - opt.rootBBox || (opt.rootBBox = Rect()); - opt.selectors || (opt.selectors = this.selectors); // selector collection to use - - // Cache table for query results and bounding box calculation. - // Note that `selectorCache` needs to be invalidated for all - // `updateAttributes` calls, as the selectors might pointing - // to nodes designated by an attribute or elements dynamically - // created. - var selectorCache = {}; - var bboxCache = {}; - var relativeItems = []; - var relativeRefItems = []; - var item, node, nodeAttrs, nodeData, processedAttrs; - - var roAttrs = opt.roAttributes; - var nodesAttrs = this.findNodesAttributes(roAttrs || attrs, rootNode, selectorCache, opt.selectors); - // `nodesAttrs` are different from all attributes, when - // rendering only attributes sent to this method. - var nodesAllAttrs = (roAttrs) - ? this.findNodesAttributes(attrs, rootNode, selectorCache, opt.selectors) - : nodesAttrs; - - for (var nodeId in nodesAttrs) { - nodeData = nodesAttrs[nodeId]; - nodeAttrs = nodeData.attributes; - node = nodeData.node; - processedAttrs = this.processNodeAttributes(node, nodeAttrs); - - if (!processedAttrs.set && !processedAttrs.position && !processedAttrs.offset) { - // Set all the normal attributes right on the SVG/HTML element. - this.setNodeAttributes(node, processedAttrs.normal); - - } else { - - var nodeAllAttrs = nodesAllAttrs[nodeId] && nodesAllAttrs[nodeId].attributes; - var refSelector = (nodeAllAttrs && (nodeAttrs.ref === undefined)) - ? nodeAllAttrs.ref - : nodeAttrs.ref; - - var refNode; - if (refSelector) { - refNode = (selectorCache[refSelector] || this.findBySelector(refSelector, rootNode, opt.selectors))[0]; - if (!refNode) { - throw new Error('dia.CellView: "' + refSelector + '" reference does not exist.'); - } - } else { - refNode = null; - } + var oldVertices = toArray(vertices).map(Point); + var newVertices = []; + var tailPoint = sourceAnchor; // the origin of first route's grid, does not need snapping - item = { - node: node, - refNode: refNode, - processedAttributes: processedAttrs, - allAttributes: nodeAllAttrs - }; + // find a route by concatenating all partial routes (routes need to pass through vertices) + // source -> vertex[1] -> ... -> vertex[n] -> target + var to, from; - if (refNode) { - // If an element in the list is positioned relative to this one, then - // we want to insert this one before it in the list. - var itemIndex = relativeRefItems.findIndex(function(item) { - return item.refNode === node; - }); + for (var i = 0, len = oldVertices.length; i <= len; i++) { - if (itemIndex > -1) { - relativeRefItems.splice(itemIndex, 0, item); - } else { - relativeRefItems.push(item); - } - } else { - // A node with no ref attribute. To be updated before the nodes referencing other nodes. - // The order of no-ref-items is not specified/important. - relativeItems.push(item); - } - } - } + var partialRoute = null; - relativeItems.push.apply(relativeItems, relativeRefItems); + from = to || sourceBBox; + to = oldVertices[i]; - var rotatableMatrix; - for (var i = 0, n = relativeItems.length; i < n; i++) { - item = relativeItems[i]; - node = item.node; - refNode = item.refNode; + if (!to) { + // this is the last iteration + // we ran through all vertices in oldVertices + // 'to' is not a vertex. - // Find the reference element bounding box. If no reference was provided, we - // use the optional bounding box. - var vRotatable = V(opt.rotatableNode); - var refNodeId = refNode ? V.ensureId(refNode) : ''; - var isRefNodeRotatable = !!vRotatable && !!refNode && vRotatable.contains(refNode); - var unrotatedRefBBox = bboxCache[refNodeId]; - if (!unrotatedRefBBox) { - // Get the bounding box of the reference element relative to the `rotatable` `` (without rotation) - // or to the root `` element if no rotatable group present if reference node present. - // Uses the bounding box provided. - var transformationTarget = (isRefNodeRotatable) ? vRotatable : rootNode; - unrotatedRefBBox = bboxCache[refNodeId] = (refNode) - ? V(refNode).getBBox({ target: transformationTarget }) - : opt.rootBBox; - } + to = targetBBox; - if (roAttrs) { - // if there was a special attribute affecting the position amongst passed-in attributes - // we have to merge it with the rest of the element's attributes as they are necessary - // to update the position relatively (i.e `ref-x` && 'ref-dx') - processedAttrs = this.processNodeAttributes(node, item.allAttributes); - this.mergeProcessedAttributes(processedAttrs, item.processedAttributes); + // If the target is a point (i.e. it's not an element), we + // should use dragging route instead of main routing method if it has been provided. + var isEndingAtPoint = !linkView.model.get('source').id || !linkView.model.get('target').id; - } else { - processedAttrs = item.processedAttributes; - } + if (isEndingAtPoint && isFunction(opt.draggingRoute)) { + // Make sure we are passing points only (not rects). + var dragFrom = (from === sourceBBox) ? sourceAnchor : from; + var dragTo = to.origin(); - var refBBox = unrotatedRefBBox; - if (isRefNodeRotatable && !vRotatable.contains(node)) { - // if the referenced node is inside the rotatable group while the updated node is outside, - // we need to take the rotatable node transformation into account - if (!rotatableMatrix) { rotatableMatrix = V.transformStringToMatrix(vRotatable.attr('transform')); } - refBBox = V.transformRect(unrotatedRefBBox, rotatableMatrix); + partialRoute = opt.draggingRoute.call(linkView, dragFrom, dragTo, opt); } - - this.updateRelativeAttributes(node, processedAttrs, refBBox, opt); - } - }, - - mergeProcessedAttributes: function(processedAttrs, roProcessedAttrs) { - - processedAttrs.set || (processedAttrs.set = {}); - processedAttrs.position || (processedAttrs.position = {}); - processedAttrs.offset || (processedAttrs.offset = {}); - - assign(processedAttrs.set, roProcessedAttrs.set); - assign(processedAttrs.position, roProcessedAttrs.position); - assign(processedAttrs.offset, roProcessedAttrs.offset); - - // Handle also the special transform property. - var transform = processedAttrs.normal && processedAttrs.normal.transform; - if (transform !== undefined && roProcessedAttrs.normal) { - roProcessedAttrs.normal.transform = transform; - } - processedAttrs.normal = roProcessedAttrs.normal; - }, - - onRemove: function() { - this.removeTools(); - this.removeHighlighters(); - }, - - _toolsView: null, - - hasTools: function(name) { - var toolsView = this._toolsView; - if (!toolsView) { return false; } - if (!name) { return true; } - return (toolsView.getName() === name); - }, - - addTools: function(toolsView) { - - this.removeTools(); - - if (toolsView) { - this._toolsView = toolsView; - toolsView.configure({ relatedView: this }); - toolsView.listenTo(this.paper, 'tools:event', this.onToolEvent.bind(this)); - } - return this; - }, - - updateTools: function(opt) { - - var toolsView = this._toolsView; - if (toolsView) { toolsView.update(opt); } - return this; - }, - - removeTools: function() { - - var toolsView = this._toolsView; - if (toolsView) { - toolsView.remove(); - this._toolsView = null; } - return this; - }, - - hideTools: function() { - - var toolsView = this._toolsView; - if (toolsView) { toolsView.hide(); } - return this; - }, - showTools: function() { - - var toolsView = this._toolsView; - if (toolsView) { toolsView.show(); } - return this; - }, + // if partial route has not been calculated yet use the main routing method to find one + partialRoute = partialRoute || findRoute.call(linkView, from, to, isPointObstacle, opt); - onToolEvent: function(event) { - switch (event) { - case 'remove': - this.removeTools(); - break; - case 'hide': - this.hideTools(); - break; - case 'show': - this.showTools(); - break; + if (partialRoute === null) { // the partial route cannot be found + return opt.fallbackRouter(vertices, opt, linkView); } - }, - - removeHighlighters: function() { - HighlighterView.remove(this); - }, - - updateHighlighters: function(dirty) { - if ( dirty === void 0 ) dirty = false; - - HighlighterView.update(this, null, dirty); - }, - - transformHighlighters: function() { - HighlighterView.transform(this); - }, - // Interaction. The controller part. - // --------------------------------- - - // Interaction is handled by the paper and delegated to the view in interest. - // `x` & `y` parameters passed to these functions represent the coordinates already snapped to the paper grid. - // If necessary, real coordinates can be obtained from the `evt` event object. - - // These functions are supposed to be overriden by the views that inherit from `joint.dia.Cell`, - // i.e. `joint.dia.Element` and `joint.dia.Link`. - - pointerdblclick: function(evt, x, y) { - - this.notify('cell:pointerdblclick', evt, x, y); - }, - - pointerclick: function(evt, x, y) { - - this.notify('cell:pointerclick', evt, x, y); - }, - - contextmenu: function(evt, x, y) { - - this.notify('cell:contextmenu', evt, x, y); - }, - - pointerdown: function(evt, x, y) { - - var ref = this; - var model = ref.model; - var graph = model.graph; - if (graph) { - model.startBatch('pointer'); - this.eventData(evt, { graph: graph }); - } + var leadPoint = partialRoute[0]; - this.notify('cell:pointerdown', evt, x, y); - }, + // remove the first point if the previous partial route had the same point as last + if (leadPoint && leadPoint.equals(tailPoint)) { partialRoute.shift(); } - pointermove: function(evt, x, y) { + // save tailPoint for next iteration + tailPoint = partialRoute[partialRoute.length - 1] || tailPoint; - this.notify('cell:pointermove', evt, x, y); - }, + Array.prototype.push.apply(newVertices, partialRoute); + } - pointerup: function(evt, x, y) { + return newVertices; + } - var ref = this.eventData(evt); - var graph = ref.graph; + // public function + var manhattan = function(vertices, opt, linkView) { + return router(vertices, assign({}, config$1, opt), linkView); + }; - this.notify('cell:pointerup', evt, x, y); + var config$2 = { - if (graph) { - // we don't want to trigger event on model as model doesn't - // need to be member of collection anymore (remove) - graph.stopBatch('pointer', { cell: this.model }); - } - }, + maxAllowedDirectionChange: 45, - mouseover: function(evt) { + // cost of a diagonal step + diagonalCost: function() { - this.notify('cell:mouseover', evt); + var step = this.step; + return Math.ceil(Math.sqrt(step * step << 1)); }, - mouseout: function(evt) { - - this.notify('cell:mouseout', evt); - }, + // an array of directions to find next points on the route + // different from start/end directions + directions: function() { - mouseenter: function(evt) { + var step = this.step; + var cost = this.cost(); + var diagonalCost = this.diagonalCost(); - this.notify('cell:mouseenter', evt); + return [ + { offsetX: step, offsetY: 0, cost: cost }, + { offsetX: step, offsetY: step, cost: diagonalCost }, + { offsetX: 0, offsetY: step, cost: cost }, + { offsetX: -step, offsetY: step, cost: diagonalCost }, + { offsetX: -step, offsetY: 0, cost: cost }, + { offsetX: -step, offsetY: -step, cost: diagonalCost }, + { offsetX: 0, offsetY: -step, cost: cost }, + { offsetX: step, offsetY: -step, cost: diagonalCost } + ]; }, - mouseleave: function(evt) { + // a simple route used in situations when main routing method fails + // (exceed max number of loop iterations, inaccessible) + fallbackRoute: function(from, to, opt) { - this.notify('cell:mouseleave', evt); - }, + // Find a route which breaks by 45 degrees ignoring all obstacles. - mousewheel: function(evt, x, y, delta) { + var theta = from.theta(to); - this.notify('cell:mousewheel', evt, x, y, delta); - }, + var route = []; - onevent: function(evt, eventName, x, y) { + var a = { x: to.x, y: from.y }; + var b = { x: from.x, y: to.y }; - this.notify(eventName, evt, x, y); - }, + if (theta % 180 > 90) { + var t = a; + a = b; + b = t; + } - onmagnet: function() { + var p1 = (theta % 90) < 45 ? a : b; + var l1 = new Line(from, p1); - // noop - }, + var alpha = 90 * Math.ceil(theta / 90); - magnetpointerdblclick: function() { + var p2 = Point.fromPolar(l1.squaredLength(), toRad(alpha + 135), p1); + var l2 = new Line(to, p2); - // noop - }, + var intersectionPoint = l1.intersection(l2); + var point = intersectionPoint ? intersectionPoint : to; - magnetcontextmenu: function() { + var directionFrom = intersectionPoint ? point : from; - // noop - }, + var quadrant = 360 / opt.directions.length; + var angleTheta = directionFrom.theta(to); + var normalizedAngle = normalizeAngle(angleTheta + (quadrant / 2)); + var directionAngle = quadrant * Math.floor(normalizedAngle / quadrant); - checkMouseleave: function checkMouseleave(evt) { - var ref = this; - var paper = ref.paper; - if (paper.isAsync()) { - // Do the updates of the current view synchronously now - paper.dumpView(this); - } - var target = this.getEventTarget(evt, { fromPoint: true }); - var view = paper.findView(target); - if (view === this) { return; } - // Leaving the current view - this.mouseleave(evt); - if (!view) { return; } - // Entering another view - view.mouseenter(evt); - }, + opt.previousDirectionAngle = directionAngle; - setInteractivity: function(value) { + if (point) { route.push(point.round()); } + route.push(to); - this.options.interactive = value; + return route; } - }, { + }; - Highlighting: HighlightingTypes, + // public function + var metro = function(vertices, opt, linkView) { - addPresentationAttributes: function(presentationAttributes) { - return merge({}, result(this.prototype, 'presentationAttributes'), presentationAttributes, function(a, b) { - if (!a || !b) { return; } - if (typeof a === 'string') { a = [a]; } - if (typeof b === 'string') { b = [b]; } - if (Array.isArray(a) && Array.isArray(b)) { return uniq(a.concat(b)); } - }); + if (!isFunction(manhattan)) { + throw new Error('Metro requires the manhattan router.'); } - }); - var Flags = { - UPDATE: 'UPDATE', - TRANSLATE: 'TRANSLATE', - TOOLS: 'TOOLS', - RESIZE: 'RESIZE', - PORTS: 'PORTS', - ROTATE: 'ROTATE', - RENDER: 'RENDER' + return manhattan(vertices, assign({}, config$2, opt), linkView); }; - // Element base view and controller. - // ------------------------------------------- - - var ElementView = CellView.extend({ - - /** - * @abstract - */ - _removePorts: function() { - // implemented in ports.js - }, - /** - * - * @abstract - */ - _renderPorts: function() { - // implemented in ports.js - }, - className: function() { + var routers = ({ + normal: normal, + oneSide: oneSide, + orthogonal: orthogonal, + manhattan: manhattan, + metro: metro + }); - var classNames = CellView.prototype.className.apply(this).split(' '); + // default size of jump if not specified in options + var JUMP_SIZE = 5; - classNames.push('element'); + // available jump types + // first one taken as default + var JUMP_TYPES = ['arc', 'gap', 'cubic']; - return classNames.join(' '); - }, + // default radius + var RADIUS = 0; - initialize: function() { + // takes care of math. error for case when jump is too close to end of line + var CLOSE_PROXIMITY_PADDING = 1; - CellView.prototype.initialize.apply(this, arguments); + // list of connector types not to jump over. + var IGNORED_CONNECTORS = ['smooth']; - this._initializePorts(); - }, + // internal constants for round segment + var _13 = 1 / 3; + var _23 = 2 / 3; - presentationAttributes: { - 'attrs': [Flags.UPDATE], - 'position': [Flags.TRANSLATE, Flags.TOOLS], - 'size': [Flags.RESIZE, Flags.PORTS, Flags.TOOLS], - 'angle': [Flags.ROTATE, Flags.TOOLS], - 'markup': [Flags.RENDER], - 'ports': [Flags.PORTS], - }, + /** + * Transform start/end and route into series of lines + * @param {g.point} sourcePoint start point + * @param {g.point} targetPoint end point + * @param {g.point[]} route optional list of route + * @return {g.line[]} [description] + */ + function createLines(sourcePoint, targetPoint, route) { + // make a flattened array of all points + var points = [].concat(sourcePoint, route, targetPoint); + return points.reduce(function(resultLines, point, idx) { + // if there is a next point, make a line with it + var nextPoint = points[idx + 1]; + if (nextPoint != null) { + resultLines[idx] = line(point, nextPoint); + } + return resultLines; + }, []); + } - initFlag: [Flags.RENDER], + function setupUpdating(jumpOverLinkView) { + var paper = jumpOverLinkView.paper; + var updateList = paper._jumpOverUpdateList; - UPDATE_PRIORITY: 0, + // first time setup for this paper + if (updateList == null) { + updateList = paper._jumpOverUpdateList = []; + var graph = paper.model; + graph.on('batch:stop', function() { + if (this.hasActiveBatch()) { return; } + updateJumpOver(paper); + }); + graph.on('reset', function() { + updateList = paper._jumpOverUpdateList = []; + }); + } - confirmUpdate: function(flag, opt) { + // add this link to a list so it can be updated when some other link is updated + if (updateList.indexOf(jumpOverLinkView) < 0) { + updateList.push(jumpOverLinkView); - var useCSSSelectors = config.useCSSSelectors; - if (this.hasFlag(flag, Flags.PORTS)) { - this._removePorts(); - this._cleanPortsCache(); - } - var transformHighlighters = false; - if (this.hasFlag(flag, Flags.RENDER)) { - this.render(); - this.updateTools(opt); - this.updateHighlighters(true); - transformHighlighters = true; - flag = this.removeFlag(flag, [Flags.RENDER, Flags.UPDATE, Flags.RESIZE, Flags.TRANSLATE, Flags.ROTATE, Flags.PORTS, Flags.TOOLS]); - } else { - var updateHighlighters = false; + // watch for change of connector type or removal of link itself + // to remove the link from a list of jump over connectors + jumpOverLinkView.listenToOnce(jumpOverLinkView.model, 'change:connector remove', function() { + updateList.splice(updateList.indexOf(jumpOverLinkView), 1); + }); + } + } - // Skip this branch if render is required - if (this.hasFlag(flag, Flags.RESIZE)) { - this.resize(opt); - updateHighlighters = true; - // Resize method is calling `update()` internally - flag = this.removeFlag(flag, [Flags.RESIZE, Flags.UPDATE]); - } - if (this.hasFlag(flag, Flags.UPDATE)) { - this.update(this.model, null, opt); - flag = this.removeFlag(flag, Flags.UPDATE); - updateHighlighters = true; - if (useCSSSelectors) { - // `update()` will render ports when useCSSSelectors are enabled - flag = this.removeFlag(flag, Flags.PORTS); - } - } - if (this.hasFlag(flag, Flags.TRANSLATE)) { - this.translate(); - flag = this.removeFlag(flag, Flags.TRANSLATE); - transformHighlighters = true; - } - if (this.hasFlag(flag, Flags.ROTATE)) { - this.rotate(); - flag = this.removeFlag(flag, Flags.ROTATE); - transformHighlighters = true; - } - if (this.hasFlag(flag, Flags.PORTS)) { - this._renderPorts(); - updateHighlighters = true; - flag = this.removeFlag(flag, Flags.PORTS); - } + /** + * Handler for a batch:stop event to force + * update of all registered links with jump over connector + * @param {object} batchEvent optional object with info about batch + */ + function updateJumpOver(paper) { + var updateList = paper._jumpOverUpdateList; + for (var i = 0; i < updateList.length; i++) { + var linkView = updateList[i]; + var updateFlag = linkView.getFlag(linkView.constructor.Flags.CONNECTOR); + linkView.requestUpdate(updateFlag); + } + } - if (updateHighlighters) { - this.updateHighlighters(false); - } + /** + * Utility function to collect all intersection points of a single + * line against group of other lines. + * @param {g.line} line where to find points + * @param {g.line[]} crossCheckLines lines to cross + * @return {g.point[]} list of intersection points + */ + function findLineIntersections(line, crossCheckLines) { + return toArray(crossCheckLines).reduce(function(res, crossCheckLine) { + var intersection = line.intersection(crossCheckLine); + if (intersection) { + res.push(intersection); } + return res; + }, []); + } - if (transformHighlighters) { - this.transformHighlighters(); - } + /** + * Sorting function for list of points by their distance. + * @param {g.point} p1 first point + * @param {g.point} p2 second point + * @return {number} squared distance between points + */ + function sortPoints(p1, p2) { + return line(p1, p2).squaredLength(); + } - if (this.hasFlag(flag, Flags.TOOLS)) { - this.updateTools(opt); - flag = this.removeFlag(flag, Flags.TOOLS); + /** + * Split input line into multiple based on intersection points. + * @param {g.line} line input line to split + * @param {g.point[]} intersections points where to split the line + * @param {number} jumpSize the size of jump arc (length empty spot on a line) + * @return {g.line[]} list of lines being split + */ + function createJumps(line$1, intersections, jumpSize) { + return intersections.reduce(function(resultLines, point$1, idx) { + // skipping points that were merged with the previous line + // to make bigger arc over multiple lines that are close to each other + if (point$1.skip === true) { + return resultLines; } - return flag; - }, - - /** - * @abstract - */ - _initializePorts: function() { - - }, + // always grab the last line from buffer and modify it + var lastLine = resultLines.pop() || line$1; - update: function(_, renderingOnlyAttrs) { + // calculate start and end of jump by moving by a given size of jump + var jumpStart = point(point$1).move(lastLine.start, -(jumpSize)); + var jumpEnd = point(point$1).move(lastLine.start, +(jumpSize)); - this.cleanNodesCache(); + // now try to look at the next intersection point + var nextPoint = intersections[idx + 1]; + if (nextPoint != null) { + var distance = jumpEnd.distance(nextPoint); + if (distance <= jumpSize) { + // next point is close enough, move the jump end by this + // difference and mark the next point to be skipped + jumpEnd = nextPoint.move(lastLine.start, distance); + nextPoint.skip = true; + } + } else { + // this block is inside of `else` as an optimization so the distance is + // not calculated when we know there are no other intersection points + var endDistance = jumpStart.distance(lastLine.end); + // if the end is too close to possible jump, draw remaining line instead of a jump + if (endDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) { + resultLines.push(lastLine); + return resultLines; + } + } - // When CSS selector strings are used, make sure no rule matches port nodes. - var useCSSSelectors = config.useCSSSelectors; - if (useCSSSelectors) { this._removePorts(); } + var startDistance = jumpEnd.distance(lastLine.start); + if (startDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) { + // if the start of line is too close to jump, draw that line instead of a jump + resultLines.push(lastLine); + return resultLines; + } - var model = this.model; - var modelAttrs = model.attr(); - this.updateDOMSubtreeAttributes(this.el, modelAttrs, { - rootBBox: new Rect(model.size()), - selectors: this.selectors, - scalableNode: this.scalableNode, - rotatableNode: this.rotatableNode, - // Use rendering only attributes if they differs from the model attributes - roAttributes: (renderingOnlyAttrs === modelAttrs) ? null : renderingOnlyAttrs - }); + // finally create a jump line + var jumpLine = line(jumpStart, jumpEnd); + // it's just simple line but with a `isJump` property + jumpLine.isJump = true; - if (useCSSSelectors) { - this._renderPorts(); - } - }, + resultLines.push( + line(lastLine.start, jumpStart), + jumpLine, + line(jumpEnd, lastLine.end) + ); + return resultLines; + }, []); + } - rotatableSelector: 'rotatable', - scalableSelector: 'scalable', - scalableNode: null, - rotatableNode: null, + /** + * Assemble `D` attribute of a SVG path by iterating given lines. + * @param {g.line[]} lines source lines to use + * @param {number} jumpSize the size of jump arc (length empty spot on a line) + * @param {number} radius the radius + * @return {string} + */ + function buildPath(lines, jumpSize, jumpType, radius) { - // `prototype.markup` is rendered by default. Set the `markup` attribute on the model if the - // default markup is not desirable. - renderMarkup: function() { + var path = new Path(); + var segment; - var element = this.model; - var markup = element.get('markup') || element.markup; - if (!markup) { throw new Error('dia.ElementView: markup required'); } - if (Array.isArray(markup)) { return this.renderJSONMarkup(markup); } - if (typeof markup === 'string') { return this.renderStringMarkup(markup); } - throw new Error('dia.ElementView: invalid markup'); - }, + // first move to the start of a first line + segment = Path.createSegment('M', lines[0].start); + path.appendSegment(segment); - renderJSONMarkup: function(markup) { + // make a paths from lines + toArray(lines).forEach(function(line, index) { - var doc = this.parseDOMJSON(markup, this.el); - var selectors = this.selectors = doc.selectors; - this.rotatableNode = V(selectors[this.rotatableSelector]) || null; - this.scalableNode = V(selectors[this.scalableSelector]) || null; - // Fragment - this.vel.append(doc.fragment); - }, + if (line.isJump) { + var angle, diff; - renderStringMarkup: function(markup) { + var control1, control2; - var vel = this.vel; - vel.append(V(markup)); - // Cache transformation groups - this.rotatableNode = vel.findOne('.rotatable'); - this.scalableNode = vel.findOne('.scalable'); + if (jumpType === 'arc') { // approximates semicircle with 2 curves + angle = -90; + // determine rotation of arc based on difference between points + diff = line.start.difference(line.end); + // make sure the arc always points up (or right) + var xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0)); + if (xAxisRotate) { angle += 180; } - var selectors = this.selectors = {}; - selectors[this.selector] = this.el; - }, + var midpoint = line.midpoint(); + var centerLine = new Line(midpoint, line.end).rotate(midpoint, angle); - render: function() { + var halfLine; - this.vel.empty(); - this.renderMarkup(); - if (this.scalableNode) { - // Double update is necessary for elements with the scalable group only - // Note the resize() triggers the other `update`. - this.update(); - } - this.resize(); - if (this.rotatableNode) { - // Translate transformation is applied on `this.el` while the rotation transformation - // on `this.rotatableNode` - this.rotate(); - this.translate(); - } else { - this.updateTransformation(); - } - if (!config.useCSSSelectors) { this._renderPorts(); } - return this; - }, + // first half + halfLine = new Line(line.start, midpoint); - resize: function(opt) { + control1 = halfLine.pointAt(2 / 3).rotate(line.start, angle); + control2 = centerLine.pointAt(1 / 3).rotate(centerLine.end, -angle); - if (this.scalableNode) { return this.sgResize(opt); } - if (this.model.attributes.angle) { this.rotate(); } - this.update(); - }, + segment = Path.createSegment('C', control1, control2, centerLine.end); + path.appendSegment(segment); - translate: function() { + // second half + halfLine = new Line(midpoint, line.end); - if (this.rotatableNode) { return this.rgTranslate(); } - this.updateTransformation(); - }, + control1 = centerLine.pointAt(1 / 3).rotate(centerLine.end, angle); + control2 = halfLine.pointAt(1 / 3).rotate(line.end, -angle); - rotate: function() { + segment = Path.createSegment('C', control1, control2, line.end); + path.appendSegment(segment); - if (this.rotatableNode) { - this.rgRotate(); - // It's necessary to call the update for the nodes outside - // the rotatable group referencing nodes inside the group - this.update(); - return; - } - this.updateTransformation(); - }, + } else if (jumpType === 'gap') { + segment = Path.createSegment('M', line.end); + path.appendSegment(segment); - updateTransformation: function() { + } else if (jumpType === 'cubic') { // approximates semicircle with 1 curve + angle = line.start.theta(line.end); - var transformation = this.getTranslateString(); - var rotateString = this.getRotateString(); - if (rotateString) { transformation += ' ' + rotateString; } - this.vel.attr('transform', transformation); - }, + var xOffset = jumpSize * 0.6; + var yOffset = jumpSize * 1.35; - getTranslateString: function() { + // determine rotation of arc based on difference between points + diff = line.start.difference(line.end); + // make sure the arc always points up (or right) + xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0)); + if (xAxisRotate) { yOffset *= -1; } - var position = this.model.attributes.position; - return 'translate(' + position.x + ',' + position.y + ')'; - }, + control1 = Point(line.start.x + xOffset, line.start.y + yOffset).rotate(line.start, angle); + control2 = Point(line.end.x - xOffset, line.end.y + yOffset).rotate(line.end, angle); - getRotateString: function() { - var attributes = this.model.attributes; - var angle = attributes.angle; - if (!angle) { return null; } - var size = attributes.size; - return 'rotate(' + angle + ',' + (size.width / 2) + ',' + (size.height / 2) + ')'; - }, + segment = Path.createSegment('C', control1, control2, line.end); + path.appendSegment(segment); + } - // Rotatable & Scalable Group - // always slower, kept mainly for backwards compatibility + } else { + var nextLine = lines[index + 1]; + if (radius == 0 || !nextLine || nextLine.isJump) { + segment = Path.createSegment('L', line.end); + path.appendSegment(segment); + } else { + buildRoundedSegment(radius, path, line.end, line.start, nextLine.end); + } + } + }); - rgRotate: function() { + return path; + } - this.rotatableNode.attr('transform', this.getRotateString()); - }, + function buildRoundedSegment(offset, path, curr, prev, next) { + var prevDistance = curr.distance(prev) / 2; + var nextDistance = curr.distance(next) / 2; - rgTranslate: function() { + var startMove = -Math.min(offset, prevDistance); + var endMove = -Math.min(offset, nextDistance); - this.vel.attr('transform', this.getTranslateString()); - }, + var roundedStart = curr.clone().move(prev, startMove).round(); + var roundedEnd = curr.clone().move(next, endMove).round(); - sgResize: function(opt) { + var control1 = new Point((_13 * roundedStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedStart.y)); + var control2 = new Point((_13 * roundedEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedEnd.y)); - var model = this.model; - var angle = model.angle(); - var size = model.size(); - var scalable = this.scalableNode; + var segment; + segment = Path.createSegment('L', roundedStart); + path.appendSegment(segment); - // Getting scalable group's bbox. - // Due to a bug in webkit's native SVG .getBBox implementation, the bbox of groups with path children includes the paths' control points. - // To work around the issue, we need to check whether there are any path elements inside the scalable group. - var recursive = false; - if (scalable.node.getElementsByTagName('path').length > 0) { - // If scalable has at least one descendant that is a path, we need to switch to recursive bbox calculation. - // If there are no path descendants, group bbox calculation works and so we can use the (faster) native function directly. - recursive = true; - } - var scalableBBox = scalable.getBBox({ recursive: recursive }); + segment = Path.createSegment('C', control1, control2, roundedEnd); + path.appendSegment(segment); + } - // Make sure `scalableBbox.width` and `scalableBbox.height` are not zero which can happen if the element does not have any content. By making - // the width/height 1, we prevent HTML errors of the type `scale(Infinity, Infinity)`. - var sx = (size.width / (scalableBBox.width || 1)); - var sy = (size.height / (scalableBBox.height || 1)); - scalable.attr('transform', 'scale(' + sx + ',' + sy + ')'); + /** + * Actual connector function that will be run on every update. + * @param {g.point} sourcePoint start point of this link + * @param {g.point} targetPoint end point of this link + * @param {g.point[]} route of this link + * @param {object} opt options + * @property {number} size optional size of a jump arc + * @return {string} created `D` attribute of SVG path + */ + var jumpover = function(sourcePoint, targetPoint, route, opt) { // eslint-disable-line max-params - // Now the interesting part. The goal is to be able to store the object geometry via just `x`, `y`, `angle`, `width` and `height` - // Order of transformations is significant but we want to reconstruct the object always in the order: - // resize(), rotate(), translate() no matter of how the object was transformed. For that to work, - // we must adjust the `x` and `y` coordinates of the object whenever we resize it (because the origin of the - // rotation changes). The new `x` and `y` coordinates are computed by canceling the previous rotation - // around the center of the resized object (which is a different origin then the origin of the previous rotation) - // and getting the top-left corner of the resulting object. Then we clean up the rotation back to what it originally was. + setupUpdating(this); - // Cancel the rotation but now around a different origin, which is the center of the scaled object. - var rotatable = this.rotatableNode; - var rotation = rotatable && rotatable.attr('transform'); - if (rotation) { + var raw = opt.raw; + var jumpSize = opt.size || JUMP_SIZE; + var jumpType = opt.jump && ('' + opt.jump).toLowerCase(); + var radius = opt.radius || RADIUS; + var ignoreConnectors = opt.ignoreConnectors || IGNORED_CONNECTORS; - rotatable.attr('transform', rotation + ' rotate(' + (-angle) + ',' + (size.width / 2) + ',' + (size.height / 2) + ')'); - var rotatableBBox = scalable.getBBox({ target: this.paper.cells }); + // grab the first jump type as a default if specified one is invalid + if (JUMP_TYPES.indexOf(jumpType) === -1) { + jumpType = JUMP_TYPES[0]; + } - // Store new x, y and perform rotate() again against the new rotation origin. - model.set('position', { x: rotatableBBox.x, y: rotatableBBox.y }, assign({ updateHandled: true }, opt)); - this.translate(); - this.rotate(); - } + var paper = this.paper; + var graph = paper.model; + var allLinks = graph.getLinks(); - // Update must always be called on non-rotated element. Otherwise, relative positioning - // would work with wrong (rotated) bounding boxes. - this.update(); - }, + // there is just one link, draw it directly + if (allLinks.length === 1) { + return buildPath( + createLines(sourcePoint, targetPoint, route), + jumpSize, jumpType, radius + ); + } - // Embedding mode methods. - // ----------------------- + var thisModel = this.model; + var thisIndex = allLinks.indexOf(thisModel); + var defaultConnector = paper.options.defaultConnector || {}; - prepareEmbedding: function(data) { - if ( data === void 0 ) data = {}; + // not all links are meant to be jumped over. + var links = allLinks.filter(function(link, idx) { + var connector = link.get('connector') || defaultConnector; - var element = data.model || this.model; - var paper = data.paper || this.paper; - var graph = paper.model; + // avoid jumping over links with connector type listed in `ignored connectors`. + if (toArray(ignoreConnectors).includes(connector.name)) { + return false; + } + // filter out links that are above this one and have the same connector type + // otherwise there would double hoops for each intersection + if (idx > thisIndex) { + return connector.name !== 'jumpover'; + } + return true; + }); - var initialZIndices = data.initialZIndices = {}; - var embeddedCells = element.getEmbeddedCells({ deep: true }); - var connectedLinks = graph.getConnectedLinks(element, { deep: true, includeEnclosed: true }); + // find views for all links + var linkViews = links.map(function(link) { + return paper.findViewByModel(link); + }); - // Note: an embedded cell can be a connect link, but it's fine - // to iterate over the cell twice. - [ - element ].concat( embeddedCells, - connectedLinks - ).forEach(function (cell) { return initialZIndices[cell.id] = cell.attributes.z; }); + // create lines for this link + var thisLines = createLines( + sourcePoint, + targetPoint, + route + ); - element.startBatch('to-front'); + // create lines for all other links + var linkLines = linkViews.map(function(linkView) { + if (linkView == null) { + return []; + } + if (linkView === this) { + return thisLines; + } + return createLines( + linkView.sourcePoint, + linkView.targetPoint, + linkView.route + ); + }, this); - // Bring the model to the front with all his embeds. - element.toFront({ deep: true, ui: true }); + // transform lines for this link by splitting with jump lines at + // points of intersection with other links + var jumpingLines = thisLines.reduce(function(resultLines, thisLine) { + // iterate all links and grab the intersections with this line + // these are then sorted by distance so the line can be split more easily - // Note that at this point cells in the collection are not sorted by z index (it's running in the batch, see - // the dia.Graph._sortOnChangeZ), so we can't assume that the last cell in the collection has the highest z. - var maxZ = graph.getElements().reduce(function (max, cell) { return Math.max(max, cell.attributes.z || 0); }, 0); + var intersections = links.reduce(function(res, link, i) { + // don't intersection with itself + if (link !== thisModel) { - // Move to front also all the inbound and outbound links that are connected - // to any of the element descendant. If we bring to front only embedded elements, - // links connected to them would stay in the background. - connectedLinks.forEach(function (link) { - if (link.attributes.z <= maxZ) { - link.set('z', maxZ + 1, { ui: true }); + var lineIntersections = findLineIntersections(thisLine, linkLines[i]); + res.push.apply(res, lineIntersections); } + return res; + }, []).sort(function(a, b) { + return sortPoints(thisLine.start, a) - sortPoints(thisLine.start, b); }); - element.stopBatch('to-front'); - - // Before we start looking for suitable parent we remove the current one. - var parentId = element.parent(); - if (parentId) { - var parent = graph.getCell(parentId); - parent.unembed(element, { ui: true }); - data.initialParentId = parentId; + if (intersections.length > 0) { + // split the line based on found intersection points + resultLines.push.apply(resultLines, createJumps(thisLine, intersections, jumpSize)); } else { - data.initialParentId = null; + // without any intersection the line goes uninterrupted + resultLines.push(thisLine); } - }, + return resultLines; + }, []); - processEmbedding: function(data, evt, x, y) { - if ( data === void 0 ) data = {}; + var path = buildPath(jumpingLines, jumpSize, jumpType, radius); + return (raw) ? path : path.serialize(); + }; + var normal$1 = function(sourcePoint, targetPoint, route, opt) { - var model = data.model || this.model; - var paper = data.paper || this.paper; - var graph = paper.model; - var ref = paper.options; - var findParentBy = ref.findParentBy; - var frontParentOnly = ref.frontParentOnly; - var validateEmbedding = ref.validateEmbedding; + var raw = opt && opt.raw; + var points = [sourcePoint].concat(route).concat([targetPoint]); - var candidates; - if (isFunction(findParentBy)) { - candidates = toArray(findParentBy.call(graph, this, evt, x, y)); - } else if (findParentBy === 'pointer') { - candidates = toArray(graph.findModelsFromPoint({ x: x, y: y })); - } else { - candidates = graph.findModelsUnderElement(model, { searchBy: findParentBy }); - } + var polyline = new Polyline(points); + var path = new Path(polyline); - candidates = candidates.filter(function (el) { - return (el instanceof Cell) && (model.id !== el.id) && !el.isEmbeddedIn(model); - }); + return (raw) ? path : path.serialize(); + }; - if (frontParentOnly) { - // pick the element with the highest `z` index - candidates = candidates.slice(-1); - } + var rounded = function(sourcePoint, targetPoint, route, opt) { - var newCandidateView = null; - var prevCandidateView = data.candidateEmbedView; + opt || (opt = {}); - // iterate over all candidates starting from the last one (has the highest z-index). - for (var i = candidates.length - 1; i >= 0; i--) { - var candidate = candidates[i]; - if (prevCandidateView && prevCandidateView.model.id == candidate.id) { - // candidate remains the same - newCandidateView = prevCandidateView; - break; - } else { - var view = candidate.findView(paper); - if (!isFunction(validateEmbedding) || validateEmbedding.call(paper, this, view)) { - // flip to the new candidate - newCandidateView = view; - break; - } - } - } + var offset = opt.radius || 10; + var raw = opt.raw; + var path = new Path(); + var segment; - if (newCandidateView && newCandidateView != prevCandidateView) { - // A new candidate view found. Highlight the new one. - this.clearEmbedding(data); - data.candidateEmbedView = newCandidateView.highlight( - newCandidateView.findProxyNode(null, 'container'), - { embedding: true } - ); - } + segment = Path.createSegment('M', sourcePoint); + path.appendSegment(segment); - if (!newCandidateView && prevCandidateView) { - // No candidate view found. Unhighlight the previous candidate. - this.clearEmbedding(data); - } - }, + var _13 = 1 / 3; + var _23 = 2 / 3; - clearEmbedding: function(data) { + var curr; + var prev, next; + var prevDistance, nextDistance; + var startMove, endMove; + var roundedStart, roundedEnd; + var control1, control2; - data || (data = {}); + for (var index = 0, n = route.length; index < n; index++) { - var candidateView = data.candidateEmbedView; - if (candidateView) { - // No candidate view found. Unhighlight the previous candidate. - candidateView.unhighlight( - candidateView.findProxyNode(null, 'container'), - { embedding: true } - ); - data.candidateEmbedView = null; - } - }, + curr = new Point(route[index]); - finalizeEmbedding: function(data) { - if ( data === void 0 ) data = {}; + prev = route[index - 1] || sourcePoint; + next = route[index + 1] || targetPoint; + prevDistance = nextDistance || (curr.distance(prev) / 2); + nextDistance = curr.distance(next) / 2; - var candidateView = data.candidateEmbedView; - var element = data.model || this.model; - var paper = data.paper || this.paper; + startMove = -Math.min(offset, prevDistance); + endMove = -Math.min(offset, nextDistance); - if (candidateView) { + roundedStart = curr.clone().move(prev, startMove).round(); + roundedEnd = curr.clone().move(next, endMove).round(); - // We finished embedding. Candidate view is chosen to become the parent of the model. - candidateView.model.embed(element, { ui: true }); - candidateView.unhighlight(candidateView.findProxyNode(null, 'container'), { embedding: true }); + control1 = new Point((_13 * roundedStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedStart.y)); + control2 = new Point((_13 * roundedEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedEnd.y)); - data.candidateEmbedView = null; + segment = Path.createSegment('L', roundedStart); + path.appendSegment(segment); - } else { + segment = Path.createSegment('C', control1, control2, roundedEnd); + path.appendSegment(segment); + } - var ref = paper.options; - var validateUnembedding = ref.validateUnembedding; - var initialParentId = data.initialParentId; - // The element was originally embedded into another element. - // The interaction would unembed the element. Let's validate - // if the element can be unembedded. - if ( - initialParentId && - typeof validateUnembedding === 'function' && - !validateUnembedding.call(paper, this) - ) { - this._disallowUnembed(data); - return; - } - } + segment = Path.createSegment('L', targetPoint); + path.appendSegment(segment); - paper.model.getConnectedLinks(element, { deep: true }).forEach(function (link) { - link.reparent({ ui: true }); - }); - }, + return (raw) ? path : path.serialize(); + }; - _disallowUnembed: function(data) { - var model = data.model; - var whenNotAllowed = data.whenNotAllowed; if ( whenNotAllowed === void 0 ) whenNotAllowed = 'revert'; - var element = model || this.model; - var paper = data.paper || this.paper; - var graph = paper.model; - switch (whenNotAllowed) { - case 'remove': { - element.remove({ ui: true }); - break; - } - case 'revert': { - var initialParentId = data.initialParentId; - var initialPosition = data.initialPosition; - var initialZIndices = data.initialZIndices; - // Revert the element's position (and the position of its embedded cells if any) - if (initialPosition) { - var x = initialPosition.x; - var y = initialPosition.y; - element.position(x, y, { deep: true, ui: true }); - } - // Revert all the z-indices changed during the embedding - if (initialZIndices) { - Object.keys(initialZIndices).forEach(function (id) { - var cell = graph.getCell(id); - if (cell) { - cell.set('z', initialZIndices[id], { ui: true }); - } - }); - } - // Revert the original parent - var parent = graph.getCell(initialParentId); - if (parent) { - parent.embed(element, { ui: true }); - } - break; - } - } - }, + var smooth = function(sourcePoint, targetPoint, route, opt) { - getDelegatedView: function() { + var raw = opt && opt.raw; + var path; - var view = this; - var model = view.model; - var paper = view.paper; + if (route && route.length !== 0) { - while (view) { - if (model.isLink()) { break; } - if (!model.isEmbedded() || view.can('stopDelegation')) { return view; } - model = model.getParentCell(); - view = paper.findViewByModel(model); - } + var points = [sourcePoint].concat(route).concat([targetPoint]); + var curves = Curve.throughPoints(points); - return null; - }, + path = new Path(curves); - findProxyNode: function(el, type) { - el || (el = this.el); - var nodeSelector = el.getAttribute((type + "-selector")); - if (nodeSelector) { - var port = this.findAttribute('port', el); - if (port) { - var proxyPortNode = this.findPortNode(port, nodeSelector); - if (proxyPortNode) { return proxyPortNode; } - } else { - var ref = this.findBySelector(nodeSelector); - var proxyNode = ref[0]; - if (proxyNode) { return proxyNode; } - } - } - return el; - }, + } else { + // if we have no route, use a default cubic bezier curve + // cubic bezier requires two control points + // the control points have `x` midway between source and target + // this produces an S-like curve - // Interaction. The controller part. - // --------------------------------- + path = new Path(); - notifyPointerdown: function notifyPointerdown(evt, x, y) { - CellView.prototype.pointerdown.call(this, evt, x, y); - this.notify('element:pointerdown', evt, x, y); - }, + var segment; - notifyPointermove: function notifyPointermove(evt, x, y) { - CellView.prototype.pointermove.call(this, evt, x, y); - this.notify('element:pointermove', evt, x, y); - }, + segment = Path.createSegment('M', sourcePoint); + path.appendSegment(segment); - notifyPointerup: function notifyPointerup(evt, x, y) { - this.notify('element:pointerup', evt, x, y); - CellView.prototype.pointerup.call(this, evt, x, y); - }, + if ((Math.abs(sourcePoint.x - targetPoint.x)) >= (Math.abs(sourcePoint.y - targetPoint.y))) { + var controlPointX = (sourcePoint.x + targetPoint.x) / 2; - pointerdblclick: function(evt, x, y) { + segment = Path.createSegment('C', controlPointX, sourcePoint.y, controlPointX, targetPoint.y, targetPoint.x, targetPoint.y); + path.appendSegment(segment); - CellView.prototype.pointerdblclick.apply(this, arguments); - this.notify('element:pointerdblclick', evt, x, y); - }, + } else { + var controlPointY = (sourcePoint.y + targetPoint.y) / 2; - pointerclick: function(evt, x, y) { + segment = Path.createSegment('C', sourcePoint.x, controlPointY, targetPoint.x, controlPointY, targetPoint.x, targetPoint.y); + path.appendSegment(segment); - CellView.prototype.pointerclick.apply(this, arguments); - this.notify('element:pointerclick', evt, x, y); - }, + } + } - contextmenu: function(evt, x, y) { + return (raw) ? path : path.serialize(); + }; - CellView.prototype.contextmenu.apply(this, arguments); - this.notify('element:contextmenu', evt, x, y); - }, + var Directions = { + AUTO: 'auto', + HORIZONTAL: 'horizontal', + VERTICAL: 'vertical', + CLOSEST_POINT: 'closest-point', + OUTWARDS: 'outwards' + }; - pointerdown: function(evt, x, y) { + var TangentDirections = { + UP: 'up', + DOWN: 'down', + LEFT: 'left', + RIGHT: 'right', + AUTO: 'auto', + CLOSEST_POINT: 'closest-point', + OUTWARDS: 'outwards' + }; - if (this.isPropagationStopped(evt)) { return; } + var curve = function(sourcePoint, targetPoint, route, opt, linkView) { + if ( route === void 0 ) route = []; + if ( opt === void 0 ) opt = {}; - this.notifyPointerdown(evt, x, y); - this.dragStart(evt, x, y); - }, + var raw = Boolean(opt.raw); + // distanceCoefficient - a coefficient of the tangent vector length relative to the distance between points. + // angleTangentCoefficient - a coefficient of the end tangents length in the case of angles larger than 45 degrees. + // tension - a Catmull-Rom curve tension parameter. + // sourceTangent - a tangent vector along the curve at the sourcePoint. + // sourceDirection - a unit direction vector along the curve at the sourcePoint. + // targetTangent - a tangent vector along the curve at the targetPoint. + // targetDirection - a unit direction vector along the curve at the targetPoint. + // precision - a rounding precision for path values. + var direction = opt.direction; if ( direction === void 0 ) direction = Directions.AUTO; + var precision = opt.precision; if ( precision === void 0 ) precision = 3; + var options = { + coeff: opt.distanceCoefficient || 0.6, + angleTangentCoefficient: opt.angleTangentCoefficient || 80, + tau: opt.tension || 0.5, + sourceTangent: opt.sourceTangent ? new Point(opt.sourceTangent) : null, + targetTangent: opt.targetTangent ? new Point(opt.targetTangent) : null + }; + if (typeof opt.sourceDirection === 'string') + { options.sourceDirection = opt.sourceDirection; } + else if (typeof opt.sourceDirection === 'number') + { options.sourceDirection = new Point(1, 0).rotate(null, opt.sourceDirection); } + else + { options.sourceDirection = opt.sourceDirection ? new Point(opt.sourceDirection).normalize() : null; } - pointermove: function(evt, x, y) { + if (typeof opt.targetDirection === 'string') + { options.targetDirection = opt.targetDirection; } + else if (typeof opt.targetDirection === 'number') + { options.targetDirection = new Point(1, 0).rotate(null, opt.targetDirection); } + else + { options.targetDirection = opt.targetDirection ? new Point(opt.targetDirection).normalize() : null; } - var data = this.eventData(evt); + var completeRoute = [sourcePoint ].concat( route.map(function (p) { return new Point(p); }), [targetPoint]); - switch (data.action) { - case 'magnet': - this.dragMagnet(evt, x, y); - break; - case 'move': - (data.delegatedView || this).drag(evt, x, y); - // eslint: no-fallthrough=false - default: - this.notifyPointermove(evt, x, y); - break; + // The calculation of a sourceTangent + var sourceTangent; + if (options.sourceTangent) { + sourceTangent = options.sourceTangent; + } else { + var sourceDirection = getSourceTangentDirection(linkView, completeRoute, direction, options); + var tangentLength = completeRoute[0].distance(completeRoute[1]) * options.coeff; + var pointsVector = completeRoute[1].difference(completeRoute[0]).normalize(); + var angle = angleBetweenVectors(sourceDirection, pointsVector); + if (angle > Math.PI / 4) { + var updatedLength = tangentLength + (angle - Math.PI / 4) * options.angleTangentCoefficient; + sourceTangent = sourceDirection.clone().scale(updatedLength, updatedLength); + } else { + sourceTangent = sourceDirection.clone().scale(tangentLength, tangentLength); } + } - // Make sure the element view data is passed along. - // It could have been wiped out in the handlers above. - this.eventData(evt, data); - }, - - pointerup: function(evt, x, y) { - - var data = this.eventData(evt); - switch (data.action) { - case 'magnet': - this.dragMagnetEnd(evt, x, y); - break; - case 'move': - (data.delegatedView || this).dragEnd(evt, x, y); - // eslint: no-fallthrough=false - default: - this.notifyPointerup(evt, x, y); + // The calculation of a targetTangent + var targetTangent; + if (options.targetTangent) { + targetTangent = options.targetTangent; + } else { + var targetDirection = getTargetTangentDirection(linkView, completeRoute, direction, options); + var last = completeRoute.length - 1; + var tangentLength$1 = completeRoute[last - 1].distance(completeRoute[last]) * options.coeff; + var pointsVector$1 = completeRoute[last - 1].difference(completeRoute[last]).normalize(); + var angle$1 = angleBetweenVectors(targetDirection, pointsVector$1); + if (angle$1 > Math.PI / 4) { + var updatedLength$1 = tangentLength$1 + (angle$1 - Math.PI / 4) * options.angleTangentCoefficient; + targetTangent = targetDirection.clone().scale(updatedLength$1, updatedLength$1); + } else { + targetTangent = targetDirection.clone().scale(tangentLength$1, tangentLength$1); } + } + + var catmullRomCurves = createCatmullRomCurves(completeRoute, sourceTangent, targetTangent, options); + var bezierCurves = catmullRomCurves.map(function (curve) { return catmullRomToBezier(curve, options); }); + var path = new Path(bezierCurves).round(precision); - var magnet = data.targetMagnet; - if (magnet) { this.magnetpointerclick(evt, magnet, x, y); } - - this.checkMouseleave(evt); - }, - - mouseover: function(evt) { - - CellView.prototype.mouseover.apply(this, arguments); - this.notify('element:mouseover', evt); - }, - - mouseout: function(evt) { - - CellView.prototype.mouseout.apply(this, arguments); - this.notify('element:mouseout', evt); - }, + return (raw) ? path : path.serialize(); + }; + curve.Directions = Directions; + curve.TangentDirections = TangentDirections; - mouseenter: function(evt) { + function getHorizontalSourceDirection(linkView, route, options) { + var sourceBBox = linkView.sourceBBox; - CellView.prototype.mouseenter.apply(this, arguments); - this.notify('element:mouseenter', evt); - }, + var sourceSide; + if (!sourceBBox.width || !sourceBBox.height) { + if (sourceBBox.x > route[1].x) + { sourceSide = 'right'; } + else + { sourceSide = 'left'; } + } else { + sourceSide = sourceBBox.sideNearestToPoint(route[0]); + } - mouseleave: function(evt) { + switch (sourceSide) { + case 'left': { + return new Point(-1, 0); + } + case 'right': + default: { + return new Point(1, 0); + } + } + } - CellView.prototype.mouseleave.apply(this, arguments); - this.notify('element:mouseleave', evt); - }, + function getHorizontalTargetDirection(linkView, route, options) { + var targetBBox = linkView.targetBBox; - mousewheel: function(evt, x, y, delta) { + var targetSide; + if (!targetBBox.width || !targetBBox.height) { + if (targetBBox.x > route[route.length - 2].x) + { targetSide = 'left'; } + else + { targetSide = 'right'; } + } else { + targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); + } - CellView.prototype.mousewheel.apply(this, arguments); - this.notify('element:mousewheel', evt, x, y, delta); - }, + switch (targetSide) { + case 'left': { + return new Point(-1, 0); + } + case 'right': + default: { + return new Point(1, 0); + } + } + } - onmagnet: function(evt, x, y) { + function getVerticalSourceDirection(linkView, route, options) { + var sourceBBox = linkView.sourceBBox; - this.dragMagnetStart(evt, x, y); - }, + var sourceSide; + if (!sourceBBox.width || !sourceBBox.height) { + if (sourceBBox.y > route[1].y) + { sourceSide = 'bottom'; } + else + { sourceSide = 'top'; } + } else { + sourceSide = sourceBBox.sideNearestToPoint(route[0]); + } - magnetpointerdblclick: function(evt, magnet, x, y) { + switch (sourceSide) { + case 'top': { + return new Point(0, -1); + } + case 'bottom': + default: { + return new Point(0, 1); + } + } + } - this.notify('element:magnet:pointerdblclick', evt, magnet, x, y); - }, + function getVerticalTargetDirection(linkView, route, options) { + var targetBBox = linkView.targetBBox; - magnetcontextmenu: function(evt, magnet, x, y) { + var targetSide; + if (!targetBBox.width || !targetBBox.height) { + if (targetBBox.y > route[route.length - 2].y) + { targetSide = 'top'; } + else + { targetSide = 'bottom'; } + } else { + targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); + } - this.notify('element:magnet:contextmenu', evt, magnet, x, y); - }, + switch (targetSide) { + case 'top': { + return new Point(0, -1); + } + case 'bottom': + default: { + return new Point(0, 1); + } + } + } - // Drag Start Handlers + function getAutoSourceDirection(linkView, route, options) { + var sourceBBox = linkView.sourceBBox; - dragStart: function(evt, x, y) { + var sourceSide; + if (!sourceBBox.width || !sourceBBox.height) { + sourceSide = sourceBBox.sideNearestToPoint(route[1]); + } else { + sourceSide = sourceBBox.sideNearestToPoint(route[0]); + } - var view = this.getDelegatedView(); - if (!view || !view.can('elementMove')) { return; } + switch (sourceSide) { + case 'top': + return new Point(0, -1); + case 'bottom': + return new Point(0, 1); + case 'right': + return new Point(1, 0); + case 'left': + return new Point(-1, 0); + } + } - this.eventData(evt, { - action: 'move', - delegatedView: view - }); + function getAutoTargetDirection(linkView, route, options) { + var targetBBox = linkView.targetBBox; + + var targetSide; + if (!targetBBox.width || !targetBBox.height) { + targetSide = targetBBox.sideNearestToPoint(route[route.length - 2]); + } else { + targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); + } - var position = view.model.position(); - view.eventData(evt, { - initialPosition: position, - pointerOffset: position.difference(x, y), - restrictedArea: this.paper.getRestrictedArea(view, x, y) - }); - }, + switch (targetSide) { + case 'top': + return new Point(0, -1); + case 'bottom': + return new Point(0, 1); + case 'right': + return new Point(1, 0); + case 'left': + return new Point(-1, 0); + } + } - dragMagnetStart: function(evt, x, y) { + function getClosestPointSourceDirection(linkView, route, options) { + return route[1].difference(route[0]).normalize(); + } - if (!this.can('addLinkFromMagnet')) { return; } + function getClosestPointTargetDirection(linkView, route, options) { + var last = route.length - 1; + return route[last - 1].difference(route[last]).normalize(); + } - var magnet = evt.currentTarget; - var paper = this.paper; - this.eventData(evt, { targetMagnet: magnet }); - evt.stopPropagation(); + function getOutwardsSourceDirection(linkView, route, options) { + var sourceBBox = linkView.sourceBBox; + var sourceCenter = sourceBBox.center(); + return route[0].difference(sourceCenter).normalize(); + } - if (paper.options.validateMagnet(this, magnet, evt)) { + function getOutwardsTargetDirection(linkView, route, options) { + var targetBBox = linkView.targetBBox; + var targetCenter = targetBBox.center(); + return route[route.length - 1].difference(targetCenter).normalize(); + } - if (paper.options.magnetThreshold <= 0) { - this.dragLinkStart(evt, magnet, x, y); + function getSourceTangentDirection(linkView, route, direction, options) { + if (options.sourceDirection) { + switch(options.sourceDirection) { + case TangentDirections.UP: + return new Point(0, -1); + case TangentDirections.DOWN: + return new Point(0, 1); + case TangentDirections.LEFT: + return new Point(-1, 0); + case TangentDirections.RIGHT: + return new Point(0, 1); + case TangentDirections.AUTO: + return getAutoSourceDirection(linkView, route, options); + case TangentDirections.CLOSEST_POINT: + return getClosestPointSourceDirection(linkView, route, options); + case TangentDirections.OUTWARDS: { + return getOutwardsSourceDirection(linkView, route, options); } + default: + return options.sourceDirection; + } + } + + switch (direction) { + case Directions.HORIZONTAL: + return getHorizontalSourceDirection(linkView, route, options); + case Directions.VERTICAL: + return getVerticalSourceDirection(linkView, route, options); + case Directions.CLOSEST_POINT: + return getClosestPointSourceDirection(linkView, route, options); + case Directions.OUTWARDS: + return getOutwardsSourceDirection(linkView, route, options); + case Directions.AUTO: + default: + return getAutoSourceDirection(linkView, route, options); + } + } - this.eventData(evt, { action: 'magnet' }); - this.stopPropagation(evt); - - } else { - - this.pointerdown(evt, x, y); + function getTargetTangentDirection(linkView, route, direction, options) { + if (options.targetDirection) { + switch(options.targetDirection) { + case TangentDirections.UP: + return new Point(0, -1); + case TangentDirections.DOWN: + return new Point(0, 1); + case TangentDirections.LEFT: + return new Point(-1, 0); + case TangentDirections.RIGHT: + return new Point(0, 1); + case TangentDirections.AUTO: + return getAutoTargetDirection(linkView, route, options); + case TangentDirections.CLOSEST_POINT: + return getClosestPointTargetDirection(linkView, route, options); + case TangentDirections.OUTWARDS: { + return getOutwardsTargetDirection(linkView, route, options); + } + default: + return options.targetDirection; } + } + + switch (direction) { + case Directions.HORIZONTAL: + return getHorizontalTargetDirection(linkView, route, options); + case Directions.VERTICAL: + return getVerticalTargetDirection(linkView, route, options); + case Directions.CLOSEST_POINT: + return getClosestPointTargetDirection(linkView, route, options); + case Directions.OUTWARDS: + return getOutwardsTargetDirection(linkView, route, options); + case Directions.AUTO: + default: + return getAutoTargetDirection(linkView, route, options); + } + } - paper.delegateDragEvents(this, evt.data); - }, + function rotateVector(vector, angle) { + var cos = Math.cos(angle); + var sin = Math.sin(angle); + var x = cos * vector.x - sin * vector.y; + var y = sin * vector.x + cos * vector.y; + vector.x = x; + vector.y = y; + } - // Drag Handlers + function angleBetweenVectors(v1, v2) { + var cos = v1.dot(v2) / (v1.magnitude() * v2.magnitude()); + if (cos < -1) { cos = -1; } + if (cos > 1) { cos = 1; } + return Math.acos(cos); + } - drag: function(evt, x, y) { + function determinant(v1, v2) { + return v1.x * v2.y - v1.y * v2.x; + } - var paper = this.paper; - var grid = paper.options.gridSize; - var element = this.model; - var data = this.eventData(evt); - var pointerOffset = data.pointerOffset; - var restrictedArea = data.restrictedArea; - var embedding = data.embedding; + function createCatmullRomCurves(points, sourceTangent, targetTangent, options) { + var tau = options.tau; + var coeff = options.coeff; + var distances = []; + var tangents = []; + var catmullRomCurves = []; + var n = points.length - 1; - // Make sure the new element's position always snaps to the current grid - var elX = snapToGrid(x + pointerOffset.x, grid); - var elY = snapToGrid(y + pointerOffset.y, grid); + for (var i = 0; i < n; i++) { + distances[i] = points[i].distance(points[i + 1]); + } - element.position(elX, elY, { restrictedArea: restrictedArea, deep: true, ui: true }); + tangents[0] = sourceTangent; + tangents[n] = targetTangent; - if (paper.options.embeddingMode) { - if (!embedding) { - // Prepare the element for embedding only if the pointer moves. - // We don't want to do unnecessary action with the element - // if an user only clicks/dblclicks on it. - this.prepareEmbedding(data); - embedding = true; - } - this.processEmbedding(data, evt, x, y); + // The calculation of tangents of vertices + for (var i$1 = 1; i$1 < n; i$1++) { + var tpPrev = (void 0); + var tpNext = (void 0); + if (i$1 === 1) { + tpPrev = points[i$1 - 1].clone().offset(tangents[i$1 - 1].x, tangents[i$1 - 1].y); + } else { + tpPrev = points[i$1 - 1].clone(); + } + if (i$1 === n - 1) { + tpNext = points[i$1 + 1].clone().offset(tangents[i$1 + 1].x, tangents[i$1 + 1].y); + } else { + tpNext = points[i$1 + 1].clone(); } + var v1 = tpPrev.difference(points[i$1]).normalize(); + var v2 = tpNext.difference(points[i$1]).normalize(); + var vAngle = angleBetweenVectors(v1, v2); - this.eventData(evt, { - embedding: embedding - }); - }, + var rot = (Math.PI - vAngle) / 2; + var t = (void 0); + var vectorDeterminant = determinant(v1, v2); + var pointsDeterminant = (void 0); + pointsDeterminant = determinant(points[i$1].difference(points[i$1 + 1]), points[i$1].difference(points[i$1 - 1])); + if (vectorDeterminant < 0) { + rot = -rot; + } + if ((vAngle < Math.PI / 2) && ((rot < 0 && pointsDeterminant < 0) || (rot > 0 && pointsDeterminant > 0))) { + rot = rot - Math.PI; + } + t = v2.clone(); + rotateVector(t, rot); + + var t1 = t.clone(); + var t2 = t.clone(); + var scaleFactor1 = distances[i$1 - 1] * coeff; + var scaleFactor2 = distances[i$1] * coeff; + t1.scale(scaleFactor1, scaleFactor1); + t2.scale(scaleFactor2, scaleFactor2); - dragMagnet: function(evt, x, y) { - this.dragLink(evt, x, y); - }, + tangents[i$1] = [t1, t2]; + } - // Drag End Handlers + // The building of a Catmull-Rom curve based of tangents of points + for (var i$2 = 0; i$2 < n; i$2++) { + var p0 = (void 0); + var p3 = (void 0); + if (i$2 === 0) { + p0 = points[i$2 + 1].difference(tangents[i$2].x / tau, tangents[i$2].y / tau); + } else { + p0 = points[i$2 + 1].difference(tangents[i$2][1].x / tau, tangents[i$2][1].y / tau); + } + if (i$2 === n - 1) { + p3 = points[i$2].clone().offset(tangents[i$2 + 1].x / tau, tangents[i$2 + 1].y / tau); + } else { + p3 = points[i$2].difference(tangents[i$2 + 1][0].x / tau, tangents[i$2 + 1][0].y / tau); + } - dragEnd: function(evt, x, y) { + catmullRomCurves[i$2] = [p0, points[i$2], points[i$2 + 1], p3]; + } + return catmullRomCurves; + } - var data = this.eventData(evt); - if (data.embedding) { this.finalizeEmbedding(data); } - }, + // The function to convert Catmull-Rom curve to Bezier curve using the tension (tau) + function catmullRomToBezier(points, options) { + var tau = options.tau; - dragMagnetEnd: function(evt, x, y) { - this.dragLinkEnd(evt, x, y); - }, + var bcp1 = new Point(); + bcp1.x = points[1].x + (points[2].x - points[0].x) / (6 * tau); + bcp1.y = points[1].y + (points[2].y - points[0].y) / (6 * tau); - magnetpointerclick: function(evt, magnet, x, y) { - var paper = this.paper; - if (paper.eventData(evt).mousemoved > paper.options.clickThreshold) { return; } - this.notify('element:magnet:pointerclick', evt, magnet, x, y); - } + var bcp2 = new Point(); + bcp2.x = points[2].x + (points[3].x - points[1].x) / (6 * tau); + bcp2.y = points[2].y + (points[3].y - points[1].y) / (6 * tau); + return new Curve( + points[1], + bcp1, + bcp2, + points[2] + ); + } - }, { - Flags: Flags, - }); - assign(ElementView.prototype, elementViewPortPrototype); + var connectors = ({ + jumpover: jumpover, + normal: normal$1, + rounded: rounded, + smooth: smooth, + curve: curve + }); var Flags$1 = { RENDER: 'RENDER', @@ -26675,840 +24262,1649 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. .rotate(angle); }, - getLabelCoordinates: function(labelPosition) { + getLabelCoordinates: function(labelPosition) { + + var transformationMatrix = this._getLabelTransformationMatrix(labelPosition); + return new Point(transformationMatrix.e, transformationMatrix.f); + }, + + getVertexIndex: function(x, y) { + + var model = this.model; + var vertices = model.vertices(); + + var vertexLength = this.getClosestPointLength(new Point(x, y)); + + var idx = 0; + for (var n = vertices.length; idx < n; idx++) { + var currentVertex = vertices[idx]; + var currentVertexLength = this.getClosestPointLength(currentVertex); + if (vertexLength < currentVertexLength) { break; } + } + + return idx; + }, + + // Interaction. The controller part. + // --------------------------------- + + notifyPointerdown: function notifyPointerdown(evt, x, y) { + CellView.prototype.pointerdown.call(this, evt, x, y); + this.notify('link:pointerdown', evt, x, y); + }, + + notifyPointermove: function notifyPointermove(evt, x, y) { + CellView.prototype.pointermove.call(this, evt, x, y); + this.notify('link:pointermove', evt, x, y); + }, + + notifyPointerup: function notifyPointerup(evt, x, y) { + this.notify('link:pointerup', evt, x, y); + CellView.prototype.pointerup.call(this, evt, x, y); + }, + + pointerdblclick: function(evt, x, y) { + + CellView.prototype.pointerdblclick.apply(this, arguments); + this.notify('link:pointerdblclick', evt, x, y); + }, + + pointerclick: function(evt, x, y) { + + CellView.prototype.pointerclick.apply(this, arguments); + this.notify('link:pointerclick', evt, x, y); + }, + + contextmenu: function(evt, x, y) { + + CellView.prototype.contextmenu.apply(this, arguments); + this.notify('link:contextmenu', evt, x, y); + }, + + pointerdown: function(evt, x, y) { + + this.notifyPointerdown(evt, x, y); + + // Backwards compatibility for the default markup + var className = evt.target.getAttribute('class'); + switch (className) { + + case 'marker-vertex': + this.dragVertexStart(evt, x, y); + return; + + case 'marker-vertex-remove': + case 'marker-vertex-remove-area': + this.dragVertexRemoveStart(evt, x, y); + return; + + case 'marker-arrowhead': + this.dragArrowheadStart(evt, x, y); + return; + + case 'connection': + case 'connection-wrap': + this.dragConnectionStart(evt, x, y); + return; + + case 'marker-source': + case 'marker-target': + return; + } + + this.dragStart(evt, x, y); + }, + + pointermove: function(evt, x, y) { + + // Backwards compatibility + var dragData = this._dragData; + if (dragData) { this.eventData(evt, dragData); } + + var data = this.eventData(evt); + switch (data.action) { + + case 'vertex-move': + this.dragVertex(evt, x, y); + break; + + case 'label-move': + this.dragLabel(evt, x, y); + break; + + case 'arrowhead-move': + this.dragArrowhead(evt, x, y); + break; + + case 'move': + this.drag(evt, x, y); + break; + } + + // Backwards compatibility + if (dragData) { assign(dragData, this.eventData(evt)); } + + this.notifyPointermove(evt, x, y); + }, + + pointerup: function(evt, x, y) { + + // Backwards compatibility + var dragData = this._dragData; + if (dragData) { + this.eventData(evt, dragData); + this._dragData = null; + } + + var data = this.eventData(evt); + switch (data.action) { + + case 'vertex-move': + this.dragVertexEnd(evt, x, y); + break; + + case 'label-move': + this.dragLabelEnd(evt, x, y); + break; + + case 'arrowhead-move': + this.dragArrowheadEnd(evt, x, y); + break; + + case 'move': + this.dragEnd(evt, x, y); + } + + this.notifyPointerup(evt, x, y); + this.checkMouseleave(evt); + }, + + mouseover: function(evt) { + + CellView.prototype.mouseover.apply(this, arguments); + this.notify('link:mouseover', evt); + }, + + mouseout: function(evt) { + + CellView.prototype.mouseout.apply(this, arguments); + this.notify('link:mouseout', evt); + }, + + mouseenter: function(evt) { + + CellView.prototype.mouseenter.apply(this, arguments); + this.notify('link:mouseenter', evt); + }, + + mouseleave: function(evt) { + + CellView.prototype.mouseleave.apply(this, arguments); + this.notify('link:mouseleave', evt); + }, + + mousewheel: function(evt, x, y, delta) { + + CellView.prototype.mousewheel.apply(this, arguments); + this.notify('link:mousewheel', evt, x, y, delta); + }, + + onevent: function(evt, eventName, x, y) { + + // Backwards compatibility + var linkTool = V(evt.target).findParentByClass('link-tool', this.el); + if (linkTool) { + // No further action to be executed + evt.stopPropagation(); + + // Allow `interactive.useLinkTools=false` + if (this.can('useLinkTools')) { + if (eventName === 'remove') { + // Built-in remove event + this.model.remove({ ui: true }); + // Do not trigger link pointerdown + return; + + } else { + // link:options and other custom events inside the link tools + this.notify(eventName, evt, x, y); + } + } + + this.notifyPointerdown(evt, x, y); + this.paper.delegateDragEvents(this, evt.data); + + } else { + CellView.prototype.onevent.apply(this, arguments); + } + }, + + onlabel: function(evt, x, y) { + + this.notifyPointerdown(evt, x, y); + + this.dragLabelStart(evt, x, y); + + var stopPropagation = this.eventData(evt).stopPropagation; + if (stopPropagation) { evt.stopPropagation(); } + }, + + // Drag Start Handlers + + dragConnectionStart: function(evt, x, y) { + + if (!this.can('vertexAdd')) { return; } + + // Store the index at which the new vertex has just been placed. + // We'll be update the very same vertex position in `pointermove()`. + var vertexIdx = this.addVertex({ x: x, y: y }, { ui: true }); + this.eventData(evt, { + action: 'vertex-move', + vertexIdx: vertexIdx + }); + }, + + dragLabelStart: function(evt, _x, _y) { + + if (this.can('labelMove')) { + + var labelNode = evt.currentTarget; + var labelIdx = parseInt(labelNode.getAttribute('label-idx'), 10); + + var positionAngle = this._getLabelPositionAngle(labelIdx); + var labelPositionArgs = this._getLabelPositionArgs(labelIdx); + var defaultLabelPositionArgs = this._getDefaultLabelPositionArgs(); + var positionArgs = this._mergeLabelPositionArgs(labelPositionArgs, defaultLabelPositionArgs); + + this.eventData(evt, { + action: 'label-move', + labelIdx: labelIdx, + positionAngle: positionAngle, + positionArgs: positionArgs, + stopPropagation: true + }); + + } else { + + // Backwards compatibility: + // If labels can't be dragged no default action is triggered. + this.eventData(evt, { stopPropagation: true }); + } + + this.paper.delegateDragEvents(this, evt.data); + }, + + dragVertexStart: function(evt, x, y) { + + if (!this.can('vertexMove')) { return; } + + var vertexNode = evt.target; + var vertexIdx = parseInt(vertexNode.getAttribute('idx'), 10); + this.eventData(evt, { + action: 'vertex-move', + vertexIdx: vertexIdx + }); + }, + + dragVertexRemoveStart: function(evt, x, y) { + + if (!this.can('vertexRemove')) { return; } + + var removeNode = evt.target; + var vertexIdx = parseInt(removeNode.getAttribute('idx'), 10); + this.model.removeVertex(vertexIdx); + }, + + dragArrowheadStart: function(evt, x, y) { + + if (!this.can('arrowheadMove')) { return; } + + var arrowheadNode = evt.target; + var arrowheadType = arrowheadNode.getAttribute('end'); + var data = this.startArrowheadMove(arrowheadType, { ignoreBackwardsCompatibility: true }); + + this.eventData(evt, data); + }, + + dragStart: function(evt, x, y) { + + if (!this.can('linkMove')) { return; } + + this.eventData(evt, { + action: 'move', + dx: x, + dy: y + }); + }, + + // Drag Handlers + dragLabel: function(evt, x, y) { + + var data = this.eventData(evt); + var label = { position: this.getLabelPosition(x, y, data.positionAngle, data.positionArgs) }; + if (this.paper.options.snapLabels) { delete label.position.offset; } + this.model.label(data.labelIdx, label); + }, + + dragVertex: function(evt, x, y) { + + var data = this.eventData(evt); + this.model.vertex(data.vertexIdx, { x: x, y: y }, { ui: true }); + }, + + dragArrowhead: function(evt, x, y) { + + if (this.paper.options.snapLinks) { + + this._snapArrowhead(evt, x, y); + + } else { + + this._connectArrowhead(this.getEventTarget(evt), x, y, this.eventData(evt)); + } + }, + + drag: function(evt, x, y) { + + var data = this.eventData(evt); + this.model.translate(x - data.dx, y - data.dy, { ui: true }); + this.eventData(evt, { + dx: x, + dy: y + }); + }, + + // Drag End Handlers + + dragLabelEnd: function() { + // noop + }, + + dragVertexEnd: function() { + // noop + }, + + dragArrowheadEnd: function(evt, x, y) { + + var data = this.eventData(evt); + var paper = this.paper; + + if (paper.options.snapLinks) { + this._snapArrowheadEnd(data); + } else { + this._connectArrowheadEnd(data, x, y); + } + + if (!paper.linkAllowed(this)) { + // If the changed link is not allowed, revert to its previous state. + this._disallow(data); + } else { + this._finishEmbedding(data); + this._notifyConnectEvent(data, evt); + } + + this._afterArrowheadMove(data); + }, + + dragEnd: function() { + // noop + }, + + _disallow: function(data) { + + switch (data.whenNotAllowed) { + + case 'remove': + this.model.remove({ ui: true }); + break; + + case 'revert': + default: + this.model.set(data.arrowhead, data.initialEnd, { ui: true }); + break; + } + }, + + _finishEmbedding: function(data) { + + // Reparent the link if embedding is enabled + if (this.paper.options.embeddingMode && this.model.reparent()) { + // Make sure we don't reverse to the original 'z' index (see afterArrowheadMove()). + data.z = null; + } + }, + + _notifyConnectEvent: function(data, evt) { + + var arrowhead = data.arrowhead; + var initialEnd = data.initialEnd; + var currentEnd = this.model.prop(arrowhead); + var endChanged = currentEnd && !Link.endsEqual(initialEnd, currentEnd); + if (endChanged) { + var paper = this.paper; + if (initialEnd.id) { + this.notify('link:disconnect', evt, paper.findViewByModel(initialEnd.id), data.initialMagnet, arrowhead); + } + if (currentEnd.id) { + this.notify('link:connect', evt, paper.findViewByModel(currentEnd.id), data.magnetUnderPointer, arrowhead); + } + } + }, + + _snapArrowhead: function(evt, x, y) { + + var ref = this; + var paper = ref.paper; + var ref$1 = paper.options; + var snapLinks = ref$1.snapLinks; + var connectionStrategy = ref$1.connectionStrategy; + var data = this.eventData(evt); + // checking view in close area of the pointer + + var r = snapLinks.radius || 50; + var viewsInArea = paper.findViewsInArea({ x: x - r, y: y - r, width: 2 * r, height: 2 * r }); + + var prevClosestView = data.closestView || null; + var prevClosestMagnet = data.closestMagnet || null; + var prevMagnetProxy = data.magnetProxy || null; + + data.closestView = data.closestMagnet = data.magnetProxy = null; + + var minDistance = Number.MAX_VALUE; + var pointer = new Point(x, y); + + viewsInArea.forEach(function(view) { + var candidates = []; + // skip connecting to the element in case '.': { magnet: false } attribute present + if (view.el.getAttribute('magnet') !== 'false') { + candidates.push({ + bbox: view.model.getBBox(), + magnet: view.el + }); + } + + view.$('[magnet]').toArray().forEach(function (magnet) { + candidates.push({ + bbox: view.getNodeBBox(magnet), + magnet: magnet + }); + }); + + candidates.forEach(function (candidate) { + var magnet = candidate.magnet; + var bbox = candidate.bbox; + // find distance from the center of the model to pointer coordinates + var distance = bbox.center().squaredDistance(pointer); + // the connection is looked up in a circle area by `distance < r` + if (distance < minDistance) { + var isAlreadyValidated = prevClosestMagnet === magnet; + if (isAlreadyValidated || paper.options.validateConnection.apply( + paper, data.validateConnectionArgs(view, (view.el === magnet) ? null : magnet) + )) { + minDistance = distance; + data.closestView = view; + data.closestMagnet = magnet; + } + } + }); + + }, this); + + var end; + var magnetProxy = null; + var closestView = data.closestView; + var closestMagnet = data.closestMagnet; + if (closestMagnet) { + magnetProxy = data.magnetProxy = closestView.findProxyNode(closestMagnet, 'highlighter'); + } + var endType = data.arrowhead; + var newClosestMagnet = (prevClosestMagnet !== closestMagnet); + if (prevClosestView && newClosestMagnet) { + prevClosestView.unhighlight(prevMagnetProxy, { + connecting: true, + snapping: true + }); + } + + if (closestView) { + var prevEnd = data.prevEnd; + var prevX = data.prevX; + var prevY = data.prevY; + data.prevX = x; + data.prevY = y; + + if (!newClosestMagnet) { + if (typeof connectionStrategy !== 'function' || (prevX === x && prevY === y)) { + // the magnet has not changed and the link's end does not depend on the x and y + return; + } + } - var transformationMatrix = this._getLabelTransformationMatrix(labelPosition); - return new Point(transformationMatrix.e, transformationMatrix.f); - }, + end = closestView.getLinkEnd(closestMagnet, x, y, this.model, endType); + if (!newClosestMagnet && isEqual(prevEnd, end)) { + // the source/target json has not changed + return; + } - getVertexIndex: function(x, y) { + data.prevEnd = end; - var model = this.model; - var vertices = model.vertices(); + if (newClosestMagnet) { + closestView.highlight(magnetProxy, { + connecting: true, + snapping: true + }); + } - var vertexLength = this.getClosestPointLength(new Point(x, y)); + } else { - var idx = 0; - for (var n = vertices.length; idx < n; idx++) { - var currentVertex = vertices[idx]; - var currentVertexLength = this.getClosestPointLength(currentVertex); - if (vertexLength < currentVertexLength) { break; } + end = { x: x, y: y }; } - return idx; - }, - - // Interaction. The controller part. - // --------------------------------- + this.model.set(endType, end || { x: x, y: y }, { ui: true }); - notifyPointerdown: function notifyPointerdown(evt, x, y) { - CellView.prototype.pointerdown.call(this, evt, x, y); - this.notify('link:pointerdown', evt, x, y); + if (prevClosestView) { + this.notify('link:snap:disconnect', evt, prevClosestView, prevClosestMagnet, endType); + } + if (closestView) { + this.notify('link:snap:connect', evt, closestView, closestMagnet, endType); + } }, - notifyPointermove: function notifyPointermove(evt, x, y) { - CellView.prototype.pointermove.call(this, evt, x, y); - this.notify('link:pointermove', evt, x, y); - }, + _snapArrowheadEnd: function(data) { - notifyPointerup: function notifyPointerup(evt, x, y) { - this.notify('link:pointerup', evt, x, y); - CellView.prototype.pointerup.call(this, evt, x, y); - }, + // Finish off link snapping. + // Everything except view unhighlighting was already done on pointermove. + var closestView = data.closestView; + var closestMagnet = data.closestMagnet; + if (closestView && closestMagnet) { - pointerdblclick: function(evt, x, y) { + closestView.unhighlight(data.magnetProxy, { connecting: true, snapping: true }); + data.magnetUnderPointer = closestView.findMagnet(closestMagnet); + } - CellView.prototype.pointerdblclick.apply(this, arguments); - this.notify('link:pointerdblclick', evt, x, y); + data.closestView = data.closestMagnet = null; }, - pointerclick: function(evt, x, y) { + _connectArrowhead: function(target, x, y, data) { - CellView.prototype.pointerclick.apply(this, arguments); - this.notify('link:pointerclick', evt, x, y); - }, + // checking views right under the pointer + var ref = this; + var paper = ref.paper; + var model = ref.model; - contextmenu: function(evt, x, y) { + if (data.eventTarget !== target) { + // Unhighlight the previous view under pointer if there was one. + if (data.magnetProxy) { + data.viewUnderPointer.unhighlight(data.magnetProxy, { + connecting: true + }); + } - CellView.prototype.contextmenu.apply(this, arguments); - this.notify('link:contextmenu', evt, x, y); - }, + var viewUnderPointer = data.viewUnderPointer = paper.findView(target); + if (viewUnderPointer) { + // If we found a view that is under the pointer, we need to find the closest + // magnet based on the real target element of the event. + var magnetUnderPointer = data.magnetUnderPointer = viewUnderPointer.findMagnet(target); + var magnetProxy = data.magnetProxy = viewUnderPointer.findProxyNode(magnetUnderPointer, 'highlighter'); - pointerdown: function(evt, x, y) { + if (magnetUnderPointer && this.paper.options.validateConnection.apply( + paper, + data.validateConnectionArgs(viewUnderPointer, magnetUnderPointer) + )) { + // If there was no magnet found, do not highlight anything and assume there + // is no view under pointer we're interested in reconnecting to. + // This can only happen if the overall element has the attribute `'.': { magnet: false }`. + if (magnetProxy) { + viewUnderPointer.highlight(magnetProxy, { + connecting: true + }); + } + } else { + // This type of connection is not valid. Disregard this magnet. + data.magnetUnderPointer = null; + data.magnetProxy = null; + } + } else { + // Make sure we'll unset previous magnet. + data.magnetUnderPointer = null; + data.magnetProxy = null; + } + } - this.notifyPointerdown(evt, x, y); + data.eventTarget = target; - // Backwards compatibility for the default markup - var className = evt.target.getAttribute('class'); - switch (className) { + model.set(data.arrowhead, { x: x, y: y }, { ui: true }); + }, - case 'marker-vertex': - this.dragVertexStart(evt, x, y); - return; + _connectArrowheadEnd: function(data, x, y) { + if ( data === void 0 ) data = {}; - case 'marker-vertex-remove': - case 'marker-vertex-remove-area': - this.dragVertexRemoveStart(evt, x, y); - return; - case 'marker-arrowhead': - this.dragArrowheadStart(evt, x, y); - return; + var ref = this; + var model = ref.model; + var viewUnderPointer = data.viewUnderPointer; + var magnetUnderPointer = data.magnetUnderPointer; + var magnetProxy = data.magnetProxy; + var arrowhead = data.arrowhead; - case 'connection': - case 'connection-wrap': - this.dragConnectionStart(evt, x, y); - return; + if (!magnetUnderPointer || !magnetProxy || !viewUnderPointer) { return; } - case 'marker-source': - case 'marker-target': - return; - } + viewUnderPointer.unhighlight(magnetProxy, { connecting: true }); - this.dragStart(evt, x, y); + // The link end is taken from the magnet under the pointer, not the proxy. + var end = viewUnderPointer.getLinkEnd(magnetUnderPointer, x, y, model, arrowhead); + model.set(arrowhead, end, { ui: true }); }, - pointermove: function(evt, x, y) { - - // Backwards compatibility - var dragData = this._dragData; - if (dragData) { this.eventData(evt, dragData); } + _beforeArrowheadMove: function(data) { - var data = this.eventData(evt); - switch (data.action) { + data.z = this.model.get('z'); + this.model.toFront(); - case 'vertex-move': - this.dragVertex(evt, x, y); - break; + // Let the pointer propagate through the link view elements so that + // the `evt.target` is another element under the pointer, not the link itself. + var style = this.el.style; + data.pointerEvents = style.pointerEvents; + style.pointerEvents = 'none'; - case 'label-move': - this.dragLabel(evt, x, y); - break; + if (this.paper.options.markAvailable) { + this._markAvailableMagnets(data); + } + }, - case 'arrowhead-move': - this.dragArrowhead(evt, x, y); - break; + _afterArrowheadMove: function(data) { - case 'move': - this.drag(evt, x, y); - break; + if (data.z !== null) { + this.model.set('z', data.z, { ui: true }); + data.z = null; } - // Backwards compatibility - if (dragData) { assign(dragData, this.eventData(evt)); } + // Put `pointer-events` back to its original value. See `_beforeArrowheadMove()` for explanation. + this.el.style.pointerEvents = data.pointerEvents; - this.notifyPointermove(evt, x, y); + if (this.paper.options.markAvailable) { + this._unmarkAvailableMagnets(data); + } }, - pointerup: function(evt, x, y) { + _createValidateConnectionArgs: function(arrowhead) { + // It makes sure the arguments for validateConnection have the following form: + // (source view, source magnet, target view, target magnet and link view) + var args = []; - // Backwards compatibility - var dragData = this._dragData; - if (dragData) { - this.eventData(evt, dragData); - this._dragData = null; - } + args[4] = arrowhead; + args[5] = this; - var data = this.eventData(evt); - switch (data.action) { + var oppositeArrowhead; + var i = 0; + var j = 0; - case 'vertex-move': - this.dragVertexEnd(evt, x, y); - break; + if (arrowhead === 'source') { + i = 2; + oppositeArrowhead = 'target'; + } else { + j = 2; + oppositeArrowhead = 'source'; + } - case 'label-move': - this.dragLabelEnd(evt, x, y); - break; + var end = this.model.get(oppositeArrowhead); - case 'arrowhead-move': - this.dragArrowheadEnd(evt, x, y); - break; + if (end.id) { + var view = args[i] = this.paper.findViewByModel(end.id); + var magnet = view.getMagnetFromLinkEnd(end); + if (magnet === view.el) { magnet = undefined; } + args[i + 1] = magnet; + } - case 'move': - this.dragEnd(evt, x, y); + function validateConnectionArgs(cellView, magnet) { + args[j] = cellView; + args[j + 1] = cellView.el === magnet ? undefined : magnet; + return args; } - this.notifyPointerup(evt, x, y); - this.checkMouseleave(evt); + return validateConnectionArgs; }, - mouseover: function(evt) { - - CellView.prototype.mouseover.apply(this, arguments); - this.notify('link:mouseover', evt); - }, + _markAvailableMagnets: function(data) { - mouseout: function(evt) { + function isMagnetAvailable(view, magnet) { + var paper = view.paper; + var validate = paper.options.validateConnection; + return validate.apply(paper, this.validateConnectionArgs(view, magnet)); + } - CellView.prototype.mouseout.apply(this, arguments); - this.notify('link:mouseout', evt); - }, + var paper = this.paper; + var elements = paper.model.getCells(); + data.marked = {}; - mouseenter: function(evt) { + for (var i = 0, n = elements.length; i < n; i++) { + var view = elements[i].findView(paper); - CellView.prototype.mouseenter.apply(this, arguments); - this.notify('link:mouseenter', evt); - }, + if (!view) { + continue; + } - mouseleave: function(evt) { + var magnets = Array.prototype.slice.call(view.el.querySelectorAll('[magnet]')); + if (view.el.getAttribute('magnet') !== 'false') { + // Element wrapping group is also a magnet + magnets.push(view.el); + } - CellView.prototype.mouseleave.apply(this, arguments); - this.notify('link:mouseleave', evt); - }, + var availableMagnets = magnets.filter(isMagnetAvailable.bind(data, view)); - mousewheel: function(evt, x, y, delta) { + if (availableMagnets.length > 0) { + // highlight all available magnets + for (var j = 0, m = availableMagnets.length; j < m; j++) { + view.highlight(availableMagnets[j], { magnetAvailability: true }); + } + // highlight the entire view + view.highlight(null, { elementAvailability: true }); - CellView.prototype.mousewheel.apply(this, arguments); - this.notify('link:mousewheel', evt, x, y, delta); + data.marked[view.model.id] = availableMagnets; + } + } }, - onevent: function(evt, eventName, x, y) { + _unmarkAvailableMagnets: function(data) { - // Backwards compatibility - var linkTool = V(evt.target).findParentByClass('link-tool', this.el); - if (linkTool) { - // No further action to be executed - evt.stopPropagation(); + var markedKeys = Object.keys(data.marked); + var id; + var markedMagnets; - // Allow `interactive.useLinkTools=false` - if (this.can('useLinkTools')) { - if (eventName === 'remove') { - // Built-in remove event - this.model.remove({ ui: true }); - // Do not trigger link pointerdown - return; + for (var i = 0, n = markedKeys.length; i < n; i++) { + id = markedKeys[i]; + markedMagnets = data.marked[id]; - } else { - // link:options and other custom events inside the link tools - this.notify(eventName, evt, x, y); + var view = this.paper.findViewByModel(id); + if (view) { + for (var j = 0, m = markedMagnets.length; j < m; j++) { + view.unhighlight(markedMagnets[j], { magnetAvailability: true }); } + view.unhighlight(null, { elementAvailability: true }); } - - this.notifyPointerdown(evt, x, y); - this.paper.delegateDragEvents(this, evt.data); - - } else { - CellView.prototype.onevent.apply(this, arguments); } + + data.marked = null; }, - onlabel: function(evt, x, y) { + startArrowheadMove: function(end, opt) { - this.notifyPointerdown(evt, x, y); + opt || (opt = {}); - this.dragLabelStart(evt, x, y); + // Allow to delegate events from an another view to this linkView in order to trigger arrowhead + // move without need to click on the actual arrowhead dom element. + var data = { + action: 'arrowhead-move', + arrowhead: end, + whenNotAllowed: opt.whenNotAllowed || 'revert', + initialMagnet: this[end + 'Magnet'] || (this[end + 'View'] ? this[end + 'View'].el : null), + initialEnd: clone(this.model.get(end)), + validateConnectionArgs: this._createValidateConnectionArgs(end) + }; - var stopPropagation = this.eventData(evt).stopPropagation; - if (stopPropagation) { evt.stopPropagation(); } - }, + this._beforeArrowheadMove(data); - // Drag Start Handlers + if (opt.ignoreBackwardsCompatibility !== true) { + this._dragData = data; + } - dragConnectionStart: function(evt, x, y) { + return data; + }, - if (!this.can('vertexAdd')) { return; } + onRemove: function() { + CellView.prototype.onRemove.apply(this, arguments); + this.unmountLabels(); + } - // Store the index at which the new vertex has just been placed. - // We'll be update the very same vertex position in `pointermove()`. - var vertexIdx = this.addVertex({ x: x, y: y }, { ui: true }); - this.eventData(evt, { - action: 'vertex-move', - vertexIdx: vertexIdx - }); - }, + }, { - dragLabelStart: function(evt, _x, _y) { + Flags: Flags$1, + }); - if (this.can('labelMove')) { + Object.defineProperty(LinkView.prototype, 'sourceBBox', { - var labelNode = evt.currentTarget; - var labelIdx = parseInt(labelNode.getAttribute('label-idx'), 10); + enumerable: true, - var positionAngle = this._getLabelPositionAngle(labelIdx); - var labelPositionArgs = this._getLabelPositionArgs(labelIdx); - var defaultLabelPositionArgs = this._getDefaultLabelPositionArgs(); - var positionArgs = this._mergeLabelPositionArgs(labelPositionArgs, defaultLabelPositionArgs); + get: function() { + var sourceView = this.sourceView; + if (!sourceView) { + var sourceDef = this.model.source(); + return new Rect(sourceDef.x, sourceDef.y); + } + var sourceMagnet = this.sourceMagnet; + if (sourceView.isNodeConnection(sourceMagnet)) { + return new Rect(this.sourceAnchor); + } + return sourceView.getNodeBBox(sourceMagnet || sourceView.el); + } - this.eventData(evt, { - action: 'label-move', - labelIdx: labelIdx, - positionAngle: positionAngle, - positionArgs: positionArgs, - stopPropagation: true - }); + }); - } else { + Object.defineProperty(LinkView.prototype, 'targetBBox', { - // Backwards compatibility: - // If labels can't be dragged no default action is triggered. - this.eventData(evt, { stopPropagation: true }); + enumerable: true, + + get: function() { + var targetView = this.targetView; + if (!targetView) { + var targetDef = this.model.target(); + return new Rect(targetDef.x, targetDef.y); + } + var targetMagnet = this.targetMagnet; + if (targetView.isNodeConnection(targetMagnet)) { + return new Rect(this.targetAnchor); } + return targetView.getNodeBBox(targetMagnet || targetView.el); + } + }); - this.paper.delegateDragEvents(this, evt.data); + var stroke = HighlighterView.extend({ + + tagName: 'path', + className: 'highlight-stroke', + attributes: { + 'pointer-events': 'none', + 'vector-effect': 'non-scaling-stroke', + 'fill': 'none' }, - dragVertexStart: function(evt, x, y) { + options: { + padding: 3, + rx: 0, + ry: 0, + useFirstSubpath: false, + attrs: { + 'stroke-width': 3, + 'stroke': '#FEB663' + } + }, - if (!this.can('vertexMove')) { return; } + getPathData: function getPathData(cellView, node) { + var ref = this; + var options = ref.options; + var useFirstSubpath = options.useFirstSubpath; + var d; + try { + var vNode = V(node); + d = vNode.convertToPathData().trim(); + if (vNode.tagName() === 'PATH' && useFirstSubpath) { + var secondSubpathIndex = d.search(/.M/i) + 1; + if (secondSubpathIndex > 0) { + d = d.substr(0, secondSubpathIndex); + } + } + } catch (error) { + // Failed to get path data from magnet element. + // Draw a rectangle around the node instead. + var nodeBBox = cellView.getNodeBoundingRect(node); + d = V.rectToPath(assign({}, options, nodeBBox.toJSON())); + } + return d; + }, - var vertexNode = evt.target; - var vertexIdx = parseInt(vertexNode.getAttribute('idx'), 10); - this.eventData(evt, { - action: 'vertex-move', - vertexIdx: vertexIdx + highlightConnection: function highlightConnection(cellView) { + this.vel.attr('d', cellView.getSerializedConnection()); + }, + + highlightNode: function highlightNode(cellView, node) { + var ref = this; + var vel = ref.vel; + var options = ref.options; + var padding = options.padding; + var layer = options.layer; + var highlightMatrix = cellView.getNodeMatrix(node); + // Add padding to the highlight element. + if (padding) { + if (!layer && node === cellView.el) { + // If the highlighter is appended to the cellView + // and we measure the size of the cellView wrapping group + // it's necessary to remove the highlighter first + vel.remove(); + } + var nodeBBox = cellView.getNodeBoundingRect(node); + var cx = nodeBBox.x + (nodeBBox.width / 2); + var cy = nodeBBox.y + (nodeBBox.height / 2); + nodeBBox = V.transformRect(nodeBBox, highlightMatrix); + var width = Math.max(nodeBBox.width, 1); + var height = Math.max(nodeBBox.height, 1); + var sx = (width + padding) / width; + var sy = (height + padding) / height; + var paddingMatrix = V.createSVGMatrix({ + a: sx, + b: 0, + c: 0, + d: sy, + e: cx - sx * cx, + f: cy - sy * cy + }); + highlightMatrix = highlightMatrix.multiply(paddingMatrix); + } + vel.attr({ + 'd': this.getPathData(cellView, node), + 'transform': V.matrixToTransformString(highlightMatrix) }); }, - dragVertexRemoveStart: function(evt, x, y) { - - if (!this.can('vertexRemove')) { return; } - - var removeNode = evt.target; - var vertexIdx = parseInt(removeNode.getAttribute('idx'), 10); - this.model.removeVertex(vertexIdx); - }, - - dragArrowheadStart: function(evt, x, y) { - - if (!this.can('arrowheadMove')) { return; } + highlight: function highlight(cellView, node) { + var ref = this; + var vel = ref.vel; + var options = ref.options; + vel.attr(options.attrs); + if (cellView.isNodeConnection(node)) { + this.highlightConnection(cellView); + } else { + this.highlightNode(cellView, node); + } + } - var arrowheadNode = evt.target; - var arrowheadType = arrowheadNode.getAttribute('end'); - var data = this.startArrowheadMove(arrowheadType, { ignoreBackwardsCompatibility: true }); + }); - this.eventData(evt, data); - }, + var MASK_CLIP = 20; - dragStart: function(evt, x, y) { + function forEachDescendant(vel, fn) { + var descendants = vel.children(); + while (descendants.length > 0) { + var descendant = descendants.shift(); + if (fn(descendant)) { + descendants.push.apply(descendants, descendant.children()); + } + } + } - if (!this.can('linkMove')) { return; } + var mask = HighlighterView.extend({ - this.eventData(evt, { - action: 'move', - dx: x, - dy: y - }); + tagName: 'rect', + className: 'highlight-mask', + attributes: { + 'pointer-events': 'none' }, - // Drag Handlers - dragLabel: function(evt, x, y) { - - var data = this.eventData(evt); - var label = { position: this.getLabelPosition(x, y, data.positionAngle, data.positionArgs) }; - if (this.paper.options.snapLabels) { delete label.position.offset; } - this.model.label(data.labelIdx, label); + options: { + padding: 3, + maskClip: MASK_CLIP, + deep: false, + attrs: { + 'stroke': '#FEB663', + 'stroke-width': 3, + 'stroke-linecap': 'butt', + 'stroke-linejoin': 'miter', + } }, - dragVertex: function(evt, x, y) { - - var data = this.eventData(evt); - this.model.vertex(data.vertexIdx, { x: x, y: y }, { ui: true }); - }, + VISIBLE: 'white', + INVISIBLE: 'black', - dragArrowhead: function(evt, x, y) { + MASK_ROOT_ATTRIBUTE_BLACKLIST: [ + 'marker-start', + 'marker-end', + 'marker-mid', + 'transform', + 'stroke-dasharray' + ], - if (this.paper.options.snapLinks) { + MASK_CHILD_ATTRIBUTE_BLACKLIST: [ + 'stroke', + 'fill', + 'stroke-width', + 'stroke-opacity', + 'stroke-dasharray', + 'fill-opacity', + 'marker-start', + 'marker-end', + 'marker-mid' + ], - this._snapArrowhead(evt, x, y); + // TODO: change the list to a function callback + MASK_REPLACE_TAGS: [ + 'FOREIGNOBJECT', + 'IMAGE', + 'USE', + 'TEXT', + 'TSPAN', + 'TEXTPATH' + ], - } else { + // TODO: change the list to a function callback + MASK_REMOVE_TAGS: [ + 'TEXT', + 'TSPAN', + 'TEXTPATH' + ], - this._connectArrowhead(this.getEventTarget(evt), x, y, this.eventData(evt)); + transformMaskChild: function transformMaskChild(cellView, childEl) { + var ref = this; + var MASK_CHILD_ATTRIBUTE_BLACKLIST = ref.MASK_CHILD_ATTRIBUTE_BLACKLIST; + var MASK_REPLACE_TAGS = ref.MASK_REPLACE_TAGS; + var MASK_REMOVE_TAGS = ref.MASK_REMOVE_TAGS; + var childTagName = childEl.tagName(); + // Do not include the element in the mask's image + if (!V.isSVGGraphicsElement(childEl) || MASK_REMOVE_TAGS.includes(childTagName)) { + childEl.remove(); + return false; } - }, - - drag: function(evt, x, y) { - - var data = this.eventData(evt); - this.model.translate(x - data.dx, y - data.dy, { ui: true }); - this.eventData(evt, { - dx: x, - dy: y + // Replace the element with a rectangle + if (MASK_REPLACE_TAGS.includes(childTagName)) { + // Note: clone() method does not change the children ids + var originalChild = cellView.vel.findOne(("#" + (childEl.id))); + if (originalChild) { + var originalNode = originalChild.node; + var childBBox = cellView.getNodeBoundingRect(originalNode); + if (cellView.model.isElement()) { + childBBox = V.transformRect(childBBox, cellView.getNodeMatrix(originalNode)); + } + var replacement = V('rect', childBBox.toJSON()); + var ref$1 = childBBox.center(); + var ox = ref$1.x; + var oy = ref$1.y; + var ref$2 = originalChild.rotate(); + var angle = ref$2.angle; + var cx = ref$2.cx; if ( cx === void 0 ) cx = ox; + var cy = ref$2.cy; if ( cy === void 0 ) cy = oy; + if (angle) { replacement.rotate(angle, cx, cy); } + // Note: it's not important to keep the same sibling index since all subnodes are filled + childEl.parent().append(replacement); + } + childEl.remove(); + return false; + } + // Keep the element, but clean it from certain attributes + MASK_CHILD_ATTRIBUTE_BLACKLIST.forEach(function (attrName) { + if (attrName === 'fill' && childEl.attr('fill') === 'none') { return; } + childEl.removeAttr(attrName); }); + return true; }, - // Drag End Handlers - - dragLabelEnd: function() { - // noop - }, - - dragVertexEnd: function() { - // noop + transformMaskRoot: function transformMaskRoot(_cellView, rootEl) { + var ref = this; + var MASK_ROOT_ATTRIBUTE_BLACKLIST = ref.MASK_ROOT_ATTRIBUTE_BLACKLIST; + MASK_ROOT_ATTRIBUTE_BLACKLIST.forEach(function (attrName) { + rootEl.removeAttr(attrName); + }); }, - dragArrowheadEnd: function(evt, x, y) { - - var data = this.eventData(evt); - var paper = this.paper; - - if (paper.options.snapLinks) { - this._snapArrowheadEnd(data); - } else { - this._connectArrowheadEnd(data, x, y); - } + getMaskShape: function getMaskShape(cellView, vel) { + var this$1 = this; - if (!paper.linkAllowed(this)) { - // If the changed link is not allowed, revert to its previous state. - this._disallow(data); + var ref = this; + var options = ref.options; + var MASK_REPLACE_TAGS = ref.MASK_REPLACE_TAGS; + var deep = options.deep; + var tagName = vel.tagName(); + var maskRoot; + if (tagName === 'G') { + if (!deep) { return null; } + maskRoot = vel.clone(); + forEachDescendant(maskRoot, function (maskChild) { return this$1.transformMaskChild(cellView, maskChild); }); } else { - this._finishEmbedding(data); - this._notifyConnectEvent(data, evt); + if (MASK_REPLACE_TAGS.includes(tagName)) { return null; } + maskRoot = vel.clone(); } - - this._afterArrowheadMove(data); + this.transformMaskRoot(cellView, maskRoot); + return maskRoot; }, - dragEnd: function() { - // noop + getMaskId: function getMaskId() { + return ("highlight-mask-" + (this.cid)); }, - _disallow: function(data) { - - switch (data.whenNotAllowed) { + getMask: function getMask(cellView, vNode) { - case 'remove': - this.model.remove({ ui: true }); - break; + var ref = this; + var VISIBLE = ref.VISIBLE; + var INVISIBLE = ref.INVISIBLE; + var options = ref.options; + var padding = options.padding; + var attrs = options.attrs; - case 'revert': - default: - this.model.set(data.arrowhead, data.initialEnd, { ui: true }); - break; + var strokeWidth = ('stroke-width' in attrs) ? attrs['stroke-width'] : 1; + var hasNodeFill = vNode.attr('fill') !== 'none'; + var magnetStrokeWidth = parseFloat(vNode.attr('stroke-width')); + if (isNaN(magnetStrokeWidth)) { magnetStrokeWidth = 1; } + // stroke of the invisible shape + var minStrokeWidth = magnetStrokeWidth + padding * 2; + // stroke of the visible shape + var maxStrokeWidth = minStrokeWidth + strokeWidth * 2; + var maskEl = this.getMaskShape(cellView, vNode); + if (!maskEl) { + var nodeBBox = cellView.getNodeBoundingRect(vNode.node); + // Make sure the rect is visible + nodeBBox.inflate(nodeBBox.width ? 0 : 0.5, nodeBBox.height ? 0 : 0.5); + maskEl = V('rect', nodeBBox.toJSON()); } + maskEl.attr(attrs); + return V('mask', { + 'id': this.getMaskId() + }).append([ + maskEl.clone().attr({ + 'fill': hasNodeFill ? VISIBLE : 'none', + 'stroke': VISIBLE, + 'stroke-width': maxStrokeWidth + }), + maskEl.clone().attr({ + 'fill': hasNodeFill ? INVISIBLE : 'none', + 'stroke': INVISIBLE, + 'stroke-width': minStrokeWidth + }) + ]); }, - _finishEmbedding: function(data) { - - // Reparent the link if embedding is enabled - if (this.paper.options.embeddingMode && this.model.reparent()) { - // Make sure we don't reverse to the original 'z' index (see afterArrowheadMove()). - data.z = null; + removeMask: function removeMask(paper) { + var maskNode = paper.svg.getElementById(this.getMaskId()); + if (maskNode) { + paper.defs.removeChild(maskNode); } }, - _notifyConnectEvent: function(data, evt) { - - var arrowhead = data.arrowhead; - var initialEnd = data.initialEnd; - var currentEnd = this.model.prop(arrowhead); - var endChanged = currentEnd && !Link.endsEqual(initialEnd, currentEnd); - if (endChanged) { - var paper = this.paper; - if (initialEnd.id) { - this.notify('link:disconnect', evt, paper.findViewByModel(initialEnd.id), data.initialMagnet, arrowhead); - } - if (currentEnd.id) { - this.notify('link:connect', evt, paper.findViewByModel(currentEnd.id), data.magnetUnderPointer, arrowhead); - } - } + addMask: function addMask(paper, maskEl) { + paper.defs.appendChild(maskEl.node); }, - _snapArrowhead: function(evt, x, y) { - + highlight: function highlight(cellView, node) { var ref = this; - var paper = ref.paper; - var ref$1 = paper.options; - var snapLinks = ref$1.snapLinks; - var connectionStrategy = ref$1.connectionStrategy; - var data = this.eventData(evt); - // checking view in close area of the pointer + var options = ref.options; + var vel = ref.vel; + var padding = options.padding; + var attrs = options.attrs; + var maskClip = options.maskClip; if ( maskClip === void 0 ) maskClip = MASK_CLIP; + var layer = options.layer; + var color = ('stroke' in attrs) ? attrs['stroke'] : '#000000'; + if (!layer && node === cellView.el) { + // If the highlighter is appended to the cellView + // and we measure the size of the cellView wrapping group + // it's necessary to remove the highlighter first + vel.remove(); + } + var highlighterBBox = cellView.getNodeBoundingRect(node).inflate(padding + maskClip); + var maskEl = this.getMask(cellView, V(node)); + this.addMask(cellView.paper, maskEl); + vel.attr(highlighterBBox.toJSON()); + vel.attr({ + 'transform': V.matrixToTransformString(cellView.getNodeMatrix(node)), + 'mask': ("url(#" + (maskEl.id) + ")"), + 'fill': color + }); + }, - var r = snapLinks.radius || 50; - var viewsInArea = paper.findViewsInArea({ x: x - r, y: y - r, width: 2 * r, height: 2 * r }); + unhighlight: function unhighlight(cellView) { + this.removeMask(cellView.paper); + } - var prevClosestView = data.closestView || null; - var prevClosestMagnet = data.closestMagnet || null; - var prevMagnetProxy = data.magnetProxy || null; + }); - data.closestView = data.closestMagnet = data.magnetProxy = null; + var opacity = HighlighterView.extend({ - var minDistance = Number.MAX_VALUE; - var pointer = new Point(x, y); + UPDATABLE: false, + MOUNTABLE: false, - viewsInArea.forEach(function(view) { - var candidates = []; - // skip connecting to the element in case '.': { magnet: false } attribute present - if (view.el.getAttribute('magnet') !== 'false') { - candidates.push({ - bbox: view.model.getBBox(), - magnet: view.el - }); - } + opacityClassName: addClassNamePrefix('highlight-opacity'), - view.$('[magnet]').toArray().forEach(function (magnet) { - candidates.push({ - bbox: view.getNodeBBox(magnet), - magnet: magnet - }); - }); + highlight: function(_cellView, node) { + V(node).addClass(this.opacityClassName); + }, - candidates.forEach(function (candidate) { - var magnet = candidate.magnet; - var bbox = candidate.bbox; - // find distance from the center of the model to pointer coordinates - var distance = bbox.center().squaredDistance(pointer); - // the connection is looked up in a circle area by `distance < r` - if (distance < minDistance) { - var isAlreadyValidated = prevClosestMagnet === magnet; - if (isAlreadyValidated || paper.options.validateConnection.apply( - paper, data.validateConnectionArgs(view, (view.el === magnet) ? null : magnet) - )) { - minDistance = distance; - data.closestView = view; - data.closestMagnet = magnet; - } - } - }); + unhighlight: function(_cellView, node) { + V(node).removeClass(this.opacityClassName); + } - }, this); + }); - var end; - var magnetProxy = null; - var closestView = data.closestView; - var closestMagnet = data.closestMagnet; - if (closestMagnet) { - magnetProxy = data.magnetProxy = closestView.findProxyNode(closestMagnet, 'highlighter'); - } - var endType = data.arrowhead; - var newClosestMagnet = (prevClosestMagnet !== closestMagnet); - if (prevClosestView && newClosestMagnet) { - prevClosestView.unhighlight(prevMagnetProxy, { - connecting: true, - snapping: true - }); - } + var className = addClassNamePrefix('highlighted'); - if (closestView) { - var prevEnd = data.prevEnd; - var prevX = data.prevX; - var prevY = data.prevY; - data.prevX = x; - data.prevY = y; + var addClass = HighlighterView.extend({ - if (!newClosestMagnet) { - if (typeof connectionStrategy !== 'function' || (prevX === x && prevY === y)) { - // the magnet has not changed and the link's end does not depend on the x and y - return; - } - } + UPDATABLE: false, + MOUNTABLE: false, - end = closestView.getLinkEnd(closestMagnet, x, y, this.model, endType); - if (!newClosestMagnet && isEqual(prevEnd, end)) { - // the source/target json has not changed - return; - } + options: { + className: className + }, - data.prevEnd = end; + highlight: function(_cellView, node) { + V(node).addClass(this.options.className); + }, - if (newClosestMagnet) { - closestView.highlight(magnetProxy, { - connecting: true, - snapping: true - }); - } + unhighlight: function(_cellView, node) { + V(node).removeClass(this.options.className); + } - } else { + }, { + // Backwards Compatibility + className: className + }); - end = { x: x, y: y }; - } - this.model.set(endType, end || { x: x, y: y }, { ui: true }); - if (prevClosestView) { - this.notify('link:snap:disconnect', evt, prevClosestView, prevClosestMagnet, endType); - } - if (closestView) { - this.notify('link:snap:connect', evt, closestView, closestMagnet, endType); - } - }, + var highlighters = ({ + stroke: stroke, + mask: mask, + opacity: opacity, + addClass: addClass + }); - _snapArrowheadEnd: function(data) { + function connectionRatio(view, _magnet, _refPoint, opt) { - // Finish off link snapping. - // Everything except view unhighlighting was already done on pointermove. - var closestView = data.closestView; - var closestMagnet = data.closestMagnet; - if (closestView && closestMagnet) { + var ratio = ('ratio' in opt) ? opt.ratio : 0.5; + return view.getPointAtRatio(ratio); + } - closestView.unhighlight(data.magnetProxy, { connecting: true, snapping: true }); - data.magnetUnderPointer = closestView.findMagnet(closestMagnet); - } + function connectionLength(view, _magnet, _refPoint, opt) { - data.closestView = data.closestMagnet = null; - }, + var length = ('length' in opt) ? opt.length : 20; + return view.getPointAtLength(length); + } - _connectArrowhead: function(target, x, y, data) { + function _connectionPerpendicular(view, _magnet, refPoint, opt) { - // checking views right under the pointer - var ref = this; - var paper = ref.paper; - var model = ref.model; + var OFFSET = 1e6; + var path = view.getConnection(); + var segmentSubdivisions = view.getConnectionSubdivisions(); + var verticalLine = new Line(refPoint.clone().offset(0, OFFSET), refPoint.clone().offset(0, -OFFSET)); + var horizontalLine = new Line(refPoint.clone().offset(OFFSET, 0), refPoint.clone().offset(-OFFSET, 0)); + var verticalIntersections = verticalLine.intersect(path, { segmentSubdivisions: segmentSubdivisions }); + var horizontalIntersections = horizontalLine.intersect(path, { segmentSubdivisions: segmentSubdivisions }); + var intersections = []; + if (verticalIntersections) { Array.prototype.push.apply(intersections, verticalIntersections); } + if (horizontalIntersections) { Array.prototype.push.apply(intersections, horizontalIntersections); } + if (intersections.length > 0) { return refPoint.chooseClosest(intersections); } + if ('fallbackAt' in opt) { + return getPointAtLink(view, opt.fallbackAt); + } + return connectionClosest(view, _magnet, refPoint, opt); + } - if (data.eventTarget !== target) { - // Unhighlight the previous view under pointer if there was one. - if (data.magnetProxy) { - data.viewUnderPointer.unhighlight(data.magnetProxy, { - connecting: true - }); - } + function _connectionClosest(view, _magnet, refPoint, _opt) { - var viewUnderPointer = data.viewUnderPointer = paper.findView(target); - if (viewUnderPointer) { - // If we found a view that is under the pointer, we need to find the closest - // magnet based on the real target element of the event. - var magnetUnderPointer = data.magnetUnderPointer = viewUnderPointer.findMagnet(target); - var magnetProxy = data.magnetProxy = viewUnderPointer.findProxyNode(magnetUnderPointer, 'highlighter'); + var closestPoint = view.getClosestPoint(refPoint); + if (!closestPoint) { return new Point(); } + return closestPoint; + } - if (magnetUnderPointer && this.paper.options.validateConnection.apply( - paper, - data.validateConnectionArgs(viewUnderPointer, magnetUnderPointer) - )) { - // If there was no magnet found, do not highlight anything and assume there - // is no view under pointer we're interested in reconnecting to. - // This can only happen if the overall element has the attribute `'.': { magnet: false }`. - if (magnetProxy) { - viewUnderPointer.highlight(magnetProxy, { - connecting: true - }); - } + function resolveRef(fn) { + return function(view, magnet, ref, opt) { + if (ref instanceof Element) { + var refView = this.paper.findView(ref); + var refPoint; + if (refView) { + if (refView.isNodeConnection(ref)) { + var distance = ('fixedAt' in opt) ? opt.fixedAt : '50%'; + refPoint = getPointAtLink(refView, distance); } else { - // This type of connection is not valid. Disregard this magnet. - data.magnetUnderPointer = null; - data.magnetProxy = null; + refPoint = refView.getNodeBBox(ref).center(); } } else { - // Make sure we'll unset previous magnet. - data.magnetUnderPointer = null; - data.magnetProxy = null; + // Something went wrong + refPoint = new Point(); } + return fn.call(this, view, magnet, refPoint, opt); } + return fn.apply(this, arguments); + }; + } - data.eventTarget = target; - - model.set(data.arrowhead, { x: x, y: y }, { ui: true }); - }, + function getPointAtLink(view, value) { + var parsedValue = parseFloat(value); + if (isPercentage(value)) { + return view.getPointAtRatio(parsedValue / 100); + } else { + return view.getPointAtLength(parsedValue); + } + } + var connectionPerpendicular = resolveRef(_connectionPerpendicular); + var connectionClosest = resolveRef(_connectionClosest); - _connectArrowheadEnd: function(data, x, y) { - if ( data === void 0 ) data = {}; + var linkAnchors = ({ + resolveRef: resolveRef, + connectionRatio: connectionRatio, + connectionLength: connectionLength, + connectionPerpendicular: connectionPerpendicular, + connectionClosest: connectionClosest + }); + function offsetPoint(p1, p2, offset) { + if (isPlainObject(offset)) { + var x = offset.x; + var y = offset.y; + if (isFinite(y)) { + var line = new Line(p2, p1); + var ref = line.parallel(y); + var start = ref.start; + var end = ref.end; + p2 = start; + p1 = end; + } + offset = x; + } + if (!isFinite(offset)) { return p1; } + var length = p1.distance(p2); + if (offset === 0 && length > 0) { return p1; } + return p1.move(p2, -Math.min(offset, length - 1)); + } - var ref = this; - var model = ref.model; - var viewUnderPointer = data.viewUnderPointer; - var magnetUnderPointer = data.magnetUnderPointer; - var magnetProxy = data.magnetProxy; - var arrowhead = data.arrowhead; + function stroke$1(magnet) { - if (!magnetUnderPointer || !magnetProxy || !viewUnderPointer) { return; } + var stroke = magnet.getAttribute('stroke-width'); + if (stroke === null) { return 0; } + return parseFloat(stroke) || 0; + } - viewUnderPointer.unhighlight(magnetProxy, { connecting: true }); + function alignLine(line, type, offset) { + if ( offset === void 0 ) offset = 0; - // The link end is taken from the magnet under the pointer, not the proxy. - var end = viewUnderPointer.getLinkEnd(magnetUnderPointer, x, y, model, arrowhead); - model.set(arrowhead, end, { ui: true }); - }, + var coordinate, a, b, direction; + var start = line.start; + var end = line.end; + switch (type) { + case 'left': + coordinate = 'x'; + a = end; + b = start; + direction = -1; + break; + case 'right': + coordinate = 'x'; + a = start; + b = end; + direction = 1; + break; + case 'top': + coordinate = 'y'; + a = end; + b = start; + direction = -1; + break; + case 'bottom': + coordinate = 'y'; + a = start; + b = end; + direction = 1; + break; + default: + return; + } + if (start[coordinate] < end[coordinate]) { + a[coordinate] = b[coordinate]; + } else { + b[coordinate] = a[coordinate]; + } + if (isFinite(offset)) { + a[coordinate] += direction * offset; + b[coordinate] += direction * offset; + } + } - _beforeArrowheadMove: function(data) { + // Connection Points - data.z = this.model.get('z'); - this.model.toFront(); + function anchorConnectionPoint(line, _view, _magnet, opt) { + var offset = opt.offset; + var alignOffset = opt.alignOffset; + var align = opt.align; + if (align) { alignLine(line, align, alignOffset); } + return offsetPoint(line.end, line.start, offset); + } - // Let the pointer propagate through the link view elements so that - // the `evt.target` is another element under the pointer, not the link itself. - var style = this.el.style; - data.pointerEvents = style.pointerEvents; - style.pointerEvents = 'none'; + function bboxIntersection(line, view, magnet, opt) { - if (this.paper.options.markAvailable) { - this._markAvailableMagnets(data); - } - }, + var bbox = view.getNodeBBox(magnet); + if (opt.stroke) { bbox.inflate(stroke$1(magnet) / 2); } + var intersections = line.intersect(bbox); + var cp = (intersections) + ? line.start.chooseClosest(intersections) + : line.end; + return offsetPoint(cp, line.start, opt.offset); + } - _afterArrowheadMove: function(data) { + function rectangleIntersection(line, view, magnet, opt) { - if (data.z !== null) { - this.model.set('z', data.z, { ui: true }); - data.z = null; - } + var angle = view.model.angle(); + if (angle === 0) { + return bboxIntersection(line, view, magnet, opt); + } - // Put `pointer-events` back to its original value. See `_beforeArrowheadMove()` for explanation. - this.el.style.pointerEvents = data.pointerEvents; + var bboxWORotation = view.getNodeUnrotatedBBox(magnet); + if (opt.stroke) { bboxWORotation.inflate(stroke$1(magnet) / 2); } + var center = bboxWORotation.center(); + var lineWORotation = line.clone().rotate(center, angle); + var intersections = lineWORotation.setLength(1e6).intersect(bboxWORotation); + var cp = (intersections) + ? lineWORotation.start.chooseClosest(intersections).rotate(center, -angle) + : line.end; + return offsetPoint(cp, line.start, opt.offset); + } - if (this.paper.options.markAvailable) { - this._unmarkAvailableMagnets(data); - } - }, + function findShapeNode(magnet) { + if (!magnet) { return null; } + var node = magnet; + do { + var tagName = node.tagName; + if (typeof tagName !== 'string') { return null; } + tagName = tagName.toUpperCase(); + if (tagName === 'G') { + node = node.firstElementChild; + } else if (tagName === 'TITLE') { + node = node.nextElementSibling; + } else { break; } + } while (node); + return node; + } - _createValidateConnectionArgs: function(arrowhead) { - // It makes sure the arguments for validateConnection have the following form: - // (source view, source magnet, target view, target magnet and link view) - var args = []; + var BNDR_SUBDIVISIONS = 'segmentSubdivisons'; + var BNDR_SHAPE_BBOX = 'shapeBBox'; - args[4] = arrowhead; - args[5] = this; + function boundaryIntersection(line, view, magnet, opt) { - var oppositeArrowhead; - var i = 0; - var j = 0; + var node, intersection; + var selector = opt.selector; + var anchor = line.end; - if (arrowhead === 'source') { - i = 2; - oppositeArrowhead = 'target'; - } else { - j = 2; - oppositeArrowhead = 'source'; - } + if (typeof selector === 'string') { + node = view.findBySelector(selector)[0]; + } else if (Array.isArray(selector)) { + node = getByPath(magnet, selector); + } else { + node = findShapeNode(magnet); + } - var end = this.model.get(oppositeArrowhead); + if (!V.isSVGGraphicsElement(node)) { + if (node === magnet || !V.isSVGGraphicsElement(magnet)) { return anchor; } + node = magnet; + } - if (end.id) { - var view = args[i] = this.paper.findViewByModel(end.id); - var magnet = view.getMagnetFromLinkEnd(end); - if (magnet === view.el) { magnet = undefined; } - args[i + 1] = magnet; - } + var localShape = view.getNodeShape(node); + var magnetMatrix = view.getNodeMatrix(node); + var translateMatrix = view.getRootTranslateMatrix(); + var rotateMatrix = view.getRootRotateMatrix(); + var targetMatrix = translateMatrix.multiply(rotateMatrix).multiply(magnetMatrix); + var localMatrix = targetMatrix.inverse(); + var localLine = V.transformLine(line, localMatrix); + var localRef = localLine.start.clone(); + var data = view.getNodeData(node); - function validateConnectionArgs(cellView, magnet) { - args[j] = cellView; - args[j + 1] = cellView.el === magnet ? undefined : magnet; - return args; - } + if (opt.insideout === false) { + if (!data[BNDR_SHAPE_BBOX]) { data[BNDR_SHAPE_BBOX] = localShape.bbox(); } + var localBBox = data[BNDR_SHAPE_BBOX]; + if (localBBox.containsPoint(localRef)) { return anchor; } + } - return validateConnectionArgs; - }, + // Caching segment subdivisions for paths + var pathOpt; + if (localShape instanceof Path) { + var precision = opt.precision || 2; + if (!data[BNDR_SUBDIVISIONS]) { data[BNDR_SUBDIVISIONS] = localShape.getSegmentSubdivisions({ precision: precision }); } + pathOpt = { + precision: precision, + segmentSubdivisions: data[BNDR_SUBDIVISIONS] + }; + } - _markAvailableMagnets: function(data) { + if (opt.extrapolate === true) { localLine.setLength(1e6); } - function isMagnetAvailable(view, magnet) { - var paper = view.paper; - var validate = paper.options.validateConnection; - return validate.apply(paper, this.validateConnectionArgs(view, magnet)); + intersection = localLine.intersect(localShape, pathOpt); + if (intersection) { + // More than one intersection + if (V.isArray(intersection)) { intersection = localRef.chooseClosest(intersection); } + } else if (opt.sticky === true) { + // No intersection, find the closest point instead + if (localShape instanceof Rect) { + intersection = localShape.pointNearestToPoint(localRef); + } else if (localShape instanceof Ellipse) { + intersection = localShape.intersectionWithLineFromCenterToPoint(localRef); + } else { + intersection = localShape.closestPoint(localRef, pathOpt); } + } - var paper = this.paper; - var elements = paper.model.getCells(); - data.marked = {}; + var cp = (intersection) ? V.transformPoint(intersection, targetMatrix) : anchor; + var cpOffset = opt.offset || 0; + if (opt.stroke) { cpOffset += stroke$1(node) / 2; } - for (var i = 0, n = elements.length; i < n; i++) { - var view = elements[i].findView(paper); + return offsetPoint(cp, line.start, cpOffset); + } - if (!view) { - continue; - } + var anchor = anchorConnectionPoint; + var bbox = bboxIntersection; + var rectangle = rectangleIntersection; + var boundary = boundaryIntersection; - var magnets = Array.prototype.slice.call(view.el.querySelectorAll('[magnet]')); - if (view.el.getAttribute('magnet') !== 'false') { - // Element wrapping group is also a magnet - magnets.push(view.el); - } + var connectionPoints = ({ + anchor: anchor, + bbox: bbox, + rectangle: rectangle, + boundary: boundary + }); - var availableMagnets = magnets.filter(isMagnetAvailable.bind(data, view)); + function bboxWrapper(method) { - if (availableMagnets.length > 0) { - // highlight all available magnets - for (var j = 0, m = availableMagnets.length; j < m; j++) { - view.highlight(availableMagnets[j], { magnetAvailability: true }); - } - // highlight the entire view - view.highlight(null, { elementAvailability: true }); + return function(view, magnet, ref, opt) { - data.marked[view.model.id] = availableMagnets; + var rotate = !!opt.rotate; + var bbox = (rotate) ? view.getNodeUnrotatedBBox(magnet) : view.getNodeBBox(magnet); + var anchor = bbox[method](); + + var dx = opt.dx; + if (dx) { + var dxPercentage = isPercentage(dx); + dx = parseFloat(dx); + if (isFinite(dx)) { + if (dxPercentage) { + dx /= 100; + dx *= bbox.width; + } + anchor.x += dx; } } - }, - - _unmarkAvailableMagnets: function(data) { - var markedKeys = Object.keys(data.marked); - var id; - var markedMagnets; - - for (var i = 0, n = markedKeys.length; i < n; i++) { - id = markedKeys[i]; - markedMagnets = data.marked[id]; - - var view = this.paper.findViewByModel(id); - if (view) { - for (var j = 0, m = markedMagnets.length; j < m; j++) { - view.unhighlight(markedMagnets[j], { magnetAvailability: true }); + var dy = opt.dy; + if (dy) { + var dyPercentage = isPercentage(dy); + dy = parseFloat(dy); + if (isFinite(dy)) { + if (dyPercentage) { + dy /= 100; + dy *= bbox.height; } - view.unhighlight(null, { elementAvailability: true }); + anchor.y += dy; } } - data.marked = null; - }, + return (rotate) ? anchor.rotate(view.model.getBBox().center(), -view.model.angle()) : anchor; + }; + } - startArrowheadMove: function(end, opt) { + function _perpendicular(view, magnet, refPoint, opt) { - opt || (opt = {}); + var angle = view.model.angle(); + var bbox = view.getNodeBBox(magnet); + var anchor = bbox.center(); + var topLeft = bbox.origin(); + var bottomRight = bbox.corner(); - // Allow to delegate events from an another view to this linkView in order to trigger arrowhead - // move without need to click on the actual arrowhead dom element. - var data = { - action: 'arrowhead-move', - arrowhead: end, - whenNotAllowed: opt.whenNotAllowed || 'revert', - initialMagnet: this[end + 'Magnet'] || (this[end + 'View'] ? this[end + 'View'].el : null), - initialEnd: clone(this.model.get(end)), - validateConnectionArgs: this._createValidateConnectionArgs(end) - }; + var padding = opt.padding; + if (!isFinite(padding)) { padding = 0; } - this._beforeArrowheadMove(data); + if ((topLeft.y + padding) <= refPoint.y && refPoint.y <= (bottomRight.y - padding)) { + var dy = (refPoint.y - anchor.y); + anchor.x += (angle === 0 || angle === 180) ? 0 : dy * 1 / Math.tan(toRad(angle)); + anchor.y += dy; + } else if ((topLeft.x + padding) <= refPoint.x && refPoint.x <= (bottomRight.x - padding)) { + var dx = (refPoint.x - anchor.x); + anchor.y += (angle === 90 || angle === 270) ? 0 : dx * Math.tan(toRad(angle)); + anchor.x += dx; + } - if (opt.ignoreBackwardsCompatibility !== true) { - this._dragData = data; - } + return anchor; + } - return data; - }, + function _midSide(view, magnet, refPoint, opt) { - onRemove: function() { - CellView.prototype.onRemove.apply(this, arguments); - this.unmountLabels(); + var rotate = !!opt.rotate; + var bbox, angle, center; + if (rotate) { + bbox = view.getNodeUnrotatedBBox(magnet); + center = view.model.getBBox().center(); + angle = view.model.angle(); + } else { + bbox = view.getNodeBBox(magnet); } - }, { - - Flags: Flags$1, - }); - - Object.defineProperty(LinkView.prototype, 'sourceBBox', { + var padding = opt.padding; + if (isFinite(padding)) { bbox.inflate(padding); } - enumerable: true, + if (rotate) { refPoint.rotate(center, angle); } - get: function() { - var sourceView = this.sourceView; - if (!sourceView) { - var sourceDef = this.model.source(); - return new Rect(sourceDef.x, sourceDef.y); - } - var sourceMagnet = this.sourceMagnet; - if (sourceView.isNodeConnection(sourceMagnet)) { - return new Rect(this.sourceAnchor); - } - return sourceView.getNodeBBox(sourceMagnet || sourceView.el); + var side = bbox.sideNearestToPoint(refPoint); + var anchor; + switch (side) { + case 'left': + anchor = bbox.leftMiddle(); + break; + case 'right': + anchor = bbox.rightMiddle(); + break; + case 'top': + anchor = bbox.topMiddle(); + break; + case 'bottom': + anchor = bbox.bottomMiddle(); + break; } - }); + return (rotate) ? anchor.rotate(center, -angle) : anchor; + } - Object.defineProperty(LinkView.prototype, 'targetBBox', { + // Can find anchor from model, when there is no selector or the link end + // is connected to a port + function _modelCenter(view, _magnet, _refPoint, opt, endType) { + return view.model.getPointFromConnectedLink(this.model, endType).offset(opt.dx, opt.dy); + } - enumerable: true, + //joint.anchors + var center = bboxWrapper('center'); + var top$2 = bboxWrapper('topMiddle'); + var bottom$2 = bboxWrapper('bottomMiddle'); + var left$2 = bboxWrapper('leftMiddle'); + var right$2 = bboxWrapper('rightMiddle'); + var topLeft = bboxWrapper('origin'); + var topRight = bboxWrapper('topRight'); + var bottomLeft = bboxWrapper('bottomLeft'); + var bottomRight = bboxWrapper('corner'); + var perpendicular = resolveRef(_perpendicular); + var midSide = resolveRef(_midSide); + var modelCenter = _modelCenter; - get: function() { - var targetView = this.targetView; - if (!targetView) { - var targetDef = this.model.target(); - return new Rect(targetDef.x, targetDef.y); - } - var targetMagnet = this.targetMagnet; - if (targetView.isNodeConnection(targetMagnet)) { - return new Rect(this.targetAnchor); - } - return targetView.getNodeBBox(targetMagnet || targetView.el); - } + var anchors = ({ + center: center, + top: top$2, + bottom: bottom$2, + left: left$2, + right: right$2, + topLeft: topLeft, + topRight: topRight, + bottomLeft: bottomLeft, + bottomRight: bottomRight, + perpendicular: perpendicular, + midSide: midSide, + modelCenter: modelCenter }); var sortingTypes = { @@ -29793,3371 +28189,4975 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. evt = normalizeEvent(evt); if (this.guard(evt, view)) { return; } - var localPoint = this.snapToGrid(evt.clientX, evt.clientY); - view.onlabel(evt, localPoint.x, localPoint.y); + var localPoint = this.snapToGrid(evt.clientX, evt.clientY); + view.onlabel(evt, localPoint.x, localPoint.y); + } + }, + + getPointerArgs: function getPointerArgs(evt) { + var normalizedEvt = normalizeEvent(evt); + var ref = this.snapToGrid(normalizedEvt.clientX, normalizedEvt.clientY); + var x = ref.x; + var y = ref.y; + return [normalizedEvt, x, y]; + }, + + delegateDragEvents: function(view, data) { + + data || (data = {}); + this.eventData({ data: data }, { sourceView: view || null, mousemoved: 0 }); + this.delegateDocumentEvents(null, data); + this.undelegateEvents(); + }, + + // Guard the specified event. If the event is not interesting, guard returns `true`. + // Otherwise, it returns `false`. + guard: function(evt, view) { + + if (evt.type === 'mousedown' && evt.button === 2) { + // handled as `contextmenu` type + return true; + } + + if (this.options.guard && this.options.guard(evt, view)) { + return true; + } + + if (evt.data && evt.data.guarded !== undefined) { + return evt.data.guarded; + } + + if (view && view.model && (view.model instanceof Cell)) { + return false; + } + + if (this.svg === evt.target || this.el === evt.target || $.contains(this.svg, evt.target)) { + return false; + } + + return true; // Event guarded. Paper should not react on it in any way. + }, + + setGridSize: function(gridSize) { + + this.options.gridSize = gridSize; + + if (this.options.drawGrid) { + this.drawGrid(); + } + + return this; + }, + + clearGrid: function() { + + if (this.$grid) { + this.$grid.css('backgroundImage', 'none'); + } + return this; + }, + + _getGridRefs: function() { + + if (!this._gridCache) { + + this._gridCache = { + root: V('svg', { width: '100%', height: '100%' }, V('defs')), + patterns: {}, + add: function(id, vel) { + V(this.root.node.childNodes[0]).append(vel); + this.patterns[id] = vel; + this.root.append(V('rect', { width: '100%', height: '100%', fill: 'url(#' + id + ')' })); + }, + get: function(id) { + return this.patterns[id]; + }, + exist: function(id) { + return this.patterns[id] !== undefined; + } + }; + } + + return this._gridCache; + }, + + setGrid: function(drawGrid) { + + this.clearGrid(); + + this._gridCache = null; + this._gridSettings = []; + + var optionsList = Array.isArray(drawGrid) ? drawGrid : [drawGrid || {}]; + optionsList.forEach(function(item) { + this._gridSettings.push.apply(this._gridSettings, this._resolveDrawGridOption(item)); + }, this); + return this; + }, + + _resolveDrawGridOption: function(opt) { + + var namespace = this.constructor.gridPatterns; + if (isString(opt) && Array.isArray(namespace[opt])) { + return namespace[opt].map(function(item) { + return assign({}, item); + }); + } + + var options = opt || { args: [{}] }; + var isArray = Array.isArray(options); + var name = options.name; + + if (!isArray && !name && !options.markup) { + name = 'dot'; + } + + if (name && Array.isArray(namespace[name])) { + var pattern = namespace[name].map(function(item) { + return assign({}, item); + }); + + var args = Array.isArray(options.args) ? options.args : [options.args || {}]; + + defaults(args[0], omit(opt, 'args')); + for (var i = 0; i < args.length; i++) { + if (pattern[i]) { + assign(pattern[i], args[i]); + } + } + return pattern; + } + + return isArray ? options : [options]; + }, + + drawGrid: function(opt) { + + var gridSize = this.options.gridSize; + if (gridSize <= 1) { + return this.clearGrid(); + } + + var localOptions = Array.isArray(opt) ? opt : [opt]; + + var ctm = this.matrix(); + var refs = this._getGridRefs(); + + this._gridSettings.forEach(function(gridLayerSetting, index) { + + var id = 'pattern_' + index; + var options = merge(gridLayerSetting, localOptions[index], { + sx: ctm.a || 1, + sy: ctm.d || 1, + ox: ctm.e || 0, + oy: ctm.f || 0 + }); + + options.width = gridSize * (ctm.a || 1) * (options.scaleFactor || 1); + options.height = gridSize * (ctm.d || 1) * (options.scaleFactor || 1); + + if (!refs.exist(id)) { + refs.add(id, V('pattern', { id: id, patternUnits: 'userSpaceOnUse' }, V(options.markup))); + } + + var patternDefVel = refs.get(id); + + if (isFunction(options.update)) { + options.update(patternDefVel.node.childNodes[0], options); + } + + var x = options.ox % options.width; + if (x < 0) { x += options.width; } + + var y = options.oy % options.height; + if (y < 0) { y += options.height; } + + patternDefVel.attr({ + x: x, + y: y, + width: options.width, + height: options.height + }); + }); + + var patternUri = new XMLSerializer().serializeToString(refs.root.node); + patternUri = 'url(data:image/svg+xml;base64,' + btoa(patternUri) + ')'; + + this.$grid.css('backgroundImage', patternUri); + + return this; + }, + + updateBackgroundImage: function(opt) { + + opt = opt || {}; + + var backgroundPosition = opt.position || 'center'; + var backgroundSize = opt.size || 'auto auto'; + + var currentScale = this.scale(); + var currentTranslate = this.translate(); + + // backgroundPosition + if (isObject$1(backgroundPosition)) { + var x = currentTranslate.tx + (currentScale.sx * (backgroundPosition.x || 0)); + var y = currentTranslate.ty + (currentScale.sy * (backgroundPosition.y || 0)); + backgroundPosition = x + 'px ' + y + 'px'; + } + + // backgroundSize + if (isObject$1(backgroundSize)) { + backgroundSize = new Rect(backgroundSize).scale(currentScale.sx, currentScale.sy); + backgroundSize = backgroundSize.width + 'px ' + backgroundSize.height + 'px'; + } + + this.$background.css({ + backgroundSize: backgroundSize, + backgroundPosition: backgroundPosition + }); + }, + + drawBackgroundImage: function(img, opt) { + + // Clear the background image if no image provided + if (!(img instanceof HTMLImageElement)) { + this.$background.css('backgroundImage', ''); + return; + } + + if (!this._background || this._background.id !== opt.id) { + // Draw only the last image requested (see drawBackground()) + return; + } + + opt = opt || {}; + + var backgroundImage; + var backgroundSize = opt.size; + var backgroundRepeat = opt.repeat || 'no-repeat'; + var backgroundOpacity = opt.opacity || 1; + var backgroundQuality = Math.abs(opt.quality) || 1; + var backgroundPattern = this.constructor.backgroundPatterns[camelCase(backgroundRepeat)]; + + if (isFunction(backgroundPattern)) { + // 'flip-x', 'flip-y', 'flip-xy', 'watermark' and custom + img.width *= backgroundQuality; + img.height *= backgroundQuality; + var canvas = backgroundPattern(img, opt); + if (!(canvas instanceof HTMLCanvasElement)) { + throw new Error('dia.Paper: background pattern must return an HTML Canvas instance'); + } + + backgroundImage = canvas.toDataURL('image/png'); + backgroundRepeat = 'repeat'; + if (isObject$1(backgroundSize)) { + // recalculate the tile size if an object passed in + backgroundSize.width *= canvas.width / img.width; + backgroundSize.height *= canvas.height / img.height; + } else if (backgroundSize === undefined) { + // calculate the tile size if no provided + opt.size = { + width: canvas.width / backgroundQuality, + height: canvas.height / backgroundQuality + }; + } + } else { + // backgroundRepeat: + // no-repeat', 'round', 'space', 'repeat', 'repeat-x', 'repeat-y' + backgroundImage = img.src; + if (backgroundSize === undefined) { + // pass the image size for the backgroundSize if no size provided + opt.size = { + width: img.width, + height: img.height + }; + } + } + + this.$background.css({ + opacity: backgroundOpacity, + backgroundRepeat: backgroundRepeat, + backgroundImage: 'url(' + backgroundImage + ')' + }); + + this.updateBackgroundImage(opt); + }, + + updateBackgroundColor: function(color) { + + this.$el.css('backgroundColor', color || ''); + }, + + drawBackground: function(opt) { + + opt = opt || {}; + + this.updateBackgroundColor(opt.color); + + if (opt.image) { + opt = this._background = cloneDeep(opt); + guid(opt); + var img = document.createElement('img'); + img.onload = this.drawBackgroundImage.bind(this, img, opt); + img.src = opt.image; + } else { + this.drawBackgroundImage(null); + this._background = null; + } + + return this; + }, + + setInteractivity: function(value) { + + this.options.interactive = value; + + invoke(this._views, 'setInteractivity', value); + }, + + // Paper definitions. + // ------------------ + + isDefined: function(defId) { + + return !!this.svg.getElementById(defId); + }, + + defineFilter: function(filter$1) { + + if (!isObject$1(filter$1)) { + throw new TypeError('dia.Paper: defineFilter() requires 1. argument to be an object.'); + } + + var filterId = filter$1.id; + var name = filter$1.name; + // Generate a hash code from the stringified filter definition. This gives us + // a unique filter ID for different definitions. + if (!filterId) { + filterId = name + this.svg.id + hashCode(JSON.stringify(filter$1)); + } + // If the filter already exists in the document, + // we're done and we can just use it (reference it using `url()`). + // If not, create one. + if (!this.isDefined(filterId)) { + + var namespace = filter; + var filterSVGString = namespace[name] && namespace[name](filter$1.args || {}); + if (!filterSVGString) { + throw new Error('Non-existing filter ' + name); + } + + // Set the filter area to be 3x the bounding box of the cell + // and center the filter around the cell. + var filterAttrs = assign({ + filterUnits: 'objectBoundingBox', + x: -1, + y: -1, + width: 3, + height: 3 + }, filter$1.attrs, { + id: filterId + }); + + V(filterSVGString, filterAttrs).appendTo(this.defs); } - }, - getPointerArgs: function getPointerArgs(evt) { - var normalizedEvt = normalizeEvent(evt); - var ref = this.snapToGrid(normalizedEvt.clientX, normalizedEvt.clientY); - var x = ref.x; - var y = ref.y; - return [normalizedEvt, x, y]; + return filterId; }, - delegateDragEvents: function(view, data) { + defineGradient: function(gradient) { + if (!isObject$1(gradient)) { + throw new TypeError('dia.Paper: defineGradient() requires 1. argument to be an object.'); + } + var ref = this; + var svg = ref.svg; + var defs = ref.defs; + var type = gradient.type; + var id = gradient.id; if ( id === void 0 ) id = type + svg.id + hashCode(JSON.stringify(gradient)); + var stops = gradient.stops; + var attrs = gradient.attrs; if ( attrs === void 0 ) attrs = {}; + // If the gradient already exists in the document, + // we're done and we can just use it (reference it using `url()`). + if (this.isDefined(id)) { return id; } + // If not, create one. + var stopVEls = toArray(stops).map(function (ref) { + var offset = ref.offset; + var color = ref.color; + var opacity = ref.opacity; - data || (data = {}); - this.eventData({ data: data }, { sourceView: view || null, mousemoved: 0 }); - this.delegateDocumentEvents(null, data); - this.undelegateEvents(); + return V('stop').attr({ + 'offset': offset, + 'stop-color': color, + 'stop-opacity': Number.isFinite(opacity) ? opacity : 1 + }); + }); + var gradientVEl = V(type, attrs, stopVEls); + gradientVEl.id = id; + gradientVEl.appendTo(defs); + return id; }, - // Guard the specified event. If the event is not interesting, guard returns `true`. - // Otherwise, it returns `false`. - guard: function(evt, view) { - - if (evt.type === 'mousedown' && evt.button === 2) { - // handled as `contextmenu` type - return true; + definePattern: function(pattern) { + if (!isObject$1(pattern)) { + throw new TypeError('dia.Paper: definePattern() requires 1. argument to be an object.'); } - - if (this.options.guard && this.options.guard(evt, view)) { - return true; + var ref = this; + var svg = ref.svg; + var defs = ref.defs; + var id = pattern.id; if ( id === void 0 ) id = svg.id + hashCode(JSON.stringify(pattern)); + var markup = pattern.markup; + var attrs = pattern.attrs; if ( attrs === void 0 ) attrs = {}; + if (!markup) { + throw new TypeError('dia.Paper: definePattern() requires markup.'); } - - if (evt.data && evt.data.guarded !== undefined) { - return evt.data.guarded; + // If the gradient already exists in the document, + // we're done and we can just use it (reference it using `url()`). + if (this.isDefined(id)) { return id; } + // If not, create one. + var patternVEl = V('pattern', { + patternUnits: 'userSpaceOnUse' + }); + patternVEl.id = id; + patternVEl.attr(attrs); + if (typeof markup === 'string') { + patternVEl.append(V(markup)); + } else { + var ref$1 = parseDOMJSON(markup); + var fragment = ref$1.fragment; + patternVEl.append(fragment); } + patternVEl.appendTo(defs); + return id; + }, - if (view && view.model && (view.model instanceof Cell)) { - return false; + defineMarker: function(marker) { + if (!isObject$1(marker)) { + throw new TypeError('dia.Paper: defineMarker() requires 1. argument to be an object.'); } - - if (this.svg === evt.target || this.el === evt.target || $.contains(this.svg, evt.target)) { - return false; + var ref = this; + var svg = ref.svg; + var defs = ref.defs; + var id = marker.id; if ( id === void 0 ) id = svg.id + hashCode(JSON.stringify(marker)); + var markup = marker.markup; + var attrs = marker.attrs; if ( attrs === void 0 ) attrs = {}; + var markerUnits = marker.markerUnits; if ( markerUnits === void 0 ) markerUnits = 'userSpaceOnUse'; + // If the marker already exists in the document, + // we're done and we can just use it (reference it using `url()`). + if (this.isDefined(id)) { return id; } + // If not, create one. + var markerVEl = V('marker', { + orient: 'auto', + overflow: 'visible', + markerUnits: markerUnits + }); + markerVEl.id = id; + markerVEl.attr(attrs); + if (markup) { + if (typeof markup === 'string') { + markerVEl.append(V(markup)); + } else { + var ref$1 = parseDOMJSON(markup); + var fragment = ref$1.fragment; + markerVEl.append(fragment); + } + } else { + // marker object is a flat structure + var type = marker.type; if ( type === void 0 ) type = 'path'; + var markerContentVEl = V(type, omit(marker, 'type', 'id', 'markup', 'attrs', 'markerUnits')); + markerVEl.append(markerContentVEl); } + markerVEl.appendTo(defs); + return id; + } - return true; // Event guarded. Paper should not react on it in any way. - }, + }, { - setGridSize: function(gridSize) { + sorting: sortingTypes, - this.options.gridSize = gridSize; + Layers: LayersNames, - if (this.options.drawGrid) { - this.drawGrid(); - } + backgroundPatterns: { - return this; - }, + flipXy: function(img) { + // d b + // q p - clearGrid: function() { + var canvas = document.createElement('canvas'); + var imgWidth = img.width; + var imgHeight = img.height; - if (this.$grid) { - this.$grid.css('backgroundImage', 'none'); - } - return this; - }, + canvas.width = 2 * imgWidth; + canvas.height = 2 * imgHeight; - _getGridRefs: function() { + var ctx = canvas.getContext('2d'); + // top-left image + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // xy-flipped bottom-right image + ctx.setTransform(-1, 0, 0, -1, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // x-flipped top-right image + ctx.setTransform(-1, 0, 0, 1, canvas.width, 0); + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // y-flipped bottom-left image + ctx.setTransform(1, 0, 0, -1, 0, canvas.height); + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - if (!this._gridCache) { + return canvas; + }, - this._gridCache = { - root: V('svg', { width: '100%', height: '100%' }, V('defs')), - patterns: {}, - add: function(id, vel) { - V(this.root.node.childNodes[0]).append(vel); - this.patterns[id] = vel; - this.root.append(V('rect', { width: '100%', height: '100%', fill: 'url(#' + id + ')' })); - }, - get: function(id) { - return this.patterns[id]; - }, - exist: function(id) { - return this.patterns[id] !== undefined; - } - }; - } + flipX: function(img) { + // d b + // d b - return this._gridCache; - }, + var canvas = document.createElement('canvas'); + var imgWidth = img.width; + var imgHeight = img.height; - setGrid: function(drawGrid) { + canvas.width = imgWidth * 2; + canvas.height = imgHeight; - this.clearGrid(); + var ctx = canvas.getContext('2d'); + // left image + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // flipped right image + ctx.translate(2 * imgWidth, 0); + ctx.scale(-1, 1); + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - this._gridCache = null; - this._gridSettings = []; + return canvas; + }, - var optionsList = Array.isArray(drawGrid) ? drawGrid : [drawGrid || {}]; - optionsList.forEach(function(item) { - this._gridSettings.push.apply(this._gridSettings, this._resolveDrawGridOption(item)); - }, this); - return this; - }, + flipY: function(img) { + // d d + // q q - _resolveDrawGridOption: function(opt) { + var canvas = document.createElement('canvas'); + var imgWidth = img.width; + var imgHeight = img.height; - var namespace = this.constructor.gridPatterns; - if (isString(opt) && Array.isArray(namespace[opt])) { - return namespace[opt].map(function(item) { - return assign({}, item); - }); - } + canvas.width = imgWidth; + canvas.height = imgHeight * 2; - var options = opt || { args: [{}] }; - var isArray = Array.isArray(options); - var name = options.name; + var ctx = canvas.getContext('2d'); + // top image + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // flipped bottom image + ctx.translate(0, 2 * imgHeight); + ctx.scale(1, -1); + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - if (!isArray && !name && !options.markup) { - name = 'dot'; - } + return canvas; + }, - if (name && Array.isArray(namespace[name])) { - var pattern = namespace[name].map(function(item) { - return assign({}, item); - }); + watermark: function(img, opt) { + // d + // d - var args = Array.isArray(options.args) ? options.args : [options.args || {}]; + opt = opt || {}; - defaults(args[0], omit(opt, 'args')); - for (var i = 0; i < args.length; i++) { - if (pattern[i]) { - assign(pattern[i], args[i]); + var imgWidth = img.width; + var imgHeight = img.height; + + var canvas = document.createElement('canvas'); + canvas.width = imgWidth * 3; + canvas.height = imgHeight * 3; + + var ctx = canvas.getContext('2d'); + var angle = isNumber(opt.watermarkAngle) ? -opt.watermarkAngle : -20; + var radians = toRad(angle); + var stepX = canvas.width / 4; + var stepY = canvas.height / 4; + + for (var i = 0; i < 4; i++) { + for (var j = 0; j < 4; j++) { + if ((i + j) % 2 > 0) { + // reset the current transformations + ctx.setTransform(1, 0, 0, 1, (2 * i - 1) * stepX, (2 * j - 1) * stepY); + ctx.rotate(radians); + ctx.drawImage(img, -imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight); + } } } - return pattern; - } - return isArray ? options : [options]; + return canvas; + } }, - drawGrid: function(opt) { - - var gridSize = this.options.gridSize; - if (gridSize <= 1) { - return this.clearGrid(); - } + gridPatterns: { + dot: [{ + color: '#AAAAAA', + thickness: 1, + markup: 'rect', + update: function(el, opt) { + V(el).attr({ + width: opt.thickness * opt.sx, + height: opt.thickness * opt.sy, + fill: opt.color + }); + } + }], + fixedDot: [{ + color: '#AAAAAA', + thickness: 1, + markup: 'rect', + update: function(el, opt) { + var size = opt.sx <= 1 ? opt.thickness * opt.sx : opt.thickness; + V(el).attr({ width: size, height: size, fill: opt.color }); + } + }], + mesh: [{ + color: '#AAAAAA', + thickness: 1, + markup: 'path', + update: function(el, opt) { - var localOptions = Array.isArray(opt) ? opt : [opt]; + var d; + var width = opt.width; + var height = opt.height; + var thickness = opt.thickness; - var ctm = this.matrix(); - var refs = this._getGridRefs(); + if (width - thickness >= 0 && height - thickness >= 0) { + d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); + } else { + d = 'M 0 0 0 0'; + } - this._gridSettings.forEach(function(gridLayerSetting, index) { + V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); + } + }], + doubleMesh: [{ + color: '#AAAAAA', + thickness: 1, + markup: 'path', + update: function(el, opt) { - var id = 'pattern_' + index; - var options = merge(gridLayerSetting, localOptions[index], { - sx: ctm.a || 1, - sy: ctm.d || 1, - ox: ctm.e || 0, - oy: ctm.f || 0 - }); + var d; + var width = opt.width; + var height = opt.height; + var thickness = opt.thickness; - options.width = gridSize * (ctm.a || 1) * (options.scaleFactor || 1); - options.height = gridSize * (ctm.d || 1) * (options.scaleFactor || 1); + if (width - thickness >= 0 && height - thickness >= 0) { + d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); + } else { + d = 'M 0 0 0 0'; + } - if (!refs.exist(id)) { - refs.add(id, V('pattern', { id: id, patternUnits: 'userSpaceOnUse' }, V(options.markup))); + V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); } + }, { + color: '#000000', + thickness: 3, + scaleFactor: 4, + markup: 'path', + update: function(el, opt) { - var patternDefVel = refs.get(id); + var d; + var width = opt.width; + var height = opt.height; + var thickness = opt.thickness; - if (isFunction(options.update)) { - options.update(patternDefVel.node.childNodes[0], options); + if (width - thickness >= 0 && height - thickness >= 0) { + d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); + } else { + d = 'M 0 0 0 0'; + } + + V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); } + }] + } + }); - var x = options.ox % options.width; - if (x < 0) { x += options.width; } + var ToolView = View.extend({ + name: null, + tagName: 'g', + className: 'tool', + svgElement: true, + _visible: true, - var y = options.oy % options.height; - if (y < 0) { y += options.height; } + init: function() { + var name = this.name; + if (name) { this.vel.attr('data-tool-name', name); } + }, - patternDefVel.attr({ - x: x, - y: y, - width: options.width, - height: options.height - }); - }); + configure: function(view, toolsView) { + this.relatedView = view; + this.paper = view.paper; + this.parentView = toolsView; + this.simulateRelatedView(this.el); + // Delegate events in case the ToolView was removed from the DOM and reused. + this.delegateEvents(); + return this; + }, - var patternUri = new XMLSerializer().serializeToString(refs.root.node); - patternUri = 'url(data:image/svg+xml;base64,' + btoa(patternUri) + ')'; + simulateRelatedView: function(el) { + if (el) { el.setAttribute('model-id', this.relatedView.model.id); } + }, - this.$grid.css('backgroundImage', patternUri); + getName: function() { + return this.name; + }, - return this; + show: function() { + this.el.style.display = ''; + this._visible = true; }, - updateBackgroundImage: function(opt) { + hide: function() { + this.el.style.display = 'none'; + this._visible = false; + }, - opt = opt || {}; + isVisible: function() { + return !!this._visible; + }, - var backgroundPosition = opt.position || 'center'; - var backgroundSize = opt.size || 'auto auto'; + focus: function() { + var opacity = this.options.focusOpacity; + if (isFinite(opacity)) { this.el.style.opacity = opacity; } + this.parentView.focusTool(this); + }, - var currentScale = this.scale(); - var currentTranslate = this.translate(); + blur: function() { + this.el.style.opacity = ''; + this.parentView.blurTool(this); + }, - // backgroundPosition - if (isObject$1(backgroundPosition)) { - var x = currentTranslate.tx + (currentScale.sx * (backgroundPosition.x || 0)); - var y = currentTranslate.ty + (currentScale.sy * (backgroundPosition.y || 0)); - backgroundPosition = x + 'px ' + y + 'px'; - } + update: function() { + // to be overridden + }, - // backgroundSize - if (isObject$1(backgroundSize)) { - backgroundSize = new Rect(backgroundSize).scale(currentScale.sx, currentScale.sy); - backgroundSize = backgroundSize.width + 'px ' + backgroundSize.height + 'px'; + guard: function(evt) { + // Let the context-menu event bubble up to the relatedView + var ref = this; + var paper = ref.paper; + var relatedView = ref.relatedView; + if (!paper || !relatedView) { return true; } + return paper.guard(evt, relatedView); + } + }); + + var ToolsView = View.extend({ + tagName: 'g', + className: 'tools', + svgElement: true, + tools: null, + isRendered: false, + options: { + tools: null, + relatedView: null, + name: null, + // layer?: LayersNames.TOOLS + // z?: number + }, + + configure: function(options) { + options = assign(this.options, options); + var tools = options.tools; + if (!Array.isArray(tools)) { return this; } + var relatedView = options.relatedView; + if (!(relatedView instanceof CellView)) { return this; } + var views = this.tools = []; + for (var i = 0, n = tools.length; i < n; i++) { + var tool = tools[i]; + if (!(tool instanceof ToolView)) { continue; } + tool.configure(relatedView, this); + this.vel.append(tool.el); + views.push(tool); } + this.isRendered = false; + relatedView.requestUpdate(relatedView.getFlag('TOOLS')); + return this; + }, - this.$background.css({ - backgroundSize: backgroundSize, - backgroundPosition: backgroundPosition - }); + getName: function() { + return this.options.name; }, - drawBackgroundImage: function(img, opt) { + update: function(opt) { - // Clear the background image if no image provided - if (!(img instanceof HTMLImageElement)) { - this.$background.css('backgroundImage', ''); - return; + opt || (opt = {}); + var tools = this.tools; + if (!tools) { return this; } + var isRendered = this.isRendered; + for (var i = 0, n = tools.length; i < n; i++) { + var tool = tools[i]; + if (!isRendered) { + // First update executes render() + tool.render(); + } else if (opt.tool !== tool.cid && tool.isVisible()) { + tool.update(); + } } - - if (!this._background || this._background.id !== opt.id) { - // Draw only the last image requested (see drawBackground()) - return; + if (!isRendered) { + this.mount(); + // Make sure tools are visible (if they were hidden and the tool removed) + this.blurTool(); + this.isRendered = true; } + return this; + }, - opt = opt || {}; - - var backgroundImage; - var backgroundSize = opt.size; - var backgroundRepeat = opt.repeat || 'no-repeat'; - var backgroundOpacity = opt.opacity || 1; - var backgroundQuality = Math.abs(opt.quality) || 1; - var backgroundPattern = this.constructor.backgroundPatterns[camelCase(backgroundRepeat)]; + focusTool: function(focusedTool) { - if (isFunction(backgroundPattern)) { - // 'flip-x', 'flip-y', 'flip-xy', 'watermark' and custom - img.width *= backgroundQuality; - img.height *= backgroundQuality; - var canvas = backgroundPattern(img, opt); - if (!(canvas instanceof HTMLCanvasElement)) { - throw new Error('dia.Paper: background pattern must return an HTML Canvas instance'); + var tools = this.tools; + if (!tools) { return this; } + for (var i = 0, n = tools.length; i < n; i++) { + var tool = tools[i]; + if (focusedTool === tool) { + tool.show(); + } else { + tool.hide(); } + } + return this; + }, - backgroundImage = canvas.toDataURL('image/png'); - backgroundRepeat = 'repeat'; - if (isObject$1(backgroundSize)) { - // recalculate the tile size if an object passed in - backgroundSize.width *= canvas.width / img.width; - backgroundSize.height *= canvas.height / img.height; - } else if (backgroundSize === undefined) { - // calculate the tile size if no provided - opt.size = { - width: canvas.width / backgroundQuality, - height: canvas.height / backgroundQuality - }; - } - } else { - // backgroundRepeat: - // no-repeat', 'round', 'space', 'repeat', 'repeat-x', 'repeat-y' - backgroundImage = img.src; - if (backgroundSize === undefined) { - // pass the image size for the backgroundSize if no size provided - opt.size = { - width: img.width, - height: img.height - }; + blurTool: function(blurredTool) { + var tools = this.tools; + if (!tools) { return this; } + for (var i = 0, n = tools.length; i < n; i++) { + var tool = tools[i]; + if (tool !== blurredTool && !tool.isVisible()) { + tool.show(); + tool.update(); } } + return this; + }, - this.$background.css({ - opacity: backgroundOpacity, - backgroundRepeat: backgroundRepeat, - backgroundImage: 'url(' + backgroundImage + ')' - }); - - this.updateBackgroundImage(opt); + hide: function() { + return this.focusTool(null); }, - updateBackgroundColor: function(color) { + show: function() { + return this.blurTool(null); + }, - this.$el.css('backgroundColor', color || ''); + onRemove: function() { + var tools = this.tools; + if (!tools) { return this; } + for (var i = 0, n = tools.length; i < n; i++) { + tools[i].remove(); + } + this.tools = null; }, - drawBackground: function(opt) { + mount: function() { + var ref = this; + var options = ref.options; + var el = ref.el; + var relatedView = options.relatedView; + var layer = options.layer; if ( layer === void 0 ) layer = LayersNames.TOOLS; + var z = options.z; + if (relatedView) { + if (layer) { + relatedView.paper.getLayerView(layer).insertSortedNode(el, z); + } else { + relatedView.el.appendChild(el); + } + } + return this; + } - opt = opt || {}; + }); - this.updateBackgroundColor(opt.color); - if (opt.image) { - opt = this._background = cloneDeep(opt); - guid(opt); - var img = document.createElement('img'); - img.onload = this.drawBackgroundImage.bind(this, img, opt); - img.src = opt.image; - } else { - this.drawBackgroundImage(null); - this._background = null; - } - return this; - }, + var index$2 = ({ + Graph: Graph, + attributes: attributes, + LayersNames: LayersNames, + PaperLayer: PaperLayer, + Cell: Cell, + CellView: CellView, + Element: Element$1, + ElementView: ElementView, + Link: Link, + LinkView: LinkView, + Paper: Paper, + ToolView: ToolView, + ToolsView: ToolsView, + HighlighterView: HighlighterView + }); - setInteractivity: function(value) { + var DirectedGraph = { - this.options.interactive = value; + exportElement: function(element) { - invoke(this._views, 'setInteractivity', value); + // The width and height of the element. + return element.size(); }, - // Paper definitions. - // ------------------ + exportLink: function(link) { - isDefined: function(defId) { + var labelSize = link.get('labelSize') || {}; + var edge = { + // The number of ranks to keep between the source and target of the edge. + minLen: link.get('minLen') || 1, + // The weight to assign edges. Higher weight edges are generally + // made shorter and straighter than lower weight edges. + weight: link.get('weight') || 1, + // Where to place the label relative to the edge. + // l = left, c = center r = right. + labelpos: link.get('labelPosition') || 'c', + // How many pixels to move the label away from the edge. + // Applies only when labelpos is l or r. + labeloffset: link.get('labelOffset') || 0, + // The width of the edge label in pixels. + width: labelSize.width || 0, + // The height of the edge label in pixels. + height: labelSize.height || 0 + }; - return !!this.svg.getElementById(defId); + return edge; }, - defineFilter: function(filter$1) { + importElement: function(opt, v, gl) { - if (!isObject$1(filter$1)) { - throw new TypeError('dia.Paper: defineFilter() requires 1. argument to be an object.'); - } + var element = this.getCell(v); + var glNode = gl.node(v); - var filterId = filter$1.id; - var name = filter$1.name; - // Generate a hash code from the stringified filter definition. This gives us - // a unique filter ID for different definitions. - if (!filterId) { - filterId = name + this.svg.id + hashCode(JSON.stringify(filter$1)); + if (opt.setPosition) { + opt.setPosition(element, glNode); + } else { + element.set('position', { + x: glNode.x - glNode.width / 2, + y: glNode.y - glNode.height / 2 + }); } - // If the filter already exists in the document, - // we're done and we can just use it (reference it using `url()`). - // If not, create one. - if (!this.isDefined(filterId)) { + }, - var namespace = filter; - var filterSVGString = namespace[name] && namespace[name](filter$1.args || {}); - if (!filterSVGString) { - throw new Error('Non-existing filter ' + name); - } + importLink: function(opt, edgeObj, gl) { - // Set the filter area to be 3x the bounding box of the cell - // and center the filter around the cell. - var filterAttrs = assign({ - filterUnits: 'objectBoundingBox', - x: -1, - y: -1, - width: 3, - height: 3 - }, filter$1.attrs, { - id: filterId - }); + var SIMPLIFY_THRESHOLD = 0.001; - V(filterSVGString, filterAttrs).appendTo(this.defs); + var link = this.getCell(edgeObj.name); + var glEdge = gl.edge(edgeObj); + var points = glEdge.points || []; + var polyline = new Polyline(points); + + // check the `setLinkVertices` here for backwards compatibility + if (opt.setVertices || opt.setLinkVertices) { + if (isFunction(opt.setVertices)) { + opt.setVertices(link, points); + } else { + // simplify the `points` polyline + polyline.simplify({ threshold: SIMPLIFY_THRESHOLD }); + var polylinePoints = polyline.points.map(function (point) { return (point.toJSON()); }); // JSON of points after simplification + var numPolylinePoints = polylinePoints.length; // number of points after simplification + // set simplified polyline points as link vertices + // remove first and last polyline points (= source/target sonnectionPoints) + link.set('vertices', polylinePoints.slice(1, numPolylinePoints - 1)); + } } - return filterId; + if (opt.setLabels && ('x' in glEdge) && ('y' in glEdge)) { + var labelPosition = { x: glEdge.x, y: glEdge.y }; + if (isFunction(opt.setLabels)) { + opt.setLabels(link, labelPosition, points); + } else { + // convert the absolute label position to a relative position + // towards the closest point on the edge + var length = polyline.closestPointLength(labelPosition); + var closestPoint = polyline.pointAtLength(length); + var distance = (length / polyline.length()); + var offset = new Point(labelPosition).difference(closestPoint).toJSON(); + link.label(0, { + position: { + distance: distance, + offset: offset + } + }); + } + } }, - defineGradient: function(gradient) { - if (!isObject$1(gradient)) { - throw new TypeError('dia.Paper: defineGradient() requires 1. argument to be an object.'); - } - var ref = this; - var svg = ref.svg; - var defs = ref.defs; - var type = gradient.type; - var id = gradient.id; if ( id === void 0 ) id = type + svg.id + hashCode(JSON.stringify(gradient)); - var stops = gradient.stops; - var attrs = gradient.attrs; if ( attrs === void 0 ) attrs = {}; - // If the gradient already exists in the document, - // we're done and we can just use it (reference it using `url()`). - if (this.isDefined(id)) { return id; } - // If not, create one. - var stopVEls = toArray(stops).map(function (ref) { - var offset = ref.offset; - var color = ref.color; - var opacity = ref.opacity; + layout: function(graphOrCells, opt) { - return V('stop').attr({ - 'offset': offset, - 'stop-color': color, - 'stop-opacity': Number.isFinite(opacity) ? opacity : 1 - }); - }); - var gradientVEl = V(type, attrs, stopVEls); - gradientVEl.id = id; - gradientVEl.appendTo(defs); - return id; - }, + var graph; - definePattern: function(pattern) { - if (!isObject$1(pattern)) { - throw new TypeError('dia.Paper: definePattern() requires 1. argument to be an object.'); - } - var ref = this; - var svg = ref.svg; - var defs = ref.defs; - var id = pattern.id; if ( id === void 0 ) id = svg.id + hashCode(JSON.stringify(pattern)); - var markup = pattern.markup; - var attrs = pattern.attrs; if ( attrs === void 0 ) attrs = {}; - if (!markup) { - throw new TypeError('dia.Paper: definePattern() requires markup.'); - } - // If the gradient already exists in the document, - // we're done and we can just use it (reference it using `url()`). - if (this.isDefined(id)) { return id; } - // If not, create one. - var patternVEl = V('pattern', { - patternUnits: 'userSpaceOnUse' - }); - patternVEl.id = id; - patternVEl.attr(attrs); - if (typeof markup === 'string') { - patternVEl.append(V(markup)); + if (graphOrCells instanceof Graph) { + graph = graphOrCells; } else { - var ref$1 = parseDOMJSON(markup); - var fragment = ref$1.fragment; - patternVEl.append(fragment); + // Reset cells in dry mode so the graph reference is not stored on the cells. + // `sort: false` to prevent elements to change their order based on the z-index + graph = (new Graph()).resetCells(graphOrCells, { dry: true, sort: false }); } - patternVEl.appendTo(defs); - return id; - }, - defineMarker: function(marker) { - if (!isObject$1(marker)) { - throw new TypeError('dia.Paper: defineMarker() requires 1. argument to be an object.'); - } - var ref = this; - var svg = ref.svg; - var defs = ref.defs; - var id = marker.id; if ( id === void 0 ) id = svg.id + hashCode(JSON.stringify(marker)); - var markup = marker.markup; - var attrs = marker.attrs; if ( attrs === void 0 ) attrs = {}; - var markerUnits = marker.markerUnits; if ( markerUnits === void 0 ) markerUnits = 'userSpaceOnUse'; - // If the marker already exists in the document, - // we're done and we can just use it (reference it using `url()`). - if (this.isDefined(id)) { return id; } - // If not, create one. - var markerVEl = V('marker', { - orient: 'auto', - overflow: 'visible', - markerUnits: markerUnits + // This is not needed anymore. + graphOrCells = null; + + opt = defaults(opt || {}, { + resizeClusters: true, + clusterPadding: 10, + exportElement: this.exportElement, + exportLink: this.exportLink }); - markerVEl.id = id; - markerVEl.attr(attrs); - if (markup) { - if (typeof markup === 'string') { - markerVEl.append(V(markup)); - } else { - var ref$1 = parseDOMJSON(markup); - var fragment = ref$1.fragment; - markerVEl.append(fragment); + + /* eslint-disable no-undef */ + var dagreUtil = opt.dagre || (typeof dagre !== 'undefined' ? dagre : undefined); + /* eslint-enable no-undef */ + + if (dagreUtil === undefined) { throw new Error('The the "dagre" utility is a mandatory dependency.'); } + + // create a graphlib.Graph that represents the joint.dia.Graph + // var glGraph = graph.toGraphLib({ + var glGraph = DirectedGraph.toGraphLib(graph, { + graphlib: opt.graphlib, + directed: true, + // We are about to use edge naming feature. + multigraph: true, + // We are able to layout graphs with embeds. + compound: true, + setNodeLabel: opt.exportElement, + setEdgeLabel: opt.exportLink, + setEdgeName: function(link) { + // Graphlib edges have no ids. We use edge name property + // to store and retrieve ids instead. + return link.id; } - } else { - // marker object is a flat structure - var type = marker.type; if ( type === void 0 ) type = 'path'; - var markerContentVEl = V(type, omit(marker, 'type', 'id', 'markup', 'attrs', 'markerUnits')); - markerVEl.append(markerContentVEl); - } - markerVEl.appendTo(defs); - return id; - } + }); - }, { + var glLabel = {}; + var marginX = opt.marginX || 0; + var marginY = opt.marginY || 0; - sorting: sortingTypes, + // Dagre layout accepts options as lower case. + // Direction for rank nodes. Can be TB, BT, LR, or RL + if (opt.rankDir) { glLabel.rankdir = opt.rankDir; } + // Alignment for rank nodes. Can be UL, UR, DL, or DR + if (opt.align) { glLabel.align = opt.align; } + // Number of pixels that separate nodes horizontally in the layout. + if (opt.nodeSep) { glLabel.nodesep = opt.nodeSep; } + // Number of pixels that separate edges horizontally in the layout. + if (opt.edgeSep) { glLabel.edgesep = opt.edgeSep; } + // Number of pixels between each rank in the layout. + if (opt.rankSep) { glLabel.ranksep = opt.rankSep; } + // Type of algorithm to assign a rank to each node in the input graph. + // Possible values: network-simplex, tight-tree or longest-path + if (opt.ranker) { glLabel.ranker = opt.ranker; } + // Number of pixels to use as a margin around the left and right of the graph. + if (marginX) { glLabel.marginx = marginX; } + // Number of pixels to use as a margin around the top and bottom of the graph. + if (marginY) { glLabel.marginy = marginY; } - Layers: LayersNames, + // Set the option object for the graph label. + glGraph.setGraph(glLabel); - backgroundPatterns: { + // Executes the layout. + dagreUtil.layout(glGraph, { debugTiming: !!opt.debugTiming }); - flipXy: function(img) { - // d b - // q p + // Wrap all graph changes into a batch. + graph.startBatch('layout'); - var canvas = document.createElement('canvas'); - var imgWidth = img.width; - var imgHeight = img.height; + DirectedGraph.fromGraphLib(glGraph, { + importNode: this.importElement.bind(graph, opt), + importEdge: this.importLink.bind(graph, opt) + }); - canvas.width = 2 * imgWidth; - canvas.height = 2 * imgHeight; + // // Update the graph. + // graph.fromGraphLib(glGraph, { + // importNode: this.importElement.bind(graph, opt), + // importEdge: this.importLink.bind(graph, opt) + // }); - var ctx = canvas.getContext('2d'); - // top-left image - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - // xy-flipped bottom-right image - ctx.setTransform(-1, 0, 0, -1, canvas.width, canvas.height); - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - // x-flipped top-right image - ctx.setTransform(-1, 0, 0, 1, canvas.width, 0); - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - // y-flipped bottom-left image - ctx.setTransform(1, 0, 0, -1, 0, canvas.height); - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + if (opt.resizeClusters) { + // Resize and reposition cluster elements (parents of other elements) + // to fit their children. + // 1. filter clusters only + // 2. map id on cells + // 3. sort cells by their depth (the deepest first) + // 4. resize cell to fit their direct children only. + var clusters = glGraph.nodes() + .filter(function(v) { return glGraph.children(v).length > 0; }) + .map(graph.getCell.bind(graph)) + .sort(function(aCluster, bCluster) { + return bCluster.getAncestors().length - aCluster.getAncestors().length; + }); - return canvas; - }, + invoke(clusters, 'fitEmbeds', { padding: opt.clusterPadding }); + } + + graph.stopBatch('layout'); + + // Width and height of the graph extended by margins. + var glSize = glGraph.graph(); + // Return the bounding box of the graph after the layout. + return new Rect( + marginX, + marginY, + Math.abs(glSize.width - 2 * marginX), + Math.abs(glSize.height - 2 * marginY) + ); + }, + + fromGraphLib: function(glGraph, opt) { + + opt = opt || {}; + + var importNode = opt.importNode || noop; + var importEdge = opt.importEdge || noop; + var graph = (this instanceof Graph) ? this : new Graph; - flipX: function(img) { - // d b - // d b + // Import all nodes. + glGraph.nodes().forEach(function(node) { + importNode.call(graph, node, glGraph, graph, opt); + }); - var canvas = document.createElement('canvas'); - var imgWidth = img.width; - var imgHeight = img.height; + // Import all edges. + glGraph.edges().forEach(function(edge) { + importEdge.call(graph, edge, glGraph, graph, opt); + }); - canvas.width = imgWidth * 2; - canvas.height = imgHeight; + return graph; + }, - var ctx = canvas.getContext('2d'); - // left image - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - // flipped right image - ctx.translate(2 * imgWidth, 0); - ctx.scale(-1, 1); - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // Create new graphlib graph from existing JointJS graph. + toGraphLib: function(graph, opt) { - return canvas; - }, + opt = opt || {}; - flipY: function(img) { - // d d - // q q + /* eslint-disable no-undef */ + var graphlibUtil = opt.graphlib || (typeof graphlib !== 'undefined' ? graphlib : undefined); + /* eslint-enable no-undef */ - var canvas = document.createElement('canvas'); - var imgWidth = img.width; - var imgHeight = img.height; + if (graphlibUtil === undefined) { throw new Error('The the "graphlib" utility is a mandatory dependency.'); } - canvas.width = imgWidth; - canvas.height = imgHeight * 2; + var glGraphType = pick(opt, 'directed', 'compound', 'multigraph'); + var glGraph = new graphlibUtil.Graph(glGraphType); + var setNodeLabel = opt.setNodeLabel || noop; + var setEdgeLabel = opt.setEdgeLabel || noop; + var setEdgeName = opt.setEdgeName || noop; + var collection = graph.get('cells'); - var ctx = canvas.getContext('2d'); - // top image - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - // flipped bottom image - ctx.translate(0, 2 * imgHeight); - ctx.scale(1, -1); - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + for (var i = 0, n = collection.length; i < n; i++) { - return canvas; - }, + var cell = collection.at(i); + if (cell.isLink()) { - watermark: function(img, opt) { - // d - // d + var source = cell.get('source'); + var target = cell.get('target'); - opt = opt || {}; + // Links that end at a point are ignored. + if (!source.id || !target.id) { break; } - var imgWidth = img.width; - var imgHeight = img.height; + // Note that if we are creating a multigraph we can name the edges. If + // we try to name edges on a non-multigraph an exception is thrown. + glGraph.setEdge(source.id, target.id, setEdgeLabel(cell), setEdgeName(cell)); - var canvas = document.createElement('canvas'); - canvas.width = imgWidth * 3; - canvas.height = imgHeight * 3; + } else { - var ctx = canvas.getContext('2d'); - var angle = isNumber(opt.watermarkAngle) ? -opt.watermarkAngle : -20; - var radians = toRad(angle); - var stepX = canvas.width / 4; - var stepY = canvas.height / 4; + glGraph.setNode(cell.id, setNodeLabel(cell)); - for (var i = 0; i < 4; i++) { - for (var j = 0; j < 4; j++) { - if ((i + j) % 2 > 0) { - // reset the current transformations - ctx.setTransform(1, 0, 0, 1, (2 * i - 1) * stepX, (2 * j - 1) * stepY); - ctx.rotate(radians); - ctx.drawImage(img, -imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight); + // For the compound graphs we have to take embeds into account. + if (glGraph.isCompound() && cell.has('parent')) { + var parentId = cell.get('parent'); + if (collection.has(parentId)) { + // Make sure the parent cell is included in the graph (this can + // happen when the layout is run on part of the graph only). + glGraph.setParent(cell.id, parentId); } } } - - return canvas; } - }, - gridPatterns: { - dot: [{ - color: '#AAAAAA', - thickness: 1, - markup: 'rect', - update: function(el, opt) { - V(el).attr({ - width: opt.thickness * opt.sx, - height: opt.thickness * opt.sy, - fill: opt.color - }); - } - }], - fixedDot: [{ - color: '#AAAAAA', - thickness: 1, - markup: 'rect', - update: function(el, opt) { - var size = opt.sx <= 1 ? opt.thickness * opt.sx : opt.thickness; - V(el).attr({ width: size, height: size, fill: opt.color }); - } - }], - mesh: [{ - color: '#AAAAAA', - thickness: 1, - markup: 'path', - update: function(el, opt) { + return glGraph; + } + }; - var d; - var width = opt.width; - var height = opt.height; - var thickness = opt.thickness; + Graph.prototype.toGraphLib = function(opt) { - if (width - thickness >= 0 && height - thickness >= 0) { - d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); - } else { - d = 'M 0 0 0 0'; - } + return DirectedGraph.toGraphLib(this, opt); + }; - V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); - } - }], - doubleMesh: [{ - color: '#AAAAAA', - thickness: 1, - markup: 'path', - update: function(el, opt) { + Graph.prototype.fromGraphLib = function(glGraph, opt) { - var d; - var width = opt.width; - var height = opt.height; - var thickness = opt.thickness; + return DirectedGraph.fromGraphLib.call(this, glGraph, opt); + }; - if (width - thickness >= 0 && height - thickness >= 0) { - d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); - } else { - d = 'M 0 0 0 0'; - } + var env = { - V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); - } - }, { - color: '#000000', - thickness: 3, - scaleFactor: 4, - markup: 'path', - update: function(el, opt) { + _results: {}, - var d; - var width = opt.width; - var height = opt.height; - var thickness = opt.thickness; + _tests: { - if (width - thickness >= 0 && height - thickness >= 0) { - d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); - } else { - d = 'M 0 0 0 0'; - } + svgforeignobject: function() { + return !!document.createElementNS && + /SVGForeignObject/.test(({}).toString.call(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'))); + } + }, - V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); - } - }] - } - }); + addTest: function(name, fn) { - var ToolsView = View.extend({ - tagName: 'g', - className: 'tools', - svgElement: true, - tools: null, - isRendered: false, - options: { - tools: null, - relatedView: null, - name: null, - // layer?: LayersNames.TOOLS - // z?: number + return this._tests[name] = fn; }, - configure: function(options) { - options = assign(this.options, options); - var tools = options.tools; - if (!Array.isArray(tools)) { return this; } - var relatedView = options.relatedView; - if (!(relatedView instanceof CellView)) { return this; } - var views = this.tools = []; - for (var i = 0, n = tools.length; i < n; i++) { - var tool = tools[i]; - if (!(tool instanceof ToolView)) { continue; } - tool.configure(relatedView, this); - this.vel.append(tool.el); - views.push(tool); + test: function(name) { + + var fn = this._tests[name]; + + if (!fn) { + throw new Error('Test not defined ("' + name + '"). Use `joint.env.addTest(name, fn) to add a new test.`'); } - this.isRendered = false; - relatedView.requestUpdate(relatedView.getFlag('TOOLS')); - return this; - }, - getName: function() { - return this.options.name; - }, + var result = this._results[name]; - update: function(opt) { + if (typeof result !== 'undefined') { + return result; + } - opt || (opt = {}); - var tools = this.tools; - if (!tools) { return this; } - var isRendered = this.isRendered; - for (var i = 0, n = tools.length; i < n; i++) { - var tool = tools[i]; - if (!isRendered) { - // First update executes render() - tool.render(); - } else if (opt.tool !== tool.cid && tool.isVisible()) { - tool.update(); - } + try { + result = fn(); + } catch (error) { + result = false; } - if (!isRendered) { - this.mount(); - // Make sure tools are visible (if they were hidden and the tool removed) - this.blurTool(); - this.isRendered = true; + + // Cache the test result. + this._results[name] = result; + + return result; + } + }; + + var Generic = Element$1.define('basic.Generic', { + attrs: { + '.': { fill: '#ffffff', stroke: 'none' } + } + }); + + var Rect$1 = Generic.define('basic.Rect', { + attrs: { + 'rect': { + fill: '#ffffff', + stroke: '#000000', + width: 100, + height: 60 + }, + 'text': { + fill: '#000000', + text: '', + 'font-size': 14, + 'ref-x': .5, + 'ref-y': .5, + 'text-anchor': 'middle', + 'y-alignment': 'middle', + 'font-family': 'Arial, helvetica, sans-serif' } - return this; - }, + } + }, { + markup: '' + }); - focusTool: function(focusedTool) { + var TextView = ElementView.extend({ - var tools = this.tools; - if (!tools) { return this; } - for (var i = 0, n = tools.length; i < n; i++) { - var tool = tools[i]; - if (focusedTool === tool) { - tool.show(); - } else { - tool.hide(); - } + presentationAttributes: ElementView.addPresentationAttributes({ + // The element view is not automatically re-scaled to fit the model size + // when the attribute 'attrs' is changed. + attrs: ['SCALE'] + }), + + confirmUpdate: function() { + var flags = ElementView.prototype.confirmUpdate.apply(this, arguments); + if (this.hasFlag(flags, 'SCALE')) { + this.resize(); + flags = this.removeFlag(flags, 'SCALE'); } - return this; - }, + return flags; + } + }); - blurTool: function(blurredTool) { - var tools = this.tools; - if (!tools) { return this; } - for (var i = 0, n = tools.length; i < n; i++) { - var tool = tools[i]; - if (tool !== blurredTool && !tool.isVisible()) { - tool.show(); - tool.update(); - } + var Text = Generic.define('basic.Text', { + attrs: { + 'text': { + 'font-size': 18, + fill: '#000000' } - return this; - }, + } + }, { + markup: '', + }); - hide: function() { - return this.focusTool(null); - }, + var Circle = Generic.define('basic.Circle', { + size: { width: 60, height: 60 }, + attrs: { + 'circle': { + fill: '#ffffff', + stroke: '#000000', + r: 30, + cx: 30, + cy: 30 + }, + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-y': .5, + 'y-alignment': 'middle', + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' + } + } + }, { + markup: '', + }); - show: function() { - return this.blurTool(null); - }, + var Ellipse$1 = Generic.define('basic.Ellipse', { + size: { width: 60, height: 40 }, + attrs: { + 'ellipse': { + fill: '#ffffff', + stroke: '#000000', + rx: 30, + ry: 20, + cx: 30, + cy: 20 + }, + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-y': .5, + 'y-alignment': 'middle', + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' + } + } + }, { + markup: '', + }); - onRemove: function() { - var tools = this.tools; - if (!tools) { return this; } - for (var i = 0, n = tools.length; i < n; i++) { - tools[i].remove(); + var Polygon$1 = Generic.define('basic.Polygon', { + size: { width: 60, height: 40 }, + attrs: { + 'polygon': { + fill: '#ffffff', + stroke: '#000000' + }, + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-dy': 20, + 'y-alignment': 'middle', + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' } - this.tools = null; - }, + } + }, { + markup: '', + }); - mount: function() { - var ref = this; - var options = ref.options; - var el = ref.el; - var relatedView = options.relatedView; - var layer = options.layer; if ( layer === void 0 ) layer = LayersNames.TOOLS; - var z = options.z; - if (relatedView) { - if (layer) { - relatedView.paper.getLayerView(layer).insertSortedNode(el, z); - } else { - relatedView.el.appendChild(el); - } + var Polyline$1 = Generic.define('basic.Polyline', { + size: { width: 60, height: 40 }, + attrs: { + 'polyline': { + fill: '#ffffff', + stroke: '#000000' + }, + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-dy': 20, + 'y-alignment': 'middle', + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' } - return this; } + }, { + markup: '', + }); + var Image = Generic.define('basic.Image', { + attrs: { + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-dy': 20, + 'y-alignment': 'middle', + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' + } + } + }, { + markup: '', }); + var Path$1 = Generic.define('basic.Path', { + size: { width: 60, height: 60 }, + attrs: { + 'path': { + fill: '#ffffff', + stroke: '#000000' + }, + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref': 'path', + 'ref-x': .5, + 'ref-dy': 10, + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' + } + } + }, { + markup: '', + }); - var index$5 = ({ - Graph: Graph, - attributes: attributes, - LayersNames: LayersNames, - PaperLayer: PaperLayer, - Cell: Cell, - CellView: CellView, - Element: Element$1, - ElementView: ElementView, - Link: Link, - LinkView: LinkView, - Paper: Paper, - ToolView: ToolView, - ToolsView: ToolsView, - HighlighterView: HighlighterView + var Rhombus = Path$1.define('basic.Rhombus', { + attrs: { + 'path': { + d: 'M 30 0 L 60 30 30 60 0 30 z' + }, + 'text': { + 'ref-y': .5, + 'ref-dy': null, + 'y-alignment': 'middle' + } + } }); - var DirectedGraph = { - - exportElement: function(element) { + var svgForeignObjectSupported = env.test('svgforeignobject'); - // The width and height of the element. - return element.size(); + var TextBlock = Generic.define('basic.TextBlock', { + // see joint.css for more element styles + attrs: { + rect: { + fill: '#ffffff', + stroke: '#000000', + width: 80, + height: 100 + }, + text: { + fill: '#000000', + 'font-size': 14, + 'font-family': 'Arial, helvetica, sans-serif' + }, + '.content': { + text: '', + 'ref-x': .5, + 'ref-y': .5, + 'y-alignment': 'middle', + 'x-alignment': 'middle' + } }, - exportLink: function(link) { + content: '' + }, { + markup: [ + '', + '', + svgForeignObjectSupported + ? '
' + : '', + '' + ].join(''), - var labelSize = link.get('labelSize') || {}; - var edge = { - // The number of ranks to keep between the source and target of the edge. - minLen: link.get('minLen') || 1, - // The weight to assign edges. Higher weight edges are generally - // made shorter and straighter than lower weight edges. - weight: link.get('weight') || 1, - // Where to place the label relative to the edge. - // l = left, c = center r = right. - labelpos: link.get('labelPosition') || 'c', - // How many pixels to move the label away from the edge. - // Applies only when labelpos is l or r. - labeloffset: link.get('labelOffset') || 0, - // The width of the edge label in pixels. - width: labelSize.width || 0, - // The height of the edge label in pixels. - height: labelSize.height || 0 - }; + initialize: function() { - return edge; + this.listenTo(this, 'change:size', this.updateSize); + this.listenTo(this, 'change:content', this.updateContent); + this.updateSize(this, this.get('size')); + this.updateContent(this, this.get('content')); + Generic.prototype.initialize.apply(this, arguments); }, - importElement: function(opt, v, gl) { - - var element = this.getCell(v); - var glNode = gl.node(v); + updateSize: function(cell, size) { - if (opt.setPosition) { - opt.setPosition(element, glNode); - } else { - element.set('position', { - x: glNode.x - glNode.width / 2, - y: glNode.y - glNode.height / 2 - }); - } + // Selector `foreignObject' doesn't work across all browsers, we're using class selector instead. + // We have to clone size as we don't want attributes.div.style to be same object as attributes.size. + this.attr({ + '.fobj': assign({}, size), + div: { + style: assign({}, size) + } + }); }, - importLink: function(opt, edgeObj, gl) { + updateContent: function(cell, content) { - var SIMPLIFY_THRESHOLD = 0.001; + if (svgForeignObjectSupported) { - var link = this.getCell(edgeObj.name); - var glEdge = gl.edge(edgeObj); - var points = glEdge.points || []; - var polyline = new Polyline(points); + // Content element is a
element. + this.attr({ + '.content': { + html: sanitizeHTML(content) + } + }); - // check the `setLinkVertices` here for backwards compatibility - if (opt.setVertices || opt.setLinkVertices) { - if (isFunction(opt.setVertices)) { - opt.setVertices(link, points); - } else { - // simplify the `points` polyline - polyline.simplify({ threshold: SIMPLIFY_THRESHOLD }); - var polylinePoints = polyline.points.map(function (point) { return (point.toJSON()); }); // JSON of points after simplification - var numPolylinePoints = polylinePoints.length; // number of points after simplification - // set simplified polyline points as link vertices - // remove first and last polyline points (= source/target sonnectionPoints) - link.set('vertices', polylinePoints.slice(1, numPolylinePoints - 1)); - } - } + } else { - if (opt.setLabels && ('x' in glEdge) && ('y' in glEdge)) { - var labelPosition = { x: glEdge.x, y: glEdge.y }; - if (isFunction(opt.setLabels)) { - opt.setLabels(link, labelPosition, points); - } else { - // convert the absolute label position to a relative position - // towards the closest point on the edge - var length = polyline.closestPointLength(labelPosition); - var closestPoint = polyline.pointAtLength(length); - var distance = (length / polyline.length()); - var offset = new Point(labelPosition).difference(closestPoint).toJSON(); - link.label(0, { - position: { - distance: distance, - offset: offset - } - }); - } + // Content element is a element. + // SVG elements don't have innerHTML attribute. + this.attr({ + '.content': { + text: content + } + }); } }, - layout: function(graphOrCells, opt) { - - var graph; - - if (graphOrCells instanceof Graph) { - graph = graphOrCells; - } else { - // Reset cells in dry mode so the graph reference is not stored on the cells. - // `sort: false` to prevent elements to change their order based on the z-index - graph = (new Graph()).resetCells(graphOrCells, { dry: true, sort: false }); - } + // Here for backwards compatibility: + setForeignObjectSize: function() { - // This is not needed anymore. - graphOrCells = null; + this.updateSize.apply(this, arguments); + }, - opt = defaults(opt || {}, { - resizeClusters: true, - clusterPadding: 10, - exportElement: this.exportElement, - exportLink: this.exportLink - }); + // Here for backwards compatibility: + setDivContent: function() { - /* eslint-disable no-undef */ - var dagreUtil = opt.dagre || (typeof dagre !== 'undefined' ? dagre : undefined); - /* eslint-enable no-undef */ + this.updateContent.apply(this, arguments); + } + }); - if (dagreUtil === undefined) { throw new Error('The the "dagre" utility is a mandatory dependency.'); } + // TextBlockView implements the fallback for IE when no foreignObject exists and + // the text needs to be manually broken. + var TextBlockView = ElementView.extend({ - // create a graphlib.Graph that represents the joint.dia.Graph - // var glGraph = graph.toGraphLib({ - var glGraph = DirectedGraph.toGraphLib(graph, { - graphlib: opt.graphlib, - directed: true, - // We are about to use edge naming feature. - multigraph: true, - // We are able to layout graphs with embeds. - compound: true, - setNodeLabel: opt.exportElement, - setEdgeLabel: opt.exportLink, - setEdgeName: function(link) { - // Graphlib edges have no ids. We use edge name property - // to store and retrieve ids instead. - return link.id; - } - }); + presentationAttributes: svgForeignObjectSupported + ? ElementView.prototype.presentationAttributes + : ElementView.addPresentationAttributes({ + content: ['CONTENT'], + size: ['CONTENT'] + }), - var glLabel = {}; - var marginX = opt.marginX || 0; - var marginY = opt.marginY || 0; + initFlag: ['RENDER', 'CONTENT'], - // Dagre layout accepts options as lower case. - // Direction for rank nodes. Can be TB, BT, LR, or RL - if (opt.rankDir) { glLabel.rankdir = opt.rankDir; } - // Alignment for rank nodes. Can be UL, UR, DL, or DR - if (opt.align) { glLabel.align = opt.align; } - // Number of pixels that separate nodes horizontally in the layout. - if (opt.nodeSep) { glLabel.nodesep = opt.nodeSep; } - // Number of pixels that separate edges horizontally in the layout. - if (opt.edgeSep) { glLabel.edgesep = opt.edgeSep; } - // Number of pixels between each rank in the layout. - if (opt.rankSep) { glLabel.ranksep = opt.rankSep; } - // Type of algorithm to assign a rank to each node in the input graph. - // Possible values: network-simplex, tight-tree or longest-path - if (opt.ranker) { glLabel.ranker = opt.ranker; } - // Number of pixels to use as a margin around the left and right of the graph. - if (marginX) { glLabel.marginx = marginX; } - // Number of pixels to use as a margin around the top and bottom of the graph. - if (marginY) { glLabel.marginy = marginY; } + confirmUpdate: function() { + var flags = ElementView.prototype.confirmUpdate.apply(this, arguments); + if (this.hasFlag(flags, 'CONTENT')) { + this.updateContent(this.model); + flags = this.removeFlag(flags, 'CONTENT'); + } + return flags; + }, - // Set the option object for the graph label. - glGraph.setGraph(glLabel); + update: function(_, renderingOnlyAttrs) { - // Executes the layout. - dagreUtil.layout(glGraph, { debugTiming: !!opt.debugTiming }); + var model = this.model; - // Wrap all graph changes into a batch. - graph.startBatch('layout'); + if (!svgForeignObjectSupported) { - DirectedGraph.fromGraphLib(glGraph, { - importNode: this.importElement.bind(graph, opt), - importEdge: this.importLink.bind(graph, opt) - }); + // Update everything but the content first. + var noTextAttrs = omit(renderingOnlyAttrs || model.get('attrs'), '.content'); + ElementView.prototype.update.call(this, model, noTextAttrs); - // // Update the graph. - // graph.fromGraphLib(glGraph, { - // importNode: this.importElement.bind(graph, opt), - // importEdge: this.importLink.bind(graph, opt) - // }); + if (!renderingOnlyAttrs || has$2(renderingOnlyAttrs, '.content')) { + // Update the content itself. + this.updateContent(model, renderingOnlyAttrs); + } - if (opt.resizeClusters) { - // Resize and reposition cluster elements (parents of other elements) - // to fit their children. - // 1. filter clusters only - // 2. map id on cells - // 3. sort cells by their depth (the deepest first) - // 4. resize cell to fit their direct children only. - var clusters = glGraph.nodes() - .filter(function(v) { return glGraph.children(v).length > 0; }) - .map(graph.getCell.bind(graph)) - .sort(function(aCluster, bCluster) { - return bCluster.getAncestors().length - aCluster.getAncestors().length; - }); + } else { - invoke(clusters, 'fitEmbeds', { padding: opt.clusterPadding }); + ElementView.prototype.update.call(this, model, renderingOnlyAttrs); } - - graph.stopBatch('layout'); - - // Width and height of the graph extended by margins. - var glSize = glGraph.graph(); - // Return the bounding box of the graph after the layout. - return new Rect( - marginX, - marginY, - Math.abs(glSize.width - 2 * marginX), - Math.abs(glSize.height - 2 * marginY) - ); }, - fromGraphLib: function(glGraph, opt) { - - opt = opt || {}; + updateContent: function(cell, renderingOnlyAttrs) { - var importNode = opt.importNode || noop; - var importEdge = opt.importEdge || noop; - var graph = (this instanceof Graph) ? this : new Graph; + // Create copy of the text attributes + var textAttrs = merge({}, (renderingOnlyAttrs || cell.get('attrs'))['.content']); - // Import all nodes. - glGraph.nodes().forEach(function(node) { - importNode.call(graph, node, glGraph, graph, opt); - }); + textAttrs = omit(textAttrs, 'text'); - // Import all edges. - glGraph.edges().forEach(function(edge) { - importEdge.call(graph, edge, glGraph, graph, opt); + // Break the content to fit the element size taking into account the attributes + // set on the model. + var text = breakText(cell.get('content'), cell.get('size'), textAttrs, { + // measuring sandbox svg document + svgDocument: this.paper.svg }); - return graph; - }, - - // Create new graphlib graph from existing JointJS graph. - toGraphLib: function(graph, opt) { - - opt = opt || {}; - - /* eslint-disable no-undef */ - var graphlibUtil = opt.graphlib || (typeof graphlib !== 'undefined' ? graphlib : undefined); - /* eslint-enable no-undef */ - - if (graphlibUtil === undefined) { throw new Error('The the "graphlib" utility is a mandatory dependency.'); } - - var glGraphType = pick(opt, 'directed', 'compound', 'multigraph'); - var glGraph = new graphlibUtil.Graph(glGraphType); - var setNodeLabel = opt.setNodeLabel || noop; - var setEdgeLabel = opt.setEdgeLabel || noop; - var setEdgeName = opt.setEdgeName || noop; - var collection = graph.get('cells'); - - for (var i = 0, n = collection.length; i < n; i++) { - - var cell = collection.at(i); - if (cell.isLink()) { - - var source = cell.get('source'); - var target = cell.get('target'); + // Create a new attrs with same structure as the model attrs { text: { *textAttributes* }} + var attrs = setByPath({}, '.content', textAttrs, '/'); - // Links that end at a point are ignored. - if (!source.id || !target.id) { break; } + // Replace text attribute with the one we just processed. + attrs['.content'].text = text; - // Note that if we are creating a multigraph we can name the edges. If - // we try to name edges on a non-multigraph an exception is thrown. - glGraph.setEdge(source.id, target.id, setEdgeLabel(cell), setEdgeName(cell)); + // Update the view using renderingOnlyAttributes parameter. + ElementView.prototype.update.call(this, cell, attrs); + } + }); - } else { + var basic = ({ + Generic: Generic, + Rect: Rect$1, + TextView: TextView, + Text: Text, + Circle: Circle, + Ellipse: Ellipse$1, + Polygon: Polygon$1, + Polyline: Polyline$1, + Image: Image, + Path: Path$1, + Rhombus: Rhombus, + TextBlock: TextBlock, + TextBlockView: TextBlockView + }); - glGraph.setNode(cell.id, setNodeLabel(cell)); + // ELEMENTS - // For the compound graphs we have to take embeds into account. - if (glGraph.isCompound() && cell.has('parent')) { - var parentId = cell.get('parent'); - if (collection.has(parentId)) { - // Make sure the parent cell is included in the graph (this can - // happen when the layout is run on part of the graph only). - glGraph.setParent(cell.id, parentId); - } - } - } + var Rectangle = Element$1.define('standard.Rectangle', { + attrs: { + body: { + refWidth: '100%', + refHeight: '100%', + strokeWidth: 2, + stroke: '#000000', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' } - - return glGraph; } - }; + }, { + markup: [{ + tagName: 'rect', + selector: 'body', + }, { + tagName: 'text', + selector: 'label' + }] + }); - Graph.prototype.toGraphLib = function(opt) { + var Circle$1 = Element$1.define('standard.Circle', { + attrs: { + body: { + refCx: '50%', + refCy: '50%', + refR: '50%', + strokeWidth: 2, + stroke: '#333333', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' + } + } + }, { + markup: [{ + tagName: 'circle', + selector: 'body' + }, { + tagName: 'text', + selector: 'label' + }] + }); - return DirectedGraph.toGraphLib(this, opt); - }; + var Ellipse$2 = Element$1.define('standard.Ellipse', { + attrs: { + body: { + refCx: '50%', + refCy: '50%', + refRx: '50%', + refRy: '50%', + strokeWidth: 2, + stroke: '#333333', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' + } + } + }, { + markup: [{ + tagName: 'ellipse', + selector: 'body' + }, { + tagName: 'text', + selector: 'label' + }] + }); - Graph.prototype.fromGraphLib = function(glGraph, opt) { + var Path$2 = Element$1.define('standard.Path', { + attrs: { + body: { + refD: 'M 0 0 L 10 0 10 10 0 10 Z', + strokeWidth: 2, + stroke: '#333333', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' + } + } + }, { + markup: [{ + tagName: 'path', + selector: 'body' + }, { + tagName: 'text', + selector: 'label' + }] + }); - return DirectedGraph.fromGraphLib.call(this, glGraph, opt); - }; + var Polygon$2 = Element$1.define('standard.Polygon', { + attrs: { + body: { + refPoints: '0 0 10 0 10 10 0 10', + strokeWidth: 2, + stroke: '#333333', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' + } + } + }, { + markup: [{ + tagName: 'polygon', + selector: 'body' + }, { + tagName: 'text', + selector: 'label' + }] + }); - var Generic = Element$1.define('basic.Generic', { + var Polyline$2 = Element$1.define('standard.Polyline', { attrs: { - '.': { fill: '#ffffff', stroke: 'none' } + body: { + refPoints: '0 0 10 0 10 10 0 10 0 0', + strokeWidth: 2, + stroke: '#333333', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' + } } + }, { + markup: [{ + tagName: 'polyline', + selector: 'body' + }, { + tagName: 'text', + selector: 'label' + }] }); - var Rect$1 = Generic.define('basic.Rect', { + var Image$1 = Element$1.define('standard.Image', { attrs: { - 'rect': { - fill: '#ffffff', - stroke: '#000000', - width: 100, - height: 60 + image: { + refWidth: '100%', + refHeight: '100%', + // xlinkHref: '[URL]' }, - 'text': { - fill: '#000000', - text: '', - 'font-size': 14, - 'ref-x': .5, - 'ref-y': .5, - 'text-anchor': 'middle', - 'y-alignment': 'middle', - 'font-family': 'Arial, helvetica, sans-serif' + label: { + textVerticalAnchor: 'top', + textAnchor: 'middle', + refX: '50%', + refY: '100%', + refY2: 10, + fontSize: 14, + fill: '#333333' } } }, { - markup: '' + markup: [{ + tagName: 'image', + selector: 'image' + }, { + tagName: 'text', + selector: 'label' + }] }); - var TextView = ElementView.extend({ - - presentationAttributes: ElementView.addPresentationAttributes({ - // The element view is not automatically re-scaled to fit the model size - // when the attribute 'attrs' is changed. - attrs: ['SCALE'] - }), - - confirmUpdate: function() { - var flags = ElementView.prototype.confirmUpdate.apply(this, arguments); - if (this.hasFlag(flags, 'SCALE')) { - this.resize(); - flags = this.removeFlag(flags, 'SCALE'); + var BorderedImage = Element$1.define('standard.BorderedImage', { + attrs: { + border: { + refWidth: '100%', + refHeight: '100%', + stroke: '#333333', + strokeWidth: 2 + }, + background: { + refWidth: -1, + refHeight: -1, + x: 0.5, + y: 0.5, + fill: '#FFFFFF' + }, + image: { + // xlinkHref: '[URL]' + refWidth: -1, + refHeight: -1, + x: 0.5, + y: 0.5 + }, + label: { + textVerticalAnchor: 'top', + textAnchor: 'middle', + refX: '50%', + refY: '100%', + refY2: 10, + fontSize: 14, + fill: '#333333' } - return flags; } + }, { + markup: [{ + tagName: 'rect', + selector: 'background', + attributes: { + 'stroke': 'none' + } + }, { + tagName: 'image', + selector: 'image' + }, { + tagName: 'rect', + selector: 'border', + attributes: { + 'fill': 'none' + } + }, { + tagName: 'text', + selector: 'label' + }] }); - var Text = Generic.define('basic.Text', { + var EmbeddedImage = Element$1.define('standard.EmbeddedImage', { attrs: { - 'text': { - 'font-size': 18, - fill: '#000000' + body: { + refWidth: '100%', + refHeight: '100%', + stroke: '#333333', + fill: '#FFFFFF', + strokeWidth: 2 + }, + image: { + // xlinkHref: '[URL]' + refWidth: '30%', + refHeight: -20, + x: 10, + y: 10, + preserveAspectRatio: 'xMidYMin' + }, + label: { + textVerticalAnchor: 'top', + textAnchor: 'left', + refX: '30%', + refX2: 20, // 10 + 10 + refY: 10, + fontSize: 14, + fill: '#333333' } } }, { - markup: '', + markup: [{ + tagName: 'rect', + selector: 'body' + }, { + tagName: 'image', + selector: 'image' + }, { + tagName: 'text', + selector: 'label' + }] }); - var Circle = Generic.define('basic.Circle', { - size: { width: 60, height: 60 }, + var InscribedImage = Element$1.define('standard.InscribedImage', { attrs: { - 'circle': { - fill: '#ffffff', - stroke: '#000000', - r: 30, - cx: 30, - cy: 30 + border: { + refRx: '50%', + refRy: '50%', + refCx: '50%', + refCy: '50%', + stroke: '#333333', + strokeWidth: 2 }, - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-y': .5, - 'y-alignment': 'middle', - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + background: { + refRx: '50%', + refRy: '50%', + refCx: '50%', + refCy: '50%', + fill: '#FFFFFF' + }, + image: { + // The image corners touch the border when its size is Math.sqrt(2) / 2 = 0.707.. ~= 70% + refWidth: '68%', + refHeight: '68%', + // The image offset is calculated as (100% - 68%) / 2 + refX: '16%', + refY: '16%', + preserveAspectRatio: 'xMidYMid' + // xlinkHref: '[URL]' + }, + label: { + textVerticalAnchor: 'top', + textAnchor: 'middle', + refX: '50%', + refY: '100%', + refY2: 10, + fontSize: 14, + fill: '#333333' } } }, { - markup: '', + markup: [{ + tagName: 'ellipse', + selector: 'background' + }, { + tagName: 'image', + selector: 'image' + }, { + tagName: 'ellipse', + selector: 'border', + attributes: { + 'fill': 'none' + } + }, { + tagName: 'text', + selector: 'label' + }] }); - var Ellipse$1 = Generic.define('basic.Ellipse', { - size: { width: 60, height: 40 }, + var HeaderedRectangle = Element$1.define('standard.HeaderedRectangle', { attrs: { - 'ellipse': { - fill: '#ffffff', + body: { + refWidth: '100%', + refHeight: '100%', + strokeWidth: 2, stroke: '#000000', - rx: 30, - ry: 20, - cx: 30, - cy: 20 + fill: '#FFFFFF' }, - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-y': .5, - 'y-alignment': 'middle', - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + header: { + refWidth: '100%', + height: 30, + strokeWidth: 2, + stroke: '#000000', + fill: '#FFFFFF' + }, + headerText: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: 15, + fontSize: 16, + fill: '#333333' + }, + bodyText: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + refY2: 15, + fontSize: 14, + fill: '#333333' } } }, { - markup: '', + markup: [{ + tagName: 'rect', + selector: 'body' + }, { + tagName: 'rect', + selector: 'header' + }, { + tagName: 'text', + selector: 'headerText' + }, { + tagName: 'text', + selector: 'bodyText' + }] }); - var Polygon$1 = Generic.define('basic.Polygon', { - size: { width: 60, height: 40 }, + var CYLINDER_TILT = 10; + + var Cylinder = Element$1.define('standard.Cylinder', { attrs: { - 'polygon': { - fill: '#ffffff', - stroke: '#000000' + body: { + lateralArea: CYLINDER_TILT, + fill: '#FFFFFF', + stroke: '#333333', + strokeWidth: 2 }, - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-dy': 20, - 'y-alignment': 'middle', - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + top: { + refCx: '50%', + cy: CYLINDER_TILT, + refRx: '50%', + ry: CYLINDER_TILT, + fill: '#FFFFFF', + stroke: '#333333', + strokeWidth: 2 + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '100%', + refY2: 15, + fontSize: 14, + fill: '#333333' } } }, { - markup: '', + markup: [{ + tagName: 'path', + selector: 'body' + }, { + tagName: 'ellipse', + selector: 'top' + }, { + tagName: 'text', + selector: 'label' + }], + + topRy: function(t, opt) { + // getter + if (t === undefined) { return this.attr('body/lateralArea'); } + + // setter + var isPercentageSetter = isPercentage(t); + + var bodyAttrs = { lateralArea: t }; + var topAttrs = isPercentageSetter + ? { refCy: t, refRy: t, cy: null, ry: null } + : { refCy: null, refRy: null, cy: t, ry: t }; + + return this.attr({ body: bodyAttrs, top: topAttrs }, opt); + } + + }, { + attributes: { + lateralArea: { + set: function(t, refBBox) { + var isPercentageSetter = isPercentage(t); + if (isPercentageSetter) { t = parseFloat(t) / 100; } + + var x = refBBox.x; + var y = refBBox.y; + var w = refBBox.width; + var h = refBBox.height; + + // curve control point variables + var rx = w / 2; + var ry = isPercentageSetter ? (h * t) : t; + + var kappa = V.KAPPA; + var cx = kappa * rx; + var cy = kappa * (isPercentageSetter ? (h * t) : t); + + // shape variables + var xLeft = x; + var xCenter = x + (w / 2); + var xRight = x + w; + + var ySideTop = y + ry; + var yCurveTop = ySideTop - ry; + var ySideBottom = y + h - ry; + var yCurveBottom = y + h; + + // return calculated shape + var data = [ + 'M', xLeft, ySideTop, + 'L', xLeft, ySideBottom, + 'C', x, (ySideBottom + cy), (xCenter - cx), yCurveBottom, xCenter, yCurveBottom, + 'C', (xCenter + cx), yCurveBottom, xRight, (ySideBottom + cy), xRight, ySideBottom, + 'L', xRight, ySideTop, + 'C', xRight, (ySideTop - cy), (xCenter + cx), yCurveTop, xCenter, yCurveTop, + 'C', (xCenter - cx), yCurveTop, xLeft, (ySideTop - cy), xLeft, ySideTop, + 'Z' + ]; + return { d: data.join(' ') }; + } + } + } }); - var Polyline$1 = Generic.define('basic.Polyline', { - size: { width: 60, height: 40 }, + var foLabelMarkup = { + tagName: 'foreignObject', + selector: 'foreignObject', + attributes: { + 'overflow': 'hidden' + }, + children: [{ + tagName: 'div', + namespaceURI: 'http://www.w3.org/1999/xhtml', + selector: 'label', + style: { + width: '100%', + height: '100%', + position: 'static', + backgroundColor: 'transparent', + textAlign: 'center', + margin: 0, + padding: '0px 5px', + boxSizing: 'border-box', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + } + }] + }; + + var svgLabelMarkup = { + tagName: 'text', + selector: 'label', + attributes: { + 'text-anchor': 'middle' + } + }; + + var labelMarkup = (env.test('svgforeignobject')) ? foLabelMarkup : svgLabelMarkup; + + var TextBlock$1 = Element$1.define('standard.TextBlock', { attrs: { - 'polyline': { + body: { + refWidth: '100%', + refHeight: '100%', + stroke: '#333333', fill: '#ffffff', - stroke: '#000000' + strokeWidth: 2 }, - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-dy': 20, - 'y-alignment': 'middle', - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + foreignObject: { + refWidth: '100%', + refHeight: '100%' + }, + label: { + style: { + fontSize: 14 + } } } }, { - markup: '', + markup: [{ + tagName: 'rect', + selector: 'body' + }, labelMarkup] + }, { + attributes: { + text: { + set: function(text, refBBox, node, attrs) { + if (node instanceof HTMLElement) { + node.textContent = text; + } else { + // No foreign object + var style = attrs.style || {}; + var wrapValue = { text: text, width: -5, height: '100%' }; + var wrapAttrs = assign({ textVerticalAnchor: 'middle' }, style); + attributes.textWrap.set.call(this, wrapValue, refBBox, node, wrapAttrs); + return { fill: style.color || null }; + } + }, + position: function(text, refBBox, node) { + // No foreign object + if (node instanceof SVGElement) { return refBBox.center(); } + } + } + } }); - var Image = Generic.define('basic.Image', { + // LINKS + + var Link$1 = Link.define('standard.Link', { attrs: { - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-dy': 20, - 'y-alignment': 'middle', - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + line: { + connection: true, + stroke: '#333333', + strokeWidth: 2, + strokeLinejoin: 'round', + targetMarker: { + 'type': 'path', + 'd': 'M 10 -5 0 0 10 5 z' + } + }, + wrapper: { + connection: true, + strokeWidth: 10, + strokeLinejoin: 'round' } } }, { - markup: '', + markup: [{ + tagName: 'path', + selector: 'wrapper', + attributes: { + 'fill': 'none', + 'cursor': 'pointer', + 'stroke': 'transparent', + 'stroke-linecap': 'round' + } + }, { + tagName: 'path', + selector: 'line', + attributes: { + 'fill': 'none', + 'pointer-events': 'none' + } + }] }); - var Path$1 = Generic.define('basic.Path', { - size: { width: 60, height: 60 }, + var DoubleLink = Link.define('standard.DoubleLink', { attrs: { - 'path': { - fill: '#ffffff', - stroke: '#000000' + line: { + connection: true, + stroke: '#DDDDDD', + strokeWidth: 4, + strokeLinejoin: 'round', + targetMarker: { + type: 'path', + stroke: '#000000', + d: 'M 10 -3 10 -10 -2 0 10 10 10 3' + } }, - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref': 'path', - 'ref-x': .5, - 'ref-dy': 10, - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + outline: { + connection: true, + stroke: '#000000', + strokeWidth: 6, + strokeLinejoin: 'round' } } - }, { - markup: '', + markup: [{ + tagName: 'path', + selector: 'outline', + attributes: { + 'fill': 'none' + } + }, { + tagName: 'path', + selector: 'line', + attributes: { + 'fill': 'none' + } + }] }); - var Rhombus = Path$1.define('basic.Rhombus', { + var ShadowLink = Link.define('standard.ShadowLink', { attrs: { - 'path': { - d: 'M 30 0 L 60 30 30 60 0 30 z' + line: { + connection: true, + stroke: '#FF0000', + strokeWidth: 20, + strokeLinejoin: 'round', + targetMarker: { + 'type': 'path', + 'stroke': 'none', + 'd': 'M 0 -10 -10 0 0 10 z' + }, + sourceMarker: { + 'type': 'path', + 'stroke': 'none', + 'd': 'M -10 -10 0 0 -10 10 0 10 0 -10 z' + } }, - 'text': { - 'ref-y': .5, - 'ref-dy': null, - 'y-alignment': 'middle' + shadow: { + connection: true, + refX: 3, + refY: 6, + stroke: '#000000', + strokeOpacity: 0.2, + strokeWidth: 20, + strokeLinejoin: 'round', + targetMarker: { + 'type': 'path', + 'd': 'M 0 -10 -10 0 0 10 z', + 'stroke': 'none' + }, + sourceMarker: { + 'type': 'path', + 'stroke': 'none', + 'd': 'M -10 -10 0 0 -10 10 0 10 0 -10 z' + } } } + }, { + markup: [{ + tagName: 'path', + selector: 'shadow', + attributes: { + 'fill': 'none' + } + }, { + tagName: 'path', + selector: 'line', + attributes: { + 'fill': 'none' + } + }] }); - var svgForeignObjectSupported = env.test('svgforeignobject'); + var standard = ({ + Rectangle: Rectangle, + Circle: Circle$1, + Ellipse: Ellipse$2, + Path: Path$2, + Polygon: Polygon$2, + Polyline: Polyline$2, + Image: Image$1, + BorderedImage: BorderedImage, + EmbeddedImage: EmbeddedImage, + InscribedImage: InscribedImage, + HeaderedRectangle: HeaderedRectangle, + Cylinder: Cylinder, + TextBlock: TextBlock$1, + Link: Link$1, + DoubleLink: DoubleLink, + ShadowLink: ShadowLink + }); - var TextBlock = Generic.define('basic.TextBlock', { - // see joint.css for more element styles + /** + * @deprecated use the port api instead + */ + var Model = Generic.define('devs.Model', { + inPorts: [], + outPorts: [], + size: { + width: 80, + height: 80 + }, attrs: { - rect: { - fill: '#ffffff', - stroke: '#000000', - width: 80, - height: 100 - }, - text: { - fill: '#000000', - 'font-size': 14, - 'font-family': 'Arial, helvetica, sans-serif' + '.': { + magnet: false }, - '.content': { - text: '', + '.label': { + text: 'Model', 'ref-x': .5, - 'ref-y': .5, - 'y-alignment': 'middle', - 'x-alignment': 'middle' + 'ref-y': 10, + 'font-size': 18, + 'text-anchor': 'middle', + fill: '#000' + }, + '.body': { + 'ref-width': '100%', + 'ref-height': '100%', + stroke: '#000' } }, - - content: '' + ports: { + groups: { + 'in': { + position: { + name: 'left' + }, + attrs: { + '.port-label': { + fill: '#000' + }, + '.port-body': { + fill: '#fff', + stroke: '#000', + r: 10, + magnet: true + } + }, + label: { + position: { + name: 'left', + args: { + y: 10 + } + } + } + }, + 'out': { + position: { + name: 'right' + }, + attrs: { + '.port-label': { + fill: '#000' + }, + '.port-body': { + fill: '#fff', + stroke: '#000', + r: 10, + magnet: true + } + }, + label: { + position: { + name: 'right', + args: { + y: 10 + } + } + } + } + } + } }, { - markup: [ - '', - '', - svgForeignObjectSupported - ? '
' - : '', - '' - ].join(''), + markup: '', + portMarkup: '', + portLabelMarkup: '', initialize: function() { - this.listenTo(this, 'change:size', this.updateSize); - this.listenTo(this, 'change:content', this.updateContent); - this.updateSize(this, this.get('size')); - this.updateContent(this, this.get('content')); Generic.prototype.initialize.apply(this, arguments); - }, - - updateSize: function(cell, size) { - // Selector `foreignObject' doesn't work across all browsers, we're using class selector instead. - // We have to clone size as we don't want attributes.div.style to be same object as attributes.size. - this.attr({ - '.fobj': assign({}, size), - div: { - style: assign({}, size) - } - }); + this.on('change:inPorts change:outPorts', this.updatePortItems, this); + this.updatePortItems(); }, - updateContent: function(cell, content) { + updatePortItems: function(model, changed, opt) { - if (svgForeignObjectSupported) { + // Make sure all ports are unique. + var inPorts = uniq(this.get('inPorts')); + var outPorts = difference(uniq(this.get('outPorts')), inPorts); - // Content element is a
element. - this.attr({ - '.content': { - html: sanitizeHTML(content) - } - }); + var inPortItems = this.createPortItems('in', inPorts); + var outPortItems = this.createPortItems('out', outPorts); - } else { + this.prop('ports/items', inPortItems.concat(outPortItems), assign({ rewrite: true }, opt)); + }, - // Content element is a element. - // SVG elements don't have innerHTML attribute. - this.attr({ - '.content': { - text: content + createPortItem: function(group, port) { + + return { + id: port, + group: group, + attrs: { + '.port-label': { + text: port } - }); - } + } + }; }, - // Here for backwards compatibility: - setForeignObjectSize: function() { + createPortItems: function(group, ports) { - this.updateSize.apply(this, arguments); + return toArray(ports).map(this.createPortItem.bind(this, group)); }, - // Here for backwards compatibility: - setDivContent: function() { + _addGroupPort: function(port, group, opt) { - this.updateContent.apply(this, arguments); - } - }); + var ports = this.get(group); + return this.set(group, Array.isArray(ports) ? ports.concat(port) : [port], opt); + }, - // TextBlockView implements the fallback for IE when no foreignObject exists and - // the text needs to be manually broken. - var TextBlockView = ElementView.extend({ + addOutPort: function(port, opt) { - presentationAttributes: svgForeignObjectSupported - ? ElementView.prototype.presentationAttributes - : ElementView.addPresentationAttributes({ - content: ['CONTENT'], - size: ['CONTENT'] - }), + return this._addGroupPort(port, 'outPorts', opt); + }, - initFlag: ['RENDER', 'CONTENT'], + addInPort: function(port, opt) { - confirmUpdate: function() { - var flags = ElementView.prototype.confirmUpdate.apply(this, arguments); - if (this.hasFlag(flags, 'CONTENT')) { - this.updateContent(this.model); - flags = this.removeFlag(flags, 'CONTENT'); - } - return flags; + return this._addGroupPort(port, 'inPorts', opt); }, - update: function(_, renderingOnlyAttrs) { - - var model = this.model; + _removeGroupPort: function(port, group, opt) { - if (!svgForeignObjectSupported) { + return this.set(group, without(this.get(group), port), opt); + }, - // Update everything but the content first. - var noTextAttrs = omit(renderingOnlyAttrs || model.get('attrs'), '.content'); - ElementView.prototype.update.call(this, model, noTextAttrs); + removeOutPort: function(port, opt) { - if (!renderingOnlyAttrs || has$2(renderingOnlyAttrs, '.content')) { - // Update the content itself. - this.updateContent(model, renderingOnlyAttrs); - } + return this._removeGroupPort(port, 'outPorts', opt); + }, - } else { + removeInPort: function(port, opt) { - ElementView.prototype.update.call(this, model, renderingOnlyAttrs); - } + return this._removeGroupPort(port, 'inPorts', opt); }, - updateContent: function(cell, renderingOnlyAttrs) { - - // Create copy of the text attributes - var textAttrs = merge({}, (renderingOnlyAttrs || cell.get('attrs'))['.content']); + _changeGroup: function(group, properties, opt) { - textAttrs = omit(textAttrs, 'text'); + return this.prop('ports/groups/' + group, isObject$1(properties) ? properties : {}, opt); + }, - // Break the content to fit the element size taking into account the attributes - // set on the model. - var text = breakText(cell.get('content'), cell.get('size'), textAttrs, { - // measuring sandbox svg document - svgDocument: this.paper.svg - }); + changeInGroup: function(properties, opt) { - // Create a new attrs with same structure as the model attrs { text: { *textAttributes* }} - var attrs = setByPath({}, '.content', textAttrs, '/'); + return this._changeGroup('in', properties, opt); + }, - // Replace text attribute with the one we just processed. - attrs['.content'].text = text; + changeOutGroup: function(properties, opt) { - // Update the view using renderingOnlyAttributes parameter. - ElementView.prototype.update.call(this, cell, attrs); + return this._changeGroup('out', properties, opt); } }); - var basic = ({ - Generic: Generic, - Rect: Rect$1, - TextView: TextView, - Text: Text, - Circle: Circle, - Ellipse: Ellipse$1, - Polygon: Polygon$1, - Polyline: Polyline$1, - Image: Image, - Path: Path$1, - Rhombus: Rhombus, - TextBlock: TextBlock, - TextBlockView: TextBlockView - }); - - // ELEMENTS - - var Rectangle = Element$1.define('standard.Rectangle', { + var Atomic = Model.define('devs.Atomic', { + size: { + width: 80, + height: 80 + }, attrs: { - body: { - refWidth: '100%', - refHeight: '100%', - strokeWidth: 2, - stroke: '#000000', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' + '.label': { + text: 'Atomic' } } - }, { - markup: [{ - tagName: 'rect', - selector: 'body', - }, { - tagName: 'text', - selector: 'label' - }] }); - var Circle$1 = Element$1.define('standard.Circle', { + var Coupled = Model.define('devs.Coupled', { + size: { + width: 200, + height: 300 + }, attrs: { - body: { - refCx: '50%', - refCy: '50%', - refR: '50%', - strokeWidth: 2, - stroke: '#333333', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' + '.label': { + text: 'Coupled' } } - }, { - markup: [{ - tagName: 'circle', - selector: 'body' - }, { - tagName: 'text', - selector: 'label' - }] }); - var Ellipse$2 = Element$1.define('standard.Ellipse', { + var Link$2 = Link.define('devs.Link', { attrs: { - body: { - refCx: '50%', - refCy: '50%', - refRx: '50%', - refRy: '50%', - strokeWidth: 2, - stroke: '#333333', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' + '.connection': { + 'stroke-width': 2 } } - }, { - markup: [{ - tagName: 'ellipse', - selector: 'body' - }, { - tagName: 'text', - selector: 'label' - }] }); - var Path$2 = Element$1.define('standard.Path', { + var devs = ({ + Model: Model, + Atomic: Atomic, + Coupled: Coupled, + Link: Link$2 + }); + + var Gate = Generic.define('logic.Gate', { + size: { width: 80, height: 40 }, + attrs: { + '.': { magnet: false }, + '.body': { width: 100, height: 50 }, + circle: { r: 7, stroke: 'black', fill: 'transparent', 'stroke-width': 2 } + } + }, { + operation: function() { + return true; + } + }); + + var IO = Gate.define('logic.IO', { + size: { width: 60, height: 30 }, attrs: { - body: { - refD: 'M 0 0 L 10 0 10 10 0 10 Z', - strokeWidth: 2, - stroke: '#333333', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' + '.body': { fill: 'white', stroke: 'black', 'stroke-width': 2 }, + '.wire': { ref: '.body', 'ref-y': .5, stroke: 'black' }, + text: { + fill: 'black', + ref: '.body', 'ref-x': .5, 'ref-y': .5, 'y-alignment': 'middle', + 'text-anchor': 'middle', + 'font-weight': 'bold', + 'font-variant': 'small-caps', + 'text-transform': 'capitalize', + 'font-size': '14px' } } }, { - markup: [{ - tagName: 'path', - selector: 'body' - }, { - tagName: 'text', - selector: 'label' - }] + markup: '', }); - var Polygon$2 = Element$1.define('standard.Polygon', { + var Input = IO.define('logic.Input', { attrs: { - body: { - refPoints: '0 0 10 0 10 10 0 10', - strokeWidth: 2, - stroke: '#333333', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' - } + '.wire': { 'ref-dx': 0, d: 'M 0 0 L 23 0' }, + circle: { ref: '.body', 'ref-dx': 30, 'ref-y': 0.5, magnet: true, 'class': 'output', port: 'out' }, + text: { text: 'input' } } - }, { - markup: [{ - tagName: 'polygon', - selector: 'body' - }, { - tagName: 'text', - selector: 'label' - }] }); - var Polyline$2 = Element$1.define('standard.Polyline', { + var Output = IO.define('logic.Output', { attrs: { - body: { - refPoints: '0 0 10 0 10 10 0 10 0 0', - strokeWidth: 2, - stroke: '#333333', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' - } + '.wire': { 'ref-x': 0, d: 'M 0 0 L -23 0' }, + circle: { ref: '.body', 'ref-x': -30, 'ref-y': 0.5, magnet: 'passive', 'class': 'input', port: 'in' }, + text: { text: 'output' } } - }, { - markup: [{ - tagName: 'polyline', - selector: 'body' - }, { - tagName: 'text', - selector: 'label' - }] }); - var Image$1 = Element$1.define('standard.Image', { + var Gate11 = Gate.define('logic.Gate11', { attrs: { - image: { - refWidth: '100%', - refHeight: '100%', - // xlinkHref: '[URL]' - }, - label: { - textVerticalAnchor: 'top', - textAnchor: 'middle', - refX: '50%', - refY: '100%', - refY2: 10, - fontSize: 14, - fill: '#333333' - } + '.input': { ref: '.body', 'ref-x': -2, 'ref-y': 0.5, magnet: 'passive', port: 'in' }, + '.output': { ref: '.body', 'ref-dx': 2, 'ref-y': 0.5, magnet: true, port: 'out' } } }, { - markup: [{ - tagName: 'image', - selector: 'image' - }, { - tagName: 'text', - selector: 'label' - }] + markup: '', }); - var BorderedImage = Element$1.define('standard.BorderedImage', { + var Gate21 = Gate.define('logic.Gate21', { attrs: { - border: { - refWidth: '100%', - refHeight: '100%', - stroke: '#333333', - strokeWidth: 2 - }, - background: { - refWidth: -1, - refHeight: -1, - x: 0.5, - y: 0.5, - fill: '#FFFFFF' - }, - image: { - // xlinkHref: '[URL]' - refWidth: -1, - refHeight: -1, - x: 0.5, - y: 0.5 - }, - label: { - textVerticalAnchor: 'top', - textAnchor: 'middle', - refX: '50%', - refY: '100%', - refY2: 10, - fontSize: 14, - fill: '#333333' - } + '.input1': { ref: '.body', 'ref-x': -2, 'ref-y': 0.3, magnet: 'passive', port: 'in1' }, + '.input2': { ref: '.body', 'ref-x': -2, 'ref-y': 0.7, magnet: 'passive', port: 'in2' }, + '.output': { ref: '.body', 'ref-dx': 2, 'ref-y': 0.5, magnet: true, port: 'out' } } }, { - markup: [{ - tagName: 'rect', - selector: 'background', - attributes: { - 'stroke': 'none' - } - }, { - tagName: 'image', - selector: 'image' - }, { - tagName: 'rect', - selector: 'border', - attributes: { - 'fill': 'none' - } - }, { - tagName: 'text', - selector: 'label' - }] + markup: '', }); - var EmbeddedImage = Element$1.define('standard.EmbeddedImage', { - attrs: { - body: { - refWidth: '100%', - refHeight: '100%', - stroke: '#333333', - fill: '#FFFFFF', - strokeWidth: 2 - }, - image: { - // xlinkHref: '[URL]' - refWidth: '30%', - refHeight: -20, - x: 10, - y: 10, - preserveAspectRatio: 'xMidYMin' - }, - label: { - textVerticalAnchor: 'top', - textAnchor: 'left', - refX: '30%', - refX2: 20, // 10 + 10 - refY: 10, - fontSize: 14, - fill: '#333333' - } + var Repeater = Gate11.define('logic.Repeater', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzIuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICA8L2c+Cjwvc3ZnPgo=' }} + }, { + operation: function(input) { + return input; } + }); + + var Not = Gate11.define('logic.Not', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzkuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI2NzEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzYiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA4MCwyNSBBIDQsNCAwIDEgMSA3MiwyNSBBIDQsNCAwIDEgMSA4MCwyNSB6IgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEsMCkiIC8+CiAgPC9nPgo8L3N2Zz4K' }} }, { - markup: [{ - tagName: 'rect', - selector: 'body' - }, { - tagName: 'image', - selector: 'image' - }, { - tagName: 'text', - selector: 'label' - }] + operation: function(input) { + return !input; + } }); - var InscribedImage = Element$1.define('standard.InscribedImage', { - attrs: { - border: { - refRx: '50%', - refRy: '50%', - refCx: '50%', - refCy: '50%', - stroke: '#333333', - strokeWidth: 2 - }, - background: { - refRx: '50%', - refRy: '50%', - refCx: '50%', - refCy: '50%', - fill: '#FFFFFF' - }, - image: { - // The image corners touch the border when its size is Math.sqrt(2) / 2 = 0.707.. ~= 70% - refWidth: '68%', - refHeight: '68%', - // The image offset is calculated as (100% - 68%) / 2 - refX: '16%', - refY: '16%', - preserveAspectRatio: 'xMidYMid' - // xlinkHref: '[URL]' - }, - label: { - textVerticalAnchor: 'top', - textAnchor: 'middle', - refX: '50%', - refY: '100%', - refY2: 10, - fontSize: 14, - fill: '#333333' - } + var Or = Gate21.define('logic.Or', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik9SIEFOU0kuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTAgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjcxNCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iMSA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMC41IDogMC4zMzMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODA2IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODE5IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjM3Mi4wNDcyNCA6IDM1MC43ODczOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI3NDQuMDk0NDggOiA1MjYuMTgxMDkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzc3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49Ijc1IDogNDAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTUwIDogNjAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDYwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTMyNzUiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNTAgOiAzMy4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTAwIDogNTAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTU1MzMiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjU1NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDE2LjY2NjY2NyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDI1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAyNSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iNCIKICAgICBpbmtzY2FwZTpjeD0iMTEzLjAwMDM5IgogICAgIGlua3NjYXBlOmN5PSIxMi44OTM3MzEiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImcyNTYwIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1wb2ludHM9InRydWUiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAwMDAiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMzk5IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg3NCIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMzciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii00IgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNzAsMjUgYyAyMCwwIDI1LDAgMjUsMCIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMSwxNSA1LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzIsMzUgNSwzNSIKICAgICAgIGlkPSJwYXRoMzk0NCIgLz4KICAgIDxnCiAgICAgICBpZD0iZzI1NjAiCiAgICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2LjUsLTM5LjUpIj4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=' }} + }, { + operation: function(input1, input2) { + return input1 || input2; } + }); + + var And = Gate21.define('logic.And', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IkFORCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI4IgogICAgIGlua3NjYXBlOmN4PSI1Ni42OTgzNDgiCiAgICAgaW5rc2NhcGU6Y3k9IjI1LjMyNjg5OSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTptZWRpdW07Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWluZGVudDowO3RleHQtYWxpZ246c3RhcnQ7dGV4dC1kZWNvcmF0aW9uOm5vbmU7bGluZS1oZWlnaHQ6bm9ybWFsO2xldHRlci1zcGFjaW5nOm5vcm1hbDt3b3JkLXNwYWNpbmc6bm9ybWFsO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlyZWN0aW9uOmx0cjtibG9jay1wcm9ncmVzc2lvbjp0Yjt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDozO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGU7Zm9udC1mYW1pbHk6Qml0c3RyZWFtIFZlcmEgU2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkJpdHN0cmVhbSBWZXJhIFNhbnMiCiAgICAgICBkPSJNIDMwLDUgTCAzMCw2LjQyODU3MTQgTCAzMCw0My41NzE0MjkgTCAzMCw0NSBMIDMxLjQyODU3MSw0NSBMIDUwLjQ3NjE5LDQ1IEMgNjEuNzQ0MDk4LDQ1IDcwLjQ3NjE5LDM1Ljk5OTk1NSA3MC40NzYxOSwyNSBDIDcwLjQ3NjE5LDE0LjAwMDA0NSA2MS43NDQwOTksNS4wMDAwMDAyIDUwLjQ3NjE5LDUgQyA1MC40NzYxOSw1IDUwLjQ3NjE5LDUgMzEuNDI4NTcxLDUgTCAzMCw1IHogTSAzMi44NTcxNDMsNy44NTcxNDI5IEMgNDAuODM0MjY0LDcuODU3MTQyOSA0NS45MTgzNjgsNy44NTcxNDI5IDQ4LjA5NTIzOCw3Ljg1NzE0MjkgQyA0OS4yODU3MTQsNy44NTcxNDI5IDQ5Ljg4MDk1Miw3Ljg1NzE0MjkgNTAuMTc4NTcxLDcuODU3MTQyOSBDIDUwLjMyNzM4MSw3Ljg1NzE0MjkgNTAuNDA5MjI3LDcuODU3MTQyOSA1MC40NDY0MjksNy44NTcxNDI5IEMgNTAuNDY1MDI5LDcuODU3MTQyOSA1MC40NzE1NDMsNy44NTcxNDI5IDUwLjQ3NjE5LDcuODU3MTQyOSBDIDYwLjIzNjg1Myw3Ljg1NzE0MyA2Ny4xNDI4NTcsMTUuNDk3MDk4IDY3LjE0Mjg1NywyNSBDIDY3LjE0Mjg1NywzNC41MDI5MDIgNTkuNzYwNjYyLDQyLjE0Mjg1NyA1MCw0Mi4xNDI4NTcgTCAzMi44NTcxNDMsNDIuMTQyODU3IEwgMzIuODU3MTQzLDcuODU3MTQyOSB6IgogICAgICAgaWQ9InBhdGgyODg0IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NzY2NjY3Nzc3NzY2NjIiAvPgogIDwvZz4KPC9zdmc+Cg==' }} + }, { - markup: [{ - tagName: 'ellipse', - selector: 'background' - }, { - tagName: 'image', - selector: 'image' - }, { - tagName: 'ellipse', - selector: 'border', - attributes: { - 'fill': 'none' - } - }, { - tagName: 'text', - selector: 'label' - }] + operation: function(input1, input2) { + return input1 && input2; + } }); - var HeaderedRectangle = Element$1.define('standard.HeaderedRectangle', { - attrs: { - body: { - refWidth: '100%', - refHeight: '100%', - strokeWidth: 2, - stroke: '#000000', - fill: '#FFFFFF' - }, - header: { - refWidth: '100%', - height: 30, - strokeWidth: 2, - stroke: '#000000', - fill: '#FFFFFF' - }, - headerText: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: 15, - fontSize: 16, - fill: '#333333' - }, - bodyText: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - refY2: 15, - fontSize: 14, - fill: '#333333' - } + var Nor = Gate21.define('logic.Nor', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjEiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjY3NzY0NCIKICAgICBpbmtzY2FwZTpjeT0iMjIuMTAyMzQ0IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjM3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItNCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc5LDI1IEMgOTksMjUgOTUsMjUgOTUsMjUiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBpZD0icGF0aDI2MDQiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI3NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjI1IgogICAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgICAgc29kaXBvZGk6cnk9IjQiCiAgICAgICAgIGQ9Ik0gNzksMjUgQSA0LDQgMCAxIDEgNzEsMjUgQSA0LDQgMCAxIDEgNzksMjUgeiIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI2LjUsMzkuNSkiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K' }} + }, { + operation: function(input1, input2) { + return !(input1 || input2); } + }); + + var Nand = Gate21.define('logic.Nand', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5BTkQgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTYiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjI4MzMwNyIKICAgICBpbmtzY2FwZTpjeT0iMTYuNDQyODQzIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzksMjUgQyA5MS44LDI1IDk1LDI1IDk1LDI1IgogICAgICAgaWQ9InBhdGgzMDU5IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMxLDE1IDUsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMiwzNSA1LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmb250LXNpemU6bWVkaXVtO2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1pbmRlbnQ6MDt0ZXh0LWFsaWduOnN0YXJ0O3RleHQtZGVjb3JhdGlvbjpub25lO2xpbmUtaGVpZ2h0Om5vcm1hbDtsZXR0ZXItc3BhY2luZzpub3JtYWw7d29yZC1zcGFjaW5nOm5vcm1hbDt0ZXh0LXRyYW5zZm9ybTpub25lO2RpcmVjdGlvbjpsdHI7YmxvY2stcHJvZ3Jlc3Npb246dGI7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MzttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO2ZvbnQtZmFtaWx5OkJpdHN0cmVhbSBWZXJhIFNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpCaXRzdHJlYW0gVmVyYSBTYW5zIgogICAgICAgZD0iTSAzMCw1IEwgMzAsNi40Mjg1NzE0IEwgMzAsNDMuNTcxNDI5IEwgMzAsNDUgTCAzMS40Mjg1NzEsNDUgTCA1MC40NzYxOSw0NSBDIDYxLjc0NDA5OCw0NSA3MC40NzYxOSwzNS45OTk5NTUgNzAuNDc2MTksMjUgQyA3MC40NzYxOSwxNC4wMDAwNDUgNjEuNzQ0MDk5LDUuMDAwMDAwMiA1MC40NzYxOSw1IEMgNTAuNDc2MTksNSA1MC40NzYxOSw1IDMxLjQyODU3MSw1IEwgMzAsNSB6IE0gMzIuODU3MTQzLDcuODU3MTQyOSBDIDQwLjgzNDI2NCw3Ljg1NzE0MjkgNDUuOTE4MzY4LDcuODU3MTQyOSA0OC4wOTUyMzgsNy44NTcxNDI5IEMgNDkuMjg1NzE0LDcuODU3MTQyOSA0OS44ODA5NTIsNy44NTcxNDI5IDUwLjE3ODU3MSw3Ljg1NzE0MjkgQyA1MC4zMjczODEsNy44NTcxNDI5IDUwLjQwOTIyNyw3Ljg1NzE0MjkgNTAuNDQ2NDI5LDcuODU3MTQyOSBDIDUwLjQ2NTAyOSw3Ljg1NzE0MjkgNTAuNDcxNTQzLDcuODU3MTQyOSA1MC40NzYxOSw3Ljg1NzE0MjkgQyA2MC4yMzY4NTMsNy44NTcxNDMgNjcuMTQyODU3LDE1LjQ5NzA5OCA2Ny4xNDI4NTcsMjUgQyA2Ny4xNDI4NTcsMzQuNTAyOTAyIDU5Ljc2MDY2Miw0Mi4xNDI4NTcgNTAsNDIuMTQyODU3IEwgMzIuODU3MTQzLDQyLjE0Mjg1NyBMIDMyLjg1NzE0Myw3Ljg1NzE0MjkgeiIKICAgICAgIGlkPSJwYXRoMjg4NCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2Njc2NjY2Nzc3Nzc2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDQwMDgiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg==' }} }, { - markup: [{ - tagName: 'rect', - selector: 'body' - }, { - tagName: 'rect', - selector: 'header' - }, { - tagName: 'text', - selector: 'headerText' - }, { - tagName: 'text', - selector: 'bodyText' - }] + operation: function(input1, input2) { + return !(input1 && input2); + } }); - var CYLINDER_TILT = 10; + var Xor = Gate21.define('logic.Xor', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhPUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iMjUuOTM4MTE2IgogICAgIGlua3NjYXBlOmN5PSIxNy4yMzAwNSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzAuMzg1NzE3LDE1IEwgNC45OTk5OTk4LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5NzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEuMzYyMDkxLDM1IEwgNC45OTk5OTk4LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPGcKICAgICAgIGlkPSJnMjU2MCIKICAgICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYuNSwtMzkuNSkiPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDM1MTYiCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi4yNSw4MS41MDAwMDUgQyAtMy44NDczNzQsODQuMTQ0NDA1IC00LjUsODQuNTAwMDA1IC00LjUsODQuNTAwMDA1IEwgLTguMTU2MjUsODQuNTAwMDA1IEwgLTYuMTU2MjUsODIuMDYyNTA1IEMgLTYuMTU2MjUsODIuMDYyNTA1IC0wLjUsNzUuMDYyNDUxIC0wLjUsNjQuNSBDIC0wLjUsNTMuOTM3NTQ5IC02LjE1NjI1LDQ2LjkzNzUgLTYuMTU2MjUsNDYuOTM3NSBMIC04LjE1NjI1LDQ0LjUgTCAtNC41LDQ0LjUgQyAtMy43MTg3NSw0NS40Mzc1IC0zLjA3ODEyNSw0Ni4xNTYyNSAtMi4yODEyNSw0Ny41IEMgLTAuNDA4NTMxLDUwLjU5OTgxNSAyLjUsNTYuNTI2NjQ2IDIuNSw2NC41IEMgMi41LDcyLjQ1MDY1IC0wLjM5NjY5Nyw3OC4zNzk0MjUgLTIuMjUsODEuNTAwMDA1IHoiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY3NjY2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K' }} + }, { + operation: function(input1, input2) { + return (!input1 || input2) && (input1 || !input2); + } + }); - var Cylinder = Element$1.define('standard.Cylinder', { - attrs: { - body: { - lateralArea: CYLINDER_TILT, - fill: '#FFFFFF', - stroke: '#333333', - strokeWidth: 2 - }, - top: { - refCx: '50%', - cy: CYLINDER_TILT, - refRx: '50%', - ry: CYLINDER_TILT, - fill: '#FFFFFF', - stroke: '#333333', - strokeWidth: 2 - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '100%', - refY2: 15, - fontSize: 14, - fill: '#333333' - } + var Xnor = Gate21.define('logic.Xnor', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhOT1IgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNTU3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTYuNjY2NjY3IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDI1IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0IgogICAgIGlua3NjYXBlOmN4PSI5NS43MjM2NiIKICAgICBpbmtzY2FwZTpjeT0iLTI2Ljc3NTAyMyIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyLjAwMDAwMDI0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc4LjMzMzMzMiwyNSBDIDkxLjY2NjY2NiwyNSA5NSwyNSA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMC4zODU3MTcsMTUgTCA0Ljk5OTk5OTgsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk3NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMS4zNjIwOTEsMzUgTCA0Ljk5OTk5OTgsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMzUxNiIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjI1LDgxLjUwMDAwNSBDIC0zLjg0NzM3NCw4NC4xNDQ0MDUgLTQuNSw4NC41MDAwMDUgLTQuNSw4NC41MDAwMDUgTCAtOC4xNTYyNSw4NC41MDAwMDUgTCAtNi4xNTYyNSw4Mi4wNjI1MDUgQyAtNi4xNTYyNSw4Mi4wNjI1MDUgLTAuNSw3NS4wNjI0NTEgLTAuNSw2NC41IEMgLTAuNSw1My45Mzc1NDkgLTYuMTU2MjUsNDYuOTM3NSAtNi4xNTYyNSw0Ni45Mzc1IEwgLTguMTU2MjUsNDQuNSBMIC00LjUsNDQuNSBDIC0zLjcxODc1LDQ1LjQzNzUgLTMuMDc4MTI1LDQ2LjE1NjI1IC0yLjI4MTI1LDQ3LjUgQyAtMC40MDg1MzEsNTAuNTk5ODE1IDIuNSw1Ni41MjY2NDYgMi41LDY0LjUgQyAyLjUsNzIuNDUwNjUgLTAuMzk2Njk3LDc4LjM3OTQyNSAtMi4yNSw4MS41MDAwMDUgeiIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Njc2NjY2NzYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDM1NTEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg==' }} + }, { + operation: function(input1, input2) { + return (!input1 || !input2) && (input1 || input2); } + }); + + var Wire = Link.define('logic.Wire', { + attrs: { + '.connection': { 'stroke-width': 2 }, + '.marker-vertex': { r: 7 } + }, + + router: { name: 'orthogonal' }, + connector: { name: 'rounded', args: { radius: 10 }} }, { - markup: [{ - tagName: 'path', - selector: 'body' - }, { - tagName: 'ellipse', - selector: 'top' - }, { - tagName: 'text', - selector: 'label' - }], + arrowheadMarkup: [ + '', + '', + '' + ].join(''), - topRy: function(t, opt) { - // getter - if (t === undefined) { return this.attr('body/lateralArea'); } + vertexMarkup: [ + '', + '', + '', + '', + '', + 'Remove vertex.', + '', + '', + '' + ].join('') + }); - // setter - var isPercentageSetter = isPercentage(t); + var logic = ({ + Gate: Gate, + IO: IO, + Input: Input, + Output: Output, + Gate11: Gate11, + Gate21: Gate21, + Repeater: Repeater, + Not: Not, + Or: Or, + And: And, + Nor: Nor, + Nand: Nand, + Xor: Xor, + Xnor: Xnor, + Wire: Wire + }); - var bodyAttrs = { lateralArea: t }; - var topAttrs = isPercentageSetter - ? { refCy: t, refRy: t, cy: null, ry: null } - : { refCy: null, refRy: null, cy: t, ry: t }; + var KingWhite = Generic.define('chess.KingWhite', { + size: { width: 42, height: 38 } + }, { + markup: ' ' + }); - return this.attr({ body: bodyAttrs, top: topAttrs }, opt); - } + var KingBlack = Generic.define('chess.KingBlack', { + size: { width: 42, height: 38 } + }, { + markup: ' ' + }); + var QueenWhite = Generic.define('chess.QueenWhite', { + size: { width: 42, height: 38 } }, { - attributes: { - lateralArea: { - set: function(t, refBBox) { - var isPercentageSetter = isPercentage(t); - if (isPercentageSetter) { t = parseFloat(t) / 100; } + markup: ' ' + }); - var x = refBBox.x; - var y = refBBox.y; - var w = refBBox.width; - var h = refBBox.height; + var QueenBlack = Generic.define('chess.QueenBlack', { + size: { width: 42, height: 38 } + }, { + markup: ' ' + }); - // curve control point variables - var rx = w / 2; - var ry = isPercentageSetter ? (h * t) : t; + var RookWhite = Generic.define('chess.RookWhite', { + size: { width: 32, height: 34 } + }, { + markup: ' ' + }); + + var RookBlack = Generic.define('chess.RookBlack', { + size: { width: 32, height: 34 } + }, { + markup: ' ' + }); + + var BishopWhite = Generic.define('chess.BishopWhite', { + size: { width: 38, height: 38 } + }, { + markup: ' ' + }); + + var BishopBlack = Generic.define('chess.BishopBlack', { + size: { width: 38, height: 38 } + }, { + markup: ' ' + }); + + var KnightWhite = Generic.define('chess.KnightWhite', { + size: { width: 38, height: 37 } + }, { + markup: ' ' + }); + + var KnightBlack = Generic.define('chess.KnightBlack', { + size: { width: 38, height: 37 } + }, { + markup: ' ' + }); - var kappa = V.KAPPA; - var cx = kappa * rx; - var cy = kappa * (isPercentageSetter ? (h * t) : t); + var PawnWhite = Generic.define('chess.PawnWhite', { + size: { width: 28, height: 33 } + }, { + markup: '' + }); - // shape variables - var xLeft = x; - var xCenter = x + (w / 2); - var xRight = x + w; + var PawnBlack = Generic.define('chess.PawnBlack', { + size: { width: 28, height: 33 } + }, { + markup: '' + }); - var ySideTop = y + ry; - var yCurveTop = ySideTop - ry; - var ySideBottom = y + h - ry; - var yCurveBottom = y + h; + var chess = ({ + KingWhite: KingWhite, + KingBlack: KingBlack, + QueenWhite: QueenWhite, + QueenBlack: QueenBlack, + RookWhite: RookWhite, + RookBlack: RookBlack, + BishopWhite: BishopWhite, + BishopBlack: BishopBlack, + KnightWhite: KnightWhite, + KnightBlack: KnightBlack, + PawnWhite: PawnWhite, + PawnBlack: PawnBlack + }); - // return calculated shape - var data = [ - 'M', xLeft, ySideTop, - 'L', xLeft, ySideBottom, - 'C', x, (ySideBottom + cy), (xCenter - cx), yCurveBottom, xCenter, yCurveBottom, - 'C', (xCenter + cx), yCurveBottom, xRight, (ySideBottom + cy), xRight, ySideBottom, - 'L', xRight, ySideTop, - 'C', xRight, (ySideTop - cy), (xCenter + cx), yCurveTop, xCenter, yCurveTop, - 'C', (xCenter - cx), yCurveTop, xLeft, (ySideTop - cy), xLeft, ySideTop, - 'Z' - ]; - return { d: data.join(' ') }; - } + var Entity = Element$1.define('erd.Entity', { + size: { width: 150, height: 60 }, + attrs: { + '.outer': { + fill: '#2ECC71', stroke: '#27AE60', 'stroke-width': 2, + points: '100,0 100,60 0,60 0,0' + }, + '.inner': { + fill: '#2ECC71', stroke: '#27AE60', 'stroke-width': 2, + points: '95,5 95,55 5,55 5,5', + display: 'none' + }, + text: { + text: 'Entity', + 'font-family': 'Arial', 'font-size': 14, + 'ref-x': .5, 'ref-y': .5, + 'y-alignment': 'middle', 'text-anchor': 'middle' } } + }, { + markup: '', }); - var foLabelMarkup = { - tagName: 'foreignObject', - selector: 'foreignObject', - attributes: { - 'overflow': 'hidden' - }, - children: [{ - tagName: 'div', - namespaceURI: 'http://www.w3.org/1999/xhtml', - selector: 'label', - style: { - width: '100%', - height: '100%', - position: 'static', - backgroundColor: 'transparent', - textAlign: 'center', - margin: 0, - padding: '0px 5px', - boxSizing: 'border-box', - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - } - }] - }; - - var svgLabelMarkup = { - tagName: 'text', - selector: 'label', - attributes: { - 'text-anchor': 'middle' + var WeakEntity = Entity.define('erd.WeakEntity', { + attrs: { + '.inner': { display: 'auto' }, + text: { text: 'Weak Entity' } } - }; - - var labelMarkup = (env.test('svgforeignobject')) ? foLabelMarkup : svgLabelMarkup; + }); - var TextBlock$1 = Element$1.define('standard.TextBlock', { + var Relationship = Element$1.define('erd.Relationship', { + size: { width: 80, height: 80 }, attrs: { - body: { - refWidth: '100%', - refHeight: '100%', - stroke: '#333333', - fill: '#ffffff', - strokeWidth: 2 + '.outer': { + fill: '#3498DB', stroke: '#2980B9', 'stroke-width': 2, + points: '40,0 80,40 40,80 0,40' }, - foreignObject: { - refWidth: '100%', - refHeight: '100%' + '.inner': { + fill: '#3498DB', stroke: '#2980B9', 'stroke-width': 2, + points: '40,5 75,40 40,75 5,40', + display: 'none' }, - label: { - style: { - fontSize: 14 - } - } - } - }, { - markup: [{ - tagName: 'rect', - selector: 'body' - }, labelMarkup] - }, { - attributes: { text: { - set: function(text, refBBox, node, attrs) { - if (node instanceof HTMLElement) { - node.textContent = text; - } else { - // No foreign object - var style = attrs.style || {}; - var wrapValue = { text: text, width: -5, height: '100%' }; - var wrapAttrs = assign({ textVerticalAnchor: 'middle' }, style); - attributes.textWrap.set.call(this, wrapValue, refBBox, node, wrapAttrs); - return { fill: style.color || null }; - } - }, - position: function(text, refBBox, node) { - // No foreign object - if (node instanceof SVGElement) { return refBBox.center(); } - } + text: 'Relationship', + 'font-family': 'Arial', 'font-size': 12, + 'ref-x': .5, 'ref-y': .5, + 'y-alignment': 'middle', 'text-anchor': 'middle' } } + }, { + markup: '', }); - // LINKS + var IdentifyingRelationship = Relationship.define('erd.IdentifyingRelationship', { + attrs: { + '.inner': { display: 'auto' }, + text: { text: 'Identifying' } + } + }); - var Link$1 = Link.define('standard.Link', { + var Attribute = Element$1.define('erd.Attribute', { + size: { width: 100, height: 50 }, attrs: { - line: { - connection: true, - stroke: '#333333', - strokeWidth: 2, - strokeLinejoin: 'round', - targetMarker: { - 'type': 'path', - 'd': 'M 10 -5 0 0 10 5 z' - } + 'ellipse': { + transform: 'translate(50, 25)' }, - wrapper: { - connection: true, - strokeWidth: 10, - strokeLinejoin: 'round' + '.outer': { + stroke: '#D35400', 'stroke-width': 2, + cx: 0, cy: 0, rx: 50, ry: 25, + fill: '#E67E22' + }, + '.inner': { + stroke: '#D35400', 'stroke-width': 2, + cx: 0, cy: 0, rx: 45, ry: 20, + fill: '#E67E22', display: 'none' + }, + text: { + 'font-family': 'Arial', 'font-size': 14, + 'ref-x': .5, 'ref-y': .5, + 'y-alignment': 'middle', 'text-anchor': 'middle' } } }, { - markup: [{ - tagName: 'path', - selector: 'wrapper', - attributes: { - 'fill': 'none', - 'cursor': 'pointer', - 'stroke': 'transparent', - 'stroke-linecap': 'round' - } - }, { - tagName: 'path', - selector: 'line', - attributes: { - 'fill': 'none', - 'pointer-events': 'none' - } - }] + markup: '', }); - var DoubleLink = Link.define('standard.DoubleLink', { + var Multivalued = Attribute.define('erd.Multivalued', { attrs: { - line: { - connection: true, - stroke: '#DDDDDD', - strokeWidth: 4, - strokeLinejoin: 'round', - targetMarker: { - type: 'path', - stroke: '#000000', - d: 'M 10 -3 10 -10 -2 0 10 10 10 3' - } - }, - outline: { - connection: true, - stroke: '#000000', - strokeWidth: 6, - strokeLinejoin: 'round' - } + '.inner': { display: 'block' }, + text: { text: 'multivalued' } } - }, { - markup: [{ - tagName: 'path', - selector: 'outline', - attributes: { - 'fill': 'none' - } - }, { - tagName: 'path', - selector: 'line', - attributes: { - 'fill': 'none' - } - }] }); - var ShadowLink = Link.define('standard.ShadowLink', { + var Derived = Attribute.define('erd.Derived', { attrs: { - line: { - connection: true, - stroke: '#FF0000', - strokeWidth: 20, - strokeLinejoin: 'round', - targetMarker: { - 'type': 'path', - 'stroke': 'none', - 'd': 'M 0 -10 -10 0 0 10 z' - }, - sourceMarker: { - 'type': 'path', - 'stroke': 'none', - 'd': 'M -10 -10 0 0 -10 10 0 10 0 -10 z' - } - }, - shadow: { - connection: true, - refX: 3, - refY: 6, - stroke: '#000000', - strokeOpacity: 0.2, - strokeWidth: 20, - strokeLinejoin: 'round', - targetMarker: { - 'type': 'path', - 'd': 'M 0 -10 -10 0 0 10 z', - 'stroke': 'none' - }, - sourceMarker: { - 'type': 'path', - 'stroke': 'none', - 'd': 'M -10 -10 0 0 -10 10 0 10 0 -10 z' - } - } + '.outer': { 'stroke-dasharray': '3,5' }, + text: { text: 'derived' } } - }, { - markup: [{ - tagName: 'path', - selector: 'shadow', - attributes: { - 'fill': 'none' - } - }, { - tagName: 'path', - selector: 'line', - attributes: { - 'fill': 'none' - } - }] }); - var standard = ({ - Rectangle: Rectangle, - Circle: Circle$1, - Ellipse: Ellipse$2, - Path: Path$2, - Polygon: Polygon$2, - Polyline: Polyline$2, - Image: Image$1, - BorderedImage: BorderedImage, - EmbeddedImage: EmbeddedImage, - InscribedImage: InscribedImage, - HeaderedRectangle: HeaderedRectangle, - Cylinder: Cylinder, - TextBlock: TextBlock$1, - Link: Link$1, - DoubleLink: DoubleLink, - ShadowLink: ShadowLink + var Key = Attribute.define('erd.Key', { + attrs: { + ellipse: { 'stroke-width': 4 }, + text: { text: 'key', 'font-weight': '800', 'text-decoration': 'underline' } + } }); - /** - * @deprecated use the port api instead - */ - var Model = Generic.define('devs.Model', { - inPorts: [], - outPorts: [], - size: { - width: 80, - height: 80 - }, + var Normal = Attribute.define('erd.Normal', { + attrs: { text: { text: 'Normal' }} + }); + + var ISA = Element$1.define('erd.ISA', { + type: 'erd.ISA', + size: { width: 100, height: 50 }, attrs: { - '.': { - magnet: false - }, - '.label': { - text: 'Model', - 'ref-x': .5, - 'ref-y': 10, - 'font-size': 18, - 'text-anchor': 'middle', - fill: '#000' + polygon: { + points: '0,0 50,50 100,0', + fill: '#F1C40F', stroke: '#F39C12', 'stroke-width': 2 }, - '.body': { - 'ref-width': '100%', - 'ref-height': '100%', - stroke: '#000' - } - }, - ports: { - groups: { - 'in': { - position: { - name: 'left' - }, - attrs: { - '.port-label': { - fill: '#000' - }, - '.port-body': { - fill: '#fff', - stroke: '#000', - r: 10, - magnet: true - } - }, - label: { - position: { - name: 'left', - args: { - y: 10 - } - } - } - }, - 'out': { - position: { - name: 'right' - }, - attrs: { - '.port-label': { - fill: '#000' - }, - '.port-body': { - fill: '#fff', - stroke: '#000', - r: 10, - magnet: true - } - }, - label: { - position: { - name: 'right', - args: { - y: 10 - } - } - } - } + text: { + text: 'ISA', 'font-size': 18, + 'ref-x': .5, 'ref-y': .3, + 'y-alignment': 'middle', 'text-anchor': 'middle' } } }, { - markup: '', - portMarkup: '', - portLabelMarkup: '', + markup: '', + }); - initialize: function() { + var Line$1 = Link.define('erd.Line', {}, { + cardinality: function(value) { + this.set('labels', [{ position: -20, attrs: { text: { dy: -8, text: value }}}]); + } + }); - Generic.prototype.initialize.apply(this, arguments); + var erd = ({ + Entity: Entity, + WeakEntity: WeakEntity, + Relationship: Relationship, + IdentifyingRelationship: IdentifyingRelationship, + Attribute: Attribute, + Multivalued: Multivalued, + Derived: Derived, + Key: Key, + Normal: Normal, + ISA: ISA, + Line: Line$1 + }); - this.on('change:inPorts change:outPorts', this.updatePortItems, this); - this.updatePortItems(); - }, + var State = Circle.define('fsa.State', { + attrs: { + circle: { 'stroke-width': 3 }, + text: { 'font-weight': '800' } + } + }); - updatePortItems: function(model, changed, opt) { + var StartState = Element$1.define('fsa.StartState', { + size: { width: 20, height: 20 }, + attrs: { + circle: { + transform: 'translate(10, 10)', + r: 10, + fill: '#000000' + } + } + }, { + markup: '', + }); - // Make sure all ports are unique. - var inPorts = uniq(this.get('inPorts')); - var outPorts = difference(uniq(this.get('outPorts')), inPorts); + var EndState = Element$1.define('fsa.EndState', { + size: { width: 20, height: 20 }, + attrs: { + '.outer': { + transform: 'translate(10, 10)', + r: 10, + fill: '#ffffff', + stroke: '#000000' + }, - var inPortItems = this.createPortItems('in', inPorts); - var outPortItems = this.createPortItems('out', outPorts); + '.inner': { + transform: 'translate(10, 10)', + r: 6, + fill: '#000000' + } + } + }, { + markup: '', + }); - this.prop('ports/items', inPortItems.concat(outPortItems), assign({ rewrite: true }, opt)); - }, + var Arrow = Link.define('fsa.Arrow', { + attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }}, + smooth: true + }); - createPortItem: function(group, port) { + var fsa = ({ + State: State, + StartState: StartState, + EndState: EndState, + Arrow: Arrow + }); - return { - id: port, - group: group, - attrs: { - '.port-label': { - text: port - } - } - }; - }, + var Member = Element$1.define('org.Member', { + size: { width: 180, height: 70 }, + attrs: { + rect: { width: 170, height: 60 }, - createPortItems: function(group, ports) { + '.card': { + fill: '#FFFFFF', stroke: '#000000', 'stroke-width': 2, + 'pointer-events': 'visiblePainted', rx: 10, ry: 10 + }, - return toArray(ports).map(this.createPortItem.bind(this, group)); - }, + image: { + width: 48, height: 48, + ref: '.card', 'ref-x': 10, 'ref-y': 5 + }, - _addGroupPort: function(port, group, opt) { + '.rank': { + 'text-decoration': 'underline', + ref: '.card', 'ref-x': 0.9, 'ref-y': 0.2, + 'font-family': 'Courier New', 'font-size': 14, + 'text-anchor': 'end' + }, - var ports = this.get(group); - return this.set(group, Array.isArray(ports) ? ports.concat(port) : [port], opt); - }, + '.name': { + 'font-weight': '800', + ref: '.card', 'ref-x': 0.9, 'ref-y': 0.6, + 'font-family': 'Courier New', 'font-size': 14, + 'text-anchor': 'end' + } + } + }, { + markup: '', + }); - addOutPort: function(port, opt) { + var Arrow$1 = Link.define('org.Arrow', { + source: { selector: '.card' }, target: { selector: '.card' }, + attrs: { '.connection': { stroke: '#585858', 'stroke-width': 3 }}, + z: -1 + }); - return this._addGroupPort(port, 'outPorts', opt); - }, + var org = ({ + Member: Member, + Arrow: Arrow$1 + }); - addInPort: function(port, opt) { + var Place = Generic.define('pn.Place', { + size: { width: 50, height: 50 }, + attrs: { + '.root': { + r: 25, + fill: '#ffffff', + stroke: '#000000', + transform: 'translate(25, 25)' + }, + '.label': { + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-y': -20, + ref: '.root', + fill: '#000000', + 'font-size': 12 + }, + '.tokens > circle': { + fill: '#000000', + r: 5 + }, + '.tokens.one > circle': { transform: 'translate(25, 25)' }, - return this._addGroupPort(port, 'inPorts', opt); - }, + '.tokens.two > circle:nth-child(1)': { transform: 'translate(19, 25)' }, + '.tokens.two > circle:nth-child(2)': { transform: 'translate(31, 25)' }, - _removeGroupPort: function(port, group, opt) { + '.tokens.three > circle:nth-child(1)': { transform: 'translate(18, 29)' }, + '.tokens.three > circle:nth-child(2)': { transform: 'translate(25, 19)' }, + '.tokens.three > circle:nth-child(3)': { transform: 'translate(32, 29)' }, - return this.set(group, without(this.get(group), port), opt); - }, + '.tokens.alot > text': { + transform: 'translate(25, 18)', + 'text-anchor': 'middle', + fill: '#000000' + } + } + }, { + markup: '', + }); - removeOutPort: function(port, opt) { + var PlaceView = ElementView.extend({ - return this._removeGroupPort(port, 'outPorts', opt); - }, + presentationAttributes: ElementView.addPresentationAttributes({ + tokens: ['TOKENS'] + }), - removeInPort: function(port, opt) { + initFlag: ElementView.prototype.initFlag.concat(['TOKENS']), - return this._removeGroupPort(port, 'inPorts', opt); + confirmUpdate: function() { + var ref; + + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + var flags = (ref = ElementView.prototype.confirmUpdate).call.apply(ref, [ this ].concat( args )); + if (this.hasFlag(flags, 'TOKENS')) { + this.renderTokens(); + this.update(); + flags = this.removeFlag(flags, 'TOKENS'); + } + return flags; }, - _changeGroup: function(group, properties, opt) { + renderTokens: function() { - return this.prop('ports/groups/' + group, isObject$1(properties) ? properties : {}, opt); - }, + var vTokens = this.vel.findOne('.tokens').empty(); + ['one', 'two', 'three', 'alot'].forEach(function(className) { + vTokens.removeClass(className); + }); - changeInGroup: function(properties, opt) { + var tokens = this.model.get('tokens'); + if (!tokens) { return; } - return this._changeGroup('in', properties, opt); - }, + switch (tokens) { - changeOutGroup: function(properties, opt) { + case 1: + vTokens.addClass('one'); + vTokens.append(V('circle')); + break; - return this._changeGroup('out', properties, opt); - } - }); + case 2: + vTokens.addClass('two'); + vTokens.append([V('circle'), V('circle')]); + break; - var Atomic = Model.define('devs.Atomic', { - size: { - width: 80, - height: 80 - }, - attrs: { - '.label': { - text: 'Atomic' + case 3: + vTokens.addClass('three'); + vTokens.append([V('circle'), V('circle'), V('circle')]); + break; + + default: + vTokens.addClass('alot'); + vTokens.append(V('text').text(tokens + '')); + break; } } }); - var Coupled = Model.define('devs.Coupled', { - size: { - width: 200, - height: 300 - }, + var Transition = Generic.define('pn.Transition', { + size: { width: 12, height: 50 }, attrs: { + 'rect': { + width: 12, + height: 50, + fill: '#000000', + stroke: '#000000' + }, '.label': { - text: 'Coupled' + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-y': -20, + ref: 'rect', + fill: '#000000', + 'font-size': 12 } } + }, { + markup: '', }); - var Link$2 = Link.define('devs.Link', { - attrs: { - '.connection': { - 'stroke-width': 2 - } - } + var Link$3 = Link.define('pn.Link', { + attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }} }); - var devs = ({ - Model: Model, - Atomic: Atomic, - Coupled: Coupled, - Link: Link$2 + var pn = ({ + Place: Place, + PlaceView: PlaceView, + Transition: Transition, + Link: Link$3 }); - var Gate = Generic.define('logic.Gate', { - size: { width: 80, height: 40 }, + var Class = Generic.define('uml.Class', { attrs: { - '.': { magnet: false }, - '.body': { width: 100, height: 50 }, - circle: { r: 7, stroke: 'black', fill: 'transparent', 'stroke-width': 2 } - } - }, { - operation: function() { - return true; - } - }); + rect: { 'width': 200 }, - var IO = Gate.define('logic.IO', { - size: { width: 60, height: 30 }, - attrs: { - '.body': { fill: 'white', stroke: 'black', 'stroke-width': 2 }, - '.wire': { ref: '.body', 'ref-y': .5, stroke: 'black' }, - text: { - fill: 'black', - ref: '.body', 'ref-x': .5, 'ref-y': .5, 'y-alignment': 'middle', + '.uml-class-name-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#3498db' }, + '.uml-class-attrs-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#2980b9' }, + '.uml-class-methods-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#2980b9' }, + + '.uml-class-name-text': { + 'ref': '.uml-class-name-rect', + 'ref-y': .5, + 'ref-x': .5, 'text-anchor': 'middle', + 'y-alignment': 'middle', 'font-weight': 'bold', - 'font-variant': 'small-caps', - 'text-transform': 'capitalize', - 'font-size': '14px' + 'fill': 'black', + 'font-size': 12, + 'font-family': 'Times New Roman' + }, + '.uml-class-attrs-text': { + 'ref': '.uml-class-attrs-rect', 'ref-y': 5, 'ref-x': 5, + 'fill': 'black', 'font-size': 12, 'font-family': 'Times New Roman' + }, + '.uml-class-methods-text': { + 'ref': '.uml-class-methods-rect', 'ref-y': 5, 'ref-x': 5, + 'fill': 'black', 'font-size': 12, 'font-family': 'Times New Roman' } - } + }, + + name: [], + attributes: [], + methods: [] }, { - markup: '', - }); + markup: [ + '', + '', + '', + '', + '', + '' + ].join(''), - var Input = IO.define('logic.Input', { - attrs: { - '.wire': { 'ref-dx': 0, d: 'M 0 0 L 23 0' }, - circle: { ref: '.body', 'ref-dx': 30, 'ref-y': 0.5, magnet: true, 'class': 'output', port: 'out' }, - text: { text: 'input' } - } - }); + initialize: function() { - var Output = IO.define('logic.Output', { - attrs: { - '.wire': { 'ref-x': 0, d: 'M 0 0 L -23 0' }, - circle: { ref: '.body', 'ref-x': -30, 'ref-y': 0.5, magnet: 'passive', 'class': 'input', port: 'in' }, - text: { text: 'output' } + this.on('change:name change:attributes change:methods', function() { + this.updateRectangles(); + this.trigger('uml-update'); + }, this); + + this.updateRectangles(); + + Generic.prototype.initialize.apply(this, arguments); + }, + + getClassName: function() { + return this.get('name'); + }, + + updateRectangles: function() { + + var attrs = this.get('attrs'); + + var rects = [ + { type: 'name', text: this.getClassName() }, + { type: 'attrs', text: this.get('attributes') }, + { type: 'methods', text: this.get('methods') } + ]; + + var offsetY = 0; + + rects.forEach(function(rect) { + + var lines = Array.isArray(rect.text) ? rect.text : [rect.text]; + var rectHeight = lines.length * 20 + 20; + + attrs['.uml-class-' + rect.type + '-text'].text = lines.join('\n'); + attrs['.uml-class-' + rect.type + '-rect'].height = rectHeight; + attrs['.uml-class-' + rect.type + '-rect'].transform = 'translate(0,' + offsetY + ')'; + + offsetY += rectHeight; + }); } + }); - var Gate11 = Gate.define('logic.Gate11', { - attrs: { - '.input': { ref: '.body', 'ref-x': -2, 'ref-y': 0.5, magnet: 'passive', port: 'in' }, - '.output': { ref: '.body', 'ref-dx': 2, 'ref-y': 0.5, magnet: true, port: 'out' } + var ClassView = ElementView.extend({ + + initialize: function() { + + ElementView.prototype.initialize.apply(this, arguments); + + this.listenTo(this.model, 'uml-update', function() { + this.update(); + this.resize(); + }); } - }, { - markup: '', }); - var Gate21 = Gate.define('logic.Gate21', { + var Abstract = Class.define('uml.Abstract', { attrs: { - '.input1': { ref: '.body', 'ref-x': -2, 'ref-y': 0.3, magnet: 'passive', port: 'in1' }, - '.input2': { ref: '.body', 'ref-x': -2, 'ref-y': 0.7, magnet: 'passive', port: 'in2' }, - '.output': { ref: '.body', 'ref-dx': 2, 'ref-y': 0.5, magnet: true, port: 'out' } + '.uml-class-name-rect': { fill: '#e74c3c' }, + '.uml-class-attrs-rect': { fill: '#c0392b' }, + '.uml-class-methods-rect': { fill: '#c0392b' } } }, { - markup: '', - }); - var Repeater = Gate11.define('logic.Repeater', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzIuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICA8L2c+Cjwvc3ZnPgo=' }} - }, { - operation: function(input) { - return input; + getClassName: function() { + return ['<>', this.get('name')]; } - }); - var Not = Gate11.define('logic.Not', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzkuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI2NzEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzYiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA4MCwyNSBBIDQsNCAwIDEgMSA3MiwyNSBBIDQsNCAwIDEgMSA4MCwyNSB6IgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEsMCkiIC8+CiAgPC9nPgo8L3N2Zz4K' }} - }, { - operation: function(input) { - return !input; - } }); + var AbstractView = ClassView; - var Or = Gate21.define('logic.Or', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik9SIEFOU0kuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTAgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjcxNCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iMSA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMC41IDogMC4zMzMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODA2IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODE5IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjM3Mi4wNDcyNCA6IDM1MC43ODczOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI3NDQuMDk0NDggOiA1MjYuMTgxMDkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzc3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49Ijc1IDogNDAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTUwIDogNjAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDYwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTMyNzUiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNTAgOiAzMy4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTAwIDogNTAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTU1MzMiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjU1NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDE2LjY2NjY2NyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDI1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAyNSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iNCIKICAgICBpbmtzY2FwZTpjeD0iMTEzLjAwMDM5IgogICAgIGlua3NjYXBlOmN5PSIxMi44OTM3MzEiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImcyNTYwIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1wb2ludHM9InRydWUiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAwMDAiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMzk5IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg3NCIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMzciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii00IgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNzAsMjUgYyAyMCwwIDI1LDAgMjUsMCIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMSwxNSA1LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzIsMzUgNSwzNSIKICAgICAgIGlkPSJwYXRoMzk0NCIgLz4KICAgIDxnCiAgICAgICBpZD0iZzI1NjAiCiAgICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2LjUsLTM5LjUpIj4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=' }} - }, { - operation: function(input1, input2) { - return input1 || input2; + var Interface = Class.define('uml.Interface', { + attrs: { + '.uml-class-name-rect': { fill: '#f1c40f' }, + '.uml-class-attrs-rect': { fill: '#f39c12' }, + '.uml-class-methods-rect': { fill: '#f39c12' } } - }); - - var And = Gate21.define('logic.And', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IkFORCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI4IgogICAgIGlua3NjYXBlOmN4PSI1Ni42OTgzNDgiCiAgICAgaW5rc2NhcGU6Y3k9IjI1LjMyNjg5OSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTptZWRpdW07Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWluZGVudDowO3RleHQtYWxpZ246c3RhcnQ7dGV4dC1kZWNvcmF0aW9uOm5vbmU7bGluZS1oZWlnaHQ6bm9ybWFsO2xldHRlci1zcGFjaW5nOm5vcm1hbDt3b3JkLXNwYWNpbmc6bm9ybWFsO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlyZWN0aW9uOmx0cjtibG9jay1wcm9ncmVzc2lvbjp0Yjt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDozO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGU7Zm9udC1mYW1pbHk6Qml0c3RyZWFtIFZlcmEgU2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkJpdHN0cmVhbSBWZXJhIFNhbnMiCiAgICAgICBkPSJNIDMwLDUgTCAzMCw2LjQyODU3MTQgTCAzMCw0My41NzE0MjkgTCAzMCw0NSBMIDMxLjQyODU3MSw0NSBMIDUwLjQ3NjE5LDQ1IEMgNjEuNzQ0MDk4LDQ1IDcwLjQ3NjE5LDM1Ljk5OTk1NSA3MC40NzYxOSwyNSBDIDcwLjQ3NjE5LDE0LjAwMDA0NSA2MS43NDQwOTksNS4wMDAwMDAyIDUwLjQ3NjE5LDUgQyA1MC40NzYxOSw1IDUwLjQ3NjE5LDUgMzEuNDI4NTcxLDUgTCAzMCw1IHogTSAzMi44NTcxNDMsNy44NTcxNDI5IEMgNDAuODM0MjY0LDcuODU3MTQyOSA0NS45MTgzNjgsNy44NTcxNDI5IDQ4LjA5NTIzOCw3Ljg1NzE0MjkgQyA0OS4yODU3MTQsNy44NTcxNDI5IDQ5Ljg4MDk1Miw3Ljg1NzE0MjkgNTAuMTc4NTcxLDcuODU3MTQyOSBDIDUwLjMyNzM4MSw3Ljg1NzE0MjkgNTAuNDA5MjI3LDcuODU3MTQyOSA1MC40NDY0MjksNy44NTcxNDI5IEMgNTAuNDY1MDI5LDcuODU3MTQyOSA1MC40NzE1NDMsNy44NTcxNDI5IDUwLjQ3NjE5LDcuODU3MTQyOSBDIDYwLjIzNjg1Myw3Ljg1NzE0MyA2Ny4xNDI4NTcsMTUuNDk3MDk4IDY3LjE0Mjg1NywyNSBDIDY3LjE0Mjg1NywzNC41MDI5MDIgNTkuNzYwNjYyLDQyLjE0Mjg1NyA1MCw0Mi4xNDI4NTcgTCAzMi44NTcxNDMsNDIuMTQyODU3IEwgMzIuODU3MTQzLDcuODU3MTQyOSB6IgogICAgICAgaWQ9InBhdGgyODg0IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NzY2NjY3Nzc3NzY2NjIiAvPgogIDwvZz4KPC9zdmc+Cg==' }} - }, { - operation: function(input1, input2) { - return input1 && input2; + getClassName: function() { + return ['<>', this.get('name')]; } }); + var InterfaceView = ClassView; - var Nor = Gate21.define('logic.Nor', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjEiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjY3NzY0NCIKICAgICBpbmtzY2FwZTpjeT0iMjIuMTAyMzQ0IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjM3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItNCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc5LDI1IEMgOTksMjUgOTUsMjUgOTUsMjUiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBpZD0icGF0aDI2MDQiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI3NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjI1IgogICAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgICAgc29kaXBvZGk6cnk9IjQiCiAgICAgICAgIGQ9Ik0gNzksMjUgQSA0LDQgMCAxIDEgNzEsMjUgQSA0LDQgMCAxIDEgNzksMjUgeiIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI2LjUsMzkuNSkiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K' }} - }, { - operation: function(input1, input2) { - return !(input1 || input2); - } + var Generalization = Link.define('uml.Generalization', { + attrs: { '.marker-target': { d: 'M 20 0 L 0 10 L 20 20 z', fill: 'white' }} }); - var Nand = Gate21.define('logic.Nand', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5BTkQgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTYiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjI4MzMwNyIKICAgICBpbmtzY2FwZTpjeT0iMTYuNDQyODQzIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzksMjUgQyA5MS44LDI1IDk1LDI1IDk1LDI1IgogICAgICAgaWQ9InBhdGgzMDU5IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMxLDE1IDUsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMiwzNSA1LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmb250LXNpemU6bWVkaXVtO2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1pbmRlbnQ6MDt0ZXh0LWFsaWduOnN0YXJ0O3RleHQtZGVjb3JhdGlvbjpub25lO2xpbmUtaGVpZ2h0Om5vcm1hbDtsZXR0ZXItc3BhY2luZzpub3JtYWw7d29yZC1zcGFjaW5nOm5vcm1hbDt0ZXh0LXRyYW5zZm9ybTpub25lO2RpcmVjdGlvbjpsdHI7YmxvY2stcHJvZ3Jlc3Npb246dGI7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MzttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO2ZvbnQtZmFtaWx5OkJpdHN0cmVhbSBWZXJhIFNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpCaXRzdHJlYW0gVmVyYSBTYW5zIgogICAgICAgZD0iTSAzMCw1IEwgMzAsNi40Mjg1NzE0IEwgMzAsNDMuNTcxNDI5IEwgMzAsNDUgTCAzMS40Mjg1NzEsNDUgTCA1MC40NzYxOSw0NSBDIDYxLjc0NDA5OCw0NSA3MC40NzYxOSwzNS45OTk5NTUgNzAuNDc2MTksMjUgQyA3MC40NzYxOSwxNC4wMDAwNDUgNjEuNzQ0MDk5LDUuMDAwMDAwMiA1MC40NzYxOSw1IEMgNTAuNDc2MTksNSA1MC40NzYxOSw1IDMxLjQyODU3MSw1IEwgMzAsNSB6IE0gMzIuODU3MTQzLDcuODU3MTQyOSBDIDQwLjgzNDI2NCw3Ljg1NzE0MjkgNDUuOTE4MzY4LDcuODU3MTQyOSA0OC4wOTUyMzgsNy44NTcxNDI5IEMgNDkuMjg1NzE0LDcuODU3MTQyOSA0OS44ODA5NTIsNy44NTcxNDI5IDUwLjE3ODU3MSw3Ljg1NzE0MjkgQyA1MC4zMjczODEsNy44NTcxNDI5IDUwLjQwOTIyNyw3Ljg1NzE0MjkgNTAuNDQ2NDI5LDcuODU3MTQyOSBDIDUwLjQ2NTAyOSw3Ljg1NzE0MjkgNTAuNDcxNTQzLDcuODU3MTQyOSA1MC40NzYxOSw3Ljg1NzE0MjkgQyA2MC4yMzY4NTMsNy44NTcxNDMgNjcuMTQyODU3LDE1LjQ5NzA5OCA2Ny4xNDI4NTcsMjUgQyA2Ny4xNDI4NTcsMzQuNTAyOTAyIDU5Ljc2MDY2Miw0Mi4xNDI4NTcgNTAsNDIuMTQyODU3IEwgMzIuODU3MTQzLDQyLjE0Mjg1NyBMIDMyLjg1NzE0Myw3Ljg1NzE0MjkgeiIKICAgICAgIGlkPSJwYXRoMjg4NCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2Njc2NjY2Nzc3Nzc2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDQwMDgiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg==' }} - }, { - operation: function(input1, input2) { - return !(input1 && input2); + var Implementation = Link.define('uml.Implementation', { + attrs: { + '.marker-target': { d: 'M 20 0 L 0 10 L 20 20 z', fill: 'white' }, + '.connection': { 'stroke-dasharray': '3,3' } } }); - var Xor = Gate21.define('logic.Xor', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhPUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iMjUuOTM4MTE2IgogICAgIGlua3NjYXBlOmN5PSIxNy4yMzAwNSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzAuMzg1NzE3LDE1IEwgNC45OTk5OTk4LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5NzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEuMzYyMDkxLDM1IEwgNC45OTk5OTk4LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPGcKICAgICAgIGlkPSJnMjU2MCIKICAgICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYuNSwtMzkuNSkiPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDM1MTYiCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi4yNSw4MS41MDAwMDUgQyAtMy44NDczNzQsODQuMTQ0NDA1IC00LjUsODQuNTAwMDA1IC00LjUsODQuNTAwMDA1IEwgLTguMTU2MjUsODQuNTAwMDA1IEwgLTYuMTU2MjUsODIuMDYyNTA1IEMgLTYuMTU2MjUsODIuMDYyNTA1IC0wLjUsNzUuMDYyNDUxIC0wLjUsNjQuNSBDIC0wLjUsNTMuOTM3NTQ5IC02LjE1NjI1LDQ2LjkzNzUgLTYuMTU2MjUsNDYuOTM3NSBMIC04LjE1NjI1LDQ0LjUgTCAtNC41LDQ0LjUgQyAtMy43MTg3NSw0NS40Mzc1IC0zLjA3ODEyNSw0Ni4xNTYyNSAtMi4yODEyNSw0Ny41IEMgLTAuNDA4NTMxLDUwLjU5OTgxNSAyLjUsNTYuNTI2NjQ2IDIuNSw2NC41IEMgMi41LDcyLjQ1MDY1IC0wLjM5NjY5Nyw3OC4zNzk0MjUgLTIuMjUsODEuNTAwMDA1IHoiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY3NjY2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K' }} - }, { - operation: function(input1, input2) { - return (!input1 || input2) && (input1 || !input2); - } + var Aggregation = Link.define('uml.Aggregation', { + attrs: { '.marker-target': { d: 'M 40 10 L 20 20 L 0 10 L 20 0 z', fill: 'white' }} }); - var Xnor = Gate21.define('logic.Xnor', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhOT1IgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNTU3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTYuNjY2NjY3IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDI1IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0IgogICAgIGlua3NjYXBlOmN4PSI5NS43MjM2NiIKICAgICBpbmtzY2FwZTpjeT0iLTI2Ljc3NTAyMyIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyLjAwMDAwMDI0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc4LjMzMzMzMiwyNSBDIDkxLjY2NjY2NiwyNSA5NSwyNSA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMC4zODU3MTcsMTUgTCA0Ljk5OTk5OTgsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk3NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMS4zNjIwOTEsMzUgTCA0Ljk5OTk5OTgsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMzUxNiIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjI1LDgxLjUwMDAwNSBDIC0zLjg0NzM3NCw4NC4xNDQ0MDUgLTQuNSw4NC41MDAwMDUgLTQuNSw4NC41MDAwMDUgTCAtOC4xNTYyNSw4NC41MDAwMDUgTCAtNi4xNTYyNSw4Mi4wNjI1MDUgQyAtNi4xNTYyNSw4Mi4wNjI1MDUgLTAuNSw3NS4wNjI0NTEgLTAuNSw2NC41IEMgLTAuNSw1My45Mzc1NDkgLTYuMTU2MjUsNDYuOTM3NSAtNi4xNTYyNSw0Ni45Mzc1IEwgLTguMTU2MjUsNDQuNSBMIC00LjUsNDQuNSBDIC0zLjcxODc1LDQ1LjQzNzUgLTMuMDc4MTI1LDQ2LjE1NjI1IC0yLjI4MTI1LDQ3LjUgQyAtMC40MDg1MzEsNTAuNTk5ODE1IDIuNSw1Ni41MjY2NDYgMi41LDY0LjUgQyAyLjUsNzIuNDUwNjUgLTAuMzk2Njk3LDc4LjM3OTQyNSAtMi4yNSw4MS41MDAwMDUgeiIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Njc2NjY2NzYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDM1NTEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg==' }} - }, { - operation: function(input1, input2) { - return (!input1 || !input2) && (input1 || input2); - } + var Composition = Link.define('uml.Composition', { + attrs: { '.marker-target': { d: 'M 40 10 L 20 20 L 0 10 L 20 0 z', fill: 'black' }} }); - var Wire = Link.define('logic.Wire', { + var Association = Link.define('uml.Association'); + + // Statechart + + var State$1 = Generic.define('uml.State', { attrs: { - '.connection': { 'stroke-width': 2 }, - '.marker-vertex': { r: 7 } + '.uml-state-body': { + 'width': 200, 'height': 200, 'rx': 10, 'ry': 10, + 'fill': '#ecf0f1', 'stroke': '#bdc3c7', 'stroke-width': 3 + }, + '.uml-state-separator': { + 'stroke': '#bdc3c7', 'stroke-width': 2 + }, + '.uml-state-name': { + 'ref': '.uml-state-body', 'ref-x': .5, 'ref-y': 5, 'text-anchor': 'middle', + 'fill': '#000000', 'font-family': 'Courier New', 'font-size': 14 + }, + '.uml-state-events': { + 'ref': '.uml-state-separator', 'ref-x': 5, 'ref-y': 5, + 'fill': '#000000', 'font-family': 'Courier New', 'font-size': 14 + } }, - router: { name: 'orthogonal' }, - connector: { name: 'rounded', args: { radius: 10 }} + name: 'State', + events: [] + }, { - arrowheadMarkup: [ - '', - '', + markup: [ + '', + '', + '', + '', + '', + '', + '', '' ].join(''), - vertexMarkup: [ - '', - '', - '', - '', - '', - 'Remove vertex.', - '', - '', - '' - ].join('') + initialize: function() { + + this.on({ + 'change:name': this.updateName, + 'change:events': this.updateEvents, + 'change:size': this.updatePath + }, this); + + this.updateName(); + this.updateEvents(); + this.updatePath(); + + Generic.prototype.initialize.apply(this, arguments); + }, + + updateName: function() { + + this.attr('.uml-state-name/text', this.get('name')); + }, + + updateEvents: function() { + + this.attr('.uml-state-events/text', this.get('events').join('\n')); + }, + + updatePath: function() { + + var d = 'M 0 20 L ' + this.get('size').width + ' 20'; + + // We are using `silent: true` here because updatePath() is meant to be called + // on resize and there's no need to to update the element twice (`change:size` + // triggers also an update). + this.attr('.uml-state-separator/d', d, { silent: true }); + } }); - var logic = ({ - Gate: Gate, - IO: IO, - Input: Input, - Output: Output, - Gate11: Gate11, - Gate21: Gate21, - Repeater: Repeater, - Not: Not, - Or: Or, - And: And, - Nor: Nor, - Nand: Nand, - Xor: Xor, - Xnor: Xnor, - Wire: Wire + var StartState$1 = Circle.define('uml.StartState', { + type: 'uml.StartState', + attrs: { circle: { 'fill': '#34495e', 'stroke': '#2c3e50', 'stroke-width': 2, 'rx': 1 }} }); - var KingWhite = Generic.define('chess.KingWhite', { - size: { width: 42, height: 38 } + var EndState$1 = Generic.define('uml.EndState', { + size: { width: 20, height: 20 }, + attrs: { + 'circle.outer': { + transform: 'translate(10, 10)', + r: 10, + fill: '#ffffff', + stroke: '#2c3e50' + }, + + 'circle.inner': { + transform: 'translate(10, 10)', + r: 6, + fill: '#34495e' + } + } }, { - markup: ' ' + markup: '', }); - var KingBlack = Generic.define('chess.KingBlack', { - size: { width: 42, height: 38 } - }, { - markup: ' ' + var Transition$1 = Link.define('uml.Transition', { + attrs: { + '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z', fill: '#34495e', stroke: '#2c3e50' }, + '.connection': { stroke: '#2c3e50' } + } }); - var QueenWhite = Generic.define('chess.QueenWhite', { - size: { width: 42, height: 38 } - }, { - markup: ' ' + var uml = ({ + Class: Class, + ClassView: ClassView, + Abstract: Abstract, + AbstractView: AbstractView, + Interface: Interface, + InterfaceView: InterfaceView, + Generalization: Generalization, + Implementation: Implementation, + Aggregation: Aggregation, + Composition: Composition, + Association: Association, + State: State$1, + StartState: StartState$1, + EndState: EndState$1, + Transition: Transition$1 }); - var QueenBlack = Generic.define('chess.QueenBlack', { - size: { width: 42, height: 38 } - }, { - markup: ' ' - }); - var RookWhite = Generic.define('chess.RookWhite', { - size: { width: 32, height: 34 } - }, { - markup: ' ' - }); - var RookBlack = Generic.define('chess.RookBlack', { - size: { width: 32, height: 34 } - }, { - markup: ' ' + var index$3 = ({ + basic: basic, + standard: standard, + devs: devs, + logic: logic, + chess: chess, + erd: erd, + fsa: fsa, + org: org, + pn: pn, + uml: uml }); - var BishopWhite = Generic.define('chess.BishopWhite', { - size: { width: 38, height: 38 } - }, { - markup: ' ' - }); + function abs2rel(value, max) { - var BishopBlack = Generic.define('chess.BishopBlack', { - size: { width: 38, height: 38 } - }, { - markup: ' ' - }); + if (max === 0) { return '0%'; } + return Math.round(value / max * 100) + '%'; + } - var KnightWhite = Generic.define('chess.KnightWhite', { - size: { width: 38, height: 37 } - }, { - markup: ' ' - }); + function pin(relative) { - var KnightBlack = Generic.define('chess.KnightBlack', { - size: { width: 38, height: 37 } - }, { - markup: ' ' - }); + return function(end, view, magnet, coords) { + var fn = (view.isNodeConnection(magnet)) ? pinnedLinkEnd : pinnedElementEnd; + return fn(relative, end, view, magnet, coords); + }; + } - var PawnWhite = Generic.define('chess.PawnWhite', { - size: { width: 28, height: 33 } - }, { - markup: '' - }); + function pinnedElementEnd(relative, end, view, magnet, coords) { - var PawnBlack = Generic.define('chess.PawnBlack', { - size: { width: 28, height: 33 } - }, { - markup: '' - }); + var angle = view.model.angle(); + var bbox = view.getNodeUnrotatedBBox(magnet); + var origin = view.model.getBBox().center(); + coords.rotate(origin, angle); + var dx = coords.x - bbox.x; + var dy = coords.y - bbox.y; - var chess = ({ - KingWhite: KingWhite, - KingBlack: KingBlack, - QueenWhite: QueenWhite, - QueenBlack: QueenBlack, - RookWhite: RookWhite, - RookBlack: RookBlack, - BishopWhite: BishopWhite, - BishopBlack: BishopBlack, - KnightWhite: KnightWhite, - KnightBlack: KnightBlack, - PawnWhite: PawnWhite, - PawnBlack: PawnBlack - }); + if (relative) { + dx = abs2rel(dx, bbox.width); + dy = abs2rel(dy, bbox.height); + } - var Entity = Element$1.define('erd.Entity', { - size: { width: 150, height: 60 }, - attrs: { - '.outer': { - fill: '#2ECC71', stroke: '#27AE60', 'stroke-width': 2, - points: '100,0 100,60 0,60 0,0' - }, - '.inner': { - fill: '#2ECC71', stroke: '#27AE60', 'stroke-width': 2, - points: '95,5 95,55 5,55 5,5', - display: 'none' - }, - text: { - text: 'Entity', - 'font-family': 'Arial', 'font-size': 14, - 'ref-x': .5, 'ref-y': .5, - 'y-alignment': 'middle', 'text-anchor': 'middle' + end.anchor = { + name: 'topLeft', + args: { + dx: dx, + dy: dy, + rotate: true } - } - }, { - markup: '', - }); + }; - var WeakEntity = Entity.define('erd.WeakEntity', { - attrs: { - '.inner': { display: 'auto' }, - text: { text: 'Weak Entity' } + return end; + } + + function pinnedLinkEnd(relative, end, view, _magnet, coords) { + + var connection = view.getConnection(); + if (!connection) { return end; } + var length = connection.closestPointLength(coords); + if (relative) { + var totalLength = connection.length(); + end.anchor = { + name: 'connectionRatio', + args: { + ratio: length / totalLength + } + }; + } else { + end.anchor = { + name: 'connectionLength', + args: { + length: length + } + }; } + return end; + } + + var useDefaults = noop; + var pinAbsolute = pin(false); + var pinRelative = pin(true); + + var index$4 = ({ + useDefaults: useDefaults, + pinAbsolute: pinAbsolute, + pinRelative: pinRelative }); - var Relationship = Element$1.define('erd.Relationship', { - size: { width: 80, height: 80 }, - attrs: { - '.outer': { - fill: '#3498DB', stroke: '#2980B9', 'stroke-width': 2, - points: '40,0 80,40 40,80 0,40' - }, - '.inner': { - fill: '#3498DB', stroke: '#2980B9', 'stroke-width': 2, - points: '40,5 75,40 40,75 5,40', - display: 'none' - }, - text: { - text: 'Relationship', - 'font-family': 'Arial', 'font-size': 12, - 'ref-x': .5, 'ref-y': .5, - 'y-alignment': 'middle', 'text-anchor': 'middle' - } + function getAnchor(coords, view, magnet) { + // take advantage of an existing logic inside of the + // pin relative connection strategy + var end = pinRelative.call( + this.paper, + {}, + view, + magnet, + coords, + this.model + ); + return end.anchor; + } + + function snapAnchor(coords, view, magnet, type, relatedView, toolView) { + var snapRadius = toolView.options.snapRadius; + var isSource = (type === 'source'); + var refIndex = (isSource ? 0 : -1); + var ref = this.model.vertex(refIndex) || this.getEndAnchor(isSource ? 'target' : 'source'); + if (ref) { + if (Math.abs(ref.x - coords.x) < snapRadius) { coords.x = ref.x; } + if (Math.abs(ref.y - coords.y) < snapRadius) { coords.y = ref.y; } } - }, { - markup: '', - }); + return coords; + } - var IdentifyingRelationship = Relationship.define('erd.IdentifyingRelationship', { - attrs: { - '.inner': { display: 'auto' }, - text: { text: 'Identifying' } + function getViewBBox(view, useModelGeometry) { + var model = view.model; + if (useModelGeometry) { return model.getBBox(); } + return (model.isLink()) ? view.getConnection().bbox() : view.getNodeUnrotatedBBox(view.el); + } + + // Vertex Handles + var VertexHandle = View.extend({ + tagName: 'circle', + svgElement: true, + className: 'marker-vertex', + events: { + mousedown: 'onPointerDown', + touchstart: 'onPointerDown', + dblclick: 'onDoubleClick', + dbltap: 'onDoubleClick' + }, + documentEvents: { + mousemove: 'onPointerMove', + touchmove: 'onPointerMove', + mouseup: 'onPointerUp', + touchend: 'onPointerUp', + touchcancel: 'onPointerUp' + }, + attributes: { + 'r': 6, + 'fill': '#33334F', + 'stroke': '#FFFFFF', + 'stroke-width': 2, + 'cursor': 'move' + }, + position: function(x, y) { + this.vel.attr({ cx: x, cy: y }); + }, + onPointerDown: function(evt) { + if (this.options.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + this.options.paper.undelegateEvents(); + this.delegateDocumentEvents(null, evt.data); + this.trigger('will-change', this, evt); + }, + onPointerMove: function(evt) { + this.trigger('changing', this, evt); + }, + onDoubleClick: function(evt) { + this.trigger('remove', this, evt); + }, + onPointerUp: function(evt) { + this.trigger('changed', this, evt); + this.undelegateDocumentEvents(); + this.options.paper.delegateEvents(); } }); - var Attribute = Element$1.define('erd.Attribute', { - size: { width: 100, height: 50 }, - attrs: { - 'ellipse': { - transform: 'translate(50, 25)' - }, - '.outer': { - stroke: '#D35400', 'stroke-width': 2, - cx: 0, cy: 0, rx: 50, ry: 25, - fill: '#E67E22' - }, - '.inner': { - stroke: '#D35400', 'stroke-width': 2, - cx: 0, cy: 0, rx: 45, ry: 20, - fill: '#E67E22', display: 'none' - }, - text: { - 'font-family': 'Arial', 'font-size': 14, - 'ref-x': .5, 'ref-y': .5, - 'y-alignment': 'middle', 'text-anchor': 'middle' + var Vertices = ToolView.extend({ + name: 'vertices', + options: { + handleClass: VertexHandle, + snapRadius: 20, + redundancyRemoval: true, + vertexAdding: true, + stopPropagation: true + }, + children: [{ + tagName: 'path', + selector: 'connection', + className: 'joint-vertices-path', + attributes: { + 'fill': 'none', + 'stroke': 'transparent', + 'stroke-width': 10, + 'cursor': 'cell' + } + }], + handles: null, + events: { + 'mousedown .joint-vertices-path': 'onPathPointerDown', + 'touchstart .joint-vertices-path': 'onPathPointerDown' + }, + onRender: function() { + if (this.options.vertexAdding) { + this.renderChildren(); + this.updatePath(); + } + this.resetHandles(); + this.renderHandles(); + return this; + }, + update: function() { + var relatedView = this.relatedView; + var vertices = relatedView.model.vertices(); + if (vertices.length === this.handles.length) { + this.updateHandles(); + } else { + this.resetHandles(); + this.renderHandles(); + } + if (this.options.vertexAdding) { + this.updatePath(); + } + return this; + }, + resetHandles: function() { + var handles = this.handles; + this.handles = []; + this.stopListening(); + if (!Array.isArray(handles)) { return; } + for (var i = 0, n = handles.length; i < n; i++) { + handles[i].remove(); + } + }, + renderHandles: function() { + var this$1 = this; + + var relatedView = this.relatedView; + var vertices = relatedView.model.vertices(); + for (var i = 0, n = vertices.length; i < n; i++) { + var vertex = vertices[i]; + var handle = new (this.options.handleClass)({ + index: i, + paper: this.paper, + guard: function (evt) { return this$1.guard(evt); } + }); + handle.render(); + handle.position(vertex.x, vertex.y); + this.simulateRelatedView(handle.el); + handle.vel.appendTo(this.el); + this.handles.push(handle); + this.startHandleListening(handle); + } + }, + updateHandles: function() { + var relatedView = this.relatedView; + var vertices = relatedView.model.vertices(); + for (var i = 0, n = vertices.length; i < n; i++) { + var vertex = vertices[i]; + var handle = this.handles[i]; + if (!handle) { return; } + handle.position(vertex.x, vertex.y); + } + }, + updatePath: function() { + var connection = this.childNodes.connection; + if (connection) { connection.setAttribute('d', this.relatedView.getSerializedConnection()); } + }, + startHandleListening: function(handle) { + var relatedView = this.relatedView; + if (relatedView.can('vertexMove')) { + this.listenTo(handle, 'will-change', this.onHandleWillChange); + this.listenTo(handle, 'changing', this.onHandleChanging); + this.listenTo(handle, 'changed', this.onHandleChanged); + } + if (relatedView.can('vertexRemove')) { + this.listenTo(handle, 'remove', this.onHandleRemove); + } + }, + getNeighborPoints: function(index) { + var linkView = this.relatedView; + var vertices = linkView.model.vertices(); + var prev = (index > 0) ? vertices[index - 1] : linkView.sourceAnchor; + var next = (index < vertices.length - 1) ? vertices[index + 1] : linkView.targetAnchor; + return { + prev: new Point(prev), + next: new Point(next) + }; + }, + onHandleWillChange: function(_handle, evt) { + this.focus(); + var ref = this; + var relatedView = ref.relatedView; + var options = ref.options; + relatedView.model.startBatch('vertex-move', { ui: true, tool: this.cid }); + if (!options.stopPropagation) { relatedView.notifyPointerdown.apply(relatedView, relatedView.paper.getPointerArgs(evt)); } + }, + onHandleChanging: function(handle, evt) { + var ref = this; + var options = ref.options; + var linkView = ref.relatedView; + var index = handle.options.index; + var ref$1 = linkView.paper.getPointerArgs(evt); + var normalizedEvent = ref$1[0]; + var x = ref$1[1]; + var y = ref$1[2]; + var vertex = { x: x, y: y }; + this.snapVertex(vertex, index); + linkView.model.vertex(index, vertex, { ui: true, tool: this.cid }); + handle.position(vertex.x, vertex.y); + if (!options.stopPropagation) { linkView.notifyPointermove(normalizedEvent, x, y); } + }, + onHandleChanged: function(_handle, evt) { + var ref = this; + var options = ref.options; + var linkView = ref.relatedView; + if (options.vertexAdding) { this.updatePath(); } + if (!options.redundancyRemoval) { return; } + var verticesRemoved = linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); + if (verticesRemoved) { this.render(); } + this.blur(); + linkView.model.stopBatch('vertex-move', { ui: true, tool: this.cid }); + if (this.eventData(evt).vertexAdded) { + linkView.model.stopBatch('vertex-add', { ui: true, tool: this.cid }); + } + var ref$1 = linkView.paper.getPointerArgs(evt); + var normalizedEvt = ref$1[0]; + var x = ref$1[1]; + var y = ref$1[2]; + if (!options.stopPropagation) { linkView.notifyPointerup(normalizedEvt, x, y); } + linkView.checkMouseleave(normalizedEvt); + }, + snapVertex: function(vertex, index) { + var snapRadius = this.options.snapRadius; + if (snapRadius > 0) { + var neighbors = this.getNeighborPoints(index); + var prev = neighbors.prev; + var next = neighbors.next; + if (Math.abs(vertex.x - prev.x) < snapRadius) { + vertex.x = prev.x; + } else if (Math.abs(vertex.x - next.x) < snapRadius) { + vertex.x = next.x; + } + if (Math.abs(vertex.y - prev.y) < snapRadius) { + vertex.y = neighbors.prev.y; + } else if (Math.abs(vertex.y - next.y) < snapRadius) { + vertex.y = next.y; + } } + }, + onHandleRemove: function(handle, evt) { + var index$1 = handle.options.index; + var linkView = this.relatedView; + linkView.model.removeVertex(index$1, { ui: true }); + if (this.options.vertexAdding) { this.updatePath(); } + linkView.checkMouseleave(normalizeEvent(evt)); + }, + onPathPointerDown: function(evt) { + if (this.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + var normalizedEvent = normalizeEvent(evt); + var vertex = this.paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY).toJSON(); + var relatedView = this.relatedView; + relatedView.model.startBatch('vertex-add', { ui: true, tool: this.cid }); + var index$1 = relatedView.getVertexIndex(vertex.x, vertex.y); + this.snapVertex(vertex, index$1); + relatedView.model.insertVertex(index$1, vertex, { ui: true, tool: this.cid }); + this.update(); + var handle = this.handles[index$1]; + this.eventData(normalizedEvent, { vertexAdded: true }); + handle.onPointerDown(normalizedEvent); + }, + onRemove: function() { + this.resetHandles(); } }, { - markup: '', + VertexHandle: VertexHandle // keep as class property }); - var Multivalued = Attribute.define('erd.Multivalued', { - attrs: { - '.inner': { display: 'block' }, - text: { text: 'multivalued' } - } - }); + var SegmentHandle = View.extend({ + tagName: 'g', + svgElement: true, + className: 'marker-segment', + events: { + mousedown: 'onPointerDown', + touchstart: 'onPointerDown' + }, + documentEvents: { + mousemove: 'onPointerMove', + touchmove: 'onPointerMove', + mouseup: 'onPointerUp', + touchend: 'onPointerUp', + touchcancel: 'onPointerUp' + }, + children: [{ + tagName: 'line', + selector: 'line', + attributes: { + 'stroke': '#33334F', + 'stroke-width': 2, + 'fill': 'none', + 'pointer-events': 'none' + } + }, { + tagName: 'rect', + selector: 'handle', + attributes: { + 'width': 20, + 'height': 8, + 'x': -10, + 'y': -4, + 'rx': 4, + 'ry': 4, + 'fill': '#33334F', + 'stroke': '#FFFFFF', + 'stroke-width': 2 + } + }], + onRender: function() { + this.renderChildren(); + }, + position: function(x, y, angle, view) { - var Derived = Attribute.define('erd.Derived', { - attrs: { - '.outer': { 'stroke-dasharray': '3,5' }, - text: { text: 'derived' } - } - }); + var matrix = V.createSVGMatrix().translate(x, y).rotate(angle); + var handle = this.childNodes.handle; + handle.setAttribute('transform', V.matrixToTransformString(matrix)); + handle.setAttribute('cursor', (angle % 180 === 0) ? 'row-resize' : 'col-resize'); - var Key = Attribute.define('erd.Key', { - attrs: { - ellipse: { 'stroke-width': 4 }, - text: { text: 'key', 'font-weight': '800', 'text-decoration': 'underline' } + var viewPoint = view.getClosestPoint(new Point(x, y)); + var line = this.childNodes.line; + line.setAttribute('x1', x); + line.setAttribute('y1', y); + line.setAttribute('x2', viewPoint.x); + line.setAttribute('y2', viewPoint.y); + }, + onPointerDown: function(evt) { + if (this.options.guard(evt)) { return; } + this.trigger('change:start', this, evt); + evt.stopPropagation(); + evt.preventDefault(); + this.options.paper.undelegateEvents(); + this.delegateDocumentEvents(null, evt.data); + }, + onPointerMove: function(evt) { + this.trigger('changing', this, evt); + }, + onPointerUp: function(evt) { + this.undelegateDocumentEvents(); + this.options.paper.delegateEvents(); + this.trigger('change:end', this, evt); + }, + show: function() { + this.el.style.display = ''; + }, + hide: function() { + this.el.style.display = 'none'; } }); - var Normal = Attribute.define('erd.Normal', { - attrs: { text: { text: 'Normal' }} - }); + var Segments = ToolView.extend({ + name: 'segments', + precision: .5, + options: { + handleClass: SegmentHandle, + segmentLengthThreshold: 40, + redundancyRemoval: true, + anchor: getAnchor, + snapRadius: 10, + snapHandle: true, + stopPropagation: true + }, + handles: null, + onRender: function() { + this.resetHandles(); + var relatedView = this.relatedView; + var vertices = relatedView.model.vertices(); + vertices.unshift(relatedView.sourcePoint); + vertices.push(relatedView.targetPoint); + for (var i = 0, n = vertices.length; i < n - 1; i++) { + var vertex = vertices[i]; + var nextVertex = vertices[i + 1]; + var handle = this.renderHandle(vertex, nextVertex); + this.simulateRelatedView(handle.el); + this.handles.push(handle); + handle.options.index = i; + } + return this; + }, + renderHandle: function(vertex, nextVertex) { + var this$1 = this; - var ISA = Element$1.define('erd.ISA', { - type: 'erd.ISA', - size: { width: 100, height: 50 }, - attrs: { - polygon: { - points: '0,0 50,50 100,0', - fill: '#F1C40F', stroke: '#F39C12', 'stroke-width': 2 - }, - text: { - text: 'ISA', 'font-size': 18, - 'ref-x': .5, 'ref-y': .3, - 'y-alignment': 'middle', 'text-anchor': 'middle' + var handle = new (this.options.handleClass)({ + paper: this.paper, + guard: function (evt) { return this$1.guard(evt); } + }); + handle.render(); + this.updateHandle(handle, vertex, nextVertex); + handle.vel.appendTo(this.el); + this.startHandleListening(handle); + return handle; + }, + update: function() { + this.render(); + return this; + }, + startHandleListening: function(handle) { + this.listenTo(handle, 'change:start', this.onHandleChangeStart); + this.listenTo(handle, 'changing', this.onHandleChanging); + this.listenTo(handle, 'change:end', this.onHandleChangeEnd); + }, + resetHandles: function() { + var handles = this.handles; + this.handles = []; + this.stopListening(); + if (!Array.isArray(handles)) { return; } + for (var i = 0, n = handles.length; i < n; i++) { + handles[i].remove(); } - } - }, { - markup: '', - }); + }, + shiftHandleIndexes: function(value) { + var handles = this.handles; + for (var i = 0, n = handles.length; i < n; i++) { handles[i].options.index += value; } + }, + resetAnchor: function(type, anchor) { + var relatedModel = this.relatedView.model; + if (anchor) { + relatedModel.prop([type, 'anchor'], anchor, { + rewrite: true, + ui: true, + tool: this.cid + }); + } else { + relatedModel.removeProp([type, 'anchor'], { + ui: true, + tool: this.cid + }); + } + }, + snapHandle: function(handle, position, data) { - var Line$1 = Link.define('erd.Line', {}, { - cardinality: function(value) { - this.set('labels', [{ position: -20, attrs: { text: { dy: -8, text: value }}}]); - } - }); + var index = handle.options.index; + var linkView = this.relatedView; + var link = linkView.model; + var vertices = link.vertices(); + var axis = handle.options.axis; + var prev = vertices[index - 2] || data.sourceAnchor; + var next = vertices[index + 1] || data.targetAnchor; + var snapRadius = this.options.snapRadius; + if (Math.abs(position[axis] - prev[axis]) < snapRadius) { + position[axis] = prev[axis]; + } else if (Math.abs(position[axis] - next[axis]) < snapRadius) { + position[axis] = next[axis]; + } + return position; + }, - var erd = ({ - Entity: Entity, - WeakEntity: WeakEntity, - Relationship: Relationship, - IdentifyingRelationship: IdentifyingRelationship, - Attribute: Attribute, - Multivalued: Multivalued, - Derived: Derived, - Key: Key, - Normal: Normal, - ISA: ISA, - Line: Line$1 - }); + onHandleChanging: function(handle, evt) { - var State = Circle.define('fsa.State', { - attrs: { - circle: { 'stroke-width': 3 }, - text: { 'font-weight': '800' } - } - }); + var ref = this; + var options = ref.options; + var data = this.eventData(evt); + var relatedView = this.relatedView; + var paper = relatedView.paper; + var index$1 = handle.options.index - 1; + var normalizedEvent = normalizeEvent(evt); + var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + var position = this.snapHandle(handle, coords.clone(), data); + var axis = handle.options.axis; + var offset = (this.options.snapHandle) ? 0 : (coords[axis] - position[axis]); + var link = relatedView.model; + var vertices = cloneDeep(link.vertices()); + var vertex = vertices[index$1]; + var nextVertex = vertices[index$1 + 1]; + var anchorFn = this.options.anchor; + if (typeof anchorFn !== 'function') { anchorFn = null; } - var StartState = Element$1.define('fsa.StartState', { - size: { width: 20, height: 20 }, - attrs: { - circle: { - transform: 'translate(10, 10)', - r: 10, - fill: '#000000' + // First Segment + var sourceView = relatedView.sourceView; + var sourceBBox = relatedView.sourceBBox; + var changeSourceAnchor = false; + var deleteSourceAnchor = false; + if (!vertex) { + vertex = relatedView.sourceAnchor.toJSON(); + vertex[axis] = position[axis]; + if (sourceBBox.containsPoint(vertex)) { + vertex[axis] = position[axis]; + changeSourceAnchor = true; + } else { + // we left the area of the source magnet for the first time + vertices.unshift(vertex); + this.shiftHandleIndexes(1); + deleteSourceAnchor = true; + } + } else if (index$1 === 0) { + if (sourceBBox.containsPoint(vertex)) { + vertices.shift(); + this.shiftHandleIndexes(-1); + changeSourceAnchor = true; + } else { + vertex[axis] = position[axis]; + deleteSourceAnchor = true; + } + } else { + vertex[axis] = position[axis]; } - } - }, { - markup: '', - }); - var EndState = Element$1.define('fsa.EndState', { - size: { width: 20, height: 20 }, - attrs: { - '.outer': { - transform: 'translate(10, 10)', - r: 10, - fill: '#ffffff', - stroke: '#000000' - }, + if (anchorFn && sourceView) { + if (changeSourceAnchor) { + var sourceAnchorPosition = data.sourceAnchor.clone(); + sourceAnchorPosition[axis] = position[axis]; + var sourceAnchor = anchorFn.call(relatedView, sourceAnchorPosition, sourceView, relatedView.sourceMagnet || sourceView.el, 'source', relatedView); + this.resetAnchor('source', sourceAnchor); + } + if (deleteSourceAnchor) { + this.resetAnchor('source', data.sourceAnchorDef); + } + } - '.inner': { - transform: 'translate(10, 10)', - r: 6, - fill: '#000000' + // Last segment + var targetView = relatedView.targetView; + var targetBBox = relatedView.targetBBox; + var changeTargetAnchor = false; + var deleteTargetAnchor = false; + if (!nextVertex) { + nextVertex = relatedView.targetAnchor.toJSON(); + nextVertex[axis] = position[axis]; + if (targetBBox.containsPoint(nextVertex)) { + changeTargetAnchor = true; + } else { + // we left the area of the target magnet for the first time + vertices.push(nextVertex); + deleteTargetAnchor = true; + } + } else if (index$1 === vertices.length - 2) { + if (targetBBox.containsPoint(nextVertex)) { + vertices.pop(); + changeTargetAnchor = true; + } else { + nextVertex[axis] = position[axis]; + deleteTargetAnchor = true; + } + } else { + nextVertex[axis] = position[axis]; } - } - }, { - markup: '', - }); - var Arrow = Link.define('fsa.Arrow', { - attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }}, - smooth: true - }); + if (anchorFn && targetView) { + if (changeTargetAnchor) { + var targetAnchorPosition = data.targetAnchor.clone(); + targetAnchorPosition[axis] = position[axis]; + var targetAnchor = anchorFn.call(relatedView, targetAnchorPosition, targetView, relatedView.targetMagnet || targetView.el, 'target', relatedView); + this.resetAnchor('target', targetAnchor); + } + if (deleteTargetAnchor) { + this.resetAnchor('target', data.targetAnchorDef); + } + } - var fsa = ({ - State: State, - StartState: StartState, - EndState: EndState, - Arrow: Arrow + link.vertices(vertices, { ui: true, tool: this.cid }); + this.updateHandle(handle, vertex, nextVertex, offset); + if (!options.stopPropagation) { relatedView.notifyPointermove(normalizedEvent, coords.x, coords.y); } + }, + onHandleChangeStart: function(handle, evt) { + var ref = this; + var options = ref.options; + var handles = ref.handles; + var linkView = ref.relatedView; + var model = linkView.model; + var paper = linkView.paper; + var index$1 = handle.options.index; + if (!Array.isArray(handles)) { return; } + for (var i = 0, n = handles.length; i < n; i++) { + if (i !== index$1) { handles[i].hide(); } + } + this.focus(); + this.eventData(evt, { + sourceAnchor: linkView.sourceAnchor.clone(), + targetAnchor: linkView.targetAnchor.clone(), + sourceAnchorDef: clone(model.prop(['source', 'anchor'])), + targetAnchorDef: clone(model.prop(['target', 'anchor'])) + }); + model.startBatch('segment-move', { ui: true, tool: this.cid }); + if (!options.stopPropagation) { linkView.notifyPointerdown.apply(linkView, paper.getPointerArgs(evt)); } + }, + onHandleChangeEnd: function(_handle, evt) { + var ref= this; + var options = ref.options; + var linkView = ref.relatedView; + var paper = linkView.paper; + var model = linkView.model; + if (options.redundancyRemoval) { + linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); + } + var normalizedEvent = normalizeEvent(evt); + var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + this.render(); + this.blur(); + model.stopBatch('segment-move', { ui: true, tool: this.cid }); + if (!options.stopPropagation) { linkView.notifyPointerup(normalizedEvent, coords.x, coords.y); } + linkView.checkMouseleave(normalizedEvent); + }, + updateHandle: function(handle, vertex, nextVertex, offset) { + var vertical = Math.abs(vertex.x - nextVertex.x) < this.precision; + var horizontal = Math.abs(vertex.y - nextVertex.y) < this.precision; + if (vertical || horizontal) { + var segmentLine = new Line(vertex, nextVertex); + var length = segmentLine.length(); + if (length < this.options.segmentLengthThreshold) { + handle.hide(); + } else { + var position = segmentLine.midpoint(); + var axis = (vertical) ? 'x' : 'y'; + position[axis] += offset || 0; + var angle = segmentLine.vector().vectorAngle(new Point(1, 0)); + handle.position(position.x, position.y, angle, this.relatedView); + handle.show(); + handle.options.axis = axis; + } + } else { + handle.hide(); + } + }, + onRemove: function() { + this.resetHandles(); + } + }, { + SegmentHandle: SegmentHandle // keep as class property }); - var Member = Element$1.define('org.Member', { - size: { width: 180, height: 70 }, - attrs: { - rect: { width: 170, height: 60 }, - - '.card': { - fill: '#FFFFFF', stroke: '#000000', 'stroke-width': 2, - 'pointer-events': 'visiblePainted', rx: 10, ry: 10 - }, - - image: { - width: 48, height: 48, - ref: '.card', 'ref-x': 10, 'ref-y': 5 - }, - - '.rank': { - 'text-decoration': 'underline', - ref: '.card', 'ref-x': 0.9, 'ref-y': 0.2, - 'font-family': 'Courier New', 'font-size': 14, - 'text-anchor': 'end' - }, - - '.name': { - 'font-weight': '800', - ref: '.card', 'ref-x': 0.9, 'ref-y': 0.6, - 'font-family': 'Courier New', 'font-size': 14, - 'text-anchor': 'end' + // End Markers + var Arrowhead = ToolView.extend({ + tagName: 'path', + xAxisVector: new Point(1, 0), + events: { + mousedown: 'onPointerDown', + touchstart: 'onPointerDown' + }, + documentEvents: { + mousemove: 'onPointerMove', + touchmove: 'onPointerMove', + mouseup: 'onPointerUp', + touchend: 'onPointerUp', + touchcancel: 'onPointerUp' + }, + onRender: function() { + this.update(); + }, + update: function() { + var ratio = this.ratio; + var view = this.relatedView; + var tangent = view.getTangentAtRatio(ratio); + var position, angle; + if (tangent) { + position = tangent.start; + angle = tangent.vector().vectorAngle(this.xAxisVector) || 0; + } else { + position = view.getPointAtRatio(ratio); + angle = 0; + } + if (!position) { return this; } + var matrix = V.createSVGMatrix().translate(position.x, position.y).rotate(angle); + this.vel.transform(matrix, { absolute: true }); + return this; + }, + onPointerDown: function(evt) { + if (this.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + var relatedView = this.relatedView; + relatedView.model.startBatch('arrowhead-move', { ui: true, tool: this.cid }); + if (relatedView.can('arrowheadMove')) { + relatedView.startArrowheadMove(this.arrowheadType); + this.delegateDocumentEvents(); + relatedView.paper.undelegateEvents(); } + this.focus(); + this.el.style.pointerEvents = 'none'; + }, + onPointerMove: function(evt) { + var normalizedEvent = normalizeEvent(evt); + var coords = this.paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + this.relatedView.pointermove(normalizedEvent, coords.x, coords.y); + }, + onPointerUp: function(evt) { + this.undelegateDocumentEvents(); + var relatedView = this.relatedView; + var paper = relatedView.paper; + var normalizedEvent = normalizeEvent(evt); + var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + relatedView.pointerup(normalizedEvent, coords.x, coords.y); + paper.delegateEvents(); + this.blur(); + this.el.style.pointerEvents = ''; + relatedView.model.stopBatch('arrowhead-move', { ui: true, tool: this.cid }); } - }, { - markup: '', }); - var Arrow$1 = Link.define('org.Arrow', { - source: { selector: '.card' }, target: { selector: '.card' }, - attrs: { '.connection': { stroke: '#585858', 'stroke-width': 3 }}, - z: -1 + var TargetArrowhead = Arrowhead.extend({ + name: 'target-arrowhead', + ratio: 1, + arrowheadType: 'target', + attributes: { + 'd': 'M -10 -8 10 0 -10 8 Z', + 'fill': '#33334F', + 'stroke': '#FFFFFF', + 'stroke-width': 2, + 'cursor': 'move', + 'class': 'target-arrowhead' + } }); - var org = ({ - Member: Member, - Arrow: Arrow$1 + var SourceArrowhead = Arrowhead.extend({ + name: 'source-arrowhead', + ratio: 0, + arrowheadType: 'source', + attributes: { + 'd': 'M 10 -8 -10 0 10 8 Z', + 'fill': '#33334F', + 'stroke': '#FFFFFF', + 'stroke-width': 2, + 'cursor': 'move', + 'class': 'source-arrowhead' + } }); - var Place = Generic.define('pn.Place', { - size: { width: 50, height: 50 }, - attrs: { - '.root': { - r: 25, - fill: '#ffffff', - stroke: '#000000', - transform: 'translate(25, 25)' - }, - '.label': { - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-y': -20, - ref: '.root', - fill: '#000000', - 'font-size': 12 - }, - '.tokens > circle': { - fill: '#000000', - r: 5 - }, - '.tokens.one > circle': { transform: 'translate(25, 25)' }, - - '.tokens.two > circle:nth-child(1)': { transform: 'translate(19, 25)' }, - '.tokens.two > circle:nth-child(2)': { transform: 'translate(31, 25)' }, - - '.tokens.three > circle:nth-child(1)': { transform: 'translate(18, 29)' }, - '.tokens.three > circle:nth-child(2)': { transform: 'translate(25, 19)' }, - '.tokens.three > circle:nth-child(3)': { transform: 'translate(32, 29)' }, - - '.tokens.alot > text': { - transform: 'translate(25, 18)', - 'text-anchor': 'middle', - fill: '#000000' + var Button = ToolView.extend({ + name: 'button', + events: { + 'mousedown': 'onPointerDown', + 'touchstart': 'onPointerDown' + }, + options: { + distance: 0, + offset: 0, + rotate: false + }, + onRender: function() { + this.renderChildren(this.options.markup); + this.update(); + }, + update: function() { + this.position(); + return this; + }, + position: function() { + var ref = this; + var view = ref.relatedView; + var vel = ref.vel; + var matrix = view.model.isLink() ? this.getLinkMatrix() : this.getElementMatrix(); + vel.transform(matrix, { absolute: true }); + }, + getElementMatrix: function getElementMatrix() { + var ref = this; + var view = ref.relatedView; + var options = ref.options; + var x = options.x; if ( x === void 0 ) x = 0; + var y = options.y; if ( y === void 0 ) y = 0; + var offset = options.offset; if ( offset === void 0 ) offset = {}; + var useModelGeometry = options.useModelGeometry; + var rotate = options.rotate; + var bbox = getViewBBox(view, useModelGeometry); + var angle = view.model.angle(); + if (!rotate) { bbox = bbox.bbox(angle); } + var offsetX = offset.x; if ( offsetX === void 0 ) offsetX = 0; + var offsetY = offset.y; if ( offsetY === void 0 ) offsetY = 0; + if (isPercentage(x)) { + x = parseFloat(x) / 100 * bbox.width; + } + if (isPercentage(y)) { + y = parseFloat(y) / 100 * bbox.height; + } + var matrix = V.createSVGMatrix().translate(bbox.x + bbox.width / 2, bbox.y + bbox.height / 2); + if (rotate) { matrix = matrix.rotate(angle); } + matrix = matrix.translate(x + offsetX - bbox.width / 2, y + offsetY - bbox.height / 2); + return matrix; + }, + getLinkMatrix: function getLinkMatrix() { + var ref = this; + var view = ref.relatedView; + var options = ref.options; + var offset = options.offset; if ( offset === void 0 ) offset = 0; + var distance = options.distance; if ( distance === void 0 ) distance = 0; + var rotate = options.rotate; + var tangent, position, angle; + if (isPercentage(distance)) { + tangent = view.getTangentAtRatio(parseFloat(distance) / 100); + } else { + tangent = view.getTangentAtLength(distance); + } + if (tangent) { + position = tangent.start; + angle = tangent.vector().vectorAngle(new Point(1, 0)) || 0; + } else { + position = view.getConnection().start; + angle = 0; + } + var matrix = V.createSVGMatrix() + .translate(position.x, position.y) + .rotate(angle) + .translate(0, offset); + if (!rotate) { matrix = matrix.rotate(-angle); } + return matrix; + }, + onPointerDown: function(evt) { + if (this.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + var actionFn = this.options.action; + if (typeof actionFn === 'function') { + actionFn.call(this.relatedView, evt, this.relatedView, this); } } - }, { - markup: '', }); - var PlaceView = ElementView.extend({ - - presentationAttributes: ElementView.addPresentationAttributes({ - tokens: ['TOKENS'] - }), - - initFlag: ElementView.prototype.initFlag.concat(['TOKENS']), - confirmUpdate: function() { - var ref; + var Remove = Button.extend({ + children: [{ + tagName: 'circle', + selector: 'button', + attributes: { + 'r': 7, + 'fill': '#FF1D00', + 'cursor': 'pointer' + } + }, { + tagName: 'path', + selector: 'icon', + attributes: { + 'd': 'M -3 -3 3 3 M -3 3 3 -3', + 'fill': 'none', + 'stroke': '#FFFFFF', + 'stroke-width': 2, + 'pointer-events': 'none' + } + }], + options: { + distance: 60, + offset: 0, + action: function(evt, view, tool) { + view.model.remove({ ui: true, tool: tool.cid }); + } + } + }); - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - var flags = (ref = ElementView.prototype.confirmUpdate).call.apply(ref, [ this ].concat( args )); - if (this.hasFlag(flags, 'TOKENS')) { - this.renderTokens(); - this.update(); - flags = this.removeFlag(flags, 'TOKENS'); + var Connect = Button.extend({ + name: 'connect', + documentEvents: { + mousemove: 'drag', + touchmove: 'drag', + mouseup: 'dragend', + touchend: 'dragend', + touchcancel: 'dragend' + }, + children: [{ + tagName: 'circle', + selector: 'button', + attributes: { + 'r': 7, + 'fill': '#333333', + 'cursor': 'pointer' } - return flags; + }, { + tagName: 'path', + selector: 'icon', + attributes: { + 'd': 'M -4 -1 L 0 -1 L 0 -4 L 4 0 L 0 4 0 1 -4 1 z', + 'fill': '#FFFFFF', + 'stroke': 'none', + 'stroke-width': 2, + 'pointer-events': 'none' + } + }], + options: { + distance: 80, + offset: 0, + magnet: function (view) { return view.el; }, + action: function (evt, _view, tool) { return tool.dragstart(evt); }, }, + getMagnetNode: function() { + var assign; - renderTokens: function() { - - var vTokens = this.vel.findOne('.tokens').empty(); - ['one', 'two', 'three', 'alot'].forEach(function(className) { - vTokens.removeClass(className); - }); - - var tokens = this.model.get('tokens'); - if (!tokens) { return; } - - switch (tokens) { - - case 1: - vTokens.addClass('one'); - vTokens.append(V('circle')); - break; - - case 2: - vTokens.addClass('two'); - vTokens.append([V('circle'), V('circle')]); + var ref = this; + var options = ref.options; + var relatedView = ref.relatedView; + var magnet = options.magnet; + var magnetNode; + switch (typeof magnet) { + case 'function': { + magnetNode = magnet.call(this, relatedView, this); break; - - case 3: - vTokens.addClass('three'); - vTokens.append([V('circle'), V('circle'), V('circle')]); + } + case 'string': { + (assign = relatedView.findBySelector(magnet), magnetNode = assign[0]); break; - - default: - vTokens.addClass('alot'); - vTokens.append(V('text').text(tokens + '')); + } + default: { + magnetNode = magnet; break; + } } + if (!magnetNode) { magnetNode = relatedView.el; } + if (magnetNode instanceof SVGElement) { return magnetNode; } + throw new Error('Connect: magnet must be an SVGElement'); + }, + dragstart: function(evt) { + var ref = this; + var paper = ref.paper; + var relatedView = ref.relatedView; + var normalizedEvent = normalizeEvent(evt); + var ref$1 = paper.clientToLocalPoint(normalizedEvent.clientX, normalizedEvent.clientY); + var x = ref$1.x; + var y = ref$1.y; + relatedView.dragLinkStart(normalizedEvent, this.getMagnetNode(), x, y); + paper.undelegateEvents(); + this.delegateDocumentEvents(null, evt.data); + this.focus(); + }, + drag: function(evt) { + var ref = this; + var paper = ref.paper; + var relatedView = ref.relatedView; + var normalizedEvent = normalizeEvent(evt); + var ref$1 = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + var x = ref$1.x; + var y = ref$1.y; + relatedView.dragLink(normalizedEvent, x, y); + }, + dragend: function(evt) { + var ref = this; + var paper = ref.paper; + var relatedView = ref.relatedView; + var normalizedEvent = normalizeEvent(evt); + var ref$1 = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + var x = ref$1.x; + var y = ref$1.y; + relatedView.dragLinkEnd(normalizedEvent, x, y); + this.undelegateDocumentEvents(); + paper.delegateEvents(); + this.blur(); + relatedView.checkMouseleave(normalizedEvent); } }); - var Transition = Generic.define('pn.Transition', { - size: { width: 12, height: 50 }, - attrs: { - 'rect': { - width: 12, - height: 50, - fill: '#000000', - stroke: '#000000' - }, - '.label': { - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-y': -20, - ref: 'rect', - fill: '#000000', - 'font-size': 12 + + var Boundary = ToolView.extend({ + name: 'boundary', + tagName: 'rect', + options: { + padding: 10, + useModelGeometry: false, + }, + attributes: { + 'fill': 'none', + 'stroke': '#33334F', + 'stroke-width': .5, + 'stroke-dasharray': '5, 5', + 'pointer-events': 'none' + }, + onRender: function() { + this.update(); + }, + update: function() { + var ref = this; + var view = ref.relatedView; + var options = ref.options; + var vel = ref.vel; + var useModelGeometry = options.useModelGeometry; + var rotate = options.rotate; + var padding = normalizeSides(options.padding); + var bbox = getViewBBox(view, useModelGeometry).moveAndExpand({ + x: -padding.left, + y: -padding.top, + width: padding.left + padding.right, + height: padding.top + padding.bottom + }); + var model = view.model; + if (model.isElement()) { + var angle = model.angle(); + if (angle) { + if (rotate) { + var origin = model.getBBox().center(); + vel.rotate(angle, origin.x, origin.y, { absolute: true }); + } else { + bbox = bbox.bbox(angle); + } + } } + vel.attr(bbox.toJSON()); + return this; } - }, { - markup: '', - }); - - var Link$3 = Link.define('pn.Link', { - attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }} - }); - - var pn = ({ - Place: Place, - PlaceView: PlaceView, - Transition: Transition, - Link: Link$3 }); - var Class = Generic.define('uml.Class', { - attrs: { - rect: { 'width': 200 }, - - '.uml-class-name-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#3498db' }, - '.uml-class-attrs-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#2980b9' }, - '.uml-class-methods-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#2980b9' }, - - '.uml-class-name-text': { - 'ref': '.uml-class-name-rect', - 'ref-y': .5, - 'ref-x': .5, - 'text-anchor': 'middle', - 'y-alignment': 'middle', - 'font-weight': 'bold', - 'fill': 'black', - 'font-size': 12, - 'font-family': 'Times New Roman' + var Anchor = ToolView.extend({ + tagName: 'g', + type: null, + children: [{ + tagName: 'circle', + selector: 'anchor', + attributes: { + 'cursor': 'pointer' + } + }, { + tagName: 'rect', + selector: 'area', + attributes: { + 'pointer-events': 'none', + 'fill': 'none', + 'stroke': '#33334F', + 'stroke-dasharray': '2,4', + 'rx': 5, + 'ry': 5 + } + }], + events: { + mousedown: 'onPointerDown', + touchstart: 'onPointerDown', + dblclick: 'onPointerDblClick', + dbltap: 'onPointerDblClick' + }, + documentEvents: { + mousemove: 'onPointerMove', + touchmove: 'onPointerMove', + mouseup: 'onPointerUp', + touchend: 'onPointerUp', + touchcancel: 'onPointerUp' + }, + options: { + snap: snapAnchor, + anchor: getAnchor, + resetAnchor: true, + customAnchorAttributes: { + 'stroke-width': 4, + 'stroke': '#33334F', + 'fill': '#FFFFFF', + 'r': 5 }, - '.uml-class-attrs-text': { - 'ref': '.uml-class-attrs-rect', 'ref-y': 5, 'ref-x': 5, - 'fill': 'black', 'font-size': 12, 'font-family': 'Times New Roman' + defaultAnchorAttributes: { + 'stroke-width': 2, + 'stroke': '#FFFFFF', + 'fill': '#33334F', + 'r': 6 }, - '.uml-class-methods-text': { - 'ref': '.uml-class-methods-rect', 'ref-y': 5, 'ref-x': 5, - 'fill': 'black', 'font-size': 12, 'font-family': 'Times New Roman' + areaPadding: 6, + snapRadius: 10, + restrictArea: true, + redundancyRemoval: true + }, + onRender: function() { + this.renderChildren(); + this.toggleArea(false); + this.update(); + }, + update: function() { + var type = this.type; + var relatedView = this.relatedView; + var view = relatedView.getEndView(type); + if (view) { + this.updateAnchor(); + this.updateArea(); + this.el.style.display = ''; + } else { + this.el.style.display = 'none'; + } + return this; + }, + updateAnchor: function() { + var childNodes = this.childNodes; + if (!childNodes) { return; } + var anchorNode = childNodes.anchor; + if (!anchorNode) { return; } + var relatedView = this.relatedView; + var type = this.type; + var position = relatedView.getEndAnchor(type); + var options = this.options; + var customAnchor = relatedView.model.prop([type, 'anchor']); + anchorNode.setAttribute('transform', 'translate(' + position.x + ',' + position.y + ')'); + var anchorAttributes = (customAnchor) ? options.customAnchorAttributes : options.defaultAnchorAttributes; + for (var attrName in anchorAttributes) { + anchorNode.setAttribute(attrName, anchorAttributes[attrName]); + } + }, + updateArea: function() { + var childNodes = this.childNodes; + if (!childNodes) { return; } + var areaNode = childNodes.area; + if (!areaNode) { return; } + var relatedView = this.relatedView; + var type = this.type; + var view = relatedView.getEndView(type); + var model = view.model; + var magnet = relatedView.getEndMagnet(type); + var padding = this.options.areaPadding; + if (!isFinite(padding)) { padding = 0; } + var bbox, angle, center; + if (view.isNodeConnection(magnet)) { + bbox = view.getBBox(); + angle = 0; + center = bbox.center(); + } else { + bbox = view.getNodeUnrotatedBBox(magnet); + angle = model.angle(); + center = bbox.center(); + if (angle) { center.rotate(model.getBBox().center(), -angle); } + // TODO: get the link's magnet rotation into account } + bbox.inflate(padding); + areaNode.setAttribute('x', -bbox.width / 2); + areaNode.setAttribute('y', -bbox.height / 2); + areaNode.setAttribute('width', bbox.width); + areaNode.setAttribute('height', bbox.height); + areaNode.setAttribute('transform', 'translate(' + center.x + ',' + center.y + ') rotate(' + angle + ')'); }, - - name: [], - attributes: [], - methods: [] - }, { - markup: [ - '', - '', - '', - '', - '', - '' - ].join(''), - - initialize: function() { - - this.on('change:name change:attributes change:methods', function() { - this.updateRectangles(); - this.trigger('uml-update'); - }, this); - - this.updateRectangles(); - - Generic.prototype.initialize.apply(this, arguments); + toggleArea: function(visible) { + this.childNodes.area.style.display = (visible) ? '' : 'none'; }, - - getClassName: function() { - return this.get('name'); + onPointerDown: function(evt) { + if (this.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + this.paper.undelegateEvents(); + this.delegateDocumentEvents(); + this.focus(); + this.toggleArea(this.options.restrictArea); + this.relatedView.model.startBatch('anchor-move', { ui: true, tool: this.cid }); }, + resetAnchor: function(anchor) { + var type = this.type; + var relatedModel = this.relatedView.model; + if (anchor) { + relatedModel.prop([type, 'anchor'], anchor, { + rewrite: true, + ui: true, + tool: this.cid + }); + } else { + relatedModel.removeProp([type, 'anchor'], { + ui: true, + tool: this.cid + }); + } + }, + onPointerMove: function(evt) { - updateRectangles: function() { - - var attrs = this.get('attrs'); - - var rects = [ - { type: 'name', text: this.getClassName() }, - { type: 'attrs', text: this.get('attributes') }, - { type: 'methods', text: this.get('methods') } - ]; - - var offsetY = 0; - - rects.forEach(function(rect) { - - var lines = Array.isArray(rect.text) ? rect.text : [rect.text]; - var rectHeight = lines.length * 20 + 20; - - attrs['.uml-class-' + rect.type + '-text'].text = lines.join('\n'); - attrs['.uml-class-' + rect.type + '-rect'].height = rectHeight; - attrs['.uml-class-' + rect.type + '-rect'].transform = 'translate(0,' + offsetY + ')'; - - offsetY += rectHeight; - }); - } - - }); - - var ClassView = ElementView.extend({ - - initialize: function() { - - ElementView.prototype.initialize.apply(this, arguments); + var relatedView = this.relatedView; + var type = this.type; + var view = relatedView.getEndView(type); + var model = view.model; + var magnet = relatedView.getEndMagnet(type); + var normalizedEvent = normalizeEvent(evt); + var coords = this.paper.clientToLocalPoint(normalizedEvent.clientX, normalizedEvent.clientY); + var snapFn = this.options.snap; + if (typeof snapFn === 'function') { + coords = snapFn.call(relatedView, coords, view, magnet, type, relatedView, this); + coords = new Point(coords); + } - this.listenTo(this.model, 'uml-update', function() { - this.update(); - this.resize(); - }); - } - }); + if (this.options.restrictArea) { + if (view.isNodeConnection(magnet)) { + // snap coords to the link's connection + var pointAtConnection = view.getClosestPoint(coords); + if (pointAtConnection) { coords = pointAtConnection; } + } else { + // snap coords within node bbox + var bbox = view.getNodeUnrotatedBBox(magnet); + var angle = model.angle(); + var origin = model.getBBox().center(); + var rotatedCoords = coords.clone().rotate(origin, angle); + if (!bbox.containsPoint(rotatedCoords)) { + coords = bbox.pointNearestToPoint(rotatedCoords).rotate(origin, -angle); + } + } + } - var Abstract = Class.define('uml.Abstract', { - attrs: { - '.uml-class-name-rect': { fill: '#e74c3c' }, - '.uml-class-attrs-rect': { fill: '#c0392b' }, - '.uml-class-methods-rect': { fill: '#c0392b' } - } - }, { + var anchor; + var anchorFn = this.options.anchor; + if (typeof anchorFn === 'function') { + anchor = anchorFn.call(relatedView, coords, view, magnet, type, relatedView); + } - getClassName: function() { - return ['<>', this.get('name')]; - } + this.resetAnchor(anchor); + this.update(); + }, - }); - var AbstractView = ClassView; + onPointerUp: function(evt) { + this.paper.delegateEvents(); + this.undelegateDocumentEvents(); + this.blur(); + this.toggleArea(false); + var linkView = this.relatedView; + if (this.options.redundancyRemoval) { linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); } + linkView.model.stopBatch('anchor-move', { ui: true, tool: this.cid }); + }, - var Interface = Class.define('uml.Interface', { - attrs: { - '.uml-class-name-rect': { fill: '#f1c40f' }, - '.uml-class-attrs-rect': { fill: '#f39c12' }, - '.uml-class-methods-rect': { fill: '#f39c12' } - } - }, { - getClassName: function() { - return ['<>', this.get('name')]; + onPointerDblClick: function() { + var anchor = this.options.resetAnchor; + if (anchor === false) { return; } // reset anchor disabled + if (anchor === true) { anchor = null; } // remove the current anchor + this.resetAnchor(cloneDeep(anchor)); + this.update(); } }); - var InterfaceView = ClassView; - - var Generalization = Link.define('uml.Generalization', { - attrs: { '.marker-target': { d: 'M 20 0 L 0 10 L 20 20 z', fill: 'white' }} - }); - var Implementation = Link.define('uml.Implementation', { - attrs: { - '.marker-target': { d: 'M 20 0 L 0 10 L 20 20 z', fill: 'white' }, - '.connection': { 'stroke-dasharray': '3,3' } - } + var SourceAnchor = Anchor.extend({ + name: 'source-anchor', + type: 'source' }); - var Aggregation = Link.define('uml.Aggregation', { - attrs: { '.marker-target': { d: 'M 40 10 L 20 20 L 0 10 L 20 0 z', fill: 'white' }} + var TargetAnchor = Anchor.extend({ + name: 'target-anchor', + type: 'target' }); - var Composition = Link.define('uml.Composition', { - attrs: { '.marker-target': { d: 'M 40 10 L 20 20 L 0 10 L 20 0 z', fill: 'black' }} + var index$5 = ({ + Vertices: Vertices, + Segments: Segments, + SourceArrowhead: SourceArrowhead, + TargetArrowhead: TargetArrowhead, + SourceAnchor: SourceAnchor, + TargetAnchor: TargetAnchor, + Button: Button, + Remove: Remove, + Connect: Connect, + Boundary: Boundary }); - var Association = Link.define('uml.Association'); - - // Statechart - - var State$1 = Generic.define('uml.State', { - attrs: { - '.uml-state-body': { - 'width': 200, 'height': 200, 'rx': 10, 'ry': 10, - 'fill': '#ecf0f1', 'stroke': '#bdc3c7', 'stroke-width': 3 - }, - '.uml-state-separator': { - 'stroke': '#bdc3c7', 'stroke-width': 2 - }, - '.uml-state-name': { - 'ref': '.uml-state-body', 'ref-x': .5, 'ref-y': 5, 'text-anchor': 'middle', - 'fill': '#000000', 'font-family': 'Courier New', 'font-size': 14 - }, - '.uml-state-events': { - 'ref': '.uml-state-separator', 'ref-x': 5, 'ref-y': 5, - 'fill': '#000000', 'font-family': 'Courier New', 'font-size': 14 + var Control = ToolView.extend({ + tagName: 'g', + children: [{ + tagName: 'circle', + selector: 'handle', + attributes: { + 'cursor': 'pointer', + 'stroke-width': 2, + 'stroke': '#FFFFFF', + 'fill': '#33334F', + 'r': 6 + } + }, { + tagName: 'rect', + selector: 'extras', + attributes: { + 'pointer-events': 'none', + 'fill': 'none', + 'stroke': '#33334F', + 'stroke-dasharray': '2,4', + 'rx': 5, + 'ry': 5 } + }], + events: { + mousedown: 'onPointerDown', + touchstart: 'onPointerDown', + dblclick: 'onPointerDblClick', + dbltap: 'onPointerDblClick' }, - - name: 'State', - events: [] - - }, { - markup: [ - '', - '', - '', - '', - '', - '', - '', - '' - ].join(''), - - initialize: function() { - - this.on({ - 'change:name': this.updateName, - 'change:events': this.updateEvents, - 'change:size': this.updatePath - }, this); - - this.updateName(); - this.updateEvents(); - this.updatePath(); - - Generic.prototype.initialize.apply(this, arguments); + documentEvents: { + mousemove: 'onPointerMove', + touchmove: 'onPointerMove', + mouseup: 'onPointerUp', + touchend: 'onPointerUp', + touchcancel: 'onPointerUp' }, - - updateName: function() { - - this.attr('.uml-state-name/text', this.get('name')); + options: { + handleAttributes: null, + selector: 'root', + padding: 6, }, - updateEvents: function() { - - this.attr('.uml-state-events/text', this.get('events').join('\n')); + getPosition: function() { + // To be overridden }, - - updatePath: function() { - - var d = 'M 0 20 L ' + this.get('size').width + ' 20'; - - // We are using `silent: true` here because updatePath() is meant to be called - // on resize and there's no need to to update the element twice (`change:size` - // triggers also an update). - this.attr('.uml-state-separator/d', d, { silent: true }); + setPosition: function() { + // To be overridden + }, + resetPosition: function() { + // To be overridden + }, + onRender: function() { + this.renderChildren(); + this.toggleExtras(false); + this.update(); + }, + update: function() { + var ref = this.childNodes; + var handle = ref.handle; + var extras = ref.extras; + if (handle) { + this.updateHandle(handle); + } else { + throw new Error('Control: markup selector `handle` is required'); + } + if (extras) { + this.updateExtras(extras); + } + return this; + }, + updateHandle: function(handleNode) { + var ref = this; + var relatedView = ref.relatedView; + var options = ref.options; + var model = relatedView.model; + var relativePos = this.getPosition(relatedView, this); + var absolutePos = model.getAbsolutePointFromRelative(relativePos); + handleNode.setAttribute('transform', ("translate(" + (absolutePos.x) + "," + (absolutePos.y) + ")")); + var handleAttributes = options.handleAttributes; + if (handleAttributes) { + for (var attrName in handleAttributes) { + handleNode.setAttribute(attrName, handleAttributes[attrName]); + } + } + }, + updateExtras: function(extrasNode) { + var ref = this; + var relatedView = ref.relatedView; + var options = ref.options; + var ref$1 = this.options; + var selector = ref$1.selector; + if (!selector) { + this.toggleExtras(false); + return; + } + var ref$2 = relatedView.findBySelector(selector); + var magnet = ref$2[0]; + if (!magnet) { throw new Error('Control: invalid selector.'); } + var padding = options.padding; + if (!isFinite(padding)) { padding = 0; } + var bbox = relatedView.getNodeUnrotatedBBox(magnet); + var model = relatedView.model; + var angle = model.angle(); + var center = bbox.center(); + if (angle) { center.rotate(model.getBBox().center(), -angle); } + bbox.inflate(padding); + extrasNode.setAttribute('x', -bbox.width / 2); + extrasNode.setAttribute('y', -bbox.height / 2); + extrasNode.setAttribute('width', bbox.width); + extrasNode.setAttribute('height', bbox.height); + extrasNode.setAttribute('transform', ("translate(" + (center.x) + "," + (center.y) + ") rotate(" + angle + ")")); + }, + toggleExtras: function(visible) { + var ref = this.childNodes; + var extras = ref.extras; + if (!extras) { return; } + extras.style.display = (visible) ? '' : 'none'; + }, + onPointerDown: function(evt) { + var ref = this; + var relatedView = ref.relatedView; + var paper = ref.paper; + if (this.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + paper.undelegateEvents(); + this.delegateDocumentEvents(); + this.focus(); + this.toggleExtras(true); + relatedView.model.startBatch('control-move', { ui: true, tool: this.cid }); + }, + onPointerMove: function(evt) { + var ref = this; + var relatedView = ref.relatedView; + var paper = ref.paper; + var model = relatedView.model; + var ref$1 = normalizeEvent(evt); + var clientX = ref$1.clientX; + var clientY = ref$1.clientY; + var coords = paper.clientToLocalPoint(clientX, clientY); + var relativeCoords = model.getRelativePointFromAbsolute(coords); + this.setPosition(relatedView, relativeCoords, this); + this.update(); + }, + onPointerUp: function(_evt) { + var ref = this; + var relatedView = ref.relatedView; + var paper = ref.paper; + paper.delegateEvents(); + this.undelegateDocumentEvents(); + this.blur(); + this.toggleExtras(false); + relatedView.model.stopBatch('control-move', { ui: true, tool: this.cid }); + }, + onPointerDblClick: function() { + var ref = this; + var relatedView = ref.relatedView; + this.resetPosition(relatedView, this); + this.update(); } - }); - var StartState$1 = Circle.define('uml.StartState', { - type: 'uml.StartState', - attrs: { circle: { 'fill': '#34495e', 'stroke': '#2c3e50', 'stroke-width': 2, 'rx': 1 }} }); - var EndState$1 = Generic.define('uml.EndState', { - size: { width: 20, height: 20 }, - attrs: { - 'circle.outer': { - transform: 'translate(10, 10)', - r: 10, - fill: '#ffffff', - stroke: '#2c3e50' - }, - 'circle.inner': { - transform: 'translate(10, 10)', - r: 6, - fill: '#34495e' - } - } - }, { - markup: '', - }); - var Transition$1 = Link.define('uml.Transition', { - attrs: { - '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z', fill: '#34495e', stroke: '#2c3e50' }, - '.connection': { stroke: '#2c3e50' } - } + var index$6 = ({ + Button: Button, + Remove: Remove, + Connect: Connect, + Boundary: Boundary, + Control: Control }); - var uml = ({ - Class: Class, - ClassView: ClassView, - Abstract: Abstract, - AbstractView: AbstractView, - Interface: Interface, - InterfaceView: InterfaceView, - Generalization: Generalization, - Implementation: Implementation, - Aggregation: Aggregation, - Composition: Composition, - Association: Association, - State: State$1, - StartState: StartState$1, - EndState: EndState$1, - Transition: Transition$1 - }); + var version = "3.5.1"; + var Vectorizer = V; + var layout = { PortLabel: PortLabel, Port: Port }; + var setTheme = function(theme, opt) { + opt = opt || {}; - var index$6 = ({ - basic: basic, - standard: standard, - devs: devs, - logic: logic, - chess: chess, - erd: erd, - fsa: fsa, - org: org, - pn: pn, - uml: uml - }); + invoke(views, 'setTheme', theme, opt); + + // Update the default theme on the view prototype. + View.prototype.defaultTheme = theme; + }; var layout$1 = { DirectedGraph: DirectedGraph, PortLabel: PortLabel, Port: Port }; @@ -33170,21 +33170,21 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. exports.anchors = anchors; exports.config = config; exports.connectionPoints = connectionPoints; - exports.connectionStrategies = index$2; + exports.connectionStrategies = index$4; exports.connectors = connectors; - exports.dia = index$5; - exports.elementTools = index$4; + exports.dia = index$2; + exports.elementTools = index$6; exports.env = env; exports.format = format$1; exports.g = g; exports.highlighters = highlighters; exports.layout = layout$1; exports.linkAnchors = linkAnchors; - exports.linkTools = index$3; + exports.linkTools = index$5; exports.mvc = index$1; exports.routers = routers; exports.setTheme = setTheme; - exports.shapes = index$6; + exports.shapes = index$3; exports.ui = ui; exports.util = index; exports.version = version; diff --git a/dist/joint.layout.DirectedGraph.js b/dist/joint.layout.DirectedGraph.js index 823b535f4..4c6e503e3 100644 --- a/dist/joint.layout.DirectedGraph.js +++ b/dist/joint.layout.DirectedGraph.js @@ -1,4 +1,4 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public diff --git a/dist/joint.layout.DirectedGraph.min.js b/dist/joint.layout.DirectedGraph.min.js index 7661f2c26..6ad503996 100644 --- a/dist/joint.layout.DirectedGraph.min.js +++ b/dist/joint.layout.DirectedGraph.min.js @@ -1,4 +1,4 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public diff --git a/dist/joint.min.css b/dist/joint.min.css index eac51af0e..cc92a1dcd 100644 --- a/dist/joint.min.css +++ b/dist/joint.min.css @@ -1,4 +1,4 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public diff --git a/dist/joint.min.js b/dist/joint.min.js index 7bc3ff3cc..7569a7f16 100644 --- a/dist/joint.min.js +++ b/dist/joint.min.js @@ -1,8 +1,8 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -if(function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("backbone"),require("lodash"),require("jquery")):"function"==typeof define&&define.amd?define(["exports","backbone","lodash","jquery"],e):e((t=t||self).joint={},t.Backbone,t._,t.$)}(this,function(t,s,i,S){"use strict";s=s&&s.hasOwnProperty("default")?s.default:s,i=i&&i.hasOwnProperty("default")?i.default:i,S=S&&S.hasOwnProperty("default")?S.default:S;var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function n(t,e){return t(e={exports:{}},e.exports),e.exports}var r=function(t){return t&&t.Math==Math&&t},u=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof e&&e)||function(){return this}()||Function("return this")(),a=function(t){try{return!!t()}catch(t){return!0}},l=!a(function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}),o={}.propertyIsEnumerable,c=Object.getOwnPropertyDescriptor,h={f:c&&!o.call({1:2},1)?function(t){var e=c(this,t);return!!e&&e.enumerable}:o},C=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},g={}.toString,d=function(t){return g.call(t).slice(8,-1)},f="".split,y=a(function(){return!Object("z").propertyIsEnumerable(0)})?function(t){return"String"==d(t)?f.call(t,""):Object(t)}:Object,p=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},m=function(t){return y(p(t))},I=function(t){return"object"==typeof t?null!==t:"function"==typeof t},v=function(t,e){if(!I(t))return t;var i,n;if(e&&"function"==typeof(i=t.toString)&&!I(n=i.call(t)))return n;if("function"==typeof(i=t.valueOf)&&!I(n=i.call(t)))return n;if(!e&&"function"==typeof(i=t.toString)&&!I(n=i.call(t)))return n;throw TypeError("Can't convert object to primitive value")},A={}.hasOwnProperty,w=function(t,e){return A.call(t,e)},b=u.document,N=I(b)&&I(b.createElement),x=function(t){return N?b.createElement(t):{}},M=!l&&!a(function(){return 7!=Object.defineProperty(x("div"),"a",{get:function(){return 7}}).a}),k=Object.getOwnPropertyDescriptor,D={f:l?k:function(t,e){if(t=m(t),e=v(e,!0),M)try{return k(t,e)}catch(t){}if(w(t,e))return C(!h.f.call(t,e),t[e])}},T=function(t){if(!I(t))throw TypeError(String(t)+" is not an object");return t},L=Object.defineProperty,z={f:l?L:function(t,e,i){if(T(t),e=v(e,!0),T(i),M)try{return L(t,e,i)}catch(t){}if("get"in i||"set"in i)throw TypeError("Accessors not supported");return"value"in i&&(t[e]=i.value),t}},P=l?function(t,e,i){return z.f(t,e,C(1,i))}:function(t,e,i){return t[e]=i,t},j=function(e,i){try{P(u,e,i)}catch(t){u[e]=i}return i},Z="__core-js_shared__",G=u[Z]||j(Z,{}),B=Function.toString;"function"!=typeof G.inspectSource&&(G.inspectSource=function(t){return B.call(t)});var O,E,R,Y=G.inspectSource,W=u.WeakMap,U="function"==typeof W&&/native code/.test(Y(W)),F=n(function(t){(t.exports=function(t,e){return G[t]||(G[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.8.3",mode:"global",copyright:"\xa9 2021 Denis Pushkarev (zloirock.ru)"})}),V=0,X=Math.random(),H=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++V+X).toString(36)},J=F("keys"),_=function(t){return J[t]||(J[t]=H(t))},Q={},K=u.WeakMap;if(U){var q=G.state||(G.state=new K),$=q.get,tt=q.has,et=q.set;O=function(t,e){return e.facade=t,et.call(q,t,e),e},E=function(t){return $.call(q,t)||{}},R=function(t){return tt.call(q,t)}}else{var it=_("state");Q[it]=!0,O=function(t,e){return e.facade=t,P(t,it,e),e},E=function(t){return w(t,it)?t[it]:{}},R=function(t){return w(t,it)}}var nt,rt={set:O,get:E,has:R,enforce:function(t){return R(t)?E(t):O(t,{})},getterFor:function(i){return function(t){var e;if(!I(t)||(e=E(t)).type!==i)throw TypeError("Incompatible receiver, "+i+" required");return e}}},ot=n(function(t){var e=rt.get,l=rt.enforce,c=String(String).split("String");(t.exports=function(t,e,i,n){var r,o=!!n&&!!n.unsafe,a=!!n&&!!n.enumerable,s=!!n&&!!n.noTargetGet;"function"==typeof i&&("string"!=typeof e||w(i,"name")||P(i,"name",e),(r=l(i)).source||(r.source=c.join("string"==typeof e?e:""))),t!==u?(o?!s&&t[e]&&(a=!0):delete t[e],a?t[e]=i:P(t,e,i)):a?t[e]=i:j(e,i)})(Function.prototype,"toString",function(){return"function"==typeof this&&e(this).source||Y(this)})}),at=u,st=function(t){return"function"==typeof t?t:void 0},lt=function(t,e){return arguments.length<2?st(at[t])||st(u[t]):at[t]&&at[t][e]||u[t]&&u[t][e]},ct=Math.ceil,ut=Math.floor,ht=function(t){return isNaN(t=+t)?0:(0r;)w(n,i=e[r++])&&(~vt(o,i)||o.push(i));return o},Ct=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],yt=Ct.concat("length","prototype"),wt={f:Object.getOwnPropertyNames||function(t){return At(t,yt)}},bt={f:Object.getOwnPropertySymbols},Nt=lt("Reflect","ownKeys")||function(t){var e=wt.f(T(t)),i=bt.f;return i?e.concat(i(t)):e},xt=function(t,e){for(var i=Nt(e),n=z.f,r=D.f,o=0;o"+t+""},_t=function(){try{nt=document.domain&&new ActiveXObject("htmlfile")}catch(t){}var t,e;_t=nt?function(t){t.write(Jt("")),t.close();var e=t.parentWindow.Object;return t=null,e}(nt):((e=x("iframe")).style.display="none",Ut.appendChild(e),e.src=String("javascript:"),(t=e.contentWindow.document).open(),t.write(Jt("document.F=Object")),t.close(),t.F);for(var i=Ct.length;i--;)delete _t[Ft][Ct[i]];return _t()};Q[Xt]=!0;var Qt=Object.create||function(t,e){var i;return null!==t?(Ht[Ft]=T(t),i=new Ht,Ht[Ft]=null,i[Xt]=t):i=_t(),void 0===e?i:Wt(i,e)},Kt=Rt("unscopables"),qt=Array.prototype;null==qt[Kt]&&z.f(qt,Kt,{configurable:!0,value:Qt(null)});var $t=function(t){qt[Kt][t]=!0},te=Object.defineProperty,ee={},ie=function(t){throw t},ne=function(t,e){if(w(ee,t))return ee[t];e||(e={});var i=[][t],n=!!w(e,"ACCESSORS")&&e.ACCESSORS,r=w(e,0)?e[0]:ie,o=w(e,1)?e[1]:void 0;return ee[t]=!!i&&!a(function(){if(n&&!l)return!0;var t={length:-1};n?te(t,1,{enumerable:!0,get:ie}):t[1]=1,i.call(t,r,o)})},re=It.includes,oe=ne("indexOf",{ACCESSORS:!0,1:0});jt({target:"Array",proto:!0,forced:!oe},{includes:function(t){return re(this,t,1=i.length?{value:void 0,done:!0}:(t=Xe(i,n),e.index+=t.length,{value:t,done:!1})});var Qe=function(e,t,i,n){try{return n?t(T(i)[0],i[1]):t(i)}catch(t){throw function(t){var e=t.return;if(void 0!==e)T(e.call(t)).value}(e),t}},Ke=Rt("iterator"),qe=Array.prototype,$e=function(t,e,i){var n=v(e);n in t?z.f(t,n,C(0,i)):t[n]=i},ti={};ti[Rt("toStringTag")]="z";var ei="[object z]"===String(ti),ii=Rt("toStringTag"),ni="Arguments"==d(function(){return arguments}()),ri=ei?d:function(t){var e,i,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(i=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),ii))?i:ni?d(e):"Object"==(n=d(e))&&"function"==typeof e.callee?"Arguments":n},oi=Rt("iterator"),ai=Rt("iterator"),si=!1;try{var li=0,ci={next:function(){return{done:!!li++}},return:function(){si=!0}};ci[ai]=function(){return this},Array.from(ci,function(){throw 2})}catch(t){}var ui=!function(t,e){if(!e&&!si)return!1;var i=!1;try{var n={};n[ai]=function(){return{next:function(){return{done:i=!0}}}},t(n)}catch(t){}return i}(function(t){Array.from(t)});jt({target:"Array",stat:!0,forced:ui},{from:function(t){var e,i,n,r,o,a,s,l=ce(t),c="function"==typeof this?this:Array,u=arguments.length,h=1>8-r%1*8)){if(255<(i=n.charCodeAt(r+=.75)))throw new l("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");e=e<<8|i}return a}),t.atob||(t.atob=function(t){var e=String(t).replace(/=+$/,"");if(e.length%4==1)throw new l("'atob' failed: The string to be decoded is not correctly encoded.");for(var i,n,r=0,o=0,a="";n=e.charAt(o++);~n&&(i=r%4?64*i+n:n,r++%4)?a+=String.fromCharCode(255&i>>(-2*r&6)):0)n=s.indexOf(n);return a})}()});jt({target:"Number",stat:!0},{isNaN:function(t){return t!=t}});at.Number.isNaN;var pi=u.isFinite,mi=Number.isFinite||function(t){return"number"==typeof t&&pi(t)};jt({target:"Number",stat:!0},{isFinite:mi});at.Number.isFinite;var Ii=Rt("match"),vi=function(t){if(I(e=t)&&(void 0!==(i=e[Ii])?i:"RegExp"==d(e)))throw TypeError("The method doesn't accept regular expressions");var e,i;return t},Ai=Rt("match"),Ci=function(e){var i=/./;try{"/./"[e](i)}catch(t){try{return i[Ai]=!1,"/./"[e](i)}catch(t){}}return!1};jt({target:"String",proto:!0,forced:!Ci("includes")},{includes:function(t){return!!~String(p(this)).indexOf(vi(t),1n)&&!(new nn(t,i).length()>n)},divideAt:function(t){var e=this.pointAt(t);return[new nn(this.start,e),new nn(e,this.end)]},divideAtLength:function(t){var e=this.pointAtLength(t);return[new nn(this.start,e),new nn(e,this.end)]},equals:function(t){return!!t&&this.start.x===t.start.x&&this.start.y===t.start.y&&this.end.x===t.end.x&&this.end.y===t.end.y},intersect:function(t,e){if(t&&t.intersectionWithLine){var i=t.intersectionWithLine(this,e);return i&&t instanceof nn&&(i=i[0]),i}return null},intersectionWithLine:function(t){var e=new qi(this.end.x-this.start.x,this.end.y-this.start.y),i=new qi(t.end.x-t.start.x,t.end.y-t.start.y),n=e.x*i.y-e.y*i.x,r=new qi(t.start.x-this.start.x,t.start.y-this.start.y),o=r.x*i.y-r.y*i.x,a=r.x*e.y-r.y*e.x;if(0===n||o*n<0||a*n<0)return null;if(0s.x+o/2,h=ns.x?r-30:r+30)-c)/(a*a*(n-l))+l:e=a*a/(r-c)-a*a*(n-l)*((i=r>s.y?n+30:n-30)-l)/(o*o*(r-c))+c,new qi(i,e).theta(t)},toString:function(){return new qi(this.x,this.y).toString()+" "+this.a+" "+this.b}};var cn=ln,un=Math.abs,hn=Math.cos,gn=Math.sin,dn=Math.min,fn=Math.max,pn=Math.round,mn=Math.pow,In=function(t,e,i,n){if(!(this instanceof In))return new In(t,e,i,n);Object(t)===t&&(e=t.y,i=t.width,n=t.height,t=t.x),this.x=void 0===t?0:t,this.y=void 0===e?0:e,this.width=void 0===i?0:i,this.height=void 0===n?0:n};In.fromEllipse=function(t){return t=new ln(t),new In(t.x-t.a,t.y-t.b,2*t.a,2*t.b)},In.fromPointUnion=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(0===t.length)return null;var i,n,r,o,a=new qi;i=n=1/0,r=o=-1/0;for(var s=0;s=this.x&&t.x<=this.x+this.width&&t.y>=this.y&&t.y<=this.y+this.height},containsRect:function(t){var e=new In(this).normalize(),i=new In(t).normalize(),n=e.width,r=e.height,o=i.width,a=i.height;if(!(n&&r&&o&&a))return!1;var s=e.x,l=e.y,c=i.x,u=i.y;return o+=c,n+=s,a+=u,r+=l,s<=c&&o<=n&&l<=u&&a<=r},corner:function(){return new qi(this.x+this.width,this.y+this.height)},equals:function(t){var e=new In(this).normalize(),i=new In(t).normalize();return e.x===i.x&&e.y===i.y&&e.width===i.width&&e.height===i.height},inflate:function(t,e){return void 0===t&&(t=0),void 0===e&&(e=t),this.x-=t,this.y-=e,this.width+=2*t,this.height+=2*e,this},intersect:function(t){var e=this.origin(),i=this.corner(),n=t.origin(),r=t.corner();if(r.x<=e.x||r.y<=e.y||n.x>=i.x||n.y>=i.y)return null;var o=fn(e.x,n.x),a=fn(e.y,n.y);return new In(o,a,dn(i.x,r.x)-o,dn(i.y,r.y)-a)},intersectionWithLine:function(t){var e,i,n=[this.topLine(),this.rightLine(),this.bottomLine(),this.leftLine()],r=[],o=[],a=n.length;for(i=0;iu&&(n=(this.x+this.width-u)/(d.x-u)),d.y>h&&(s=(this.y+this.height-h)/(d.y-h));var f=t.topRight();f.x>u&&(r=(this.x+this.width-u)/(f.x-u)),f.yh&&(c=(this.y+this.height-h)/(p.y-h)),{sx:dn(i,n,r,o),sy:dn(a,s,l,c)}},maxRectUniformScaleToFit:function(t,e){var i=this.maxRectScaleToFit(t,e);return dn(i.sx,i.sy)},moveAndExpand:function(t){return this.x+=t.x||0,this.y+=t.y||0,this.width+=t.width||0,this.height+=t.height||0,this},normalize:function(){var t=this.x,e=this.y,i=this.width,n=this.height;return this.width<0&&(t=this.x+this.width,i=-this.width),this.height<0&&(e=this.y+this.height,n=-this.height),this.x=t,this.y=e,this.width=i,this.height=n,this},offset:function(t,e){return qi.prototype.offset.call(this,t,e)},origin:function(){return new qi(this.x,this.y)},pointNearestToPoint:function(t){if(t=new qi(t),this.containsPoint(t))switch(this.sideNearestToPoint(t)){case"right":return new qi(this.x+this.width,t.y);case"left":return new qi(this.x,t.y);case"bottom":return new qi(t.x,this.y+this.height);case"top":return new qi(t.x,this.y)}return t.adhereToRect(this)},rightLine:function(){return new nn(this.topRight(),this.bottomRight())},rightMiddle:function(){return new qi(this.x+this.width,this.y+this.height/2)},round:function(t){var e=1;if(t)switch(t){case 1:e=10;break;case 2:e=100;break;case 3:e=1e3;break;default:e=mn(10,t)}return this.x=pn(this.x*e)/e,this.y=pn(this.y*e)/e,this.width=pn(this.width*e)/e,this.height=pn(this.height*e)/e,this},scale:function(t,e,i){return i=this.origin().scale(t,e,i),this.x=i.x,this.y=i.y,this.width*=t,this.height*=e,this},sideNearestToPoint:function(t){var e=(t=new qi(t)).x-this.x,i=this.x+this.width-t.x,n=t.y-this.y,r=e,o="left";return in.x&&(n=t[e]);var a=[];for(e=0;eg.y||r>h.y&&r<=g.y){var d=h.x-n>g.x-n?h.x-n:g.x-n;0<=d&&(u.x=n+d,u.y=r,c.start=t,c.end=u,l.intersect(c)&&s++)}o=a}return s%2==1},close:function(){var t=this.start,e=this.end,i=this.points;return t&&e&&!t.equals(e)&&i.push(t.clone()),this},lengthPoints:function(){return this.points},convexHull:function(){return new wn(yn(this.points))},equals:function(t){if(!t)return!1;var e=this.points,i=t.points,n=e.length;if(i.length!==n)return!1;for(var r=0;rr+a&&n.x>r+a||i.xo+s&&n.y>o+s||i.ye.x&&t.ye.y}function Qn(t,e){return hr(t,e,{interior:!1})}function Kn(t,e){return gr(t,e,{interior:!1})}function qn(t,e){return dr(t,e,{interior:!1})}function $n(t,e){return pr(t,e,{interior:!1})}function tr(t,e){return hr(t,e,{interior:!0})}function er(t,e){return gr(t,e,{interior:!0})}function ir(t,e){return dr(t,e,{interior:!0})}function nr(t,e){return pr(t,e,{interior:!0})}function rr(t,e){return mr(t,e,{interior:!0})}function or(t,i,n){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?tr(e,i):Qn(e,i)})}function ar(t,i,n){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?er(e,i):Kn(e,i)})}function sr(t,e,i){return cr(t,Un.fromRect(e),i)}function lr(t,e,i){return fr(t,e,i,{interior:!1})}function cr(t,e,i){return fr(t,e,i,{interior:!0})}function ur(t,i,n,r){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?cr(i,e,r):lr(i,e,r)})}function hr(t,e,i){void 0===i&&(i={});var n,r=i.interior;if(void 0===r&&(r=!1),r){if(t.containsPoint(e.start))return!0;var o=t.start,a=t.end,s=t.points;n=a.equals(o)?s:s.concat([o])}else n=t.points;for(var l=n.length,c=new nn,u=0;u'+t+"";return L.parseXML(e,{async:!1}).documentElement}var i=document.createElementNS(T.svg,"svg");return i.setAttributeNS(T.xmlns,"xmlns:xlink",T.xlink),i.setAttribute("version","1.1"),i},L.createSVGStyle=function(t){return L("style",{type:"text/css"},[L.createCDATASection(t)]).node},L.createCDATASection=function(t){return void 0===t&&(t=""),document.implementation.createDocument(null,"xml",null).createCDATASection(t)},L.idCounter=0,L.uniqueId=function(){return"v-"+ ++L.idCounter},L.toNode=function(t){return L.isV(t)?t.node:t.nodeName&&t||t[0]},L.ensureId=function(t){return(t=L.toNode(t)).id||(t.id=L.uniqueId())},L.sanitizeText=function(t){return(t||"").replace(/ /g,"\xa0")},L.isUndefined=function(t){return void 0===t},L.isString=function(t){return"string"==typeof t},L.isObject=function(t){return t&&"object"==typeof t},L.isArray=Array.isArray,L.parseXML=function(t,e){var i;e=e||{};try{var n=new DOMParser;L.isUndefined(e.async)||(n.async=e.async),i=n.parseFromString(t,"text/xml")}catch(t){i=void 0}if(!i||i.getElementsByTagName("parsererror").length)throw new Error("Invalid XML: "+t);return i},L.qualifyAttr=function(t){if(-1===t.indexOf(":"))return{ns:null,local:t};var e=t.split(":");return{ns:T[e[0]],local:e[1]}},L.transformRegex=/(\w+)\(([^,)]+),?([^)]+)?\)/gi,L.transformSeparatorRegex=/[ ,]+/,L.transformationListRegex=/^(\w+)\((.*)\)/,L.transformStringToMatrix=function(t){var e=L.createSVGMatrix(),i=t&&t.match(L.transformRegex);if(!i)return e;for(var n=0,r=i.length;n=t.start&&et.start&&i<=t.end||t.start>=e&&t.end=e?t.end+=i:t.start>=e&&(t.start+=i,t.end+=i)}),t},L.convertLineToPathData=function(t){return["M",(t=L(t)).attr("x1"),t.attr("y1"),"L",t.attr("x2"),t.attr("y2")].join(" ")},L.convertPolygonToPathData=function(t){var e=L.getPointsFromSvgNode(t);return 0===e.length?null:L.svgPointsToPath(e)+" Z"},L.convertPolylineToPathData=function(t){var e=L.getPointsFromSvgNode(t);return 0===e.length?null:L.svgPointsToPath(e)},L.svgPointsToPath=function(t){for(var e=0,i=t.length;e=o[r]&&(a.push([e].concat(n.splice(0,o[r]))),o[r]););}),a}(t)),!t||!t.length)return[["M",0,0]];for(var e,i=[],n=0,r=0,o=0,a=0,s=t.length,l=0;lg){var M=N,S=l,k=c;f=t(l=y+n*V(N=b+g*(s&&bd)T=d-1;else if(void 0!==o){var L;if(void 0===m)m=(L="auto"===i.lineHeight?{value:1.5,unit:"em"}:Rr(i.lineHeight,["em"])||{value:1,unit:"em"}).value,"em"===L.unit&&(m*=l.getBBox().height);m*A.length>o&&(T=Math.floor(o/m)-1)}if(null!==T){A.splice(T+1);var z=n.ellipsis;if(!z||T<0)break;"string"!=typeof z&&(z="\u2026");var P=A[T];if(!P&&!N)break;var j,Z,G=P.length;do{if(Z=P[G],j=P.substring(0,G),Z?Z.match(u)&&(j+=Z):j+="string"==typeof u?u:" ",j+=z,c.data=j,s.getComputedTextLength()<=r){A[T]=j;break}G--}while(0<=G);break}}}return n.svgDocument?a.removeChild(l):document.body.removeChild(a),A.join(h)},Wr=function(t){var e=S(S.parseHTML("
"+t+"
",null,!1));return e.find("*").each(function(){var i=this;S.each(i.attributes,function(){var t=this.name,e=this.value;(t.startsWith("on")||e.startsWith("javascript:")||e.startsWith("data:")||e.startsWith("vbscript:"))&&S(i).removeAttr(t)})}),e.html()},Ur=function(t,e){if(window.navigator.msSaveBlob)window.navigator.msSaveBlob(t,e);else{var i=window.URL.createObjectURL(t),n=document.createElement("a");n.href=i,n.download=e,document.body.appendChild(n),n.click(),document.body.removeChild(n),window.URL.revokeObjectURL(i)}},Fr=function(t){t=t.replace(/\s/g,"");var e,i=(t=decodeURIComponent(t)).indexOf(","),n=t.slice(0,i),r=n.split(":")[1].split(";")[0],o=t.slice(i+1);e=0<=n.indexOf("base64")?atob(o):unescape(encodeURIComponent(o));for(var a=new Uint8Array(e.length),s=0;s')({color:t.color||"blue",opacity:Number.isFinite(t.opacity)?t.opacity:1,outerRadius:e+i,innerRadius:e})},highlight:function(t){return Kr('')({color:t.color||"red",width:Number.isFinite(t.width)?t.width:1,blur:Number.isFinite(t.blur)?t.blur:0,opacity:Number.isFinite(t.opacity)?t.opacity:1})},blur:function(t){var e=Number.isFinite(t.x)?t.x:2;return Kr('')({stdDeviation:Number.isFinite(t.y)?[e,t.y]:e})},dropShadow:function(t){var e="SVGFEDropShadowElement"in window?'':'';return Kr(e)({dx:t.dx||0,dy:t.dy||0,opacity:Number.isFinite(t.opacity)?t.opacity:1,color:t.color||"black",blur:Number.isFinite(t.blur)?t.blur:4})},grayscale:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({a:.2126+.7874*(1-e),b:.7152-.7152*(1-e),c:.0722-.0722*(1-e),d:.2126-.2126*(1-e),e:.7152+.2848*(1-e),f:.0722-.0722*(1-e),g:.2126-.2126*(1-e),h:.0722+.9278*(1-e)})},sepia:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({a:.393+.607*(1-e),b:.769-.769*(1-e),c:.189-.189*(1-e),d:.349-.349*(1-e),e:.686+.314*(1-e),f:.168-.168*(1-e),g:.272-.272*(1-e),h:.534-.534*(1-e),i:.131+.869*(1-e)})},saturate:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:1-e})},hueRotate:function(t){return Kr('')({angle:t.angle||0})},invert:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:e,amount2:1-e})},brightness:function(t){return Kr('')({amount:Number.isFinite(t.amount)?t.amount:1})},contrast:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:e,amount2:.5-e/2})}},Qr={number:function(t,e,o){o=o||{currency:["$",""],decimal:".",thousands:",",grouping:[3]};var i=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i.exec(t),n=i[1]||" ",r=i[2]||">",a=i[3]||"",s=i[4]||"",l=i[5],c=+i[6],u=i[7],h=i[8],g=i[9],d=1,f="",p="",m=!1;switch(h&&(h=+h.substring(1)),(l||"0"===n&&"="===r)&&(l=n="0",r="=",u&&(c-=Math.floor((c-1)/4))),g){case"n":u=!0,g="g";break;case"%":d=100,p="%",g="f";break;case"p":d=100,p="%",g="r";break;case"b":case"o":case"x":case"X":"#"===s&&(f="0"+g.toLowerCase());break;case"c":case"d":m=!0,h=0;break;case"s":d=-1,g="r"}"$"===s&&(f=o.currency[0],p=o.currency[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):"e"!=g&&"f"!=g||(h=Math.max(0,Math.min(20,h))));var I=l&&u;if(m&&e%1)return"";var v=e<0||0===e&&1/e<0?(e=-e,"-"):a,A=p;if(d<0){var C=this.prefix(e,h);e=C.scale(e),A=C.symbol+p}else e*=d;var y=(e=this.convert(g,e,h)).lastIndexOf("."),w=y<0?e:e.substring(0,y),b=y<0?"":o.decimal+e.substring(y+1);function N(t){for(var e=t.length,i=[],n=0,r=o.grouping[0];0"===r?M+v+e:"^"===r?M.substring(0,x>>=1)+v+e+M.substring(x):v+(I?e:M+e))+A},string:function(t,e){for(var i,n="{",r=!1,o=[];-1!==(i=t.indexOf(n));){var a,s,l;if(a=t.slice(0,i),r){l=(s=a.split(":")).shift().split("."),a=e;for(var c=0;c|\$\{ ?([^{} ]+) ?\}|\{\{([^{} ]+)\}\}/g;return function(n){return n=n||{},t.replace(e,function(t){for(var e=Array.from(arguments).slice(1,4).find(function(t){return!!t}).split("."),i=n[e.shift()];void 0!==i&&e.length;)i=i[e.shift()];return void 0!==i?i:""})}},qr=i.assign,$r=i.defaults,to=qr,eo=i.defaultsDeep,io=i.assign,no=i.defaults,ro=i.defaultsDeep,oo=i.invokeMap||i.invoke,ao=i.sortedIndexBy||i.sortedIndex,so=i.uniqBy||i.uniq,lo=i.clone,co=i.cloneDeep,uo=i.isEmpty,ho=i.isEqual,go=i.isFunction,fo=i.isPlainObject,po=i.toArray,mo=i.debounce,Io=i.groupBy,vo=i.sortBy,Ao=i.flattenDeep,Co=i.without,yo=i.difference,wo=i.intersection,bo=i.union,No=i.has,xo=i.result,Mo=i.omit,So=i.pick,ko=i.bindAll,Do=i.forIn,To=i.camelCase,Lo=i.uniqueId,zo=function(){if(i.mergeWith){var t=Array.from(arguments),e=t[t.length-1],n=go(e)?e:Bo;return t.push(function(t,e){var i=n(t,e);return void 0!==i?i:Array.isArray(t)&&!Array.isArray(e)?e:void 0}),i.mergeWith.apply(this,t)}return i.merge.apply(this,arguments)},Po=function(t){var e=Object.prototype.toString;return!0===t||!1===t||!!t&&"object"==typeof t&&"[object Boolean]"===e.call(t)},jo=function(t){return!!t&&("object"==typeof t||"function"==typeof t)},Zo=function(t){var e=Object.prototype.toString;return"number"==typeof t||!!t&&"object"==typeof t&&"[object Number]"===e.call(t)},Go=function(t){var e=Object.prototype.toString;return"string"==typeof t||!!t&&"object"==typeof t&&"[object String]"===e.call(t)},Bo=function(){};function Oo(t){t=so(t);var a=po(t).reduce(function(t,e){return t[e.id]=e.clone(),t},{});return po(t).forEach(function(t){var e=a[t.id];if(e.isLink()){var i=e.source(),n=e.target();i.id&&a[i.id]&&e.prop("source/id",a[i.id].id),n.id&&a[n.id]&&e.prop("target/id",a[n.id].id)}var r=t.get("parent");r&&a[r]&&e.set("parent",a[r].id);var o=po(t.get("embeds")).reduce(function(t,e){return a[e]&&t.push(a[e].id),t},[]);uo(o)||e.set("embeds",o)}),a}var Eo={x:"x",y:"y",width:"w",height:"h",minimum:"s",maximum:"l",diagonal:"d"},Ro=Object.keys(Eo).map(function(t){return Eo[t]}).join(""),Yo="[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?",Wo=/\s/g,Uo=new RegExp("^("+Yo+"\\*)?(["+Ro+"])([-+]{1,2}"+Yo+")?$","g");function Fo(t){throw new Error("Invalid calc() expression: "+t)}function Vo(t,e){var i=Uo.exec(t.replace(Wo,""));i||Fo(t),Uo.lastIndex=0;var n=i[1];void 0===n&&(n=1);var r=i[2],o=i[3];void 0===o&&(o=0);var a=e.x,s=e.y,l=e.width,c=e.height,u=0;switch(r){case Eo.width:u=l;break;case Eo.height:u=c;break;case Eo.x:u=a;break;case Eo.y:u=s;break;case Eo.minimum:u=Math.min(c,l);break;case Eo.maximum:u=Math.max(c,l);break;case Eo.diagonal:u=Math.sqrt(c*c+l*l)}return parseFloat(n)*u+function(t){if(!t)return 0;switch(t[0]){case"+":return parseFloat(t.substr(1));case"-":return-parseFloat(t.substr(1))}return parseFloat(t)}(o)}function Xo(t){return"string"==typeof t&&t.includes("calc")}var Ho="calc(",Jo=Ho.length;function _o(t,e){for(var i=t,n=0;;){var r=i.indexOf(Ho,n);if(-1===r)return i;var o=r+Jo,a=1;t:for(;;){switch(i[o]){case"(":a++;break;case")":if(0===--a)break t;break;case void 0:Fo(i)}o++}var s=i.slice(r+Jo,o);Xo(s)&&(s=_o(s,e));var l=String(Vo(s,e));i=i.slice(0,r)+l+i.slice(o+1),n=r+l.length}}function Qo(o,a){return function(t,e){var i=Er(t);t=parseFloat(t),i&&(t/=100);var n={};if(isFinite(t)){var r=i||0<=t&&t<=1?t*e[a]:Math.max(t+e[a],0);n[o]=r}return n}}function Ko(a,s,l){return function(t,e){var i,n=Er(t);if(t=parseFloat(t),n&&(t/=100),isFinite(t)){var r=e[l]();i=n||0e.width?sa:la)(t,e)})},refRCircumscribed:{set:function(t,e){var i=Er(t);t=parseFloat(t),i&&(t/=100);var n,r=Math.sqrt(e.height*e.height+e.width*e.width);return isFinite(t)&&(n=i||0<=t&&t<=1?t*r:Math.max(t+r,0)),{r:n}}},refCx:{set:Qo("cx","width")},refCy:{set:Qo("cy","height")},xAlignment:{offset:qo("x","width","right")},yAlignment:{offset:qo("y","height","bottom")},resetOffset:{offset:function(t,e){return t?{x:-e.x,y:-e.y}:{x:0,y:0}}},refDResetOffset:{set:ta({resetOffset:!0})},refDKeepOffset:{set:ta({resetOffset:!1})},refPointsResetOffset:{set:ea({resetOffset:!0})},refPointsKeepOffset:{set:ea({resetOffset:!1})},connection:{qualify:ra,set:function(t){var e,i=t.stubs;if(void 0===i&&(i=0),isFinite(i)&&0!==i){var n;n=i<0?(this.getConnectionLength()+i)/2:i;var r=this.getConnection(),o=r.divideAtLength(n),a=r.divideAtLength(-n);o&&a&&(e=o[0].serialize()+" "+a[1].serialize())}return{d:e||this.getSerializedConnection()}}},atConnectionLengthKeepGradient:{qualify:ra,set:ia("getTangentAtLength",{rotate:!0})},atConnectionLengthIgnoreGradient:{qualify:ra,set:ia("getTangentAtLength",{rotate:!1})},atConnectionRatioKeepGradient:{qualify:ra,set:ia("getTangentAtRatio",{rotate:!0})},atConnectionRatioIgnoreGradient:{qualify:ra,set:ia("getTangentAtRatio",{rotate:!1})}};["transform","d","points","width","height","cx","cy","r","rx","ry","x1","x2","y1","y2","x","y","dx","dy"].forEach(function(n){ca[n]={qualify:Xo,set:function(t,e){var i;return(i={})[n]=_o(t,e),i}}}),ca.refR=ca.refRInscribed,ca.refD=ca.refDResetOffset,ca.refPoints=ca.refPointsResetOffset,ca.atConnectionLength=ca.atConnectionLengthKeepGradient,ca.atConnectionRatio=ca.atConnectionRatioKeepGradient,ca.refX2=ca.refX,ca.refY2=ca.refY,ca.refWidth2=ca.refWidth,ca.refHeight2=ca.refHeight,ca["ref-x"]=ca.refX,ca["ref-y"]=ca.refY,ca["ref-dy"]=ca.refDy,ca["ref-dx"]=ca.refDx,ca["ref-width"]=ca.refWidth,ca["ref-height"]=ca.refHeight,ca["x-alignment"]=ca.xAlignment,ca["y-alignment"]=ca.yAlignment;var ua=ca,ha=s.Model.extend({constructor:function(t,e){var i,n=t||{};"function"==typeof this.preinitialize&&this.preinitialize.apply(this,arguments),this.cid=Lo("c"),this.attributes={},e&&e.collection&&(this.collection=e.collection),e&&e.parse&&(n=this.parse(n,e)||{}),(i=xo(this,"defaults"))&&(n=zo({},i,n)),this.set(n,e),this.changed={},this.initialize.apply(this,arguments)},translate:function(t,e,i){throw new Error("Must define a translate() method.")},toJSON:function(){var e=xo(this.constructor.prototype,"defaults").attrs||{},t=this.attributes.attrs,o={};Do(t,function(t,n){var r=e[n];Do(t,function(t,i){jo(t)&&!Array.isArray(t)?Do(t,function(t,e){r&&r[i]&&ho(r[i][e],t)||(o[n]=o[n]||{},(o[n][i]||(o[n][i]={}))[e]=t)}):r&&ho(r[i],t)||(o[n]=o[n]||{},o[n][i]=t)})});var i=co(Mo(this.attributes,"attrs"));return i.attrs=o,i},initialize:function(t){t&&t.id||this.set("id",this.generateId(),{silent:!0}),this._transitionIds={},this.processPorts(),this.on("change:attrs",this.processPorts,this)},generateId:function(){return Pr()},processPorts:function(){var t=this.ports,i={};Do(this.get("attrs"),function(t,e){t&&t.port&&(void 0!==t.port.id?i[t.port.id]=t.port:i[t.port]={id:t.port})});var n={};(Do(t,function(t,e){i[e]||(n[e]=!0)}),this.graph&&!uo(n))&&(this.graph.getConnectedLinks(this,{inbound:!0}).forEach(function(t){n[t.get("target").port]&&t.remove()}),this.graph.getConnectedLinks(this,{outbound:!0}).forEach(function(t){n[t.get("source").port]&&t.remove()}));this.ports=i},remove:function(t){void 0===t&&(t={});var e=this.graph,i=this.collection;if(!e)return i&&i.remove(this,t),this;e.startBatch("remove");var n=this.getParentCell();n&&n.unembed(this,t);for(var r=this.getEmbeddedCells(),o=0,a=r.length;oc[2]?(a=".3em",r=u,o=0,"start"):lh[2]?(a=".3em",r=-c,o=0,"end"):l','','','','','','',''].join(""),toolMarkup:['','','','',"Remove link.","",'','','',"Link options.","",""].join(""),doubleToolMarkup:void 0,vertexMarkup:['','','','',"Remove vertex.","",""].join(""),arrowheadMarkup:['','',""].join(""),defaultLabel:void 0,labelMarkup:void 0,_builtins:{defaultLabel:{markup:[{tagName:"rect",selector:"rect"},{tagName:"text",selector:"text"}],attrs:{text:{fill:"#000000",fontSize:14,textAnchor:"middle",yAlignment:"middle",pointerEvents:"none"},rect:{ref:"text",fill:"#ffffff",rx:3,ry:3,refWidth:1,refHeight:1,refX:0,refY:0}},position:{distance:.5}}},defaults:{type:"link",source:{},target:{}},isLink:function(){return!0},disconnect:function(t){return this.set({source:{x:0,y:0},target:{x:0,y:0}},t)},source:function(t,e,i){return void 0===t?lo(this.get("source")):(r=t instanceof ha?((n=lo(e)||{}).id=t.id,i):fo(t)?(n=t,e):((n=lo(e)||{}).x=t.x,n.y=t.y,i),this.set("source",n,r));var n,r},target:function(t,e,i){return void 0===t?lo(this.get("target")):(r=t instanceof ha?((n=lo(e)||{}).id=t.id,i):fo(t)?(n=t,e):((n=lo(e)||{}).x=t.x,n.y=t.y,i),this.set("target",n,r));var n,r},router:function(t,e,i){if(void 0===t){var n=this.get("router");return n?"object"==typeof n?lo(n):n:this.get("manhattan")?{name:"orthogonal"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},a=r?e:i;return this.set("router",o,a)},connector:function(t,e,i){if(void 0===t){var n=this.get("connector");return n?"object"==typeof n?lo(n):n:this.get("smooth")?{name:"smooth"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},a=r?e:i;return this.set("connector",o,a)},label:function(t,e,i){var n=this.labels();return(t=isFinite(t)&&null!==t?0|t:0)<0&&(t=n.length+t),arguments.length<=1?this.prop(["labels",t]):this.prop(["labels",t],e,i)},labels:function(t,e){return 0===arguments.length?(t=this.get("labels"),Array.isArray(t)?t.slice():[]):(Array.isArray(t)||(t=[]),this.set("labels",t,e))},hasLabels:function(){var t=this.attributes.labels;return Array.isArray(t)&&0e[1].x?"right":"left";switch(n){case"left":return new qi(-1,0);case"right":default:return new qi(1,0)}}(t,e);case Ra.VERTICAL:return function(t,e,i){var n,r=t.sourceBBox;n=r.width&&r.height?r.sideNearestToPoint(e[0]):r.y>e[1].y?"bottom":"top";switch(n){case"top":return new qi(0,-1);case"bottom":default:return new qi(0,1)}}(t,e);case Ra.CLOSEST_POINT:return Va(t,e,n);case Ra.OUTWARDS:return Ha(t,e,n);case Ra.AUTO:default:return Ua(t,e,n)}}(r,h,a,l),d=h[0].distance(h[1])*l.coeff,f=Qa(g,h[1].difference(h[0]).normalize());if(f>Math.PI/4){var p=d+(f-Math.PI/4)*l.angleTangentCoefficient;c=g.clone().scale(p,p)}else c=g.clone().scale(d,d)}if(l.targetTangent)u=l.targetTangent;else{var m=function(t,e,i,n){if(n.targetDirection)switch(n.targetDirection){case Ya.UP:return new qi(0,-1);case Ya.DOWN:return new qi(0,1);case Ya.LEFT:return new qi(-1,0);case Ya.RIGHT:return new qi(0,1);case Ya.AUTO:return Fa(t,e,n);case Ya.CLOSEST_POINT:return Xa(t,e,n);case Ya.OUTWARDS:return Ja(t,e,n);default:return n.targetDirection}switch(i){case Ra.HORIZONTAL:return function(t,e,i){var n,r=t.targetBBox;n=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.x>e[e.length-2].x?"left":"right";switch(n){case"left":return new qi(-1,0);case"right":default:return new qi(1,0)}}(t,e);case Ra.VERTICAL:return function(t,e,i){var n,r=t.targetBBox;n=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.y>e[e.length-2].y?"top":"bottom";switch(n){case"top":return new qi(0,-1);case"bottom":default:return new qi(0,1)}}(t,e);case Ra.CLOSEST_POINT:return Xa(t,e,n);case Ra.OUTWARDS:return Ja(t,e,n);case Ra.AUTO:default:return Fa(t,e,n)}}(r,h,a,l),I=h.length-1,v=h[I-1].distance(h[I])*l.coeff,A=Qa(m,h[I-1].difference(h[I]).normalize());if(A>Math.PI/4){var C=v+(A-Math.PI/4)*l.angleTangentCoefficient;u=m.clone().scale(C,C)}else u=m.clone().scale(v,v)}var y=function(t,e,i,n){for(var r=n.tau,o=n.coeff,a=[],s=[],l=[],c=t.length-1,u=0;u=Math.abs(t.y-e.y)){var c=(t.x+e.x)/2;l=Tn.createSegment("C",c,t.y,c,e.y,e.x,e.y),r.appendSegment(l)}else{var u=(t.y+e.y)/2;l=Tn.createSegment("C",t.x,u,e.x,u,e.x,e.y),r.appendSegment(l)}}return o?r:r.serialize()},curve:Wa},$a={},ts=s.View.extend({options:{},theme:null,themeClassNamePrefix:Nr("theme-"),requireSetThemeOverride:!1,defaultTheme:br.defaultTheme,children:null,childNodes:null,DETACHABLE:!0,UPDATE_PRIORITY:2,FLAG_INSERT:1<<30,FLAG_REMOVE:1<<29,constructor:function(t){this.requireSetThemeOverride=t&&!!t.theme,this.options=io({},this.options,t),s.View.call(this,t)},initialize:function(){($a[this.cid]=this).setTheme(this.options.theme||this.defaultTheme),this.init()},unmount:function(){this.svgElement?this.vel.remove():this.$el.remove()},renderChildren:function(t){if(t||(t=xo(this,"children")),t){var e=this.svgElement,i=wr.namespace[e?"svg":"xhtml"],n=Mr(t,i);(e?this.vel:this.$el).empty().append(n.fragment),this.childNodes=n.selectors}return this},findAttribute:function(t,e){for(var i=e;i&&1===i.nodeType;){var n=i.getAttribute(t);if(n)return n;if(i===this.el)return null;i=i.parentNode}return null},_ensureElement:function(){if(this.el)this.setElement(xo(this,"el"));else{var t=xo(this,"tagName"),e=io({},xo(this,"attributes")),i=io({},xo(this,"style"));this.id&&(e.id=xo(this,"id")),this.setElement(this._createElement(t)),this._setAttributes(e),this._setStyle(i)}this._ensureElClassName()},_setAttributes:function(t){this.svgElement?this.vel.attr(t):this.$el.attr(t)},_setStyle:function(t){this.$el.css(t)},_createElement:function(t){return this.svgElement?document.createElementNS(wr.namespace.svg,t):document.createElement(t)},_setElement:function(t){this.$el=t instanceof s.$?t:s.$(t),this.el=this.$el[0],this.svgElement&&(this.vel=wr(this.el))},_ensureElClassName:function(){var t=xo(this,"className");if(t){var e=Nr(t);this.svgElement?this.vel.removeClass(t).addClass(e):this.$el.removeClass(t).addClass(e)}},init:function(){},onRender:function(){},confirmUpdate:function(){return 0},setTheme:function(t,e){return e=e||{},this.theme&&this.requireSetThemeOverride&&!e.override||(this.removeThemeClassName(),this.addThemeClassName(t),this.onSetTheme(this.theme,t),this.theme=t),this},addThemeClassName:function(t){if(!(t=t||this.theme))return this;var e=this.themeClassNamePrefix+t;return this.svgElement?this.vel.addClass(e):this.$el.addClass(e),this},removeThemeClassName:function(t){t=t||this.theme;var e=this.themeClassNamePrefix+t;return this.svgElement?this.vel.removeClass(e):this.$el.removeClass(e),this},onSetTheme:function(t,e){},remove:function(){return this.onRemove(),this.undelegateDocumentEvents(),$a[this.cid]=null,s.View.prototype.remove.apply(this,arguments),this},onRemove:function(){},getEventNamespace:function(){return".joint-event-ns-"+this.cid},delegateElementEvents:function(t,e,i){if(!e)return this;i||(i={});var n=this.getEventNamespace();for(var r in e){var o=e[r];"function"!=typeof o&&(o=this[o]),o&&S(t).on(r+n,i,o.bind(this))}return this},undelegateElementEvents:function(t){return S(t).off(this.getEventNamespace()),this},delegateDocumentEvents:function(t,e){return t||(t=xo(this,"documentEvents")),this.delegateElementEvents(document,t,e)},undelegateDocumentEvents:function(){return this.undelegateElementEvents(document)},eventData:function(t,e){if(!t)throw new Error("eventData(): event object required.");var i=t.data,n="__"+this.cid+"__";return void 0===e?i&&i[n]||{}:(i||(i=t.data={}),i[n]||(i[n]={}),io(i[n],e),this)},stopPropagation:function(t){return this.eventData(t,{propagationStopped:!0}),this},isPropagationStopped:function(t){return!!this.eventData(t).propagationStopped}},{extend:function(){var t=Array.from(arguments),e=t[0]&&io({},t[0])||{},i=t[1]&&io({},t[1])||{},n=e.render||this.prototype&&this.prototype.render||null;return e.render=function(){return"function"==typeof n&&n.apply(this,arguments),this.render.__render__===n&&this.onRender(),this},e.render.__render__=n,s.View.extend.call(this,e,i)}});if(S.event&&!("dbltap"in S.event.special)){var es=br.doubleTapInterval;S.event.special.dbltap={bindType:"touchend",delegateType:"touchend",handle:function(t){for(var e,i=[],n=arguments.length-1;0e.y?"N":"S":t.y===e.y?t.x>e.x?"W":"E":null}function Ds(t){return new In(t.x,t.y,0,0)}function Ts(t){var e=Xr(t.padding||t.elementPadding||20);return{x:-e.left,y:-e.top,width:e.left+e.right,height:e.top+e.bottom}}function Ls(t,e){return t.sourceBBox.clone().moveAndExpand(Ts(e))}function zs(t,e){return t.targetBBox.clone().moveAndExpand(Ts(e))}function Ps(t,e,i){var n=new qi(t.x,e.y),r=new qi(e.x,t.y),o=ks(t,n),a=ks(t,r),s=Ns[i],l=o===i||o!==s&&(a===s||a!==i)?n:r;return{points:[l],direction:ks(l,e)}}function js(t,e,i){var n=Ms(t,e,i);return{points:[n],direction:ks(n,e)}}function Zs(e,t,i,n){var r,o={},a=[new qi(e.x,t.y),new qi(t.x,e.y)],s=a.filter(function(t){return!i.containsPoint(t)}),l=s.filter(function(t){return ks(t,e)!==n});if(0c.center().distance(t),h=u?e:t,g=u?t:e;return a=Ms(o=r?(o=qi.fromPolar(c.width+c.height,xs[r],h),c.pointNearestToPoint(o).move(o,-1)):c.pointNearestToPoint(h).move(h,1),g,c),o.round().equals(a.round())?(a=qi.fromPolar(c.width+c.height,Pi(o.theta(h))+Math.PI/2,g),s=Ms(o,a=c.pointNearestToPoint(a).move(g,1).round(),c),l.points=u?[a,s,o]:[o,s,a]):l.points=u?[a,o]:[o,a],l.direction=ks(u?o:a,e),l}function Os(t,e,i){var n,r,o,a,s,l=Ls(i,e),c=zs(i,e),u=(r=e,(n=i).sourceAnchor?n.sourceAnchor:Ls(n,r).center()),h=(a=e,(o=i).targetAnchor?o.targetAnchor:zs(o,a).center());l=l.union(Ds(u)),c=c.union(Ds(h)),(t=po(t).map(qi)).unshift(u),t.push(h);for(var g=[],d=0,f=t.length-1;dn.maxAllowedDirectionChange)){var X=_s(O.clone().offset(k.gridOffsetX,k.gridOffsetY),A,l),H=Qs(X);if(!C.isClose(H)&&!i(X)){if(0<=j.indexOf(H))if(!X.equals(d))if(Xs(V,Vs(X,d,P,A,n))>n.maxAllowedDirectionChange)continue;var J=R+k.cost+(W?0:n.penalties[D]);(!C.isOpen(H)||J "+e),i=this.getSelector(t.parentNode,i)}return i},addLinkFromMagnet:function(t,e,i){var n=this.paper,r=n.model,o=n.getDefaultLink(this,t);return o.set({source:this.getLinkEnd(t,e,i,o,"source"),target:{x:e,y:i}}).addTo(r,{async:!1,ui:!0}),o.findView(n)},getLinkEnd:function(t){for(var e,i=[],n=arguments.length-1;0r.options.clickThreshold||this.notify("element:magnet:pointerclick",t,e,i,n)}},{Flags:Gl});io(Bl.prototype,Da);var Ol={RENDER:"RENDER",UPDATE:"UPDATE",TOOLS:"TOOLS",LEGACY_TOOLS:"LEGACY_TOOLS",LABELS:"LABELS",VERTICES:"VERTICES",SOURCE:"SOURCE",TARGET:"TARGET",CONNECTOR:"CONNECTOR"},El=Zl.extend({className:function(){var t=Zl.prototype.className.apply(this).split(" ");return t.push("link"),t.join(" ")},options:{shortLinkLength:105,doubleLinkTools:!1,longLinkLength:155,linkToolsOffset:40,doubleLinkToolsOffset:65,sampleInterval:50},_labelCache:null,_labelSelectors:null,_markerCache:null,_V:null,_dragData:null,metrics:null,decimalsRounding:2,initialize:function(){Zl.prototype.initialize.apply(this,arguments),this._labelCache={},this._labelSelectors={},this._markerCache={},this._V={},this.cleanNodesCache()},presentationAttributes:{markup:[Ol.RENDER],attrs:[Ol.UPDATE],router:[Ol.UPDATE],connector:[Ol.CONNECTOR],smooth:[Ol.UPDATE],manhattan:[Ol.UPDATE],toolMarkup:[Ol.LEGACY_TOOLS],labels:[Ol.LABELS],labelMarkup:[Ol.LABELS],vertices:[Ol.VERTICES,Ol.UPDATE],vertexMarkup:[Ol.VERTICES],source:[Ol.SOURCE,Ol.UPDATE],target:[Ol.TARGET,Ol.UPDATE]},initFlag:[Ol.RENDER,Ol.SOURCE,Ol.TARGET,Ol.TOOLS],UPDATE_PRIORITY:1,confirmUpdate:function(t,e){if(e||(e={}),this.hasFlag(t,Ol.SOURCE)){if(!this.updateEndProperties("source"))return t;t=this.removeFlag(t,Ol.SOURCE)}if(this.hasFlag(t,Ol.TARGET)){if(!this.updateEndProperties("target"))return t;t=this.removeFlag(t,Ol.TARGET)}var i=this.paper,n=this.sourceView,r=this.targetView;if(i&&(n&&!i.isViewMounted(n)||r&&!i.isViewMounted(r)))return t;if(this.hasFlag(t,Ol.RENDER))return this.render(),this.updateHighlighters(!0),this.updateTools(e),t=this.removeFlag(t,[Ol.RENDER,Ol.UPDATE,Ol.VERTICES,Ol.LABELS,Ol.TOOLS,Ol.LEGACY_TOOLS,Ol.CONNECTOR]);var o=!1;this.hasFlag(t,Ol.VERTICES)&&(this.renderVertexMarkers(),t=this.removeFlag(t,Ol.VERTICES));var a=this.model,s=a.attributes,l=this.hasFlag(t,Ol.LABELS),c=this.hasFlag(t,Ol.LEGACY_TOOLS);l&&(this.onLabelsChange(a,s.labels,e),t=this.removeFlag(t,Ol.LABELS),o=!0),c&&(this.renderTools(),t=this.removeFlag(t,Ol.LEGACY_TOOLS));var u=this.hasFlag(t,Ol.UPDATE),h=this.hasFlag(t,Ol.CONNECTOR);return(u||h)&&(u?e.translateBy&&a.isRelationshipEmbeddedIn(e.translateBy)?this.translate(e.tx,e.ty):this.update():(this.updatePath(),this.updateDOM()),this.updateTools(e),t=this.removeFlag(t,[Ol.UPDATE,Ol.TOOLS,Ol.CONNECTOR]),o=!(c=l=!1)),l&&this.updateLabelPositions(),c&&this.updateToolsPosition(),o&&this.updateHighlighters(),this.hasFlag(t,Ol.TOOLS)&&(this.updateTools(e),t=this.removeFlag(t,Ol.TOOLS)),t},requestConnectionUpdate:function(t){this.requestUpdate(this.getFlag(Ol.UPDATE),t)},isLabelsRenderRequired:function(t){void 0===t&&(t={});var e=this.model.previous("labels");if(!e)return!0;if("propertyPathArray"in t&&"propertyValue"in t){var i=t.propertyPathArray||[],n=i.length;if(1=this.options.longLinkLength){var r=this.options.doubleLinkToolsOffset||e;n=this.getPointAtLength(i-r),this._tool2Cache.attr("transform","translate("+n.x+", "+n.y+") "+t),this._tool2Cache.attr("visibility","visible")}else this.options.doubleLinkTools&&this._tool2Cache.attr("visibility","hidden")}return this},updateArrowheadMarkers:function(){if(!this._V.markerArrowheads)return this;if("none"===S.css(this._V.markerArrowheads.node,"display"))return this;var t=this.getConnectionLength()'}),Kl=Bl.extend({presentationAttributes:Bl.addPresentationAttributes({attrs:["SCALE"]}),confirmUpdate:function(){var t=Bl.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"SCALE")&&(this.resize(),t=this.removeFlag(t,"SCALE")),t}}),ql=_l.define("basic.Text",{attrs:{text:{"font-size":18,fill:"#000000"}}},{markup:''}),$l=_l.define("basic.Circle",{size:{width:60,height:60},attrs:{circle:{fill:"#ffffff",stroke:"#000000",r:30,cx:30,cy:30},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),tc=_l.define("basic.Ellipse",{size:{width:60,height:40},attrs:{ellipse:{fill:"#ffffff",stroke:"#000000",rx:30,ry:20,cx:30,cy:20},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),ec=_l.define("basic.Polygon",{size:{width:60,height:40},attrs:{polygon:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),ic=_l.define("basic.Polyline",{size:{width:60,height:40},attrs:{polyline:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),nc=_l.define("basic.Image",{attrs:{text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),rc=_l.define("basic.Path",{size:{width:60,height:60},attrs:{path:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle",ref:"path","ref-x":.5,"ref-dy":10,fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),oc=rc.define("basic.Rhombus",{attrs:{path:{d:"M 30 0 L 60 30 30 60 0 30 z"},text:{"ref-y":.5,"ref-dy":null,"y-alignment":"middle"}}}),ac=Tl.test("svgforeignobject"),sc=_l.define("basic.TextBlock",{attrs:{rect:{fill:"#ffffff",stroke:"#000000",width:80,height:100},text:{fill:"#000000","font-size":14,"font-family":"Arial, helvetica, sans-serif"},".content":{text:"","ref-x":.5,"ref-y":.5,"y-alignment":"middle","x-alignment":"middle"}},content:""},{markup:['','',ac?'
':'',""].join(""),initialize:function(){this.listenTo(this,"change:size",this.updateSize),this.listenTo(this,"change:content",this.updateContent),this.updateSize(this,this.get("size")),this.updateContent(this,this.get("content")),_l.prototype.initialize.apply(this,arguments)},updateSize:function(t,e){this.attr({".fobj":io({},e),div:{style:io({},e)}})},updateContent:function(t,e){ac?this.attr({".content":{html:Wr(e)}}):this.attr({".content":{text:e}})},setForeignObjectSize:function(){this.updateSize.apply(this,arguments)},setDivContent:function(){this.updateContent.apply(this,arguments)}}),lc=Bl.extend({presentationAttributes:ac?Bl.prototype.presentationAttributes:Bl.addPresentationAttributes({content:["CONTENT"],size:["CONTENT"]}),initFlag:["RENDER","CONTENT"],confirmUpdate:function(){var t=Bl.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"CONTENT")&&(this.updateContent(this.model),t=this.removeFlag(t,"CONTENT")),t},update:function(t,e){var i=this.model;if(ac)Bl.prototype.update.call(this,i,e);else{var n=Mo(e||i.get("attrs"),".content");Bl.prototype.update.call(this,i,n),e&&!No(e,".content")||this.updateContent(i,e)}},updateContent:function(t,e){var i=zo({},(e||t.get("attrs"))[".content"]);i=Mo(i,"text");var n=Yr(t.get("content"),t.get("size"),i,{svgDocument:this.paper.svg}),r=Tr({},".content",i,"/");r[".content"].text=n,Bl.prototype.update.call(this,t,r)}}),cc={Generic:_l,Rect:Ql,TextView:Kl,Text:ql,Circle:$l,Ellipse:tc,Polygon:ec,Polyline:ic,Image:nc,Path:rc,Rhombus:oc,TextBlock:sc,TextBlockView:lc},uc=Ta.define("standard.Rectangle",{attrs:{body:{refWidth:"100%",refHeight:"100%",strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"text",selector:"label"}]}),hc=Ta.define("standard.Circle",{attrs:{body:{refCx:"50%",refCy:"50%",refR:"50%",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"circle",selector:"body"},{tagName:"text",selector:"label"}]}),gc=Ta.define("standard.Ellipse",{attrs:{body:{refCx:"50%",refCy:"50%",refRx:"50%",refRy:"50%",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"ellipse",selector:"body"},{tagName:"text",selector:"label"}]}),dc=Ta.define("standard.Path",{attrs:{body:{refD:"M 0 0 L 10 0 10 10 0 10 Z",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"path",selector:"body"},{tagName:"text",selector:"label"}]}),fc=Ta.define("standard.Polygon",{attrs:{body:{refPoints:"0 0 10 0 10 10 0 10",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"polygon",selector:"body"},{tagName:"text",selector:"label"}]}),pc=Ta.define("standard.Polyline",{attrs:{body:{refPoints:"0 0 10 0 10 10 0 10 0 0",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"polyline",selector:"body"},{tagName:"text",selector:"label"}]}),mc=Ta.define("standard.Image",{attrs:{image:{refWidth:"100%",refHeight:"100%"},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"image",selector:"image"},{tagName:"text",selector:"label"}]}),Ic=Ta.define("standard.BorderedImage",{attrs:{border:{refWidth:"100%",refHeight:"100%",stroke:"#333333",strokeWidth:2},background:{refWidth:-1,refHeight:-1,x:.5,y:.5,fill:"#FFFFFF"},image:{refWidth:-1,refHeight:-1,x:.5,y:.5},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"background",attributes:{stroke:"none"}},{tagName:"image",selector:"image"},{tagName:"rect",selector:"border",attributes:{fill:"none"}},{tagName:"text",selector:"label"}]}),vc=Ta.define("standard.EmbeddedImage",{attrs:{body:{refWidth:"100%",refHeight:"100%",stroke:"#333333",fill:"#FFFFFF",strokeWidth:2},image:{refWidth:"30%",refHeight:-20,x:10,y:10,preserveAspectRatio:"xMidYMin"},label:{textVerticalAnchor:"top",textAnchor:"left",refX:"30%",refX2:20,refY:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"image",selector:"image"},{tagName:"text",selector:"label"}]}),Ac=Ta.define("standard.InscribedImage",{attrs:{border:{refRx:"50%",refRy:"50%",refCx:"50%",refCy:"50%",stroke:"#333333",strokeWidth:2},background:{refRx:"50%",refRy:"50%",refCx:"50%",refCy:"50%",fill:"#FFFFFF"},image:{refWidth:"68%",refHeight:"68%",refX:"16%",refY:"16%",preserveAspectRatio:"xMidYMid"},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"ellipse",selector:"background"},{tagName:"image",selector:"image"},{tagName:"ellipse",selector:"border",attributes:{fill:"none"}},{tagName:"text",selector:"label"}]}),Cc=Ta.define("standard.HeaderedRectangle",{attrs:{body:{refWidth:"100%",refHeight:"100%",strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},header:{refWidth:"100%",height:30,strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},headerText:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:15,fontSize:16,fill:"#333333"},bodyText:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",refY2:15,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"rect",selector:"header"},{tagName:"text",selector:"headerText"},{tagName:"text",selector:"bodyText"}]}),yc=Ta.define("standard.Cylinder",{attrs:{body:{lateralArea:10,fill:"#FFFFFF",stroke:"#333333",strokeWidth:2},top:{refCx:"50%",cy:10,refRx:"50%",ry:10,fill:"#FFFFFF",stroke:"#333333",strokeWidth:2},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"100%",refY2:15,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"path",selector:"body"},{tagName:"ellipse",selector:"top"},{tagName:"text",selector:"label"}],topRy:function(t,e){if(void 0===t)return this.attr("body/lateralArea");var i={lateralArea:t},n=Er(t)?{refCy:t,refRy:t,cy:null,ry:null}:{refCy:null,refRy:null,cy:t,ry:t};return this.attr({body:i,top:n},e)}},{attributes:{lateralArea:{set:function(t,e){var i=Er(t);i&&(t=parseFloat(t)/100);var n=e.x,r=e.y,o=e.width,a=e.height,s=o/2,l=i?a*t:t,c=wr.KAPPA,u=c*s,h=c*(i?a*t:t),g=n+o/2,d=n+o,f=r+l,p=f-l,m=r+a-l,I=r+a;return{d:["M",n,f,"L",n,m,"C",n,m+h,g-u,I,g,I,"C",g+u,I,d,m+h,d,m,"L",d,f,"C",d,f-h,g+u,p,g,p,"C",g-u,p,n,f-h,n,f,"Z"].join(" ")}}}}}),wc=Tl.test("svgforeignobject")?{tagName:"foreignObject",selector:"foreignObject",attributes:{overflow:"hidden"},children:[{tagName:"div",namespaceURI:"http://www.w3.org/1999/xhtml",selector:"label",style:{width:"100%",height:"100%",position:"static",backgroundColor:"transparent",textAlign:"center",margin:0,padding:"0px 5px",boxSizing:"border-box",display:"flex",alignItems:"center",justifyContent:"center"}}]}:{tagName:"text",selector:"label",attributes:{"text-anchor":"middle"}},bc={Rectangle:uc,Circle:hc,Ellipse:gc,Path:dc,Polygon:fc,Polyline:pc,Image:mc,BorderedImage:Ic,EmbeddedImage:vc,InscribedImage:Ac,HeaderedRectangle:Cc,Cylinder:yc,TextBlock:Ta.define("standard.TextBlock",{attrs:{body:{refWidth:"100%",refHeight:"100%",stroke:"#333333",fill:"#ffffff",strokeWidth:2},foreignObject:{refWidth:"100%",refHeight:"100%"},label:{style:{fontSize:14}}}},{markup:[{tagName:"rect",selector:"body"},wc]},{attributes:{text:{set:function(t,e,i,n){if(!(i instanceof HTMLElement)){var r=n.style||{},o={text:t,width:-5,height:"100%"},a=io({textVerticalAnchor:"middle"},r);return ua.textWrap.set.call(this,o,e,i,a),{fill:r.color||null}}i.textContent=t},position:function(t,e,i){if(i instanceof SVGElement)return e.center()}}}}),Link:Ma.define("standard.Link",{attrs:{line:{connection:!0,stroke:"#333333",strokeWidth:2,strokeLinejoin:"round",targetMarker:{type:"path",d:"M 10 -5 0 0 10 5 z"}},wrapper:{connection:!0,strokeWidth:10,strokeLinejoin:"round"}}},{markup:[{tagName:"path",selector:"wrapper",attributes:{fill:"none",cursor:"pointer",stroke:"transparent","stroke-linecap":"round"}},{tagName:"path",selector:"line",attributes:{fill:"none","pointer-events":"none"}}]}),DoubleLink:Ma.define("standard.DoubleLink",{attrs:{line:{connection:!0,stroke:"#DDDDDD",strokeWidth:4,strokeLinejoin:"round",targetMarker:{type:"path",stroke:"#000000",d:"M 10 -3 10 -10 -2 0 10 10 10 3"}},outline:{connection:!0,stroke:"#000000",strokeWidth:6,strokeLinejoin:"round"}}},{markup:[{tagName:"path",selector:"outline",attributes:{fill:"none"}},{tagName:"path",selector:"line",attributes:{fill:"none"}}]}),ShadowLink:Ma.define("standard.ShadowLink",{attrs:{line:{connection:!0,stroke:"#FF0000",strokeWidth:20,strokeLinejoin:"round",targetMarker:{type:"path",stroke:"none",d:"M 0 -10 -10 0 0 10 z"},sourceMarker:{type:"path",stroke:"none",d:"M -10 -10 0 0 -10 10 0 10 0 -10 z"}},shadow:{connection:!0,refX:3,refY:6,stroke:"#000000",strokeOpacity:.2,strokeWidth:20,strokeLinejoin:"round",targetMarker:{type:"path",d:"M 0 -10 -10 0 0 10 z",stroke:"none"},sourceMarker:{type:"path",stroke:"none",d:"M -10 -10 0 0 -10 10 0 10 0 -10 z"}}}},{markup:[{tagName:"path",selector:"shadow",attributes:{fill:"none"}},{tagName:"path",selector:"line",attributes:{fill:"none"}}]})},Nc=_l.define("devs.Model",{inPorts:[],outPorts:[],size:{width:80,height:80},attrs:{".":{magnet:!1},".label":{text:"Model","ref-x":.5,"ref-y":10,"font-size":18,"text-anchor":"middle",fill:"#000"},".body":{"ref-width":"100%","ref-height":"100%",stroke:"#000"}},ports:{groups:{in:{position:{name:"left"},attrs:{".port-label":{fill:"#000"},".port-body":{fill:"#fff",stroke:"#000",r:10,magnet:!0}},label:{position:{name:"left",args:{y:10}}}},out:{position:{name:"right"},attrs:{".port-label":{fill:"#000"},".port-body":{fill:"#fff",stroke:"#000",r:10,magnet:!0}},label:{position:{name:"right",args:{y:10}}}}}}},{markup:'',portMarkup:'',portLabelMarkup:'',initialize:function(){_l.prototype.initialize.apply(this,arguments),this.on("change:inPorts change:outPorts",this.updatePortItems,this),this.updatePortItems()},updatePortItems:function(t,e,i){var n=so(this.get("inPorts")),r=yo(so(this.get("outPorts")),n),o=this.createPortItems("in",n),a=this.createPortItems("out",r);this.prop("ports/items",o.concat(a),io({rewrite:!0},i))},createPortItem:function(t,e){return{id:e,group:t,attrs:{".port-label":{text:e}}}},createPortItems:function(t,e){return po(e).map(this.createPortItem.bind(this,t))},_addGroupPort:function(t,e,i){var n=this.get(e);return this.set(e,Array.isArray(n)?n.concat(t):[t],i)},addOutPort:function(t,e){return this._addGroupPort(t,"outPorts",e)},addInPort:function(t,e){return this._addGroupPort(t,"inPorts",e)},_removeGroupPort:function(t,e,i){return this.set(e,Co(this.get(e),t),i)},removeOutPort:function(t,e){return this._removeGroupPort(t,"outPorts",e)},removeInPort:function(t,e){return this._removeGroupPort(t,"inPorts",e)},_changeGroup:function(t,e,i){return this.prop("ports/groups/"+t,jo(e)?e:{},i)},changeInGroup:function(t,e){return this._changeGroup("in",t,e)},changeOutGroup:function(t,e){return this._changeGroup("out",t,e)}}),xc=Nc.define("devs.Atomic",{size:{width:80,height:80},attrs:{".label":{text:"Atomic"}}}),Mc=Nc.define("devs.Coupled",{size:{width:200,height:300},attrs:{".label":{text:"Coupled"}}}),Sc={Model:Nc,Atomic:xc,Coupled:Mc,Link:Ma.define("devs.Link",{attrs:{".connection":{"stroke-width":2}}})},kc=_l.define("logic.Gate",{size:{width:80,height:40},attrs:{".":{magnet:!1},".body":{width:100,height:50},circle:{r:7,stroke:"black",fill:"transparent","stroke-width":2}}},{operation:function(){return!0}}),Dc=kc.define("logic.IO",{size:{width:60,height:30},attrs:{".body":{fill:"white",stroke:"black","stroke-width":2},".wire":{ref:".body","ref-y":.5,stroke:"black"},text:{fill:"black",ref:".body","ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle","font-weight":"bold","font-variant":"small-caps","text-transform":"capitalize","font-size":"14px"}}},{markup:''}),Tc=Dc.define("logic.Input",{attrs:{".wire":{"ref-dx":0,d:"M 0 0 L 23 0"},circle:{ref:".body","ref-dx":30,"ref-y":.5,magnet:!0,class:"output",port:"out"},text:{text:"input"}}}),Lc=Dc.define("logic.Output",{attrs:{".wire":{"ref-x":0,d:"M 0 0 L -23 0"},circle:{ref:".body","ref-x":-30,"ref-y":.5,magnet:"passive",class:"input",port:"in"},text:{text:"output"}}}),zc=kc.define("logic.Gate11",{attrs:{".input":{ref:".body","ref-x":-2,"ref-y":.5,magnet:"passive",port:"in"},".output":{ref:".body","ref-dx":2,"ref-y":.5,magnet:!0,port:"out"}}},{markup:''}),Pc=kc.define("logic.Gate21",{attrs:{".input1":{ref:".body","ref-x":-2,"ref-y":.3,magnet:"passive",port:"in1"},".input2":{ref:".body","ref-x":-2,"ref-y":.7,magnet:"passive",port:"in2"},".output":{ref:".body","ref-dx":2,"ref-y":.5,magnet:!0,port:"out"}}},{markup:''}),jc=zc.define("logic.Repeater",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzIuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICA8L2c+Cjwvc3ZnPgo="}}},{operation:function(t){return t}}),Zc=zc.define("logic.Not",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzkuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI2NzEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzYiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA4MCwyNSBBIDQsNCAwIDEgMSA3MiwyNSBBIDQsNCAwIDEgMSA4MCwyNSB6IgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEsMCkiIC8+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t){return!t}}),Gc=Pc.define("logic.Or",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik9SIEFOU0kuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTAgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjcxNCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iMSA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMC41IDogMC4zMzMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODA2IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODE5IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjM3Mi4wNDcyNCA6IDM1MC43ODczOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI3NDQuMDk0NDggOiA1MjYuMTgxMDkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzc3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49Ijc1IDogNDAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTUwIDogNjAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDYwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTMyNzUiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNTAgOiAzMy4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTAwIDogNTAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTU1MzMiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjU1NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDE2LjY2NjY2NyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDI1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAyNSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iNCIKICAgICBpbmtzY2FwZTpjeD0iMTEzLjAwMDM5IgogICAgIGlua3NjYXBlOmN5PSIxMi44OTM3MzEiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImcyNTYwIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1wb2ludHM9InRydWUiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAwMDAiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMzk5IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg3NCIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMzciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii00IgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNzAsMjUgYyAyMCwwIDI1LDAgMjUsMCIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMSwxNSA1LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzIsMzUgNSwzNSIKICAgICAgIGlkPSJwYXRoMzk0NCIgLz4KICAgIDxnCiAgICAgICBpZD0iZzI1NjAiCiAgICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2LjUsLTM5LjUpIj4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo="}}},{operation:function(t,e){return t||e}}),Bc=Pc.define("logic.And",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IkFORCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI4IgogICAgIGlua3NjYXBlOmN4PSI1Ni42OTgzNDgiCiAgICAgaW5rc2NhcGU6Y3k9IjI1LjMyNjg5OSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTptZWRpdW07Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWluZGVudDowO3RleHQtYWxpZ246c3RhcnQ7dGV4dC1kZWNvcmF0aW9uOm5vbmU7bGluZS1oZWlnaHQ6bm9ybWFsO2xldHRlci1zcGFjaW5nOm5vcm1hbDt3b3JkLXNwYWNpbmc6bm9ybWFsO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlyZWN0aW9uOmx0cjtibG9jay1wcm9ncmVzc2lvbjp0Yjt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDozO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGU7Zm9udC1mYW1pbHk6Qml0c3RyZWFtIFZlcmEgU2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkJpdHN0cmVhbSBWZXJhIFNhbnMiCiAgICAgICBkPSJNIDMwLDUgTCAzMCw2LjQyODU3MTQgTCAzMCw0My41NzE0MjkgTCAzMCw0NSBMIDMxLjQyODU3MSw0NSBMIDUwLjQ3NjE5LDQ1IEMgNjEuNzQ0MDk4LDQ1IDcwLjQ3NjE5LDM1Ljk5OTk1NSA3MC40NzYxOSwyNSBDIDcwLjQ3NjE5LDE0LjAwMDA0NSA2MS43NDQwOTksNS4wMDAwMDAyIDUwLjQ3NjE5LDUgQyA1MC40NzYxOSw1IDUwLjQ3NjE5LDUgMzEuNDI4NTcxLDUgTCAzMCw1IHogTSAzMi44NTcxNDMsNy44NTcxNDI5IEMgNDAuODM0MjY0LDcuODU3MTQyOSA0NS45MTgzNjgsNy44NTcxNDI5IDQ4LjA5NTIzOCw3Ljg1NzE0MjkgQyA0OS4yODU3MTQsNy44NTcxNDI5IDQ5Ljg4MDk1Miw3Ljg1NzE0MjkgNTAuMTc4NTcxLDcuODU3MTQyOSBDIDUwLjMyNzM4MSw3Ljg1NzE0MjkgNTAuNDA5MjI3LDcuODU3MTQyOSA1MC40NDY0MjksNy44NTcxNDI5IEMgNTAuNDY1MDI5LDcuODU3MTQyOSA1MC40NzE1NDMsNy44NTcxNDI5IDUwLjQ3NjE5LDcuODU3MTQyOSBDIDYwLjIzNjg1Myw3Ljg1NzE0MyA2Ny4xNDI4NTcsMTUuNDk3MDk4IDY3LjE0Mjg1NywyNSBDIDY3LjE0Mjg1NywzNC41MDI5MDIgNTkuNzYwNjYyLDQyLjE0Mjg1NyA1MCw0Mi4xNDI4NTcgTCAzMi44NTcxNDMsNDIuMTQyODU3IEwgMzIuODU3MTQzLDcuODU3MTQyOSB6IgogICAgICAgaWQ9InBhdGgyODg0IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NzY2NjY3Nzc3NzY2NjIiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return t&&e}}),Oc=Pc.define("logic.Nor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjEiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjY3NzY0NCIKICAgICBpbmtzY2FwZTpjeT0iMjIuMTAyMzQ0IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjM3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItNCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc5LDI1IEMgOTksMjUgOTUsMjUgOTUsMjUiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBpZD0icGF0aDI2MDQiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI3NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjI1IgogICAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgICAgc29kaXBvZGk6cnk9IjQiCiAgICAgICAgIGQ9Ik0gNzksMjUgQSA0LDQgMCAxIDEgNzEsMjUgQSA0LDQgMCAxIDEgNzksMjUgeiIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI2LjUsMzkuNSkiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t,e){return!(t||e)}}),Ec=Pc.define("logic.Nand",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5BTkQgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTYiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjI4MzMwNyIKICAgICBpbmtzY2FwZTpjeT0iMTYuNDQyODQzIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzksMjUgQyA5MS44LDI1IDk1LDI1IDk1LDI1IgogICAgICAgaWQ9InBhdGgzMDU5IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMxLDE1IDUsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMiwzNSA1LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmb250LXNpemU6bWVkaXVtO2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1pbmRlbnQ6MDt0ZXh0LWFsaWduOnN0YXJ0O3RleHQtZGVjb3JhdGlvbjpub25lO2xpbmUtaGVpZ2h0Om5vcm1hbDtsZXR0ZXItc3BhY2luZzpub3JtYWw7d29yZC1zcGFjaW5nOm5vcm1hbDt0ZXh0LXRyYW5zZm9ybTpub25lO2RpcmVjdGlvbjpsdHI7YmxvY2stcHJvZ3Jlc3Npb246dGI7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MzttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO2ZvbnQtZmFtaWx5OkJpdHN0cmVhbSBWZXJhIFNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpCaXRzdHJlYW0gVmVyYSBTYW5zIgogICAgICAgZD0iTSAzMCw1IEwgMzAsNi40Mjg1NzE0IEwgMzAsNDMuNTcxNDI5IEwgMzAsNDUgTCAzMS40Mjg1NzEsNDUgTCA1MC40NzYxOSw0NSBDIDYxLjc0NDA5OCw0NSA3MC40NzYxOSwzNS45OTk5NTUgNzAuNDc2MTksMjUgQyA3MC40NzYxOSwxNC4wMDAwNDUgNjEuNzQ0MDk5LDUuMDAwMDAwMiA1MC40NzYxOSw1IEMgNTAuNDc2MTksNSA1MC40NzYxOSw1IDMxLjQyODU3MSw1IEwgMzAsNSB6IE0gMzIuODU3MTQzLDcuODU3MTQyOSBDIDQwLjgzNDI2NCw3Ljg1NzE0MjkgNDUuOTE4MzY4LDcuODU3MTQyOSA0OC4wOTUyMzgsNy44NTcxNDI5IEMgNDkuMjg1NzE0LDcuODU3MTQyOSA0OS44ODA5NTIsNy44NTcxNDI5IDUwLjE3ODU3MSw3Ljg1NzE0MjkgQyA1MC4zMjczODEsNy44NTcxNDI5IDUwLjQwOTIyNyw3Ljg1NzE0MjkgNTAuNDQ2NDI5LDcuODU3MTQyOSBDIDUwLjQ2NTAyOSw3Ljg1NzE0MjkgNTAuNDcxNTQzLDcuODU3MTQyOSA1MC40NzYxOSw3Ljg1NzE0MjkgQyA2MC4yMzY4NTMsNy44NTcxNDMgNjcuMTQyODU3LDE1LjQ5NzA5OCA2Ny4xNDI4NTcsMjUgQyA2Ny4xNDI4NTcsMzQuNTAyOTAyIDU5Ljc2MDY2Miw0Mi4xNDI4NTcgNTAsNDIuMTQyODU3IEwgMzIuODU3MTQzLDQyLjE0Mjg1NyBMIDMyLjg1NzE0Myw3Ljg1NzE0MjkgeiIKICAgICAgIGlkPSJwYXRoMjg4NCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2Njc2NjY2Nzc3Nzc2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDQwMDgiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return!(t&&e)}}),Rc=Pc.define("logic.Xor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhPUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iMjUuOTM4MTE2IgogICAgIGlua3NjYXBlOmN5PSIxNy4yMzAwNSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzAuMzg1NzE3LDE1IEwgNC45OTk5OTk4LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5NzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEuMzYyMDkxLDM1IEwgNC45OTk5OTk4LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPGcKICAgICAgIGlkPSJnMjU2MCIKICAgICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYuNSwtMzkuNSkiPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDM1MTYiCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi4yNSw4MS41MDAwMDUgQyAtMy44NDczNzQsODQuMTQ0NDA1IC00LjUsODQuNTAwMDA1IC00LjUsODQuNTAwMDA1IEwgLTguMTU2MjUsODQuNTAwMDA1IEwgLTYuMTU2MjUsODIuMDYyNTA1IEMgLTYuMTU2MjUsODIuMDYyNTA1IC0wLjUsNzUuMDYyNDUxIC0wLjUsNjQuNSBDIC0wLjUsNTMuOTM3NTQ5IC02LjE1NjI1LDQ2LjkzNzUgLTYuMTU2MjUsNDYuOTM3NSBMIC04LjE1NjI1LDQ0LjUgTCAtNC41LDQ0LjUgQyAtMy43MTg3NSw0NS40Mzc1IC0zLjA3ODEyNSw0Ni4xNTYyNSAtMi4yODEyNSw0Ny41IEMgLTAuNDA4NTMxLDUwLjU5OTgxNSAyLjUsNTYuNTI2NjQ2IDIuNSw2NC41IEMgMi41LDcyLjQ1MDY1IC0wLjM5NjY5Nyw3OC4zNzk0MjUgLTIuMjUsODEuNTAwMDA1IHoiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY3NjY2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t,e){return(!t||e)&&(t||!e)}}),Yc=Pc.define("logic.Xnor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhOT1IgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNTU3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTYuNjY2NjY3IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDI1IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0IgogICAgIGlua3NjYXBlOmN4PSI5NS43MjM2NiIKICAgICBpbmtzY2FwZTpjeT0iLTI2Ljc3NTAyMyIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyLjAwMDAwMDI0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc4LjMzMzMzMiwyNSBDIDkxLjY2NjY2NiwyNSA5NSwyNSA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMC4zODU3MTcsMTUgTCA0Ljk5OTk5OTgsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk3NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMS4zNjIwOTEsMzUgTCA0Ljk5OTk5OTgsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMzUxNiIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjI1LDgxLjUwMDAwNSBDIC0zLjg0NzM3NCw4NC4xNDQ0MDUgLTQuNSw4NC41MDAwMDUgLTQuNSw4NC41MDAwMDUgTCAtOC4xNTYyNSw4NC41MDAwMDUgTCAtNi4xNTYyNSw4Mi4wNjI1MDUgQyAtNi4xNTYyNSw4Mi4wNjI1MDUgLTAuNSw3NS4wNjI0NTEgLTAuNSw2NC41IEMgLTAuNSw1My45Mzc1NDkgLTYuMTU2MjUsNDYuOTM3NSAtNi4xNTYyNSw0Ni45Mzc1IEwgLTguMTU2MjUsNDQuNSBMIC00LjUsNDQuNSBDIC0zLjcxODc1LDQ1LjQzNzUgLTMuMDc4MTI1LDQ2LjE1NjI1IC0yLjI4MTI1LDQ3LjUgQyAtMC40MDg1MzEsNTAuNTk5ODE1IDIuNSw1Ni41MjY2NDYgMi41LDY0LjUgQyAyLjUsNzIuNDUwNjUgLTAuMzk2Njk3LDc4LjM3OTQyNSAtMi4yNSw4MS41MDAwMDUgeiIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Njc2NjY2NzYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDM1NTEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return(!t||!e)&&(t||e)}}),Wc={Gate:kc,IO:Dc,Input:Tc,Output:Lc,Gate11:zc,Gate21:Pc,Repeater:jc,Not:Zc,Or:Gc,And:Bc,Nor:Oc,Nand:Ec,Xor:Rc,Xnor:Yc,Wire:Ma.define("logic.Wire",{attrs:{".connection":{"stroke-width":2},".marker-vertex":{r:7}},router:{name:"orthogonal"},connector:{name:"rounded",args:{radius:10}}},{arrowheadMarkup:['','',""].join(""),vertexMarkup:['','','','','',"Remove vertex.","","",""].join("")})},Uc={KingWhite:_l.define("chess.KingWhite",{size:{width:42,height:38}},{markup:' '}),KingBlack:_l.define("chess.KingBlack",{size:{width:42,height:38}},{markup:' '}),QueenWhite:_l.define("chess.QueenWhite",{size:{width:42,height:38}},{markup:' '}),QueenBlack:_l.define("chess.QueenBlack",{size:{width:42,height:38}},{markup:' '}),RookWhite:_l.define("chess.RookWhite",{size:{width:32,height:34}},{markup:' '}),RookBlack:_l.define("chess.RookBlack",{size:{width:32,height:34}},{markup:' '}),BishopWhite:_l.define("chess.BishopWhite",{size:{width:38,height:38}},{markup:' '}),BishopBlack:_l.define("chess.BishopBlack",{size:{width:38,height:38}},{markup:' '}),KnightWhite:_l.define("chess.KnightWhite",{size:{width:38,height:37}},{markup:' '}),KnightBlack:_l.define("chess.KnightBlack",{size:{width:38,height:37}},{markup:' '}),PawnWhite:_l.define("chess.PawnWhite",{size:{width:28,height:33}},{markup:''}),PawnBlack:_l.define("chess.PawnBlack",{size:{width:28,height:33}},{markup:''})},Fc=Ta.define("erd.Entity",{size:{width:150,height:60},attrs:{".outer":{fill:"#2ECC71",stroke:"#27AE60","stroke-width":2,points:"100,0 100,60 0,60 0,0"},".inner":{fill:"#2ECC71",stroke:"#27AE60","stroke-width":2,points:"95,5 95,55 5,55 5,5",display:"none"},text:{text:"Entity","font-family":"Arial","font-size":14,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Vc=Fc.define("erd.WeakEntity",{attrs:{".inner":{display:"auto"},text:{text:"Weak Entity"}}}),Xc=Ta.define("erd.Relationship",{size:{width:80,height:80},attrs:{".outer":{fill:"#3498DB",stroke:"#2980B9","stroke-width":2,points:"40,0 80,40 40,80 0,40"},".inner":{fill:"#3498DB",stroke:"#2980B9","stroke-width":2,points:"40,5 75,40 40,75 5,40",display:"none"},text:{text:"Relationship","font-family":"Arial","font-size":12,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Hc=Xc.define("erd.IdentifyingRelationship",{attrs:{".inner":{display:"auto"},text:{text:"Identifying"}}}),Jc=Ta.define("erd.Attribute",{size:{width:100,height:50},attrs:{ellipse:{transform:"translate(50, 25)"},".outer":{stroke:"#D35400","stroke-width":2,cx:0,cy:0,rx:50,ry:25,fill:"#E67E22"},".inner":{stroke:"#D35400","stroke-width":2,cx:0,cy:0,rx:45,ry:20,fill:"#E67E22",display:"none"},text:{"font-family":"Arial","font-size":14,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),_c=Jc.define("erd.Multivalued",{attrs:{".inner":{display:"block"},text:{text:"multivalued"}}}),Qc=Jc.define("erd.Derived",{attrs:{".outer":{"stroke-dasharray":"3,5"},text:{text:"derived"}}}),Kc=Jc.define("erd.Key",{attrs:{ellipse:{"stroke-width":4},text:{text:"key","font-weight":"800","text-decoration":"underline"}}}),qc=Jc.define("erd.Normal",{attrs:{text:{text:"Normal"}}}),$c={Entity:Fc,WeakEntity:Vc,Relationship:Xc,IdentifyingRelationship:Hc,Attribute:Jc,Multivalued:_c,Derived:Qc,Key:Kc,Normal:qc,ISA:Ta.define("erd.ISA",{type:"erd.ISA",size:{width:100,height:50},attrs:{polygon:{points:"0,0 50,50 100,0",fill:"#F1C40F",stroke:"#F39C12","stroke-width":2},text:{text:"ISA","font-size":18,"ref-x":.5,"ref-y":.3,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Line:Ma.define("erd.Line",{},{cardinality:function(t){this.set("labels",[{position:-20,attrs:{text:{dy:-8,text:t}}}])}})},tu={State:$l.define("fsa.State",{attrs:{circle:{"stroke-width":3},text:{"font-weight":"800"}}}),StartState:Ta.define("fsa.StartState",{size:{width:20,height:20},attrs:{circle:{transform:"translate(10, 10)",r:10,fill:"#000000"}}},{markup:''}),EndState:Ta.define("fsa.EndState",{size:{width:20,height:20},attrs:{".outer":{transform:"translate(10, 10)",r:10,fill:"#ffffff",stroke:"#000000"},".inner":{transform:"translate(10, 10)",r:6,fill:"#000000"}}},{markup:''}),Arrow:Ma.define("fsa.Arrow",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}},smooth:!0})},eu={Member:Ta.define("org.Member",{size:{width:180,height:70},attrs:{rect:{width:170,height:60},".card":{fill:"#FFFFFF",stroke:"#000000","stroke-width":2,"pointer-events":"visiblePainted",rx:10,ry:10},image:{width:48,height:48,ref:".card","ref-x":10,"ref-y":5},".rank":{"text-decoration":"underline",ref:".card","ref-x":.9,"ref-y":.2,"font-family":"Courier New","font-size":14,"text-anchor":"end"},".name":{"font-weight":"800",ref:".card","ref-x":.9,"ref-y":.6,"font-family":"Courier New","font-size":14,"text-anchor":"end"}}},{markup:''}),Arrow:Ma.define("org.Arrow",{source:{selector:".card"},target:{selector:".card"},attrs:{".connection":{stroke:"#585858","stroke-width":3}},z:-1})},iu={Place:_l.define("pn.Place",{size:{width:50,height:50},attrs:{".root":{r:25,fill:"#ffffff",stroke:"#000000",transform:"translate(25, 25)"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:".root",fill:"#000000","font-size":12},".tokens > circle":{fill:"#000000",r:5},".tokens.one > circle":{transform:"translate(25, 25)"},".tokens.two > circle:nth-child(1)":{transform:"translate(19, 25)"},".tokens.two > circle:nth-child(2)":{transform:"translate(31, 25)"},".tokens.three > circle:nth-child(1)":{transform:"translate(18, 29)"},".tokens.three > circle:nth-child(2)":{transform:"translate(25, 19)"},".tokens.three > circle:nth-child(3)":{transform:"translate(32, 29)"},".tokens.alot > text":{transform:"translate(25, 18)","text-anchor":"middle",fill:"#000000"}}},{markup:''}),PlaceView:Bl.extend({presentationAttributes:Bl.addPresentationAttributes({tokens:["TOKENS"]}),initFlag:Bl.prototype.initFlag.concat(["TOKENS"]),confirmUpdate:function(){for(var t,e=[],i=arguments.length;i--;)e[i]=arguments[i];var n=(t=Bl.prototype.confirmUpdate).call.apply(t,[this].concat(e));return this.hasFlag(n,"TOKENS")&&(this.renderTokens(),this.update(),n=this.removeFlag(n,"TOKENS")),n},renderTokens:function(){var e=this.vel.findOne(".tokens").empty();["one","two","three","alot"].forEach(function(t){e.removeClass(t)});var t=this.model.get("tokens");if(t)switch(t){case 1:e.addClass("one"),e.append(wr("circle"));break;case 2:e.addClass("two"),e.append([wr("circle"),wr("circle")]);break;case 3:e.addClass("three"),e.append([wr("circle"),wr("circle"),wr("circle")]);break;default:e.addClass("alot"),e.append(wr("text").text(t+""))}}}),Transition:_l.define("pn.Transition",{size:{width:12,height:50},attrs:{rect:{width:12,height:50,fill:"#000000",stroke:"#000000"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:"rect",fill:"#000000","font-size":12}}},{markup:''}),Link:Ma.define("pn.Link",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}}})},nu=_l.define("uml.Class",{attrs:{rect:{width:200},".uml-class-name-rect":{stroke:"black","stroke-width":2,fill:"#3498db"},".uml-class-attrs-rect":{stroke:"black","stroke-width":2,fill:"#2980b9"},".uml-class-methods-rect":{stroke:"black","stroke-width":2,fill:"#2980b9"},".uml-class-name-text":{ref:".uml-class-name-rect","ref-y":.5,"ref-x":.5,"text-anchor":"middle","y-alignment":"middle","font-weight":"bold",fill:"black","font-size":12,"font-family":"Times New Roman"},".uml-class-attrs-text":{ref:".uml-class-attrs-rect","ref-y":5,"ref-x":5,fill:"black","font-size":12,"font-family":"Times New Roman"},".uml-class-methods-text":{ref:".uml-class-methods-rect","ref-y":5,"ref-x":5,fill:"black","font-size":12,"font-family":"Times New Roman"}},name:[],attributes:[],methods:[]},{markup:['','','',"",'',""].join(""),initialize:function(){this.on("change:name change:attributes change:methods",function(){this.updateRectangles(),this.trigger("uml-update")},this),this.updateRectangles(),_l.prototype.initialize.apply(this,arguments)},getClassName:function(){return this.get("name")},updateRectangles:function(){var n=this.get("attrs"),t=[{type:"name",text:this.getClassName()},{type:"attrs",text:this.get("attributes")},{type:"methods",text:this.get("methods")}],r=0;t.forEach(function(t){var e=Array.isArray(t.text)?t.text:[t.text],i=20*e.length+20;n[".uml-class-"+t.type+"-text"].text=e.join("\n"),n[".uml-class-"+t.type+"-rect"].height=i,n[".uml-class-"+t.type+"-rect"].transform="translate(0,"+r+")",r+=i})}}),ru=Bl.extend({initialize:function(){Bl.prototype.initialize.apply(this,arguments),this.listenTo(this.model,"uml-update",function(){this.update(),this.resize()})}}),ou=nu.define("uml.Abstract",{attrs:{".uml-class-name-rect":{fill:"#e74c3c"},".uml-class-attrs-rect":{fill:"#c0392b"},".uml-class-methods-rect":{fill:"#c0392b"}}},{getClassName:function(){return["<>",this.get("name")]}}),au=ru,su=nu.define("uml.Interface",{attrs:{".uml-class-name-rect":{fill:"#f1c40f"},".uml-class-attrs-rect":{fill:"#f39c12"},".uml-class-methods-rect":{fill:"#f39c12"}}},{getClassName:function(){return["<>",this.get("name")]}}),lu={basic:cc,standard:bc,devs:Sc,logic:Wc,chess:Uc,erd:$c,fsa:tu,org:eu,pn:iu,uml:{Class:nu,ClassView:ru,Abstract:ou,AbstractView:au,Interface:su,InterfaceView:ru,Generalization:Ma.define("uml.Generalization",{attrs:{".marker-target":{d:"M 20 0 L 0 10 L 20 20 z",fill:"white"}}}),Implementation:Ma.define("uml.Implementation",{attrs:{".marker-target":{d:"M 20 0 L 0 10 L 20 20 z",fill:"white"},".connection":{"stroke-dasharray":"3,3"}}}),Aggregation:Ma.define("uml.Aggregation",{attrs:{".marker-target":{d:"M 40 10 L 20 20 L 0 10 L 20 0 z",fill:"white"}}}),Composition:Ma.define("uml.Composition",{attrs:{".marker-target":{d:"M 40 10 L 20 20 L 0 10 L 20 0 z",fill:"black"}}}),Association:Ma.define("uml.Association"),State:_l.define("uml.State",{attrs:{".uml-state-body":{width:200,height:200,rx:10,ry:10,fill:"#ecf0f1",stroke:"#bdc3c7","stroke-width":3},".uml-state-separator":{stroke:"#bdc3c7","stroke-width":2},".uml-state-name":{ref:".uml-state-body","ref-x":.5,"ref-y":5,"text-anchor":"middle",fill:"#000000","font-family":"Courier New","font-size":14},".uml-state-events":{ref:".uml-state-separator","ref-x":5,"ref-y":5,fill:"#000000","font-family":"Courier New","font-size":14}},name:"State",events:[]},{markup:['','','',"",'','','',""].join(""),initialize:function(){this.on({"change:name":this.updateName,"change:events":this.updateEvents,"change:size":this.updatePath},this),this.updateName(),this.updateEvents(),this.updatePath(),_l.prototype.initialize.apply(this,arguments)},updateName:function(){this.attr(".uml-state-name/text",this.get("name"))},updateEvents:function(){this.attr(".uml-state-events/text",this.get("events").join("\n"))},updatePath:function(){var t="M 0 20 L "+this.get("size").width+" 20";this.attr(".uml-state-separator/d",t,{silent:!0})}}),StartState:$l.define("uml.StartState",{type:"uml.StartState",attrs:{circle:{fill:"#34495e",stroke:"#2c3e50","stroke-width":2,rx:1}}}),EndState:_l.define("uml.EndState",{size:{width:20,height:20},attrs:{"circle.outer":{transform:"translate(10, 10)",r:10,fill:"#ffffff",stroke:"#2c3e50"},"circle.inner":{transform:"translate(10, 10)",r:6,fill:"#34495e"}}},{markup:''}),Transition:Ma.define("uml.Transition",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z",fill:"#34495e",stroke:"#2c3e50"},".connection":{stroke:"#2c3e50"}}})}},cu={DirectedGraph:Jl,PortLabel:xa,Port:Aa};t.V=wr,t.Vectorizer=Ll,t.anchors=gl,t.config=br,t.connectionPoints=ps,t.connectionStrategies=bs,t.connectors=qa,t.dia=Hl,t.elementTools=Dl,t.env=Tl,t.format={},t.g=yr,t.highlighters=cs,t.layout=cu,t.linkAnchors=ul,t.linkTools=kl,t.mvc=is,t.routers=ol,t.setTheme=function(t,e){oo($a,"setTheme",t,e=e||{}),ts.prototype.defaultTheme=t},t.shapes=lu,t.ui={},t.util=fa,t.version="3.5.0",Object.defineProperty(t,"__esModule",{value:!0})}),"undefined"!=typeof joint)var g=joint.g,V=joint.V,Vectorizer=joint.V; \ No newline at end of file +if(function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("backbone"),require("lodash"),require("jquery")):"function"==typeof define&&define.amd?define(["exports","backbone","lodash","jquery"],e):e((t=t||self).joint={},t.Backbone,t._,t.$)}(this,function(t,s,i,S){"use strict";s=s&&s.hasOwnProperty("default")?s.default:s,i=i&&i.hasOwnProperty("default")?i.default:i,S=S&&S.hasOwnProperty("default")?S.default:S;var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function n(t,e){return t(e={exports:{}},e.exports),e.exports}var r=function(t){return t&&t.Math==Math&&t},u=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof e&&e)||function(){return this}()||Function("return this")(),a=function(t){try{return!!t()}catch(t){return!0}},l=!a(function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}),o={}.propertyIsEnumerable,c=Object.getOwnPropertyDescriptor,h={f:c&&!o.call({1:2},1)?function(t){var e=c(this,t);return!!e&&e.enumerable}:o},C=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},g={}.toString,d=function(t){return g.call(t).slice(8,-1)},f="".split,y=a(function(){return!Object("z").propertyIsEnumerable(0)})?function(t){return"String"==d(t)?f.call(t,""):Object(t)}:Object,p=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},m=function(t){return y(p(t))},I=function(t){return"object"==typeof t?null!==t:"function"==typeof t},v=function(t,e){if(!I(t))return t;var i,n;if(e&&"function"==typeof(i=t.toString)&&!I(n=i.call(t)))return n;if("function"==typeof(i=t.valueOf)&&!I(n=i.call(t)))return n;if(!e&&"function"==typeof(i=t.toString)&&!I(n=i.call(t)))return n;throw TypeError("Can't convert object to primitive value")},A={}.hasOwnProperty,w=function(t,e){return A.call(t,e)},b=u.document,N=I(b)&&I(b.createElement),x=function(t){return N?b.createElement(t):{}},M=!l&&!a(function(){return 7!=Object.defineProperty(x("div"),"a",{get:function(){return 7}}).a}),k=Object.getOwnPropertyDescriptor,D={f:l?k:function(t,e){if(t=m(t),e=v(e,!0),M)try{return k(t,e)}catch(t){}if(w(t,e))return C(!h.f.call(t,e),t[e])}},T=function(t){if(!I(t))throw TypeError(String(t)+" is not an object");return t},L=Object.defineProperty,z={f:l?L:function(t,e,i){if(T(t),e=v(e,!0),T(i),M)try{return L(t,e,i)}catch(t){}if("get"in i||"set"in i)throw TypeError("Accessors not supported");return"value"in i&&(t[e]=i.value),t}},P=l?function(t,e,i){return z.f(t,e,C(1,i))}:function(t,e,i){return t[e]=i,t},j=function(e,i){try{P(u,e,i)}catch(t){u[e]=i}return i},Z="__core-js_shared__",G=u[Z]||j(Z,{}),B=Function.toString;"function"!=typeof G.inspectSource&&(G.inspectSource=function(t){return B.call(t)});var O,E,R,Y=G.inspectSource,W=u.WeakMap,U="function"==typeof W&&/native code/.test(Y(W)),F=n(function(t){(t.exports=function(t,e){return G[t]||(G[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.8.3",mode:"global",copyright:"\xa9 2021 Denis Pushkarev (zloirock.ru)"})}),V=0,X=Math.random(),H=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++V+X).toString(36)},J=F("keys"),_=function(t){return J[t]||(J[t]=H(t))},Q={},K=u.WeakMap;if(U){var q=G.state||(G.state=new K),$=q.get,tt=q.has,et=q.set;O=function(t,e){return e.facade=t,et.call(q,t,e),e},E=function(t){return $.call(q,t)||{}},R=function(t){return tt.call(q,t)}}else{var it=_("state");Q[it]=!0,O=function(t,e){return e.facade=t,P(t,it,e),e},E=function(t){return w(t,it)?t[it]:{}},R=function(t){return w(t,it)}}var nt,rt={set:O,get:E,has:R,enforce:function(t){return R(t)?E(t):O(t,{})},getterFor:function(i){return function(t){var e;if(!I(t)||(e=E(t)).type!==i)throw TypeError("Incompatible receiver, "+i+" required");return e}}},ot=n(function(t){var e=rt.get,l=rt.enforce,c=String(String).split("String");(t.exports=function(t,e,i,n){var r,o=!!n&&!!n.unsafe,a=!!n&&!!n.enumerable,s=!!n&&!!n.noTargetGet;"function"==typeof i&&("string"!=typeof e||w(i,"name")||P(i,"name",e),(r=l(i)).source||(r.source=c.join("string"==typeof e?e:""))),t!==u?(o?!s&&t[e]&&(a=!0):delete t[e],a?t[e]=i:P(t,e,i)):a?t[e]=i:j(e,i)})(Function.prototype,"toString",function(){return"function"==typeof this&&e(this).source||Y(this)})}),at=u,st=function(t){return"function"==typeof t?t:void 0},lt=function(t,e){return arguments.length<2?st(at[t])||st(u[t]):at[t]&&at[t][e]||u[t]&&u[t][e]},ct=Math.ceil,ut=Math.floor,ht=function(t){return isNaN(t=+t)?0:(0r;)w(n,i=e[r++])&&(~vt(o,i)||o.push(i));return o},Ct=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],yt=Ct.concat("length","prototype"),wt={f:Object.getOwnPropertyNames||function(t){return At(t,yt)}},bt={f:Object.getOwnPropertySymbols},Nt=lt("Reflect","ownKeys")||function(t){var e=wt.f(T(t)),i=bt.f;return i?e.concat(i(t)):e},xt=function(t,e){for(var i=Nt(e),n=z.f,r=D.f,o=0;o"+t+""},_t=function(){try{nt=document.domain&&new ActiveXObject("htmlfile")}catch(t){}var t,e;_t=nt?function(t){t.write(Jt("")),t.close();var e=t.parentWindow.Object;return t=null,e}(nt):((e=x("iframe")).style.display="none",Ut.appendChild(e),e.src=String("javascript:"),(t=e.contentWindow.document).open(),t.write(Jt("document.F=Object")),t.close(),t.F);for(var i=Ct.length;i--;)delete _t[Ft][Ct[i]];return _t()};Q[Xt]=!0;var Qt=Object.create||function(t,e){var i;return null!==t?(Ht[Ft]=T(t),i=new Ht,Ht[Ft]=null,i[Xt]=t):i=_t(),void 0===e?i:Wt(i,e)},Kt=Rt("unscopables"),qt=Array.prototype;null==qt[Kt]&&z.f(qt,Kt,{configurable:!0,value:Qt(null)});var $t=function(t){qt[Kt][t]=!0},te=Object.defineProperty,ee={},ie=function(t){throw t},ne=function(t,e){if(w(ee,t))return ee[t];e||(e={});var i=[][t],n=!!w(e,"ACCESSORS")&&e.ACCESSORS,r=w(e,0)?e[0]:ie,o=w(e,1)?e[1]:void 0;return ee[t]=!!i&&!a(function(){if(n&&!l)return!0;var t={length:-1};n?te(t,1,{enumerable:!0,get:ie}):t[1]=1,i.call(t,r,o)})},re=It.includes,oe=ne("indexOf",{ACCESSORS:!0,1:0});jt({target:"Array",proto:!0,forced:!oe},{includes:function(t){return re(this,t,1=i.length?{value:void 0,done:!0}:(t=Xe(i,n),e.index+=t.length,{value:t,done:!1})});var Qe=function(e,t,i,n){try{return n?t(T(i)[0],i[1]):t(i)}catch(t){throw function(t){var e=t.return;if(void 0!==e)T(e.call(t)).value}(e),t}},Ke=Rt("iterator"),qe=Array.prototype,$e=function(t,e,i){var n=v(e);n in t?z.f(t,n,C(0,i)):t[n]=i},ti={};ti[Rt("toStringTag")]="z";var ei="[object z]"===String(ti),ii=Rt("toStringTag"),ni="Arguments"==d(function(){return arguments}()),ri=ei?d:function(t){var e,i,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(i=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),ii))?i:ni?d(e):"Object"==(n=d(e))&&"function"==typeof e.callee?"Arguments":n},oi=Rt("iterator"),ai=Rt("iterator"),si=!1;try{var li=0,ci={next:function(){return{done:!!li++}},return:function(){si=!0}};ci[ai]=function(){return this},Array.from(ci,function(){throw 2})}catch(t){}var ui=!function(t,e){if(!e&&!si)return!1;var i=!1;try{var n={};n[ai]=function(){return{next:function(){return{done:i=!0}}}},t(n)}catch(t){}return i}(function(t){Array.from(t)});jt({target:"Array",stat:!0,forced:ui},{from:function(t){var e,i,n,r,o,a,s,l=ce(t),c="function"==typeof this?this:Array,u=arguments.length,h=1>8-r%1*8)){if(255<(i=n.charCodeAt(r+=.75)))throw new l("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");e=e<<8|i}return a}),t.atob||(t.atob=function(t){var e=String(t).replace(/=+$/,"");if(e.length%4==1)throw new l("'atob' failed: The string to be decoded is not correctly encoded.");for(var i,n,r=0,o=0,a="";n=e.charAt(o++);~n&&(i=r%4?64*i+n:n,r++%4)?a+=String.fromCharCode(255&i>>(-2*r&6)):0)n=s.indexOf(n);return a})}()});jt({target:"Number",stat:!0},{isNaN:function(t){return t!=t}});at.Number.isNaN;var pi=u.isFinite,mi=Number.isFinite||function(t){return"number"==typeof t&&pi(t)};jt({target:"Number",stat:!0},{isFinite:mi});at.Number.isFinite;var Ii=Rt("match"),vi=function(t){if(I(e=t)&&(void 0!==(i=e[Ii])?i:"RegExp"==d(e)))throw TypeError("The method doesn't accept regular expressions");var e,i;return t},Ai=Rt("match"),Ci=function(e){var i=/./;try{"/./"[e](i)}catch(t){try{return i[Ai]=!1,"/./"[e](i)}catch(t){}}return!1};jt({target:"String",proto:!0,forced:!Ci("includes")},{includes:function(t){return!!~String(p(this)).indexOf(vi(t),1n)&&!(new nn(t,i).length()>n)},divideAt:function(t){var e=this.pointAt(t);return[new nn(this.start,e),new nn(e,this.end)]},divideAtLength:function(t){var e=this.pointAtLength(t);return[new nn(this.start,e),new nn(e,this.end)]},equals:function(t){return!!t&&this.start.x===t.start.x&&this.start.y===t.start.y&&this.end.x===t.end.x&&this.end.y===t.end.y},intersect:function(t,e){if(t&&t.intersectionWithLine){var i=t.intersectionWithLine(this,e);return i&&t instanceof nn&&(i=i[0]),i}return null},intersectionWithLine:function(t){var e=new qi(this.end.x-this.start.x,this.end.y-this.start.y),i=new qi(t.end.x-t.start.x,t.end.y-t.start.y),n=e.x*i.y-e.y*i.x,r=new qi(t.start.x-this.start.x,t.start.y-this.start.y),o=r.x*i.y-r.y*i.x,a=r.x*e.y-r.y*e.x;if(0===n||o*n<0||a*n<0)return null;if(0s.x+o/2,h=ns.x?r-30:r+30)-c)/(a*a*(n-l))+l:e=a*a/(r-c)-a*a*(n-l)*((i=r>s.y?n+30:n-30)-l)/(o*o*(r-c))+c,new qi(i,e).theta(t)},toString:function(){return new qi(this.x,this.y).toString()+" "+this.a+" "+this.b}};var cn=ln,un=Math.abs,hn=Math.cos,gn=Math.sin,dn=Math.min,fn=Math.max,pn=Math.round,mn=Math.pow,In=function(t,e,i,n){if(!(this instanceof In))return new In(t,e,i,n);Object(t)===t&&(e=t.y,i=t.width,n=t.height,t=t.x),this.x=void 0===t?0:t,this.y=void 0===e?0:e,this.width=void 0===i?0:i,this.height=void 0===n?0:n};In.fromEllipse=function(t){return t=new ln(t),new In(t.x-t.a,t.y-t.b,2*t.a,2*t.b)},In.fromPointUnion=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(0===t.length)return null;var i,n,r,o,a=new qi;i=n=1/0,r=o=-1/0;for(var s=0;s=this.x&&t.x<=this.x+this.width&&t.y>=this.y&&t.y<=this.y+this.height},containsRect:function(t){var e=new In(this).normalize(),i=new In(t).normalize(),n=e.width,r=e.height,o=i.width,a=i.height;if(!(n&&r&&o&&a))return!1;var s=e.x,l=e.y,c=i.x,u=i.y;return o+=c,n+=s,a+=u,r+=l,s<=c&&o<=n&&l<=u&&a<=r},corner:function(){return new qi(this.x+this.width,this.y+this.height)},equals:function(t){var e=new In(this).normalize(),i=new In(t).normalize();return e.x===i.x&&e.y===i.y&&e.width===i.width&&e.height===i.height},inflate:function(t,e){return void 0===t&&(t=0),void 0===e&&(e=t),this.x-=t,this.y-=e,this.width+=2*t,this.height+=2*e,this},intersect:function(t){var e=this.origin(),i=this.corner(),n=t.origin(),r=t.corner();if(r.x<=e.x||r.y<=e.y||n.x>=i.x||n.y>=i.y)return null;var o=fn(e.x,n.x),a=fn(e.y,n.y);return new In(o,a,dn(i.x,r.x)-o,dn(i.y,r.y)-a)},intersectionWithLine:function(t){var e,i,n=[this.topLine(),this.rightLine(),this.bottomLine(),this.leftLine()],r=[],o=[],a=n.length;for(i=0;iu&&(n=(this.x+this.width-u)/(d.x-u)),d.y>h&&(s=(this.y+this.height-h)/(d.y-h));var f=t.topRight();f.x>u&&(r=(this.x+this.width-u)/(f.x-u)),f.yh&&(c=(this.y+this.height-h)/(p.y-h)),{sx:dn(i,n,r,o),sy:dn(a,s,l,c)}},maxRectUniformScaleToFit:function(t,e){var i=this.maxRectScaleToFit(t,e);return dn(i.sx,i.sy)},moveAndExpand:function(t){return this.x+=t.x||0,this.y+=t.y||0,this.width+=t.width||0,this.height+=t.height||0,this},normalize:function(){var t=this.x,e=this.y,i=this.width,n=this.height;return this.width<0&&(t=this.x+this.width,i=-this.width),this.height<0&&(e=this.y+this.height,n=-this.height),this.x=t,this.y=e,this.width=i,this.height=n,this},offset:function(t,e){return qi.prototype.offset.call(this,t,e)},origin:function(){return new qi(this.x,this.y)},pointNearestToPoint:function(t){if(t=new qi(t),this.containsPoint(t))switch(this.sideNearestToPoint(t)){case"right":return new qi(this.x+this.width,t.y);case"left":return new qi(this.x,t.y);case"bottom":return new qi(t.x,this.y+this.height);case"top":return new qi(t.x,this.y)}return t.adhereToRect(this)},rightLine:function(){return new nn(this.topRight(),this.bottomRight())},rightMiddle:function(){return new qi(this.x+this.width,this.y+this.height/2)},round:function(t){var e=1;if(t)switch(t){case 1:e=10;break;case 2:e=100;break;case 3:e=1e3;break;default:e=mn(10,t)}return this.x=pn(this.x*e)/e,this.y=pn(this.y*e)/e,this.width=pn(this.width*e)/e,this.height=pn(this.height*e)/e,this},scale:function(t,e,i){return i=this.origin().scale(t,e,i),this.x=i.x,this.y=i.y,this.width*=t,this.height*=e,this},sideNearestToPoint:function(t){var e=(t=new qi(t)).x-this.x,i=this.x+this.width-t.x,n=t.y-this.y,r=e,o="left";return in.x&&(n=t[e]);var a=[];for(e=0;eg.y||r>h.y&&r<=g.y){var d=h.x-n>g.x-n?h.x-n:g.x-n;0<=d&&(u.x=n+d,u.y=r,c.start=t,c.end=u,l.intersect(c)&&s++)}o=a}return s%2==1},close:function(){var t=this.start,e=this.end,i=this.points;return t&&e&&!t.equals(e)&&i.push(t.clone()),this},lengthPoints:function(){return this.points},convexHull:function(){return new wn(yn(this.points))},equals:function(t){if(!t)return!1;var e=this.points,i=t.points,n=e.length;if(i.length!==n)return!1;for(var r=0;rr+a&&n.x>r+a||i.xo+s&&n.y>o+s||i.ye.x&&t.ye.y}function Qn(t,e){return hr(t,e,{interior:!1})}function Kn(t,e){return gr(t,e,{interior:!1})}function qn(t,e){return dr(t,e,{interior:!1})}function $n(t,e){return pr(t,e,{interior:!1})}function tr(t,e){return hr(t,e,{interior:!0})}function er(t,e){return gr(t,e,{interior:!0})}function ir(t,e){return dr(t,e,{interior:!0})}function nr(t,e){return pr(t,e,{interior:!0})}function rr(t,e){return mr(t,e,{interior:!0})}function or(t,i,n){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?tr(e,i):Qn(e,i)})}function ar(t,i,n){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?er(e,i):Kn(e,i)})}function sr(t,e,i){return cr(t,Un.fromRect(e),i)}function lr(t,e,i){return fr(t,e,i,{interior:!1})}function cr(t,e,i){return fr(t,e,i,{interior:!0})}function ur(t,i,n,r){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?cr(i,e,r):lr(i,e,r)})}function hr(t,e,i){void 0===i&&(i={});var n,r=i.interior;if(void 0===r&&(r=!1),r){if(t.containsPoint(e.start))return!0;var o=t.start,a=t.end,s=t.points;n=a.equals(o)?s:s.concat([o])}else n=t.points;for(var l=n.length,c=new nn,u=0;u'+t+"";return L.parseXML(e,{async:!1}).documentElement}var i=document.createElementNS(T.svg,"svg");return i.setAttributeNS(T.xmlns,"xmlns:xlink",T.xlink),i.setAttribute("version","1.1"),i},L.createSVGStyle=function(t){return L("style",{type:"text/css"},[L.createCDATASection(t)]).node},L.createCDATASection=function(t){return void 0===t&&(t=""),document.implementation.createDocument(null,"xml",null).createCDATASection(t)},L.idCounter=0,L.uniqueId=function(){return"v-"+ ++L.idCounter},L.toNode=function(t){return L.isV(t)?t.node:t.nodeName&&t||t[0]},L.ensureId=function(t){return(t=L.toNode(t)).id||(t.id=L.uniqueId())},L.sanitizeText=function(t){return(t||"").replace(/ /g,"\xa0")},L.isUndefined=function(t){return void 0===t},L.isString=function(t){return"string"==typeof t},L.isObject=function(t){return t&&"object"==typeof t},L.isArray=Array.isArray,L.parseXML=function(t,e){var i;e=e||{};try{var n=new DOMParser;L.isUndefined(e.async)||(n.async=e.async),i=n.parseFromString(t,"text/xml")}catch(t){i=void 0}if(!i||i.getElementsByTagName("parsererror").length)throw new Error("Invalid XML: "+t);return i},L.qualifyAttr=function(t){if(-1===t.indexOf(":"))return{ns:null,local:t};var e=t.split(":");return{ns:T[e[0]],local:e[1]}},L.transformRegex=/(\w+)\(([^,)]+),?([^)]+)?\)/gi,L.transformSeparatorRegex=/[ ,]+/,L.transformationListRegex=/^(\w+)\((.*)\)/,L.transformStringToMatrix=function(t){var e=L.createSVGMatrix(),i=t&&t.match(L.transformRegex);if(!i)return e;for(var n=0,r=i.length;n=t.start&&et.start&&i<=t.end||t.start>=e&&t.end=e?t.end+=i:t.start>=e&&(t.start+=i,t.end+=i)}),t},L.convertLineToPathData=function(t){return["M",(t=L(t)).attr("x1"),t.attr("y1"),"L",t.attr("x2"),t.attr("y2")].join(" ")},L.convertPolygonToPathData=function(t){var e=L.getPointsFromSvgNode(t);return 0===e.length?null:L.svgPointsToPath(e)+" Z"},L.convertPolylineToPathData=function(t){var e=L.getPointsFromSvgNode(t);return 0===e.length?null:L.svgPointsToPath(e)},L.svgPointsToPath=function(t){for(var e=0,i=t.length;e=o[r]&&(a.push([e].concat(n.splice(0,o[r]))),o[r]););}),a}(t)),!t||!t.length)return[["M",0,0]];for(var e,i=[],n=0,r=0,o=0,a=0,s=t.length,l=0;lg){var M=N,S=l,k=c;f=t(l=y+n*V(N=b+g*(s&&bd)T=d-1;else if(void 0!==o){var L;if(void 0===m)m=(L="auto"===i.lineHeight?{value:1.5,unit:"em"}:Rr(i.lineHeight,["em"])||{value:1,unit:"em"}).value,"em"===L.unit&&(m*=l.getBBox().height);m*A.length>o&&(T=Math.floor(o/m)-1)}if(null!==T){A.splice(T+1);var z=n.ellipsis;if(!z||T<0)break;"string"!=typeof z&&(z="\u2026");var P=A[T];if(!P&&!N)break;var j,Z,G=P.length;do{if(Z=P[G],j=P.substring(0,G),Z?Z.match(u)&&(j+=Z):j+="string"==typeof u?u:" ",j+=z,c.data=j,s.getComputedTextLength()<=r){A[T]=j;break}G--}while(0<=G);break}}}return n.svgDocument?a.removeChild(l):document.body.removeChild(a),A.join(h)},Wr=function(t){var e=S(S.parseHTML("
"+t+"
",null,!1));return e.find("*").each(function(){var i=this;S.each(i.attributes,function(){var t=this.name,e=this.value;(t.startsWith("on")||e.startsWith("javascript:")||e.startsWith("data:")||e.startsWith("vbscript:"))&&S(i).removeAttr(t)})}),e.html()},Ur=function(t,e){if(window.navigator.msSaveBlob)window.navigator.msSaveBlob(t,e);else{var i=window.URL.createObjectURL(t),n=document.createElement("a");n.href=i,n.download=e,document.body.appendChild(n),n.click(),document.body.removeChild(n),window.URL.revokeObjectURL(i)}},Fr=function(t){t=t.replace(/\s/g,"");var e,i=(t=decodeURIComponent(t)).indexOf(","),n=t.slice(0,i),r=n.split(":")[1].split(";")[0],o=t.slice(i+1);e=0<=n.indexOf("base64")?atob(o):unescape(encodeURIComponent(o));for(var a=new Uint8Array(e.length),s=0;s')({color:t.color||"blue",opacity:Number.isFinite(t.opacity)?t.opacity:1,outerRadius:e+i,innerRadius:e})},highlight:function(t){return Kr('')({color:t.color||"red",width:Number.isFinite(t.width)?t.width:1,blur:Number.isFinite(t.blur)?t.blur:0,opacity:Number.isFinite(t.opacity)?t.opacity:1})},blur:function(t){var e=Number.isFinite(t.x)?t.x:2;return Kr('')({stdDeviation:Number.isFinite(t.y)?[e,t.y]:e})},dropShadow:function(t){var e="SVGFEDropShadowElement"in window?'':'';return Kr(e)({dx:t.dx||0,dy:t.dy||0,opacity:Number.isFinite(t.opacity)?t.opacity:1,color:t.color||"black",blur:Number.isFinite(t.blur)?t.blur:4})},grayscale:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({a:.2126+.7874*(1-e),b:.7152-.7152*(1-e),c:.0722-.0722*(1-e),d:.2126-.2126*(1-e),e:.7152+.2848*(1-e),f:.0722-.0722*(1-e),g:.2126-.2126*(1-e),h:.0722+.9278*(1-e)})},sepia:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({a:.393+.607*(1-e),b:.769-.769*(1-e),c:.189-.189*(1-e),d:.349-.349*(1-e),e:.686+.314*(1-e),f:.168-.168*(1-e),g:.272-.272*(1-e),h:.534-.534*(1-e),i:.131+.869*(1-e)})},saturate:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:1-e})},hueRotate:function(t){return Kr('')({angle:t.angle||0})},invert:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:e,amount2:1-e})},brightness:function(t){return Kr('')({amount:Number.isFinite(t.amount)?t.amount:1})},contrast:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:e,amount2:.5-e/2})}},Qr={number:function(t,e,o){o=o||{currency:["$",""],decimal:".",thousands:",",grouping:[3]};var i=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i.exec(t),n=i[1]||" ",r=i[2]||">",a=i[3]||"",s=i[4]||"",l=i[5],c=+i[6],u=i[7],h=i[8],g=i[9],d=1,f="",p="",m=!1;switch(h&&(h=+h.substring(1)),(l||"0"===n&&"="===r)&&(l=n="0",r="=",u&&(c-=Math.floor((c-1)/4))),g){case"n":u=!0,g="g";break;case"%":d=100,p="%",g="f";break;case"p":d=100,p="%",g="r";break;case"b":case"o":case"x":case"X":"#"===s&&(f="0"+g.toLowerCase());break;case"c":case"d":m=!0,h=0;break;case"s":d=-1,g="r"}"$"===s&&(f=o.currency[0],p=o.currency[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):"e"!=g&&"f"!=g||(h=Math.max(0,Math.min(20,h))));var I=l&&u;if(m&&e%1)return"";var v=e<0||0===e&&1/e<0?(e=-e,"-"):a,A=p;if(d<0){var C=this.prefix(e,h);e=C.scale(e),A=C.symbol+p}else e*=d;var y=(e=this.convert(g,e,h)).lastIndexOf("."),w=y<0?e:e.substring(0,y),b=y<0?"":o.decimal+e.substring(y+1);function N(t){for(var e=t.length,i=[],n=0,r=o.grouping[0];0"===r?M+v+e:"^"===r?M.substring(0,x>>=1)+v+e+M.substring(x):v+(I?e:M+e))+A},string:function(t,e){for(var i,n="{",r=!1,o=[];-1!==(i=t.indexOf(n));){var a,s,l;if(a=t.slice(0,i),r){l=(s=a.split(":")).shift().split("."),a=e;for(var c=0;c|\$\{ ?([^{} ]+) ?\}|\{\{([^{} ]+)\}\}/g;return function(n){return n=n||{},t.replace(e,function(t){for(var e=Array.from(arguments).slice(1,4).find(function(t){return!!t}).split("."),i=n[e.shift()];void 0!==i&&e.length;)i=i[e.shift()];return void 0!==i?i:""})}},qr=i.assign,$r=i.defaults,to=qr,eo=i.defaultsDeep,io=i.assign,no=i.defaults,ro=i.defaultsDeep,oo=i.invokeMap||i.invoke,ao=i.sortedIndexBy||i.sortedIndex,so=i.uniqBy||i.uniq,lo=i.clone,co=i.cloneDeep,uo=i.isEmpty,ho=i.isEqual,go=i.isFunction,fo=i.isPlainObject,po=i.toArray,mo=i.debounce,Io=i.groupBy,vo=i.sortBy,Ao=i.flattenDeep,Co=i.without,yo=i.difference,wo=i.intersection,bo=i.union,No=i.has,xo=i.result,Mo=i.omit,So=i.pick,ko=i.bindAll,Do=i.forIn,To=i.camelCase,Lo=i.uniqueId,zo=function(){if(i.mergeWith){var t=Array.from(arguments),e=t[t.length-1],n=go(e)?e:Bo;return t.push(function(t,e){var i=n(t,e);return void 0!==i?i:Array.isArray(t)&&!Array.isArray(e)?e:void 0}),i.mergeWith.apply(this,t)}return i.merge.apply(this,arguments)},Po=function(t){var e=Object.prototype.toString;return!0===t||!1===t||!!t&&"object"==typeof t&&"[object Boolean]"===e.call(t)},jo=function(t){return!!t&&("object"==typeof t||"function"==typeof t)},Zo=function(t){var e=Object.prototype.toString;return"number"==typeof t||!!t&&"object"==typeof t&&"[object Number]"===e.call(t)},Go=function(t){var e=Object.prototype.toString;return"string"==typeof t||!!t&&"object"==typeof t&&"[object String]"===e.call(t)},Bo=function(){};function Oo(t){t=so(t);var a=po(t).reduce(function(t,e){return t[e.id]=e.clone(),t},{});return po(t).forEach(function(t){var e=a[t.id];if(e.isLink()){var i=e.source(),n=e.target();i.id&&a[i.id]&&e.prop("source/id",a[i.id].id),n.id&&a[n.id]&&e.prop("target/id",a[n.id].id)}var r=t.get("parent");r&&a[r]&&e.set("parent",a[r].id);var o=po(t.get("embeds")).reduce(function(t,e){return a[e]&&t.push(a[e].id),t},[]);uo(o)||e.set("embeds",o)}),a}var Eo={x:"x",y:"y",width:"w",height:"h",minimum:"s",maximum:"l",diagonal:"d"},Ro=Object.keys(Eo).map(function(t){return Eo[t]}).join(""),Yo="[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?",Wo=/\s/g,Uo=new RegExp("^("+Yo+"\\*)?(["+Ro+"])([-+]{1,2}"+Yo+")?$","g");function Fo(t){throw new Error("Invalid calc() expression: "+t)}function Vo(t,e){var i=Uo.exec(t.replace(Wo,""));i||Fo(t),Uo.lastIndex=0;var n=i[1];void 0===n&&(n=1);var r=i[2],o=i[3];void 0===o&&(o=0);var a=e.x,s=e.y,l=e.width,c=e.height,u=0;switch(r){case Eo.width:u=l;break;case Eo.height:u=c;break;case Eo.x:u=a;break;case Eo.y:u=s;break;case Eo.minimum:u=Math.min(c,l);break;case Eo.maximum:u=Math.max(c,l);break;case Eo.diagonal:u=Math.sqrt(c*c+l*l)}return parseFloat(n)*u+function(t){if(!t)return 0;switch(t[0]){case"+":return parseFloat(t.substr(1));case"-":return-parseFloat(t.substr(1))}return parseFloat(t)}(o)}function Xo(t){return"string"==typeof t&&t.includes("calc")}var Ho="calc(",Jo=Ho.length;function _o(t,e){for(var i=t,n=0;;){var r=i.indexOf(Ho,n);if(-1===r)return i;var o=r+Jo,a=1;t:for(;;){switch(i[o]){case"(":a++;break;case")":if(0===--a)break t;break;case void 0:Fo(i)}o++}var s=i.slice(r+Jo,o);Xo(s)&&(s=_o(s,e));var l=String(Vo(s,e));i=i.slice(0,r)+l+i.slice(o+1),n=r+l.length}}function Qo(o,a){return function(t,e){var i=Er(t);t=parseFloat(t),i&&(t/=100);var n={};if(isFinite(t)){var r=i||0<=t&&t<=1?t*e[a]:Math.max(t+e[a],0);n[o]=r}return n}}function Ko(a,s,l){return function(t,e){var i,n=Er(t);if(t=parseFloat(t),n&&(t/=100),isFinite(t)){var r=e[l]();i=n||0e.width?sa:la)(t,e)})},refRCircumscribed:{set:function(t,e){var i=Er(t);t=parseFloat(t),i&&(t/=100);var n,r=Math.sqrt(e.height*e.height+e.width*e.width);return isFinite(t)&&(n=i||0<=t&&t<=1?t*r:Math.max(t+r,0)),{r:n}}},refCx:{set:Qo("cx","width")},refCy:{set:Qo("cy","height")},xAlignment:{offset:qo("x","width","right")},yAlignment:{offset:qo("y","height","bottom")},resetOffset:{offset:function(t,e){return t?{x:-e.x,y:-e.y}:{x:0,y:0}}},refDResetOffset:{set:ta({resetOffset:!0})},refDKeepOffset:{set:ta({resetOffset:!1})},refPointsResetOffset:{set:ea({resetOffset:!0})},refPointsKeepOffset:{set:ea({resetOffset:!1})},connection:{qualify:ra,set:function(t){var e,i=t.stubs;if(void 0===i&&(i=0),isFinite(i)&&0!==i){var n;n=i<0?(this.getConnectionLength()+i)/2:i;var r=this.getConnection(),o=r.divideAtLength(n),a=r.divideAtLength(-n);o&&a&&(e=o[0].serialize()+" "+a[1].serialize())}return{d:e||this.getSerializedConnection()}}},atConnectionLengthKeepGradient:{qualify:ra,set:ia("getTangentAtLength",{rotate:!0})},atConnectionLengthIgnoreGradient:{qualify:ra,set:ia("getTangentAtLength",{rotate:!1})},atConnectionRatioKeepGradient:{qualify:ra,set:ia("getTangentAtRatio",{rotate:!0})},atConnectionRatioIgnoreGradient:{qualify:ra,set:ia("getTangentAtRatio",{rotate:!1})}};["transform","d","points","width","height","cx","cy","r","rx","ry","x1","x2","y1","y2","x","y","dx","dy"].forEach(function(n){ca[n]={qualify:Xo,set:function(t,e){var i;return(i={})[n]=_o(t,e),i}}}),ca.refR=ca.refRInscribed,ca.refD=ca.refDResetOffset,ca.refPoints=ca.refPointsResetOffset,ca.atConnectionLength=ca.atConnectionLengthKeepGradient,ca.atConnectionRatio=ca.atConnectionRatioKeepGradient,ca.refX2=ca.refX,ca.refY2=ca.refY,ca.refWidth2=ca.refWidth,ca.refHeight2=ca.refHeight,ca["ref-x"]=ca.refX,ca["ref-y"]=ca.refY,ca["ref-dy"]=ca.refDy,ca["ref-dx"]=ca.refDx,ca["ref-width"]=ca.refWidth,ca["ref-height"]=ca.refHeight,ca["x-alignment"]=ca.xAlignment,ca["y-alignment"]=ca.yAlignment;var ua=ca,ha=s.Model.extend({constructor:function(t,e){var i,n=t||{};"function"==typeof this.preinitialize&&this.preinitialize.apply(this,arguments),this.cid=Lo("c"),this.attributes={},e&&e.collection&&(this.collection=e.collection),e&&e.parse&&(n=this.parse(n,e)||{}),(i=xo(this,"defaults"))&&(n=zo({},i,n)),this.set(n,e),this.changed={},this.initialize.apply(this,arguments)},translate:function(t,e,i){throw new Error("Must define a translate() method.")},toJSON:function(){var e=xo(this.constructor.prototype,"defaults").attrs||{},t=this.attributes.attrs,o={};Do(t,function(t,n){var r=e[n];Do(t,function(t,i){jo(t)&&!Array.isArray(t)?Do(t,function(t,e){r&&r[i]&&ho(r[i][e],t)||(o[n]=o[n]||{},(o[n][i]||(o[n][i]={}))[e]=t)}):r&&ho(r[i],t)||(o[n]=o[n]||{},o[n][i]=t)})});var i=co(Mo(this.attributes,"attrs"));return i.attrs=o,i},initialize:function(t){t&&t.id||this.set("id",this.generateId(),{silent:!0}),this._transitionIds={},this.processPorts(),this.on("change:attrs",this.processPorts,this)},generateId:function(){return Pr()},processPorts:function(){var t=this.ports,i={};Do(this.get("attrs"),function(t,e){t&&t.port&&(void 0!==t.port.id?i[t.port.id]=t.port:i[t.port]={id:t.port})});var n={};(Do(t,function(t,e){i[e]||(n[e]=!0)}),this.graph&&!uo(n))&&(this.graph.getConnectedLinks(this,{inbound:!0}).forEach(function(t){n[t.get("target").port]&&t.remove()}),this.graph.getConnectedLinks(this,{outbound:!0}).forEach(function(t){n[t.get("source").port]&&t.remove()}));this.ports=i},remove:function(t){void 0===t&&(t={});var e=this.graph,i=this.collection;if(!e)return i&&i.remove(this,t),this;e.startBatch("remove");var n=this.getParentCell();n&&n.unembed(this,t);for(var r=this.getEmbeddedCells(),o=0,a=r.length;oc[2]?(a=".3em",r=u,o=0,"start"):lh[2]?(a=".3em",r=-c,o=0,"end"):l','','','','','','',''].join(""),toolMarkup:['','','','',"Remove link.","",'','','',"Link options.","",""].join(""),doubleToolMarkup:void 0,vertexMarkup:['','','','',"Remove vertex.","",""].join(""),arrowheadMarkup:['','',""].join(""),defaultLabel:void 0,labelMarkup:void 0,_builtins:{defaultLabel:{markup:[{tagName:"rect",selector:"rect"},{tagName:"text",selector:"text"}],attrs:{text:{fill:"#000000",fontSize:14,textAnchor:"middle",yAlignment:"middle",pointerEvents:"none"},rect:{ref:"text",fill:"#ffffff",rx:3,ry:3,refWidth:1,refHeight:1,refX:0,refY:0}},position:{distance:.5}}},defaults:{type:"link",source:{},target:{}},isLink:function(){return!0},disconnect:function(t){return this.set({source:{x:0,y:0},target:{x:0,y:0}},t)},source:function(t,e,i){return void 0===t?lo(this.get("source")):(r=t instanceof ha?((n=lo(e)||{}).id=t.id,i):fo(t)?(n=t,e):((n=lo(e)||{}).x=t.x,n.y=t.y,i),this.set("source",n,r));var n,r},target:function(t,e,i){return void 0===t?lo(this.get("target")):(r=t instanceof ha?((n=lo(e)||{}).id=t.id,i):fo(t)?(n=t,e):((n=lo(e)||{}).x=t.x,n.y=t.y,i),this.set("target",n,r));var n,r},router:function(t,e,i){if(void 0===t){var n=this.get("router");return n?"object"==typeof n?lo(n):n:this.get("manhattan")?{name:"orthogonal"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},a=r?e:i;return this.set("router",o,a)},connector:function(t,e,i){if(void 0===t){var n=this.get("connector");return n?"object"==typeof n?lo(n):n:this.get("smooth")?{name:"smooth"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},a=r?e:i;return this.set("connector",o,a)},label:function(t,e,i){var n=this.labels();return(t=isFinite(t)&&null!==t?0|t:0)<0&&(t=n.length+t),arguments.length<=1?this.prop(["labels",t]):this.prop(["labels",t],e,i)},labels:function(t,e){return 0===arguments.length?(t=this.get("labels"),Array.isArray(t)?t.slice():[]):(Array.isArray(t)||(t=[]),this.set("labels",t,e))},hasLabels:function(){var t=this.attributes.labels;return Array.isArray(t)&&0 "+e),i=this.getSelector(t.parentNode,i)}return i},addLinkFromMagnet:function(t,e,i){var n=this.paper,r=n.model,o=n.getDefaultLink(this,t);return o.set({source:this.getLinkEnd(t,e,i,o,"source"),target:{x:e,y:i}}).addTo(r,{async:!1,ui:!0}),o.findView(n)},getLinkEnd:function(t){for(var e,i=[],n=arguments.length-1;0r.options.clickThreshold||this.notify("element:magnet:pointerclick",t,e,i,n)}},{Flags:Ua});io(Fa.prototype,Da);var Va={N:"S",S:"N",E:"W",W:"E"},Xa={N:-Math.PI/2*3,S:-Math.PI/2,E:0,W:Math.PI};function Ha(t,e,i){var n=new qi(t.x,e.y);return i.containsPoint(n)&&(n=new qi(e.x,t.y)),n}function Ja(t,e){return t["W"===e||"E"===e?"width":"height"]}function _a(t,e){return t.x===e.x?t.y>e.y?"N":"S":t.y===e.y?t.x>e.x?"W":"E":null}function Qa(t){return new In(t.x,t.y,0,0)}function Ka(t){var e=Xr(t.padding||t.elementPadding||20);return{x:-e.left,y:-e.top,width:e.left+e.right,height:e.top+e.bottom}}function qa(t,e){return t.sourceBBox.clone().moveAndExpand(Ka(e))}function $a(t,e){return t.targetBBox.clone().moveAndExpand(Ka(e))}function ts(t,e,i){var n=new qi(t.x,e.y),r=new qi(e.x,t.y),o=_a(t,n),a=_a(t,r),s=Va[i],l=o===i||o!==s&&(a===s||a!==i)?n:r;return{points:[l],direction:_a(l,e)}}function es(t,e,i){var n=Ha(t,e,i);return{points:[n],direction:_a(n,e)}}function is(e,t,i,n){var r,o={},a=[new qi(e.x,t.y),new qi(t.x,e.y)],s=a.filter(function(t){return!i.containsPoint(t)}),l=s.filter(function(t){return _a(t,e)!==n});if(0c.center().distance(t),h=u?e:t,g=u?t:e;return a=Ha(o=r?(o=qi.fromPolar(c.width+c.height,Xa[r],h),c.pointNearestToPoint(o).move(o,-1)):c.pointNearestToPoint(h).move(h,1),g,c),o.round().equals(a.round())?(a=qi.fromPolar(c.width+c.height,Pi(o.theta(h))+Math.PI/2,g),s=Ha(o,a=c.pointNearestToPoint(a).move(g,1).round(),c),l.points=u?[a,s,o]:[o,s,a]):l.points=u?[a,o]:[o,a],l.direction=_a(u?o:a,e),l}function os(t,e,i){var n,r,o,a,s,l=qa(i,e),c=$a(i,e),u=(r=e,(n=i).sourceAnchor?n.sourceAnchor:qa(n,r).center()),h=(a=e,(o=i).targetAnchor?o.targetAnchor:$a(o,a).center());l=l.union(Qa(u)),c=c.union(Qa(h)),(t=po(t).map(qi)).unshift(u),t.push(h);for(var g=[],d=0,f=t.length-1;dn.maxAllowedDirectionChange)){var X=ms(O.clone().offset(k.gridOffsetX,k.gridOffsetY),A,l),H=Is(X);if(!C.isClose(H)&&!i(X)){if(0<=j.indexOf(H))if(!X.equals(d))if(ds(V,gs(X,d,P,A,n))>n.maxAllowedDirectionChange)continue;var J=R+k.cost+(W?0:n.penalties[D]);(!C.isOpen(H)||Je[1].x?"right":"left";switch(n){case"left":return new qi(-1,0);case"right":default:return new qi(1,0)}}(t,e);case js.VERTICAL:return function(t,e,i){var n,r=t.sourceBBox;n=r.width&&r.height?r.sideNearestToPoint(e[0]):r.y>e[1].y?"bottom":"top";switch(n){case"top":return new qi(0,-1);case"bottom":default:return new qi(0,1)}}(t,e);case js.CLOSEST_POINT:return Es(t,e,n);case js.OUTWARDS:return Ys(t,e,n);case js.AUTO:default:return Bs(t,e,n)}}(r,h,a,l),d=h[0].distance(h[1])*l.coeff,f=Fs(g,h[1].difference(h[0]).normalize());if(f>Math.PI/4){var p=d+(f-Math.PI/4)*l.angleTangentCoefficient;c=g.clone().scale(p,p)}else c=g.clone().scale(d,d)}if(l.targetTangent)u=l.targetTangent;else{var m=function(t,e,i,n){if(n.targetDirection)switch(n.targetDirection){case Zs.UP:return new qi(0,-1);case Zs.DOWN:return new qi(0,1);case Zs.LEFT:return new qi(-1,0);case Zs.RIGHT:return new qi(0,1);case Zs.AUTO:return Os(t,e,n);case Zs.CLOSEST_POINT:return Rs(t,e,n);case Zs.OUTWARDS:return Ws(t,e,n);default:return n.targetDirection}switch(i){case js.HORIZONTAL:return function(t,e,i){var n,r=t.targetBBox;n=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.x>e[e.length-2].x?"left":"right";switch(n){case"left":return new qi(-1,0);case"right":default:return new qi(1,0)}}(t,e);case js.VERTICAL:return function(t,e,i){var n,r=t.targetBBox;n=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.y>e[e.length-2].y?"top":"bottom";switch(n){case"top":return new qi(0,-1);case"bottom":default:return new qi(0,1)}}(t,e);case js.CLOSEST_POINT:return Rs(t,e,n);case js.OUTWARDS:return Ws(t,e,n);case js.AUTO:default:return Os(t,e,n)}}(r,h,a,l),I=h.length-1,v=h[I-1].distance(h[I])*l.coeff,A=Fs(m,h[I-1].difference(h[I]).normalize());if(A>Math.PI/4){var C=v+(A-Math.PI/4)*l.angleTangentCoefficient;u=m.clone().scale(C,C)}else u=m.clone().scale(v,v)}var y=function(t,e,i,n){for(var r=n.tau,o=n.coeff,a=[],s=[],l=[],c=t.length-1,u=0;u=Math.abs(t.y-e.y)){var c=(t.x+e.x)/2;l=Tn.createSegment("C",c,t.y,c,e.y,e.x,e.y),r.appendSegment(l)}else{var u=(t.y+e.y)/2;l=Tn.createSegment("C",t.x,u,e.x,u,e.x,e.y),r.appendSegment(l)}}return o?r:r.serialize()},curve:Gs},Hs={RENDER:"RENDER",UPDATE:"UPDATE",TOOLS:"TOOLS",LEGACY_TOOLS:"LEGACY_TOOLS",LABELS:"LABELS",VERTICES:"VERTICES",SOURCE:"SOURCE",TARGET:"TARGET",CONNECTOR:"CONNECTOR"},Js=Wa.extend({className:function(){var t=Wa.prototype.className.apply(this).split(" ");return t.push("link"),t.join(" ")},options:{shortLinkLength:105,doubleLinkTools:!1,longLinkLength:155,linkToolsOffset:40,doubleLinkToolsOffset:65,sampleInterval:50},_labelCache:null,_labelSelectors:null,_markerCache:null,_V:null,_dragData:null,metrics:null,decimalsRounding:2,initialize:function(){Wa.prototype.initialize.apply(this,arguments),this._labelCache={},this._labelSelectors={},this._markerCache={},this._V={},this.cleanNodesCache()},presentationAttributes:{markup:[Hs.RENDER],attrs:[Hs.UPDATE],router:[Hs.UPDATE],connector:[Hs.CONNECTOR],smooth:[Hs.UPDATE],manhattan:[Hs.UPDATE],toolMarkup:[Hs.LEGACY_TOOLS],labels:[Hs.LABELS],labelMarkup:[Hs.LABELS],vertices:[Hs.VERTICES,Hs.UPDATE],vertexMarkup:[Hs.VERTICES],source:[Hs.SOURCE,Hs.UPDATE],target:[Hs.TARGET,Hs.UPDATE]},initFlag:[Hs.RENDER,Hs.SOURCE,Hs.TARGET,Hs.TOOLS],UPDATE_PRIORITY:1,confirmUpdate:function(t,e){if(e||(e={}),this.hasFlag(t,Hs.SOURCE)){if(!this.updateEndProperties("source"))return t;t=this.removeFlag(t,Hs.SOURCE)}if(this.hasFlag(t,Hs.TARGET)){if(!this.updateEndProperties("target"))return t;t=this.removeFlag(t,Hs.TARGET)}var i=this.paper,n=this.sourceView,r=this.targetView;if(i&&(n&&!i.isViewMounted(n)||r&&!i.isViewMounted(r)))return t;if(this.hasFlag(t,Hs.RENDER))return this.render(),this.updateHighlighters(!0),this.updateTools(e),t=this.removeFlag(t,[Hs.RENDER,Hs.UPDATE,Hs.VERTICES,Hs.LABELS,Hs.TOOLS,Hs.LEGACY_TOOLS,Hs.CONNECTOR]);var o=!1;this.hasFlag(t,Hs.VERTICES)&&(this.renderVertexMarkers(),t=this.removeFlag(t,Hs.VERTICES));var a=this.model,s=a.attributes,l=this.hasFlag(t,Hs.LABELS),c=this.hasFlag(t,Hs.LEGACY_TOOLS);l&&(this.onLabelsChange(a,s.labels,e),t=this.removeFlag(t,Hs.LABELS),o=!0),c&&(this.renderTools(),t=this.removeFlag(t,Hs.LEGACY_TOOLS));var u=this.hasFlag(t,Hs.UPDATE),h=this.hasFlag(t,Hs.CONNECTOR);return(u||h)&&(u?e.translateBy&&a.isRelationshipEmbeddedIn(e.translateBy)?this.translate(e.tx,e.ty):this.update():(this.updatePath(),this.updateDOM()),this.updateTools(e),t=this.removeFlag(t,[Hs.UPDATE,Hs.TOOLS,Hs.CONNECTOR]),o=!(c=l=!1)),l&&this.updateLabelPositions(),c&&this.updateToolsPosition(),o&&this.updateHighlighters(),this.hasFlag(t,Hs.TOOLS)&&(this.updateTools(e),t=this.removeFlag(t,Hs.TOOLS)),t},requestConnectionUpdate:function(t){this.requestUpdate(this.getFlag(Hs.UPDATE),t)},isLabelsRenderRequired:function(t){void 0===t&&(t={});var e=this.model.previous("labels");if(!e)return!0;if("propertyPathArray"in t&&"propertyValue"in t){var i=t.propertyPathArray||[],n=i.length;if(1=this.options.longLinkLength){var r=this.options.doubleLinkToolsOffset||e;n=this.getPointAtLength(i-r),this._tool2Cache.attr("transform","translate("+n.x+", "+n.y+") "+t),this._tool2Cache.attr("visibility","visible")}else this.options.doubleLinkTools&&this._tool2Cache.attr("visibility","hidden")}return this},updateArrowheadMarkers:function(){if(!this._V.markerArrowheads)return this;if("none"===S.css(this._V.markerArrowheads.node,"display"))return this;var t=this.getConnectionLength()'}),Ml=Fa.extend({presentationAttributes:Fa.addPresentationAttributes({attrs:["SCALE"]}),confirmUpdate:function(){var t=Fa.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"SCALE")&&(this.resize(),t=this.removeFlag(t,"SCALE")),t}}),Sl=Nl.define("basic.Text",{attrs:{text:{"font-size":18,fill:"#000000"}}},{markup:''}),kl=Nl.define("basic.Circle",{size:{width:60,height:60},attrs:{circle:{fill:"#ffffff",stroke:"#000000",r:30,cx:30,cy:30},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Dl=Nl.define("basic.Ellipse",{size:{width:60,height:40},attrs:{ellipse:{fill:"#ffffff",stroke:"#000000",rx:30,ry:20,cx:30,cy:20},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Tl=Nl.define("basic.Polygon",{size:{width:60,height:40},attrs:{polygon:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Ll=Nl.define("basic.Polyline",{size:{width:60,height:40},attrs:{polyline:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),zl=Nl.define("basic.Image",{attrs:{text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Pl=Nl.define("basic.Path",{size:{width:60,height:60},attrs:{path:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle",ref:"path","ref-x":.5,"ref-dy":10,fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),jl=Pl.define("basic.Rhombus",{attrs:{path:{d:"M 30 0 L 60 30 30 60 0 30 z"},text:{"ref-y":.5,"ref-dy":null,"y-alignment":"middle"}}}),Zl=bl.test("svgforeignobject"),Gl=Nl.define("basic.TextBlock",{attrs:{rect:{fill:"#ffffff",stroke:"#000000",width:80,height:100},text:{fill:"#000000","font-size":14,"font-family":"Arial, helvetica, sans-serif"},".content":{text:"","ref-x":.5,"ref-y":.5,"y-alignment":"middle","x-alignment":"middle"}},content:""},{markup:['','',Zl?'
':'',""].join(""),initialize:function(){this.listenTo(this,"change:size",this.updateSize),this.listenTo(this,"change:content",this.updateContent),this.updateSize(this,this.get("size")),this.updateContent(this,this.get("content")),Nl.prototype.initialize.apply(this,arguments)},updateSize:function(t,e){this.attr({".fobj":io({},e),div:{style:io({},e)}})},updateContent:function(t,e){Zl?this.attr({".content":{html:Wr(e)}}):this.attr({".content":{text:e}})},setForeignObjectSize:function(){this.updateSize.apply(this,arguments)},setDivContent:function(){this.updateContent.apply(this,arguments)}}),Bl=Fa.extend({presentationAttributes:Zl?Fa.prototype.presentationAttributes:Fa.addPresentationAttributes({content:["CONTENT"],size:["CONTENT"]}),initFlag:["RENDER","CONTENT"],confirmUpdate:function(){var t=Fa.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"CONTENT")&&(this.updateContent(this.model),t=this.removeFlag(t,"CONTENT")),t},update:function(t,e){var i=this.model;if(Zl)Fa.prototype.update.call(this,i,e);else{var n=Mo(e||i.get("attrs"),".content");Fa.prototype.update.call(this,i,n),e&&!No(e,".content")||this.updateContent(i,e)}},updateContent:function(t,e){var i=zo({},(e||t.get("attrs"))[".content"]);i=Mo(i,"text");var n=Yr(t.get("content"),t.get("size"),i,{svgDocument:this.paper.svg}),r=Tr({},".content",i,"/");r[".content"].text=n,Fa.prototype.update.call(this,t,r)}}),Ol={Generic:Nl,Rect:xl,TextView:Ml,Text:Sl,Circle:kl,Ellipse:Dl,Polygon:Tl,Polyline:Ll,Image:zl,Path:Pl,Rhombus:jl,TextBlock:Gl,TextBlockView:Bl},El=Ta.define("standard.Rectangle",{attrs:{body:{refWidth:"100%",refHeight:"100%",strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"text",selector:"label"}]}),Rl=Ta.define("standard.Circle",{attrs:{body:{refCx:"50%",refCy:"50%",refR:"50%",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"circle",selector:"body"},{tagName:"text",selector:"label"}]}),Yl=Ta.define("standard.Ellipse",{attrs:{body:{refCx:"50%",refCy:"50%",refRx:"50%",refRy:"50%",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"ellipse",selector:"body"},{tagName:"text",selector:"label"}]}),Wl=Ta.define("standard.Path",{attrs:{body:{refD:"M 0 0 L 10 0 10 10 0 10 Z",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"path",selector:"body"},{tagName:"text",selector:"label"}]}),Ul=Ta.define("standard.Polygon",{attrs:{body:{refPoints:"0 0 10 0 10 10 0 10",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"polygon",selector:"body"},{tagName:"text",selector:"label"}]}),Fl=Ta.define("standard.Polyline",{attrs:{body:{refPoints:"0 0 10 0 10 10 0 10 0 0",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"polyline",selector:"body"},{tagName:"text",selector:"label"}]}),Vl=Ta.define("standard.Image",{attrs:{image:{refWidth:"100%",refHeight:"100%"},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"image",selector:"image"},{tagName:"text",selector:"label"}]}),Xl=Ta.define("standard.BorderedImage",{attrs:{border:{refWidth:"100%",refHeight:"100%",stroke:"#333333",strokeWidth:2},background:{refWidth:-1,refHeight:-1,x:.5,y:.5,fill:"#FFFFFF"},image:{refWidth:-1,refHeight:-1,x:.5,y:.5},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"background",attributes:{stroke:"none"}},{tagName:"image",selector:"image"},{tagName:"rect",selector:"border",attributes:{fill:"none"}},{tagName:"text",selector:"label"}]}),Hl=Ta.define("standard.EmbeddedImage",{attrs:{body:{refWidth:"100%",refHeight:"100%",stroke:"#333333",fill:"#FFFFFF",strokeWidth:2},image:{refWidth:"30%",refHeight:-20,x:10,y:10,preserveAspectRatio:"xMidYMin"},label:{textVerticalAnchor:"top",textAnchor:"left",refX:"30%",refX2:20,refY:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"image",selector:"image"},{tagName:"text",selector:"label"}]}),Jl=Ta.define("standard.InscribedImage",{attrs:{border:{refRx:"50%",refRy:"50%",refCx:"50%",refCy:"50%",stroke:"#333333",strokeWidth:2},background:{refRx:"50%",refRy:"50%",refCx:"50%",refCy:"50%",fill:"#FFFFFF"},image:{refWidth:"68%",refHeight:"68%",refX:"16%",refY:"16%",preserveAspectRatio:"xMidYMid"},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"ellipse",selector:"background"},{tagName:"image",selector:"image"},{tagName:"ellipse",selector:"border",attributes:{fill:"none"}},{tagName:"text",selector:"label"}]}),_l=Ta.define("standard.HeaderedRectangle",{attrs:{body:{refWidth:"100%",refHeight:"100%",strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},header:{refWidth:"100%",height:30,strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},headerText:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:15,fontSize:16,fill:"#333333"},bodyText:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",refY2:15,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"rect",selector:"header"},{tagName:"text",selector:"headerText"},{tagName:"text",selector:"bodyText"}]}),Ql=Ta.define("standard.Cylinder",{attrs:{body:{lateralArea:10,fill:"#FFFFFF",stroke:"#333333",strokeWidth:2},top:{refCx:"50%",cy:10,refRx:"50%",ry:10,fill:"#FFFFFF",stroke:"#333333",strokeWidth:2},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"100%",refY2:15,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"path",selector:"body"},{tagName:"ellipse",selector:"top"},{tagName:"text",selector:"label"}],topRy:function(t,e){if(void 0===t)return this.attr("body/lateralArea");var i={lateralArea:t},n=Er(t)?{refCy:t,refRy:t,cy:null,ry:null}:{refCy:null,refRy:null,cy:t,ry:t};return this.attr({body:i,top:n},e)}},{attributes:{lateralArea:{set:function(t,e){var i=Er(t);i&&(t=parseFloat(t)/100);var n=e.x,r=e.y,o=e.width,a=e.height,s=o/2,l=i?a*t:t,c=wr.KAPPA,u=c*s,h=c*(i?a*t:t),g=n+o/2,d=n+o,f=r+l,p=f-l,m=r+a-l,I=r+a;return{d:["M",n,f,"L",n,m,"C",n,m+h,g-u,I,g,I,"C",g+u,I,d,m+h,d,m,"L",d,f,"C",d,f-h,g+u,p,g,p,"C",g-u,p,n,f-h,n,f,"Z"].join(" ")}}}}}),Kl=bl.test("svgforeignobject")?{tagName:"foreignObject",selector:"foreignObject",attributes:{overflow:"hidden"},children:[{tagName:"div",namespaceURI:"http://www.w3.org/1999/xhtml",selector:"label",style:{width:"100%",height:"100%",position:"static",backgroundColor:"transparent",textAlign:"center",margin:0,padding:"0px 5px",boxSizing:"border-box",display:"flex",alignItems:"center",justifyContent:"center"}}]}:{tagName:"text",selector:"label",attributes:{"text-anchor":"middle"}},ql={Rectangle:El,Circle:Rl,Ellipse:Yl,Path:Wl,Polygon:Ul,Polyline:Fl,Image:Vl,BorderedImage:Xl,EmbeddedImage:Hl,InscribedImage:Jl,HeaderedRectangle:_l,Cylinder:Ql,TextBlock:Ta.define("standard.TextBlock",{attrs:{body:{refWidth:"100%",refHeight:"100%",stroke:"#333333",fill:"#ffffff",strokeWidth:2},foreignObject:{refWidth:"100%",refHeight:"100%"},label:{style:{fontSize:14}}}},{markup:[{tagName:"rect",selector:"body"},Kl]},{attributes:{text:{set:function(t,e,i,n){if(!(i instanceof HTMLElement)){var r=n.style||{},o={text:t,width:-5,height:"100%"},a=io({textVerticalAnchor:"middle"},r);return ua.textWrap.set.call(this,o,e,i,a),{fill:r.color||null}}i.textContent=t},position:function(t,e,i){if(i instanceof SVGElement)return e.center()}}}}),Link:Ma.define("standard.Link",{attrs:{line:{connection:!0,stroke:"#333333",strokeWidth:2,strokeLinejoin:"round",targetMarker:{type:"path",d:"M 10 -5 0 0 10 5 z"}},wrapper:{connection:!0,strokeWidth:10,strokeLinejoin:"round"}}},{markup:[{tagName:"path",selector:"wrapper",attributes:{fill:"none",cursor:"pointer",stroke:"transparent","stroke-linecap":"round"}},{tagName:"path",selector:"line",attributes:{fill:"none","pointer-events":"none"}}]}),DoubleLink:Ma.define("standard.DoubleLink",{attrs:{line:{connection:!0,stroke:"#DDDDDD",strokeWidth:4,strokeLinejoin:"round",targetMarker:{type:"path",stroke:"#000000",d:"M 10 -3 10 -10 -2 0 10 10 10 3"}},outline:{connection:!0,stroke:"#000000",strokeWidth:6,strokeLinejoin:"round"}}},{markup:[{tagName:"path",selector:"outline",attributes:{fill:"none"}},{tagName:"path",selector:"line",attributes:{fill:"none"}}]}),ShadowLink:Ma.define("standard.ShadowLink",{attrs:{line:{connection:!0,stroke:"#FF0000",strokeWidth:20,strokeLinejoin:"round",targetMarker:{type:"path",stroke:"none",d:"M 0 -10 -10 0 0 10 z"},sourceMarker:{type:"path",stroke:"none",d:"M -10 -10 0 0 -10 10 0 10 0 -10 z"}},shadow:{connection:!0,refX:3,refY:6,stroke:"#000000",strokeOpacity:.2,strokeWidth:20,strokeLinejoin:"round",targetMarker:{type:"path",d:"M 0 -10 -10 0 0 10 z",stroke:"none"},sourceMarker:{type:"path",stroke:"none",d:"M -10 -10 0 0 -10 10 0 10 0 -10 z"}}}},{markup:[{tagName:"path",selector:"shadow",attributes:{fill:"none"}},{tagName:"path",selector:"line",attributes:{fill:"none"}}]})},$l=Nl.define("devs.Model",{inPorts:[],outPorts:[],size:{width:80,height:80},attrs:{".":{magnet:!1},".label":{text:"Model","ref-x":.5,"ref-y":10,"font-size":18,"text-anchor":"middle",fill:"#000"},".body":{"ref-width":"100%","ref-height":"100%",stroke:"#000"}},ports:{groups:{in:{position:{name:"left"},attrs:{".port-label":{fill:"#000"},".port-body":{fill:"#fff",stroke:"#000",r:10,magnet:!0}},label:{position:{name:"left",args:{y:10}}}},out:{position:{name:"right"},attrs:{".port-label":{fill:"#000"},".port-body":{fill:"#fff",stroke:"#000",r:10,magnet:!0}},label:{position:{name:"right",args:{y:10}}}}}}},{markup:'',portMarkup:'',portLabelMarkup:'',initialize:function(){Nl.prototype.initialize.apply(this,arguments),this.on("change:inPorts change:outPorts",this.updatePortItems,this),this.updatePortItems()},updatePortItems:function(t,e,i){var n=so(this.get("inPorts")),r=yo(so(this.get("outPorts")),n),o=this.createPortItems("in",n),a=this.createPortItems("out",r);this.prop("ports/items",o.concat(a),io({rewrite:!0},i))},createPortItem:function(t,e){return{id:e,group:t,attrs:{".port-label":{text:e}}}},createPortItems:function(t,e){return po(e).map(this.createPortItem.bind(this,t))},_addGroupPort:function(t,e,i){var n=this.get(e);return this.set(e,Array.isArray(n)?n.concat(t):[t],i)},addOutPort:function(t,e){return this._addGroupPort(t,"outPorts",e)},addInPort:function(t,e){return this._addGroupPort(t,"inPorts",e)},_removeGroupPort:function(t,e,i){return this.set(e,Co(this.get(e),t),i)},removeOutPort:function(t,e){return this._removeGroupPort(t,"outPorts",e)},removeInPort:function(t,e){return this._removeGroupPort(t,"inPorts",e)},_changeGroup:function(t,e,i){return this.prop("ports/groups/"+t,jo(e)?e:{},i)},changeInGroup:function(t,e){return this._changeGroup("in",t,e)},changeOutGroup:function(t,e){return this._changeGroup("out",t,e)}}),tc=$l.define("devs.Atomic",{size:{width:80,height:80},attrs:{".label":{text:"Atomic"}}}),ec=$l.define("devs.Coupled",{size:{width:200,height:300},attrs:{".label":{text:"Coupled"}}}),ic={Model:$l,Atomic:tc,Coupled:ec,Link:Ma.define("devs.Link",{attrs:{".connection":{"stroke-width":2}}})},nc=Nl.define("logic.Gate",{size:{width:80,height:40},attrs:{".":{magnet:!1},".body":{width:100,height:50},circle:{r:7,stroke:"black",fill:"transparent","stroke-width":2}}},{operation:function(){return!0}}),rc=nc.define("logic.IO",{size:{width:60,height:30},attrs:{".body":{fill:"white",stroke:"black","stroke-width":2},".wire":{ref:".body","ref-y":.5,stroke:"black"},text:{fill:"black",ref:".body","ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle","font-weight":"bold","font-variant":"small-caps","text-transform":"capitalize","font-size":"14px"}}},{markup:''}),oc=rc.define("logic.Input",{attrs:{".wire":{"ref-dx":0,d:"M 0 0 L 23 0"},circle:{ref:".body","ref-dx":30,"ref-y":.5,magnet:!0,class:"output",port:"out"},text:{text:"input"}}}),ac=rc.define("logic.Output",{attrs:{".wire":{"ref-x":0,d:"M 0 0 L -23 0"},circle:{ref:".body","ref-x":-30,"ref-y":.5,magnet:"passive",class:"input",port:"in"},text:{text:"output"}}}),sc=nc.define("logic.Gate11",{attrs:{".input":{ref:".body","ref-x":-2,"ref-y":.5,magnet:"passive",port:"in"},".output":{ref:".body","ref-dx":2,"ref-y":.5,magnet:!0,port:"out"}}},{markup:''}),lc=nc.define("logic.Gate21",{attrs:{".input1":{ref:".body","ref-x":-2,"ref-y":.3,magnet:"passive",port:"in1"},".input2":{ref:".body","ref-x":-2,"ref-y":.7,magnet:"passive",port:"in2"},".output":{ref:".body","ref-dx":2,"ref-y":.5,magnet:!0,port:"out"}}},{markup:''}),cc=sc.define("logic.Repeater",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzIuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICA8L2c+Cjwvc3ZnPgo="}}},{operation:function(t){return t}}),uc=sc.define("logic.Not",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzkuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI2NzEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzYiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA4MCwyNSBBIDQsNCAwIDEgMSA3MiwyNSBBIDQsNCAwIDEgMSA4MCwyNSB6IgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEsMCkiIC8+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t){return!t}}),hc=lc.define("logic.Or",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik9SIEFOU0kuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTAgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjcxNCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iMSA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMC41IDogMC4zMzMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODA2IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODE5IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjM3Mi4wNDcyNCA6IDM1MC43ODczOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI3NDQuMDk0NDggOiA1MjYuMTgxMDkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzc3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49Ijc1IDogNDAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTUwIDogNjAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDYwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTMyNzUiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNTAgOiAzMy4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTAwIDogNTAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTU1MzMiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjU1NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDE2LjY2NjY2NyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDI1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAyNSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iNCIKICAgICBpbmtzY2FwZTpjeD0iMTEzLjAwMDM5IgogICAgIGlua3NjYXBlOmN5PSIxMi44OTM3MzEiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImcyNTYwIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1wb2ludHM9InRydWUiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAwMDAiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMzk5IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg3NCIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMzciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii00IgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNzAsMjUgYyAyMCwwIDI1LDAgMjUsMCIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMSwxNSA1LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzIsMzUgNSwzNSIKICAgICAgIGlkPSJwYXRoMzk0NCIgLz4KICAgIDxnCiAgICAgICBpZD0iZzI1NjAiCiAgICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2LjUsLTM5LjUpIj4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo="}}},{operation:function(t,e){return t||e}}),gc=lc.define("logic.And",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IkFORCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI4IgogICAgIGlua3NjYXBlOmN4PSI1Ni42OTgzNDgiCiAgICAgaW5rc2NhcGU6Y3k9IjI1LjMyNjg5OSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTptZWRpdW07Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWluZGVudDowO3RleHQtYWxpZ246c3RhcnQ7dGV4dC1kZWNvcmF0aW9uOm5vbmU7bGluZS1oZWlnaHQ6bm9ybWFsO2xldHRlci1zcGFjaW5nOm5vcm1hbDt3b3JkLXNwYWNpbmc6bm9ybWFsO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlyZWN0aW9uOmx0cjtibG9jay1wcm9ncmVzc2lvbjp0Yjt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDozO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGU7Zm9udC1mYW1pbHk6Qml0c3RyZWFtIFZlcmEgU2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkJpdHN0cmVhbSBWZXJhIFNhbnMiCiAgICAgICBkPSJNIDMwLDUgTCAzMCw2LjQyODU3MTQgTCAzMCw0My41NzE0MjkgTCAzMCw0NSBMIDMxLjQyODU3MSw0NSBMIDUwLjQ3NjE5LDQ1IEMgNjEuNzQ0MDk4LDQ1IDcwLjQ3NjE5LDM1Ljk5OTk1NSA3MC40NzYxOSwyNSBDIDcwLjQ3NjE5LDE0LjAwMDA0NSA2MS43NDQwOTksNS4wMDAwMDAyIDUwLjQ3NjE5LDUgQyA1MC40NzYxOSw1IDUwLjQ3NjE5LDUgMzEuNDI4NTcxLDUgTCAzMCw1IHogTSAzMi44NTcxNDMsNy44NTcxNDI5IEMgNDAuODM0MjY0LDcuODU3MTQyOSA0NS45MTgzNjgsNy44NTcxNDI5IDQ4LjA5NTIzOCw3Ljg1NzE0MjkgQyA0OS4yODU3MTQsNy44NTcxNDI5IDQ5Ljg4MDk1Miw3Ljg1NzE0MjkgNTAuMTc4NTcxLDcuODU3MTQyOSBDIDUwLjMyNzM4MSw3Ljg1NzE0MjkgNTAuNDA5MjI3LDcuODU3MTQyOSA1MC40NDY0MjksNy44NTcxNDI5IEMgNTAuNDY1MDI5LDcuODU3MTQyOSA1MC40NzE1NDMsNy44NTcxNDI5IDUwLjQ3NjE5LDcuODU3MTQyOSBDIDYwLjIzNjg1Myw3Ljg1NzE0MyA2Ny4xNDI4NTcsMTUuNDk3MDk4IDY3LjE0Mjg1NywyNSBDIDY3LjE0Mjg1NywzNC41MDI5MDIgNTkuNzYwNjYyLDQyLjE0Mjg1NyA1MCw0Mi4xNDI4NTcgTCAzMi44NTcxNDMsNDIuMTQyODU3IEwgMzIuODU3MTQzLDcuODU3MTQyOSB6IgogICAgICAgaWQ9InBhdGgyODg0IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NzY2NjY3Nzc3NzY2NjIiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return t&&e}}),dc=lc.define("logic.Nor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjEiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjY3NzY0NCIKICAgICBpbmtzY2FwZTpjeT0iMjIuMTAyMzQ0IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjM3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItNCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc5LDI1IEMgOTksMjUgOTUsMjUgOTUsMjUiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBpZD0icGF0aDI2MDQiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI3NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjI1IgogICAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgICAgc29kaXBvZGk6cnk9IjQiCiAgICAgICAgIGQ9Ik0gNzksMjUgQSA0LDQgMCAxIDEgNzEsMjUgQSA0LDQgMCAxIDEgNzksMjUgeiIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI2LjUsMzkuNSkiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t,e){return!(t||e)}}),fc=lc.define("logic.Nand",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5BTkQgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTYiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjI4MzMwNyIKICAgICBpbmtzY2FwZTpjeT0iMTYuNDQyODQzIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzksMjUgQyA5MS44LDI1IDk1LDI1IDk1LDI1IgogICAgICAgaWQ9InBhdGgzMDU5IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMxLDE1IDUsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMiwzNSA1LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmb250LXNpemU6bWVkaXVtO2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1pbmRlbnQ6MDt0ZXh0LWFsaWduOnN0YXJ0O3RleHQtZGVjb3JhdGlvbjpub25lO2xpbmUtaGVpZ2h0Om5vcm1hbDtsZXR0ZXItc3BhY2luZzpub3JtYWw7d29yZC1zcGFjaW5nOm5vcm1hbDt0ZXh0LXRyYW5zZm9ybTpub25lO2RpcmVjdGlvbjpsdHI7YmxvY2stcHJvZ3Jlc3Npb246dGI7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MzttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO2ZvbnQtZmFtaWx5OkJpdHN0cmVhbSBWZXJhIFNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpCaXRzdHJlYW0gVmVyYSBTYW5zIgogICAgICAgZD0iTSAzMCw1IEwgMzAsNi40Mjg1NzE0IEwgMzAsNDMuNTcxNDI5IEwgMzAsNDUgTCAzMS40Mjg1NzEsNDUgTCA1MC40NzYxOSw0NSBDIDYxLjc0NDA5OCw0NSA3MC40NzYxOSwzNS45OTk5NTUgNzAuNDc2MTksMjUgQyA3MC40NzYxOSwxNC4wMDAwNDUgNjEuNzQ0MDk5LDUuMDAwMDAwMiA1MC40NzYxOSw1IEMgNTAuNDc2MTksNSA1MC40NzYxOSw1IDMxLjQyODU3MSw1IEwgMzAsNSB6IE0gMzIuODU3MTQzLDcuODU3MTQyOSBDIDQwLjgzNDI2NCw3Ljg1NzE0MjkgNDUuOTE4MzY4LDcuODU3MTQyOSA0OC4wOTUyMzgsNy44NTcxNDI5IEMgNDkuMjg1NzE0LDcuODU3MTQyOSA0OS44ODA5NTIsNy44NTcxNDI5IDUwLjE3ODU3MSw3Ljg1NzE0MjkgQyA1MC4zMjczODEsNy44NTcxNDI5IDUwLjQwOTIyNyw3Ljg1NzE0MjkgNTAuNDQ2NDI5LDcuODU3MTQyOSBDIDUwLjQ2NTAyOSw3Ljg1NzE0MjkgNTAuNDcxNTQzLDcuODU3MTQyOSA1MC40NzYxOSw3Ljg1NzE0MjkgQyA2MC4yMzY4NTMsNy44NTcxNDMgNjcuMTQyODU3LDE1LjQ5NzA5OCA2Ny4xNDI4NTcsMjUgQyA2Ny4xNDI4NTcsMzQuNTAyOTAyIDU5Ljc2MDY2Miw0Mi4xNDI4NTcgNTAsNDIuMTQyODU3IEwgMzIuODU3MTQzLDQyLjE0Mjg1NyBMIDMyLjg1NzE0Myw3Ljg1NzE0MjkgeiIKICAgICAgIGlkPSJwYXRoMjg4NCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2Njc2NjY2Nzc3Nzc2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDQwMDgiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return!(t&&e)}}),pc=lc.define("logic.Xor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhPUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iMjUuOTM4MTE2IgogICAgIGlua3NjYXBlOmN5PSIxNy4yMzAwNSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzAuMzg1NzE3LDE1IEwgNC45OTk5OTk4LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5NzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEuMzYyMDkxLDM1IEwgNC45OTk5OTk4LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPGcKICAgICAgIGlkPSJnMjU2MCIKICAgICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYuNSwtMzkuNSkiPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDM1MTYiCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi4yNSw4MS41MDAwMDUgQyAtMy44NDczNzQsODQuMTQ0NDA1IC00LjUsODQuNTAwMDA1IC00LjUsODQuNTAwMDA1IEwgLTguMTU2MjUsODQuNTAwMDA1IEwgLTYuMTU2MjUsODIuMDYyNTA1IEMgLTYuMTU2MjUsODIuMDYyNTA1IC0wLjUsNzUuMDYyNDUxIC0wLjUsNjQuNSBDIC0wLjUsNTMuOTM3NTQ5IC02LjE1NjI1LDQ2LjkzNzUgLTYuMTU2MjUsNDYuOTM3NSBMIC04LjE1NjI1LDQ0LjUgTCAtNC41LDQ0LjUgQyAtMy43MTg3NSw0NS40Mzc1IC0zLjA3ODEyNSw0Ni4xNTYyNSAtMi4yODEyNSw0Ny41IEMgLTAuNDA4NTMxLDUwLjU5OTgxNSAyLjUsNTYuNTI2NjQ2IDIuNSw2NC41IEMgMi41LDcyLjQ1MDY1IC0wLjM5NjY5Nyw3OC4zNzk0MjUgLTIuMjUsODEuNTAwMDA1IHoiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY3NjY2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t,e){return(!t||e)&&(t||!e)}}),mc=lc.define("logic.Xnor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhOT1IgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNTU3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTYuNjY2NjY3IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDI1IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0IgogICAgIGlua3NjYXBlOmN4PSI5NS43MjM2NiIKICAgICBpbmtzY2FwZTpjeT0iLTI2Ljc3NTAyMyIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyLjAwMDAwMDI0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc4LjMzMzMzMiwyNSBDIDkxLjY2NjY2NiwyNSA5NSwyNSA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMC4zODU3MTcsMTUgTCA0Ljk5OTk5OTgsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk3NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMS4zNjIwOTEsMzUgTCA0Ljk5OTk5OTgsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMzUxNiIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjI1LDgxLjUwMDAwNSBDIC0zLjg0NzM3NCw4NC4xNDQ0MDUgLTQuNSw4NC41MDAwMDUgLTQuNSw4NC41MDAwMDUgTCAtOC4xNTYyNSw4NC41MDAwMDUgTCAtNi4xNTYyNSw4Mi4wNjI1MDUgQyAtNi4xNTYyNSw4Mi4wNjI1MDUgLTAuNSw3NS4wNjI0NTEgLTAuNSw2NC41IEMgLTAuNSw1My45Mzc1NDkgLTYuMTU2MjUsNDYuOTM3NSAtNi4xNTYyNSw0Ni45Mzc1IEwgLTguMTU2MjUsNDQuNSBMIC00LjUsNDQuNSBDIC0zLjcxODc1LDQ1LjQzNzUgLTMuMDc4MTI1LDQ2LjE1NjI1IC0yLjI4MTI1LDQ3LjUgQyAtMC40MDg1MzEsNTAuNTk5ODE1IDIuNSw1Ni41MjY2NDYgMi41LDY0LjUgQyAyLjUsNzIuNDUwNjUgLTAuMzk2Njk3LDc4LjM3OTQyNSAtMi4yNSw4MS41MDAwMDUgeiIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Njc2NjY2NzYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDM1NTEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return(!t||!e)&&(t||e)}}),Ic={Gate:nc,IO:rc,Input:oc,Output:ac,Gate11:sc,Gate21:lc,Repeater:cc,Not:uc,Or:hc,And:gc,Nor:dc,Nand:fc,Xor:pc,Xnor:mc,Wire:Ma.define("logic.Wire",{attrs:{".connection":{"stroke-width":2},".marker-vertex":{r:7}},router:{name:"orthogonal"},connector:{name:"rounded",args:{radius:10}}},{arrowheadMarkup:['','',""].join(""),vertexMarkup:['','','','','',"Remove vertex.","","",""].join("")})},vc={KingWhite:Nl.define("chess.KingWhite",{size:{width:42,height:38}},{markup:' '}),KingBlack:Nl.define("chess.KingBlack",{size:{width:42,height:38}},{markup:' '}),QueenWhite:Nl.define("chess.QueenWhite",{size:{width:42,height:38}},{markup:' '}),QueenBlack:Nl.define("chess.QueenBlack",{size:{width:42,height:38}},{markup:' '}),RookWhite:Nl.define("chess.RookWhite",{size:{width:32,height:34}},{markup:' '}),RookBlack:Nl.define("chess.RookBlack",{size:{width:32,height:34}},{markup:' '}),BishopWhite:Nl.define("chess.BishopWhite",{size:{width:38,height:38}},{markup:' '}),BishopBlack:Nl.define("chess.BishopBlack",{size:{width:38,height:38}},{markup:' '}),KnightWhite:Nl.define("chess.KnightWhite",{size:{width:38,height:37}},{markup:' '}),KnightBlack:Nl.define("chess.KnightBlack",{size:{width:38,height:37}},{markup:' '}),PawnWhite:Nl.define("chess.PawnWhite",{size:{width:28,height:33}},{markup:''}),PawnBlack:Nl.define("chess.PawnBlack",{size:{width:28,height:33}},{markup:''})},Ac=Ta.define("erd.Entity",{size:{width:150,height:60},attrs:{".outer":{fill:"#2ECC71",stroke:"#27AE60","stroke-width":2,points:"100,0 100,60 0,60 0,0"},".inner":{fill:"#2ECC71",stroke:"#27AE60","stroke-width":2,points:"95,5 95,55 5,55 5,5",display:"none"},text:{text:"Entity","font-family":"Arial","font-size":14,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Cc=Ac.define("erd.WeakEntity",{attrs:{".inner":{display:"auto"},text:{text:"Weak Entity"}}}),yc=Ta.define("erd.Relationship",{size:{width:80,height:80},attrs:{".outer":{fill:"#3498DB",stroke:"#2980B9","stroke-width":2,points:"40,0 80,40 40,80 0,40"},".inner":{fill:"#3498DB",stroke:"#2980B9","stroke-width":2,points:"40,5 75,40 40,75 5,40",display:"none"},text:{text:"Relationship","font-family":"Arial","font-size":12,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),wc=yc.define("erd.IdentifyingRelationship",{attrs:{".inner":{display:"auto"},text:{text:"Identifying"}}}),bc=Ta.define("erd.Attribute",{size:{width:100,height:50},attrs:{ellipse:{transform:"translate(50, 25)"},".outer":{stroke:"#D35400","stroke-width":2,cx:0,cy:0,rx:50,ry:25,fill:"#E67E22"},".inner":{stroke:"#D35400","stroke-width":2,cx:0,cy:0,rx:45,ry:20,fill:"#E67E22",display:"none"},text:{"font-family":"Arial","font-size":14,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Nc=bc.define("erd.Multivalued",{attrs:{".inner":{display:"block"},text:{text:"multivalued"}}}),xc=bc.define("erd.Derived",{attrs:{".outer":{"stroke-dasharray":"3,5"},text:{text:"derived"}}}),Mc=bc.define("erd.Key",{attrs:{ellipse:{"stroke-width":4},text:{text:"key","font-weight":"800","text-decoration":"underline"}}}),Sc=bc.define("erd.Normal",{attrs:{text:{text:"Normal"}}}),kc={Entity:Ac,WeakEntity:Cc,Relationship:yc,IdentifyingRelationship:wc,Attribute:bc,Multivalued:Nc,Derived:xc,Key:Mc,Normal:Sc,ISA:Ta.define("erd.ISA",{type:"erd.ISA",size:{width:100,height:50},attrs:{polygon:{points:"0,0 50,50 100,0",fill:"#F1C40F",stroke:"#F39C12","stroke-width":2},text:{text:"ISA","font-size":18,"ref-x":.5,"ref-y":.3,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Line:Ma.define("erd.Line",{},{cardinality:function(t){this.set("labels",[{position:-20,attrs:{text:{dy:-8,text:t}}}])}})},Dc={State:kl.define("fsa.State",{attrs:{circle:{"stroke-width":3},text:{"font-weight":"800"}}}),StartState:Ta.define("fsa.StartState",{size:{width:20,height:20},attrs:{circle:{transform:"translate(10, 10)",r:10,fill:"#000000"}}},{markup:''}),EndState:Ta.define("fsa.EndState",{size:{width:20,height:20},attrs:{".outer":{transform:"translate(10, 10)",r:10,fill:"#ffffff",stroke:"#000000"},".inner":{transform:"translate(10, 10)",r:6,fill:"#000000"}}},{markup:''}),Arrow:Ma.define("fsa.Arrow",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}},smooth:!0})},Tc={Member:Ta.define("org.Member",{size:{width:180,height:70},attrs:{rect:{width:170,height:60},".card":{fill:"#FFFFFF",stroke:"#000000","stroke-width":2,"pointer-events":"visiblePainted",rx:10,ry:10},image:{width:48,height:48,ref:".card","ref-x":10,"ref-y":5},".rank":{"text-decoration":"underline",ref:".card","ref-x":.9,"ref-y":.2,"font-family":"Courier New","font-size":14,"text-anchor":"end"},".name":{"font-weight":"800",ref:".card","ref-x":.9,"ref-y":.6,"font-family":"Courier New","font-size":14,"text-anchor":"end"}}},{markup:''}),Arrow:Ma.define("org.Arrow",{source:{selector:".card"},target:{selector:".card"},attrs:{".connection":{stroke:"#585858","stroke-width":3}},z:-1})},Lc={Place:Nl.define("pn.Place",{size:{width:50,height:50},attrs:{".root":{r:25,fill:"#ffffff",stroke:"#000000",transform:"translate(25, 25)"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:".root",fill:"#000000","font-size":12},".tokens > circle":{fill:"#000000",r:5},".tokens.one > circle":{transform:"translate(25, 25)"},".tokens.two > circle:nth-child(1)":{transform:"translate(19, 25)"},".tokens.two > circle:nth-child(2)":{transform:"translate(31, 25)"},".tokens.three > circle:nth-child(1)":{transform:"translate(18, 29)"},".tokens.three > circle:nth-child(2)":{transform:"translate(25, 19)"},".tokens.three > circle:nth-child(3)":{transform:"translate(32, 29)"},".tokens.alot > text":{transform:"translate(25, 18)","text-anchor":"middle",fill:"#000000"}}},{markup:''}),PlaceView:Fa.extend({presentationAttributes:Fa.addPresentationAttributes({tokens:["TOKENS"]}),initFlag:Fa.prototype.initFlag.concat(["TOKENS"]),confirmUpdate:function(){for(var t,e=[],i=arguments.length;i--;)e[i]=arguments[i];var n=(t=Fa.prototype.confirmUpdate).call.apply(t,[this].concat(e));return this.hasFlag(n,"TOKENS")&&(this.renderTokens(),this.update(),n=this.removeFlag(n,"TOKENS")),n},renderTokens:function(){var e=this.vel.findOne(".tokens").empty();["one","two","three","alot"].forEach(function(t){e.removeClass(t)});var t=this.model.get("tokens");if(t)switch(t){case 1:e.addClass("one"),e.append(wr("circle"));break;case 2:e.addClass("two"),e.append([wr("circle"),wr("circle")]);break;case 3:e.addClass("three"),e.append([wr("circle"),wr("circle"),wr("circle")]);break;default:e.addClass("alot"),e.append(wr("text").text(t+""))}}}),Transition:Nl.define("pn.Transition",{size:{width:12,height:50},attrs:{rect:{width:12,height:50,fill:"#000000",stroke:"#000000"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:"rect",fill:"#000000","font-size":12}}},{markup:''}),Link:Ma.define("pn.Link",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}}})},zc=Nl.define("uml.Class",{attrs:{rect:{width:200},".uml-class-name-rect":{stroke:"black","stroke-width":2,fill:"#3498db"},".uml-class-attrs-rect":{stroke:"black","stroke-width":2,fill:"#2980b9"},".uml-class-methods-rect":{stroke:"black","stroke-width":2,fill:"#2980b9"},".uml-class-name-text":{ref:".uml-class-name-rect","ref-y":.5,"ref-x":.5,"text-anchor":"middle","y-alignment":"middle","font-weight":"bold",fill:"black","font-size":12,"font-family":"Times New Roman"},".uml-class-attrs-text":{ref:".uml-class-attrs-rect","ref-y":5,"ref-x":5,fill:"black","font-size":12,"font-family":"Times New Roman"},".uml-class-methods-text":{ref:".uml-class-methods-rect","ref-y":5,"ref-x":5,fill:"black","font-size":12,"font-family":"Times New Roman"}},name:[],attributes:[],methods:[]},{markup:['','','',"",'',""].join(""),initialize:function(){this.on("change:name change:attributes change:methods",function(){this.updateRectangles(),this.trigger("uml-update")},this),this.updateRectangles(),Nl.prototype.initialize.apply(this,arguments)},getClassName:function(){return this.get("name")},updateRectangles:function(){var n=this.get("attrs"),t=[{type:"name",text:this.getClassName()},{type:"attrs",text:this.get("attributes")},{type:"methods",text:this.get("methods")}],r=0;t.forEach(function(t){var e=Array.isArray(t.text)?t.text:[t.text],i=20*e.length+20;n[".uml-class-"+t.type+"-text"].text=e.join("\n"),n[".uml-class-"+t.type+"-rect"].height=i,n[".uml-class-"+t.type+"-rect"].transform="translate(0,"+r+")",r+=i})}}),Pc=Fa.extend({initialize:function(){Fa.prototype.initialize.apply(this,arguments),this.listenTo(this.model,"uml-update",function(){this.update(),this.resize()})}}),jc=zc.define("uml.Abstract",{attrs:{".uml-class-name-rect":{fill:"#e74c3c"},".uml-class-attrs-rect":{fill:"#c0392b"},".uml-class-methods-rect":{fill:"#c0392b"}}},{getClassName:function(){return["<>",this.get("name")]}}),Zc=Pc,Gc=zc.define("uml.Interface",{attrs:{".uml-class-name-rect":{fill:"#f1c40f"},".uml-class-attrs-rect":{fill:"#f39c12"},".uml-class-methods-rect":{fill:"#f39c12"}}},{getClassName:function(){return["<>",this.get("name")]}}),Bc={basic:Ol,standard:ql,devs:ic,logic:Ic,chess:vc,erd:kc,fsa:Dc,org:Tc,pn:Lc,uml:{Class:zc,ClassView:Pc,Abstract:jc,AbstractView:Zc,Interface:Gc,InterfaceView:Pc,Generalization:Ma.define("uml.Generalization",{attrs:{".marker-target":{d:"M 20 0 L 0 10 L 20 20 z",fill:"white"}}}),Implementation:Ma.define("uml.Implementation",{attrs:{".marker-target":{d:"M 20 0 L 0 10 L 20 20 z",fill:"white"},".connection":{"stroke-dasharray":"3,3"}}}),Aggregation:Ma.define("uml.Aggregation",{attrs:{".marker-target":{d:"M 40 10 L 20 20 L 0 10 L 20 0 z",fill:"white"}}}),Composition:Ma.define("uml.Composition",{attrs:{".marker-target":{d:"M 40 10 L 20 20 L 0 10 L 20 0 z",fill:"black"}}}),Association:Ma.define("uml.Association"),State:Nl.define("uml.State",{attrs:{".uml-state-body":{width:200,height:200,rx:10,ry:10,fill:"#ecf0f1",stroke:"#bdc3c7","stroke-width":3},".uml-state-separator":{stroke:"#bdc3c7","stroke-width":2},".uml-state-name":{ref:".uml-state-body","ref-x":.5,"ref-y":5,"text-anchor":"middle",fill:"#000000","font-family":"Courier New","font-size":14},".uml-state-events":{ref:".uml-state-separator","ref-x":5,"ref-y":5,fill:"#000000","font-family":"Courier New","font-size":14}},name:"State",events:[]},{markup:['','','',"",'','','',""].join(""),initialize:function(){this.on({"change:name":this.updateName,"change:events":this.updateEvents,"change:size":this.updatePath},this),this.updateName(),this.updateEvents(),this.updatePath(),Nl.prototype.initialize.apply(this,arguments)},updateName:function(){this.attr(".uml-state-name/text",this.get("name"))},updateEvents:function(){this.attr(".uml-state-events/text",this.get("events").join("\n"))},updatePath:function(){var t="M 0 20 L "+this.get("size").width+" 20";this.attr(".uml-state-separator/d",t,{silent:!0})}}),StartState:kl.define("uml.StartState",{type:"uml.StartState",attrs:{circle:{fill:"#34495e",stroke:"#2c3e50","stroke-width":2,rx:1}}}),EndState:Nl.define("uml.EndState",{size:{width:20,height:20},attrs:{"circle.outer":{transform:"translate(10, 10)",r:10,fill:"#ffffff",stroke:"#2c3e50"},"circle.inner":{transform:"translate(10, 10)",r:6,fill:"#34495e"}}},{markup:''}),Transition:Ma.define("uml.Transition",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z",fill:"#34495e",stroke:"#2c3e50"},".connection":{stroke:"#2c3e50"}}})}};function Oc(t,e){return 0===e?"0%":Math.round(t/e*100)+"%"}function Ec(r){return function(t,e,i,n){return(e.isNodeConnection(i)?Yc:Rc)(r,t,e,i,n)}}function Rc(t,e,i,n,r){var o=i.model.angle(),a=i.getNodeUnrotatedBBox(n),s=i.model.getBBox().center();r.rotate(s,o);var l=r.x-a.x,c=r.y-a.y;return t&&(l=Oc(l,a.width),c=Oc(c,a.height)),e.anchor={name:"topLeft",args:{dx:l,dy:c,rotate:!0}},e}function Yc(t,e,i,n,r){var o=i.getConnection();if(!o)return e;var a=o.closestPointLength(r);if(t){var s=o.length();e.anchor={name:"connectionRatio",args:{ratio:a/s}}}else e.anchor={name:"connectionLength",args:{length:a}};return e}var Wc=Bo,Uc=Ec(!1),Fc=Ec(!0),Vc={useDefaults:Wc,pinAbsolute:Uc,pinRelative:Fc};function Xc(t,e,i){return Fc.call(this.paper,{},e,i,t,this.model).anchor}function Hc(t,e){var i=t.model;return e?i.getBBox():i.isLink()?t.getConnection().bbox():t.getNodeUnrotatedBBox(t.el)}var Jc=ja.extend({tagName:"circle",svgElement:!0,className:"marker-vertex",events:{mousedown:"onPointerDown",touchstart:"onPointerDown",dblclick:"onDoubleClick",dbltap:"onDoubleClick"},documentEvents:{mousemove:"onPointerMove",touchmove:"onPointerMove",mouseup:"onPointerUp",touchend:"onPointerUp",touchcancel:"onPointerUp"},attributes:{r:6,fill:"#33334F",stroke:"#FFFFFF","stroke-width":2,cursor:"move"},position:function(t,e){this.vel.attr({cx:t,cy:e})},onPointerDown:function(t){this.options.guard(t)||(t.stopPropagation(),t.preventDefault(),this.options.paper.undelegateEvents(),this.delegateDocumentEvents(null,t.data),this.trigger("will-change",this,t))},onPointerMove:function(t){this.trigger("changing",this,t)},onDoubleClick:function(t){this.trigger("remove",this,t)},onPointerUp:function(t){this.trigger("changed",this,t),this.undelegateDocumentEvents(),this.options.paper.delegateEvents()}}),_c=Al.extend({name:"vertices",options:{handleClass:Jc,snapRadius:20,redundancyRemoval:!0,vertexAdding:!0,stopPropagation:!0},children:[{tagName:"path",selector:"connection",className:"joint-vertices-path",attributes:{fill:"none",stroke:"transparent","stroke-width":10,cursor:"cell"}}],handles:null,events:{"mousedown .joint-vertices-path":"onPathPointerDown","touchstart .joint-vertices-path":"onPathPointerDown"},onRender:function(){return this.options.vertexAdding&&(this.renderChildren(),this.updatePath()),this.resetHandles(),this.renderHandles(),this},update:function(){return this.relatedView.model.vertices().length===this.handles.length?this.updateHandles():(this.resetHandles(),this.renderHandles()),this.options.vertexAdding&&this.updatePath(),this},resetHandles:function(){var t=this.handles;if(this.handles=[],this.stopListening(),Array.isArray(t))for(var e=0,i=t.length;e`) node that wraps all the nodes of the Cell view. + // Expose class name setter as a separate method. + _ensureElement: function() { + if (!this.el) { + var tagName = result(this, 'tagName'); + var attrs = assign({}, result(this, 'attributes')); + var style = assign({}, result(this, 'style')); + if (this.id) { attrs.id = result(this, 'id'); } + this.setElement(this._createElement(tagName)); + this._setAttributes(attrs); + this._setStyle(style); } else { - // this block is inside of `else` as an optimization so the distance is - // not calculated when we know there are no other intersection points - var endDistance = jumpStart.distance(lastLine.end); - // if the end is too close to possible jump, draw remaining line instead of a jump - if (endDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) { - resultLines.push(lastLine); - return resultLines; - } + this.setElement(result(this, 'el')); } + this._ensureElClassName(); + }, - var startDistance = jumpEnd.distance(lastLine.start); - if (startDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) { - // if the start of line is too close to jump, draw that line instead of a jump - resultLines.push(lastLine); - return resultLines; + _setAttributes: function(attrs) { + if (this.svgElement) { + this.vel.attr(attrs); + } else { + this.$el.attr(attrs); } + }, - // finally create a jump line - var jumpLine = line(jumpStart, jumpEnd); - // it's just simple line but with a `isJump` property - jumpLine.isJump = true; + _setStyle: function(style) { + this.$el.css(style); + }, - resultLines.push( - line(lastLine.start, jumpStart), - jumpLine, - line(jumpEnd, lastLine.end) - ); - return resultLines; - }, []); - } + _createElement: function(tagName) { + if (this.svgElement) { + return document.createElementNS(V.namespace.svg, tagName); + } else { + return document.createElement(tagName); + } + }, - /** - * Assemble `D` attribute of a SVG path by iterating given lines. - * @param {g.line[]} lines source lines to use - * @param {number} jumpSize the size of jump arc (length empty spot on a line) - * @param {number} radius the radius - * @return {string} - */ - function buildPath(lines, jumpSize, jumpType, radius) { + // Utilize an alternative DOM manipulation API by + // adding an element reference wrapped in Vectorizer. + _setElement: function(el) { + this.$el = el instanceof Backbone.$ ? el : Backbone.$(el); + this.el = this.$el[0]; + if (this.svgElement) { this.vel = V(this.el); } + }, - var path = new Path(); - var segment; + _ensureElClassName: function() { + var className = result(this, 'className'); + if (!className) { return; } + var prefixedClassName = addClassNamePrefix(className); + // Note: className removal here kept for backwards compatibility only + if (this.svgElement) { + this.vel.removeClass(className).addClass(prefixedClassName); + } else { + this.$el.removeClass(className).addClass(prefixedClassName); + } + }, - // first move to the start of a first line - segment = Path.createSegment('M', lines[0].start); - path.appendSegment(segment); + init: function() { + // Intentionally empty. + // This method is meant to be overridden. + }, - // make a paths from lines - toArray(lines).forEach(function(line, index) { + onRender: function() { + // Intentionally empty. + // This method is meant to be overridden. + }, - if (line.isJump) { - var angle, diff; + confirmUpdate: function() { + // Intentionally empty. + // This method is meant to be overridden. + return 0; + }, - var control1, control2; + setTheme: function(theme, opt) { - if (jumpType === 'arc') { // approximates semicircle with 2 curves - angle = -90; - // determine rotation of arc based on difference between points - diff = line.start.difference(line.end); - // make sure the arc always points up (or right) - var xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0)); - if (xAxisRotate) { angle += 180; } + opt = opt || {}; - var midpoint = line.midpoint(); - var centerLine = new Line(midpoint, line.end).rotate(midpoint, angle); + // Theme is already set, override is required, and override has not been set. + // Don't set the theme. + if (this.theme && this.requireSetThemeOverride && !opt.override) { + return this; + } - var halfLine; + this.removeThemeClassName(); + this.addThemeClassName(theme); + this.onSetTheme(this.theme/* oldTheme */, theme/* newTheme */); + this.theme = theme; - // first half - halfLine = new Line(line.start, midpoint); + return this; + }, - control1 = halfLine.pointAt(2 / 3).rotate(line.start, angle); - control2 = centerLine.pointAt(1 / 3).rotate(centerLine.end, -angle); + addThemeClassName: function(theme) { - segment = Path.createSegment('C', control1, control2, centerLine.end); - path.appendSegment(segment); + theme = theme || this.theme; + if (!theme) { return this; } - // second half - halfLine = new Line(midpoint, line.end); + var className = this.themeClassNamePrefix + theme; - control1 = centerLine.pointAt(1 / 3).rotate(centerLine.end, angle); - control2 = halfLine.pointAt(1 / 3).rotate(line.end, -angle); + if (this.svgElement) { + this.vel.addClass(className); + } else { + this.$el.addClass(className); + } - segment = Path.createSegment('C', control1, control2, line.end); - path.appendSegment(segment); + return this; + }, - } else if (jumpType === 'gap') { - segment = Path.createSegment('M', line.end); - path.appendSegment(segment); + removeThemeClassName: function(theme) { - } else if (jumpType === 'cubic') { // approximates semicircle with 1 curve - angle = line.start.theta(line.end); + theme = theme || this.theme; - var xOffset = jumpSize * 0.6; - var yOffset = jumpSize * 1.35; + var className = this.themeClassNamePrefix + theme; - // determine rotation of arc based on difference between points - diff = line.start.difference(line.end); - // make sure the arc always points up (or right) - xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0)); - if (xAxisRotate) { yOffset *= -1; } + if (this.svgElement) { + this.vel.removeClass(className); + } else { + this.$el.removeClass(className); + } - control1 = Point(line.start.x + xOffset, line.start.y + yOffset).rotate(line.start, angle); - control2 = Point(line.end.x - xOffset, line.end.y + yOffset).rotate(line.end, angle); + return this; + }, - segment = Path.createSegment('C', control1, control2, line.end); - path.appendSegment(segment); - } + onSetTheme: function(oldTheme, newTheme) { + // Intentionally empty. + // This method is meant to be overridden. + }, - } else { - var nextLine = lines[index + 1]; - if (radius == 0 || !nextLine || nextLine.isJump) { - segment = Path.createSegment('L', line.end); - path.appendSegment(segment); - } else { - buildRoundedSegment(radius, path, line.end, line.start, nextLine.end); - } - } - }); + remove: function() { - return path; - } + this.onRemove(); + this.undelegateDocumentEvents(); - function buildRoundedSegment(offset, path, curr, prev, next) { - var prevDistance = curr.distance(prev) / 2; - var nextDistance = curr.distance(next) / 2; + views[this.cid] = null; - var startMove = -Math.min(offset, prevDistance); - var endMove = -Math.min(offset, nextDistance); + Backbone.View.prototype.remove.apply(this, arguments); - var roundedStart = curr.clone().move(prev, startMove).round(); - var roundedEnd = curr.clone().move(next, endMove).round(); + return this; + }, - var control1 = new Point((_13 * roundedStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedStart.y)); - var control2 = new Point((_13 * roundedEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedEnd.y)); + onRemove: function() { + // Intentionally empty. + // This method is meant to be overridden. + }, - var segment; - segment = Path.createSegment('L', roundedStart); - path.appendSegment(segment); + getEventNamespace: function() { + // Returns a per-session unique namespace + return '.joint-event-ns-' + this.cid; + }, - segment = Path.createSegment('C', control1, control2, roundedEnd); - path.appendSegment(segment); - } + delegateElementEvents: function(element, events, data) { + if (!events) { return this; } + data || (data = {}); + var eventNS = this.getEventNamespace(); + for (var eventName in events) { + var method = events[eventName]; + if (typeof method !== 'function') { method = this[method]; } + if (!method) { continue; } + $(element).on(eventName + eventNS, data, method.bind(this)); + } + return this; + }, - /** - * Actual connector function that will be run on every update. - * @param {g.point} sourcePoint start point of this link - * @param {g.point} targetPoint end point of this link - * @param {g.point[]} route of this link - * @param {object} opt options - * @property {number} size optional size of a jump arc - * @return {string} created `D` attribute of SVG path - */ - var jumpover = function(sourcePoint, targetPoint, route, opt) { // eslint-disable-line max-params + undelegateElementEvents: function(element) { + $(element).off(this.getEventNamespace()); + return this; + }, - setupUpdating(this); + delegateDocumentEvents: function(events, data) { + events || (events = result(this, 'documentEvents')); + return this.delegateElementEvents(document, events, data); + }, - var raw = opt.raw; - var jumpSize = opt.size || JUMP_SIZE; - var jumpType = opt.jump && ('' + opt.jump).toLowerCase(); - var radius = opt.radius || RADIUS; - var ignoreConnectors = opt.ignoreConnectors || IGNORED_CONNECTORS; + undelegateDocumentEvents: function() { + return this.undelegateElementEvents(document); + }, - // grab the first jump type as a default if specified one is invalid - if (JUMP_TYPES.indexOf(jumpType) === -1) { - jumpType = JUMP_TYPES[0]; - } + eventData: function(evt, data) { + if (!evt) { throw new Error('eventData(): event object required.'); } + var currentData = evt.data; + var key = '__' + this.cid + '__'; + if (data === undefined) { + if (!currentData) { return {}; } + return currentData[key] || {}; + } + currentData || (currentData = evt.data = {}); + currentData[key] || (currentData[key] = {}); + assign(currentData[key], data); + return this; + }, - var paper = this.paper; - var graph = paper.model; - var allLinks = graph.getLinks(); + stopPropagation: function(evt) { + this.eventData(evt, { propagationStopped: true }); + return this; + }, - // there is just one link, draw it directly - if (allLinks.length === 1) { - return buildPath( - createLines(sourcePoint, targetPoint, route), - jumpSize, jumpType, radius - ); + isPropagationStopped: function(evt) { + return !!this.eventData(evt).propagationStopped; } - var thisModel = this.model; - var thisIndex = allLinks.indexOf(thisModel); - var defaultConnector = paper.options.defaultConnector || {}; - - // not all links are meant to be jumped over. - var links = allLinks.filter(function(link, idx) { + }, { - var connector = link.get('connector') || defaultConnector; + extend: function() { - // avoid jumping over links with connector type listed in `ignored connectors`. - if (toArray(ignoreConnectors).includes(connector.name)) { - return false; - } - // filter out links that are above this one and have the same connector type - // otherwise there would double hoops for each intersection - if (idx > thisIndex) { - return connector.name !== 'jumpover'; - } - return true; - }); + var args = Array.from(arguments); - // find views for all links - var linkViews = links.map(function(link) { - return paper.findViewByModel(link); - }); + // Deep clone the prototype and static properties objects. + // This prevents unexpected behavior where some properties are overwritten outside of this function. + var protoProps = args[0] && assign({}, args[0]) || {}; + var staticProps = args[1] && assign({}, args[1]) || {}; - // create lines for this link - var thisLines = createLines( - sourcePoint, - targetPoint, - route - ); + // Need the real render method so that we can wrap it and call it later. + var renderFn = protoProps.render || (this.prototype && this.prototype.render) || null; - // create lines for all other links - var linkLines = linkViews.map(function(linkView) { - if (linkView == null) { - return []; - } - if (linkView === this) { - return thisLines; - } - return createLines( - linkView.sourcePoint, - linkView.targetPoint, - linkView.route - ); - }, this); - - // transform lines for this link by splitting with jump lines at - // points of intersection with other links - var jumpingLines = thisLines.reduce(function(resultLines, thisLine) { - // iterate all links and grab the intersections with this line - // these are then sorted by distance so the line can be split more easily + /* + Wrap the real render method so that: + .. `onRender` is always called. + .. `this` is always returned. + */ + protoProps.render = function() { - var intersections = links.reduce(function(res, link, i) { - // don't intersection with itself - if (link !== thisModel) { + if (typeof renderFn === 'function') { + // Call the original render method. + renderFn.apply(this, arguments); + } - var lineIntersections = findLineIntersections(thisLine, linkLines[i]); - res.push.apply(res, lineIntersections); + if (this.render.__render__ === renderFn) { + // Should always call onRender() method. + // Should call it only once when renderFn is actual prototype method i.e. not the wrapper + this.onRender(); } - return res; - }, []).sort(function(a, b) { - return sortPoints(thisLine.start, a) - sortPoints(thisLine.start, b); - }); - if (intersections.length > 0) { - // split the line based on found intersection points - resultLines.push.apply(resultLines, createJumps(thisLine, intersections, jumpSize)); - } else { - // without any intersection the line goes uninterrupted - resultLines.push(thisLine); - } - return resultLines; - }, []); + // Should always return itself. + return this; + }; - var path = buildPath(jumpingLines, jumpSize, jumpType, radius); - return (raw) ? path : path.serialize(); - }; + protoProps.render.__render__ = renderFn; - var normal = function(sourcePoint, targetPoint, route, opt) { + return Backbone.View.extend.call(this, protoProps, staticProps); + } + }); - var raw = opt && opt.raw; - var points = [sourcePoint].concat(route).concat([targetPoint]); + var DoubleTapEventName = 'dbltap'; + if ($.event && !(DoubleTapEventName in $.event.special)) { + var maxDelay = config.doubleTapInterval; + var minDelay = 30; + $.event.special[DoubleTapEventName] = { + bindType: 'touchend', + delegateType: 'touchend', + handle: function(event) { + var ref; - var polyline = new Polyline(points); - var path = new Path(polyline); + var args = [], len = arguments.length - 1; + while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; + var handleObj = event.handleObj; + var target = event.target; + var targetData = $.data(target); + var now = new Date().getTime(); + var delta = 'lastTouch' in targetData ? now - targetData.lastTouch : 0; + if (delta < maxDelay && delta > minDelay) { + targetData.lastTouch = null; + event.type = handleObj.origType; + // let jQuery handle the triggering of "dbltap" event handlers + (ref = handleObj.handler).call.apply(ref, [ this, event ].concat( args )); + } else { + targetData.lastTouch = now; + } + } + }; + } - return (raw) ? path : path.serialize(); + var index$1 = ({ + views: views, + View: View + }); + + var LayersNames = { + CELLS: 'cells', + BACK: 'back', + FRONT: 'front', + TOOLS: 'tools', + LABELS: 'labels' }; - var rounded = function(sourcePoint, targetPoint, route, opt) { + var PaperLayer = View.extend({ - opt || (opt = {}); + tagName: 'g', + svgElement: true, + pivotNodes: null, + defaultTheme: null, - var offset = opt.radius || 10; - var raw = opt.raw; - var path = new Path(); - var segment; + options: { + name: '' + }, - segment = Path.createSegment('M', sourcePoint); - path.appendSegment(segment); + className: function() { + return addClassNamePrefix(((this.options.name) + "-layer")); + }, - var _13 = 1 / 3; - var _23 = 2 / 3; + init: function() { + this.pivotNodes = {}; + }, - var curr; - var prev, next; - var prevDistance, nextDistance; - var startMove, endMove; - var roundedStart, roundedEnd; - var control1, control2; + insertSortedNode: function(node, z) { + this.el.insertBefore(node, this.insertPivot(z)); + }, - for (var index = 0, n = route.length; index < n; index++) { + insertNode: function(node) { + var ref = this; + var el = ref.el; + if (node.parentNode !== el) { + el.appendChild(node); + } + }, - curr = new Point(route[index]); + insertPivot: function(z) { + var ref = this; + var el = ref.el; + var pivotNodes = ref.pivotNodes; + z = +z; + z || (z = 0); + var pivotNode = pivotNodes[z]; + if (pivotNode) { return pivotNode; } + pivotNode = pivotNodes[z] = document.createComment('z-index:' + (z + 1)); + var neighborZ = -Infinity; + for (var currentZ in pivotNodes) { + currentZ = +currentZ; + if (currentZ < z && currentZ > neighborZ) { + neighborZ = currentZ; + if (neighborZ === z - 1) { continue; } + } + } + if (neighborZ !== -Infinity) { + var neighborPivot = pivotNodes[neighborZ]; + // Insert After + el.insertBefore(pivotNode, neighborPivot.nextSibling); + } else { + // First Child + el.insertBefore(pivotNode, el.firstChild); + } + return pivotNode; + }, - prev = route[index - 1] || sourcePoint; - next = route[index + 1] || targetPoint; + removePivots: function() { + var ref = this; + var el = ref.el; + var pivotNodes = ref.pivotNodes; + for (var z in pivotNodes) { el.removeChild(pivotNodes[z]); } + this.pivotNodes = {}; + } - prevDistance = nextDistance || (curr.distance(prev) / 2); - nextDistance = curr.distance(next) / 2; + }); - startMove = -Math.min(offset, prevDistance); - endMove = -Math.min(offset, nextDistance); + function toArray$1(obj) { + if (!obj) { return []; } + if (Array.isArray(obj)) { return obj; } + return [obj]; + } - roundedStart = curr.clone().move(prev, startMove).round(); - roundedEnd = curr.clone().move(next, endMove).round(); + var HighlighterView = View.extend({ - control1 = new Point((_13 * roundedStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedStart.y)); - control2 = new Point((_13 * roundedEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedEnd.y)); + tagName: 'g', + svgElement: true, + className: 'highlight', - segment = Path.createSegment('L', roundedStart); - path.appendSegment(segment); + HIGHLIGHT_FLAG: 1, + UPDATE_PRIORITY: 3, + DETACHABLE: false, + UPDATABLE: true, + MOUNTABLE: true, - segment = Path.createSegment('C', control1, control2, roundedEnd); - path.appendSegment(segment); - } + cellView: null, + nodeSelector: null, + node: null, + updateRequested: false, + transformGroup: null, - segment = Path.createSegment('L', targetPoint); - path.appendSegment(segment); + requestUpdate: function requestUpdate(cellView, nodeSelector) { + var paper = cellView.paper; + this.cellView = cellView; + this.nodeSelector = nodeSelector; + if (paper) { + this.updateRequested = true; + paper.requestViewUpdate(this, this.HIGHLIGHT_FLAG, this.UPDATE_PRIORITY); + } + }, - return (raw) ? path : path.serialize(); - }; + confirmUpdate: function confirmUpdate() { + // The cellView is now rendered/updated since it has a higher update priority. + this.updateRequested = false; + var ref = this; + var cellView = ref.cellView; + var nodeSelector = ref.nodeSelector; + this.update(cellView, nodeSelector); + this.mount(); + this.transform(); + return 0; + }, - var smooth = function(sourcePoint, targetPoint, route, opt) { + findNode: function findNode(cellView, nodeSelector) { + var assign, assign$1; - var raw = opt && opt.raw; - var path; + if ( nodeSelector === void 0 ) nodeSelector = null; + var el; + if (typeof nodeSelector === 'string') { + (assign = cellView.findBySelector(nodeSelector), el = assign[0]); + } else if (isPlainObject(nodeSelector)) { + var isLink = cellView.model.isLink(); + var label = nodeSelector.label; if ( label === void 0 ) label = null; + var port = nodeSelector.port; + var selector = nodeSelector.selector; + if (isLink && label !== null) { + // Link Label Selector + el = cellView.findLabelNode(label, selector); + } else if (!isLink && port) { + // Element Port Selector + el = cellView.findPortNode(port, selector); + } else { + // Cell Selector + (assign$1 = cellView.findBySelector(selector), el = assign$1[0]); + } + } else if (nodeSelector) { + el = V.toNode(nodeSelector); + if (!(el instanceof SVGElement)) { el = null; } + } + return el ? el : null; + }, - if (route && route.length !== 0) { + mount: function mount() { + var ref = this; + var MOUNTABLE = ref.MOUNTABLE; + var cellView = ref.cellView; + var el = ref.el; + var options = ref.options; + var transformGroup = ref.transformGroup; + if (!MOUNTABLE || transformGroup) { return; } + var cellViewRoot = cellView.vel; + var paper = cellView.paper; + var layerName = options.layer; + if (layerName) { + var vGroup = this.transformGroup = V('g').addClass('highlight-transform').append(el); + paper.getLayerView(layerName).insertSortedNode(vGroup.node, options.z); + } else { + // TODO: prepend vs append + if (!el.parentNode || el.nextSibling) { + // Not appended yet or not the last child + cellViewRoot.append(el); + } + } + }, - var points = [sourcePoint].concat(route).concat([targetPoint]); - var curves = Curve.throughPoints(points); + unmount: function unmount() { + var ref = this; + var MOUNTABLE = ref.MOUNTABLE; + var transformGroup = ref.transformGroup; + var vel = ref.vel; + if (!MOUNTABLE) { return; } + if (transformGroup) { + this.transformGroup = null; + transformGroup.remove(); + } else { + vel.remove(); + } + }, - path = new Path(curves); + transform: function transform() { + var ref = this; + var transformGroup = ref.transformGroup; + var cellView = ref.cellView; + var updateRequested = ref.updateRequested; + if (!transformGroup || cellView.model.isLink() || updateRequested) { return; } + var translateMatrix = cellView.getRootTranslateMatrix(); + var rotateMatrix = cellView.getRootRotateMatrix(); + var transformMatrix = translateMatrix.multiply(rotateMatrix); + transformGroup.attr('transform', V.matrixToTransformString(transformMatrix)); + }, - } else { - // if we have no route, use a default cubic bezier curve - // cubic bezier requires two control points - // the control points have `x` midway between source and target - // this produces an S-like curve + update: function update() { + var ref = this; + var prevNode = ref.node; + var cellView = ref.cellView; + var nodeSelector = ref.nodeSelector; + var updateRequested = ref.updateRequested; + var id = ref.id; + if (updateRequested) { return; } + var node = this.node = this.findNode(cellView, nodeSelector); + if (prevNode) { + this.unhighlight(cellView, prevNode); + } + if (node) { + this.highlight(cellView, node); + this.mount(); + } else { + this.unmount(); + cellView.notify('cell:highlight:invalid', id, this); + } + }, - path = new Path(); + onRemove: function onRemove() { + var ref = this; + var node = ref.node; + var cellView = ref.cellView; + var id = ref.id; + var constructor = ref.constructor; + if (node) { + this.unhighlight(cellView, node); + } + this.unmount(); + constructor._removeRef(cellView, id); + }, - var segment; + highlight: function highlight(_cellView, _node) { + // to be overridden + }, - segment = Path.createSegment('M', sourcePoint); - path.appendSegment(segment); + unhighlight: function unhighlight(_cellView, _node) { + // to be overridden + } - if ((Math.abs(sourcePoint.x - targetPoint.x)) >= (Math.abs(sourcePoint.y - targetPoint.y))) { - var controlPointX = (sourcePoint.x + targetPoint.x) / 2; + }, { - segment = Path.createSegment('C', controlPointX, sourcePoint.y, controlPointX, targetPoint.y, targetPoint.x, targetPoint.y); - path.appendSegment(segment); + _views: {}, - } else { - var controlPointY = (sourcePoint.y + targetPoint.y) / 2; + // Used internally by CellView highlight() + highlight: function(cellView, node, opt) { + var id = this.uniqueId(node, opt); + this.add(cellView, node, id, opt); + }, - segment = Path.createSegment('C', sourcePoint.x, controlPointY, targetPoint.x, controlPointY, targetPoint.x, targetPoint.y); - path.appendSegment(segment); + // Used internally by CellView unhighlight() + unhighlight: function(cellView, node, opt) { + var id = this.uniqueId(node, opt); + this.remove(cellView, id); + }, + + get: function get(cellView, id) { + if ( id === void 0 ) id = null; + var cid = cellView.cid; + var ref$2 = this; + var _views = ref$2._views; + var refs = _views[cid]; + if (id === null) { + // all highlighters + var views = []; + if (!refs) { return views; } + for (var hid in refs) { + var ref = refs[hid]; + if (ref instanceof this) { + views.push(ref); + } + } + return views; + } else { + // single highlighter + if (!refs) { return null; } + if (id in refs) { + var ref$1 = refs[id]; + if (ref$1 instanceof this) { return ref$1; } + } + return null; } - } + }, - return (raw) ? path : path.serialize(); - }; + add: function add(cellView, nodeSelector, id, opt) { + if ( opt === void 0 ) opt = {}; - var Directions = { - AUTO: 'auto', - HORIZONTAL: 'horizontal', - VERTICAL: 'vertical', - CLOSEST_POINT: 'closest-point', - OUTWARDS: 'outwards' - }; + if (!id) { throw new Error('dia.HighlighterView: An ID required.'); } + // Search the existing view amongst all the highlighters + var previousView = HighlighterView.get(cellView, id); + if (previousView) { previousView.remove(); } + var view = new this(opt); + view.id = id; + this._addRef(cellView, id, view); + view.requestUpdate(cellView, nodeSelector); + return view; + }, - var TangentDirections = { - UP: 'up', - DOWN: 'down', - LEFT: 'left', - RIGHT: 'right', - AUTO: 'auto', - CLOSEST_POINT: 'closest-point', - OUTWARDS: 'outwards' - }; + _addRef: function _addRef(cellView, id, view) { + var cid = cellView.cid; + var ref = this; + var _views = ref._views; + var refs = _views[cid]; + if (!refs) { refs = _views[cid] = {}; } + refs[id] = view; + }, - var curve = function(sourcePoint, targetPoint, route, opt, linkView) { - if ( route === void 0 ) route = []; - if ( opt === void 0 ) opt = {}; + _removeRef: function _removeRef(cellView, id) { + var cid = cellView.cid; + var ref = this; + var _views = ref._views; + var refs = _views[cid]; + if (!refs) { return; } + if (id) { delete refs[id]; } + for (var _ in refs) { return; } + delete _views[cid]; + }, - var raw = Boolean(opt.raw); - // distanceCoefficient - a coefficient of the tangent vector length relative to the distance between points. - // angleTangentCoefficient - a coefficient of the end tangents length in the case of angles larger than 45 degrees. - // tension - a Catmull-Rom curve tension parameter. - // sourceTangent - a tangent vector along the curve at the sourcePoint. - // sourceDirection - a unit direction vector along the curve at the sourcePoint. - // targetTangent - a tangent vector along the curve at the targetPoint. - // targetDirection - a unit direction vector along the curve at the targetPoint. - // precision - a rounding precision for path values. - var direction = opt.direction; if ( direction === void 0 ) direction = Directions.AUTO; - var precision = opt.precision; if ( precision === void 0 ) precision = 3; - var options = { - coeff: opt.distanceCoefficient || 0.6, - angleTangentCoefficient: opt.angleTangentCoefficient || 80, - tau: opt.tension || 0.5, - sourceTangent: opt.sourceTangent ? new Point(opt.sourceTangent) : null, - targetTangent: opt.targetTangent ? new Point(opt.targetTangent) : null - }; - if (typeof opt.sourceDirection === 'string') - { options.sourceDirection = opt.sourceDirection; } - else if (typeof opt.sourceDirection === 'number') - { options.sourceDirection = new Point(1, 0).rotate(null, opt.sourceDirection); } - else - { options.sourceDirection = opt.sourceDirection ? new Point(opt.sourceDirection).normalize() : null; } + remove: function remove(cellView, id) { + if ( id === void 0 ) id = null; - if (typeof opt.targetDirection === 'string') - { options.targetDirection = opt.targetDirection; } - else if (typeof opt.targetDirection === 'number') - { options.targetDirection = new Point(1, 0).rotate(null, opt.targetDirection); } - else - { options.targetDirection = opt.targetDirection ? new Point(opt.targetDirection).normalize() : null; } + toArray$1(this.get(cellView, id)).forEach(function (view) { + view.remove(); + }); + }, - var completeRoute = [sourcePoint ].concat( route.map(function (p) { return new Point(p); }), [targetPoint]); + update: function update(cellView, id, dirty) { + if ( id === void 0 ) id = null; + if ( dirty === void 0 ) dirty = false; - // The calculation of a sourceTangent - var sourceTangent; - if (options.sourceTangent) { - sourceTangent = options.sourceTangent; - } else { - var sourceDirection = getSourceTangentDirection(linkView, completeRoute, direction, options); - var tangentLength = completeRoute[0].distance(completeRoute[1]) * options.coeff; - var pointsVector = completeRoute[1].difference(completeRoute[0]).normalize(); - var angle = angleBetweenVectors(sourceDirection, pointsVector); - if (angle > Math.PI / 4) { - var updatedLength = tangentLength + (angle - Math.PI / 4) * options.angleTangentCoefficient; - sourceTangent = sourceDirection.clone().scale(updatedLength, updatedLength); - } else { - sourceTangent = sourceDirection.clone().scale(tangentLength, tangentLength); - } - } + toArray$1(this.get(cellView, id)).forEach(function (view) { + if (dirty || view.UPDATABLE) { view.update(); } + }); + }, - // The calculation of a targetTangent - var targetTangent; - if (options.targetTangent) { - targetTangent = options.targetTangent; - } else { - var targetDirection = getTargetTangentDirection(linkView, completeRoute, direction, options); - var last = completeRoute.length - 1; - var tangentLength$1 = completeRoute[last - 1].distance(completeRoute[last]) * options.coeff; - var pointsVector$1 = completeRoute[last - 1].difference(completeRoute[last]).normalize(); - var angle$1 = angleBetweenVectors(targetDirection, pointsVector$1); - if (angle$1 > Math.PI / 4) { - var updatedLength$1 = tangentLength$1 + (angle$1 - Math.PI / 4) * options.angleTangentCoefficient; - targetTangent = targetDirection.clone().scale(updatedLength$1, updatedLength$1); - } else { - targetTangent = targetDirection.clone().scale(tangentLength$1, tangentLength$1); - } + transform: function transform(cellView, id) { + if ( id === void 0 ) id = null; + + toArray$1(this.get(cellView, id)).forEach(function (view) { + if (view.UPDATABLE) { view.transform(); } + }); + }, + + uniqueId: function uniqueId(node, opt) { + if ( opt === void 0 ) opt = ''; + + return V.ensureId(node) + JSON.stringify(opt); } - - var catmullRomCurves = createCatmullRomCurves(completeRoute, sourceTangent, targetTangent, options); - var bezierCurves = catmullRomCurves.map(function (curve) { return catmullRomToBezier(curve, options); }); - var path = new Path(bezierCurves).round(precision); - return (raw) ? path : path.serialize(); + }); + + var HighlightingTypes = { + DEFAULT: 'default', + EMBEDDING: 'embedding', + CONNECTING: 'connecting', + MAGNET_AVAILABILITY: 'magnetAvailability', + ELEMENT_AVAILABILITY: 'elementAvailability' }; - curve.Directions = Directions; - curve.TangentDirections = TangentDirections; - function getHorizontalSourceDirection(linkView, route, options) { - var sourceBBox = linkView.sourceBBox; + // CellView base view and controller. + // -------------------------------------------- - var sourceSide; - if (!sourceBBox.width || !sourceBBox.height) { - if (sourceBBox.x > route[1].x) - { sourceSide = 'right'; } - else - { sourceSide = 'left'; } - } else { - sourceSide = sourceBBox.sideNearestToPoint(route[0]); - } + // This is the base view and controller for `ElementView` and `LinkView`. + var CellView = View.extend({ - switch (sourceSide) { - case 'left': { - return new Point(-1, 0); - } - case 'right': - default: { - return new Point(1, 0); - } - } - } + tagName: 'g', - function getHorizontalTargetDirection(linkView, route, options) { - var targetBBox = linkView.targetBBox; + svgElement: true, - var targetSide; - if (!targetBBox.width || !targetBBox.height) { - if (targetBBox.x > route[route.length - 2].x) - { targetSide = 'left'; } - else - { targetSide = 'right'; } - } else { - targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); - } + selector: 'root', - switch (targetSide) { - case 'left': { - return new Point(-1, 0); - } - case 'right': - default: { - return new Point(1, 0); - } - } - } + metrics: null, - function getVerticalSourceDirection(linkView, route, options) { - var sourceBBox = linkView.sourceBBox; + className: function() { - var sourceSide; - if (!sourceBBox.width || !sourceBBox.height) { - if (sourceBBox.y > route[1].y) - { sourceSide = 'bottom'; } - else - { sourceSide = 'top'; } - } else { - sourceSide = sourceBBox.sideNearestToPoint(route[0]); - } + var classNames = ['cell']; + var type = this.model.get('type'); - switch (sourceSide) { - case 'top': { - return new Point(0, -1); - } - case 'bottom': - default: { - return new Point(0, 1); + if (type) { + + type.toLowerCase().split('.').forEach(function(value, index, list) { + classNames.push('type-' + list.slice(0, index + 1).join('-')); + }); } - } - } - function getVerticalTargetDirection(linkView, route, options) { - var targetBBox = linkView.targetBBox; + return classNames.join(' '); + }, - var targetSide; - if (!targetBBox.width || !targetBBox.height) { - if (targetBBox.y > route[route.length - 2].y) - { targetSide = 'top'; } - else - { targetSide = 'bottom'; } - } else { - targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); - } + _presentationAttributes: null, + _flags: null, - switch (targetSide) { - case 'top': { - return new Point(0, -1); - } - case 'bottom': - default: { - return new Point(0, 1); + setFlags: function() { + var flags = {}; + var attributes = {}; + var shift = 0; + var i, n, label; + var presentationAttributes = result(this, 'presentationAttributes'); + for (var attribute in presentationAttributes) { + if (!presentationAttributes.hasOwnProperty(attribute)) { continue; } + var labels = presentationAttributes[attribute]; + if (!Array.isArray(labels)) { labels = [labels]; } + for (i = 0, n = labels.length; i < n; i++) { + label = labels[i]; + var flag = flags[label]; + if (!flag) { + flag = flags[label] = 1<<(shift++); + } + attributes[attribute] |= flag; + } + } + var initFlag = result(this, 'initFlag'); + if (!Array.isArray(initFlag)) { initFlag = [initFlag]; } + for (i = 0, n = initFlag.length; i < n; i++) { + label = initFlag[i]; + if (!flags[label]) { flags[label] = 1<<(shift++); } } - } - } - function getAutoSourceDirection(linkView, route, options) { - var sourceBBox = linkView.sourceBBox; + // 26 - 30 are reserved for paper flags + // 31+ overflows maximal number + if (shift > 25) { throw new Error('dia.CellView: Maximum number of flags exceeded.'); } - var sourceSide; - if (!sourceBBox.width || !sourceBBox.height) { - sourceSide = sourceBBox.sideNearestToPoint(route[1]); - } else { - sourceSide = sourceBBox.sideNearestToPoint(route[0]); - } + this._flags = flags; + this._presentationAttributes = attributes; + }, - switch (sourceSide) { - case 'top': - return new Point(0, -1); - case 'bottom': - return new Point(0, 1); - case 'right': - return new Point(1, 0); - case 'left': - return new Point(-1, 0); - } - } + hasFlag: function(flag, label) { + return flag & this.getFlag(label); + }, - function getAutoTargetDirection(linkView, route, options) { - var targetBBox = linkView.targetBBox; - - var targetSide; - if (!targetBBox.width || !targetBBox.height) { - targetSide = targetBBox.sideNearestToPoint(route[route.length - 2]); - } else { - targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); - } + removeFlag: function(flag, label) { + return flag ^ (flag & this.getFlag(label)); + }, - switch (targetSide) { - case 'top': - return new Point(0, -1); - case 'bottom': - return new Point(0, 1); - case 'right': - return new Point(1, 0); - case 'left': - return new Point(-1, 0); - } - } + getFlag: function(label) { + var flags = this._flags; + if (!flags) { return 0; } + var flag = 0; + if (Array.isArray(label)) { + for (var i = 0, n = label.length; i < n; i++) { flag |= flags[label[i]]; } + } else { + flag |= flags[label]; + } + return flag; + }, - function getClosestPointSourceDirection(linkView, route, options) { - return route[1].difference(route[0]).normalize(); - } + attributes: function() { + var cell = this.model; + return { + 'model-id': cell.id, + 'data-type': cell.attributes.type + }; + }, - function getClosestPointTargetDirection(linkView, route, options) { - var last = route.length - 1; - return route[last - 1].difference(route[last]).normalize(); - } + constructor: function(options) { - function getOutwardsSourceDirection(linkView, route, options) { - var sourceBBox = linkView.sourceBBox; - var sourceCenter = sourceBBox.center(); - return route[0].difference(sourceCenter).normalize(); - } + // Make sure a global unique id is assigned to this view. Store this id also to the properties object. + // The global unique id makes sure that the same view can be rendered on e.g. different machines and + // still be associated to the same object among all those clients. This is necessary for real-time + // collaboration mechanism. + options.id = options.id || guid(this); - function getOutwardsTargetDirection(linkView, route, options) { - var targetBBox = linkView.targetBBox; - var targetCenter = targetBBox.center(); - return route[route.length - 1].difference(targetCenter).normalize(); - } + View.call(this, options); + }, - function getSourceTangentDirection(linkView, route, direction, options) { - if (options.sourceDirection) { - switch(options.sourceDirection) { - case TangentDirections.UP: - return new Point(0, -1); - case TangentDirections.DOWN: - return new Point(0, 1); - case TangentDirections.LEFT: - return new Point(-1, 0); - case TangentDirections.RIGHT: - return new Point(0, 1); - case TangentDirections.AUTO: - return getAutoSourceDirection(linkView, route, options); - case TangentDirections.CLOSEST_POINT: - return getClosestPointSourceDirection(linkView, route, options); - case TangentDirections.OUTWARDS: { - return getOutwardsSourceDirection(linkView, route, options); - } - default: - return options.sourceDirection; - } - } - - switch (direction) { - case Directions.HORIZONTAL: - return getHorizontalSourceDirection(linkView, route, options); - case Directions.VERTICAL: - return getVerticalSourceDirection(linkView, route, options); - case Directions.CLOSEST_POINT: - return getClosestPointSourceDirection(linkView, route, options); - case Directions.OUTWARDS: - return getOutwardsSourceDirection(linkView, route, options); - case Directions.AUTO: - default: - return getAutoSourceDirection(linkView, route, options); - } - } + initialize: function() { - function getTargetTangentDirection(linkView, route, direction, options) { - if (options.targetDirection) { - switch(options.targetDirection) { - case TangentDirections.UP: - return new Point(0, -1); - case TangentDirections.DOWN: - return new Point(0, 1); - case TangentDirections.LEFT: - return new Point(-1, 0); - case TangentDirections.RIGHT: - return new Point(0, 1); - case TangentDirections.AUTO: - return getAutoTargetDirection(linkView, route, options); - case TangentDirections.CLOSEST_POINT: - return getClosestPointTargetDirection(linkView, route, options); - case TangentDirections.OUTWARDS: { - return getOutwardsTargetDirection(linkView, route, options); - } - default: - return options.targetDirection; - } - } - - switch (direction) { - case Directions.HORIZONTAL: - return getHorizontalTargetDirection(linkView, route, options); - case Directions.VERTICAL: - return getVerticalTargetDirection(linkView, route, options); - case Directions.CLOSEST_POINT: - return getClosestPointTargetDirection(linkView, route, options); - case Directions.OUTWARDS: - return getOutwardsTargetDirection(linkView, route, options); - case Directions.AUTO: - default: - return getAutoTargetDirection(linkView, route, options); - } - } + this.setFlags(); - function rotateVector(vector, angle) { - var cos = Math.cos(angle); - var sin = Math.sin(angle); - var x = cos * vector.x - sin * vector.y; - var y = sin * vector.x + cos * vector.y; - vector.x = x; - vector.y = y; - } + View.prototype.initialize.apply(this, arguments); - function angleBetweenVectors(v1, v2) { - var cos = v1.dot(v2) / (v1.magnitude() * v2.magnitude()); - if (cos < -1) { cos = -1; } - if (cos > 1) { cos = 1; } - return Math.acos(cos); - } + this.cleanNodesCache(); - function determinant(v1, v2) { - return v1.x * v2.y - v1.y * v2.x; - } + // Store reference to this to the DOM element so that the view is accessible through the DOM tree. + this.$el.data('view', this); - function createCatmullRomCurves(points, sourceTangent, targetTangent, options) { - var tau = options.tau; - var coeff = options.coeff; - var distances = []; - var tangents = []; - var catmullRomCurves = []; - var n = points.length - 1; + this.startListening(); + }, - for (var i = 0; i < n; i++) { - distances[i] = points[i].distance(points[i + 1]); - } + onMount: function onMount() { + // To be overridden + }, - tangents[0] = sourceTangent; - tangents[n] = targetTangent; + startListening: function() { + this.listenTo(this.model, 'change', this.onAttributesChange); + }, - // The calculation of tangents of vertices - for (var i$1 = 1; i$1 < n; i$1++) { - var tpPrev = (void 0); - var tpNext = (void 0); - if (i$1 === 1) { - tpPrev = points[i$1 - 1].clone().offset(tangents[i$1 - 1].x, tangents[i$1 - 1].y); - } else { - tpPrev = points[i$1 - 1].clone(); - } - if (i$1 === n - 1) { - tpNext = points[i$1 + 1].clone().offset(tangents[i$1 + 1].x, tangents[i$1 + 1].y); - } else { - tpNext = points[i$1 + 1].clone(); - } - var v1 = tpPrev.difference(points[i$1]).normalize(); - var v2 = tpNext.difference(points[i$1]).normalize(); - var vAngle = angleBetweenVectors(v1, v2); + onAttributesChange: function(model, opt) { + var flag = model.getChangeFlag(this._presentationAttributes); + if (opt.updateHandled || !flag) { return; } + if (opt.dirty && this.hasFlag(flag, 'UPDATE')) { flag |= this.getFlag('RENDER'); } + // TODO: tool changes does not need to be sync + // Fix Segments tools + if (opt.tool) { opt.async = false; } + this.requestUpdate(flag, opt); + }, - var rot = (Math.PI - vAngle) / 2; - var t = (void 0); - var vectorDeterminant = determinant(v1, v2); - var pointsDeterminant = (void 0); - pointsDeterminant = determinant(points[i$1].difference(points[i$1 + 1]), points[i$1].difference(points[i$1 - 1])); - if (vectorDeterminant < 0) { - rot = -rot; - } - if ((vAngle < Math.PI / 2) && ((rot < 0 && pointsDeterminant < 0) || (rot > 0 && pointsDeterminant > 0))) { - rot = rot - Math.PI; + requestUpdate: function(flags, opt) { + var ref = this; + var paper = ref.paper; + if (paper && flags > 0) { + paper.requestViewUpdate(this, flags, this.UPDATE_PRIORITY, opt); } - t = v2.clone(); - rotateVector(t, rot); - - var t1 = t.clone(); - var t2 = t.clone(); - var scaleFactor1 = distances[i$1 - 1] * coeff; - var scaleFactor2 = distances[i$1] * coeff; - t1.scale(scaleFactor1, scaleFactor1); - t2.scale(scaleFactor2, scaleFactor2); + }, - tangents[i$1] = [t1, t2]; - } + parseDOMJSON: function(markup, root) { - // The building of a Catmull-Rom curve based of tangents of points - for (var i$2 = 0; i$2 < n; i$2++) { - var p0 = (void 0); - var p3 = (void 0); - if (i$2 === 0) { - p0 = points[i$2 + 1].difference(tangents[i$2].x / tau, tangents[i$2].y / tau); - } else { - p0 = points[i$2 + 1].difference(tangents[i$2][1].x / tau, tangents[i$2][1].y / tau); + var doc = parseDOMJSON(markup); + var selectors = doc.selectors; + var groups = doc.groupSelectors; + for (var group in groups) { + if (selectors[group]) { throw new Error('dia.CellView: ambiguous group selector'); } + selectors[group] = groups[group]; } - if (i$2 === n - 1) { - p3 = points[i$2].clone().offset(tangents[i$2 + 1].x / tau, tangents[i$2 + 1].y / tau); - } else { - p3 = points[i$2].difference(tangents[i$2 + 1][0].x / tau, tangents[i$2 + 1][0].y / tau); + if (root) { + var rootSelector = this.selector; + if (selectors[rootSelector]) { throw new Error('dia.CellView: ambiguous root selector.'); } + selectors[rootSelector] = root; } + return { fragment: doc.fragment, selectors: selectors }; + }, - catmullRomCurves[i$2] = [p0, points[i$2], points[i$2 + 1], p3]; - } - return catmullRomCurves; - } + // Return `true` if cell link is allowed to perform a certain UI `feature`. + // Example: `can('vertexMove')`, `can('labelMove')`. + can: function(feature) { - // The function to convert Catmull-Rom curve to Bezier curve using the tension (tau) - function catmullRomToBezier(points, options) { - var tau = options.tau; + var interactive = isFunction(this.options.interactive) + ? this.options.interactive(this) + : this.options.interactive; - var bcp1 = new Point(); - bcp1.x = points[1].x + (points[2].x - points[0].x) / (6 * tau); - bcp1.y = points[1].y + (points[2].y - points[0].y) / (6 * tau); + return (isObject$1(interactive) && interactive[feature] !== false) || + (isBoolean(interactive) && interactive !== false); + }, - var bcp2 = new Point(); - bcp2.x = points[2].x + (points[3].x - points[1].x) / (6 * tau); - bcp2.y = points[2].y + (points[3].y - points[1].y) / (6 * tau); - return new Curve( - points[1], - bcp1, - bcp2, - points[2] - ); - } + findBySelector: function(selector, root, selectors) { + root || (root = this.el); + selectors || (selectors = this.selectors); + // These are either descendants of `this.$el` of `this.$el` itself. + // `.` is a special selector used to select the wrapping `` element. + if (!selector || selector === '.') { return [root]; } + if (selectors) { + var nodes = selectors[selector]; + if (nodes) { + if (Array.isArray(nodes)) { return nodes; } + return [nodes]; + } + } - var connectors = ({ - jumpover: jumpover, - normal: normal, - rounded: rounded, - smooth: smooth, - curve: curve - }); - - var views = {}; + // Maintaining backwards compatibility + // e.g. `circle:first` would fail with querySelector() call + if (config.useCSSSelectors) { return $(root).find(selector).toArray(); } - var View = Backbone.View.extend({ + return []; + }, - options: {}, - theme: null, - themeClassNamePrefix: addClassNamePrefix('theme-'), - requireSetThemeOverride: false, - defaultTheme: config.defaultTheme, - children: null, - childNodes: null, + notify: function(eventName) { - DETACHABLE: true, - UPDATE_PRIORITY: 2, - FLAG_INSERT: 1<<30, - FLAG_REMOVE: 1<<29, + if (this.paper) { - constructor: function(options) { + var args = Array.prototype.slice.call(arguments, 1); - this.requireSetThemeOverride = options && !!options.theme; - this.options = assign({}, this.options, options); + // Trigger the event on both the element itself and also on the paper. + this.trigger.apply(this, [eventName].concat(args)); - Backbone.View.call(this, options); + // Paper event handlers receive the view object as the first argument. + this.paper.trigger.apply(this.paper, [eventName, this].concat(args)); + } }, - initialize: function() { - - views[this.cid] = this; - - this.setTheme(this.options.theme || this.defaultTheme); - this.init(); - }, + getBBox: function(opt) { - unmount: function() { - if (this.svgElement) { - this.vel.remove(); + var bbox; + if (opt && opt.useModelGeometry) { + var model = this.model; + bbox = model.getBBox().bbox(model.angle()); } else { - this.$el.remove(); + bbox = this.getNodeBBox(this.el); } - }, - renderChildren: function(children) { - children || (children = result(this, 'children')); - if (children) { - var isSVG = this.svgElement; - var namespace = V.namespace[isSVG ? 'svg' : 'xhtml']; - var doc = parseDOMJSON(children, namespace); - (isSVG ? this.vel : this.$el).empty().append(doc.fragment); - this.childNodes = doc.selectors; - } - return this; + return this.paper.localToPaperRect(bbox); }, - findAttribute: function(attributeName, node) { + getNodeBBox: function(magnet) { - var currentNode = node; + var rect = this.getNodeBoundingRect(magnet); + var magnetMatrix = this.getNodeMatrix(magnet); + var translateMatrix = this.getRootTranslateMatrix(); + var rotateMatrix = this.getRootRotateMatrix(); + return V.transformRect(rect, translateMatrix.multiply(rotateMatrix).multiply(magnetMatrix)); + }, - while (currentNode && currentNode.nodeType === 1) { - var attributeValue = currentNode.getAttribute(attributeName); - // attribute found - if (attributeValue) { return attributeValue; } - // do not climb up the DOM - if (currentNode === this.el) { return null; } - // try parent node - currentNode = currentNode.parentNode; - } + getNodeUnrotatedBBox: function(magnet) { - return null; + var rect = this.getNodeBoundingRect(magnet); + var magnetMatrix = this.getNodeMatrix(magnet); + var translateMatrix = this.getRootTranslateMatrix(); + return V.transformRect(rect, translateMatrix.multiply(magnetMatrix)); }, - // Override the Backbone `_ensureElement()` method in order to create an - // svg element (e.g., ``) node that wraps all the nodes of the Cell view. - // Expose class name setter as a separate method. - _ensureElement: function() { - if (!this.el) { - var tagName = result(this, 'tagName'); - var attrs = assign({}, result(this, 'attributes')); - var style = assign({}, result(this, 'style')); - if (this.id) { attrs.id = result(this, 'id'); } - this.setElement(this._createElement(tagName)); - this._setAttributes(attrs); - this._setStyle(style); - } else { - this.setElement(result(this, 'el')); - } - this._ensureElClassName(); - }, + getRootTranslateMatrix: function() { - _setAttributes: function(attrs) { - if (this.svgElement) { - this.vel.attr(attrs); - } else { - this.$el.attr(attrs); - } + var model = this.model; + var position = model.position(); + var mt = V.createSVGMatrix().translate(position.x, position.y); + return mt; }, - _setStyle: function(style) { - this.$el.css(style); - }, + getRootRotateMatrix: function() { - _createElement: function(tagName) { - if (this.svgElement) { - return document.createElementNS(V.namespace.svg, tagName); - } else { - return document.createElement(tagName); + var mr = V.createSVGMatrix(); + var model = this.model; + var angle = model.angle(); + if (angle) { + var bbox = model.getBBox(); + var cx = bbox.width / 2; + var cy = bbox.height / 2; + mr = mr.translate(cx, cy).rotate(angle).translate(-cx, -cy); } + return mr; }, - // Utilize an alternative DOM manipulation API by - // adding an element reference wrapped in Vectorizer. - _setElement: function(el) { - this.$el = el instanceof Backbone.$ ? el : Backbone.$(el); - this.el = this.$el[0]; - if (this.svgElement) { this.vel = V(this.el); } - }, + _notifyHighlight: function(eventName, el, opt) { + var assign, assign$1; - _ensureElClassName: function() { - var className = result(this, 'className'); - if (!className) { return; } - var prefixedClassName = addClassNamePrefix(className); - // Note: className removal here kept for backwards compatibility only - if (this.svgElement) { - this.vel.removeClass(className).addClass(prefixedClassName); + if ( opt === void 0 ) opt = {}; + var ref = this; + var rootNode = ref.el; + var node; + if (typeof el === 'string') { + (assign = this.findBySelector(el), node = assign[0], node = node === void 0 ? rootNode : node); } else { - this.$el.removeClass(className).addClass(prefixedClassName); + (assign$1 = this.$(el), node = assign$1[0], node = node === void 0 ? rootNode : node); + } + // set partial flag if the highlighted element is not the entire view. + opt.partial = (node !== rootNode); + // translate type flag into a type string + if (opt.type === undefined) { + var type; + switch (true) { + case opt.embedding: + type = HighlightingTypes.EMBEDDING; + break; + case opt.connecting: + type = HighlightingTypes.CONNECTING; + break; + case opt.magnetAvailability: + type = HighlightingTypes.MAGNET_AVAILABILITY; + break; + case opt.elementAvailability: + type = HighlightingTypes.ELEMENT_AVAILABILITY; + break; + default: + type = HighlightingTypes.DEFAULT; + break; + } + opt.type = type; } + this.notify(eventName, node, opt); + return this; }, - init: function() { - // Intentionally empty. - // This method is meant to be overridden. + highlight: function(el, opt) { + return this._notifyHighlight('cell:highlight', el, opt); }, - onRender: function() { - // Intentionally empty. - // This method is meant to be overridden. - }, + unhighlight: function(el, opt) { + if ( opt === void 0 ) opt = {}; - confirmUpdate: function() { - // Intentionally empty. - // This method is meant to be overridden. - return 0; + return this._notifyHighlight('cell:unhighlight', el, opt); }, - setTheme: function(theme, opt) { - - opt = opt || {}; + // Find the closest element that has the `magnet` attribute set to `true`. If there was not such + // an element found, return the root element of the cell view. + findMagnet: function(el) { - // Theme is already set, override is required, and override has not been set. - // Don't set the theme. - if (this.theme && this.requireSetThemeOverride && !opt.override) { - return this; + var root = this.el; + var magnet = this.$(el)[0]; + if (!magnet) { + magnet = root; } - this.removeThemeClassName(); - this.addThemeClassName(theme); - this.onSetTheme(this.theme/* oldTheme */, theme/* newTheme */); - this.theme = theme; + do { + var magnetAttribute = magnet.getAttribute('magnet'); + var isMagnetRoot = (magnet === root); + if ((magnetAttribute || isMagnetRoot) && magnetAttribute !== 'false') { + return magnet; + } + if (isMagnetRoot) { + // If the overall cell has set `magnet === false`, then return `undefined` to + // announce there is no magnet found for this cell. + // This is especially useful to set on cells that have 'ports'. In this case, + // only the ports have set `magnet === true` and the overall element has `magnet === false`. + return undefined; + } + magnet = magnet.parentNode; + } while (magnet); - return this; + return undefined; }, - addThemeClassName: function(theme) { + findProxyNode: function(el, type) { + el || (el = this.el); + var nodeSelector = el.getAttribute((type + "-selector")); + if (nodeSelector) { + var ref = this.findBySelector(nodeSelector); + var proxyNode = ref[0]; + if (proxyNode) { return proxyNode; } + } + return el; + }, - theme = theme || this.theme; - if (!theme) { return this; } + // Construct a unique selector for the `el` element within this view. + // `prevSelector` is being collected through the recursive call. + // No value for `prevSelector` is expected when using this method. + getSelector: function(el, prevSelector) { - var className = this.themeClassNamePrefix + theme; + var selector; - if (this.svgElement) { - this.vel.addClass(className); - } else { - this.$el.addClass(className); + if (el === this.el) { + if (typeof prevSelector === 'string') { selector = '> ' + prevSelector; } + return selector; } - return this; - }, - - removeThemeClassName: function(theme) { + if (el) { - theme = theme || this.theme; + var nthChild = V(el).index() + 1; + selector = el.tagName + ':nth-child(' + nthChild + ')'; - var className = this.themeClassNamePrefix + theme; + if (prevSelector) { + selector += ' > ' + prevSelector; + } - if (this.svgElement) { - this.vel.removeClass(className); - } else { - this.$el.removeClass(className); + selector = this.getSelector(el.parentNode, selector); } - return this; + return selector; }, - onSetTheme: function(oldTheme, newTheme) { - // Intentionally empty. - // This method is meant to be overridden. - }, + addLinkFromMagnet: function(magnet, x, y) { - remove: function() { + var paper = this.paper; + var graph = paper.model; - this.onRemove(); - this.undelegateDocumentEvents(); + var link = paper.getDefaultLink(this, magnet); + link.set({ + source: this.getLinkEnd(magnet, x, y, link, 'source'), + target: { x: x, y: y } + }).addTo(graph, { + async: false, + ui: true + }); - views[this.cid] = null; + return link.findView(paper); + }, - Backbone.View.prototype.remove.apply(this, arguments); + getLinkEnd: function(magnet) { + var ref; - return this; - }, + var args = [], len = arguments.length - 1; + while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; - onRemove: function() { - // Intentionally empty. - // This method is meant to be overridden. - }, + var model = this.model; + var id = model.id; + var port = this.findAttribute('port', magnet); + // Find a unique `selector` of the element under pointer that is a magnet. + var selector = magnet.getAttribute('joint-selector'); - getEventNamespace: function() { - // Returns a per-session unique namespace - return '.joint-event-ns-' + this.cid; + var end = { id: id }; + if (selector != null) { end.magnet = selector; } + if (port != null) { + end.port = port; + if (!model.hasPort(port) && !selector) { + // port created via the `port` attribute (not API) + end.selector = this.getSelector(magnet); + } + } else if (selector == null && this.el !== magnet) { + end.selector = this.getSelector(magnet); + } + + return (ref = this).customizeLinkEnd.apply(ref, [ end, magnet ].concat( args )); }, - delegateElementEvents: function(element, events, data) { - if (!events) { return this; } - data || (data = {}); - var eventNS = this.getEventNamespace(); - for (var eventName in events) { - var method = events[eventName]; - if (typeof method !== 'function') { method = this[method]; } - if (!method) { continue; } - $(element).on(eventName + eventNS, data, method.bind(this)); + customizeLinkEnd: function(end, magnet, x, y, link, endType) { + var ref = this; + var paper = ref.paper; + var ref$1 = paper.options; + var connectionStrategy = ref$1.connectionStrategy; + if (typeof connectionStrategy === 'function') { + var strategy = connectionStrategy.call(paper, end, this, magnet, new Point(x, y), link, endType, paper); + if (strategy) { return strategy; } } - return this; + return end; }, - undelegateElementEvents: function(element) { - $(element).off(this.getEventNamespace()); - return this; - }, + getMagnetFromLinkEnd: function(end) { - delegateDocumentEvents: function(events, data) { - events || (events = result(this, 'documentEvents')); - return this.delegateElementEvents(document, events, data); + var root = this.el; + var port = end.port; + var selector = end.magnet; + var model = this.model; + var magnet; + if (port != null && model.isElement() && model.hasPort(port)) { + magnet = this.findPortNode(port, selector) || root; + } else { + if (!selector) { selector = end.selector; } + if (!selector && port != null) { + // link end has only `id` and `port` property referencing + // a port created via the `port` attribute (not API). + selector = '[port="' + port + '"]'; + } + magnet = this.findBySelector(selector, root, this.selectors)[0]; + } + + return this.findProxyNode(magnet, 'magnet'); }, - undelegateDocumentEvents: function() { - return this.undelegateElementEvents(document); + dragLinkStart: function(evt, magnet, x, y) { + this.model.startBatch('add-link'); + var linkView = this.addLinkFromMagnet(magnet, x, y); + // backwards compatibility events + linkView.notifyPointerdown(evt, x, y); + linkView.eventData(evt, linkView.startArrowheadMove('target', { whenNotAllowed: 'remove' })); + this.eventData(evt, { linkView: linkView }); }, - eventData: function(evt, data) { - if (!evt) { throw new Error('eventData(): event object required.'); } - var currentData = evt.data; - var key = '__' + this.cid + '__'; - if (data === undefined) { - if (!currentData) { return {}; } - return currentData[key] || {}; + dragLink: function(evt, x, y) { + var data = this.eventData(evt); + var linkView = data.linkView; + if (linkView) { + linkView.pointermove(evt, x, y); + } else { + var paper = this.paper; + var magnetThreshold = paper.options.magnetThreshold; + var currentTarget = this.getEventTarget(evt); + var targetMagnet = data.targetMagnet; + if (magnetThreshold === 'onleave') { + // magnetThreshold when the pointer leaves the magnet + if (targetMagnet === currentTarget || V(targetMagnet).contains(currentTarget)) { return; } + } else { + // magnetThreshold defined as a number of movements + if (paper.eventData(evt).mousemoved <= magnetThreshold) { return; } + } + this.dragLinkStart(evt, targetMagnet, x, y); } - currentData || (currentData = evt.data = {}); - currentData[key] || (currentData[key] = {}); - assign(currentData[key], data); - return this; }, - stopPropagation: function(evt) { - this.eventData(evt, { propagationStopped: true }); - return this; + dragLinkEnd: function(evt, x, y) { + var data = this.eventData(evt); + var linkView = data.linkView; + if (!linkView) { return; } + linkView.pointerup(evt, x, y); + this.model.stopBatch('add-link'); }, - isPropagationStopped: function(evt) { - return !!this.eventData(evt).propagationStopped; - } - - }, { - - extend: function() { + getAttributeDefinition: function(attrName) { - var args = Array.from(arguments); + return this.model.constructor.getAttributeDefinition(attrName); + }, - // Deep clone the prototype and static properties objects. - // This prevents unexpected behavior where some properties are overwritten outside of this function. - var protoProps = args[0] && assign({}, args[0]) || {}; - var staticProps = args[1] && assign({}, args[1]) || {}; + setNodeAttributes: function(node, attrs) { - // Need the real render method so that we can wrap it and call it later. - var renderFn = protoProps.render || (this.prototype && this.prototype.render) || null; + if (!isEmpty(attrs)) { + if (node instanceof SVGElement) { + V(node).attr(attrs); + } else { + $(node).attr(attrs); + } + } + }, - /* - Wrap the real render method so that: - .. `onRender` is always called. - .. `this` is always returned. - */ - protoProps.render = function() { + processNodeAttributes: function(node, attrs) { - if (typeof renderFn === 'function') { - // Call the original render method. - renderFn.apply(this, arguments); + var attrName, attrVal, def, i, n; + var normalAttrs, setAttrs, positionAttrs, offsetAttrs; + var relatives = []; + // divide the attributes between normal and special + for (attrName in attrs) { + if (!attrs.hasOwnProperty(attrName)) { continue; } + attrVal = attrs[attrName]; + def = this.getAttributeDefinition(attrName); + if (def && (!isFunction(def.qualify) || def.qualify.call(this, attrVal, node, attrs))) { + if (isString(def.set)) { + normalAttrs || (normalAttrs = {}); + normalAttrs[def.set] = attrVal; + } + if (attrVal !== null) { + relatives.push(attrName, def); + } + } else { + normalAttrs || (normalAttrs = {}); + normalAttrs[toKebabCase(attrName)] = attrVal; } + } - if (this.render.__render__ === renderFn) { - // Should always call onRender() method. - // Should call it only once when renderFn is actual prototype method i.e. not the wrapper - this.onRender(); + // handle the rest of attributes via related method + // from the special attributes namespace. + for (i = 0, n = relatives.length; i < n; i+=2) { + attrName = relatives[i]; + def = relatives[i+1]; + attrVal = attrs[attrName]; + if (isFunction(def.set)) { + setAttrs || (setAttrs = {}); + setAttrs[attrName] = attrVal; } + if (isFunction(def.position)) { + positionAttrs || (positionAttrs = {}); + positionAttrs[attrName] = attrVal; + } + if (isFunction(def.offset)) { + offsetAttrs || (offsetAttrs = {}); + offsetAttrs[attrName] = attrVal; + } + } - // Should always return itself. - return this; - }; - - protoProps.render.__render__ = renderFn; - - return Backbone.View.extend.call(this, protoProps, staticProps); - } - }); - - var DoubleTapEventName = 'dbltap'; - if ($.event && !(DoubleTapEventName in $.event.special)) { - var maxDelay = config.doubleTapInterval; - var minDelay = 30; - $.event.special[DoubleTapEventName] = { - bindType: 'touchend', - delegateType: 'touchend', - handle: function(event) { - var ref; - - var args = [], len = arguments.length - 1; - while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; - var handleObj = event.handleObj; - var target = event.target; - var targetData = $.data(target); - var now = new Date().getTime(); - var delta = 'lastTouch' in targetData ? now - targetData.lastTouch : 0; - if (delta < maxDelay && delta > minDelay) { - targetData.lastTouch = null; - event.type = handleObj.origType; - // let jQuery handle the triggering of "dbltap" event handlers - (ref = handleObj.handler).call.apply(ref, [ this, event ].concat( args )); - } else { - targetData.lastTouch = now; - } - } - }; - } - - var index$1 = ({ - views: views, - View: View - }); - - function toArray$1(obj) { - if (!obj) { return []; } - if (Array.isArray(obj)) { return obj; } - return [obj]; - } + return { + raw: attrs, + normal: normalAttrs, + set: setAttrs, + position: positionAttrs, + offset: offsetAttrs + }; + }, - var HighlighterView = View.extend({ + updateRelativeAttributes: function(node, attrs, refBBox, opt) { - tagName: 'g', - svgElement: true, - className: 'highlight', + opt || (opt = {}); - HIGHLIGHT_FLAG: 1, - UPDATE_PRIORITY: 3, - DETACHABLE: false, - UPDATABLE: true, - MOUNTABLE: true, + var attrName, attrVal, def; + var rawAttrs = attrs.raw || {}; + var nodeAttrs = attrs.normal || {}; + var setAttrs = attrs.set; + var positionAttrs = attrs.position; + var offsetAttrs = attrs.offset; - cellView: null, - nodeSelector: null, - node: null, - updateRequested: false, - transformGroup: null, + for (attrName in setAttrs) { + attrVal = setAttrs[attrName]; + def = this.getAttributeDefinition(attrName); + // SET - set function should return attributes to be set on the node, + // which will affect the node dimensions based on the reference bounding + // box. e.g. `width`, `height`, `d`, `rx`, `ry`, `points + var setResult = def.set.call(this, attrVal, refBBox.clone(), node, rawAttrs); + if (isObject$1(setResult)) { + assign(nodeAttrs, setResult); + } else if (setResult !== undefined) { + nodeAttrs[attrName] = setResult; + } + } - requestUpdate: function requestUpdate(cellView, nodeSelector) { - var paper = cellView.paper; - this.cellView = cellView; - this.nodeSelector = nodeSelector; - if (paper) { - this.updateRequested = true; - paper.requestViewUpdate(this, this.HIGHLIGHT_FLAG, this.UPDATE_PRIORITY); + if (node instanceof HTMLElement) { + // TODO: setting the `transform` attribute on HTMLElements + // via `node.style.transform = 'matrix(...)';` would introduce + // a breaking change (e.g. basic.TextBlock). + this.setNodeAttributes(node, nodeAttrs); + return; } - }, - confirmUpdate: function confirmUpdate() { - // The cellView is now rendered/updated since it has a higher update priority. - this.updateRequested = false; - var ref = this; - var cellView = ref.cellView; - var nodeSelector = ref.nodeSelector; - this.update(cellView, nodeSelector); - this.mount(); - this.transform(); - return 0; - }, + // The final translation of the subelement. + var nodeTransform = nodeAttrs.transform; + var nodeMatrix = V.transformStringToMatrix(nodeTransform); + var nodePosition = Point(nodeMatrix.e, nodeMatrix.f); + if (nodeTransform) { + nodeAttrs = omit(nodeAttrs, 'transform'); + nodeMatrix.e = nodeMatrix.f = 0; + } - findNode: function findNode(cellView, nodeSelector) { - var assign, assign$1; + // Calculate node scale determined by the scalable group + // only if later needed. + var sx, sy, translation; + if (positionAttrs || offsetAttrs) { + var nodeScale = this.getNodeScale(node, opt.scalableNode); + sx = nodeScale.sx; + sy = nodeScale.sy; + } - if ( nodeSelector === void 0 ) nodeSelector = null; - var el; - if (typeof nodeSelector === 'string') { - (assign = cellView.findBySelector(nodeSelector), el = assign[0]); - } else if (isPlainObject(nodeSelector)) { - var isLink = cellView.model.isLink(); - var label = nodeSelector.label; if ( label === void 0 ) label = null; - var port = nodeSelector.port; - var selector = nodeSelector.selector; - if (isLink && label !== null) { - // Link Label Selector - el = cellView.findLabelNode(label, selector); - } else if (!isLink && port) { - // Element Port Selector - el = cellView.findPortNode(port, selector); - } else { - // Cell Selector - (assign$1 = cellView.findBySelector(selector), el = assign$1[0]); + var positioned = false; + for (attrName in positionAttrs) { + attrVal = positionAttrs[attrName]; + def = this.getAttributeDefinition(attrName); + // POSITION - position function should return a point from the + // reference bounding box. The default position of the node is x:0, y:0 of + // the reference bounding box or could be further specify by some + // SVG attributes e.g. `x`, `y` + translation = def.position.call(this, attrVal, refBBox.clone(), node, rawAttrs); + if (translation) { + nodePosition.offset(Point(translation).scale(sx, sy)); + positioned || (positioned = true); } - } else if (nodeSelector) { - el = V.toNode(nodeSelector); - if (!(el instanceof SVGElement)) { el = null; } } - return el ? el : null; - }, - mount: function mount() { - var ref = this; - var MOUNTABLE = ref.MOUNTABLE; - var cellView = ref.cellView; - var el = ref.el; - var options = ref.options; - var transformGroup = ref.transformGroup; - if (!MOUNTABLE || transformGroup) { return; } - var cellViewRoot = cellView.vel; - var paper = cellView.paper; - var layerName = options.layer; - if (layerName) { - var vGroup = this.transformGroup = V('g').addClass('highlight-transform').append(el); - paper.getLayerView(layerName).insertSortedNode(vGroup.node, options.z); - } else { - // TODO: prepend vs append - if (!el.parentNode || el.nextSibling) { - // Not appended yet or not the last child - cellViewRoot.append(el); + // The node bounding box could depend on the `size` set from the previous loop. + // Here we know, that all the size attributes have been already set. + this.setNodeAttributes(node, nodeAttrs); + + var offseted = false; + if (offsetAttrs) { + // Check if the node is visible + var nodeBoundingRect = this.getNodeBoundingRect(node); + if (nodeBoundingRect.width > 0 && nodeBoundingRect.height > 0) { + var nodeBBox = V.transformRect(nodeBoundingRect, nodeMatrix).scale(1 / sx, 1 / sy); + for (attrName in offsetAttrs) { + attrVal = offsetAttrs[attrName]; + def = this.getAttributeDefinition(attrName); + // OFFSET - offset function should return a point from the element + // bounding box. The default offset point is x:0, y:0 (origin) or could be further + // specify with some SVG attributes e.g. `text-anchor`, `cx`, `cy` + translation = def.offset.call(this, attrVal, nodeBBox, node, rawAttrs); + if (translation) { + nodePosition.offset(Point(translation).scale(sx, sy)); + offseted || (offseted = true); + } + } } } - }, - unmount: function unmount() { - var ref = this; - var MOUNTABLE = ref.MOUNTABLE; - var transformGroup = ref.transformGroup; - var vel = ref.vel; - if (!MOUNTABLE) { return; } - if (transformGroup) { - this.transformGroup = null; - transformGroup.remove(); - } else { - vel.remove(); + // Do not touch node's transform attribute if there is no transformation applied. + if (nodeTransform !== undefined || positioned || offseted) { + // Round the coordinates to 1 decimal point. + nodePosition.round(1); + nodeMatrix.e = nodePosition.x; + nodeMatrix.f = nodePosition.y; + node.setAttribute('transform', V.matrixToTransformString(nodeMatrix)); + // TODO: store nodeMatrix metrics? } }, - transform: function transform() { - var ref = this; - var transformGroup = ref.transformGroup; - var cellView = ref.cellView; - var updateRequested = ref.updateRequested; - if (!transformGroup || cellView.model.isLink() || updateRequested) { return; } - var translateMatrix = cellView.getRootTranslateMatrix(); - var rotateMatrix = cellView.getRootRotateMatrix(); - var transformMatrix = translateMatrix.multiply(rotateMatrix); - transformGroup.attr('transform', V.matrixToTransformString(transformMatrix)); - }, + getNodeScale: function(node, scalableNode) { - update: function update() { - var ref = this; - var prevNode = ref.node; - var cellView = ref.cellView; - var nodeSelector = ref.nodeSelector; - var updateRequested = ref.updateRequested; - var id = ref.id; - if (updateRequested) { return; } - var node = this.node = this.findNode(cellView, nodeSelector); - if (prevNode) { - this.unhighlight(cellView, prevNode); - } - if (node) { - this.highlight(cellView, node); - this.mount(); + // Check if the node is a descendant of the scalable group. + var sx, sy; + if (scalableNode && scalableNode.contains(node)) { + var scale = scalableNode.scale(); + sx = 1 / scale.sx; + sy = 1 / scale.sy; } else { - this.unmount(); - cellView.notify('cell:highlight:invalid', id, this); + sx = 1; + sy = 1; } - }, - onRemove: function onRemove() { - var ref = this; - var node = ref.node; - var cellView = ref.cellView; - var id = ref.id; - var constructor = ref.constructor; - if (node) { - this.unhighlight(cellView, node); - } - this.unmount(); - constructor._removeRef(cellView, id); + return { sx: sx, sy: sy }; }, - highlight: function highlight(_cellView, _node) { - // to be overridden + cleanNodesCache: function() { + this.metrics = {}; }, - unhighlight: function unhighlight(_cellView, _node) { - // to be overridden - } + nodeCache: function(magnet) { - }, { + var metrics = this.metrics; + // Don't use cache? It most likely a custom view with overridden update. + if (!metrics) { return {}; } + var id = V.ensureId(magnet); + var value = metrics[id]; + if (!value) { value = metrics[id] = {}; } + return value; + }, - _views: {}, + getNodeData: function(magnet) { - // Used internally by CellView highlight() - highlight: function(cellView, node, opt) { - var id = this.uniqueId(node, opt); - this.add(cellView, node, id, opt); + var metrics = this.nodeCache(magnet); + if (!metrics.data) { metrics.data = {}; } + return metrics.data; }, - // Used internally by CellView unhighlight() - unhighlight: function(cellView, node, opt) { - var id = this.uniqueId(node, opt); - this.remove(cellView, id); + getNodeBoundingRect: function(magnet) { + + var metrics = this.nodeCache(magnet); + if (metrics.boundingRect === undefined) { metrics.boundingRect = V(magnet).getBBox(); } + return new Rect(metrics.boundingRect); }, - get: function get(cellView, id) { - if ( id === void 0 ) id = null; + getNodeMatrix: function(magnet) { - var cid = cellView.cid; - var ref$2 = this; - var _views = ref$2._views; - var refs = _views[cid]; - if (id === null) { - // all highlighters - var views = []; - if (!refs) { return views; } - for (var hid in refs) { - var ref = refs[hid]; - if (ref instanceof this) { - views.push(ref); - } - } - return views; - } else { - // single highlighter - if (!refs) { return null; } - if (id in refs) { - var ref$1 = refs[id]; - if (ref$1 instanceof this) { return ref$1; } - } - return null; + var metrics = this.nodeCache(magnet); + if (metrics.magnetMatrix === undefined) { + var target = this.rotatableNode || this.el; + metrics.magnetMatrix = V(magnet).getTransformToElement(target); } + return V.createSVGMatrix(metrics.magnetMatrix); }, - add: function add(cellView, nodeSelector, id, opt) { - if ( opt === void 0 ) opt = {}; + getNodeShape: function(magnet) { - if (!id) { throw new Error('dia.HighlighterView: An ID required.'); } - // Search the existing view amongst all the highlighters - var previousView = HighlighterView.get(cellView, id); - if (previousView) { previousView.remove(); } - var view = new this(opt); - view.id = id; - this._addRef(cellView, id, view); - view.requestUpdate(cellView, nodeSelector); - return view; + var metrics = this.nodeCache(magnet); + if (metrics.geometryShape === undefined) { metrics.geometryShape = V(magnet).toGeometryShape(); } + return metrics.geometryShape.clone(); }, - _addRef: function _addRef(cellView, id, view) { - var cid = cellView.cid; - var ref = this; - var _views = ref._views; - var refs = _views[cid]; - if (!refs) { refs = _views[cid] = {}; } - refs[id] = view; + isNodeConnection: function(node) { + return this.model.isLink() && (!node || node === this.el); }, - _removeRef: function _removeRef(cellView, id) { - var cid = cellView.cid; - var ref = this; - var _views = ref._views; - var refs = _views[cid]; - if (!refs) { return; } - if (id) { delete refs[id]; } - for (var _ in refs) { return; } - delete _views[cid]; - }, + findNodesAttributes: function(attrs, root, selectorCache, selectors) { - remove: function remove(cellView, id) { - if ( id === void 0 ) id = null; + var i, n, nodeAttrs, nodeId; + var nodesAttrs = {}; + var mergeIds = []; + for (var selector in attrs) { + if (!attrs.hasOwnProperty(selector)) { continue; } + nodeAttrs = attrs[selector]; + if (!isPlainObject(nodeAttrs)) { continue; } // Not a valid selector-attributes pair + var selected = selectorCache[selector] = this.findBySelector(selector, root, selectors); + for (i = 0, n = selected.length; i < n; i++) { + var node = selected[i]; + nodeId = V.ensureId(node); + // "unique" selectors are selectors that referencing a single node (defined by `selector`) + // groupSelector referencing a single node is not "unique" + var unique = (selectors && selectors[selector] === node); + var prevNodeAttrs = nodesAttrs[nodeId]; + if (prevNodeAttrs) { + // Note, that nodes referenced by deprecated `CSS selectors` are not taken into account. + // e.g. css:`.circle` and selector:`circle` can be applied in a random order + if (!prevNodeAttrs.array) { + mergeIds.push(nodeId); + prevNodeAttrs.array = true; + prevNodeAttrs.attributes = [prevNodeAttrs.attributes]; + prevNodeAttrs.selectedLength = [prevNodeAttrs.selectedLength]; + } + var attributes = prevNodeAttrs.attributes; + var selectedLength = prevNodeAttrs.selectedLength; + if (unique) { + // node referenced by `selector` + attributes.unshift(nodeAttrs); + selectedLength.unshift(-1); + } else { + // node referenced by `groupSelector` + var sortIndex = sortedIndex(selectedLength, n); + attributes.splice(sortIndex, 0, nodeAttrs); + selectedLength.splice(sortIndex, 0, n); + } + } else { + nodesAttrs[nodeId] = { + attributes: nodeAttrs, + selectedLength: unique ? -1 : n, + node: node, + array: false + }; + } + } + } - toArray$1(this.get(cellView, id)).forEach(function (view) { - view.remove(); - }); + for (i = 0, n = mergeIds.length; i < n; i++) { + nodeId = mergeIds[i]; + nodeAttrs = nodesAttrs[nodeId]; + nodeAttrs.attributes = merge.apply(void 0, [ {} ].concat( nodeAttrs.attributes.reverse() )); + } + + return nodesAttrs; }, - update: function update(cellView, id, dirty) { - if ( id === void 0 ) id = null; - if ( dirty === void 0 ) dirty = false; + getEventTarget: function(evt, opt) { + if ( opt === void 0 ) opt = {}; - toArray$1(this.get(cellView, id)).forEach(function (view) { - if (dirty || view.UPDATABLE) { view.update(); } - }); + // Touchmove/Touchend event's target is not reflecting the element under the coordinates as mousemove does. + // It holds the element when a touchstart triggered. + var target = evt.target; + var type = evt.type; + var clientX = evt.clientX; if ( clientX === void 0 ) clientX = 0; + var clientY = evt.clientY; if ( clientY === void 0 ) clientY = 0; + if (opt.fromPoint || type === 'touchmove' || type === 'touchend') { + return document.elementFromPoint(clientX, clientY); + } + + return target; }, - transform: function transform(cellView, id) { - if ( id === void 0 ) id = null; + // Default is to process the `model.attributes.attrs` object and set attributes on subelements based on the selectors, + // unless `attrs` parameter was passed. + updateDOMSubtreeAttributes: function(rootNode, attrs, opt) { - toArray$1(this.get(cellView, id)).forEach(function (view) { - if (view.UPDATABLE) { view.transform(); } - }); - }, + opt || (opt = {}); + opt.rootBBox || (opt.rootBBox = Rect()); + opt.selectors || (opt.selectors = this.selectors); // selector collection to use - uniqueId: function uniqueId(node, opt) { - if ( opt === void 0 ) opt = ''; + // Cache table for query results and bounding box calculation. + // Note that `selectorCache` needs to be invalidated for all + // `updateAttributes` calls, as the selectors might pointing + // to nodes designated by an attribute or elements dynamically + // created. + var selectorCache = {}; + var bboxCache = {}; + var relativeItems = []; + var relativeRefItems = []; + var item, node, nodeAttrs, nodeData, processedAttrs; - return V.ensureId(node) + JSON.stringify(opt); - } + var roAttrs = opt.roAttributes; + var nodesAttrs = this.findNodesAttributes(roAttrs || attrs, rootNode, selectorCache, opt.selectors); + // `nodesAttrs` are different from all attributes, when + // rendering only attributes sent to this method. + var nodesAllAttrs = (roAttrs) + ? this.findNodesAttributes(attrs, rootNode, selectorCache, opt.selectors) + : nodesAttrs; - }); + for (var nodeId in nodesAttrs) { + nodeData = nodesAttrs[nodeId]; + nodeAttrs = nodeData.attributes; + node = nodeData.node; + processedAttrs = this.processNodeAttributes(node, nodeAttrs); - var stroke = HighlighterView.extend({ + if (!processedAttrs.set && !processedAttrs.position && !processedAttrs.offset) { + // Set all the normal attributes right on the SVG/HTML element. + this.setNodeAttributes(node, processedAttrs.normal); - tagName: 'path', - className: 'highlight-stroke', - attributes: { - 'pointer-events': 'none', - 'vector-effect': 'non-scaling-stroke', - 'fill': 'none' - }, + } else { - options: { - padding: 3, - rx: 0, - ry: 0, - useFirstSubpath: false, - attrs: { - 'stroke-width': 3, - 'stroke': '#FEB663' - } - }, + var nodeAllAttrs = nodesAllAttrs[nodeId] && nodesAllAttrs[nodeId].attributes; + var refSelector = (nodeAllAttrs && (nodeAttrs.ref === undefined)) + ? nodeAllAttrs.ref + : nodeAttrs.ref; - getPathData: function getPathData(cellView, node) { - var ref = this; - var options = ref.options; - var useFirstSubpath = options.useFirstSubpath; - var d; - try { - var vNode = V(node); - d = vNode.convertToPathData().trim(); - if (vNode.tagName() === 'PATH' && useFirstSubpath) { - var secondSubpathIndex = d.search(/.M/i) + 1; - if (secondSubpathIndex > 0) { - d = d.substr(0, secondSubpathIndex); + var refNode; + if (refSelector) { + refNode = (selectorCache[refSelector] || this.findBySelector(refSelector, rootNode, opt.selectors))[0]; + if (!refNode) { + throw new Error('dia.CellView: "' + refSelector + '" reference does not exist.'); + } + } else { + refNode = null; + } + + item = { + node: node, + refNode: refNode, + processedAttributes: processedAttrs, + allAttributes: nodeAllAttrs + }; + + if (refNode) { + // If an element in the list is positioned relative to this one, then + // we want to insert this one before it in the list. + var itemIndex = relativeRefItems.findIndex(function(item) { + return item.refNode === node; + }); + + if (itemIndex > -1) { + relativeRefItems.splice(itemIndex, 0, item); + } else { + relativeRefItems.push(item); + } + } else { + // A node with no ref attribute. To be updated before the nodes referencing other nodes. + // The order of no-ref-items is not specified/important. + relativeItems.push(item); } } - } catch (error) { - // Failed to get path data from magnet element. - // Draw a rectangle around the node instead. - var nodeBBox = cellView.getNodeBoundingRect(node); - d = V.rectToPath(assign({}, options, nodeBBox.toJSON())); } - return d; - }, - highlightConnection: function highlightConnection(cellView) { - this.vel.attr('d', cellView.getSerializedConnection()); - }, + relativeItems.push.apply(relativeItems, relativeRefItems); - highlightNode: function highlightNode(cellView, node) { - var ref = this; - var vel = ref.vel; - var options = ref.options; - var padding = options.padding; - var layer = options.layer; - var highlightMatrix = cellView.getNodeMatrix(node); - // Add padding to the highlight element. - if (padding) { - if (!layer && node === cellView.el) { - // If the highlighter is appended to the cellView - // and we measure the size of the cellView wrapping group - // it's necessary to remove the highlighter first - vel.remove(); + var rotatableMatrix; + for (var i = 0, n = relativeItems.length; i < n; i++) { + item = relativeItems[i]; + node = item.node; + refNode = item.refNode; + + // Find the reference element bounding box. If no reference was provided, we + // use the optional bounding box. + var vRotatable = V(opt.rotatableNode); + var refNodeId = refNode ? V.ensureId(refNode) : ''; + var isRefNodeRotatable = !!vRotatable && !!refNode && vRotatable.contains(refNode); + var unrotatedRefBBox = bboxCache[refNodeId]; + if (!unrotatedRefBBox) { + // Get the bounding box of the reference element relative to the `rotatable` `` (without rotation) + // or to the root `` element if no rotatable group present if reference node present. + // Uses the bounding box provided. + var transformationTarget = (isRefNodeRotatable) ? vRotatable : rootNode; + unrotatedRefBBox = bboxCache[refNodeId] = (refNode) + ? V(refNode).getBBox({ target: transformationTarget }) + : opt.rootBBox; } - var nodeBBox = cellView.getNodeBoundingRect(node); - var cx = nodeBBox.x + (nodeBBox.width / 2); - var cy = nodeBBox.y + (nodeBBox.height / 2); - nodeBBox = V.transformRect(nodeBBox, highlightMatrix); - var width = Math.max(nodeBBox.width, 1); - var height = Math.max(nodeBBox.height, 1); - var sx = (width + padding) / width; - var sy = (height + padding) / height; - var paddingMatrix = V.createSVGMatrix({ - a: sx, - b: 0, - c: 0, - d: sy, - e: cx - sx * cx, - f: cy - sy * cy - }); - highlightMatrix = highlightMatrix.multiply(paddingMatrix); + + if (roAttrs) { + // if there was a special attribute affecting the position amongst passed-in attributes + // we have to merge it with the rest of the element's attributes as they are necessary + // to update the position relatively (i.e `ref-x` && 'ref-dx') + processedAttrs = this.processNodeAttributes(node, item.allAttributes); + this.mergeProcessedAttributes(processedAttrs, item.processedAttributes); + + } else { + processedAttrs = item.processedAttributes; + } + + var refBBox = unrotatedRefBBox; + if (isRefNodeRotatable && !vRotatable.contains(node)) { + // if the referenced node is inside the rotatable group while the updated node is outside, + // we need to take the rotatable node transformation into account + if (!rotatableMatrix) { rotatableMatrix = V.transformStringToMatrix(vRotatable.attr('transform')); } + refBBox = V.transformRect(unrotatedRefBBox, rotatableMatrix); + } + + this.updateRelativeAttributes(node, processedAttrs, refBBox, opt); } - vel.attr({ - 'd': this.getPathData(cellView, node), - 'transform': V.matrixToTransformString(highlightMatrix) - }); }, - highlight: function highlight(cellView, node) { - var ref = this; - var vel = ref.vel; - var options = ref.options; - vel.attr(options.attrs); - if (cellView.isNodeConnection(node)) { - this.highlightConnection(cellView); - } else { - this.highlightNode(cellView, node); - } - } + mergeProcessedAttributes: function(processedAttrs, roProcessedAttrs) { - }); + processedAttrs.set || (processedAttrs.set = {}); + processedAttrs.position || (processedAttrs.position = {}); + processedAttrs.offset || (processedAttrs.offset = {}); - var MASK_CLIP = 20; + assign(processedAttrs.set, roProcessedAttrs.set); + assign(processedAttrs.position, roProcessedAttrs.position); + assign(processedAttrs.offset, roProcessedAttrs.offset); - function forEachDescendant(vel, fn) { - var descendants = vel.children(); - while (descendants.length > 0) { - var descendant = descendants.shift(); - if (fn(descendant)) { - descendants.push.apply(descendants, descendant.children()); + // Handle also the special transform property. + var transform = processedAttrs.normal && processedAttrs.normal.transform; + if (transform !== undefined && roProcessedAttrs.normal) { + roProcessedAttrs.normal.transform = transform; } - } - } + processedAttrs.normal = roProcessedAttrs.normal; + }, - var mask = HighlighterView.extend({ + onRemove: function() { + this.removeTools(); + this.removeHighlighters(); + }, - tagName: 'rect', - className: 'highlight-mask', - attributes: { - 'pointer-events': 'none' + _toolsView: null, + + hasTools: function(name) { + var toolsView = this._toolsView; + if (!toolsView) { return false; } + if (!name) { return true; } + return (toolsView.getName() === name); }, - options: { - padding: 3, - maskClip: MASK_CLIP, - deep: false, - attrs: { - 'stroke': '#FEB663', - 'stroke-width': 3, - 'stroke-linecap': 'butt', - 'stroke-linejoin': 'miter', + addTools: function(toolsView) { + + this.removeTools(); + + if (toolsView) { + this._toolsView = toolsView; + toolsView.configure({ relatedView: this }); + toolsView.listenTo(this.paper, 'tools:event', this.onToolEvent.bind(this)); } + return this; }, - VISIBLE: 'white', - INVISIBLE: 'black', + updateTools: function(opt) { - MASK_ROOT_ATTRIBUTE_BLACKLIST: [ - 'marker-start', - 'marker-end', - 'marker-mid', - 'transform', - 'stroke-dasharray' - ], + var toolsView = this._toolsView; + if (toolsView) { toolsView.update(opt); } + return this; + }, - MASK_CHILD_ATTRIBUTE_BLACKLIST: [ - 'stroke', - 'fill', - 'stroke-width', - 'stroke-opacity', - 'stroke-dasharray', - 'fill-opacity', - 'marker-start', - 'marker-end', - 'marker-mid' - ], + removeTools: function() { - // TODO: change the list to a function callback - MASK_REPLACE_TAGS: [ - 'FOREIGNOBJECT', - 'IMAGE', - 'USE', - 'TEXT', - 'TSPAN', - 'TEXTPATH' - ], + var toolsView = this._toolsView; + if (toolsView) { + toolsView.remove(); + this._toolsView = null; + } + return this; + }, - // TODO: change the list to a function callback - MASK_REMOVE_TAGS: [ - 'TEXT', - 'TSPAN', - 'TEXTPATH' - ], + hideTools: function() { - transformMaskChild: function transformMaskChild(cellView, childEl) { - var ref = this; - var MASK_CHILD_ATTRIBUTE_BLACKLIST = ref.MASK_CHILD_ATTRIBUTE_BLACKLIST; - var MASK_REPLACE_TAGS = ref.MASK_REPLACE_TAGS; - var MASK_REMOVE_TAGS = ref.MASK_REMOVE_TAGS; - var childTagName = childEl.tagName(); - // Do not include the element in the mask's image - if (!V.isSVGGraphicsElement(childEl) || MASK_REMOVE_TAGS.includes(childTagName)) { - childEl.remove(); - return false; - } - // Replace the element with a rectangle - if (MASK_REPLACE_TAGS.includes(childTagName)) { - // Note: clone() method does not change the children ids - var originalChild = cellView.vel.findOne(("#" + (childEl.id))); - if (originalChild) { - var originalNode = originalChild.node; - var childBBox = cellView.getNodeBoundingRect(originalNode); - if (cellView.model.isElement()) { - childBBox = V.transformRect(childBBox, cellView.getNodeMatrix(originalNode)); - } - var replacement = V('rect', childBBox.toJSON()); - var ref$1 = childBBox.center(); - var ox = ref$1.x; - var oy = ref$1.y; - var ref$2 = originalChild.rotate(); - var angle = ref$2.angle; - var cx = ref$2.cx; if ( cx === void 0 ) cx = ox; - var cy = ref$2.cy; if ( cy === void 0 ) cy = oy; - if (angle) { replacement.rotate(angle, cx, cy); } - // Note: it's not important to keep the same sibling index since all subnodes are filled - childEl.parent().append(replacement); - } - childEl.remove(); - return false; + var toolsView = this._toolsView; + if (toolsView) { toolsView.hide(); } + return this; + }, + + showTools: function() { + + var toolsView = this._toolsView; + if (toolsView) { toolsView.show(); } + return this; + }, + + onToolEvent: function(event) { + switch (event) { + case 'remove': + this.removeTools(); + break; + case 'hide': + this.hideTools(); + break; + case 'show': + this.showTools(); + break; } - // Keep the element, but clean it from certain attributes - MASK_CHILD_ATTRIBUTE_BLACKLIST.forEach(function (attrName) { - if (attrName === 'fill' && childEl.attr('fill') === 'none') { return; } - childEl.removeAttr(attrName); - }); - return true; }, - transformMaskRoot: function transformMaskRoot(_cellView, rootEl) { - var ref = this; - var MASK_ROOT_ATTRIBUTE_BLACKLIST = ref.MASK_ROOT_ATTRIBUTE_BLACKLIST; - MASK_ROOT_ATTRIBUTE_BLACKLIST.forEach(function (attrName) { - rootEl.removeAttr(attrName); - }); + removeHighlighters: function() { + HighlighterView.remove(this); }, - getMaskShape: function getMaskShape(cellView, vel) { - var this$1 = this; + updateHighlighters: function(dirty) { + if ( dirty === void 0 ) dirty = false; - var ref = this; - var options = ref.options; - var MASK_REPLACE_TAGS = ref.MASK_REPLACE_TAGS; - var deep = options.deep; - var tagName = vel.tagName(); - var maskRoot; - if (tagName === 'G') { - if (!deep) { return null; } - maskRoot = vel.clone(); - forEachDescendant(maskRoot, function (maskChild) { return this$1.transformMaskChild(cellView, maskChild); }); - } else { - if (MASK_REPLACE_TAGS.includes(tagName)) { return null; } - maskRoot = vel.clone(); - } - this.transformMaskRoot(cellView, maskRoot); - return maskRoot; + HighlighterView.update(this, null, dirty); }, - getMaskId: function getMaskId() { - return ("highlight-mask-" + (this.cid)); + transformHighlighters: function() { + HighlighterView.transform(this); }, - getMask: function getMask(cellView, vNode) { + // Interaction. The controller part. + // --------------------------------- - var ref = this; - var VISIBLE = ref.VISIBLE; - var INVISIBLE = ref.INVISIBLE; - var options = ref.options; - var padding = options.padding; - var attrs = options.attrs; + // Interaction is handled by the paper and delegated to the view in interest. + // `x` & `y` parameters passed to these functions represent the coordinates already snapped to the paper grid. + // If necessary, real coordinates can be obtained from the `evt` event object. - var strokeWidth = ('stroke-width' in attrs) ? attrs['stroke-width'] : 1; - var hasNodeFill = vNode.attr('fill') !== 'none'; - var magnetStrokeWidth = parseFloat(vNode.attr('stroke-width')); - if (isNaN(magnetStrokeWidth)) { magnetStrokeWidth = 1; } - // stroke of the invisible shape - var minStrokeWidth = magnetStrokeWidth + padding * 2; - // stroke of the visible shape - var maxStrokeWidth = minStrokeWidth + strokeWidth * 2; - var maskEl = this.getMaskShape(cellView, vNode); - if (!maskEl) { - var nodeBBox = cellView.getNodeBoundingRect(vNode.node); - // Make sure the rect is visible - nodeBBox.inflate(nodeBBox.width ? 0 : 0.5, nodeBBox.height ? 0 : 0.5); - maskEl = V('rect', nodeBBox.toJSON()); - } - maskEl.attr(attrs); - return V('mask', { - 'id': this.getMaskId() - }).append([ - maskEl.clone().attr({ - 'fill': hasNodeFill ? VISIBLE : 'none', - 'stroke': VISIBLE, - 'stroke-width': maxStrokeWidth - }), - maskEl.clone().attr({ - 'fill': hasNodeFill ? INVISIBLE : 'none', - 'stroke': INVISIBLE, - 'stroke-width': minStrokeWidth - }) - ]); + // These functions are supposed to be overriden by the views that inherit from `joint.dia.Cell`, + // i.e. `joint.dia.Element` and `joint.dia.Link`. + + pointerdblclick: function(evt, x, y) { + + this.notify('cell:pointerdblclick', evt, x, y); }, - removeMask: function removeMask(paper) { - var maskNode = paper.svg.getElementById(this.getMaskId()); - if (maskNode) { - paper.defs.removeChild(maskNode); - } + pointerclick: function(evt, x, y) { + + this.notify('cell:pointerclick', evt, x, y); }, - addMask: function addMask(paper, maskEl) { - paper.defs.appendChild(maskEl.node); + contextmenu: function(evt, x, y) { + + this.notify('cell:contextmenu', evt, x, y); }, - highlight: function highlight(cellView, node) { + pointerdown: function(evt, x, y) { + var ref = this; - var options = ref.options; - var vel = ref.vel; - var padding = options.padding; - var attrs = options.attrs; - var maskClip = options.maskClip; if ( maskClip === void 0 ) maskClip = MASK_CLIP; - var layer = options.layer; - var color = ('stroke' in attrs) ? attrs['stroke'] : '#000000'; - if (!layer && node === cellView.el) { - // If the highlighter is appended to the cellView - // and we measure the size of the cellView wrapping group - // it's necessary to remove the highlighter first - vel.remove(); + var model = ref.model; + var graph = model.graph; + if (graph) { + model.startBatch('pointer'); + this.eventData(evt, { graph: graph }); } - var highlighterBBox = cellView.getNodeBoundingRect(node).inflate(padding + maskClip); - var maskEl = this.getMask(cellView, V(node)); - this.addMask(cellView.paper, maskEl); - vel.attr(highlighterBBox.toJSON()); - vel.attr({ - 'transform': V.matrixToTransformString(cellView.getNodeMatrix(node)), - 'mask': ("url(#" + (maskEl.id) + ")"), - 'fill': color - }); + + this.notify('cell:pointerdown', evt, x, y); }, - unhighlight: function unhighlight(cellView) { - this.removeMask(cellView.paper); - } + pointermove: function(evt, x, y) { - }); + this.notify('cell:pointermove', evt, x, y); + }, - var opacity = HighlighterView.extend({ + pointerup: function(evt, x, y) { - UPDATABLE: false, - MOUNTABLE: false, + var ref = this.eventData(evt); + var graph = ref.graph; - opacityClassName: addClassNamePrefix('highlight-opacity'), + this.notify('cell:pointerup', evt, x, y); - highlight: function(_cellView, node) { - V(node).addClass(this.opacityClassName); + if (graph) { + // we don't want to trigger event on model as model doesn't + // need to be member of collection anymore (remove) + graph.stopBatch('pointer', { cell: this.model }); + } }, - unhighlight: function(_cellView, node) { - V(node).removeClass(this.opacityClassName); - } + mouseover: function(evt) { - }); + this.notify('cell:mouseover', evt); + }, - var className = addClassNamePrefix('highlighted'); + mouseout: function(evt) { - var addClass = HighlighterView.extend({ + this.notify('cell:mouseout', evt); + }, - UPDATABLE: false, - MOUNTABLE: false, + mouseenter: function(evt) { - options: { - className: className + this.notify('cell:mouseenter', evt); }, - highlight: function(_cellView, node) { - V(node).addClass(this.options.className); + mouseleave: function(evt) { + + this.notify('cell:mouseleave', evt); }, - unhighlight: function(_cellView, node) { - V(node).removeClass(this.options.className); - } + mousewheel: function(evt, x, y, delta) { - }, { - // Backwards Compatibility - className: className - }); + this.notify('cell:mousewheel', evt, x, y, delta); + }, + onevent: function(evt, eventName, x, y) { + this.notify(eventName, evt, x, y); + }, - var highlighters = ({ - stroke: stroke, - mask: mask, - opacity: opacity, - addClass: addClass - }); + onmagnet: function() { - function offsetPoint(p1, p2, offset) { - if (isPlainObject(offset)) { - var x = offset.x; - var y = offset.y; - if (isFinite(y)) { - var line = new Line(p2, p1); - var ref = line.parallel(y); - var start = ref.start; - var end = ref.end; - p2 = start; - p1 = end; - } - offset = x; - } - if (!isFinite(offset)) { return p1; } - var length = p1.distance(p2); - if (offset === 0 && length > 0) { return p1; } - return p1.move(p2, -Math.min(offset, length - 1)); - } + // noop + }, - function stroke$1(magnet) { + magnetpointerdblclick: function() { - var stroke = magnet.getAttribute('stroke-width'); - if (stroke === null) { return 0; } - return parseFloat(stroke) || 0; - } + // noop + }, - function alignLine(line, type, offset) { - if ( offset === void 0 ) offset = 0; + magnetcontextmenu: function() { - var coordinate, a, b, direction; - var start = line.start; - var end = line.end; - switch (type) { - case 'left': - coordinate = 'x'; - a = end; - b = start; - direction = -1; - break; - case 'right': - coordinate = 'x'; - a = start; - b = end; - direction = 1; - break; - case 'top': - coordinate = 'y'; - a = end; - b = start; - direction = -1; - break; - case 'bottom': - coordinate = 'y'; - a = start; - b = end; - direction = 1; - break; - default: - return; - } - if (start[coordinate] < end[coordinate]) { - a[coordinate] = b[coordinate]; - } else { - b[coordinate] = a[coordinate]; - } - if (isFinite(offset)) { - a[coordinate] += direction * offset; - b[coordinate] += direction * offset; + // noop + }, + + checkMouseleave: function checkMouseleave(evt) { + var ref = this; + var paper = ref.paper; + if (paper.isAsync()) { + // Do the updates of the current view synchronously now + paper.dumpView(this); + } + var target = this.getEventTarget(evt, { fromPoint: true }); + var view = paper.findView(target); + if (view === this) { return; } + // Leaving the current view + this.mouseleave(evt); + if (!view) { return; } + // Entering another view + view.mouseenter(evt); + }, + + setInteractivity: function(value) { + + this.options.interactive = value; } - } + }, { - // Connection Points + Highlighting: HighlightingTypes, - function anchorConnectionPoint(line, _view, _magnet, opt) { - var offset = opt.offset; - var alignOffset = opt.alignOffset; - var align = opt.align; - if (align) { alignLine(line, align, alignOffset); } - return offsetPoint(line.end, line.start, offset); - } + addPresentationAttributes: function(presentationAttributes) { + return merge({}, result(this.prototype, 'presentationAttributes'), presentationAttributes, function(a, b) { + if (!a || !b) { return; } + if (typeof a === 'string') { a = [a]; } + if (typeof b === 'string') { b = [b]; } + if (Array.isArray(a) && Array.isArray(b)) { return uniq(a.concat(b)); } + }); + } + }); - function bboxIntersection(line, view, magnet, opt) { + var Flags = { + UPDATE: 'UPDATE', + TRANSLATE: 'TRANSLATE', + TOOLS: 'TOOLS', + RESIZE: 'RESIZE', + PORTS: 'PORTS', + ROTATE: 'ROTATE', + RENDER: 'RENDER' + }; - var bbox = view.getNodeBBox(magnet); - if (opt.stroke) { bbox.inflate(stroke$1(magnet) / 2); } - var intersections = line.intersect(bbox); - var cp = (intersections) - ? line.start.chooseClosest(intersections) - : line.end; - return offsetPoint(cp, line.start, opt.offset); - } + // Element base view and controller. + // ------------------------------------------- - function rectangleIntersection(line, view, magnet, opt) { + var ElementView = CellView.extend({ - var angle = view.model.angle(); - if (angle === 0) { - return bboxIntersection(line, view, magnet, opt); - } + /** + * @abstract + */ + _removePorts: function() { + // implemented in ports.js + }, - var bboxWORotation = view.getNodeUnrotatedBBox(magnet); - if (opt.stroke) { bboxWORotation.inflate(stroke$1(magnet) / 2); } - var center = bboxWORotation.center(); - var lineWORotation = line.clone().rotate(center, angle); - var intersections = lineWORotation.setLength(1e6).intersect(bboxWORotation); - var cp = (intersections) - ? lineWORotation.start.chooseClosest(intersections).rotate(center, -angle) - : line.end; - return offsetPoint(cp, line.start, opt.offset); - } + /** + * + * @abstract + */ + _renderPorts: function() { + // implemented in ports.js + }, - function findShapeNode(magnet) { - if (!magnet) { return null; } - var node = magnet; - do { - var tagName = node.tagName; - if (typeof tagName !== 'string') { return null; } - tagName = tagName.toUpperCase(); - if (tagName === 'G') { - node = node.firstElementChild; - } else if (tagName === 'TITLE') { - node = node.nextElementSibling; - } else { break; } - } while (node); - return node; - } + className: function() { - var BNDR_SUBDIVISIONS = 'segmentSubdivisons'; - var BNDR_SHAPE_BBOX = 'shapeBBox'; + var classNames = CellView.prototype.className.apply(this).split(' '); - function boundaryIntersection(line, view, magnet, opt) { + classNames.push('element'); - var node, intersection; - var selector = opt.selector; - var anchor = line.end; + return classNames.join(' '); + }, - if (typeof selector === 'string') { - node = view.findBySelector(selector)[0]; - } else if (Array.isArray(selector)) { - node = getByPath(magnet, selector); - } else { - node = findShapeNode(magnet); - } + initialize: function() { - if (!V.isSVGGraphicsElement(node)) { - if (node === magnet || !V.isSVGGraphicsElement(magnet)) { return anchor; } - node = magnet; - } + CellView.prototype.initialize.apply(this, arguments); - var localShape = view.getNodeShape(node); - var magnetMatrix = view.getNodeMatrix(node); - var translateMatrix = view.getRootTranslateMatrix(); - var rotateMatrix = view.getRootRotateMatrix(); - var targetMatrix = translateMatrix.multiply(rotateMatrix).multiply(magnetMatrix); - var localMatrix = targetMatrix.inverse(); - var localLine = V.transformLine(line, localMatrix); - var localRef = localLine.start.clone(); - var data = view.getNodeData(node); + this._initializePorts(); + }, - if (opt.insideout === false) { - if (!data[BNDR_SHAPE_BBOX]) { data[BNDR_SHAPE_BBOX] = localShape.bbox(); } - var localBBox = data[BNDR_SHAPE_BBOX]; - if (localBBox.containsPoint(localRef)) { return anchor; } - } + presentationAttributes: { + 'attrs': [Flags.UPDATE], + 'position': [Flags.TRANSLATE, Flags.TOOLS], + 'size': [Flags.RESIZE, Flags.PORTS, Flags.TOOLS], + 'angle': [Flags.ROTATE, Flags.TOOLS], + 'markup': [Flags.RENDER], + 'ports': [Flags.PORTS], + }, - // Caching segment subdivisions for paths - var pathOpt; - if (localShape instanceof Path) { - var precision = opt.precision || 2; - if (!data[BNDR_SUBDIVISIONS]) { data[BNDR_SUBDIVISIONS] = localShape.getSegmentSubdivisions({ precision: precision }); } - pathOpt = { - precision: precision, - segmentSubdivisions: data[BNDR_SUBDIVISIONS] - }; - } + initFlag: [Flags.RENDER], - if (opt.extrapolate === true) { localLine.setLength(1e6); } + UPDATE_PRIORITY: 0, - intersection = localLine.intersect(localShape, pathOpt); - if (intersection) { - // More than one intersection - if (V.isArray(intersection)) { intersection = localRef.chooseClosest(intersection); } - } else if (opt.sticky === true) { - // No intersection, find the closest point instead - if (localShape instanceof Rect) { - intersection = localShape.pointNearestToPoint(localRef); - } else if (localShape instanceof Ellipse) { - intersection = localShape.intersectionWithLineFromCenterToPoint(localRef); - } else { - intersection = localShape.closestPoint(localRef, pathOpt); + confirmUpdate: function(flag, opt) { + + var useCSSSelectors = config.useCSSSelectors; + if (this.hasFlag(flag, Flags.PORTS)) { + this._removePorts(); + this._cleanPortsCache(); } - } + var transformHighlighters = false; + if (this.hasFlag(flag, Flags.RENDER)) { + this.render(); + this.updateTools(opt); + this.updateHighlighters(true); + transformHighlighters = true; + flag = this.removeFlag(flag, [Flags.RENDER, Flags.UPDATE, Flags.RESIZE, Flags.TRANSLATE, Flags.ROTATE, Flags.PORTS, Flags.TOOLS]); + } else { + var updateHighlighters = false; - var cp = (intersection) ? V.transformPoint(intersection, targetMatrix) : anchor; - var cpOffset = opt.offset || 0; - if (opt.stroke) { cpOffset += stroke$1(node) / 2; } + // Skip this branch if render is required + if (this.hasFlag(flag, Flags.RESIZE)) { + this.resize(opt); + updateHighlighters = true; + // Resize method is calling `update()` internally + flag = this.removeFlag(flag, [Flags.RESIZE, Flags.UPDATE]); + } + if (this.hasFlag(flag, Flags.UPDATE)) { + this.update(this.model, null, opt); + flag = this.removeFlag(flag, Flags.UPDATE); + updateHighlighters = true; + if (useCSSSelectors) { + // `update()` will render ports when useCSSSelectors are enabled + flag = this.removeFlag(flag, Flags.PORTS); + } + } + if (this.hasFlag(flag, Flags.TRANSLATE)) { + this.translate(); + flag = this.removeFlag(flag, Flags.TRANSLATE); + transformHighlighters = true; + } + if (this.hasFlag(flag, Flags.ROTATE)) { + this.rotate(); + flag = this.removeFlag(flag, Flags.ROTATE); + transformHighlighters = true; + } + if (this.hasFlag(flag, Flags.PORTS)) { + this._renderPorts(); + updateHighlighters = true; + flag = this.removeFlag(flag, Flags.PORTS); + } - return offsetPoint(cp, line.start, cpOffset); - } + if (updateHighlighters) { + this.updateHighlighters(false); + } + } - var anchor = anchorConnectionPoint; - var bbox = bboxIntersection; - var rectangle = rectangleIntersection; - var boundary = boundaryIntersection; + if (transformHighlighters) { + this.transformHighlighters(); + } - var connectionPoints = ({ - anchor: anchor, - bbox: bbox, - rectangle: rectangle, - boundary: boundary - }); + if (this.hasFlag(flag, Flags.TOOLS)) { + this.updateTools(opt); + flag = this.removeFlag(flag, Flags.TOOLS); + } - function abs2rel(value, max) { + return flag; + }, - if (max === 0) { return '0%'; } - return Math.round(value / max * 100) + '%'; - } + /** + * @abstract + */ + _initializePorts: function() { - function pin(relative) { + }, - return function(end, view, magnet, coords) { - var fn = (view.isNodeConnection(magnet)) ? pinnedLinkEnd : pinnedElementEnd; - return fn(relative, end, view, magnet, coords); - }; - } + update: function(_, renderingOnlyAttrs) { - function pinnedElementEnd(relative, end, view, magnet, coords) { + this.cleanNodesCache(); - var angle = view.model.angle(); - var bbox = view.getNodeUnrotatedBBox(magnet); - var origin = view.model.getBBox().center(); - coords.rotate(origin, angle); - var dx = coords.x - bbox.x; - var dy = coords.y - bbox.y; + // When CSS selector strings are used, make sure no rule matches port nodes. + var useCSSSelectors = config.useCSSSelectors; + if (useCSSSelectors) { this._removePorts(); } - if (relative) { - dx = abs2rel(dx, bbox.width); - dy = abs2rel(dy, bbox.height); - } + var model = this.model; + var modelAttrs = model.attr(); + this.updateDOMSubtreeAttributes(this.el, modelAttrs, { + rootBBox: new Rect(model.size()), + selectors: this.selectors, + scalableNode: this.scalableNode, + rotatableNode: this.rotatableNode, + // Use rendering only attributes if they differs from the model attributes + roAttributes: (renderingOnlyAttrs === modelAttrs) ? null : renderingOnlyAttrs + }); - end.anchor = { - name: 'topLeft', - args: { - dx: dx, - dy: dy, - rotate: true + if (useCSSSelectors) { + this._renderPorts(); } - }; + }, - return end; - } + rotatableSelector: 'rotatable', + scalableSelector: 'scalable', + scalableNode: null, + rotatableNode: null, - function pinnedLinkEnd(relative, end, view, _magnet, coords) { + // `prototype.markup` is rendered by default. Set the `markup` attribute on the model if the + // default markup is not desirable. + renderMarkup: function() { - var connection = view.getConnection(); - if (!connection) { return end; } - var length = connection.closestPointLength(coords); - if (relative) { - var totalLength = connection.length(); - end.anchor = { - name: 'connectionRatio', - args: { - ratio: length / totalLength - } - }; - } else { - end.anchor = { - name: 'connectionLength', - args: { - length: length - } - }; - } - return end; - } + var element = this.model; + var markup = element.get('markup') || element.markup; + if (!markup) { throw new Error('dia.ElementView: markup required'); } + if (Array.isArray(markup)) { return this.renderJSONMarkup(markup); } + if (typeof markup === 'string') { return this.renderStringMarkup(markup); } + throw new Error('dia.ElementView: invalid markup'); + }, - var useDefaults = noop; - var pinAbsolute = pin(false); - var pinRelative = pin(true); + renderJSONMarkup: function(markup) { - var index$2 = ({ - useDefaults: useDefaults, - pinAbsolute: pinAbsolute, - pinRelative: pinRelative - }); + var doc = this.parseDOMJSON(markup, this.el); + var selectors = this.selectors = doc.selectors; + this.rotatableNode = V(selectors[this.rotatableSelector]) || null; + this.scalableNode = V(selectors[this.scalableSelector]) || null; + // Fragment + this.vel.append(doc.fragment); + }, - // Does not make any changes to vertices. - // Returns the arguments that are passed to it, unchanged. - var normal$1 = function(vertices, opt, linkView) { + renderStringMarkup: function(markup) { - return vertices; - }; + var vel = this.vel; + vel.append(V(markup)); + // Cache transformation groups + this.rotatableNode = vel.findOne('.rotatable'); + this.scalableNode = vel.findOne('.scalable'); - // Routes the link always to/from a certain side - // - // Arguments: - // padding ... gap between the element and the first vertex. :: Default 40. - // side ... 'left' | 'right' | 'top' | 'bottom' :: Default 'bottom'. - // - var oneSide = function(vertices, opt, linkView) { + var selectors = this.selectors = {}; + selectors[this.selector] = this.el; + }, - var side = opt.side || 'bottom'; - var padding = normalizeSides(opt.padding || 40); + render: function() { - // LinkView contains cached source an target bboxes. - // Note that those are Geometry rectangle objects. - var sourceBBox = linkView.sourceBBox; - var targetBBox = linkView.targetBBox; - var sourcePoint = sourceBBox.center(); - var targetPoint = targetBBox.center(); + this.vel.empty(); + this.renderMarkup(); + if (this.scalableNode) { + // Double update is necessary for elements with the scalable group only + // Note the resize() triggers the other `update`. + this.update(); + } + this.resize(); + if (this.rotatableNode) { + // Translate transformation is applied on `this.el` while the rotation transformation + // on `this.rotatableNode` + this.rotate(); + this.translate(); + } else { + this.updateTransformation(); + } + if (!config.useCSSSelectors) { this._renderPorts(); } + return this; + }, - var coordinate, dimension, direction; + resize: function(opt) { - switch (side) { - case 'bottom': - direction = 1; - coordinate = 'y'; - dimension = 'height'; - break; - case 'top': - direction = -1; - coordinate = 'y'; - dimension = 'height'; - break; - case 'left': - direction = -1; - coordinate = 'x'; - dimension = 'width'; - break; - case 'right': - direction = 1; - coordinate = 'x'; - dimension = 'width'; - break; - default: - throw new Error('Router: invalid side'); - } + if (this.scalableNode) { return this.sgResize(opt); } + if (this.model.attributes.angle) { this.rotate(); } + this.update(); + }, - // move the points from the center of the element to outside of it. - sourcePoint[coordinate] += direction * (sourceBBox[dimension] / 2 + padding[side]); - targetPoint[coordinate] += direction * (targetBBox[dimension] / 2 + padding[side]); + translate: function() { - // make link orthogonal (at least the first and last vertex). - if ((direction * (sourcePoint[coordinate] - targetPoint[coordinate])) > 0) { - targetPoint[coordinate] = sourcePoint[coordinate]; - } else { - sourcePoint[coordinate] = targetPoint[coordinate]; - } + if (this.rotatableNode) { return this.rgTranslate(); } + this.updateTransformation(); + }, - return [sourcePoint].concat(vertices, targetPoint); - }; + rotate: function() { - // bearing -> opposite bearing - var opposites = { - N: 'S', - S: 'N', - E: 'W', - W: 'E' - }; + if (this.rotatableNode) { + this.rgRotate(); + // It's necessary to call the update for the nodes outside + // the rotatable group referencing nodes inside the group + this.update(); + return; + } + this.updateTransformation(); + }, - // bearing -> radians - var radians = { - N: -Math.PI / 2 * 3, - S: -Math.PI / 2, - E: 0, - W: Math.PI - }; + updateTransformation: function() { - // HELPERS // + var transformation = this.getTranslateString(); + var rotateString = this.getRotateString(); + if (rotateString) { transformation += ' ' + rotateString; } + this.vel.attr('transform', transformation); + }, - // returns a point `p` where lines p,p1 and p,p2 are perpendicular and p is not contained - // in the given box - function freeJoin(p1, p2, bbox) { + getTranslateString: function() { - var p = new Point(p1.x, p2.y); - if (bbox.containsPoint(p)) { p = new Point(p2.x, p1.y); } - // kept for reference - // if (bbox.containsPoint(p)) p = null; + var position = this.model.attributes.position; + return 'translate(' + position.x + ',' + position.y + ')'; + }, - return p; - } + getRotateString: function() { + var attributes = this.model.attributes; + var angle = attributes.angle; + if (!angle) { return null; } + var size = attributes.size; + return 'rotate(' + angle + ',' + (size.width / 2) + ',' + (size.height / 2) + ')'; + }, - // returns either width or height of a bbox based on the given bearing - function getBBoxSize(bbox, bearing) { + // Rotatable & Scalable Group + // always slower, kept mainly for backwards compatibility - return bbox[(bearing === 'W' || bearing === 'E') ? 'width' : 'height']; - } + rgRotate: function() { - // simple bearing method (calculates only orthogonal cardinals) - function getBearing(from, to) { + this.rotatableNode.attr('transform', this.getRotateString()); + }, - if (from.x === to.x) { return (from.y > to.y) ? 'N' : 'S'; } - if (from.y === to.y) { return (from.x > to.x) ? 'W' : 'E'; } - return null; - } + rgTranslate: function() { - // transform point to a rect - function getPointBox(p) { + this.vel.attr('transform', this.getTranslateString()); + }, - return new Rect(p.x, p.y, 0, 0); - } + sgResize: function(opt) { - function getPaddingBox(opt) { + var model = this.model; + var angle = model.angle(); + var size = model.size(); + var scalable = this.scalableNode; - // if both provided, opt.padding wins over opt.elementPadding - var sides = normalizeSides(opt.padding || opt.elementPadding || 20); + // Getting scalable group's bbox. + // Due to a bug in webkit's native SVG .getBBox implementation, the bbox of groups with path children includes the paths' control points. + // To work around the issue, we need to check whether there are any path elements inside the scalable group. + var recursive = false; + if (scalable.node.getElementsByTagName('path').length > 0) { + // If scalable has at least one descendant that is a path, we need to switch to recursive bbox calculation. + // If there are no path descendants, group bbox calculation works and so we can use the (faster) native function directly. + recursive = true; + } + var scalableBBox = scalable.getBBox({ recursive: recursive }); - return { - x: -sides.left, - y: -sides.top, - width: sides.left + sides.right, - height: sides.top + sides.bottom - }; - } + // Make sure `scalableBbox.width` and `scalableBbox.height` are not zero which can happen if the element does not have any content. By making + // the width/height 1, we prevent HTML errors of the type `scale(Infinity, Infinity)`. + var sx = (size.width / (scalableBBox.width || 1)); + var sy = (size.height / (scalableBBox.height || 1)); + scalable.attr('transform', 'scale(' + sx + ',' + sy + ')'); - // return source bbox - function getSourceBBox(linkView, opt) { + // Now the interesting part. The goal is to be able to store the object geometry via just `x`, `y`, `angle`, `width` and `height` + // Order of transformations is significant but we want to reconstruct the object always in the order: + // resize(), rotate(), translate() no matter of how the object was transformed. For that to work, + // we must adjust the `x` and `y` coordinates of the object whenever we resize it (because the origin of the + // rotation changes). The new `x` and `y` coordinates are computed by canceling the previous rotation + // around the center of the resized object (which is a different origin then the origin of the previous rotation) + // and getting the top-left corner of the resulting object. Then we clean up the rotation back to what it originally was. - return linkView.sourceBBox.clone().moveAndExpand(getPaddingBox(opt)); - } + // Cancel the rotation but now around a different origin, which is the center of the scaled object. + var rotatable = this.rotatableNode; + var rotation = rotatable && rotatable.attr('transform'); + if (rotation) { - // return target bbox - function getTargetBBox(linkView, opt) { + rotatable.attr('transform', rotation + ' rotate(' + (-angle) + ',' + (size.width / 2) + ',' + (size.height / 2) + ')'); + var rotatableBBox = scalable.getBBox({ target: this.paper.cells }); - return linkView.targetBBox.clone().moveAndExpand(getPaddingBox(opt)); - } + // Store new x, y and perform rotate() again against the new rotation origin. + model.set('position', { x: rotatableBBox.x, y: rotatableBBox.y }, assign({ updateHandled: true }, opt)); + this.translate(); + this.rotate(); + } - // return source anchor - function getSourceAnchor(linkView, opt) { + // Update must always be called on non-rotated element. Otherwise, relative positioning + // would work with wrong (rotated) bounding boxes. + this.update(); + }, - if (linkView.sourceAnchor) { return linkView.sourceAnchor; } + // Embedding mode methods. + // ----------------------- - // fallback: center of bbox - var sourceBBox = getSourceBBox(linkView, opt); - return sourceBBox.center(); - } + prepareEmbedding: function(data) { + if ( data === void 0 ) data = {}; - // return target anchor - function getTargetAnchor(linkView, opt) { - if (linkView.targetAnchor) { return linkView.targetAnchor; } + var element = data.model || this.model; + var paper = data.paper || this.paper; + var graph = paper.model; - // fallback: center of bbox - var targetBBox = getTargetBBox(linkView, opt); - return targetBBox.center(); // default - } + var initialZIndices = data.initialZIndices = {}; + var embeddedCells = element.getEmbeddedCells({ deep: true }); + var connectedLinks = graph.getConnectedLinks(element, { deep: true, includeEnclosed: true }); - // PARTIAL ROUTERS // + // Note: an embedded cell can be a connect link, but it's fine + // to iterate over the cell twice. + [ + element ].concat( embeddedCells, + connectedLinks + ).forEach(function (cell) { return initialZIndices[cell.id] = cell.attributes.z; }); - function vertexVertex(from, to, bearing) { + element.startBatch('to-front'); - var p1 = new Point(from.x, to.y); - var p2 = new Point(to.x, from.y); - var d1 = getBearing(from, p1); - var d2 = getBearing(from, p2); - var opposite = opposites[bearing]; + // Bring the model to the front with all his embeds. + element.toFront({ deep: true, ui: true }); - var p = (d1 === bearing || (d1 !== opposite && (d2 === opposite || d2 !== bearing))) ? p1 : p2; + // Note that at this point cells in the collection are not sorted by z index (it's running in the batch, see + // the dia.Graph._sortOnChangeZ), so we can't assume that the last cell in the collection has the highest z. + var maxZ = graph.getElements().reduce(function (max, cell) { return Math.max(max, cell.attributes.z || 0); }, 0); - return { points: [p], direction: getBearing(p, to) }; - } + // Move to front also all the inbound and outbound links that are connected + // to any of the element descendant. If we bring to front only embedded elements, + // links connected to them would stay in the background. + connectedLinks.forEach(function (link) { + if (link.attributes.z <= maxZ) { + link.set('z', maxZ + 1, { ui: true }); + } + }); - function elementVertex(from, to, fromBBox) { + element.stopBatch('to-front'); - var p = freeJoin(from, to, fromBBox); + // Before we start looking for suitable parent we remove the current one. + var parentId = element.parent(); + if (parentId) { + var parent = graph.getCell(parentId); + parent.unembed(element, { ui: true }); + data.initialParentId = parentId; + } else { + data.initialParentId = null; + } + }, - return { points: [p], direction: getBearing(p, to) }; - } + processEmbedding: function(data, evt, x, y) { + if ( data === void 0 ) data = {}; - function vertexElement(from, to, toBBox, bearing) { - var route = {}; + var model = data.model || this.model; + var paper = data.paper || this.paper; + var graph = paper.model; + var ref = paper.options; + var findParentBy = ref.findParentBy; + var frontParentOnly = ref.frontParentOnly; + var validateEmbedding = ref.validateEmbedding; - var points = [new Point(from.x, to.y), new Point(to.x, from.y)]; - var freePoints = points.filter(function(pt) { - return !toBBox.containsPoint(pt); - }); - var freeBearingPoints = freePoints.filter(function(pt) { - return getBearing(pt, from) !== bearing; - }); + var candidates; + if (isFunction(findParentBy)) { + candidates = toArray(findParentBy.call(graph, this, evt, x, y)); + } else if (findParentBy === 'pointer') { + candidates = toArray(graph.findModelsFromPoint({ x: x, y: y })); + } else { + candidates = graph.findModelsUnderElement(model, { searchBy: findParentBy }); + } - var p; + candidates = candidates.filter(function (el) { + return (el instanceof Cell) && (model.id !== el.id) && !el.isEmbeddedIn(model); + }); - if (freeBearingPoints.length > 0) { - // Try to pick a point which bears the same direction as the previous segment. + if (frontParentOnly) { + // pick the element with the highest `z` index + candidates = candidates.slice(-1); + } - p = freeBearingPoints.filter(function(pt) { - return getBearing(from, pt) === bearing; - }).pop(); - p = p || freeBearingPoints[0]; + var newCandidateView = null; + var prevCandidateView = data.candidateEmbedView; - route.points = [p]; - route.direction = getBearing(p, to); + // iterate over all candidates starting from the last one (has the highest z-index). + for (var i = candidates.length - 1; i >= 0; i--) { + var candidate = candidates[i]; + if (prevCandidateView && prevCandidateView.model.id == candidate.id) { + // candidate remains the same + newCandidateView = prevCandidateView; + break; + } else { + var view = candidate.findView(paper); + if (!isFunction(validateEmbedding) || validateEmbedding.call(paper, this, view)) { + // flip to the new candidate + newCandidateView = view; + break; + } + } + } - } else { - // Here we found only points which are either contained in the element or they would create - // a link segment going in opposite direction from the previous one. - // We take the point inside element and move it outside the element in the direction the - // route is going. Now we can join this point with the current end (using freeJoin). + if (newCandidateView && newCandidateView != prevCandidateView) { + // A new candidate view found. Highlight the new one. + this.clearEmbedding(data); + data.candidateEmbedView = newCandidateView.highlight( + newCandidateView.findProxyNode(null, 'container'), + { embedding: true } + ); + } - p = difference(points, freePoints)[0]; + if (!newCandidateView && prevCandidateView) { + // No candidate view found. Unhighlight the previous candidate. + this.clearEmbedding(data); + } + }, - var p2 = (new Point(to)).move(p, -getBBoxSize(toBBox, bearing) / 2); - var p1 = freeJoin(p2, from, toBBox); + clearEmbedding: function(data) { - route.points = [p1, p2]; - route.direction = getBearing(p2, to); - } + data || (data = {}); - return route; - } + var candidateView = data.candidateEmbedView; + if (candidateView) { + // No candidate view found. Unhighlight the previous candidate. + candidateView.unhighlight( + candidateView.findProxyNode(null, 'container'), + { embedding: true } + ); + data.candidateEmbedView = null; + } + }, - function elementElement(from, to, fromBBox, toBBox) { + finalizeEmbedding: function(data) { + if ( data === void 0 ) data = {}; - var route = elementVertex(to, from, toBBox); - var p1 = route.points[0]; - if (fromBBox.containsPoint(p1)) { + var candidateView = data.candidateEmbedView; + var element = data.model || this.model; + var paper = data.paper || this.paper; - route = elementVertex(from, to, fromBBox); - var p2 = route.points[0]; + if (candidateView) { - if (toBBox.containsPoint(p2)) { + // We finished embedding. Candidate view is chosen to become the parent of the model. + candidateView.model.embed(element, { ui: true }); + candidateView.unhighlight(candidateView.findProxyNode(null, 'container'), { embedding: true }); - var fromBorder = (new Point(from)).move(p2, -getBBoxSize(fromBBox, getBearing(from, p2)) / 2); - var toBorder = (new Point(to)).move(p1, -getBBoxSize(toBBox, getBearing(to, p1)) / 2); - var mid = (new Line(fromBorder, toBorder)).midpoint(); + data.candidateEmbedView = null; - var startRoute = elementVertex(from, mid, fromBBox); - var endRoute = vertexVertex(mid, to, startRoute.direction); + } else { - route.points = [startRoute.points[0], endRoute.points[0]]; - route.direction = endRoute.direction; + var ref = paper.options; + var validateUnembedding = ref.validateUnembedding; + var initialParentId = data.initialParentId; + // The element was originally embedded into another element. + // The interaction would unembed the element. Let's validate + // if the element can be unembedded. + if ( + initialParentId && + typeof validateUnembedding === 'function' && + !validateUnembedding.call(paper, this) + ) { + this._disallowUnembed(data); + return; + } } - } - - return route; - } - // Finds route for situations where one element is inside the other. - // Typically the route is directed outside the outer element first and - // then back towards the inner element. - function insideElement(from, to, fromBBox, toBBox, bearing) { - - var route = {}; - var boundary = fromBBox.union(toBBox).inflate(1); - - // start from the point which is closer to the boundary - var reversed = boundary.center().distance(to) > boundary.center().distance(from); - var start = reversed ? to : from; - var end = reversed ? from : to; + paper.model.getConnectedLinks(element, { deep: true }).forEach(function (link) { + link.reparent({ ui: true }); + }); + }, - var p1, p2, p3; + _disallowUnembed: function(data) { + var model = data.model; + var whenNotAllowed = data.whenNotAllowed; if ( whenNotAllowed === void 0 ) whenNotAllowed = 'revert'; + var element = model || this.model; + var paper = data.paper || this.paper; + var graph = paper.model; + switch (whenNotAllowed) { + case 'remove': { + element.remove({ ui: true }); + break; + } + case 'revert': { + var initialParentId = data.initialParentId; + var initialPosition = data.initialPosition; + var initialZIndices = data.initialZIndices; + // Revert the element's position (and the position of its embedded cells if any) + if (initialPosition) { + var x = initialPosition.x; + var y = initialPosition.y; + element.position(x, y, { deep: true, ui: true }); + } + // Revert all the z-indices changed during the embedding + if (initialZIndices) { + Object.keys(initialZIndices).forEach(function (id) { + var cell = graph.getCell(id); + if (cell) { + cell.set('z', initialZIndices[id], { ui: true }); + } + }); + } + // Revert the original parent + var parent = graph.getCell(initialParentId); + if (parent) { + parent.embed(element, { ui: true }); + } + break; + } + } + }, - if (bearing) { - // Points on circle with radius equals 'W + H` are always outside the rectangle - // with width W and height H if the center of that circle is the center of that rectangle. - p1 = Point.fromPolar(boundary.width + boundary.height, radians[bearing], start); - p1 = boundary.pointNearestToPoint(p1).move(p1, -1); + getDelegatedView: function() { - } else { - p1 = boundary.pointNearestToPoint(start).move(start, 1); - } + var view = this; + var model = view.model; + var paper = view.paper; - p2 = freeJoin(p1, end, boundary); + while (view) { + if (model.isLink()) { break; } + if (!model.isEmbedded() || view.can('stopDelegation')) { return view; } + model = model.getParentCell(); + view = paper.findViewByModel(model); + } - if (p1.round().equals(p2.round())) { - p2 = Point.fromPolar(boundary.width + boundary.height, toRad(p1.theta(start)) + Math.PI / 2, end); - p2 = boundary.pointNearestToPoint(p2).move(end, 1).round(); - p3 = freeJoin(p1, p2, boundary); - route.points = reversed ? [p2, p3, p1] : [p1, p3, p2]; + return null; + }, - } else { - route.points = reversed ? [p2, p1] : [p1, p2]; - } + findProxyNode: function(el, type) { + el || (el = this.el); + var nodeSelector = el.getAttribute((type + "-selector")); + if (nodeSelector) { + var port = this.findAttribute('port', el); + if (port) { + var proxyPortNode = this.findPortNode(port, nodeSelector); + if (proxyPortNode) { return proxyPortNode; } + } else { + var ref = this.findBySelector(nodeSelector); + var proxyNode = ref[0]; + if (proxyNode) { return proxyNode; } + } + } + return el; + }, - route.direction = reversed ? getBearing(p1, to) : getBearing(p2, to); + // Interaction. The controller part. + // --------------------------------- - return route; - } + notifyPointerdown: function notifyPointerdown(evt, x, y) { + CellView.prototype.pointerdown.call(this, evt, x, y); + this.notify('element:pointerdown', evt, x, y); + }, - // MAIN ROUTER // + notifyPointermove: function notifyPointermove(evt, x, y) { + CellView.prototype.pointermove.call(this, evt, x, y); + this.notify('element:pointermove', evt, x, y); + }, - // Return points through which a connection needs to be drawn in order to obtain an orthogonal link - // routing from source to target going through `vertices`. - function orthogonal(vertices, opt, linkView) { + notifyPointerup: function notifyPointerup(evt, x, y) { + this.notify('element:pointerup', evt, x, y); + CellView.prototype.pointerup.call(this, evt, x, y); + }, - var sourceBBox = getSourceBBox(linkView, opt); - var targetBBox = getTargetBBox(linkView, opt); + pointerdblclick: function(evt, x, y) { - var sourceAnchor = getSourceAnchor(linkView, opt); - var targetAnchor = getTargetAnchor(linkView, opt); + CellView.prototype.pointerdblclick.apply(this, arguments); + this.notify('element:pointerdblclick', evt, x, y); + }, - // if anchor lies outside of bbox, the bbox expands to include it - sourceBBox = sourceBBox.union(getPointBox(sourceAnchor)); - targetBBox = targetBBox.union(getPointBox(targetAnchor)); + pointerclick: function(evt, x, y) { - vertices = toArray(vertices).map(Point); - vertices.unshift(sourceAnchor); - vertices.push(targetAnchor); + CellView.prototype.pointerclick.apply(this, arguments); + this.notify('element:pointerclick', evt, x, y); + }, - var bearing; // bearing of previous route segment + contextmenu: function(evt, x, y) { - var orthogonalVertices = []; // the array of found orthogonal vertices to be returned - for (var i = 0, max = vertices.length - 1; i < max; i++) { + CellView.prototype.contextmenu.apply(this, arguments); + this.notify('element:contextmenu', evt, x, y); + }, - var route = null; + pointerdown: function(evt, x, y) { - var from = vertices[i]; - var to = vertices[i + 1]; + if (this.isPropagationStopped(evt)) { return; } - var isOrthogonal = !!getBearing(from, to); + this.notifyPointerdown(evt, x, y); + this.dragStart(evt, x, y); + }, - if (i === 0) { // source + pointermove: function(evt, x, y) { - if (i + 1 === max) { // route source -> target + var data = this.eventData(evt); - // Expand one of the elements by 1px to detect situations when the two - // elements are positioned next to each other with no gap in between. - if (sourceBBox.intersect(targetBBox.clone().inflate(1))) { - route = insideElement(from, to, sourceBBox, targetBBox); - - } else if (!isOrthogonal) { - route = elementElement(from, to, sourceBBox, targetBBox); - } - - } else { // route source -> vertex - - if (sourceBBox.containsPoint(to)) { - route = insideElement(from, to, sourceBBox, getPointBox(to).moveAndExpand(getPaddingBox(opt))); - - } else if (!isOrthogonal) { - route = elementVertex(from, to, sourceBBox); - } - } - - } else if (i + 1 === max) { // route vertex -> target - - // prevent overlaps with previous line segment - var isOrthogonalLoop = isOrthogonal && getBearing(to, from) === bearing; - - if (targetBBox.containsPoint(from) || isOrthogonalLoop) { - route = insideElement(from, to, getPointBox(from).moveAndExpand(getPaddingBox(opt)), targetBBox, bearing); - - } else if (!isOrthogonal) { - route = vertexElement(from, to, targetBBox, bearing); - } - - } else if (!isOrthogonal) { // route vertex -> vertex - route = vertexVertex(from, to, bearing); + switch (data.action) { + case 'magnet': + this.dragMagnet(evt, x, y); + break; + case 'move': + (data.delegatedView || this).drag(evt, x, y); + // eslint: no-fallthrough=false + default: + this.notifyPointermove(evt, x, y); + break; } - // applicable to all routes: - - // set bearing for next iteration - if (route) { - Array.prototype.push.apply(orthogonalVertices, route.points); - bearing = route.direction; + // Make sure the element view data is passed along. + // It could have been wiped out in the handlers above. + this.eventData(evt, data); + }, - } else { - // orthogonal route and not looped - bearing = getBearing(from, to); - } + pointerup: function(evt, x, y) { - // push `to` point to identified orthogonal vertices array - if (i + 1 < max) { - orthogonalVertices.push(to); + var data = this.eventData(evt); + switch (data.action) { + case 'magnet': + this.dragMagnetEnd(evt, x, y); + break; + case 'move': + (data.delegatedView || this).dragEnd(evt, x, y); + // eslint: no-fallthrough=false + default: + this.notifyPointerup(evt, x, y); } - } - return orthogonalVertices; - } + var magnet = data.targetMagnet; + if (magnet) { this.magnetpointerclick(evt, magnet, x, y); } - var config$1 = { + this.checkMouseleave(evt); + }, - // size of the step to find a route (the grid of the manhattan pathfinder) - step: 10, + mouseover: function(evt) { - // the number of route finding loops that cause the router to abort - // returns fallback route instead - maximumLoops: 2000, + CellView.prototype.mouseover.apply(this, arguments); + this.notify('element:mouseover', evt); + }, - // the number of decimal places to round floating point coordinates - precision: 1, + mouseout: function(evt) { - // maximum change of direction - maxAllowedDirectionChange: 90, + CellView.prototype.mouseout.apply(this, arguments); + this.notify('element:mouseout', evt); + }, - // should the router use perpendicular linkView option? - // does not connect anchor of element but rather a point close-by that is orthogonal - // this looks much better - perpendicular: true, + mouseenter: function(evt) { - // should the source and/or target not be considered as obstacles? - excludeEnds: [], // 'source', 'target' + CellView.prototype.mouseenter.apply(this, arguments); + this.notify('element:mouseenter', evt); + }, - // should certain types of elements not be considered as obstacles? - excludeTypes: ['basic.Text'], + mouseleave: function(evt) { - // possible starting directions from an element - startDirections: ['top', 'right', 'bottom', 'left'], + CellView.prototype.mouseleave.apply(this, arguments); + this.notify('element:mouseleave', evt); + }, - // possible ending directions to an element - endDirections: ['top', 'right', 'bottom', 'left'], + mousewheel: function(evt, x, y, delta) { - // specify the directions used above and what they mean - directionMap: { - top: { x: 0, y: -1 }, - right: { x: 1, y: 0 }, - bottom: { x: 0, y: 1 }, - left: { x: -1, y: 0 } + CellView.prototype.mousewheel.apply(this, arguments); + this.notify('element:mousewheel', evt, x, y, delta); }, - // cost of an orthogonal step - cost: function() { + onmagnet: function(evt, x, y) { - return this.step; + this.dragMagnetStart(evt, x, y); }, - // an array of directions to find next points on the route - // different from start/end directions - directions: function() { - - var step = this.step; - var cost = this.cost(); + magnetpointerdblclick: function(evt, magnet, x, y) { - return [ - { offsetX: step, offsetY: 0, cost: cost }, - { offsetX: -step, offsetY: 0, cost: cost }, - { offsetX: 0, offsetY: step, cost: cost }, - { offsetX: 0, offsetY: -step, cost: cost } - ]; + this.notify('element:magnet:pointerdblclick', evt, magnet, x, y); }, - // a penalty received for direction change - penalties: function() { + magnetcontextmenu: function(evt, magnet, x, y) { - return { - 0: 0, - 45: this.step / 2, - 90: this.step / 2 - }; + this.notify('element:magnet:contextmenu', evt, magnet, x, y); }, - // padding applied on the element bounding boxes - paddingBox: function() { + // Drag Start Handlers - var step = this.step; + dragStart: function(evt, x, y) { - return { - x: -step, - y: -step, - width: 2 * step, - height: 2 * step - }; - }, + var view = this.getDelegatedView(); + if (!view || !view.can('elementMove')) { return; } - // A function that determines whether a given point is an obstacle or not. - // If used, the `padding`, `excludeEnds`and `excludeTypes` options are ignored. - // (point: dia.Point) => boolean; - isPointObstacle: null, + this.eventData(evt, { + action: 'move', + delegatedView: view + }); - // a router to use when the manhattan router fails - // (one of the partial routes returns null) - fallbackRouter: function(vertices, opt, linkView) { + var position = view.model.position(); + view.eventData(evt, { + initialPosition: position, + pointerOffset: position.difference(x, y), + restrictedArea: this.paper.getRestrictedArea(view, x, y) + }); + }, - if (!isFunction(orthogonal)) { - throw new Error('Manhattan requires the orthogonal router as default fallback.'); - } + dragMagnetStart: function(evt, x, y) { - return orthogonal(vertices, assign({}, config$1, opt), linkView); - }, + if (!this.can('addLinkFromMagnet')) { return; } - /* Deprecated */ - // a simple route used in situations when main routing method fails - // (exceed max number of loop iterations, inaccessible) - fallbackRoute: function(from, to, opt) { + var magnet = evt.currentTarget; + var paper = this.paper; + this.eventData(evt, { targetMagnet: magnet }); + evt.stopPropagation(); - return null; // null result will trigger the fallbackRouter + if (paper.options.validateMagnet(this, magnet, evt)) { - // left for reference: - /*// Find an orthogonal route ignoring obstacles. + if (paper.options.magnetThreshold <= 0) { + this.dragLinkStart(evt, magnet, x, y); + } - var point = ((opt.previousDirAngle || 0) % 180 === 0) - ? new g.Point(from.x, to.y) - : new g.Point(to.x, from.y); + this.eventData(evt, { action: 'magnet' }); + this.stopPropagation(evt); - return [point];*/ - }, + } else { - // if a function is provided, it's used to route the link while dragging an end - // i.e. function(from, to, opt) { return []; } - draggingRoute: null - }; + this.pointerdown(evt, x, y); + } - // HELPER CLASSES // + paper.delegateDragEvents(this, evt.data); + }, - // Map of obstacles - // Helper structure to identify whether a point lies inside an obstacle. - function ObstacleMap(opt) { + // Drag Handlers - this.map = {}; - this.options = opt; - // tells how to divide the paper when creating the elements map - this.mapGridSize = 100; - } + drag: function(evt, x, y) { - ObstacleMap.prototype.build = function(graph, link) { + var paper = this.paper; + var grid = paper.options.gridSize; + var element = this.model; + var data = this.eventData(evt); + var pointerOffset = data.pointerOffset; + var restrictedArea = data.restrictedArea; + var embedding = data.embedding; - var opt = this.options; + // Make sure the new element's position always snaps to the current grid + var elX = snapToGrid(x + pointerOffset.x, grid); + var elY = snapToGrid(y + pointerOffset.y, grid); - // source or target element could be excluded from set of obstacles - var excludedEnds = toArray(opt.excludeEnds).reduce(function(res, item) { + element.position(elX, elY, { restrictedArea: restrictedArea, deep: true, ui: true }); - var end = link.get(item); - if (end) { - var cell = graph.getCell(end.id); - if (cell) { - res.push(cell); + if (paper.options.embeddingMode) { + if (!embedding) { + // Prepare the element for embedding only if the pointer moves. + // We don't want to do unnecessary action with the element + // if an user only clicks/dblclicks on it. + this.prepareEmbedding(data); + embedding = true; } + this.processEmbedding(data, evt, x, y); } - return res; - }, []); + this.eventData(evt, { + embedding: embedding + }); + }, - // Exclude any embedded elements from the source and the target element. - var excludedAncestors = []; + dragMagnet: function(evt, x, y) { + this.dragLink(evt, x, y); + }, - var source = graph.getCell(link.get('source').id); - if (source) { - excludedAncestors = union(excludedAncestors, source.getAncestors().map(function(cell) { - return cell.id; - })); - } + // Drag End Handlers - var target = graph.getCell(link.get('target').id); - if (target) { - excludedAncestors = union(excludedAncestors, target.getAncestors().map(function(cell) { - return cell.id; - })); - } + dragEnd: function(evt, x, y) { - // Builds a map of all elements for quicker obstacle queries (i.e. is a point contained - // in any obstacle?) (a simplified grid search). - // The paper is divided into smaller cells, where each holds information about which - // elements belong to it. When we query whether a point lies inside an obstacle we - // don't need to go through all obstacles, we check only those in a particular cell. - var mapGridSize = this.mapGridSize; + var data = this.eventData(evt); + if (data.embedding) { this.finalizeEmbedding(data); } + }, - graph.getElements().reduce(function(map, element) { + dragMagnetEnd: function(evt, x, y) { + this.dragLinkEnd(evt, x, y); + }, - var isExcludedType = toArray(opt.excludeTypes).includes(element.get('type')); - var isExcludedEnd = excludedEnds.find(function(excluded) { - return excluded.id === element.id; - }); - var isExcludedAncestor = excludedAncestors.includes(element.id); + magnetpointerclick: function(evt, magnet, x, y) { + var paper = this.paper; + if (paper.eventData(evt).mousemoved > paper.options.clickThreshold) { return; } + this.notify('element:magnet:pointerclick', evt, magnet, x, y); + } - var isExcluded = isExcludedType || isExcludedEnd || isExcludedAncestor; - if (!isExcluded) { - var bbox = element.getBBox().moveAndExpand(opt.paddingBox); + }, { - var origin = bbox.origin().snapToGrid(mapGridSize); - var corner = bbox.corner().snapToGrid(mapGridSize); + Flags: Flags, + }); - for (var x = origin.x; x <= corner.x; x += mapGridSize) { - for (var y = origin.y; y <= corner.y; y += mapGridSize) { - var gridKey = x + '@' + y; - map[gridKey] = map[gridKey] || []; - map[gridKey].push(bbox); - } - } - } + assign(ElementView.prototype, elementViewPortPrototype); - return map; - }, this.map); + // Does not make any changes to vertices. + // Returns the arguments that are passed to it, unchanged. + var normal = function(vertices, opt, linkView) { - return this; + return vertices; }; - ObstacleMap.prototype.isPointAccessible = function(point) { + // Routes the link always to/from a certain side + // + // Arguments: + // padding ... gap between the element and the first vertex. :: Default 40. + // side ... 'left' | 'right' | 'top' | 'bottom' :: Default 'bottom'. + // + var oneSide = function(vertices, opt, linkView) { - var mapKey = point.clone().snapToGrid(this.mapGridSize).toString(); + var side = opt.side || 'bottom'; + var padding = normalizeSides(opt.padding || 40); - return toArray(this.map[mapKey]).every(function(obstacle) { - return !obstacle.containsPoint(point); - }); - }; + // LinkView contains cached source an target bboxes. + // Note that those are Geometry rectangle objects. + var sourceBBox = linkView.sourceBBox; + var targetBBox = linkView.targetBBox; + var sourcePoint = sourceBBox.center(); + var targetPoint = targetBBox.center(); - // Sorted Set - // Set of items sorted by given value. - function SortedSet() { - this.items = []; - this.hash = {}; - this.values = {}; - this.OPEN = 1; - this.CLOSE = 2; - } + var coordinate, dimension, direction; - SortedSet.prototype.add = function(item, value) { + switch (side) { + case 'bottom': + direction = 1; + coordinate = 'y'; + dimension = 'height'; + break; + case 'top': + direction = -1; + coordinate = 'y'; + dimension = 'height'; + break; + case 'left': + direction = -1; + coordinate = 'x'; + dimension = 'width'; + break; + case 'right': + direction = 1; + coordinate = 'x'; + dimension = 'width'; + break; + default: + throw new Error('Router: invalid side'); + } - if (this.hash[item]) { - // item removal - this.items.splice(this.items.indexOf(item), 1); + // move the points from the center of the element to outside of it. + sourcePoint[coordinate] += direction * (sourceBBox[dimension] / 2 + padding[side]); + targetPoint[coordinate] += direction * (targetBBox[dimension] / 2 + padding[side]); + + // make link orthogonal (at least the first and last vertex). + if ((direction * (sourcePoint[coordinate] - targetPoint[coordinate])) > 0) { + targetPoint[coordinate] = sourcePoint[coordinate]; } else { - this.hash[item] = this.OPEN; + sourcePoint[coordinate] = targetPoint[coordinate]; } - this.values[item] = value; + return [sourcePoint].concat(vertices, targetPoint); + }; - var index$1 = sortedIndex(this.items, item, function(i) { - return this.values[i]; - }.bind(this)); + // bearing -> opposite bearing + var opposites = { + N: 'S', + S: 'N', + E: 'W', + W: 'E' + }; - this.items.splice(index$1, 0, item); + // bearing -> radians + var radians = { + N: -Math.PI / 2 * 3, + S: -Math.PI / 2, + E: 0, + W: Math.PI }; - SortedSet.prototype.remove = function(item) { + // HELPERS // - this.hash[item] = this.CLOSE; - }; + // returns a point `p` where lines p,p1 and p,p2 are perpendicular and p is not contained + // in the given box + function freeJoin(p1, p2, bbox) { - SortedSet.prototype.isOpen = function(item) { + var p = new Point(p1.x, p2.y); + if (bbox.containsPoint(p)) { p = new Point(p2.x, p1.y); } + // kept for reference + // if (bbox.containsPoint(p)) p = null; - return this.hash[item] === this.OPEN; - }; + return p; + } - SortedSet.prototype.isClose = function(item) { + // returns either width or height of a bbox based on the given bearing + function getBBoxSize(bbox, bearing) { - return this.hash[item] === this.CLOSE; - }; + return bbox[(bearing === 'W' || bearing === 'E') ? 'width' : 'height']; + } - SortedSet.prototype.isEmpty = function() { + // simple bearing method (calculates only orthogonal cardinals) + function getBearing(from, to) { - return this.items.length === 0; - }; + if (from.x === to.x) { return (from.y > to.y) ? 'N' : 'S'; } + if (from.y === to.y) { return (from.x > to.x) ? 'W' : 'E'; } + return null; + } - SortedSet.prototype.pop = function() { + // transform point to a rect + function getPointBox(p) { - var item = this.items.shift(); - this.remove(item); - return item; - }; + return new Rect(p.x, p.y, 0, 0); + } - // HELPERS // + function getPaddingBox(opt) { - // return source bbox - function getSourceBBox$1(linkView, opt) { + // if both provided, opt.padding wins over opt.elementPadding + var sides = normalizeSides(opt.padding || opt.elementPadding || 20); - // expand by padding box - if (opt && opt.paddingBox) { return linkView.sourceBBox.clone().moveAndExpand(opt.paddingBox); } + return { + x: -sides.left, + y: -sides.top, + width: sides.left + sides.right, + height: sides.top + sides.bottom + }; + } - return linkView.sourceBBox.clone(); + // return source bbox + function getSourceBBox(linkView, opt) { + + return linkView.sourceBBox.clone().moveAndExpand(getPaddingBox(opt)); } // return target bbox - function getTargetBBox$1(linkView, opt) { - - // expand by padding box - if (opt && opt.paddingBox) { return linkView.targetBBox.clone().moveAndExpand(opt.paddingBox); } + function getTargetBBox(linkView, opt) { - return linkView.targetBBox.clone(); + return linkView.targetBBox.clone().moveAndExpand(getPaddingBox(opt)); } // return source anchor - function getSourceAnchor$1(linkView, opt) { + function getSourceAnchor(linkView, opt) { if (linkView.sourceAnchor) { return linkView.sourceAnchor; } // fallback: center of bbox - var sourceBBox = getSourceBBox$1(linkView, opt); + var sourceBBox = getSourceBBox(linkView, opt); return sourceBBox.center(); } // return target anchor - function getTargetAnchor$1(linkView, opt) { + function getTargetAnchor(linkView, opt) { if (linkView.targetAnchor) { return linkView.targetAnchor; } // fallback: center of bbox - var targetBBox = getTargetBBox$1(linkView, opt); + var targetBBox = getTargetBBox(linkView, opt); return targetBBox.center(); // default } - // returns a direction index from start point to end point - // corrects for grid deformation between start and end - function getDirectionAngle(start, end, numDirections, grid, opt) { + // PARTIAL ROUTERS // - var quadrant = 360 / numDirections; - var angleTheta = start.theta(fixAngleEnd(start, end, grid, opt)); - var normalizedAngle = normalizeAngle(angleTheta + (quadrant / 2)); - return quadrant * Math.floor(normalizedAngle / quadrant); - } + function vertexVertex(from, to, bearing) { - // helper function for getDirectionAngle() - // corrects for grid deformation - // (if a point is one grid steps away from another in both dimensions, - // it is considered to be 45 degrees away, even if the real angle is different) - // this causes visible angle discrepancies if `opt.step` is much larger than `paper.gridSize` - function fixAngleEnd(start, end, grid, opt) { + var p1 = new Point(from.x, to.y); + var p2 = new Point(to.x, from.y); + var d1 = getBearing(from, p1); + var d2 = getBearing(from, p2); + var opposite = opposites[bearing]; - var step = opt.step; + var p = (d1 === bearing || (d1 !== opposite && (d2 === opposite || d2 !== bearing))) ? p1 : p2; - var diffX = end.x - start.x; - var diffY = end.y - start.y; + return { points: [p], direction: getBearing(p, to) }; + } - var gridStepsX = diffX / grid.x; - var gridStepsY = diffY / grid.y; + function elementVertex(from, to, fromBBox) { - var distanceX = gridStepsX * step; - var distanceY = gridStepsY * step; + var p = freeJoin(from, to, fromBBox); - return new Point(start.x + distanceX, start.y + distanceY); + return { points: [p], direction: getBearing(p, to) }; } - // return the change in direction between two direction angles - function getDirectionChange(angle1, angle2) { + function vertexElement(from, to, toBBox, bearing) { - var directionChange = Math.abs(angle1 - angle2); - return (directionChange > 180) ? (360 - directionChange) : directionChange; - } + var route = {}; - // fix direction offsets according to current grid - function getGridOffsets(directions, grid, opt) { + var points = [new Point(from.x, to.y), new Point(to.x, from.y)]; + var freePoints = points.filter(function(pt) { + return !toBBox.containsPoint(pt); + }); + var freeBearingPoints = freePoints.filter(function(pt) { + return getBearing(pt, from) !== bearing; + }); - var step = opt.step; + var p; - toArray(opt.directions).forEach(function(direction) { + if (freeBearingPoints.length > 0) { + // Try to pick a point which bears the same direction as the previous segment. - direction.gridOffsetX = (direction.offsetX / step) * grid.x; - direction.gridOffsetY = (direction.offsetY / step) * grid.y; - }); - } + p = freeBearingPoints.filter(function(pt) { + return getBearing(from, pt) === bearing; + }).pop(); + p = p || freeBearingPoints[0]; - // get grid size in x and y dimensions, adapted to source and target positions - function getGrid(step, source, target) { + route.points = [p]; + route.direction = getBearing(p, to); - return { - source: source.clone(), - x: getGridDimension(target.x - source.x, step), - y: getGridDimension(target.y - source.y, step) - }; - } + } else { + // Here we found only points which are either contained in the element or they would create + // a link segment going in opposite direction from the previous one. + // We take the point inside element and move it outside the element in the direction the + // route is going. Now we can join this point with the current end (using freeJoin). - // helper function for getGrid() - function getGridDimension(diff, step) { + p = difference(points, freePoints)[0]; - // return step if diff = 0 - if (!diff) { return step; } + var p2 = (new Point(to)).move(p, -getBBoxSize(toBBox, bearing) / 2); + var p1 = freeJoin(p2, from, toBBox); - var absDiff = Math.abs(diff); - var numSteps = Math.round(absDiff / step); + route.points = [p1, p2]; + route.direction = getBearing(p2, to); + } - // return absDiff if less than one step apart - if (!numSteps) { return absDiff; } + return route; + } - // otherwise, return corrected step - var roundedDiff = numSteps * step; - var remainder = absDiff - roundedDiff; - var stepCorrection = remainder / numSteps; + function elementElement(from, to, fromBBox, toBBox) { - return step + stepCorrection; - } + var route = elementVertex(to, from, toBBox); + var p1 = route.points[0]; - // return a clone of point snapped to grid - function snapToGrid$1(point, grid) { + if (fromBBox.containsPoint(p1)) { - var source = grid.source; + route = elementVertex(from, to, fromBBox); + var p2 = route.points[0]; - var snappedX = snapToGrid(point.x - source.x, grid.x) + source.x; - var snappedY = snapToGrid(point.y - source.y, grid.y) + source.y; + if (toBBox.containsPoint(p2)) { - return new Point(snappedX, snappedY); - } + var fromBorder = (new Point(from)).move(p2, -getBBoxSize(fromBBox, getBearing(from, p2)) / 2); + var toBorder = (new Point(to)).move(p1, -getBBoxSize(toBBox, getBearing(to, p1)) / 2); + var mid = (new Line(fromBorder, toBorder)).midpoint(); - // round the point to opt.precision - function round$4(point, precision) { + var startRoute = elementVertex(from, mid, fromBBox); + var endRoute = vertexVertex(mid, to, startRoute.direction); - return point.round(precision); + route.points = [startRoute.points[0], endRoute.points[0]]; + route.direction = endRoute.direction; + } + } + + return route; } - // snap to grid and then round the point - function align(point, grid, precision) { + // Finds route for situations where one element is inside the other. + // Typically the route is directed outside the outer element first and + // then back towards the inner element. + function insideElement(from, to, fromBBox, toBBox, bearing) { - return round$4(snapToGrid$1(point.clone(), grid), precision); - } + var route = {}; + var boundary = fromBBox.union(toBBox).inflate(1); - // return a string representing the point - // string is rounded in both dimensions - function getKey(point) { + // start from the point which is closer to the boundary + var reversed = boundary.center().distance(to) > boundary.center().distance(from); + var start = reversed ? to : from; + var end = reversed ? from : to; - return point.clone().toString(); - } + var p1, p2, p3; - // return a normalized vector from given point - // used to determine the direction of a difference of two points - function normalizePoint(point) { + if (bearing) { + // Points on circle with radius equals 'W + H` are always outside the rectangle + // with width W and height H if the center of that circle is the center of that rectangle. + p1 = Point.fromPolar(boundary.width + boundary.height, radians[bearing], start); + p1 = boundary.pointNearestToPoint(p1).move(p1, -1); - return new Point( - point.x === 0 ? 0 : Math.abs(point.x) / point.x, - point.y === 0 ? 0 : Math.abs(point.y) / point.y - ); - } + } else { + p1 = boundary.pointNearestToPoint(start).move(start, 1); + } - // PATHFINDING // + p2 = freeJoin(p1, end, boundary); - // reconstructs a route by concatenating points with their parents - function reconstructRoute(parents, points, tailPoint, from, to, grid, opt) { + if (p1.round().equals(p2.round())) { + p2 = Point.fromPolar(boundary.width + boundary.height, toRad(p1.theta(start)) + Math.PI / 2, end); + p2 = boundary.pointNearestToPoint(p2).move(end, 1).round(); + p3 = freeJoin(p1, p2, boundary); + route.points = reversed ? [p2, p3, p1] : [p1, p3, p2]; - var route = []; + } else { + route.points = reversed ? [p2, p1] : [p1, p2]; + } - var prevDiff = normalizePoint(to.difference(tailPoint)); + route.direction = reversed ? getBearing(p1, to) : getBearing(p2, to); - // tailPoint is assumed to be aligned already - var currentKey = getKey(tailPoint); - var parent = parents[currentKey]; + return route; + } - var point; - while (parent) { + // MAIN ROUTER // - // point is assumed to be aligned already - point = points[currentKey]; + // Return points through which a connection needs to be drawn in order to obtain an orthogonal link + // routing from source to target going through `vertices`. + function orthogonal(vertices, opt, linkView) { - var diff = normalizePoint(point.difference(parent)); - if (!diff.equals(prevDiff)) { - route.unshift(point); - prevDiff = diff; - } + var sourceBBox = getSourceBBox(linkView, opt); + var targetBBox = getTargetBBox(linkView, opt); - // parent is assumed to be aligned already - currentKey = getKey(parent); - parent = parents[currentKey]; - } + var sourceAnchor = getSourceAnchor(linkView, opt); + var targetAnchor = getTargetAnchor(linkView, opt); - // leadPoint is assumed to be aligned already - var leadPoint = points[currentKey]; + // if anchor lies outside of bbox, the bbox expands to include it + sourceBBox = sourceBBox.union(getPointBox(sourceAnchor)); + targetBBox = targetBBox.union(getPointBox(targetAnchor)); - var fromDiff = normalizePoint(leadPoint.difference(from)); - if (!fromDiff.equals(prevDiff)) { - route.unshift(leadPoint); - } + vertices = toArray(vertices).map(Point); + vertices.unshift(sourceAnchor); + vertices.push(targetAnchor); - return route; - } + var bearing; // bearing of previous route segment - // heuristic method to determine the distance between two points - function estimateCost(from, endPoints) { + var orthogonalVertices = []; // the array of found orthogonal vertices to be returned + for (var i = 0, max = vertices.length - 1; i < max; i++) { - var min = Infinity; + var route = null; - for (var i = 0, len = endPoints.length; i < len; i++) { - var cost = from.manhattanDistance(endPoints[i]); - if (cost < min) { min = cost; } - } + var from = vertices[i]; + var to = vertices[i + 1]; - return min; - } + var isOrthogonal = !!getBearing(from, to); - // find points around the bbox taking given directions into account - // lines are drawn from anchor in given directions, intersections recorded - // if anchor is outside bbox, only those directions that intersect get a rect point - // the anchor itself is returned as rect point (representing some directions) - // (since those directions are unobstructed by the bbox) - function getRectPoints(anchor, bbox, directionList, grid, opt) { + if (i === 0) { // source - var precision = opt.precision; - var directionMap = opt.directionMap; + if (i + 1 === max) { // route source -> target - var anchorCenterVector = anchor.difference(bbox.center()); + // Expand one of the elements by 1px to detect situations when the two + // elements are positioned next to each other with no gap in between. + if (sourceBBox.intersect(targetBBox.clone().inflate(1))) { + route = insideElement(from, to, sourceBBox, targetBBox); - var keys = isObject$1(directionMap) ? Object.keys(directionMap) : []; - var dirList = toArray(directionList); - var rectPoints = keys.reduce(function(res, key) { + } else if (!isOrthogonal) { + route = elementElement(from, to, sourceBBox, targetBBox); + } - if (dirList.includes(key)) { - var direction = directionMap[key]; + } else { // route source -> vertex - // create a line that is guaranteed to intersect the bbox if bbox is in the direction - // even if anchor lies outside of bbox - var endpoint = new Point( - anchor.x + direction.x * (Math.abs(anchorCenterVector.x) + bbox.width), - anchor.y + direction.y * (Math.abs(anchorCenterVector.y) + bbox.height) - ); - var intersectionLine = new Line(anchor, endpoint); + if (sourceBBox.containsPoint(to)) { + route = insideElement(from, to, sourceBBox, getPointBox(to).moveAndExpand(getPaddingBox(opt))); - // get the farther intersection, in case there are two - // (that happens if anchor lies next to bbox) - var intersections = intersectionLine.intersect(bbox) || []; - var numIntersections = intersections.length; - var farthestIntersectionDistance; - var farthestIntersection = null; - for (var i = 0; i < numIntersections; i++) { - var currentIntersection = intersections[i]; - var distance = anchor.squaredDistance(currentIntersection); - if ((farthestIntersectionDistance === undefined) || (distance > farthestIntersectionDistance)) { - farthestIntersectionDistance = distance; - farthestIntersection = currentIntersection; + } else if (!isOrthogonal) { + route = elementVertex(from, to, sourceBBox); } } - // if an intersection was found in this direction, it is our rectPoint - if (farthestIntersection) { - var point = align(farthestIntersection, grid, precision); + } else if (i + 1 === max) { // route vertex -> target - // if the rectPoint lies inside the bbox, offset it by one more step - if (bbox.containsPoint(point)) { - point = align(point.offset(direction.x * grid.x, direction.y * grid.y), grid, precision); - } + // prevent overlaps with previous line segment + var isOrthogonalLoop = isOrthogonal && getBearing(to, from) === bearing; - // then add the point to the result array - // aligned - res.push(point); + if (targetBBox.containsPoint(from) || isOrthogonalLoop) { + route = insideElement(from, to, getPointBox(from).moveAndExpand(getPaddingBox(opt)), targetBBox, bearing); + + } else if (!isOrthogonal) { + route = vertexElement(from, to, targetBBox, bearing); } + + } else if (!isOrthogonal) { // route vertex -> vertex + route = vertexVertex(from, to, bearing); } - return res; - }, []); + // applicable to all routes: - // if anchor lies outside of bbox, add it to the array of points - if (!bbox.containsPoint(anchor)) { - // aligned - rectPoints.push(align(anchor, grid, precision)); + // set bearing for next iteration + if (route) { + Array.prototype.push.apply(orthogonalVertices, route.points); + bearing = route.direction; + + } else { + // orthogonal route and not looped + bearing = getBearing(from, to); + } + + // push `to` point to identified orthogonal vertices array + if (i + 1 < max) { + orthogonalVertices.push(to); + } } - return rectPoints; + return orthogonalVertices; } - // finds the route between two points/rectangles (`from`, `to`) implementing A* algorithm - // rectangles get rect points assigned by getRectPoints() - function findRoute(from, to, isPointObstacle, opt) { + var config$1 = { - var precision = opt.precision; + // size of the step to find a route (the grid of the manhattan pathfinder) + step: 10, - // Get grid for this route. + // the number of route finding loops that cause the router to abort + // returns fallback route instead + maximumLoops: 2000, - var sourceAnchor, targetAnchor; + // the number of decimal places to round floating point coordinates + precision: 1, - if (from instanceof Rect) { // `from` is sourceBBox - sourceAnchor = round$4(getSourceAnchor$1(this, opt).clone(), precision); - } else { - sourceAnchor = round$4(from.clone(), precision); - } + // maximum change of direction + maxAllowedDirectionChange: 90, - if (to instanceof Rect) { // `to` is targetBBox - targetAnchor = round$4(getTargetAnchor$1(this, opt).clone(), precision); - } else { - targetAnchor = round$4(to.clone(), precision); - } + // should the router use perpendicular linkView option? + // does not connect anchor of element but rather a point close-by that is orthogonal + // this looks much better + perpendicular: true, - var grid = getGrid(opt.step, sourceAnchor, targetAnchor); + // should the source and/or target not be considered as obstacles? + excludeEnds: [], // 'source', 'target' - // Get pathfinding points. + // should certain types of elements not be considered as obstacles? + excludeTypes: ['basic.Text'], - var start, end; // aligned with grid by definition - var startPoints, endPoints; // assumed to be aligned with grid already + // possible starting directions from an element + startDirections: ['top', 'right', 'bottom', 'left'], - // set of points we start pathfinding from - if (from instanceof Rect) { // `from` is sourceBBox - start = sourceAnchor; - startPoints = getRectPoints(start, from, opt.startDirections, grid, opt); + // possible ending directions to an element + endDirections: ['top', 'right', 'bottom', 'left'], - } else { - start = sourceAnchor; - startPoints = [start]; - } + // specify the directions used above and what they mean + directionMap: { + top: { x: 0, y: -1 }, + right: { x: 1, y: 0 }, + bottom: { x: 0, y: 1 }, + left: { x: -1, y: 0 } + }, - // set of points we want the pathfinding to finish at - if (to instanceof Rect) { // `to` is targetBBox - end = targetAnchor; - endPoints = getRectPoints(targetAnchor, to, opt.endDirections, grid, opt); + // cost of an orthogonal step + cost: function() { - } else { - end = targetAnchor; - endPoints = [end]; - } + return this.step; + }, - // take into account only accessible rect points (those not under obstacles) - startPoints = startPoints.filter(function (p) { return !isPointObstacle(p); }); - endPoints = endPoints.filter(function (p) { return !isPointObstacle(p); }); + // an array of directions to find next points on the route + // different from start/end directions + directions: function() { - // Check that there is an accessible route point on both sides. - // Otherwise, use fallbackRoute(). - if (startPoints.length > 0 && endPoints.length > 0) { + var step = this.step; + var cost = this.cost(); - // The set of tentative points to be evaluated, initially containing the start points. - // Rounded to nearest integer for simplicity. - var openSet = new SortedSet(); - // Keeps reference to actual points for given elements of the open set. - var points = {}; - // Keeps reference to a point that is immediate predecessor of given element. - var parents = {}; - // Cost from start to a point along best known path. - var costs = {}; + return [ + { offsetX: step, offsetY: 0, cost: cost }, + { offsetX: -step, offsetY: 0, cost: cost }, + { offsetX: 0, offsetY: step, cost: cost }, + { offsetX: 0, offsetY: -step, cost: cost } + ]; + }, - for (var i = 0, n = startPoints.length; i < n; i++) { - // startPoint is assumed to be aligned already - var startPoint = startPoints[i]; + // a penalty received for direction change + penalties: function() { - var key = getKey(startPoint); + return { + 0: 0, + 45: this.step / 2, + 90: this.step / 2 + }; + }, - openSet.add(key, estimateCost(startPoint, endPoints)); - points[key] = startPoint; - costs[key] = 0; - } + // padding applied on the element bounding boxes + paddingBox: function() { - var previousRouteDirectionAngle = opt.previousDirectionAngle; // undefined for first route - var isPathBeginning = (previousRouteDirectionAngle === undefined); + var step = this.step; - // directions - var direction, directionChange; - var directions = opt.directions; - getGridOffsets(directions, grid, opt); + return { + x: -step, + y: -step, + width: 2 * step, + height: 2 * step + }; + }, - var numDirections = directions.length; + // A function that determines whether a given point is an obstacle or not. + // If used, the `padding`, `excludeEnds`and `excludeTypes` options are ignored. + // (point: dia.Point) => boolean; + isPointObstacle: null, - var endPointsKeys = toArray(endPoints).reduce(function(res, endPoint) { - // endPoint is assumed to be aligned already + // a router to use when the manhattan router fails + // (one of the partial routes returns null) + fallbackRouter: function(vertices, opt, linkView) { - var key = getKey(endPoint); - res.push(key); - return res; - }, []); + if (!isFunction(orthogonal)) { + throw new Error('Manhattan requires the orthogonal router as default fallback.'); + } - // main route finding loop - var loopsRemaining = opt.maximumLoops; - while (!openSet.isEmpty() && loopsRemaining > 0) { + return orthogonal(vertices, assign({}, config$1, opt), linkView); + }, - // remove current from the open list - var currentKey = openSet.pop(); - var currentPoint = points[currentKey]; - var currentParent = parents[currentKey]; - var currentCost = costs[currentKey]; + /* Deprecated */ + // a simple route used in situations when main routing method fails + // (exceed max number of loop iterations, inaccessible) + fallbackRoute: function(from, to, opt) { - var isRouteBeginning = (currentParent === undefined); // undefined for route starts - var isStart = currentPoint.equals(start); // (is source anchor or `from` point) = can leave in any direction + return null; // null result will trigger the fallbackRouter - var previousDirectionAngle; - if (!isRouteBeginning) { previousDirectionAngle = getDirectionAngle(currentParent, currentPoint, numDirections, grid, opt); } // a vertex on the route - else if (!isPathBeginning) { previousDirectionAngle = previousRouteDirectionAngle; } // beginning of route on the path - else if (!isStart) { previousDirectionAngle = getDirectionAngle(start, currentPoint, numDirections, grid, opt); } // beginning of path, start rect point - else { previousDirectionAngle = null; } // beginning of path, source anchor or `from` point + // left for reference: + /*// Find an orthogonal route ignoring obstacles. - // check if we reached any endpoint - var samePoints = startPoints.length === endPoints.length; - if (samePoints) { - for (var j = 0; j < startPoints.length; j++) { - if (!startPoints[j].equals(endPoints[j])) { - samePoints = false; - break; - } - } - } - var skipEndCheck = (isRouteBeginning && samePoints); - if (!skipEndCheck && (endPointsKeys.indexOf(currentKey) >= 0)) { - opt.previousDirectionAngle = previousDirectionAngle; - return reconstructRoute(parents, points, currentPoint, start, end, grid, opt); - } + var point = ((opt.previousDirAngle || 0) % 180 === 0) + ? new g.Point(from.x, to.y) + : new g.Point(to.x, from.y); - // go over all possible directions and find neighbors - for (i = 0; i < numDirections; i++) { - direction = directions[i]; + return [point];*/ + }, - var directionAngle = direction.angle; - directionChange = getDirectionChange(previousDirectionAngle, directionAngle); + // if a function is provided, it's used to route the link while dragging an end + // i.e. function(from, to, opt) { return []; } + draggingRoute: null + }; - // if the direction changed rapidly, don't use this point - // any direction is allowed for starting points - if (!(isPathBeginning && isStart) && directionChange > opt.maxAllowedDirectionChange) { continue; } - - var neighborPoint = align(currentPoint.clone().offset(direction.gridOffsetX, direction.gridOffsetY), grid, precision); - var neighborKey = getKey(neighborPoint); + // HELPER CLASSES // - // Closed points from the openSet were already evaluated. - if (openSet.isClose(neighborKey) || isPointObstacle(neighborPoint)) { continue; } + // Map of obstacles + // Helper structure to identify whether a point lies inside an obstacle. + function ObstacleMap(opt) { - // We can only enter end points at an acceptable angle. - if (endPointsKeys.indexOf(neighborKey) >= 0) { // neighbor is an end point + this.map = {}; + this.options = opt; + // tells how to divide the paper when creating the elements map + this.mapGridSize = 100; + } - var isNeighborEnd = neighborPoint.equals(end); // (is target anchor or `to` point) = can be entered in any direction + ObstacleMap.prototype.build = function(graph, link) { - if (!isNeighborEnd) { - var endDirectionAngle = getDirectionAngle(neighborPoint, end, numDirections, grid, opt); - var endDirectionChange = getDirectionChange(directionAngle, endDirectionAngle); + var opt = this.options; - if (endDirectionChange > opt.maxAllowedDirectionChange) { continue; } - } - } + // source or target element could be excluded from set of obstacles + var excludedEnds = toArray(opt.excludeEnds).reduce(function(res, item) { - // The current direction is ok. + var end = link.get(item); + if (end) { + var cell = graph.getCell(end.id); + if (cell) { + res.push(cell); + } + } - var neighborCost = direction.cost; - var neighborPenalty = isStart ? 0 : opt.penalties[directionChange]; // no penalties for start point - var costFromStart = currentCost + neighborCost + neighborPenalty; + return res; + }, []); - if (!openSet.isOpen(neighborKey) || (costFromStart < costs[neighborKey])) { - // neighbor point has not been processed yet - // or the cost of the path from start is lower than previously calculated + // Exclude any embedded elements from the source and the target element. + var excludedAncestors = []; - points[neighborKey] = neighborPoint; - parents[neighborKey] = currentPoint; - costs[neighborKey] = costFromStart; - openSet.add(neighborKey, costFromStart + estimateCost(neighborPoint, endPoints)); - } - } + var source = graph.getCell(link.get('source').id); + if (source) { + excludedAncestors = union(excludedAncestors, source.getAncestors().map(function(cell) { + return cell.id; + })); + } - loopsRemaining--; - } + var target = graph.getCell(link.get('target').id); + if (target) { + excludedAncestors = union(excludedAncestors, target.getAncestors().map(function(cell) { + return cell.id; + })); } - // no route found (`to` point either wasn't accessible or finding route took - // way too much calculation) - return opt.fallbackRoute.call(this, start, end, opt); - } + // Builds a map of all elements for quicker obstacle queries (i.e. is a point contained + // in any obstacle?) (a simplified grid search). + // The paper is divided into smaller cells, where each holds information about which + // elements belong to it. When we query whether a point lies inside an obstacle we + // don't need to go through all obstacles, we check only those in a particular cell. + var mapGridSize = this.mapGridSize; - // resolve some of the options - function resolveOptions(opt) { + graph.getElements().reduce(function(map, element) { - opt.directions = result(opt, 'directions'); - opt.penalties = result(opt, 'penalties'); - opt.paddingBox = result(opt, 'paddingBox'); - opt.padding = result(opt, 'padding'); + var isExcludedType = toArray(opt.excludeTypes).includes(element.get('type')); + var isExcludedEnd = excludedEnds.find(function(excluded) { + return excluded.id === element.id; + }); + var isExcludedAncestor = excludedAncestors.includes(element.id); - if (opt.padding) { - // if both provided, opt.padding wins over opt.paddingBox - var sides = normalizeSides(opt.padding); - opt.paddingBox = { - x: -sides.left, - y: -sides.top, - width: sides.left + sides.right, - height: sides.top + sides.bottom - }; - } + var isExcluded = isExcludedType || isExcludedEnd || isExcludedAncestor; + if (!isExcluded) { + var bbox = element.getBBox().moveAndExpand(opt.paddingBox); - toArray(opt.directions).forEach(function(direction) { + var origin = bbox.origin().snapToGrid(mapGridSize); + var corner = bbox.corner().snapToGrid(mapGridSize); - var point1 = new Point(0, 0); - var point2 = new Point(direction.offsetX, direction.offsetY); + for (var x = origin.x; x <= corner.x; x += mapGridSize) { + for (var y = origin.y; y <= corner.y; y += mapGridSize) { + var gridKey = x + '@' + y; + map[gridKey] = map[gridKey] || []; + map[gridKey].push(bbox); + } + } + } - direction.angle = normalizeAngle(point1.theta(point2)); - }); - } + return map; + }, this.map); - // initialization of the route finding - function router(vertices, opt, linkView) { + return this; + }; - resolveOptions(opt); + ObstacleMap.prototype.isPointAccessible = function(point) { - // enable/disable linkView perpendicular option - linkView.options.perpendicular = !!opt.perpendicular; + var mapKey = point.clone().snapToGrid(this.mapGridSize).toString(); - var sourceBBox = getSourceBBox$1(linkView, opt); - var targetBBox = getTargetBBox$1(linkView, opt); + return toArray(this.map[mapKey]).every(function(obstacle) { + return !obstacle.containsPoint(point); + }); + }; - var sourceAnchor = getSourceAnchor$1(linkView, opt); - //var targetAnchor = getTargetAnchor(linkView, opt); + // Sorted Set + // Set of items sorted by given value. + function SortedSet() { + this.items = []; + this.hash = {}; + this.values = {}; + this.OPEN = 1; + this.CLOSE = 2; + } - // pathfinding - var isPointObstacle; - if (typeof opt.isPointObstacle === 'function') { - isPointObstacle = opt.isPointObstacle; + SortedSet.prototype.add = function(item, value) { + + if (this.hash[item]) { + // item removal + this.items.splice(this.items.indexOf(item), 1); } else { - var map = new ObstacleMap(opt); - map.build(linkView.paper.model, linkView.model); - isPointObstacle = function (point) { return !map.isPointAccessible(point); }; + this.hash[item] = this.OPEN; } - var oldVertices = toArray(vertices).map(Point); - var newVertices = []; - var tailPoint = sourceAnchor; // the origin of first route's grid, does not need snapping + this.values[item] = value; - // find a route by concatenating all partial routes (routes need to pass through vertices) - // source -> vertex[1] -> ... -> vertex[n] -> target - var to, from; + var index$1 = sortedIndex(this.items, item, function(i) { + return this.values[i]; + }.bind(this)); - for (var i = 0, len = oldVertices.length; i <= len; i++) { + this.items.splice(index$1, 0, item); + }; - var partialRoute = null; + SortedSet.prototype.remove = function(item) { - from = to || sourceBBox; - to = oldVertices[i]; + this.hash[item] = this.CLOSE; + }; - if (!to) { - // this is the last iteration - // we ran through all vertices in oldVertices - // 'to' is not a vertex. + SortedSet.prototype.isOpen = function(item) { - to = targetBBox; + return this.hash[item] === this.OPEN; + }; - // If the target is a point (i.e. it's not an element), we - // should use dragging route instead of main routing method if it has been provided. - var isEndingAtPoint = !linkView.model.get('source').id || !linkView.model.get('target').id; + SortedSet.prototype.isClose = function(item) { - if (isEndingAtPoint && isFunction(opt.draggingRoute)) { - // Make sure we are passing points only (not rects). - var dragFrom = (from === sourceBBox) ? sourceAnchor : from; - var dragTo = to.origin(); + return this.hash[item] === this.CLOSE; + }; - partialRoute = opt.draggingRoute.call(linkView, dragFrom, dragTo, opt); - } - } + SortedSet.prototype.isEmpty = function() { - // if partial route has not been calculated yet use the main routing method to find one - partialRoute = partialRoute || findRoute.call(linkView, from, to, isPointObstacle, opt); + return this.items.length === 0; + }; - if (partialRoute === null) { // the partial route cannot be found - return opt.fallbackRouter(vertices, opt, linkView); - } + SortedSet.prototype.pop = function() { - var leadPoint = partialRoute[0]; + var item = this.items.shift(); + this.remove(item); + return item; + }; - // remove the first point if the previous partial route had the same point as last - if (leadPoint && leadPoint.equals(tailPoint)) { partialRoute.shift(); } + // HELPERS // - // save tailPoint for next iteration - tailPoint = partialRoute[partialRoute.length - 1] || tailPoint; + // return source bbox + function getSourceBBox$1(linkView, opt) { - Array.prototype.push.apply(newVertices, partialRoute); - } + // expand by padding box + if (opt && opt.paddingBox) { return linkView.sourceBBox.clone().moveAndExpand(opt.paddingBox); } - return newVertices; + return linkView.sourceBBox.clone(); } - // public function - var manhattan = function(vertices, opt, linkView) { - return router(vertices, assign({}, config$1, opt), linkView); - }; + // return target bbox + function getTargetBBox$1(linkView, opt) { - var config$2 = { + // expand by padding box + if (opt && opt.paddingBox) { return linkView.targetBBox.clone().moveAndExpand(opt.paddingBox); } - maxAllowedDirectionChange: 45, + return linkView.targetBBox.clone(); + } - // cost of a diagonal step - diagonalCost: function() { + // return source anchor + function getSourceAnchor$1(linkView, opt) { - var step = this.step; - return Math.ceil(Math.sqrt(step * step << 1)); - }, + if (linkView.sourceAnchor) { return linkView.sourceAnchor; } - // an array of directions to find next points on the route - // different from start/end directions - directions: function() { + // fallback: center of bbox + var sourceBBox = getSourceBBox$1(linkView, opt); + return sourceBBox.center(); + } - var step = this.step; - var cost = this.cost(); - var diagonalCost = this.diagonalCost(); + // return target anchor + function getTargetAnchor$1(linkView, opt) { - return [ - { offsetX: step, offsetY: 0, cost: cost }, - { offsetX: step, offsetY: step, cost: diagonalCost }, - { offsetX: 0, offsetY: step, cost: cost }, - { offsetX: -step, offsetY: step, cost: diagonalCost }, - { offsetX: -step, offsetY: 0, cost: cost }, - { offsetX: -step, offsetY: -step, cost: diagonalCost }, - { offsetX: 0, offsetY: -step, cost: cost }, - { offsetX: step, offsetY: -step, cost: diagonalCost } - ]; - }, + if (linkView.targetAnchor) { return linkView.targetAnchor; } - // a simple route used in situations when main routing method fails - // (exceed max number of loop iterations, inaccessible) - fallbackRoute: function(from, to, opt) { + // fallback: center of bbox + var targetBBox = getTargetBBox$1(linkView, opt); + return targetBBox.center(); // default + } - // Find a route which breaks by 45 degrees ignoring all obstacles. + // returns a direction index from start point to end point + // corrects for grid deformation between start and end + function getDirectionAngle(start, end, numDirections, grid, opt) { - var theta = from.theta(to); + var quadrant = 360 / numDirections; + var angleTheta = start.theta(fixAngleEnd(start, end, grid, opt)); + var normalizedAngle = normalizeAngle(angleTheta + (quadrant / 2)); + return quadrant * Math.floor(normalizedAngle / quadrant); + } - var route = []; + // helper function for getDirectionAngle() + // corrects for grid deformation + // (if a point is one grid steps away from another in both dimensions, + // it is considered to be 45 degrees away, even if the real angle is different) + // this causes visible angle discrepancies if `opt.step` is much larger than `paper.gridSize` + function fixAngleEnd(start, end, grid, opt) { - var a = { x: to.x, y: from.y }; - var b = { x: from.x, y: to.y }; + var step = opt.step; - if (theta % 180 > 90) { - var t = a; - a = b; - b = t; - } + var diffX = end.x - start.x; + var diffY = end.y - start.y; - var p1 = (theta % 90) < 45 ? a : b; - var l1 = new Line(from, p1); + var gridStepsX = diffX / grid.x; + var gridStepsY = diffY / grid.y; - var alpha = 90 * Math.ceil(theta / 90); + var distanceX = gridStepsX * step; + var distanceY = gridStepsY * step; - var p2 = Point.fromPolar(l1.squaredLength(), toRad(alpha + 135), p1); - var l2 = new Line(to, p2); + return new Point(start.x + distanceX, start.y + distanceY); + } - var intersectionPoint = l1.intersection(l2); - var point = intersectionPoint ? intersectionPoint : to; + // return the change in direction between two direction angles + function getDirectionChange(angle1, angle2) { - var directionFrom = intersectionPoint ? point : from; + var directionChange = Math.abs(angle1 - angle2); + return (directionChange > 180) ? (360 - directionChange) : directionChange; + } - var quadrant = 360 / opt.directions.length; - var angleTheta = directionFrom.theta(to); - var normalizedAngle = normalizeAngle(angleTheta + (quadrant / 2)); - var directionAngle = quadrant * Math.floor(normalizedAngle / quadrant); + // fix direction offsets according to current grid + function getGridOffsets(directions, grid, opt) { - opt.previousDirectionAngle = directionAngle; + var step = opt.step; - if (point) { route.push(point.round()); } - route.push(to); + toArray(opt.directions).forEach(function(direction) { - return route; - } - }; + direction.gridOffsetX = (direction.offsetX / step) * grid.x; + direction.gridOffsetY = (direction.offsetY / step) * grid.y; + }); + } - // public function - var metro = function(vertices, opt, linkView) { + // get grid size in x and y dimensions, adapted to source and target positions + function getGrid(step, source, target) { - if (!isFunction(manhattan)) { - throw new Error('Metro requires the manhattan router.'); - } + return { + source: source.clone(), + x: getGridDimension(target.x - source.x, step), + y: getGridDimension(target.y - source.y, step) + }; + } - return manhattan(vertices, assign({}, config$2, opt), linkView); - }; + // helper function for getGrid() + function getGridDimension(diff, step) { + // return step if diff = 0 + if (!diff) { return step; } + var absDiff = Math.abs(diff); + var numSteps = Math.round(absDiff / step); - var routers = ({ - normal: normal$1, - oneSide: oneSide, - orthogonal: orthogonal, - manhattan: manhattan, - metro: metro - }); + // return absDiff if less than one step apart + if (!numSteps) { return absDiff; } - function connectionRatio(view, _magnet, _refPoint, opt) { + // otherwise, return corrected step + var roundedDiff = numSteps * step; + var remainder = absDiff - roundedDiff; + var stepCorrection = remainder / numSteps; - var ratio = ('ratio' in opt) ? opt.ratio : 0.5; - return view.getPointAtRatio(ratio); + return step + stepCorrection; } - function connectionLength(view, _magnet, _refPoint, opt) { + // return a clone of point snapped to grid + function snapToGrid$1(point, grid) { - var length = ('length' in opt) ? opt.length : 20; - return view.getPointAtLength(length); - } + var source = grid.source; - function _connectionPerpendicular(view, _magnet, refPoint, opt) { + var snappedX = snapToGrid(point.x - source.x, grid.x) + source.x; + var snappedY = snapToGrid(point.y - source.y, grid.y) + source.y; - var OFFSET = 1e6; - var path = view.getConnection(); - var segmentSubdivisions = view.getConnectionSubdivisions(); - var verticalLine = new Line(refPoint.clone().offset(0, OFFSET), refPoint.clone().offset(0, -OFFSET)); - var horizontalLine = new Line(refPoint.clone().offset(OFFSET, 0), refPoint.clone().offset(-OFFSET, 0)); - var verticalIntersections = verticalLine.intersect(path, { segmentSubdivisions: segmentSubdivisions }); - var horizontalIntersections = horizontalLine.intersect(path, { segmentSubdivisions: segmentSubdivisions }); - var intersections = []; - if (verticalIntersections) { Array.prototype.push.apply(intersections, verticalIntersections); } - if (horizontalIntersections) { Array.prototype.push.apply(intersections, horizontalIntersections); } - if (intersections.length > 0) { return refPoint.chooseClosest(intersections); } - if ('fallbackAt' in opt) { - return getPointAtLink(view, opt.fallbackAt); - } - return connectionClosest(view, _magnet, refPoint, opt); + return new Point(snappedX, snappedY); } - function _connectionClosest(view, _magnet, refPoint, _opt) { + // round the point to opt.precision + function round$4(point, precision) { - var closestPoint = view.getClosestPoint(refPoint); - if (!closestPoint) { return new Point(); } - return closestPoint; + return point.round(precision); } - function resolveRef(fn) { - return function(view, magnet, ref, opt) { - if (ref instanceof Element) { - var refView = this.paper.findView(ref); - var refPoint; - if (refView) { - if (refView.isNodeConnection(ref)) { - var distance = ('fixedAt' in opt) ? opt.fixedAt : '50%'; - refPoint = getPointAtLink(refView, distance); - } else { - refPoint = refView.getNodeBBox(ref).center(); - } - } else { - // Something went wrong - refPoint = new Point(); - } - return fn.call(this, view, magnet, refPoint, opt); - } - return fn.apply(this, arguments); - }; - } + // snap to grid and then round the point + function align(point, grid, precision) { - function getPointAtLink(view, value) { - var parsedValue = parseFloat(value); - if (isPercentage(value)) { - return view.getPointAtRatio(parsedValue / 100); - } else { - return view.getPointAtLength(parsedValue); - } + return round$4(snapToGrid$1(point.clone(), grid), precision); } - var connectionPerpendicular = resolveRef(_connectionPerpendicular); - var connectionClosest = resolveRef(_connectionClosest); - - var linkAnchors = ({ - resolveRef: resolveRef, - connectionRatio: connectionRatio, - connectionLength: connectionLength, - connectionPerpendicular: connectionPerpendicular, - connectionClosest: connectionClosest - }); - function bboxWrapper(method) { + // return a string representing the point + // string is rounded in both dimensions + function getKey(point) { - return function(view, magnet, ref, opt) { + return point.clone().toString(); + } - var rotate = !!opt.rotate; - var bbox = (rotate) ? view.getNodeUnrotatedBBox(magnet) : view.getNodeBBox(magnet); - var anchor = bbox[method](); + // return a normalized vector from given point + // used to determine the direction of a difference of two points + function normalizePoint(point) { - var dx = opt.dx; - if (dx) { - var dxPercentage = isPercentage(dx); - dx = parseFloat(dx); - if (isFinite(dx)) { - if (dxPercentage) { - dx /= 100; - dx *= bbox.width; - } - anchor.x += dx; - } - } + return new Point( + point.x === 0 ? 0 : Math.abs(point.x) / point.x, + point.y === 0 ? 0 : Math.abs(point.y) / point.y + ); + } - var dy = opt.dy; - if (dy) { - var dyPercentage = isPercentage(dy); - dy = parseFloat(dy); - if (isFinite(dy)) { - if (dyPercentage) { - dy /= 100; - dy *= bbox.height; - } - anchor.y += dy; - } - } + // PATHFINDING // - return (rotate) ? anchor.rotate(view.model.getBBox().center(), -view.model.angle()) : anchor; - }; - } + // reconstructs a route by concatenating points with their parents + function reconstructRoute(parents, points, tailPoint, from, to, grid, opt) { - function _perpendicular(view, magnet, refPoint, opt) { + var route = []; - var angle = view.model.angle(); - var bbox = view.getNodeBBox(magnet); - var anchor = bbox.center(); - var topLeft = bbox.origin(); - var bottomRight = bbox.corner(); + var prevDiff = normalizePoint(to.difference(tailPoint)); - var padding = opt.padding; - if (!isFinite(padding)) { padding = 0; } + // tailPoint is assumed to be aligned already + var currentKey = getKey(tailPoint); + var parent = parents[currentKey]; - if ((topLeft.y + padding) <= refPoint.y && refPoint.y <= (bottomRight.y - padding)) { - var dy = (refPoint.y - anchor.y); - anchor.x += (angle === 0 || angle === 180) ? 0 : dy * 1 / Math.tan(toRad(angle)); - anchor.y += dy; - } else if ((topLeft.x + padding) <= refPoint.x && refPoint.x <= (bottomRight.x - padding)) { - var dx = (refPoint.x - anchor.x); - anchor.y += (angle === 90 || angle === 270) ? 0 : dx * Math.tan(toRad(angle)); - anchor.x += dx; + var point; + while (parent) { + + // point is assumed to be aligned already + point = points[currentKey]; + + var diff = normalizePoint(point.difference(parent)); + if (!diff.equals(prevDiff)) { + route.unshift(point); + prevDiff = diff; + } + + // parent is assumed to be aligned already + currentKey = getKey(parent); + parent = parents[currentKey]; } - return anchor; + // leadPoint is assumed to be aligned already + var leadPoint = points[currentKey]; + + var fromDiff = normalizePoint(leadPoint.difference(from)); + if (!fromDiff.equals(prevDiff)) { + route.unshift(leadPoint); + } + + return route; } - function _midSide(view, magnet, refPoint, opt) { + // heuristic method to determine the distance between two points + function estimateCost(from, endPoints) { - var rotate = !!opt.rotate; - var bbox, angle, center; - if (rotate) { - bbox = view.getNodeUnrotatedBBox(magnet); - center = view.model.getBBox().center(); - angle = view.model.angle(); - } else { - bbox = view.getNodeBBox(magnet); + var min = Infinity; + + for (var i = 0, len = endPoints.length; i < len; i++) { + var cost = from.manhattanDistance(endPoints[i]); + if (cost < min) { min = cost; } } - var padding = opt.padding; - if (isFinite(padding)) { bbox.inflate(padding); } + return min; + } - if (rotate) { refPoint.rotate(center, angle); } + // find points around the bbox taking given directions into account + // lines are drawn from anchor in given directions, intersections recorded + // if anchor is outside bbox, only those directions that intersect get a rect point + // the anchor itself is returned as rect point (representing some directions) + // (since those directions are unobstructed by the bbox) + function getRectPoints(anchor, bbox, directionList, grid, opt) { - var side = bbox.sideNearestToPoint(refPoint); - var anchor; - switch (side) { - case 'left': - anchor = bbox.leftMiddle(); - break; - case 'right': - anchor = bbox.rightMiddle(); - break; - case 'top': - anchor = bbox.topMiddle(); - break; - case 'bottom': - anchor = bbox.bottomMiddle(); - break; + var precision = opt.precision; + var directionMap = opt.directionMap; + + var anchorCenterVector = anchor.difference(bbox.center()); + + var keys = isObject$1(directionMap) ? Object.keys(directionMap) : []; + var dirList = toArray(directionList); + var rectPoints = keys.reduce(function(res, key) { + + if (dirList.includes(key)) { + var direction = directionMap[key]; + + // create a line that is guaranteed to intersect the bbox if bbox is in the direction + // even if anchor lies outside of bbox + var endpoint = new Point( + anchor.x + direction.x * (Math.abs(anchorCenterVector.x) + bbox.width), + anchor.y + direction.y * (Math.abs(anchorCenterVector.y) + bbox.height) + ); + var intersectionLine = new Line(anchor, endpoint); + + // get the farther intersection, in case there are two + // (that happens if anchor lies next to bbox) + var intersections = intersectionLine.intersect(bbox) || []; + var numIntersections = intersections.length; + var farthestIntersectionDistance; + var farthestIntersection = null; + for (var i = 0; i < numIntersections; i++) { + var currentIntersection = intersections[i]; + var distance = anchor.squaredDistance(currentIntersection); + if ((farthestIntersectionDistance === undefined) || (distance > farthestIntersectionDistance)) { + farthestIntersectionDistance = distance; + farthestIntersection = currentIntersection; + } + } + + // if an intersection was found in this direction, it is our rectPoint + if (farthestIntersection) { + var point = align(farthestIntersection, grid, precision); + + // if the rectPoint lies inside the bbox, offset it by one more step + if (bbox.containsPoint(point)) { + point = align(point.offset(direction.x * grid.x, direction.y * grid.y), grid, precision); + } + + // then add the point to the result array + // aligned + res.push(point); + } + } + + return res; + }, []); + + // if anchor lies outside of bbox, add it to the array of points + if (!bbox.containsPoint(anchor)) { + // aligned + rectPoints.push(align(anchor, grid, precision)); } - return (rotate) ? anchor.rotate(center, -angle) : anchor; + return rectPoints; } - // Can find anchor from model, when there is no selector or the link end - // is connected to a port - function _modelCenter(view, _magnet, _refPoint, opt, endType) { - return view.model.getPointFromConnectedLink(this.model, endType).offset(opt.dx, opt.dy); - } + // finds the route between two points/rectangles (`from`, `to`) implementing A* algorithm + // rectangles get rect points assigned by getRectPoints() + function findRoute(from, to, isPointObstacle, opt) { - //joint.anchors - var center = bboxWrapper('center'); - var top$2 = bboxWrapper('topMiddle'); - var bottom$2 = bboxWrapper('bottomMiddle'); - var left$2 = bboxWrapper('leftMiddle'); - var right$2 = bboxWrapper('rightMiddle'); - var topLeft = bboxWrapper('origin'); - var topRight = bboxWrapper('topRight'); - var bottomLeft = bboxWrapper('bottomLeft'); - var bottomRight = bboxWrapper('corner'); - var perpendicular = resolveRef(_perpendicular); - var midSide = resolveRef(_midSide); - var modelCenter = _modelCenter; + var precision = opt.precision; - var anchors = ({ - center: center, - top: top$2, - bottom: bottom$2, - left: left$2, - right: right$2, - topLeft: topLeft, - topRight: topRight, - bottomLeft: bottomLeft, - bottomRight: bottomRight, - perpendicular: perpendicular, - midSide: midSide, - modelCenter: modelCenter - }); + // Get grid for this route. - var ToolView = View.extend({ - name: null, - tagName: 'g', - className: 'tool', - svgElement: true, - _visible: true, + var sourceAnchor, targetAnchor; - init: function() { - var name = this.name; - if (name) { this.vel.attr('data-tool-name', name); } - }, + if (from instanceof Rect) { // `from` is sourceBBox + sourceAnchor = round$4(getSourceAnchor$1(this, opt).clone(), precision); + } else { + sourceAnchor = round$4(from.clone(), precision); + } - configure: function(view, toolsView) { - this.relatedView = view; - this.paper = view.paper; - this.parentView = toolsView; - this.simulateRelatedView(this.el); - // Delegate events in case the ToolView was removed from the DOM and reused. - this.delegateEvents(); - return this; - }, + if (to instanceof Rect) { // `to` is targetBBox + targetAnchor = round$4(getTargetAnchor$1(this, opt).clone(), precision); + } else { + targetAnchor = round$4(to.clone(), precision); + } - simulateRelatedView: function(el) { - if (el) { el.setAttribute('model-id', this.relatedView.model.id); } - }, + var grid = getGrid(opt.step, sourceAnchor, targetAnchor); - getName: function() { - return this.name; - }, + // Get pathfinding points. - show: function() { - this.el.style.display = ''; - this._visible = true; - }, + var start, end; // aligned with grid by definition + var startPoints, endPoints; // assumed to be aligned with grid already - hide: function() { - this.el.style.display = 'none'; - this._visible = false; - }, + // set of points we start pathfinding from + if (from instanceof Rect) { // `from` is sourceBBox + start = sourceAnchor; + startPoints = getRectPoints(start, from, opt.startDirections, grid, opt); - isVisible: function() { - return !!this._visible; - }, + } else { + start = sourceAnchor; + startPoints = [start]; + } - focus: function() { - var opacity = this.options.focusOpacity; - if (isFinite(opacity)) { this.el.style.opacity = opacity; } - this.parentView.focusTool(this); - }, + // set of points we want the pathfinding to finish at + if (to instanceof Rect) { // `to` is targetBBox + end = targetAnchor; + endPoints = getRectPoints(targetAnchor, to, opt.endDirections, grid, opt); - blur: function() { - this.el.style.opacity = ''; - this.parentView.blurTool(this); - }, + } else { + end = targetAnchor; + endPoints = [end]; + } - update: function() { - // to be overridden - }, + // take into account only accessible rect points (those not under obstacles) + startPoints = startPoints.filter(function (p) { return !isPointObstacle(p); }); + endPoints = endPoints.filter(function (p) { return !isPointObstacle(p); }); - guard: function(evt) { - // Let the context-menu event bubble up to the relatedView - var ref = this; - var paper = ref.paper; - var relatedView = ref.relatedView; - if (!paper || !relatedView) { return true; } - return paper.guard(evt, relatedView); + // Check that there is an accessible route point on both sides. + // Otherwise, use fallbackRoute(). + if (startPoints.length > 0 && endPoints.length > 0) { + + // The set of tentative points to be evaluated, initially containing the start points. + // Rounded to nearest integer for simplicity. + var openSet = new SortedSet(); + // Keeps reference to actual points for given elements of the open set. + var points = {}; + // Keeps reference to a point that is immediate predecessor of given element. + var parents = {}; + // Cost from start to a point along best known path. + var costs = {}; + + for (var i = 0, n = startPoints.length; i < n; i++) { + // startPoint is assumed to be aligned already + var startPoint = startPoints[i]; + + var key = getKey(startPoint); + + openSet.add(key, estimateCost(startPoint, endPoints)); + points[key] = startPoint; + costs[key] = 0; + } + + var previousRouteDirectionAngle = opt.previousDirectionAngle; // undefined for first route + var isPathBeginning = (previousRouteDirectionAngle === undefined); + + // directions + var direction, directionChange; + var directions = opt.directions; + getGridOffsets(directions, grid, opt); + + var numDirections = directions.length; + + var endPointsKeys = toArray(endPoints).reduce(function(res, endPoint) { + // endPoint is assumed to be aligned already + + var key = getKey(endPoint); + res.push(key); + return res; + }, []); + + // main route finding loop + var loopsRemaining = opt.maximumLoops; + while (!openSet.isEmpty() && loopsRemaining > 0) { + + // remove current from the open list + var currentKey = openSet.pop(); + var currentPoint = points[currentKey]; + var currentParent = parents[currentKey]; + var currentCost = costs[currentKey]; + + var isRouteBeginning = (currentParent === undefined); // undefined for route starts + var isStart = currentPoint.equals(start); // (is source anchor or `from` point) = can leave in any direction + + var previousDirectionAngle; + if (!isRouteBeginning) { previousDirectionAngle = getDirectionAngle(currentParent, currentPoint, numDirections, grid, opt); } // a vertex on the route + else if (!isPathBeginning) { previousDirectionAngle = previousRouteDirectionAngle; } // beginning of route on the path + else if (!isStart) { previousDirectionAngle = getDirectionAngle(start, currentPoint, numDirections, grid, opt); } // beginning of path, start rect point + else { previousDirectionAngle = null; } // beginning of path, source anchor or `from` point + + // check if we reached any endpoint + var samePoints = startPoints.length === endPoints.length; + if (samePoints) { + for (var j = 0; j < startPoints.length; j++) { + if (!startPoints[j].equals(endPoints[j])) { + samePoints = false; + break; + } + } + } + var skipEndCheck = (isRouteBeginning && samePoints); + if (!skipEndCheck && (endPointsKeys.indexOf(currentKey) >= 0)) { + opt.previousDirectionAngle = previousDirectionAngle; + return reconstructRoute(parents, points, currentPoint, start, end, grid, opt); + } + + // go over all possible directions and find neighbors + for (i = 0; i < numDirections; i++) { + direction = directions[i]; + + var directionAngle = direction.angle; + directionChange = getDirectionChange(previousDirectionAngle, directionAngle); + + // if the direction changed rapidly, don't use this point + // any direction is allowed for starting points + if (!(isPathBeginning && isStart) && directionChange > opt.maxAllowedDirectionChange) { continue; } + + var neighborPoint = align(currentPoint.clone().offset(direction.gridOffsetX, direction.gridOffsetY), grid, precision); + var neighborKey = getKey(neighborPoint); + + // Closed points from the openSet were already evaluated. + if (openSet.isClose(neighborKey) || isPointObstacle(neighborPoint)) { continue; } + + // We can only enter end points at an acceptable angle. + if (endPointsKeys.indexOf(neighborKey) >= 0) { // neighbor is an end point + + var isNeighborEnd = neighborPoint.equals(end); // (is target anchor or `to` point) = can be entered in any direction + + if (!isNeighborEnd) { + var endDirectionAngle = getDirectionAngle(neighborPoint, end, numDirections, grid, opt); + var endDirectionChange = getDirectionChange(directionAngle, endDirectionAngle); + + if (endDirectionChange > opt.maxAllowedDirectionChange) { continue; } + } + } + + // The current direction is ok. + + var neighborCost = direction.cost; + var neighborPenalty = isStart ? 0 : opt.penalties[directionChange]; // no penalties for start point + var costFromStart = currentCost + neighborCost + neighborPenalty; + + if (!openSet.isOpen(neighborKey) || (costFromStart < costs[neighborKey])) { + // neighbor point has not been processed yet + // or the cost of the path from start is lower than previously calculated + + points[neighborKey] = neighborPoint; + parents[neighborKey] = currentPoint; + costs[neighborKey] = costFromStart; + openSet.add(neighborKey, costFromStart + estimateCost(neighborPoint, endPoints)); + } + } + + loopsRemaining--; + } } - }); - function getAnchor(coords, view, magnet) { - // take advantage of an existing logic inside of the - // pin relative connection strategy - var end = pinRelative.call( - this.paper, - {}, - view, - magnet, - coords, - this.model - ); - return end.anchor; + // no route found (`to` point either wasn't accessible or finding route took + // way too much calculation) + return opt.fallbackRoute.call(this, start, end, opt); } - function snapAnchor(coords, view, magnet, type, relatedView, toolView) { - var snapRadius = toolView.options.snapRadius; - var isSource = (type === 'source'); - var refIndex = (isSource ? 0 : -1); - var ref = this.model.vertex(refIndex) || this.getEndAnchor(isSource ? 'target' : 'source'); - if (ref) { - if (Math.abs(ref.x - coords.x) < snapRadius) { coords.x = ref.x; } - if (Math.abs(ref.y - coords.y) < snapRadius) { coords.y = ref.y; } + // resolve some of the options + function resolveOptions(opt) { + + opt.directions = result(opt, 'directions'); + opt.penalties = result(opt, 'penalties'); + opt.paddingBox = result(opt, 'paddingBox'); + opt.padding = result(opt, 'padding'); + + if (opt.padding) { + // if both provided, opt.padding wins over opt.paddingBox + var sides = normalizeSides(opt.padding); + opt.paddingBox = { + x: -sides.left, + y: -sides.top, + width: sides.left + sides.right, + height: sides.top + sides.bottom + }; } - return coords; - } - function getViewBBox(view, useModelGeometry) { - var model = view.model; - if (useModelGeometry) { return model.getBBox(); } - return (model.isLink()) ? view.getConnection().bbox() : view.getNodeUnrotatedBBox(view.el); + toArray(opt.directions).forEach(function(direction) { + + var point1 = new Point(0, 0); + var point2 = new Point(direction.offsetX, direction.offsetY); + + direction.angle = normalizeAngle(point1.theta(point2)); + }); } - // Vertex Handles - var VertexHandle = View.extend({ - tagName: 'circle', - svgElement: true, - className: 'marker-vertex', - events: { - mousedown: 'onPointerDown', - touchstart: 'onPointerDown', - dblclick: 'onDoubleClick', - dbltap: 'onDoubleClick' - }, - documentEvents: { - mousemove: 'onPointerMove', - touchmove: 'onPointerMove', - mouseup: 'onPointerUp', - touchend: 'onPointerUp', - touchcancel: 'onPointerUp' - }, - attributes: { - 'r': 6, - 'fill': '#33334F', - 'stroke': '#FFFFFF', - 'stroke-width': 2, - 'cursor': 'move' - }, - position: function(x, y) { - this.vel.attr({ cx: x, cy: y }); - }, - onPointerDown: function(evt) { - if (this.options.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - this.options.paper.undelegateEvents(); - this.delegateDocumentEvents(null, evt.data); - this.trigger('will-change', this, evt); - }, - onPointerMove: function(evt) { - this.trigger('changing', this, evt); - }, - onDoubleClick: function(evt) { - this.trigger('remove', this, evt); - }, - onPointerUp: function(evt) { - this.trigger('changed', this, evt); - this.undelegateDocumentEvents(); - this.options.paper.delegateEvents(); + // initialization of the route finding + function router(vertices, opt, linkView) { + + resolveOptions(opt); + + // enable/disable linkView perpendicular option + linkView.options.perpendicular = !!opt.perpendicular; + + var sourceBBox = getSourceBBox$1(linkView, opt); + var targetBBox = getTargetBBox$1(linkView, opt); + + var sourceAnchor = getSourceAnchor$1(linkView, opt); + //var targetAnchor = getTargetAnchor(linkView, opt); + + // pathfinding + var isPointObstacle; + if (typeof opt.isPointObstacle === 'function') { + isPointObstacle = opt.isPointObstacle; + } else { + var map = new ObstacleMap(opt); + map.build(linkView.paper.model, linkView.model); + isPointObstacle = function (point) { return !map.isPointAccessible(point); }; } - }); - var Vertices = ToolView.extend({ - name: 'vertices', - options: { - handleClass: VertexHandle, - snapRadius: 20, - redundancyRemoval: true, - vertexAdding: true, - stopPropagation: true - }, - children: [{ - tagName: 'path', - selector: 'connection', - className: 'joint-vertices-path', - attributes: { - 'fill': 'none', - 'stroke': 'transparent', - 'stroke-width': 10, - 'cursor': 'cell' - } - }], - handles: null, - events: { - 'mousedown .joint-vertices-path': 'onPathPointerDown', - 'touchstart .joint-vertices-path': 'onPathPointerDown' - }, - onRender: function() { - if (this.options.vertexAdding) { - this.renderChildren(); - this.updatePath(); - } - this.resetHandles(); - this.renderHandles(); - return this; - }, - update: function() { - var relatedView = this.relatedView; - var vertices = relatedView.model.vertices(); - if (vertices.length === this.handles.length) { - this.updateHandles(); - } else { - this.resetHandles(); - this.renderHandles(); - } - if (this.options.vertexAdding) { - this.updatePath(); - } - return this; - }, - resetHandles: function() { - var handles = this.handles; - this.handles = []; - this.stopListening(); - if (!Array.isArray(handles)) { return; } - for (var i = 0, n = handles.length; i < n; i++) { - handles[i].remove(); - } - }, - renderHandles: function() { - var this$1 = this; - - var relatedView = this.relatedView; - var vertices = relatedView.model.vertices(); - for (var i = 0, n = vertices.length; i < n; i++) { - var vertex = vertices[i]; - var handle = new (this.options.handleClass)({ - index: i, - paper: this.paper, - guard: function (evt) { return this$1.guard(evt); } - }); - handle.render(); - handle.position(vertex.x, vertex.y); - this.simulateRelatedView(handle.el); - handle.vel.appendTo(this.el); - this.handles.push(handle); - this.startHandleListening(handle); - } - }, - updateHandles: function() { - var relatedView = this.relatedView; - var vertices = relatedView.model.vertices(); - for (var i = 0, n = vertices.length; i < n; i++) { - var vertex = vertices[i]; - var handle = this.handles[i]; - if (!handle) { return; } - handle.position(vertex.x, vertex.y); - } - }, - updatePath: function() { - var connection = this.childNodes.connection; - if (connection) { connection.setAttribute('d', this.relatedView.getSerializedConnection()); } - }, - startHandleListening: function(handle) { - var relatedView = this.relatedView; - if (relatedView.can('vertexMove')) { - this.listenTo(handle, 'will-change', this.onHandleWillChange); - this.listenTo(handle, 'changing', this.onHandleChanging); - this.listenTo(handle, 'changed', this.onHandleChanged); - } - if (relatedView.can('vertexRemove')) { - this.listenTo(handle, 'remove', this.onHandleRemove); - } - }, - getNeighborPoints: function(index) { - var linkView = this.relatedView; - var vertices = linkView.model.vertices(); - var prev = (index > 0) ? vertices[index - 1] : linkView.sourceAnchor; - var next = (index < vertices.length - 1) ? vertices[index + 1] : linkView.targetAnchor; - return { - prev: new Point(prev), - next: new Point(next) - }; - }, - onHandleWillChange: function(_handle, evt) { - this.focus(); - var ref = this; - var relatedView = ref.relatedView; - var options = ref.options; - relatedView.model.startBatch('vertex-move', { ui: true, tool: this.cid }); - if (!options.stopPropagation) { relatedView.notifyPointerdown.apply(relatedView, relatedView.paper.getPointerArgs(evt)); } - }, - onHandleChanging: function(handle, evt) { - var ref = this; - var options = ref.options; - var linkView = ref.relatedView; - var index = handle.options.index; - var ref$1 = linkView.paper.getPointerArgs(evt); - var normalizedEvent = ref$1[0]; - var x = ref$1[1]; - var y = ref$1[2]; - var vertex = { x: x, y: y }; - this.snapVertex(vertex, index); - linkView.model.vertex(index, vertex, { ui: true, tool: this.cid }); - handle.position(vertex.x, vertex.y); - if (!options.stopPropagation) { linkView.notifyPointermove(normalizedEvent, x, y); } - }, - onHandleChanged: function(_handle, evt) { - var ref = this; - var options = ref.options; - var linkView = ref.relatedView; - if (options.vertexAdding) { this.updatePath(); } - if (!options.redundancyRemoval) { return; } - var verticesRemoved = linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); - if (verticesRemoved) { this.render(); } - this.blur(); - linkView.model.stopBatch('vertex-move', { ui: true, tool: this.cid }); - if (this.eventData(evt).vertexAdded) { - linkView.model.stopBatch('vertex-add', { ui: true, tool: this.cid }); - } - var ref$1 = linkView.paper.getPointerArgs(evt); - var normalizedEvt = ref$1[0]; - var x = ref$1[1]; - var y = ref$1[2]; - if (!options.stopPropagation) { linkView.notifyPointerup(normalizedEvt, x, y); } - linkView.checkMouseleave(normalizedEvt); - }, - snapVertex: function(vertex, index) { - var snapRadius = this.options.snapRadius; - if (snapRadius > 0) { - var neighbors = this.getNeighborPoints(index); - var prev = neighbors.prev; - var next = neighbors.next; - if (Math.abs(vertex.x - prev.x) < snapRadius) { - vertex.x = prev.x; - } else if (Math.abs(vertex.x - next.x) < snapRadius) { - vertex.x = next.x; - } - if (Math.abs(vertex.y - prev.y) < snapRadius) { - vertex.y = neighbors.prev.y; - } else if (Math.abs(vertex.y - next.y) < snapRadius) { - vertex.y = next.y; - } - } - }, - onHandleRemove: function(handle, evt) { - var index$1 = handle.options.index; - var linkView = this.relatedView; - linkView.model.removeVertex(index$1, { ui: true }); - if (this.options.vertexAdding) { this.updatePath(); } - linkView.checkMouseleave(normalizeEvent(evt)); - }, - onPathPointerDown: function(evt) { - if (this.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - var normalizedEvent = normalizeEvent(evt); - var vertex = this.paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY).toJSON(); - var relatedView = this.relatedView; - relatedView.model.startBatch('vertex-add', { ui: true, tool: this.cid }); - var index$1 = relatedView.getVertexIndex(vertex.x, vertex.y); - this.snapVertex(vertex, index$1); - relatedView.model.insertVertex(index$1, vertex, { ui: true, tool: this.cid }); - this.update(); - var handle = this.handles[index$1]; - this.eventData(normalizedEvent, { vertexAdded: true }); - handle.onPointerDown(normalizedEvent); - }, - onRemove: function() { - this.resetHandles(); - } - }, { - VertexHandle: VertexHandle // keep as class property - }); - - var SegmentHandle = View.extend({ - tagName: 'g', - svgElement: true, - className: 'marker-segment', - events: { - mousedown: 'onPointerDown', - touchstart: 'onPointerDown' - }, - documentEvents: { - mousemove: 'onPointerMove', - touchmove: 'onPointerMove', - mouseup: 'onPointerUp', - touchend: 'onPointerUp', - touchcancel: 'onPointerUp' - }, - children: [{ - tagName: 'line', - selector: 'line', - attributes: { - 'stroke': '#33334F', - 'stroke-width': 2, - 'fill': 'none', - 'pointer-events': 'none' - } - }, { - tagName: 'rect', - selector: 'handle', - attributes: { - 'width': 20, - 'height': 8, - 'x': -10, - 'y': -4, - 'rx': 4, - 'ry': 4, - 'fill': '#33334F', - 'stroke': '#FFFFFF', - 'stroke-width': 2 - } - }], - onRender: function() { - this.renderChildren(); - }, - position: function(x, y, angle, view) { - - var matrix = V.createSVGMatrix().translate(x, y).rotate(angle); - var handle = this.childNodes.handle; - handle.setAttribute('transform', V.matrixToTransformString(matrix)); - handle.setAttribute('cursor', (angle % 180 === 0) ? 'row-resize' : 'col-resize'); - - var viewPoint = view.getClosestPoint(new Point(x, y)); - var line = this.childNodes.line; - line.setAttribute('x1', x); - line.setAttribute('y1', y); - line.setAttribute('x2', viewPoint.x); - line.setAttribute('y2', viewPoint.y); - }, - onPointerDown: function(evt) { - if (this.options.guard(evt)) { return; } - this.trigger('change:start', this, evt); - evt.stopPropagation(); - evt.preventDefault(); - this.options.paper.undelegateEvents(); - this.delegateDocumentEvents(null, evt.data); - }, - onPointerMove: function(evt) { - this.trigger('changing', this, evt); - }, - onPointerUp: function(evt) { - this.undelegateDocumentEvents(); - this.options.paper.delegateEvents(); - this.trigger('change:end', this, evt); - }, - show: function() { - this.el.style.display = ''; - }, - hide: function() { - this.el.style.display = 'none'; - } - }); - - var Segments = ToolView.extend({ - name: 'segments', - precision: .5, - options: { - handleClass: SegmentHandle, - segmentLengthThreshold: 40, - redundancyRemoval: true, - anchor: getAnchor, - snapRadius: 10, - snapHandle: true, - stopPropagation: true - }, - handles: null, - onRender: function() { - this.resetHandles(); - var relatedView = this.relatedView; - var vertices = relatedView.model.vertices(); - vertices.unshift(relatedView.sourcePoint); - vertices.push(relatedView.targetPoint); - for (var i = 0, n = vertices.length; i < n - 1; i++) { - var vertex = vertices[i]; - var nextVertex = vertices[i + 1]; - var handle = this.renderHandle(vertex, nextVertex); - this.simulateRelatedView(handle.el); - this.handles.push(handle); - handle.options.index = i; - } - return this; - }, - renderHandle: function(vertex, nextVertex) { - var this$1 = this; - - var handle = new (this.options.handleClass)({ - paper: this.paper, - guard: function (evt) { return this$1.guard(evt); } - }); - handle.render(); - this.updateHandle(handle, vertex, nextVertex); - handle.vel.appendTo(this.el); - this.startHandleListening(handle); - return handle; - }, - update: function() { - this.render(); - return this; - }, - startHandleListening: function(handle) { - this.listenTo(handle, 'change:start', this.onHandleChangeStart); - this.listenTo(handle, 'changing', this.onHandleChanging); - this.listenTo(handle, 'change:end', this.onHandleChangeEnd); - }, - resetHandles: function() { - var handles = this.handles; - this.handles = []; - this.stopListening(); - if (!Array.isArray(handles)) { return; } - for (var i = 0, n = handles.length; i < n; i++) { - handles[i].remove(); - } - }, - shiftHandleIndexes: function(value) { - var handles = this.handles; - for (var i = 0, n = handles.length; i < n; i++) { handles[i].options.index += value; } - }, - resetAnchor: function(type, anchor) { - var relatedModel = this.relatedView.model; - if (anchor) { - relatedModel.prop([type, 'anchor'], anchor, { - rewrite: true, - ui: true, - tool: this.cid - }); - } else { - relatedModel.removeProp([type, 'anchor'], { - ui: true, - tool: this.cid - }); - } - }, - snapHandle: function(handle, position, data) { - - var index = handle.options.index; - var linkView = this.relatedView; - var link = linkView.model; - var vertices = link.vertices(); - var axis = handle.options.axis; - var prev = vertices[index - 2] || data.sourceAnchor; - var next = vertices[index + 1] || data.targetAnchor; - var snapRadius = this.options.snapRadius; - if (Math.abs(position[axis] - prev[axis]) < snapRadius) { - position[axis] = prev[axis]; - } else if (Math.abs(position[axis] - next[axis]) < snapRadius) { - position[axis] = next[axis]; - } - return position; - }, - - onHandleChanging: function(handle, evt) { - - var ref = this; - var options = ref.options; - var data = this.eventData(evt); - var relatedView = this.relatedView; - var paper = relatedView.paper; - var index$1 = handle.options.index - 1; - var normalizedEvent = normalizeEvent(evt); - var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - var position = this.snapHandle(handle, coords.clone(), data); - var axis = handle.options.axis; - var offset = (this.options.snapHandle) ? 0 : (coords[axis] - position[axis]); - var link = relatedView.model; - var vertices = cloneDeep(link.vertices()); - var vertex = vertices[index$1]; - var nextVertex = vertices[index$1 + 1]; - var anchorFn = this.options.anchor; - if (typeof anchorFn !== 'function') { anchorFn = null; } - - // First Segment - var sourceView = relatedView.sourceView; - var sourceBBox = relatedView.sourceBBox; - var changeSourceAnchor = false; - var deleteSourceAnchor = false; - if (!vertex) { - vertex = relatedView.sourceAnchor.toJSON(); - vertex[axis] = position[axis]; - if (sourceBBox.containsPoint(vertex)) { - vertex[axis] = position[axis]; - changeSourceAnchor = true; - } else { - // we left the area of the source magnet for the first time - vertices.unshift(vertex); - this.shiftHandleIndexes(1); - deleteSourceAnchor = true; - } - } else if (index$1 === 0) { - if (sourceBBox.containsPoint(vertex)) { - vertices.shift(); - this.shiftHandleIndexes(-1); - changeSourceAnchor = true; - } else { - vertex[axis] = position[axis]; - deleteSourceAnchor = true; - } - } else { - vertex[axis] = position[axis]; - } - - if (anchorFn && sourceView) { - if (changeSourceAnchor) { - var sourceAnchorPosition = data.sourceAnchor.clone(); - sourceAnchorPosition[axis] = position[axis]; - var sourceAnchor = anchorFn.call(relatedView, sourceAnchorPosition, sourceView, relatedView.sourceMagnet || sourceView.el, 'source', relatedView); - this.resetAnchor('source', sourceAnchor); - } - if (deleteSourceAnchor) { - this.resetAnchor('source', data.sourceAnchorDef); - } - } - - // Last segment - var targetView = relatedView.targetView; - var targetBBox = relatedView.targetBBox; - var changeTargetAnchor = false; - var deleteTargetAnchor = false; - if (!nextVertex) { - nextVertex = relatedView.targetAnchor.toJSON(); - nextVertex[axis] = position[axis]; - if (targetBBox.containsPoint(nextVertex)) { - changeTargetAnchor = true; - } else { - // we left the area of the target magnet for the first time - vertices.push(nextVertex); - deleteTargetAnchor = true; - } - } else if (index$1 === vertices.length - 2) { - if (targetBBox.containsPoint(nextVertex)) { - vertices.pop(); - changeTargetAnchor = true; - } else { - nextVertex[axis] = position[axis]; - deleteTargetAnchor = true; - } - } else { - nextVertex[axis] = position[axis]; - } - - if (anchorFn && targetView) { - if (changeTargetAnchor) { - var targetAnchorPosition = data.targetAnchor.clone(); - targetAnchorPosition[axis] = position[axis]; - var targetAnchor = anchorFn.call(relatedView, targetAnchorPosition, targetView, relatedView.targetMagnet || targetView.el, 'target', relatedView); - this.resetAnchor('target', targetAnchor); - } - if (deleteTargetAnchor) { - this.resetAnchor('target', data.targetAnchorDef); - } - } - - link.vertices(vertices, { ui: true, tool: this.cid }); - this.updateHandle(handle, vertex, nextVertex, offset); - if (!options.stopPropagation) { relatedView.notifyPointermove(normalizedEvent, coords.x, coords.y); } - }, - onHandleChangeStart: function(handle, evt) { - var ref = this; - var options = ref.options; - var handles = ref.handles; - var linkView = ref.relatedView; - var model = linkView.model; - var paper = linkView.paper; - var index$1 = handle.options.index; - if (!Array.isArray(handles)) { return; } - for (var i = 0, n = handles.length; i < n; i++) { - if (i !== index$1) { handles[i].hide(); } - } - this.focus(); - this.eventData(evt, { - sourceAnchor: linkView.sourceAnchor.clone(), - targetAnchor: linkView.targetAnchor.clone(), - sourceAnchorDef: clone(model.prop(['source', 'anchor'])), - targetAnchorDef: clone(model.prop(['target', 'anchor'])) - }); - model.startBatch('segment-move', { ui: true, tool: this.cid }); - if (!options.stopPropagation) { linkView.notifyPointerdown.apply(linkView, paper.getPointerArgs(evt)); } - }, - onHandleChangeEnd: function(_handle, evt) { - var ref= this; - var options = ref.options; - var linkView = ref.relatedView; - var paper = linkView.paper; - var model = linkView.model; - if (options.redundancyRemoval) { - linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); - } - var normalizedEvent = normalizeEvent(evt); - var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - this.render(); - this.blur(); - model.stopBatch('segment-move', { ui: true, tool: this.cid }); - if (!options.stopPropagation) { linkView.notifyPointerup(normalizedEvent, coords.x, coords.y); } - linkView.checkMouseleave(normalizedEvent); - }, - updateHandle: function(handle, vertex, nextVertex, offset) { - var vertical = Math.abs(vertex.x - nextVertex.x) < this.precision; - var horizontal = Math.abs(vertex.y - nextVertex.y) < this.precision; - if (vertical || horizontal) { - var segmentLine = new Line(vertex, nextVertex); - var length = segmentLine.length(); - if (length < this.options.segmentLengthThreshold) { - handle.hide(); - } else { - var position = segmentLine.midpoint(); - var axis = (vertical) ? 'x' : 'y'; - position[axis] += offset || 0; - var angle = segmentLine.vector().vectorAngle(new Point(1, 0)); - handle.position(position.x, position.y, angle, this.relatedView); - handle.show(); - handle.options.axis = axis; - } - } else { - handle.hide(); - } - }, - onRemove: function() { - this.resetHandles(); - } - }, { - SegmentHandle: SegmentHandle // keep as class property - }); - - // End Markers - var Arrowhead = ToolView.extend({ - tagName: 'path', - xAxisVector: new Point(1, 0), - events: { - mousedown: 'onPointerDown', - touchstart: 'onPointerDown' - }, - documentEvents: { - mousemove: 'onPointerMove', - touchmove: 'onPointerMove', - mouseup: 'onPointerUp', - touchend: 'onPointerUp', - touchcancel: 'onPointerUp' - }, - onRender: function() { - this.update(); - }, - update: function() { - var ratio = this.ratio; - var view = this.relatedView; - var tangent = view.getTangentAtRatio(ratio); - var position, angle; - if (tangent) { - position = tangent.start; - angle = tangent.vector().vectorAngle(this.xAxisVector) || 0; - } else { - position = view.getPointAtRatio(ratio); - angle = 0; - } - if (!position) { return this; } - var matrix = V.createSVGMatrix().translate(position.x, position.y).rotate(angle); - this.vel.transform(matrix, { absolute: true }); - return this; - }, - onPointerDown: function(evt) { - if (this.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - var relatedView = this.relatedView; - relatedView.model.startBatch('arrowhead-move', { ui: true, tool: this.cid }); - if (relatedView.can('arrowheadMove')) { - relatedView.startArrowheadMove(this.arrowheadType); - this.delegateDocumentEvents(); - relatedView.paper.undelegateEvents(); - } - this.focus(); - this.el.style.pointerEvents = 'none'; - }, - onPointerMove: function(evt) { - var normalizedEvent = normalizeEvent(evt); - var coords = this.paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - this.relatedView.pointermove(normalizedEvent, coords.x, coords.y); - }, - onPointerUp: function(evt) { - this.undelegateDocumentEvents(); - var relatedView = this.relatedView; - var paper = relatedView.paper; - var normalizedEvent = normalizeEvent(evt); - var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - relatedView.pointerup(normalizedEvent, coords.x, coords.y); - paper.delegateEvents(); - this.blur(); - this.el.style.pointerEvents = ''; - relatedView.model.stopBatch('arrowhead-move', { ui: true, tool: this.cid }); - } - }); - - var TargetArrowhead = Arrowhead.extend({ - name: 'target-arrowhead', - ratio: 1, - arrowheadType: 'target', - attributes: { - 'd': 'M -10 -8 10 0 -10 8 Z', - 'fill': '#33334F', - 'stroke': '#FFFFFF', - 'stroke-width': 2, - 'cursor': 'move', - 'class': 'target-arrowhead' - } - }); - - var SourceArrowhead = Arrowhead.extend({ - name: 'source-arrowhead', - ratio: 0, - arrowheadType: 'source', - attributes: { - 'd': 'M 10 -8 -10 0 10 8 Z', - 'fill': '#33334F', - 'stroke': '#FFFFFF', - 'stroke-width': 2, - 'cursor': 'move', - 'class': 'source-arrowhead' - } - }); - - var Button = ToolView.extend({ - name: 'button', - events: { - 'mousedown': 'onPointerDown', - 'touchstart': 'onPointerDown' - }, - options: { - distance: 0, - offset: 0, - rotate: false - }, - onRender: function() { - this.renderChildren(this.options.markup); - this.update(); - }, - update: function() { - this.position(); - return this; - }, - position: function() { - var ref = this; - var view = ref.relatedView; - var vel = ref.vel; - var matrix = view.model.isLink() ? this.getLinkMatrix() : this.getElementMatrix(); - vel.transform(matrix, { absolute: true }); - }, - getElementMatrix: function getElementMatrix() { - var ref = this; - var view = ref.relatedView; - var options = ref.options; - var x = options.x; if ( x === void 0 ) x = 0; - var y = options.y; if ( y === void 0 ) y = 0; - var offset = options.offset; if ( offset === void 0 ) offset = {}; - var useModelGeometry = options.useModelGeometry; - var rotate = options.rotate; - var bbox = getViewBBox(view, useModelGeometry); - var angle = view.model.angle(); - if (!rotate) { bbox = bbox.bbox(angle); } - var offsetX = offset.x; if ( offsetX === void 0 ) offsetX = 0; - var offsetY = offset.y; if ( offsetY === void 0 ) offsetY = 0; - if (isPercentage(x)) { - x = parseFloat(x) / 100 * bbox.width; - } - if (isPercentage(y)) { - y = parseFloat(y) / 100 * bbox.height; - } - var matrix = V.createSVGMatrix().translate(bbox.x + bbox.width / 2, bbox.y + bbox.height / 2); - if (rotate) { matrix = matrix.rotate(angle); } - matrix = matrix.translate(x + offsetX - bbox.width / 2, y + offsetY - bbox.height / 2); - return matrix; - }, - getLinkMatrix: function getLinkMatrix() { - var ref = this; - var view = ref.relatedView; - var options = ref.options; - var offset = options.offset; if ( offset === void 0 ) offset = 0; - var distance = options.distance; if ( distance === void 0 ) distance = 0; - var rotate = options.rotate; - var tangent, position, angle; - if (isPercentage(distance)) { - tangent = view.getTangentAtRatio(parseFloat(distance) / 100); - } else { - tangent = view.getTangentAtLength(distance); - } - if (tangent) { - position = tangent.start; - angle = tangent.vector().vectorAngle(new Point(1, 0)) || 0; - } else { - position = view.getConnection().start; - angle = 0; - } - var matrix = V.createSVGMatrix() - .translate(position.x, position.y) - .rotate(angle) - .translate(0, offset); - if (!rotate) { matrix = matrix.rotate(-angle); } - return matrix; - }, - onPointerDown: function(evt) { - if (this.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - var actionFn = this.options.action; - if (typeof actionFn === 'function') { - actionFn.call(this.relatedView, evt, this.relatedView, this); - } - } - }); - - - var Remove = Button.extend({ - children: [{ - tagName: 'circle', - selector: 'button', - attributes: { - 'r': 7, - 'fill': '#FF1D00', - 'cursor': 'pointer' - } - }, { - tagName: 'path', - selector: 'icon', - attributes: { - 'd': 'M -3 -3 3 3 M -3 3 3 -3', - 'fill': 'none', - 'stroke': '#FFFFFF', - 'stroke-width': 2, - 'pointer-events': 'none' - } - }], - options: { - distance: 60, - offset: 0, - action: function(evt, view, tool) { - view.model.remove({ ui: true, tool: tool.cid }); - } - } - }); - - var Connect = Button.extend({ - name: 'connect', - documentEvents: { - mousemove: 'drag', - touchmove: 'drag', - mouseup: 'dragend', - touchend: 'dragend', - touchcancel: 'dragend' - }, - children: [{ - tagName: 'circle', - selector: 'button', - attributes: { - 'r': 7, - 'fill': '#333333', - 'cursor': 'pointer' - } - }, { - tagName: 'path', - selector: 'icon', - attributes: { - 'd': 'M -4 -1 L 0 -1 L 0 -4 L 4 0 L 0 4 0 1 -4 1 z', - 'fill': '#FFFFFF', - 'stroke': 'none', - 'stroke-width': 2, - 'pointer-events': 'none' - } - }], - options: { - distance: 80, - offset: 0, - magnet: function (view) { return view.el; }, - action: function (evt, _view, tool) { return tool.dragstart(evt); }, - }, - getMagnetNode: function() { - var assign; - - var ref = this; - var options = ref.options; - var relatedView = ref.relatedView; - var magnet = options.magnet; - var magnetNode; - switch (typeof magnet) { - case 'function': { - magnetNode = magnet.call(this, relatedView, this); - break; - } - case 'string': { - (assign = relatedView.findBySelector(magnet), magnetNode = assign[0]); - break; - } - default: { - magnetNode = magnet; - break; - } - } - if (!magnetNode) { magnetNode = relatedView.el; } - if (magnetNode instanceof SVGElement) { return magnetNode; } - throw new Error('Connect: magnet must be an SVGElement'); - }, - dragstart: function(evt) { - var ref = this; - var paper = ref.paper; - var relatedView = ref.relatedView; - var normalizedEvent = normalizeEvent(evt); - var ref$1 = paper.clientToLocalPoint(normalizedEvent.clientX, normalizedEvent.clientY); - var x = ref$1.x; - var y = ref$1.y; - relatedView.dragLinkStart(normalizedEvent, this.getMagnetNode(), x, y); - paper.undelegateEvents(); - this.delegateDocumentEvents(null, evt.data); - this.focus(); - }, - drag: function(evt) { - var ref = this; - var paper = ref.paper; - var relatedView = ref.relatedView; - var normalizedEvent = normalizeEvent(evt); - var ref$1 = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - var x = ref$1.x; - var y = ref$1.y; - relatedView.dragLink(normalizedEvent, x, y); - }, - dragend: function(evt) { - var ref = this; - var paper = ref.paper; - var relatedView = ref.relatedView; - var normalizedEvent = normalizeEvent(evt); - var ref$1 = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); - var x = ref$1.x; - var y = ref$1.y; - relatedView.dragLinkEnd(normalizedEvent, x, y); - this.undelegateDocumentEvents(); - paper.delegateEvents(); - this.blur(); - relatedView.checkMouseleave(normalizedEvent); - } - }); - - - var Boundary = ToolView.extend({ - name: 'boundary', - tagName: 'rect', - options: { - padding: 10, - useModelGeometry: false, - }, - attributes: { - 'fill': 'none', - 'stroke': '#33334F', - 'stroke-width': .5, - 'stroke-dasharray': '5, 5', - 'pointer-events': 'none' - }, - onRender: function() { - this.update(); - }, - update: function() { - var ref = this; - var view = ref.relatedView; - var options = ref.options; - var vel = ref.vel; - var useModelGeometry = options.useModelGeometry; - var rotate = options.rotate; - var padding = normalizeSides(options.padding); - var bbox = getViewBBox(view, useModelGeometry).moveAndExpand({ - x: -padding.left, - y: -padding.top, - width: padding.left + padding.right, - height: padding.top + padding.bottom - }); - var model = view.model; - if (model.isElement()) { - var angle = model.angle(); - if (angle) { - if (rotate) { - var origin = model.getBBox().center(); - vel.rotate(angle, origin.x, origin.y, { absolute: true }); - } else { - bbox = bbox.bbox(angle); - } - } - } - vel.attr(bbox.toJSON()); - return this; - } - }); - - var Anchor = ToolView.extend({ - tagName: 'g', - type: null, - children: [{ - tagName: 'circle', - selector: 'anchor', - attributes: { - 'cursor': 'pointer' - } - }, { - tagName: 'rect', - selector: 'area', - attributes: { - 'pointer-events': 'none', - 'fill': 'none', - 'stroke': '#33334F', - 'stroke-dasharray': '2,4', - 'rx': 5, - 'ry': 5 - } - }], - events: { - mousedown: 'onPointerDown', - touchstart: 'onPointerDown', - dblclick: 'onPointerDblClick', - dbltap: 'onPointerDblClick' - }, - documentEvents: { - mousemove: 'onPointerMove', - touchmove: 'onPointerMove', - mouseup: 'onPointerUp', - touchend: 'onPointerUp', - touchcancel: 'onPointerUp' - }, - options: { - snap: snapAnchor, - anchor: getAnchor, - resetAnchor: true, - customAnchorAttributes: { - 'stroke-width': 4, - 'stroke': '#33334F', - 'fill': '#FFFFFF', - 'r': 5 - }, - defaultAnchorAttributes: { - 'stroke-width': 2, - 'stroke': '#FFFFFF', - 'fill': '#33334F', - 'r': 6 - }, - areaPadding: 6, - snapRadius: 10, - restrictArea: true, - redundancyRemoval: true - }, - onRender: function() { - this.renderChildren(); - this.toggleArea(false); - this.update(); - }, - update: function() { - var type = this.type; - var relatedView = this.relatedView; - var view = relatedView.getEndView(type); - if (view) { - this.updateAnchor(); - this.updateArea(); - this.el.style.display = ''; - } else { - this.el.style.display = 'none'; - } - return this; - }, - updateAnchor: function() { - var childNodes = this.childNodes; - if (!childNodes) { return; } - var anchorNode = childNodes.anchor; - if (!anchorNode) { return; } - var relatedView = this.relatedView; - var type = this.type; - var position = relatedView.getEndAnchor(type); - var options = this.options; - var customAnchor = relatedView.model.prop([type, 'anchor']); - anchorNode.setAttribute('transform', 'translate(' + position.x + ',' + position.y + ')'); - var anchorAttributes = (customAnchor) ? options.customAnchorAttributes : options.defaultAnchorAttributes; - for (var attrName in anchorAttributes) { - anchorNode.setAttribute(attrName, anchorAttributes[attrName]); - } - }, - updateArea: function() { - var childNodes = this.childNodes; - if (!childNodes) { return; } - var areaNode = childNodes.area; - if (!areaNode) { return; } - var relatedView = this.relatedView; - var type = this.type; - var view = relatedView.getEndView(type); - var model = view.model; - var magnet = relatedView.getEndMagnet(type); - var padding = this.options.areaPadding; - if (!isFinite(padding)) { padding = 0; } - var bbox, angle, center; - if (view.isNodeConnection(magnet)) { - bbox = view.getBBox(); - angle = 0; - center = bbox.center(); - } else { - bbox = view.getNodeUnrotatedBBox(magnet); - angle = model.angle(); - center = bbox.center(); - if (angle) { center.rotate(model.getBBox().center(), -angle); } - // TODO: get the link's magnet rotation into account - } - bbox.inflate(padding); - areaNode.setAttribute('x', -bbox.width / 2); - areaNode.setAttribute('y', -bbox.height / 2); - areaNode.setAttribute('width', bbox.width); - areaNode.setAttribute('height', bbox.height); - areaNode.setAttribute('transform', 'translate(' + center.x + ',' + center.y + ') rotate(' + angle + ')'); - }, - toggleArea: function(visible) { - this.childNodes.area.style.display = (visible) ? '' : 'none'; - }, - onPointerDown: function(evt) { - if (this.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - this.paper.undelegateEvents(); - this.delegateDocumentEvents(); - this.focus(); - this.toggleArea(this.options.restrictArea); - this.relatedView.model.startBatch('anchor-move', { ui: true, tool: this.cid }); - }, - resetAnchor: function(anchor) { - var type = this.type; - var relatedModel = this.relatedView.model; - if (anchor) { - relatedModel.prop([type, 'anchor'], anchor, { - rewrite: true, - ui: true, - tool: this.cid - }); - } else { - relatedModel.removeProp([type, 'anchor'], { - ui: true, - tool: this.cid - }); - } - }, - onPointerMove: function(evt) { - - var relatedView = this.relatedView; - var type = this.type; - var view = relatedView.getEndView(type); - var model = view.model; - var magnet = relatedView.getEndMagnet(type); - var normalizedEvent = normalizeEvent(evt); - var coords = this.paper.clientToLocalPoint(normalizedEvent.clientX, normalizedEvent.clientY); - var snapFn = this.options.snap; - if (typeof snapFn === 'function') { - coords = snapFn.call(relatedView, coords, view, magnet, type, relatedView, this); - coords = new Point(coords); - } - - if (this.options.restrictArea) { - if (view.isNodeConnection(magnet)) { - // snap coords to the link's connection - var pointAtConnection = view.getClosestPoint(coords); - if (pointAtConnection) { coords = pointAtConnection; } - } else { - // snap coords within node bbox - var bbox = view.getNodeUnrotatedBBox(magnet); - var angle = model.angle(); - var origin = model.getBBox().center(); - var rotatedCoords = coords.clone().rotate(origin, angle); - if (!bbox.containsPoint(rotatedCoords)) { - coords = bbox.pointNearestToPoint(rotatedCoords).rotate(origin, -angle); - } - } - } - - var anchor; - var anchorFn = this.options.anchor; - if (typeof anchorFn === 'function') { - anchor = anchorFn.call(relatedView, coords, view, magnet, type, relatedView); - } - - this.resetAnchor(anchor); - this.update(); - }, - - onPointerUp: function(evt) { - this.paper.delegateEvents(); - this.undelegateDocumentEvents(); - this.blur(); - this.toggleArea(false); - var linkView = this.relatedView; - if (this.options.redundancyRemoval) { linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); } - linkView.model.stopBatch('anchor-move', { ui: true, tool: this.cid }); - }, - - onPointerDblClick: function() { - var anchor = this.options.resetAnchor; - if (anchor === false) { return; } // reset anchor disabled - if (anchor === true) { anchor = null; } // remove the current anchor - this.resetAnchor(cloneDeep(anchor)); - this.update(); - } - }); - - var SourceAnchor = Anchor.extend({ - name: 'source-anchor', - type: 'source' - }); - - var TargetAnchor = Anchor.extend({ - name: 'target-anchor', - type: 'target' - }); - - var index$3 = ({ - Vertices: Vertices, - Segments: Segments, - SourceArrowhead: SourceArrowhead, - TargetArrowhead: TargetArrowhead, - SourceAnchor: SourceAnchor, - TargetAnchor: TargetAnchor, - Button: Button, - Remove: Remove, - Connect: Connect, - Boundary: Boundary - }); - - var Control = ToolView.extend({ - tagName: 'g', - children: [{ - tagName: 'circle', - selector: 'handle', - attributes: { - 'cursor': 'pointer', - 'stroke-width': 2, - 'stroke': '#FFFFFF', - 'fill': '#33334F', - 'r': 6 - } - }, { - tagName: 'rect', - selector: 'extras', - attributes: { - 'pointer-events': 'none', - 'fill': 'none', - 'stroke': '#33334F', - 'stroke-dasharray': '2,4', - 'rx': 5, - 'ry': 5 - } - }], - events: { - mousedown: 'onPointerDown', - touchstart: 'onPointerDown', - dblclick: 'onPointerDblClick', - dbltap: 'onPointerDblClick' - }, - documentEvents: { - mousemove: 'onPointerMove', - touchmove: 'onPointerMove', - mouseup: 'onPointerUp', - touchend: 'onPointerUp', - touchcancel: 'onPointerUp' - }, - options: { - handleAttributes: null, - selector: 'root', - padding: 6, - }, - - getPosition: function() { - // To be overridden - }, - setPosition: function() { - // To be overridden - }, - resetPosition: function() { - // To be overridden - }, - onRender: function() { - this.renderChildren(); - this.toggleExtras(false); - this.update(); - }, - update: function() { - var ref = this.childNodes; - var handle = ref.handle; - var extras = ref.extras; - if (handle) { - this.updateHandle(handle); - } else { - throw new Error('Control: markup selector `handle` is required'); - } - if (extras) { - this.updateExtras(extras); - } - return this; - }, - updateHandle: function(handleNode) { - var ref = this; - var relatedView = ref.relatedView; - var options = ref.options; - var model = relatedView.model; - var relativePos = this.getPosition(relatedView, this); - var absolutePos = model.getAbsolutePointFromRelative(relativePos); - handleNode.setAttribute('transform', ("translate(" + (absolutePos.x) + "," + (absolutePos.y) + ")")); - var handleAttributes = options.handleAttributes; - if (handleAttributes) { - for (var attrName in handleAttributes) { - handleNode.setAttribute(attrName, handleAttributes[attrName]); - } - } - }, - updateExtras: function(extrasNode) { - var ref = this; - var relatedView = ref.relatedView; - var options = ref.options; - var ref$1 = this.options; - var selector = ref$1.selector; - if (!selector) { - this.toggleExtras(false); - return; - } - var ref$2 = relatedView.findBySelector(selector); - var magnet = ref$2[0]; - if (!magnet) { throw new Error('Control: invalid selector.'); } - var padding = options.padding; - if (!isFinite(padding)) { padding = 0; } - var bbox = relatedView.getNodeUnrotatedBBox(magnet); - var model = relatedView.model; - var angle = model.angle(); - var center = bbox.center(); - if (angle) { center.rotate(model.getBBox().center(), -angle); } - bbox.inflate(padding); - extrasNode.setAttribute('x', -bbox.width / 2); - extrasNode.setAttribute('y', -bbox.height / 2); - extrasNode.setAttribute('width', bbox.width); - extrasNode.setAttribute('height', bbox.height); - extrasNode.setAttribute('transform', ("translate(" + (center.x) + "," + (center.y) + ") rotate(" + angle + ")")); - }, - toggleExtras: function(visible) { - var ref = this.childNodes; - var extras = ref.extras; - if (!extras) { return; } - extras.style.display = (visible) ? '' : 'none'; - }, - onPointerDown: function(evt) { - var ref = this; - var relatedView = ref.relatedView; - var paper = ref.paper; - if (this.guard(evt)) { return; } - evt.stopPropagation(); - evt.preventDefault(); - paper.undelegateEvents(); - this.delegateDocumentEvents(); - this.focus(); - this.toggleExtras(true); - relatedView.model.startBatch('control-move', { ui: true, tool: this.cid }); - }, - onPointerMove: function(evt) { - var ref = this; - var relatedView = ref.relatedView; - var paper = ref.paper; - var model = relatedView.model; - var ref$1 = normalizeEvent(evt); - var clientX = ref$1.clientX; - var clientY = ref$1.clientY; - var coords = paper.clientToLocalPoint(clientX, clientY); - var relativeCoords = model.getRelativePointFromAbsolute(coords); - this.setPosition(relatedView, relativeCoords, this); - this.update(); - }, - onPointerUp: function(_evt) { - var ref = this; - var relatedView = ref.relatedView; - var paper = ref.paper; - paper.delegateEvents(); - this.undelegateDocumentEvents(); - this.blur(); - this.toggleExtras(false); - relatedView.model.stopBatch('control-move', { ui: true, tool: this.cid }); - }, - onPointerDblClick: function() { - var ref = this; - var relatedView = ref.relatedView; - this.resetPosition(relatedView, this); - this.update(); - } - - }); - - - - var index$4 = ({ - Button: Button, - Remove: Remove, - Connect: Connect, - Boundary: Boundary, - Control: Control - }); - - var version = "3.5.0"; - - var env = { - - _results: {}, - - _tests: { - - svgforeignobject: function() { - return !!document.createElementNS && - /SVGForeignObject/.test(({}).toString.call(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'))); - } - }, - - addTest: function(name, fn) { - - return this._tests[name] = fn; - }, - - test: function(name) { - - var fn = this._tests[name]; - - if (!fn) { - throw new Error('Test not defined ("' + name + '"). Use `joint.env.addTest(name, fn) to add a new test.`'); - } - - var result = this._results[name]; - - if (typeof result !== 'undefined') { - return result; - } - - try { - result = fn(); - } catch (error) { - result = false; - } - - // Cache the test result. - this._results[name] = result; - - return result; - } - }; - - var Vectorizer = V; - var layout = { PortLabel: PortLabel, Port: Port }; - var setTheme = function(theme, opt) { - - opt = opt || {}; - - invoke(views, 'setTheme', theme, opt); - - // Update the default theme on the view prototype. - View.prototype.defaultTheme = theme; - }; - - var LayersNames = { - CELLS: 'cells', - BACK: 'back', - FRONT: 'front', - TOOLS: 'tools', - LABELS: 'labels' - }; - - var PaperLayer = View.extend({ - - tagName: 'g', - svgElement: true, - pivotNodes: null, - defaultTheme: null, - - options: { - name: '' - }, - - className: function() { - return addClassNamePrefix(((this.options.name) + "-layer")); - }, - - init: function() { - this.pivotNodes = {}; - }, - - insertSortedNode: function(node, z) { - this.el.insertBefore(node, this.insertPivot(z)); - }, - - insertNode: function(node) { - var ref = this; - var el = ref.el; - if (node.parentNode !== el) { - el.appendChild(node); - } - }, - - insertPivot: function(z) { - var ref = this; - var el = ref.el; - var pivotNodes = ref.pivotNodes; - z = +z; - z || (z = 0); - var pivotNode = pivotNodes[z]; - if (pivotNode) { return pivotNode; } - pivotNode = pivotNodes[z] = document.createComment('z-index:' + (z + 1)); - var neighborZ = -Infinity; - for (var currentZ in pivotNodes) { - currentZ = +currentZ; - if (currentZ < z && currentZ > neighborZ) { - neighborZ = currentZ; - if (neighborZ === z - 1) { continue; } - } - } - if (neighborZ !== -Infinity) { - var neighborPivot = pivotNodes[neighborZ]; - // Insert After - el.insertBefore(pivotNode, neighborPivot.nextSibling); - } else { - // First Child - el.insertBefore(pivotNode, el.firstChild); - } - return pivotNode; - }, - - removePivots: function() { - var ref = this; - var el = ref.el; - var pivotNodes = ref.pivotNodes; - for (var z in pivotNodes) { el.removeChild(pivotNodes[z]); } - this.pivotNodes = {}; - } - - }); - - var HighlightingTypes = { - DEFAULT: 'default', - EMBEDDING: 'embedding', - CONNECTING: 'connecting', - MAGNET_AVAILABILITY: 'magnetAvailability', - ELEMENT_AVAILABILITY: 'elementAvailability' - }; - - // CellView base view and controller. - // -------------------------------------------- - - // This is the base view and controller for `ElementView` and `LinkView`. - var CellView = View.extend({ - - tagName: 'g', - - svgElement: true, - - selector: 'root', - - metrics: null, - - className: function() { - - var classNames = ['cell']; - var type = this.model.get('type'); - - if (type) { - - type.toLowerCase().split('.').forEach(function(value, index, list) { - classNames.push('type-' + list.slice(0, index + 1).join('-')); - }); - } - - return classNames.join(' '); - }, - - _presentationAttributes: null, - _flags: null, - - setFlags: function() { - var flags = {}; - var attributes = {}; - var shift = 0; - var i, n, label; - var presentationAttributes = result(this, 'presentationAttributes'); - for (var attribute in presentationAttributes) { - if (!presentationAttributes.hasOwnProperty(attribute)) { continue; } - var labels = presentationAttributes[attribute]; - if (!Array.isArray(labels)) { labels = [labels]; } - for (i = 0, n = labels.length; i < n; i++) { - label = labels[i]; - var flag = flags[label]; - if (!flag) { - flag = flags[label] = 1<<(shift++); - } - attributes[attribute] |= flag; - } - } - var initFlag = result(this, 'initFlag'); - if (!Array.isArray(initFlag)) { initFlag = [initFlag]; } - for (i = 0, n = initFlag.length; i < n; i++) { - label = initFlag[i]; - if (!flags[label]) { flags[label] = 1<<(shift++); } - } - - // 26 - 30 are reserved for paper flags - // 31+ overflows maximal number - if (shift > 25) { throw new Error('dia.CellView: Maximum number of flags exceeded.'); } - - this._flags = flags; - this._presentationAttributes = attributes; - }, - - hasFlag: function(flag, label) { - return flag & this.getFlag(label); - }, - - removeFlag: function(flag, label) { - return flag ^ (flag & this.getFlag(label)); - }, - - getFlag: function(label) { - var flags = this._flags; - if (!flags) { return 0; } - var flag = 0; - if (Array.isArray(label)) { - for (var i = 0, n = label.length; i < n; i++) { flag |= flags[label[i]]; } - } else { - flag |= flags[label]; - } - return flag; - }, - - attributes: function() { - var cell = this.model; - return { - 'model-id': cell.id, - 'data-type': cell.attributes.type - }; - }, - - constructor: function(options) { - - // Make sure a global unique id is assigned to this view. Store this id also to the properties object. - // The global unique id makes sure that the same view can be rendered on e.g. different machines and - // still be associated to the same object among all those clients. This is necessary for real-time - // collaboration mechanism. - options.id = options.id || guid(this); - - View.call(this, options); - }, - - initialize: function() { - - this.setFlags(); - - View.prototype.initialize.apply(this, arguments); - - this.cleanNodesCache(); - - // Store reference to this to the DOM element so that the view is accessible through the DOM tree. - this.$el.data('view', this); - - this.startListening(); - }, - - onMount: function onMount() { - // To be overridden - }, - - startListening: function() { - this.listenTo(this.model, 'change', this.onAttributesChange); - }, - - onAttributesChange: function(model, opt) { - var flag = model.getChangeFlag(this._presentationAttributes); - if (opt.updateHandled || !flag) { return; } - if (opt.dirty && this.hasFlag(flag, 'UPDATE')) { flag |= this.getFlag('RENDER'); } - // TODO: tool changes does not need to be sync - // Fix Segments tools - if (opt.tool) { opt.async = false; } - this.requestUpdate(flag, opt); - }, - - requestUpdate: function(flags, opt) { - var ref = this; - var paper = ref.paper; - if (paper && flags > 0) { - paper.requestViewUpdate(this, flags, this.UPDATE_PRIORITY, opt); - } - }, - - parseDOMJSON: function(markup, root) { - - var doc = parseDOMJSON(markup); - var selectors = doc.selectors; - var groups = doc.groupSelectors; - for (var group in groups) { - if (selectors[group]) { throw new Error('dia.CellView: ambiguous group selector'); } - selectors[group] = groups[group]; - } - if (root) { - var rootSelector = this.selector; - if (selectors[rootSelector]) { throw new Error('dia.CellView: ambiguous root selector.'); } - selectors[rootSelector] = root; - } - return { fragment: doc.fragment, selectors: selectors }; - }, - - // Return `true` if cell link is allowed to perform a certain UI `feature`. - // Example: `can('vertexMove')`, `can('labelMove')`. - can: function(feature) { - - var interactive = isFunction(this.options.interactive) - ? this.options.interactive(this) - : this.options.interactive; - - return (isObject$1(interactive) && interactive[feature] !== false) || - (isBoolean(interactive) && interactive !== false); - }, - - findBySelector: function(selector, root, selectors) { - - root || (root = this.el); - selectors || (selectors = this.selectors); - - // These are either descendants of `this.$el` of `this.$el` itself. - // `.` is a special selector used to select the wrapping `` element. - if (!selector || selector === '.') { return [root]; } - if (selectors) { - var nodes = selectors[selector]; - if (nodes) { - if (Array.isArray(nodes)) { return nodes; } - return [nodes]; - } - } - - // Maintaining backwards compatibility - // e.g. `circle:first` would fail with querySelector() call - if (config.useCSSSelectors) { return $(root).find(selector).toArray(); } - - return []; - }, - - notify: function(eventName) { - - if (this.paper) { - - var args = Array.prototype.slice.call(arguments, 1); - - // Trigger the event on both the element itself and also on the paper. - this.trigger.apply(this, [eventName].concat(args)); - - // Paper event handlers receive the view object as the first argument. - this.paper.trigger.apply(this.paper, [eventName, this].concat(args)); - } - }, - - getBBox: function(opt) { - - var bbox; - if (opt && opt.useModelGeometry) { - var model = this.model; - bbox = model.getBBox().bbox(model.angle()); - } else { - bbox = this.getNodeBBox(this.el); - } - - return this.paper.localToPaperRect(bbox); - }, - - getNodeBBox: function(magnet) { - - var rect = this.getNodeBoundingRect(magnet); - var magnetMatrix = this.getNodeMatrix(magnet); - var translateMatrix = this.getRootTranslateMatrix(); - var rotateMatrix = this.getRootRotateMatrix(); - return V.transformRect(rect, translateMatrix.multiply(rotateMatrix).multiply(magnetMatrix)); - }, - - getNodeUnrotatedBBox: function(magnet) { - - var rect = this.getNodeBoundingRect(magnet); - var magnetMatrix = this.getNodeMatrix(magnet); - var translateMatrix = this.getRootTranslateMatrix(); - return V.transformRect(rect, translateMatrix.multiply(magnetMatrix)); - }, - - getRootTranslateMatrix: function() { - - var model = this.model; - var position = model.position(); - var mt = V.createSVGMatrix().translate(position.x, position.y); - return mt; - }, - - getRootRotateMatrix: function() { - - var mr = V.createSVGMatrix(); - var model = this.model; - var angle = model.angle(); - if (angle) { - var bbox = model.getBBox(); - var cx = bbox.width / 2; - var cy = bbox.height / 2; - mr = mr.translate(cx, cy).rotate(angle).translate(-cx, -cy); - } - return mr; - }, - - _notifyHighlight: function(eventName, el, opt) { - var assign, assign$1; - - if ( opt === void 0 ) opt = {}; - var ref = this; - var rootNode = ref.el; - var node; - if (typeof el === 'string') { - (assign = this.findBySelector(el), node = assign[0], node = node === void 0 ? rootNode : node); - } else { - (assign$1 = this.$(el), node = assign$1[0], node = node === void 0 ? rootNode : node); - } - // set partial flag if the highlighted element is not the entire view. - opt.partial = (node !== rootNode); - // translate type flag into a type string - if (opt.type === undefined) { - var type; - switch (true) { - case opt.embedding: - type = HighlightingTypes.EMBEDDING; - break; - case opt.connecting: - type = HighlightingTypes.CONNECTING; - break; - case opt.magnetAvailability: - type = HighlightingTypes.MAGNET_AVAILABILITY; - break; - case opt.elementAvailability: - type = HighlightingTypes.ELEMENT_AVAILABILITY; - break; - default: - type = HighlightingTypes.DEFAULT; - break; - } - opt.type = type; - } - this.notify(eventName, node, opt); - return this; - }, - - highlight: function(el, opt) { - return this._notifyHighlight('cell:highlight', el, opt); - }, - - unhighlight: function(el, opt) { - if ( opt === void 0 ) opt = {}; - - return this._notifyHighlight('cell:unhighlight', el, opt); - }, - - // Find the closest element that has the `magnet` attribute set to `true`. If there was not such - // an element found, return the root element of the cell view. - findMagnet: function(el) { - - var root = this.el; - var magnet = this.$(el)[0]; - if (!magnet) { - magnet = root; - } - - do { - var magnetAttribute = magnet.getAttribute('magnet'); - var isMagnetRoot = (magnet === root); - if ((magnetAttribute || isMagnetRoot) && magnetAttribute !== 'false') { - return magnet; - } - if (isMagnetRoot) { - // If the overall cell has set `magnet === false`, then return `undefined` to - // announce there is no magnet found for this cell. - // This is especially useful to set on cells that have 'ports'. In this case, - // only the ports have set `magnet === true` and the overall element has `magnet === false`. - return undefined; - } - magnet = magnet.parentNode; - } while (magnet); - - return undefined; - }, - - findProxyNode: function(el, type) { - el || (el = this.el); - var nodeSelector = el.getAttribute((type + "-selector")); - if (nodeSelector) { - var ref = this.findBySelector(nodeSelector); - var proxyNode = ref[0]; - if (proxyNode) { return proxyNode; } - } - return el; - }, - - // Construct a unique selector for the `el` element within this view. - // `prevSelector` is being collected through the recursive call. - // No value for `prevSelector` is expected when using this method. - getSelector: function(el, prevSelector) { - - var selector; - - if (el === this.el) { - if (typeof prevSelector === 'string') { selector = '> ' + prevSelector; } - return selector; - } - - if (el) { - - var nthChild = V(el).index() + 1; - selector = el.tagName + ':nth-child(' + nthChild + ')'; - - if (prevSelector) { - selector += ' > ' + prevSelector; - } - - selector = this.getSelector(el.parentNode, selector); - } - - return selector; - }, - - addLinkFromMagnet: function(magnet, x, y) { - - var paper = this.paper; - var graph = paper.model; - - var link = paper.getDefaultLink(this, magnet); - link.set({ - source: this.getLinkEnd(magnet, x, y, link, 'source'), - target: { x: x, y: y } - }).addTo(graph, { - async: false, - ui: true - }); - - return link.findView(paper); - }, - - getLinkEnd: function(magnet) { - var ref; - - var args = [], len = arguments.length - 1; - while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; - - var model = this.model; - var id = model.id; - var port = this.findAttribute('port', magnet); - // Find a unique `selector` of the element under pointer that is a magnet. - var selector = magnet.getAttribute('joint-selector'); - - var end = { id: id }; - if (selector != null) { end.magnet = selector; } - if (port != null) { - end.port = port; - if (!model.hasPort(port) && !selector) { - // port created via the `port` attribute (not API) - end.selector = this.getSelector(magnet); - } - } else if (selector == null && this.el !== magnet) { - end.selector = this.getSelector(magnet); - } - - return (ref = this).customizeLinkEnd.apply(ref, [ end, magnet ].concat( args )); - }, - - customizeLinkEnd: function(end, magnet, x, y, link, endType) { - var ref = this; - var paper = ref.paper; - var ref$1 = paper.options; - var connectionStrategy = ref$1.connectionStrategy; - if (typeof connectionStrategy === 'function') { - var strategy = connectionStrategy.call(paper, end, this, magnet, new Point(x, y), link, endType, paper); - if (strategy) { return strategy; } - } - return end; - }, - - getMagnetFromLinkEnd: function(end) { - - var root = this.el; - var port = end.port; - var selector = end.magnet; - var model = this.model; - var magnet; - if (port != null && model.isElement() && model.hasPort(port)) { - magnet = this.findPortNode(port, selector) || root; - } else { - if (!selector) { selector = end.selector; } - if (!selector && port != null) { - // link end has only `id` and `port` property referencing - // a port created via the `port` attribute (not API). - selector = '[port="' + port + '"]'; - } - magnet = this.findBySelector(selector, root, this.selectors)[0]; - } - - return this.findProxyNode(magnet, 'magnet'); - }, - - dragLinkStart: function(evt, magnet, x, y) { - this.model.startBatch('add-link'); - var linkView = this.addLinkFromMagnet(magnet, x, y); - // backwards compatibility events - linkView.notifyPointerdown(evt, x, y); - linkView.eventData(evt, linkView.startArrowheadMove('target', { whenNotAllowed: 'remove' })); - this.eventData(evt, { linkView: linkView }); - }, - - dragLink: function(evt, x, y) { - var data = this.eventData(evt); - var linkView = data.linkView; - if (linkView) { - linkView.pointermove(evt, x, y); - } else { - var paper = this.paper; - var magnetThreshold = paper.options.magnetThreshold; - var currentTarget = this.getEventTarget(evt); - var targetMagnet = data.targetMagnet; - if (magnetThreshold === 'onleave') { - // magnetThreshold when the pointer leaves the magnet - if (targetMagnet === currentTarget || V(targetMagnet).contains(currentTarget)) { return; } - } else { - // magnetThreshold defined as a number of movements - if (paper.eventData(evt).mousemoved <= magnetThreshold) { return; } - } - this.dragLinkStart(evt, targetMagnet, x, y); - } - }, - - dragLinkEnd: function(evt, x, y) { - var data = this.eventData(evt); - var linkView = data.linkView; - if (!linkView) { return; } - linkView.pointerup(evt, x, y); - this.model.stopBatch('add-link'); - }, - - getAttributeDefinition: function(attrName) { - - return this.model.constructor.getAttributeDefinition(attrName); - }, - - setNodeAttributes: function(node, attrs) { - - if (!isEmpty(attrs)) { - if (node instanceof SVGElement) { - V(node).attr(attrs); - } else { - $(node).attr(attrs); - } - } - }, - - processNodeAttributes: function(node, attrs) { - - var attrName, attrVal, def, i, n; - var normalAttrs, setAttrs, positionAttrs, offsetAttrs; - var relatives = []; - // divide the attributes between normal and special - for (attrName in attrs) { - if (!attrs.hasOwnProperty(attrName)) { continue; } - attrVal = attrs[attrName]; - def = this.getAttributeDefinition(attrName); - if (def && (!isFunction(def.qualify) || def.qualify.call(this, attrVal, node, attrs))) { - if (isString(def.set)) { - normalAttrs || (normalAttrs = {}); - normalAttrs[def.set] = attrVal; - } - if (attrVal !== null) { - relatives.push(attrName, def); - } - } else { - normalAttrs || (normalAttrs = {}); - normalAttrs[toKebabCase(attrName)] = attrVal; - } - } - - // handle the rest of attributes via related method - // from the special attributes namespace. - for (i = 0, n = relatives.length; i < n; i+=2) { - attrName = relatives[i]; - def = relatives[i+1]; - attrVal = attrs[attrName]; - if (isFunction(def.set)) { - setAttrs || (setAttrs = {}); - setAttrs[attrName] = attrVal; - } - if (isFunction(def.position)) { - positionAttrs || (positionAttrs = {}); - positionAttrs[attrName] = attrVal; - } - if (isFunction(def.offset)) { - offsetAttrs || (offsetAttrs = {}); - offsetAttrs[attrName] = attrVal; - } - } - - return { - raw: attrs, - normal: normalAttrs, - set: setAttrs, - position: positionAttrs, - offset: offsetAttrs - }; - }, - - updateRelativeAttributes: function(node, attrs, refBBox, opt) { - - opt || (opt = {}); - - var attrName, attrVal, def; - var rawAttrs = attrs.raw || {}; - var nodeAttrs = attrs.normal || {}; - var setAttrs = attrs.set; - var positionAttrs = attrs.position; - var offsetAttrs = attrs.offset; - - for (attrName in setAttrs) { - attrVal = setAttrs[attrName]; - def = this.getAttributeDefinition(attrName); - // SET - set function should return attributes to be set on the node, - // which will affect the node dimensions based on the reference bounding - // box. e.g. `width`, `height`, `d`, `rx`, `ry`, `points - var setResult = def.set.call(this, attrVal, refBBox.clone(), node, rawAttrs); - if (isObject$1(setResult)) { - assign(nodeAttrs, setResult); - } else if (setResult !== undefined) { - nodeAttrs[attrName] = setResult; - } - } - - if (node instanceof HTMLElement) { - // TODO: setting the `transform` attribute on HTMLElements - // via `node.style.transform = 'matrix(...)';` would introduce - // a breaking change (e.g. basic.TextBlock). - this.setNodeAttributes(node, nodeAttrs); - return; - } - - // The final translation of the subelement. - var nodeTransform = nodeAttrs.transform; - var nodeMatrix = V.transformStringToMatrix(nodeTransform); - var nodePosition = Point(nodeMatrix.e, nodeMatrix.f); - if (nodeTransform) { - nodeAttrs = omit(nodeAttrs, 'transform'); - nodeMatrix.e = nodeMatrix.f = 0; - } - - // Calculate node scale determined by the scalable group - // only if later needed. - var sx, sy, translation; - if (positionAttrs || offsetAttrs) { - var nodeScale = this.getNodeScale(node, opt.scalableNode); - sx = nodeScale.sx; - sy = nodeScale.sy; - } - - var positioned = false; - for (attrName in positionAttrs) { - attrVal = positionAttrs[attrName]; - def = this.getAttributeDefinition(attrName); - // POSITION - position function should return a point from the - // reference bounding box. The default position of the node is x:0, y:0 of - // the reference bounding box or could be further specify by some - // SVG attributes e.g. `x`, `y` - translation = def.position.call(this, attrVal, refBBox.clone(), node, rawAttrs); - if (translation) { - nodePosition.offset(Point(translation).scale(sx, sy)); - positioned || (positioned = true); - } - } - - // The node bounding box could depend on the `size` set from the previous loop. - // Here we know, that all the size attributes have been already set. - this.setNodeAttributes(node, nodeAttrs); - - var offseted = false; - if (offsetAttrs) { - // Check if the node is visible - var nodeBoundingRect = this.getNodeBoundingRect(node); - if (nodeBoundingRect.width > 0 && nodeBoundingRect.height > 0) { - var nodeBBox = V.transformRect(nodeBoundingRect, nodeMatrix).scale(1 / sx, 1 / sy); - for (attrName in offsetAttrs) { - attrVal = offsetAttrs[attrName]; - def = this.getAttributeDefinition(attrName); - // OFFSET - offset function should return a point from the element - // bounding box. The default offset point is x:0, y:0 (origin) or could be further - // specify with some SVG attributes e.g. `text-anchor`, `cx`, `cy` - translation = def.offset.call(this, attrVal, nodeBBox, node, rawAttrs); - if (translation) { - nodePosition.offset(Point(translation).scale(sx, sy)); - offseted || (offseted = true); - } - } - } - } - - // Do not touch node's transform attribute if there is no transformation applied. - if (nodeTransform !== undefined || positioned || offseted) { - // Round the coordinates to 1 decimal point. - nodePosition.round(1); - nodeMatrix.e = nodePosition.x; - nodeMatrix.f = nodePosition.y; - node.setAttribute('transform', V.matrixToTransformString(nodeMatrix)); - // TODO: store nodeMatrix metrics? - } - }, - - getNodeScale: function(node, scalableNode) { - - // Check if the node is a descendant of the scalable group. - var sx, sy; - if (scalableNode && scalableNode.contains(node)) { - var scale = scalableNode.scale(); - sx = 1 / scale.sx; - sy = 1 / scale.sy; - } else { - sx = 1; - sy = 1; - } - - return { sx: sx, sy: sy }; - }, - - cleanNodesCache: function() { - this.metrics = {}; - }, - - nodeCache: function(magnet) { - - var metrics = this.metrics; - // Don't use cache? It most likely a custom view with overridden update. - if (!metrics) { return {}; } - var id = V.ensureId(magnet); - var value = metrics[id]; - if (!value) { value = metrics[id] = {}; } - return value; - }, - - getNodeData: function(magnet) { - - var metrics = this.nodeCache(magnet); - if (!metrics.data) { metrics.data = {}; } - return metrics.data; - }, - - getNodeBoundingRect: function(magnet) { - - var metrics = this.nodeCache(magnet); - if (metrics.boundingRect === undefined) { metrics.boundingRect = V(magnet).getBBox(); } - return new Rect(metrics.boundingRect); - }, - - getNodeMatrix: function(magnet) { - - var metrics = this.nodeCache(magnet); - if (metrics.magnetMatrix === undefined) { - var target = this.rotatableNode || this.el; - metrics.magnetMatrix = V(magnet).getTransformToElement(target); - } - return V.createSVGMatrix(metrics.magnetMatrix); - }, - - getNodeShape: function(magnet) { - - var metrics = this.nodeCache(magnet); - if (metrics.geometryShape === undefined) { metrics.geometryShape = V(magnet).toGeometryShape(); } - return metrics.geometryShape.clone(); - }, - - isNodeConnection: function(node) { - return this.model.isLink() && (!node || node === this.el); - }, - - findNodesAttributes: function(attrs, root, selectorCache, selectors) { - - var i, n, nodeAttrs, nodeId; - var nodesAttrs = {}; - var mergeIds = []; - for (var selector in attrs) { - if (!attrs.hasOwnProperty(selector)) { continue; } - nodeAttrs = attrs[selector]; - if (!isPlainObject(nodeAttrs)) { continue; } // Not a valid selector-attributes pair - var selected = selectorCache[selector] = this.findBySelector(selector, root, selectors); - for (i = 0, n = selected.length; i < n; i++) { - var node = selected[i]; - nodeId = V.ensureId(node); - // "unique" selectors are selectors that referencing a single node (defined by `selector`) - // groupSelector referencing a single node is not "unique" - var unique = (selectors && selectors[selector] === node); - var prevNodeAttrs = nodesAttrs[nodeId]; - if (prevNodeAttrs) { - // Note, that nodes referenced by deprecated `CSS selectors` are not taken into account. - // e.g. css:`.circle` and selector:`circle` can be applied in a random order - if (!prevNodeAttrs.array) { - mergeIds.push(nodeId); - prevNodeAttrs.array = true; - prevNodeAttrs.attributes = [prevNodeAttrs.attributes]; - prevNodeAttrs.selectedLength = [prevNodeAttrs.selectedLength]; - } - var attributes = prevNodeAttrs.attributes; - var selectedLength = prevNodeAttrs.selectedLength; - if (unique) { - // node referenced by `selector` - attributes.unshift(nodeAttrs); - selectedLength.unshift(-1); - } else { - // node referenced by `groupSelector` - var sortIndex = sortedIndex(selectedLength, n); - attributes.splice(sortIndex, 0, nodeAttrs); - selectedLength.splice(sortIndex, 0, n); - } - } else { - nodesAttrs[nodeId] = { - attributes: nodeAttrs, - selectedLength: unique ? -1 : n, - node: node, - array: false - }; - } - } - } - - for (i = 0, n = mergeIds.length; i < n; i++) { - nodeId = mergeIds[i]; - nodeAttrs = nodesAttrs[nodeId]; - nodeAttrs.attributes = merge.apply(void 0, [ {} ].concat( nodeAttrs.attributes.reverse() )); - } - - return nodesAttrs; - }, - - getEventTarget: function(evt, opt) { - if ( opt === void 0 ) opt = {}; - - // Touchmove/Touchend event's target is not reflecting the element under the coordinates as mousemove does. - // It holds the element when a touchstart triggered. - var target = evt.target; - var type = evt.type; - var clientX = evt.clientX; if ( clientX === void 0 ) clientX = 0; - var clientY = evt.clientY; if ( clientY === void 0 ) clientY = 0; - if (opt.fromPoint || type === 'touchmove' || type === 'touchend') { - return document.elementFromPoint(clientX, clientY); - } - - return target; - }, - - // Default is to process the `model.attributes.attrs` object and set attributes on subelements based on the selectors, - // unless `attrs` parameter was passed. - updateDOMSubtreeAttributes: function(rootNode, attrs, opt) { - - opt || (opt = {}); - opt.rootBBox || (opt.rootBBox = Rect()); - opt.selectors || (opt.selectors = this.selectors); // selector collection to use - - // Cache table for query results and bounding box calculation. - // Note that `selectorCache` needs to be invalidated for all - // `updateAttributes` calls, as the selectors might pointing - // to nodes designated by an attribute or elements dynamically - // created. - var selectorCache = {}; - var bboxCache = {}; - var relativeItems = []; - var relativeRefItems = []; - var item, node, nodeAttrs, nodeData, processedAttrs; - - var roAttrs = opt.roAttributes; - var nodesAttrs = this.findNodesAttributes(roAttrs || attrs, rootNode, selectorCache, opt.selectors); - // `nodesAttrs` are different from all attributes, when - // rendering only attributes sent to this method. - var nodesAllAttrs = (roAttrs) - ? this.findNodesAttributes(attrs, rootNode, selectorCache, opt.selectors) - : nodesAttrs; - - for (var nodeId in nodesAttrs) { - nodeData = nodesAttrs[nodeId]; - nodeAttrs = nodeData.attributes; - node = nodeData.node; - processedAttrs = this.processNodeAttributes(node, nodeAttrs); - - if (!processedAttrs.set && !processedAttrs.position && !processedAttrs.offset) { - // Set all the normal attributes right on the SVG/HTML element. - this.setNodeAttributes(node, processedAttrs.normal); - - } else { - - var nodeAllAttrs = nodesAllAttrs[nodeId] && nodesAllAttrs[nodeId].attributes; - var refSelector = (nodeAllAttrs && (nodeAttrs.ref === undefined)) - ? nodeAllAttrs.ref - : nodeAttrs.ref; - - var refNode; - if (refSelector) { - refNode = (selectorCache[refSelector] || this.findBySelector(refSelector, rootNode, opt.selectors))[0]; - if (!refNode) { - throw new Error('dia.CellView: "' + refSelector + '" reference does not exist.'); - } - } else { - refNode = null; - } + var oldVertices = toArray(vertices).map(Point); + var newVertices = []; + var tailPoint = sourceAnchor; // the origin of first route's grid, does not need snapping - item = { - node: node, - refNode: refNode, - processedAttributes: processedAttrs, - allAttributes: nodeAllAttrs - }; + // find a route by concatenating all partial routes (routes need to pass through vertices) + // source -> vertex[1] -> ... -> vertex[n] -> target + var to, from; - if (refNode) { - // If an element in the list is positioned relative to this one, then - // we want to insert this one before it in the list. - var itemIndex = relativeRefItems.findIndex(function(item) { - return item.refNode === node; - }); + for (var i = 0, len = oldVertices.length; i <= len; i++) { - if (itemIndex > -1) { - relativeRefItems.splice(itemIndex, 0, item); - } else { - relativeRefItems.push(item); - } - } else { - // A node with no ref attribute. To be updated before the nodes referencing other nodes. - // The order of no-ref-items is not specified/important. - relativeItems.push(item); - } - } - } + var partialRoute = null; - relativeItems.push.apply(relativeItems, relativeRefItems); + from = to || sourceBBox; + to = oldVertices[i]; - var rotatableMatrix; - for (var i = 0, n = relativeItems.length; i < n; i++) { - item = relativeItems[i]; - node = item.node; - refNode = item.refNode; + if (!to) { + // this is the last iteration + // we ran through all vertices in oldVertices + // 'to' is not a vertex. - // Find the reference element bounding box. If no reference was provided, we - // use the optional bounding box. - var vRotatable = V(opt.rotatableNode); - var refNodeId = refNode ? V.ensureId(refNode) : ''; - var isRefNodeRotatable = !!vRotatable && !!refNode && vRotatable.contains(refNode); - var unrotatedRefBBox = bboxCache[refNodeId]; - if (!unrotatedRefBBox) { - // Get the bounding box of the reference element relative to the `rotatable` `` (without rotation) - // or to the root `` element if no rotatable group present if reference node present. - // Uses the bounding box provided. - var transformationTarget = (isRefNodeRotatable) ? vRotatable : rootNode; - unrotatedRefBBox = bboxCache[refNodeId] = (refNode) - ? V(refNode).getBBox({ target: transformationTarget }) - : opt.rootBBox; - } + to = targetBBox; - if (roAttrs) { - // if there was a special attribute affecting the position amongst passed-in attributes - // we have to merge it with the rest of the element's attributes as they are necessary - // to update the position relatively (i.e `ref-x` && 'ref-dx') - processedAttrs = this.processNodeAttributes(node, item.allAttributes); - this.mergeProcessedAttributes(processedAttrs, item.processedAttributes); + // If the target is a point (i.e. it's not an element), we + // should use dragging route instead of main routing method if it has been provided. + var isEndingAtPoint = !linkView.model.get('source').id || !linkView.model.get('target').id; - } else { - processedAttrs = item.processedAttributes; - } + if (isEndingAtPoint && isFunction(opt.draggingRoute)) { + // Make sure we are passing points only (not rects). + var dragFrom = (from === sourceBBox) ? sourceAnchor : from; + var dragTo = to.origin(); - var refBBox = unrotatedRefBBox; - if (isRefNodeRotatable && !vRotatable.contains(node)) { - // if the referenced node is inside the rotatable group while the updated node is outside, - // we need to take the rotatable node transformation into account - if (!rotatableMatrix) { rotatableMatrix = V.transformStringToMatrix(vRotatable.attr('transform')); } - refBBox = V.transformRect(unrotatedRefBBox, rotatableMatrix); + partialRoute = opt.draggingRoute.call(linkView, dragFrom, dragTo, opt); } - - this.updateRelativeAttributes(node, processedAttrs, refBBox, opt); - } - }, - - mergeProcessedAttributes: function(processedAttrs, roProcessedAttrs) { - - processedAttrs.set || (processedAttrs.set = {}); - processedAttrs.position || (processedAttrs.position = {}); - processedAttrs.offset || (processedAttrs.offset = {}); - - assign(processedAttrs.set, roProcessedAttrs.set); - assign(processedAttrs.position, roProcessedAttrs.position); - assign(processedAttrs.offset, roProcessedAttrs.offset); - - // Handle also the special transform property. - var transform = processedAttrs.normal && processedAttrs.normal.transform; - if (transform !== undefined && roProcessedAttrs.normal) { - roProcessedAttrs.normal.transform = transform; - } - processedAttrs.normal = roProcessedAttrs.normal; - }, - - onRemove: function() { - this.removeTools(); - this.removeHighlighters(); - }, - - _toolsView: null, - - hasTools: function(name) { - var toolsView = this._toolsView; - if (!toolsView) { return false; } - if (!name) { return true; } - return (toolsView.getName() === name); - }, - - addTools: function(toolsView) { - - this.removeTools(); - - if (toolsView) { - this._toolsView = toolsView; - toolsView.configure({ relatedView: this }); - toolsView.listenTo(this.paper, 'tools:event', this.onToolEvent.bind(this)); - } - return this; - }, - - updateTools: function(opt) { - - var toolsView = this._toolsView; - if (toolsView) { toolsView.update(opt); } - return this; - }, - - removeTools: function() { - - var toolsView = this._toolsView; - if (toolsView) { - toolsView.remove(); - this._toolsView = null; } - return this; - }, - - hideTools: function() { - - var toolsView = this._toolsView; - if (toolsView) { toolsView.hide(); } - return this; - }, - showTools: function() { - - var toolsView = this._toolsView; - if (toolsView) { toolsView.show(); } - return this; - }, + // if partial route has not been calculated yet use the main routing method to find one + partialRoute = partialRoute || findRoute.call(linkView, from, to, isPointObstacle, opt); - onToolEvent: function(event) { - switch (event) { - case 'remove': - this.removeTools(); - break; - case 'hide': - this.hideTools(); - break; - case 'show': - this.showTools(); - break; + if (partialRoute === null) { // the partial route cannot be found + return opt.fallbackRouter(vertices, opt, linkView); } - }, - - removeHighlighters: function() { - HighlighterView.remove(this); - }, - - updateHighlighters: function(dirty) { - if ( dirty === void 0 ) dirty = false; - - HighlighterView.update(this, null, dirty); - }, - - transformHighlighters: function() { - HighlighterView.transform(this); - }, - // Interaction. The controller part. - // --------------------------------- - - // Interaction is handled by the paper and delegated to the view in interest. - // `x` & `y` parameters passed to these functions represent the coordinates already snapped to the paper grid. - // If necessary, real coordinates can be obtained from the `evt` event object. - - // These functions are supposed to be overriden by the views that inherit from `joint.dia.Cell`, - // i.e. `joint.dia.Element` and `joint.dia.Link`. - - pointerdblclick: function(evt, x, y) { - - this.notify('cell:pointerdblclick', evt, x, y); - }, - - pointerclick: function(evt, x, y) { - - this.notify('cell:pointerclick', evt, x, y); - }, - - contextmenu: function(evt, x, y) { - - this.notify('cell:contextmenu', evt, x, y); - }, - - pointerdown: function(evt, x, y) { - - var ref = this; - var model = ref.model; - var graph = model.graph; - if (graph) { - model.startBatch('pointer'); - this.eventData(evt, { graph: graph }); - } + var leadPoint = partialRoute[0]; - this.notify('cell:pointerdown', evt, x, y); - }, + // remove the first point if the previous partial route had the same point as last + if (leadPoint && leadPoint.equals(tailPoint)) { partialRoute.shift(); } - pointermove: function(evt, x, y) { + // save tailPoint for next iteration + tailPoint = partialRoute[partialRoute.length - 1] || tailPoint; - this.notify('cell:pointermove', evt, x, y); - }, + Array.prototype.push.apply(newVertices, partialRoute); + } - pointerup: function(evt, x, y) { + return newVertices; + } - var ref = this.eventData(evt); - var graph = ref.graph; + // public function + var manhattan = function(vertices, opt, linkView) { + return router(vertices, assign({}, config$1, opt), linkView); + }; - this.notify('cell:pointerup', evt, x, y); + var config$2 = { - if (graph) { - // we don't want to trigger event on model as model doesn't - // need to be member of collection anymore (remove) - graph.stopBatch('pointer', { cell: this.model }); - } - }, + maxAllowedDirectionChange: 45, - mouseover: function(evt) { + // cost of a diagonal step + diagonalCost: function() { - this.notify('cell:mouseover', evt); + var step = this.step; + return Math.ceil(Math.sqrt(step * step << 1)); }, - mouseout: function(evt) { - - this.notify('cell:mouseout', evt); - }, + // an array of directions to find next points on the route + // different from start/end directions + directions: function() { - mouseenter: function(evt) { + var step = this.step; + var cost = this.cost(); + var diagonalCost = this.diagonalCost(); - this.notify('cell:mouseenter', evt); + return [ + { offsetX: step, offsetY: 0, cost: cost }, + { offsetX: step, offsetY: step, cost: diagonalCost }, + { offsetX: 0, offsetY: step, cost: cost }, + { offsetX: -step, offsetY: step, cost: diagonalCost }, + { offsetX: -step, offsetY: 0, cost: cost }, + { offsetX: -step, offsetY: -step, cost: diagonalCost }, + { offsetX: 0, offsetY: -step, cost: cost }, + { offsetX: step, offsetY: -step, cost: diagonalCost } + ]; }, - mouseleave: function(evt) { + // a simple route used in situations when main routing method fails + // (exceed max number of loop iterations, inaccessible) + fallbackRoute: function(from, to, opt) { - this.notify('cell:mouseleave', evt); - }, + // Find a route which breaks by 45 degrees ignoring all obstacles. - mousewheel: function(evt, x, y, delta) { + var theta = from.theta(to); - this.notify('cell:mousewheel', evt, x, y, delta); - }, + var route = []; - onevent: function(evt, eventName, x, y) { + var a = { x: to.x, y: from.y }; + var b = { x: from.x, y: to.y }; - this.notify(eventName, evt, x, y); - }, + if (theta % 180 > 90) { + var t = a; + a = b; + b = t; + } - onmagnet: function() { + var p1 = (theta % 90) < 45 ? a : b; + var l1 = new Line(from, p1); - // noop - }, + var alpha = 90 * Math.ceil(theta / 90); - magnetpointerdblclick: function() { + var p2 = Point.fromPolar(l1.squaredLength(), toRad(alpha + 135), p1); + var l2 = new Line(to, p2); - // noop - }, + var intersectionPoint = l1.intersection(l2); + var point = intersectionPoint ? intersectionPoint : to; - magnetcontextmenu: function() { + var directionFrom = intersectionPoint ? point : from; - // noop - }, + var quadrant = 360 / opt.directions.length; + var angleTheta = directionFrom.theta(to); + var normalizedAngle = normalizeAngle(angleTheta + (quadrant / 2)); + var directionAngle = quadrant * Math.floor(normalizedAngle / quadrant); - checkMouseleave: function checkMouseleave(evt) { - var ref = this; - var paper = ref.paper; - if (paper.isAsync()) { - // Do the updates of the current view synchronously now - paper.dumpView(this); - } - var target = this.getEventTarget(evt, { fromPoint: true }); - var view = paper.findView(target); - if (view === this) { return; } - // Leaving the current view - this.mouseleave(evt); - if (!view) { return; } - // Entering another view - view.mouseenter(evt); - }, + opt.previousDirectionAngle = directionAngle; - setInteractivity: function(value) { + if (point) { route.push(point.round()); } + route.push(to); - this.options.interactive = value; + return route; } - }, { + }; - Highlighting: HighlightingTypes, + // public function + var metro = function(vertices, opt, linkView) { - addPresentationAttributes: function(presentationAttributes) { - return merge({}, result(this.prototype, 'presentationAttributes'), presentationAttributes, function(a, b) { - if (!a || !b) { return; } - if (typeof a === 'string') { a = [a]; } - if (typeof b === 'string') { b = [b]; } - if (Array.isArray(a) && Array.isArray(b)) { return uniq(a.concat(b)); } - }); + if (!isFunction(manhattan)) { + throw new Error('Metro requires the manhattan router.'); } - }); - var Flags = { - UPDATE: 'UPDATE', - TRANSLATE: 'TRANSLATE', - TOOLS: 'TOOLS', - RESIZE: 'RESIZE', - PORTS: 'PORTS', - ROTATE: 'ROTATE', - RENDER: 'RENDER' + return manhattan(vertices, assign({}, config$2, opt), linkView); }; - // Element base view and controller. - // ------------------------------------------- - - var ElementView = CellView.extend({ - - /** - * @abstract - */ - _removePorts: function() { - // implemented in ports.js - }, - /** - * - * @abstract - */ - _renderPorts: function() { - // implemented in ports.js - }, - className: function() { + var routers = ({ + normal: normal, + oneSide: oneSide, + orthogonal: orthogonal, + manhattan: manhattan, + metro: metro + }); - var classNames = CellView.prototype.className.apply(this).split(' '); + // default size of jump if not specified in options + var JUMP_SIZE = 5; - classNames.push('element'); + // available jump types + // first one taken as default + var JUMP_TYPES = ['arc', 'gap', 'cubic']; - return classNames.join(' '); - }, + // default radius + var RADIUS = 0; - initialize: function() { + // takes care of math. error for case when jump is too close to end of line + var CLOSE_PROXIMITY_PADDING = 1; - CellView.prototype.initialize.apply(this, arguments); + // list of connector types not to jump over. + var IGNORED_CONNECTORS = ['smooth']; - this._initializePorts(); - }, + // internal constants for round segment + var _13 = 1 / 3; + var _23 = 2 / 3; - presentationAttributes: { - 'attrs': [Flags.UPDATE], - 'position': [Flags.TRANSLATE, Flags.TOOLS], - 'size': [Flags.RESIZE, Flags.PORTS, Flags.TOOLS], - 'angle': [Flags.ROTATE, Flags.TOOLS], - 'markup': [Flags.RENDER], - 'ports': [Flags.PORTS], - }, + /** + * Transform start/end and route into series of lines + * @param {g.point} sourcePoint start point + * @param {g.point} targetPoint end point + * @param {g.point[]} route optional list of route + * @return {g.line[]} [description] + */ + function createLines(sourcePoint, targetPoint, route) { + // make a flattened array of all points + var points = [].concat(sourcePoint, route, targetPoint); + return points.reduce(function(resultLines, point, idx) { + // if there is a next point, make a line with it + var nextPoint = points[idx + 1]; + if (nextPoint != null) { + resultLines[idx] = line(point, nextPoint); + } + return resultLines; + }, []); + } - initFlag: [Flags.RENDER], + function setupUpdating(jumpOverLinkView) { + var paper = jumpOverLinkView.paper; + var updateList = paper._jumpOverUpdateList; - UPDATE_PRIORITY: 0, + // first time setup for this paper + if (updateList == null) { + updateList = paper._jumpOverUpdateList = []; + var graph = paper.model; + graph.on('batch:stop', function() { + if (this.hasActiveBatch()) { return; } + updateJumpOver(paper); + }); + graph.on('reset', function() { + updateList = paper._jumpOverUpdateList = []; + }); + } - confirmUpdate: function(flag, opt) { + // add this link to a list so it can be updated when some other link is updated + if (updateList.indexOf(jumpOverLinkView) < 0) { + updateList.push(jumpOverLinkView); - var useCSSSelectors = config.useCSSSelectors; - if (this.hasFlag(flag, Flags.PORTS)) { - this._removePorts(); - this._cleanPortsCache(); - } - var transformHighlighters = false; - if (this.hasFlag(flag, Flags.RENDER)) { - this.render(); - this.updateTools(opt); - this.updateHighlighters(true); - transformHighlighters = true; - flag = this.removeFlag(flag, [Flags.RENDER, Flags.UPDATE, Flags.RESIZE, Flags.TRANSLATE, Flags.ROTATE, Flags.PORTS, Flags.TOOLS]); - } else { - var updateHighlighters = false; + // watch for change of connector type or removal of link itself + // to remove the link from a list of jump over connectors + jumpOverLinkView.listenToOnce(jumpOverLinkView.model, 'change:connector remove', function() { + updateList.splice(updateList.indexOf(jumpOverLinkView), 1); + }); + } + } - // Skip this branch if render is required - if (this.hasFlag(flag, Flags.RESIZE)) { - this.resize(opt); - updateHighlighters = true; - // Resize method is calling `update()` internally - flag = this.removeFlag(flag, [Flags.RESIZE, Flags.UPDATE]); - } - if (this.hasFlag(flag, Flags.UPDATE)) { - this.update(this.model, null, opt); - flag = this.removeFlag(flag, Flags.UPDATE); - updateHighlighters = true; - if (useCSSSelectors) { - // `update()` will render ports when useCSSSelectors are enabled - flag = this.removeFlag(flag, Flags.PORTS); - } - } - if (this.hasFlag(flag, Flags.TRANSLATE)) { - this.translate(); - flag = this.removeFlag(flag, Flags.TRANSLATE); - transformHighlighters = true; - } - if (this.hasFlag(flag, Flags.ROTATE)) { - this.rotate(); - flag = this.removeFlag(flag, Flags.ROTATE); - transformHighlighters = true; - } - if (this.hasFlag(flag, Flags.PORTS)) { - this._renderPorts(); - updateHighlighters = true; - flag = this.removeFlag(flag, Flags.PORTS); - } + /** + * Handler for a batch:stop event to force + * update of all registered links with jump over connector + * @param {object} batchEvent optional object with info about batch + */ + function updateJumpOver(paper) { + var updateList = paper._jumpOverUpdateList; + for (var i = 0; i < updateList.length; i++) { + var linkView = updateList[i]; + var updateFlag = linkView.getFlag(linkView.constructor.Flags.CONNECTOR); + linkView.requestUpdate(updateFlag); + } + } - if (updateHighlighters) { - this.updateHighlighters(false); - } + /** + * Utility function to collect all intersection points of a single + * line against group of other lines. + * @param {g.line} line where to find points + * @param {g.line[]} crossCheckLines lines to cross + * @return {g.point[]} list of intersection points + */ + function findLineIntersections(line, crossCheckLines) { + return toArray(crossCheckLines).reduce(function(res, crossCheckLine) { + var intersection = line.intersection(crossCheckLine); + if (intersection) { + res.push(intersection); } + return res; + }, []); + } - if (transformHighlighters) { - this.transformHighlighters(); - } + /** + * Sorting function for list of points by their distance. + * @param {g.point} p1 first point + * @param {g.point} p2 second point + * @return {number} squared distance between points + */ + function sortPoints(p1, p2) { + return line(p1, p2).squaredLength(); + } - if (this.hasFlag(flag, Flags.TOOLS)) { - this.updateTools(opt); - flag = this.removeFlag(flag, Flags.TOOLS); + /** + * Split input line into multiple based on intersection points. + * @param {g.line} line input line to split + * @param {g.point[]} intersections points where to split the line + * @param {number} jumpSize the size of jump arc (length empty spot on a line) + * @return {g.line[]} list of lines being split + */ + function createJumps(line$1, intersections, jumpSize) { + return intersections.reduce(function(resultLines, point$1, idx) { + // skipping points that were merged with the previous line + // to make bigger arc over multiple lines that are close to each other + if (point$1.skip === true) { + return resultLines; } - return flag; - }, - - /** - * @abstract - */ - _initializePorts: function() { - - }, + // always grab the last line from buffer and modify it + var lastLine = resultLines.pop() || line$1; - update: function(_, renderingOnlyAttrs) { + // calculate start and end of jump by moving by a given size of jump + var jumpStart = point(point$1).move(lastLine.start, -(jumpSize)); + var jumpEnd = point(point$1).move(lastLine.start, +(jumpSize)); - this.cleanNodesCache(); + // now try to look at the next intersection point + var nextPoint = intersections[idx + 1]; + if (nextPoint != null) { + var distance = jumpEnd.distance(nextPoint); + if (distance <= jumpSize) { + // next point is close enough, move the jump end by this + // difference and mark the next point to be skipped + jumpEnd = nextPoint.move(lastLine.start, distance); + nextPoint.skip = true; + } + } else { + // this block is inside of `else` as an optimization so the distance is + // not calculated when we know there are no other intersection points + var endDistance = jumpStart.distance(lastLine.end); + // if the end is too close to possible jump, draw remaining line instead of a jump + if (endDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) { + resultLines.push(lastLine); + return resultLines; + } + } - // When CSS selector strings are used, make sure no rule matches port nodes. - var useCSSSelectors = config.useCSSSelectors; - if (useCSSSelectors) { this._removePorts(); } + var startDistance = jumpEnd.distance(lastLine.start); + if (startDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) { + // if the start of line is too close to jump, draw that line instead of a jump + resultLines.push(lastLine); + return resultLines; + } - var model = this.model; - var modelAttrs = model.attr(); - this.updateDOMSubtreeAttributes(this.el, modelAttrs, { - rootBBox: new Rect(model.size()), - selectors: this.selectors, - scalableNode: this.scalableNode, - rotatableNode: this.rotatableNode, - // Use rendering only attributes if they differs from the model attributes - roAttributes: (renderingOnlyAttrs === modelAttrs) ? null : renderingOnlyAttrs - }); + // finally create a jump line + var jumpLine = line(jumpStart, jumpEnd); + // it's just simple line but with a `isJump` property + jumpLine.isJump = true; - if (useCSSSelectors) { - this._renderPorts(); - } - }, + resultLines.push( + line(lastLine.start, jumpStart), + jumpLine, + line(jumpEnd, lastLine.end) + ); + return resultLines; + }, []); + } - rotatableSelector: 'rotatable', - scalableSelector: 'scalable', - scalableNode: null, - rotatableNode: null, + /** + * Assemble `D` attribute of a SVG path by iterating given lines. + * @param {g.line[]} lines source lines to use + * @param {number} jumpSize the size of jump arc (length empty spot on a line) + * @param {number} radius the radius + * @return {string} + */ + function buildPath(lines, jumpSize, jumpType, radius) { - // `prototype.markup` is rendered by default. Set the `markup` attribute on the model if the - // default markup is not desirable. - renderMarkup: function() { + var path = new Path(); + var segment; - var element = this.model; - var markup = element.get('markup') || element.markup; - if (!markup) { throw new Error('dia.ElementView: markup required'); } - if (Array.isArray(markup)) { return this.renderJSONMarkup(markup); } - if (typeof markup === 'string') { return this.renderStringMarkup(markup); } - throw new Error('dia.ElementView: invalid markup'); - }, + // first move to the start of a first line + segment = Path.createSegment('M', lines[0].start); + path.appendSegment(segment); - renderJSONMarkup: function(markup) { + // make a paths from lines + toArray(lines).forEach(function(line, index) { - var doc = this.parseDOMJSON(markup, this.el); - var selectors = this.selectors = doc.selectors; - this.rotatableNode = V(selectors[this.rotatableSelector]) || null; - this.scalableNode = V(selectors[this.scalableSelector]) || null; - // Fragment - this.vel.append(doc.fragment); - }, + if (line.isJump) { + var angle, diff; - renderStringMarkup: function(markup) { + var control1, control2; - var vel = this.vel; - vel.append(V(markup)); - // Cache transformation groups - this.rotatableNode = vel.findOne('.rotatable'); - this.scalableNode = vel.findOne('.scalable'); + if (jumpType === 'arc') { // approximates semicircle with 2 curves + angle = -90; + // determine rotation of arc based on difference between points + diff = line.start.difference(line.end); + // make sure the arc always points up (or right) + var xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0)); + if (xAxisRotate) { angle += 180; } - var selectors = this.selectors = {}; - selectors[this.selector] = this.el; - }, + var midpoint = line.midpoint(); + var centerLine = new Line(midpoint, line.end).rotate(midpoint, angle); - render: function() { + var halfLine; - this.vel.empty(); - this.renderMarkup(); - if (this.scalableNode) { - // Double update is necessary for elements with the scalable group only - // Note the resize() triggers the other `update`. - this.update(); - } - this.resize(); - if (this.rotatableNode) { - // Translate transformation is applied on `this.el` while the rotation transformation - // on `this.rotatableNode` - this.rotate(); - this.translate(); - } else { - this.updateTransformation(); - } - if (!config.useCSSSelectors) { this._renderPorts(); } - return this; - }, + // first half + halfLine = new Line(line.start, midpoint); - resize: function(opt) { + control1 = halfLine.pointAt(2 / 3).rotate(line.start, angle); + control2 = centerLine.pointAt(1 / 3).rotate(centerLine.end, -angle); - if (this.scalableNode) { return this.sgResize(opt); } - if (this.model.attributes.angle) { this.rotate(); } - this.update(); - }, + segment = Path.createSegment('C', control1, control2, centerLine.end); + path.appendSegment(segment); - translate: function() { + // second half + halfLine = new Line(midpoint, line.end); - if (this.rotatableNode) { return this.rgTranslate(); } - this.updateTransformation(); - }, + control1 = centerLine.pointAt(1 / 3).rotate(centerLine.end, angle); + control2 = halfLine.pointAt(1 / 3).rotate(line.end, -angle); - rotate: function() { + segment = Path.createSegment('C', control1, control2, line.end); + path.appendSegment(segment); - if (this.rotatableNode) { - this.rgRotate(); - // It's necessary to call the update for the nodes outside - // the rotatable group referencing nodes inside the group - this.update(); - return; - } - this.updateTransformation(); - }, + } else if (jumpType === 'gap') { + segment = Path.createSegment('M', line.end); + path.appendSegment(segment); - updateTransformation: function() { + } else if (jumpType === 'cubic') { // approximates semicircle with 1 curve + angle = line.start.theta(line.end); - var transformation = this.getTranslateString(); - var rotateString = this.getRotateString(); - if (rotateString) { transformation += ' ' + rotateString; } - this.vel.attr('transform', transformation); - }, + var xOffset = jumpSize * 0.6; + var yOffset = jumpSize * 1.35; - getTranslateString: function() { + // determine rotation of arc based on difference between points + diff = line.start.difference(line.end); + // make sure the arc always points up (or right) + xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0)); + if (xAxisRotate) { yOffset *= -1; } - var position = this.model.attributes.position; - return 'translate(' + position.x + ',' + position.y + ')'; - }, + control1 = Point(line.start.x + xOffset, line.start.y + yOffset).rotate(line.start, angle); + control2 = Point(line.end.x - xOffset, line.end.y + yOffset).rotate(line.end, angle); - getRotateString: function() { - var attributes = this.model.attributes; - var angle = attributes.angle; - if (!angle) { return null; } - var size = attributes.size; - return 'rotate(' + angle + ',' + (size.width / 2) + ',' + (size.height / 2) + ')'; - }, + segment = Path.createSegment('C', control1, control2, line.end); + path.appendSegment(segment); + } - // Rotatable & Scalable Group - // always slower, kept mainly for backwards compatibility + } else { + var nextLine = lines[index + 1]; + if (radius == 0 || !nextLine || nextLine.isJump) { + segment = Path.createSegment('L', line.end); + path.appendSegment(segment); + } else { + buildRoundedSegment(radius, path, line.end, line.start, nextLine.end); + } + } + }); - rgRotate: function() { + return path; + } - this.rotatableNode.attr('transform', this.getRotateString()); - }, + function buildRoundedSegment(offset, path, curr, prev, next) { + var prevDistance = curr.distance(prev) / 2; + var nextDistance = curr.distance(next) / 2; - rgTranslate: function() { + var startMove = -Math.min(offset, prevDistance); + var endMove = -Math.min(offset, nextDistance); - this.vel.attr('transform', this.getTranslateString()); - }, + var roundedStart = curr.clone().move(prev, startMove).round(); + var roundedEnd = curr.clone().move(next, endMove).round(); - sgResize: function(opt) { + var control1 = new Point((_13 * roundedStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedStart.y)); + var control2 = new Point((_13 * roundedEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedEnd.y)); - var model = this.model; - var angle = model.angle(); - var size = model.size(); - var scalable = this.scalableNode; + var segment; + segment = Path.createSegment('L', roundedStart); + path.appendSegment(segment); - // Getting scalable group's bbox. - // Due to a bug in webkit's native SVG .getBBox implementation, the bbox of groups with path children includes the paths' control points. - // To work around the issue, we need to check whether there are any path elements inside the scalable group. - var recursive = false; - if (scalable.node.getElementsByTagName('path').length > 0) { - // If scalable has at least one descendant that is a path, we need to switch to recursive bbox calculation. - // If there are no path descendants, group bbox calculation works and so we can use the (faster) native function directly. - recursive = true; - } - var scalableBBox = scalable.getBBox({ recursive: recursive }); + segment = Path.createSegment('C', control1, control2, roundedEnd); + path.appendSegment(segment); + } - // Make sure `scalableBbox.width` and `scalableBbox.height` are not zero which can happen if the element does not have any content. By making - // the width/height 1, we prevent HTML errors of the type `scale(Infinity, Infinity)`. - var sx = (size.width / (scalableBBox.width || 1)); - var sy = (size.height / (scalableBBox.height || 1)); - scalable.attr('transform', 'scale(' + sx + ',' + sy + ')'); + /** + * Actual connector function that will be run on every update. + * @param {g.point} sourcePoint start point of this link + * @param {g.point} targetPoint end point of this link + * @param {g.point[]} route of this link + * @param {object} opt options + * @property {number} size optional size of a jump arc + * @return {string} created `D` attribute of SVG path + */ + var jumpover = function(sourcePoint, targetPoint, route, opt) { // eslint-disable-line max-params - // Now the interesting part. The goal is to be able to store the object geometry via just `x`, `y`, `angle`, `width` and `height` - // Order of transformations is significant but we want to reconstruct the object always in the order: - // resize(), rotate(), translate() no matter of how the object was transformed. For that to work, - // we must adjust the `x` and `y` coordinates of the object whenever we resize it (because the origin of the - // rotation changes). The new `x` and `y` coordinates are computed by canceling the previous rotation - // around the center of the resized object (which is a different origin then the origin of the previous rotation) - // and getting the top-left corner of the resulting object. Then we clean up the rotation back to what it originally was. + setupUpdating(this); - // Cancel the rotation but now around a different origin, which is the center of the scaled object. - var rotatable = this.rotatableNode; - var rotation = rotatable && rotatable.attr('transform'); - if (rotation) { + var raw = opt.raw; + var jumpSize = opt.size || JUMP_SIZE; + var jumpType = opt.jump && ('' + opt.jump).toLowerCase(); + var radius = opt.radius || RADIUS; + var ignoreConnectors = opt.ignoreConnectors || IGNORED_CONNECTORS; - rotatable.attr('transform', rotation + ' rotate(' + (-angle) + ',' + (size.width / 2) + ',' + (size.height / 2) + ')'); - var rotatableBBox = scalable.getBBox({ target: this.paper.cells }); + // grab the first jump type as a default if specified one is invalid + if (JUMP_TYPES.indexOf(jumpType) === -1) { + jumpType = JUMP_TYPES[0]; + } - // Store new x, y and perform rotate() again against the new rotation origin. - model.set('position', { x: rotatableBBox.x, y: rotatableBBox.y }, assign({ updateHandled: true }, opt)); - this.translate(); - this.rotate(); - } + var paper = this.paper; + var graph = paper.model; + var allLinks = graph.getLinks(); - // Update must always be called on non-rotated element. Otherwise, relative positioning - // would work with wrong (rotated) bounding boxes. - this.update(); - }, + // there is just one link, draw it directly + if (allLinks.length === 1) { + return buildPath( + createLines(sourcePoint, targetPoint, route), + jumpSize, jumpType, radius + ); + } - // Embedding mode methods. - // ----------------------- + var thisModel = this.model; + var thisIndex = allLinks.indexOf(thisModel); + var defaultConnector = paper.options.defaultConnector || {}; - prepareEmbedding: function(data) { - if ( data === void 0 ) data = {}; + // not all links are meant to be jumped over. + var links = allLinks.filter(function(link, idx) { + var connector = link.get('connector') || defaultConnector; - var element = data.model || this.model; - var paper = data.paper || this.paper; - var graph = paper.model; + // avoid jumping over links with connector type listed in `ignored connectors`. + if (toArray(ignoreConnectors).includes(connector.name)) { + return false; + } + // filter out links that are above this one and have the same connector type + // otherwise there would double hoops for each intersection + if (idx > thisIndex) { + return connector.name !== 'jumpover'; + } + return true; + }); - var initialZIndices = data.initialZIndices = {}; - var embeddedCells = element.getEmbeddedCells({ deep: true }); - var connectedLinks = graph.getConnectedLinks(element, { deep: true, includeEnclosed: true }); + // find views for all links + var linkViews = links.map(function(link) { + return paper.findViewByModel(link); + }); - // Note: an embedded cell can be a connect link, but it's fine - // to iterate over the cell twice. - [ - element ].concat( embeddedCells, - connectedLinks - ).forEach(function (cell) { return initialZIndices[cell.id] = cell.attributes.z; }); + // create lines for this link + var thisLines = createLines( + sourcePoint, + targetPoint, + route + ); - element.startBatch('to-front'); + // create lines for all other links + var linkLines = linkViews.map(function(linkView) { + if (linkView == null) { + return []; + } + if (linkView === this) { + return thisLines; + } + return createLines( + linkView.sourcePoint, + linkView.targetPoint, + linkView.route + ); + }, this); - // Bring the model to the front with all his embeds. - element.toFront({ deep: true, ui: true }); + // transform lines for this link by splitting with jump lines at + // points of intersection with other links + var jumpingLines = thisLines.reduce(function(resultLines, thisLine) { + // iterate all links and grab the intersections with this line + // these are then sorted by distance so the line can be split more easily - // Note that at this point cells in the collection are not sorted by z index (it's running in the batch, see - // the dia.Graph._sortOnChangeZ), so we can't assume that the last cell in the collection has the highest z. - var maxZ = graph.getElements().reduce(function (max, cell) { return Math.max(max, cell.attributes.z || 0); }, 0); + var intersections = links.reduce(function(res, link, i) { + // don't intersection with itself + if (link !== thisModel) { - // Move to front also all the inbound and outbound links that are connected - // to any of the element descendant. If we bring to front only embedded elements, - // links connected to them would stay in the background. - connectedLinks.forEach(function (link) { - if (link.attributes.z <= maxZ) { - link.set('z', maxZ + 1, { ui: true }); + var lineIntersections = findLineIntersections(thisLine, linkLines[i]); + res.push.apply(res, lineIntersections); } + return res; + }, []).sort(function(a, b) { + return sortPoints(thisLine.start, a) - sortPoints(thisLine.start, b); }); - element.stopBatch('to-front'); - - // Before we start looking for suitable parent we remove the current one. - var parentId = element.parent(); - if (parentId) { - var parent = graph.getCell(parentId); - parent.unembed(element, { ui: true }); - data.initialParentId = parentId; + if (intersections.length > 0) { + // split the line based on found intersection points + resultLines.push.apply(resultLines, createJumps(thisLine, intersections, jumpSize)); } else { - data.initialParentId = null; + // without any intersection the line goes uninterrupted + resultLines.push(thisLine); } - }, + return resultLines; + }, []); - processEmbedding: function(data, evt, x, y) { - if ( data === void 0 ) data = {}; + var path = buildPath(jumpingLines, jumpSize, jumpType, radius); + return (raw) ? path : path.serialize(); + }; + var normal$1 = function(sourcePoint, targetPoint, route, opt) { - var model = data.model || this.model; - var paper = data.paper || this.paper; - var graph = paper.model; - var ref = paper.options; - var findParentBy = ref.findParentBy; - var frontParentOnly = ref.frontParentOnly; - var validateEmbedding = ref.validateEmbedding; + var raw = opt && opt.raw; + var points = [sourcePoint].concat(route).concat([targetPoint]); - var candidates; - if (isFunction(findParentBy)) { - candidates = toArray(findParentBy.call(graph, this, evt, x, y)); - } else if (findParentBy === 'pointer') { - candidates = toArray(graph.findModelsFromPoint({ x: x, y: y })); - } else { - candidates = graph.findModelsUnderElement(model, { searchBy: findParentBy }); - } + var polyline = new Polyline(points); + var path = new Path(polyline); - candidates = candidates.filter(function (el) { - return (el instanceof Cell) && (model.id !== el.id) && !el.isEmbeddedIn(model); - }); + return (raw) ? path : path.serialize(); + }; - if (frontParentOnly) { - // pick the element with the highest `z` index - candidates = candidates.slice(-1); - } + var rounded = function(sourcePoint, targetPoint, route, opt) { - var newCandidateView = null; - var prevCandidateView = data.candidateEmbedView; + opt || (opt = {}); - // iterate over all candidates starting from the last one (has the highest z-index). - for (var i = candidates.length - 1; i >= 0; i--) { - var candidate = candidates[i]; - if (prevCandidateView && prevCandidateView.model.id == candidate.id) { - // candidate remains the same - newCandidateView = prevCandidateView; - break; - } else { - var view = candidate.findView(paper); - if (!isFunction(validateEmbedding) || validateEmbedding.call(paper, this, view)) { - // flip to the new candidate - newCandidateView = view; - break; - } - } - } + var offset = opt.radius || 10; + var raw = opt.raw; + var path = new Path(); + var segment; - if (newCandidateView && newCandidateView != prevCandidateView) { - // A new candidate view found. Highlight the new one. - this.clearEmbedding(data); - data.candidateEmbedView = newCandidateView.highlight( - newCandidateView.findProxyNode(null, 'container'), - { embedding: true } - ); - } + segment = Path.createSegment('M', sourcePoint); + path.appendSegment(segment); - if (!newCandidateView && prevCandidateView) { - // No candidate view found. Unhighlight the previous candidate. - this.clearEmbedding(data); - } - }, + var _13 = 1 / 3; + var _23 = 2 / 3; - clearEmbedding: function(data) { + var curr; + var prev, next; + var prevDistance, nextDistance; + var startMove, endMove; + var roundedStart, roundedEnd; + var control1, control2; - data || (data = {}); + for (var index = 0, n = route.length; index < n; index++) { - var candidateView = data.candidateEmbedView; - if (candidateView) { - // No candidate view found. Unhighlight the previous candidate. - candidateView.unhighlight( - candidateView.findProxyNode(null, 'container'), - { embedding: true } - ); - data.candidateEmbedView = null; - } - }, + curr = new Point(route[index]); - finalizeEmbedding: function(data) { - if ( data === void 0 ) data = {}; + prev = route[index - 1] || sourcePoint; + next = route[index + 1] || targetPoint; + prevDistance = nextDistance || (curr.distance(prev) / 2); + nextDistance = curr.distance(next) / 2; - var candidateView = data.candidateEmbedView; - var element = data.model || this.model; - var paper = data.paper || this.paper; + startMove = -Math.min(offset, prevDistance); + endMove = -Math.min(offset, nextDistance); - if (candidateView) { + roundedStart = curr.clone().move(prev, startMove).round(); + roundedEnd = curr.clone().move(next, endMove).round(); - // We finished embedding. Candidate view is chosen to become the parent of the model. - candidateView.model.embed(element, { ui: true }); - candidateView.unhighlight(candidateView.findProxyNode(null, 'container'), { embedding: true }); + control1 = new Point((_13 * roundedStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedStart.y)); + control2 = new Point((_13 * roundedEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedEnd.y)); - data.candidateEmbedView = null; + segment = Path.createSegment('L', roundedStart); + path.appendSegment(segment); - } else { + segment = Path.createSegment('C', control1, control2, roundedEnd); + path.appendSegment(segment); + } - var ref = paper.options; - var validateUnembedding = ref.validateUnembedding; - var initialParentId = data.initialParentId; - // The element was originally embedded into another element. - // The interaction would unembed the element. Let's validate - // if the element can be unembedded. - if ( - initialParentId && - typeof validateUnembedding === 'function' && - !validateUnembedding.call(paper, this) - ) { - this._disallowUnembed(data); - return; - } - } + segment = Path.createSegment('L', targetPoint); + path.appendSegment(segment); - paper.model.getConnectedLinks(element, { deep: true }).forEach(function (link) { - link.reparent({ ui: true }); - }); - }, + return (raw) ? path : path.serialize(); + }; - _disallowUnembed: function(data) { - var model = data.model; - var whenNotAllowed = data.whenNotAllowed; if ( whenNotAllowed === void 0 ) whenNotAllowed = 'revert'; - var element = model || this.model; - var paper = data.paper || this.paper; - var graph = paper.model; - switch (whenNotAllowed) { - case 'remove': { - element.remove({ ui: true }); - break; - } - case 'revert': { - var initialParentId = data.initialParentId; - var initialPosition = data.initialPosition; - var initialZIndices = data.initialZIndices; - // Revert the element's position (and the position of its embedded cells if any) - if (initialPosition) { - var x = initialPosition.x; - var y = initialPosition.y; - element.position(x, y, { deep: true, ui: true }); - } - // Revert all the z-indices changed during the embedding - if (initialZIndices) { - Object.keys(initialZIndices).forEach(function (id) { - var cell = graph.getCell(id); - if (cell) { - cell.set('z', initialZIndices[id], { ui: true }); - } - }); - } - // Revert the original parent - var parent = graph.getCell(initialParentId); - if (parent) { - parent.embed(element, { ui: true }); - } - break; - } - } - }, + var smooth = function(sourcePoint, targetPoint, route, opt) { - getDelegatedView: function() { + var raw = opt && opt.raw; + var path; - var view = this; - var model = view.model; - var paper = view.paper; + if (route && route.length !== 0) { - while (view) { - if (model.isLink()) { break; } - if (!model.isEmbedded() || view.can('stopDelegation')) { return view; } - model = model.getParentCell(); - view = paper.findViewByModel(model); - } + var points = [sourcePoint].concat(route).concat([targetPoint]); + var curves = Curve.throughPoints(points); - return null; - }, + path = new Path(curves); - findProxyNode: function(el, type) { - el || (el = this.el); - var nodeSelector = el.getAttribute((type + "-selector")); - if (nodeSelector) { - var port = this.findAttribute('port', el); - if (port) { - var proxyPortNode = this.findPortNode(port, nodeSelector); - if (proxyPortNode) { return proxyPortNode; } - } else { - var ref = this.findBySelector(nodeSelector); - var proxyNode = ref[0]; - if (proxyNode) { return proxyNode; } - } - } - return el; - }, + } else { + // if we have no route, use a default cubic bezier curve + // cubic bezier requires two control points + // the control points have `x` midway between source and target + // this produces an S-like curve - // Interaction. The controller part. - // --------------------------------- + path = new Path(); - notifyPointerdown: function notifyPointerdown(evt, x, y) { - CellView.prototype.pointerdown.call(this, evt, x, y); - this.notify('element:pointerdown', evt, x, y); - }, + var segment; - notifyPointermove: function notifyPointermove(evt, x, y) { - CellView.prototype.pointermove.call(this, evt, x, y); - this.notify('element:pointermove', evt, x, y); - }, + segment = Path.createSegment('M', sourcePoint); + path.appendSegment(segment); - notifyPointerup: function notifyPointerup(evt, x, y) { - this.notify('element:pointerup', evt, x, y); - CellView.prototype.pointerup.call(this, evt, x, y); - }, + if ((Math.abs(sourcePoint.x - targetPoint.x)) >= (Math.abs(sourcePoint.y - targetPoint.y))) { + var controlPointX = (sourcePoint.x + targetPoint.x) / 2; - pointerdblclick: function(evt, x, y) { + segment = Path.createSegment('C', controlPointX, sourcePoint.y, controlPointX, targetPoint.y, targetPoint.x, targetPoint.y); + path.appendSegment(segment); - CellView.prototype.pointerdblclick.apply(this, arguments); - this.notify('element:pointerdblclick', evt, x, y); - }, + } else { + var controlPointY = (sourcePoint.y + targetPoint.y) / 2; - pointerclick: function(evt, x, y) { + segment = Path.createSegment('C', sourcePoint.x, controlPointY, targetPoint.x, controlPointY, targetPoint.x, targetPoint.y); + path.appendSegment(segment); - CellView.prototype.pointerclick.apply(this, arguments); - this.notify('element:pointerclick', evt, x, y); - }, + } + } - contextmenu: function(evt, x, y) { + return (raw) ? path : path.serialize(); + }; - CellView.prototype.contextmenu.apply(this, arguments); - this.notify('element:contextmenu', evt, x, y); - }, + var Directions = { + AUTO: 'auto', + HORIZONTAL: 'horizontal', + VERTICAL: 'vertical', + CLOSEST_POINT: 'closest-point', + OUTWARDS: 'outwards' + }; - pointerdown: function(evt, x, y) { + var TangentDirections = { + UP: 'up', + DOWN: 'down', + LEFT: 'left', + RIGHT: 'right', + AUTO: 'auto', + CLOSEST_POINT: 'closest-point', + OUTWARDS: 'outwards' + }; - if (this.isPropagationStopped(evt)) { return; } + var curve = function(sourcePoint, targetPoint, route, opt, linkView) { + if ( route === void 0 ) route = []; + if ( opt === void 0 ) opt = {}; - this.notifyPointerdown(evt, x, y); - this.dragStart(evt, x, y); - }, + var raw = Boolean(opt.raw); + // distanceCoefficient - a coefficient of the tangent vector length relative to the distance between points. + // angleTangentCoefficient - a coefficient of the end tangents length in the case of angles larger than 45 degrees. + // tension - a Catmull-Rom curve tension parameter. + // sourceTangent - a tangent vector along the curve at the sourcePoint. + // sourceDirection - a unit direction vector along the curve at the sourcePoint. + // targetTangent - a tangent vector along the curve at the targetPoint. + // targetDirection - a unit direction vector along the curve at the targetPoint. + // precision - a rounding precision for path values. + var direction = opt.direction; if ( direction === void 0 ) direction = Directions.AUTO; + var precision = opt.precision; if ( precision === void 0 ) precision = 3; + var options = { + coeff: opt.distanceCoefficient || 0.6, + angleTangentCoefficient: opt.angleTangentCoefficient || 80, + tau: opt.tension || 0.5, + sourceTangent: opt.sourceTangent ? new Point(opt.sourceTangent) : null, + targetTangent: opt.targetTangent ? new Point(opt.targetTangent) : null + }; + if (typeof opt.sourceDirection === 'string') + { options.sourceDirection = opt.sourceDirection; } + else if (typeof opt.sourceDirection === 'number') + { options.sourceDirection = new Point(1, 0).rotate(null, opt.sourceDirection); } + else + { options.sourceDirection = opt.sourceDirection ? new Point(opt.sourceDirection).normalize() : null; } - pointermove: function(evt, x, y) { + if (typeof opt.targetDirection === 'string') + { options.targetDirection = opt.targetDirection; } + else if (typeof opt.targetDirection === 'number') + { options.targetDirection = new Point(1, 0).rotate(null, opt.targetDirection); } + else + { options.targetDirection = opt.targetDirection ? new Point(opt.targetDirection).normalize() : null; } - var data = this.eventData(evt); + var completeRoute = [sourcePoint ].concat( route.map(function (p) { return new Point(p); }), [targetPoint]); - switch (data.action) { - case 'magnet': - this.dragMagnet(evt, x, y); - break; - case 'move': - (data.delegatedView || this).drag(evt, x, y); - // eslint: no-fallthrough=false - default: - this.notifyPointermove(evt, x, y); - break; + // The calculation of a sourceTangent + var sourceTangent; + if (options.sourceTangent) { + sourceTangent = options.sourceTangent; + } else { + var sourceDirection = getSourceTangentDirection(linkView, completeRoute, direction, options); + var tangentLength = completeRoute[0].distance(completeRoute[1]) * options.coeff; + var pointsVector = completeRoute[1].difference(completeRoute[0]).normalize(); + var angle = angleBetweenVectors(sourceDirection, pointsVector); + if (angle > Math.PI / 4) { + var updatedLength = tangentLength + (angle - Math.PI / 4) * options.angleTangentCoefficient; + sourceTangent = sourceDirection.clone().scale(updatedLength, updatedLength); + } else { + sourceTangent = sourceDirection.clone().scale(tangentLength, tangentLength); } + } - // Make sure the element view data is passed along. - // It could have been wiped out in the handlers above. - this.eventData(evt, data); - }, - - pointerup: function(evt, x, y) { - - var data = this.eventData(evt); - switch (data.action) { - case 'magnet': - this.dragMagnetEnd(evt, x, y); - break; - case 'move': - (data.delegatedView || this).dragEnd(evt, x, y); - // eslint: no-fallthrough=false - default: - this.notifyPointerup(evt, x, y); + // The calculation of a targetTangent + var targetTangent; + if (options.targetTangent) { + targetTangent = options.targetTangent; + } else { + var targetDirection = getTargetTangentDirection(linkView, completeRoute, direction, options); + var last = completeRoute.length - 1; + var tangentLength$1 = completeRoute[last - 1].distance(completeRoute[last]) * options.coeff; + var pointsVector$1 = completeRoute[last - 1].difference(completeRoute[last]).normalize(); + var angle$1 = angleBetweenVectors(targetDirection, pointsVector$1); + if (angle$1 > Math.PI / 4) { + var updatedLength$1 = tangentLength$1 + (angle$1 - Math.PI / 4) * options.angleTangentCoefficient; + targetTangent = targetDirection.clone().scale(updatedLength$1, updatedLength$1); + } else { + targetTangent = targetDirection.clone().scale(tangentLength$1, tangentLength$1); } + } + + var catmullRomCurves = createCatmullRomCurves(completeRoute, sourceTangent, targetTangent, options); + var bezierCurves = catmullRomCurves.map(function (curve) { return catmullRomToBezier(curve, options); }); + var path = new Path(bezierCurves).round(precision); - var magnet = data.targetMagnet; - if (magnet) { this.magnetpointerclick(evt, magnet, x, y); } - - this.checkMouseleave(evt); - }, - - mouseover: function(evt) { - - CellView.prototype.mouseover.apply(this, arguments); - this.notify('element:mouseover', evt); - }, - - mouseout: function(evt) { - - CellView.prototype.mouseout.apply(this, arguments); - this.notify('element:mouseout', evt); - }, + return (raw) ? path : path.serialize(); + }; + curve.Directions = Directions; + curve.TangentDirections = TangentDirections; - mouseenter: function(evt) { + function getHorizontalSourceDirection(linkView, route, options) { + var sourceBBox = linkView.sourceBBox; - CellView.prototype.mouseenter.apply(this, arguments); - this.notify('element:mouseenter', evt); - }, + var sourceSide; + if (!sourceBBox.width || !sourceBBox.height) { + if (sourceBBox.x > route[1].x) + { sourceSide = 'right'; } + else + { sourceSide = 'left'; } + } else { + sourceSide = sourceBBox.sideNearestToPoint(route[0]); + } - mouseleave: function(evt) { + switch (sourceSide) { + case 'left': { + return new Point(-1, 0); + } + case 'right': + default: { + return new Point(1, 0); + } + } + } - CellView.prototype.mouseleave.apply(this, arguments); - this.notify('element:mouseleave', evt); - }, + function getHorizontalTargetDirection(linkView, route, options) { + var targetBBox = linkView.targetBBox; - mousewheel: function(evt, x, y, delta) { + var targetSide; + if (!targetBBox.width || !targetBBox.height) { + if (targetBBox.x > route[route.length - 2].x) + { targetSide = 'left'; } + else + { targetSide = 'right'; } + } else { + targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); + } - CellView.prototype.mousewheel.apply(this, arguments); - this.notify('element:mousewheel', evt, x, y, delta); - }, + switch (targetSide) { + case 'left': { + return new Point(-1, 0); + } + case 'right': + default: { + return new Point(1, 0); + } + } + } - onmagnet: function(evt, x, y) { + function getVerticalSourceDirection(linkView, route, options) { + var sourceBBox = linkView.sourceBBox; - this.dragMagnetStart(evt, x, y); - }, + var sourceSide; + if (!sourceBBox.width || !sourceBBox.height) { + if (sourceBBox.y > route[1].y) + { sourceSide = 'bottom'; } + else + { sourceSide = 'top'; } + } else { + sourceSide = sourceBBox.sideNearestToPoint(route[0]); + } - magnetpointerdblclick: function(evt, magnet, x, y) { + switch (sourceSide) { + case 'top': { + return new Point(0, -1); + } + case 'bottom': + default: { + return new Point(0, 1); + } + } + } - this.notify('element:magnet:pointerdblclick', evt, magnet, x, y); - }, + function getVerticalTargetDirection(linkView, route, options) { + var targetBBox = linkView.targetBBox; - magnetcontextmenu: function(evt, magnet, x, y) { + var targetSide; + if (!targetBBox.width || !targetBBox.height) { + if (targetBBox.y > route[route.length - 2].y) + { targetSide = 'top'; } + else + { targetSide = 'bottom'; } + } else { + targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); + } - this.notify('element:magnet:contextmenu', evt, magnet, x, y); - }, + switch (targetSide) { + case 'top': { + return new Point(0, -1); + } + case 'bottom': + default: { + return new Point(0, 1); + } + } + } - // Drag Start Handlers + function getAutoSourceDirection(linkView, route, options) { + var sourceBBox = linkView.sourceBBox; - dragStart: function(evt, x, y) { + var sourceSide; + if (!sourceBBox.width || !sourceBBox.height) { + sourceSide = sourceBBox.sideNearestToPoint(route[1]); + } else { + sourceSide = sourceBBox.sideNearestToPoint(route[0]); + } - var view = this.getDelegatedView(); - if (!view || !view.can('elementMove')) { return; } + switch (sourceSide) { + case 'top': + return new Point(0, -1); + case 'bottom': + return new Point(0, 1); + case 'right': + return new Point(1, 0); + case 'left': + return new Point(-1, 0); + } + } - this.eventData(evt, { - action: 'move', - delegatedView: view - }); + function getAutoTargetDirection(linkView, route, options) { + var targetBBox = linkView.targetBBox; + + var targetSide; + if (!targetBBox.width || !targetBBox.height) { + targetSide = targetBBox.sideNearestToPoint(route[route.length - 2]); + } else { + targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]); + } - var position = view.model.position(); - view.eventData(evt, { - initialPosition: position, - pointerOffset: position.difference(x, y), - restrictedArea: this.paper.getRestrictedArea(view, x, y) - }); - }, + switch (targetSide) { + case 'top': + return new Point(0, -1); + case 'bottom': + return new Point(0, 1); + case 'right': + return new Point(1, 0); + case 'left': + return new Point(-1, 0); + } + } - dragMagnetStart: function(evt, x, y) { + function getClosestPointSourceDirection(linkView, route, options) { + return route[1].difference(route[0]).normalize(); + } - if (!this.can('addLinkFromMagnet')) { return; } + function getClosestPointTargetDirection(linkView, route, options) { + var last = route.length - 1; + return route[last - 1].difference(route[last]).normalize(); + } - var magnet = evt.currentTarget; - var paper = this.paper; - this.eventData(evt, { targetMagnet: magnet }); - evt.stopPropagation(); + function getOutwardsSourceDirection(linkView, route, options) { + var sourceBBox = linkView.sourceBBox; + var sourceCenter = sourceBBox.center(); + return route[0].difference(sourceCenter).normalize(); + } - if (paper.options.validateMagnet(this, magnet, evt)) { + function getOutwardsTargetDirection(linkView, route, options) { + var targetBBox = linkView.targetBBox; + var targetCenter = targetBBox.center(); + return route[route.length - 1].difference(targetCenter).normalize(); + } - if (paper.options.magnetThreshold <= 0) { - this.dragLinkStart(evt, magnet, x, y); + function getSourceTangentDirection(linkView, route, direction, options) { + if (options.sourceDirection) { + switch(options.sourceDirection) { + case TangentDirections.UP: + return new Point(0, -1); + case TangentDirections.DOWN: + return new Point(0, 1); + case TangentDirections.LEFT: + return new Point(-1, 0); + case TangentDirections.RIGHT: + return new Point(0, 1); + case TangentDirections.AUTO: + return getAutoSourceDirection(linkView, route, options); + case TangentDirections.CLOSEST_POINT: + return getClosestPointSourceDirection(linkView, route, options); + case TangentDirections.OUTWARDS: { + return getOutwardsSourceDirection(linkView, route, options); } + default: + return options.sourceDirection; + } + } + + switch (direction) { + case Directions.HORIZONTAL: + return getHorizontalSourceDirection(linkView, route, options); + case Directions.VERTICAL: + return getVerticalSourceDirection(linkView, route, options); + case Directions.CLOSEST_POINT: + return getClosestPointSourceDirection(linkView, route, options); + case Directions.OUTWARDS: + return getOutwardsSourceDirection(linkView, route, options); + case Directions.AUTO: + default: + return getAutoSourceDirection(linkView, route, options); + } + } - this.eventData(evt, { action: 'magnet' }); - this.stopPropagation(evt); - - } else { - - this.pointerdown(evt, x, y); + function getTargetTangentDirection(linkView, route, direction, options) { + if (options.targetDirection) { + switch(options.targetDirection) { + case TangentDirections.UP: + return new Point(0, -1); + case TangentDirections.DOWN: + return new Point(0, 1); + case TangentDirections.LEFT: + return new Point(-1, 0); + case TangentDirections.RIGHT: + return new Point(0, 1); + case TangentDirections.AUTO: + return getAutoTargetDirection(linkView, route, options); + case TangentDirections.CLOSEST_POINT: + return getClosestPointTargetDirection(linkView, route, options); + case TangentDirections.OUTWARDS: { + return getOutwardsTargetDirection(linkView, route, options); + } + default: + return options.targetDirection; } + } + + switch (direction) { + case Directions.HORIZONTAL: + return getHorizontalTargetDirection(linkView, route, options); + case Directions.VERTICAL: + return getVerticalTargetDirection(linkView, route, options); + case Directions.CLOSEST_POINT: + return getClosestPointTargetDirection(linkView, route, options); + case Directions.OUTWARDS: + return getOutwardsTargetDirection(linkView, route, options); + case Directions.AUTO: + default: + return getAutoTargetDirection(linkView, route, options); + } + } - paper.delegateDragEvents(this, evt.data); - }, + function rotateVector(vector, angle) { + var cos = Math.cos(angle); + var sin = Math.sin(angle); + var x = cos * vector.x - sin * vector.y; + var y = sin * vector.x + cos * vector.y; + vector.x = x; + vector.y = y; + } - // Drag Handlers + function angleBetweenVectors(v1, v2) { + var cos = v1.dot(v2) / (v1.magnitude() * v2.magnitude()); + if (cos < -1) { cos = -1; } + if (cos > 1) { cos = 1; } + return Math.acos(cos); + } - drag: function(evt, x, y) { + function determinant(v1, v2) { + return v1.x * v2.y - v1.y * v2.x; + } - var paper = this.paper; - var grid = paper.options.gridSize; - var element = this.model; - var data = this.eventData(evt); - var pointerOffset = data.pointerOffset; - var restrictedArea = data.restrictedArea; - var embedding = data.embedding; + function createCatmullRomCurves(points, sourceTangent, targetTangent, options) { + var tau = options.tau; + var coeff = options.coeff; + var distances = []; + var tangents = []; + var catmullRomCurves = []; + var n = points.length - 1; - // Make sure the new element's position always snaps to the current grid - var elX = snapToGrid(x + pointerOffset.x, grid); - var elY = snapToGrid(y + pointerOffset.y, grid); + for (var i = 0; i < n; i++) { + distances[i] = points[i].distance(points[i + 1]); + } - element.position(elX, elY, { restrictedArea: restrictedArea, deep: true, ui: true }); + tangents[0] = sourceTangent; + tangents[n] = targetTangent; - if (paper.options.embeddingMode) { - if (!embedding) { - // Prepare the element for embedding only if the pointer moves. - // We don't want to do unnecessary action with the element - // if an user only clicks/dblclicks on it. - this.prepareEmbedding(data); - embedding = true; - } - this.processEmbedding(data, evt, x, y); + // The calculation of tangents of vertices + for (var i$1 = 1; i$1 < n; i$1++) { + var tpPrev = (void 0); + var tpNext = (void 0); + if (i$1 === 1) { + tpPrev = points[i$1 - 1].clone().offset(tangents[i$1 - 1].x, tangents[i$1 - 1].y); + } else { + tpPrev = points[i$1 - 1].clone(); + } + if (i$1 === n - 1) { + tpNext = points[i$1 + 1].clone().offset(tangents[i$1 + 1].x, tangents[i$1 + 1].y); + } else { + tpNext = points[i$1 + 1].clone(); } + var v1 = tpPrev.difference(points[i$1]).normalize(); + var v2 = tpNext.difference(points[i$1]).normalize(); + var vAngle = angleBetweenVectors(v1, v2); - this.eventData(evt, { - embedding: embedding - }); - }, + var rot = (Math.PI - vAngle) / 2; + var t = (void 0); + var vectorDeterminant = determinant(v1, v2); + var pointsDeterminant = (void 0); + pointsDeterminant = determinant(points[i$1].difference(points[i$1 + 1]), points[i$1].difference(points[i$1 - 1])); + if (vectorDeterminant < 0) { + rot = -rot; + } + if ((vAngle < Math.PI / 2) && ((rot < 0 && pointsDeterminant < 0) || (rot > 0 && pointsDeterminant > 0))) { + rot = rot - Math.PI; + } + t = v2.clone(); + rotateVector(t, rot); + + var t1 = t.clone(); + var t2 = t.clone(); + var scaleFactor1 = distances[i$1 - 1] * coeff; + var scaleFactor2 = distances[i$1] * coeff; + t1.scale(scaleFactor1, scaleFactor1); + t2.scale(scaleFactor2, scaleFactor2); - dragMagnet: function(evt, x, y) { - this.dragLink(evt, x, y); - }, + tangents[i$1] = [t1, t2]; + } - // Drag End Handlers + // The building of a Catmull-Rom curve based of tangents of points + for (var i$2 = 0; i$2 < n; i$2++) { + var p0 = (void 0); + var p3 = (void 0); + if (i$2 === 0) { + p0 = points[i$2 + 1].difference(tangents[i$2].x / tau, tangents[i$2].y / tau); + } else { + p0 = points[i$2 + 1].difference(tangents[i$2][1].x / tau, tangents[i$2][1].y / tau); + } + if (i$2 === n - 1) { + p3 = points[i$2].clone().offset(tangents[i$2 + 1].x / tau, tangents[i$2 + 1].y / tau); + } else { + p3 = points[i$2].difference(tangents[i$2 + 1][0].x / tau, tangents[i$2 + 1][0].y / tau); + } - dragEnd: function(evt, x, y) { + catmullRomCurves[i$2] = [p0, points[i$2], points[i$2 + 1], p3]; + } + return catmullRomCurves; + } - var data = this.eventData(evt); - if (data.embedding) { this.finalizeEmbedding(data); } - }, + // The function to convert Catmull-Rom curve to Bezier curve using the tension (tau) + function catmullRomToBezier(points, options) { + var tau = options.tau; - dragMagnetEnd: function(evt, x, y) { - this.dragLinkEnd(evt, x, y); - }, + var bcp1 = new Point(); + bcp1.x = points[1].x + (points[2].x - points[0].x) / (6 * tau); + bcp1.y = points[1].y + (points[2].y - points[0].y) / (6 * tau); - magnetpointerclick: function(evt, magnet, x, y) { - var paper = this.paper; - if (paper.eventData(evt).mousemoved > paper.options.clickThreshold) { return; } - this.notify('element:magnet:pointerclick', evt, magnet, x, y); - } + var bcp2 = new Point(); + bcp2.x = points[2].x + (points[3].x - points[1].x) / (6 * tau); + bcp2.y = points[2].y + (points[3].y - points[1].y) / (6 * tau); + return new Curve( + points[1], + bcp1, + bcp2, + points[2] + ); + } - }, { - Flags: Flags, - }); - assign(ElementView.prototype, elementViewPortPrototype); + var connectors = ({ + jumpover: jumpover, + normal: normal$1, + rounded: rounded, + smooth: smooth, + curve: curve + }); var Flags$1 = { RENDER: 'RENDER', @@ -26672,840 +24259,1649 @@ var joint = (function (exports, Backbone, _, $) { .rotate(angle); }, - getLabelCoordinates: function(labelPosition) { + getLabelCoordinates: function(labelPosition) { + + var transformationMatrix = this._getLabelTransformationMatrix(labelPosition); + return new Point(transformationMatrix.e, transformationMatrix.f); + }, + + getVertexIndex: function(x, y) { + + var model = this.model; + var vertices = model.vertices(); + + var vertexLength = this.getClosestPointLength(new Point(x, y)); + + var idx = 0; + for (var n = vertices.length; idx < n; idx++) { + var currentVertex = vertices[idx]; + var currentVertexLength = this.getClosestPointLength(currentVertex); + if (vertexLength < currentVertexLength) { break; } + } + + return idx; + }, + + // Interaction. The controller part. + // --------------------------------- + + notifyPointerdown: function notifyPointerdown(evt, x, y) { + CellView.prototype.pointerdown.call(this, evt, x, y); + this.notify('link:pointerdown', evt, x, y); + }, + + notifyPointermove: function notifyPointermove(evt, x, y) { + CellView.prototype.pointermove.call(this, evt, x, y); + this.notify('link:pointermove', evt, x, y); + }, + + notifyPointerup: function notifyPointerup(evt, x, y) { + this.notify('link:pointerup', evt, x, y); + CellView.prototype.pointerup.call(this, evt, x, y); + }, + + pointerdblclick: function(evt, x, y) { + + CellView.prototype.pointerdblclick.apply(this, arguments); + this.notify('link:pointerdblclick', evt, x, y); + }, + + pointerclick: function(evt, x, y) { + + CellView.prototype.pointerclick.apply(this, arguments); + this.notify('link:pointerclick', evt, x, y); + }, + + contextmenu: function(evt, x, y) { + + CellView.prototype.contextmenu.apply(this, arguments); + this.notify('link:contextmenu', evt, x, y); + }, + + pointerdown: function(evt, x, y) { + + this.notifyPointerdown(evt, x, y); + + // Backwards compatibility for the default markup + var className = evt.target.getAttribute('class'); + switch (className) { + + case 'marker-vertex': + this.dragVertexStart(evt, x, y); + return; + + case 'marker-vertex-remove': + case 'marker-vertex-remove-area': + this.dragVertexRemoveStart(evt, x, y); + return; + + case 'marker-arrowhead': + this.dragArrowheadStart(evt, x, y); + return; + + case 'connection': + case 'connection-wrap': + this.dragConnectionStart(evt, x, y); + return; + + case 'marker-source': + case 'marker-target': + return; + } + + this.dragStart(evt, x, y); + }, + + pointermove: function(evt, x, y) { + + // Backwards compatibility + var dragData = this._dragData; + if (dragData) { this.eventData(evt, dragData); } + + var data = this.eventData(evt); + switch (data.action) { + + case 'vertex-move': + this.dragVertex(evt, x, y); + break; + + case 'label-move': + this.dragLabel(evt, x, y); + break; + + case 'arrowhead-move': + this.dragArrowhead(evt, x, y); + break; + + case 'move': + this.drag(evt, x, y); + break; + } + + // Backwards compatibility + if (dragData) { assign(dragData, this.eventData(evt)); } + + this.notifyPointermove(evt, x, y); + }, + + pointerup: function(evt, x, y) { + + // Backwards compatibility + var dragData = this._dragData; + if (dragData) { + this.eventData(evt, dragData); + this._dragData = null; + } + + var data = this.eventData(evt); + switch (data.action) { + + case 'vertex-move': + this.dragVertexEnd(evt, x, y); + break; + + case 'label-move': + this.dragLabelEnd(evt, x, y); + break; + + case 'arrowhead-move': + this.dragArrowheadEnd(evt, x, y); + break; + + case 'move': + this.dragEnd(evt, x, y); + } + + this.notifyPointerup(evt, x, y); + this.checkMouseleave(evt); + }, + + mouseover: function(evt) { + + CellView.prototype.mouseover.apply(this, arguments); + this.notify('link:mouseover', evt); + }, + + mouseout: function(evt) { + + CellView.prototype.mouseout.apply(this, arguments); + this.notify('link:mouseout', evt); + }, + + mouseenter: function(evt) { + + CellView.prototype.mouseenter.apply(this, arguments); + this.notify('link:mouseenter', evt); + }, + + mouseleave: function(evt) { + + CellView.prototype.mouseleave.apply(this, arguments); + this.notify('link:mouseleave', evt); + }, + + mousewheel: function(evt, x, y, delta) { + + CellView.prototype.mousewheel.apply(this, arguments); + this.notify('link:mousewheel', evt, x, y, delta); + }, + + onevent: function(evt, eventName, x, y) { + + // Backwards compatibility + var linkTool = V(evt.target).findParentByClass('link-tool', this.el); + if (linkTool) { + // No further action to be executed + evt.stopPropagation(); + + // Allow `interactive.useLinkTools=false` + if (this.can('useLinkTools')) { + if (eventName === 'remove') { + // Built-in remove event + this.model.remove({ ui: true }); + // Do not trigger link pointerdown + return; + + } else { + // link:options and other custom events inside the link tools + this.notify(eventName, evt, x, y); + } + } + + this.notifyPointerdown(evt, x, y); + this.paper.delegateDragEvents(this, evt.data); + + } else { + CellView.prototype.onevent.apply(this, arguments); + } + }, + + onlabel: function(evt, x, y) { + + this.notifyPointerdown(evt, x, y); + + this.dragLabelStart(evt, x, y); + + var stopPropagation = this.eventData(evt).stopPropagation; + if (stopPropagation) { evt.stopPropagation(); } + }, + + // Drag Start Handlers + + dragConnectionStart: function(evt, x, y) { + + if (!this.can('vertexAdd')) { return; } + + // Store the index at which the new vertex has just been placed. + // We'll be update the very same vertex position in `pointermove()`. + var vertexIdx = this.addVertex({ x: x, y: y }, { ui: true }); + this.eventData(evt, { + action: 'vertex-move', + vertexIdx: vertexIdx + }); + }, + + dragLabelStart: function(evt, _x, _y) { + + if (this.can('labelMove')) { + + var labelNode = evt.currentTarget; + var labelIdx = parseInt(labelNode.getAttribute('label-idx'), 10); + + var positionAngle = this._getLabelPositionAngle(labelIdx); + var labelPositionArgs = this._getLabelPositionArgs(labelIdx); + var defaultLabelPositionArgs = this._getDefaultLabelPositionArgs(); + var positionArgs = this._mergeLabelPositionArgs(labelPositionArgs, defaultLabelPositionArgs); + + this.eventData(evt, { + action: 'label-move', + labelIdx: labelIdx, + positionAngle: positionAngle, + positionArgs: positionArgs, + stopPropagation: true + }); + + } else { + + // Backwards compatibility: + // If labels can't be dragged no default action is triggered. + this.eventData(evt, { stopPropagation: true }); + } + + this.paper.delegateDragEvents(this, evt.data); + }, + + dragVertexStart: function(evt, x, y) { + + if (!this.can('vertexMove')) { return; } + + var vertexNode = evt.target; + var vertexIdx = parseInt(vertexNode.getAttribute('idx'), 10); + this.eventData(evt, { + action: 'vertex-move', + vertexIdx: vertexIdx + }); + }, + + dragVertexRemoveStart: function(evt, x, y) { + + if (!this.can('vertexRemove')) { return; } + + var removeNode = evt.target; + var vertexIdx = parseInt(removeNode.getAttribute('idx'), 10); + this.model.removeVertex(vertexIdx); + }, + + dragArrowheadStart: function(evt, x, y) { + + if (!this.can('arrowheadMove')) { return; } + + var arrowheadNode = evt.target; + var arrowheadType = arrowheadNode.getAttribute('end'); + var data = this.startArrowheadMove(arrowheadType, { ignoreBackwardsCompatibility: true }); + + this.eventData(evt, data); + }, + + dragStart: function(evt, x, y) { + + if (!this.can('linkMove')) { return; } + + this.eventData(evt, { + action: 'move', + dx: x, + dy: y + }); + }, + + // Drag Handlers + dragLabel: function(evt, x, y) { + + var data = this.eventData(evt); + var label = { position: this.getLabelPosition(x, y, data.positionAngle, data.positionArgs) }; + if (this.paper.options.snapLabels) { delete label.position.offset; } + this.model.label(data.labelIdx, label); + }, + + dragVertex: function(evt, x, y) { + + var data = this.eventData(evt); + this.model.vertex(data.vertexIdx, { x: x, y: y }, { ui: true }); + }, + + dragArrowhead: function(evt, x, y) { + + if (this.paper.options.snapLinks) { + + this._snapArrowhead(evt, x, y); + + } else { + + this._connectArrowhead(this.getEventTarget(evt), x, y, this.eventData(evt)); + } + }, + + drag: function(evt, x, y) { + + var data = this.eventData(evt); + this.model.translate(x - data.dx, y - data.dy, { ui: true }); + this.eventData(evt, { + dx: x, + dy: y + }); + }, + + // Drag End Handlers + + dragLabelEnd: function() { + // noop + }, + + dragVertexEnd: function() { + // noop + }, + + dragArrowheadEnd: function(evt, x, y) { + + var data = this.eventData(evt); + var paper = this.paper; + + if (paper.options.snapLinks) { + this._snapArrowheadEnd(data); + } else { + this._connectArrowheadEnd(data, x, y); + } + + if (!paper.linkAllowed(this)) { + // If the changed link is not allowed, revert to its previous state. + this._disallow(data); + } else { + this._finishEmbedding(data); + this._notifyConnectEvent(data, evt); + } + + this._afterArrowheadMove(data); + }, + + dragEnd: function() { + // noop + }, + + _disallow: function(data) { + + switch (data.whenNotAllowed) { + + case 'remove': + this.model.remove({ ui: true }); + break; + + case 'revert': + default: + this.model.set(data.arrowhead, data.initialEnd, { ui: true }); + break; + } + }, + + _finishEmbedding: function(data) { + + // Reparent the link if embedding is enabled + if (this.paper.options.embeddingMode && this.model.reparent()) { + // Make sure we don't reverse to the original 'z' index (see afterArrowheadMove()). + data.z = null; + } + }, + + _notifyConnectEvent: function(data, evt) { + + var arrowhead = data.arrowhead; + var initialEnd = data.initialEnd; + var currentEnd = this.model.prop(arrowhead); + var endChanged = currentEnd && !Link.endsEqual(initialEnd, currentEnd); + if (endChanged) { + var paper = this.paper; + if (initialEnd.id) { + this.notify('link:disconnect', evt, paper.findViewByModel(initialEnd.id), data.initialMagnet, arrowhead); + } + if (currentEnd.id) { + this.notify('link:connect', evt, paper.findViewByModel(currentEnd.id), data.magnetUnderPointer, arrowhead); + } + } + }, + + _snapArrowhead: function(evt, x, y) { + + var ref = this; + var paper = ref.paper; + var ref$1 = paper.options; + var snapLinks = ref$1.snapLinks; + var connectionStrategy = ref$1.connectionStrategy; + var data = this.eventData(evt); + // checking view in close area of the pointer + + var r = snapLinks.radius || 50; + var viewsInArea = paper.findViewsInArea({ x: x - r, y: y - r, width: 2 * r, height: 2 * r }); + + var prevClosestView = data.closestView || null; + var prevClosestMagnet = data.closestMagnet || null; + var prevMagnetProxy = data.magnetProxy || null; + + data.closestView = data.closestMagnet = data.magnetProxy = null; + + var minDistance = Number.MAX_VALUE; + var pointer = new Point(x, y); + + viewsInArea.forEach(function(view) { + var candidates = []; + // skip connecting to the element in case '.': { magnet: false } attribute present + if (view.el.getAttribute('magnet') !== 'false') { + candidates.push({ + bbox: view.model.getBBox(), + magnet: view.el + }); + } + + view.$('[magnet]').toArray().forEach(function (magnet) { + candidates.push({ + bbox: view.getNodeBBox(magnet), + magnet: magnet + }); + }); + + candidates.forEach(function (candidate) { + var magnet = candidate.magnet; + var bbox = candidate.bbox; + // find distance from the center of the model to pointer coordinates + var distance = bbox.center().squaredDistance(pointer); + // the connection is looked up in a circle area by `distance < r` + if (distance < minDistance) { + var isAlreadyValidated = prevClosestMagnet === magnet; + if (isAlreadyValidated || paper.options.validateConnection.apply( + paper, data.validateConnectionArgs(view, (view.el === magnet) ? null : magnet) + )) { + minDistance = distance; + data.closestView = view; + data.closestMagnet = magnet; + } + } + }); + + }, this); + + var end; + var magnetProxy = null; + var closestView = data.closestView; + var closestMagnet = data.closestMagnet; + if (closestMagnet) { + magnetProxy = data.magnetProxy = closestView.findProxyNode(closestMagnet, 'highlighter'); + } + var endType = data.arrowhead; + var newClosestMagnet = (prevClosestMagnet !== closestMagnet); + if (prevClosestView && newClosestMagnet) { + prevClosestView.unhighlight(prevMagnetProxy, { + connecting: true, + snapping: true + }); + } + + if (closestView) { + var prevEnd = data.prevEnd; + var prevX = data.prevX; + var prevY = data.prevY; + data.prevX = x; + data.prevY = y; + + if (!newClosestMagnet) { + if (typeof connectionStrategy !== 'function' || (prevX === x && prevY === y)) { + // the magnet has not changed and the link's end does not depend on the x and y + return; + } + } - var transformationMatrix = this._getLabelTransformationMatrix(labelPosition); - return new Point(transformationMatrix.e, transformationMatrix.f); - }, + end = closestView.getLinkEnd(closestMagnet, x, y, this.model, endType); + if (!newClosestMagnet && isEqual(prevEnd, end)) { + // the source/target json has not changed + return; + } - getVertexIndex: function(x, y) { + data.prevEnd = end; - var model = this.model; - var vertices = model.vertices(); + if (newClosestMagnet) { + closestView.highlight(magnetProxy, { + connecting: true, + snapping: true + }); + } - var vertexLength = this.getClosestPointLength(new Point(x, y)); + } else { - var idx = 0; - for (var n = vertices.length; idx < n; idx++) { - var currentVertex = vertices[idx]; - var currentVertexLength = this.getClosestPointLength(currentVertex); - if (vertexLength < currentVertexLength) { break; } + end = { x: x, y: y }; } - return idx; - }, - - // Interaction. The controller part. - // --------------------------------- + this.model.set(endType, end || { x: x, y: y }, { ui: true }); - notifyPointerdown: function notifyPointerdown(evt, x, y) { - CellView.prototype.pointerdown.call(this, evt, x, y); - this.notify('link:pointerdown', evt, x, y); + if (prevClosestView) { + this.notify('link:snap:disconnect', evt, prevClosestView, prevClosestMagnet, endType); + } + if (closestView) { + this.notify('link:snap:connect', evt, closestView, closestMagnet, endType); + } }, - notifyPointermove: function notifyPointermove(evt, x, y) { - CellView.prototype.pointermove.call(this, evt, x, y); - this.notify('link:pointermove', evt, x, y); - }, + _snapArrowheadEnd: function(data) { - notifyPointerup: function notifyPointerup(evt, x, y) { - this.notify('link:pointerup', evt, x, y); - CellView.prototype.pointerup.call(this, evt, x, y); - }, + // Finish off link snapping. + // Everything except view unhighlighting was already done on pointermove. + var closestView = data.closestView; + var closestMagnet = data.closestMagnet; + if (closestView && closestMagnet) { - pointerdblclick: function(evt, x, y) { + closestView.unhighlight(data.magnetProxy, { connecting: true, snapping: true }); + data.magnetUnderPointer = closestView.findMagnet(closestMagnet); + } - CellView.prototype.pointerdblclick.apply(this, arguments); - this.notify('link:pointerdblclick', evt, x, y); + data.closestView = data.closestMagnet = null; }, - pointerclick: function(evt, x, y) { + _connectArrowhead: function(target, x, y, data) { - CellView.prototype.pointerclick.apply(this, arguments); - this.notify('link:pointerclick', evt, x, y); - }, + // checking views right under the pointer + var ref = this; + var paper = ref.paper; + var model = ref.model; - contextmenu: function(evt, x, y) { + if (data.eventTarget !== target) { + // Unhighlight the previous view under pointer if there was one. + if (data.magnetProxy) { + data.viewUnderPointer.unhighlight(data.magnetProxy, { + connecting: true + }); + } - CellView.prototype.contextmenu.apply(this, arguments); - this.notify('link:contextmenu', evt, x, y); - }, + var viewUnderPointer = data.viewUnderPointer = paper.findView(target); + if (viewUnderPointer) { + // If we found a view that is under the pointer, we need to find the closest + // magnet based on the real target element of the event. + var magnetUnderPointer = data.magnetUnderPointer = viewUnderPointer.findMagnet(target); + var magnetProxy = data.magnetProxy = viewUnderPointer.findProxyNode(magnetUnderPointer, 'highlighter'); - pointerdown: function(evt, x, y) { + if (magnetUnderPointer && this.paper.options.validateConnection.apply( + paper, + data.validateConnectionArgs(viewUnderPointer, magnetUnderPointer) + )) { + // If there was no magnet found, do not highlight anything and assume there + // is no view under pointer we're interested in reconnecting to. + // This can only happen if the overall element has the attribute `'.': { magnet: false }`. + if (magnetProxy) { + viewUnderPointer.highlight(magnetProxy, { + connecting: true + }); + } + } else { + // This type of connection is not valid. Disregard this magnet. + data.magnetUnderPointer = null; + data.magnetProxy = null; + } + } else { + // Make sure we'll unset previous magnet. + data.magnetUnderPointer = null; + data.magnetProxy = null; + } + } - this.notifyPointerdown(evt, x, y); + data.eventTarget = target; - // Backwards compatibility for the default markup - var className = evt.target.getAttribute('class'); - switch (className) { + model.set(data.arrowhead, { x: x, y: y }, { ui: true }); + }, - case 'marker-vertex': - this.dragVertexStart(evt, x, y); - return; + _connectArrowheadEnd: function(data, x, y) { + if ( data === void 0 ) data = {}; - case 'marker-vertex-remove': - case 'marker-vertex-remove-area': - this.dragVertexRemoveStart(evt, x, y); - return; - case 'marker-arrowhead': - this.dragArrowheadStart(evt, x, y); - return; + var ref = this; + var model = ref.model; + var viewUnderPointer = data.viewUnderPointer; + var magnetUnderPointer = data.magnetUnderPointer; + var magnetProxy = data.magnetProxy; + var arrowhead = data.arrowhead; - case 'connection': - case 'connection-wrap': - this.dragConnectionStart(evt, x, y); - return; + if (!magnetUnderPointer || !magnetProxy || !viewUnderPointer) { return; } - case 'marker-source': - case 'marker-target': - return; - } + viewUnderPointer.unhighlight(magnetProxy, { connecting: true }); - this.dragStart(evt, x, y); + // The link end is taken from the magnet under the pointer, not the proxy. + var end = viewUnderPointer.getLinkEnd(magnetUnderPointer, x, y, model, arrowhead); + model.set(arrowhead, end, { ui: true }); }, - pointermove: function(evt, x, y) { - - // Backwards compatibility - var dragData = this._dragData; - if (dragData) { this.eventData(evt, dragData); } + _beforeArrowheadMove: function(data) { - var data = this.eventData(evt); - switch (data.action) { + data.z = this.model.get('z'); + this.model.toFront(); - case 'vertex-move': - this.dragVertex(evt, x, y); - break; + // Let the pointer propagate through the link view elements so that + // the `evt.target` is another element under the pointer, not the link itself. + var style = this.el.style; + data.pointerEvents = style.pointerEvents; + style.pointerEvents = 'none'; - case 'label-move': - this.dragLabel(evt, x, y); - break; + if (this.paper.options.markAvailable) { + this._markAvailableMagnets(data); + } + }, - case 'arrowhead-move': - this.dragArrowhead(evt, x, y); - break; + _afterArrowheadMove: function(data) { - case 'move': - this.drag(evt, x, y); - break; + if (data.z !== null) { + this.model.set('z', data.z, { ui: true }); + data.z = null; } - // Backwards compatibility - if (dragData) { assign(dragData, this.eventData(evt)); } + // Put `pointer-events` back to its original value. See `_beforeArrowheadMove()` for explanation. + this.el.style.pointerEvents = data.pointerEvents; - this.notifyPointermove(evt, x, y); + if (this.paper.options.markAvailable) { + this._unmarkAvailableMagnets(data); + } }, - pointerup: function(evt, x, y) { + _createValidateConnectionArgs: function(arrowhead) { + // It makes sure the arguments for validateConnection have the following form: + // (source view, source magnet, target view, target magnet and link view) + var args = []; - // Backwards compatibility - var dragData = this._dragData; - if (dragData) { - this.eventData(evt, dragData); - this._dragData = null; - } + args[4] = arrowhead; + args[5] = this; - var data = this.eventData(evt); - switch (data.action) { + var oppositeArrowhead; + var i = 0; + var j = 0; - case 'vertex-move': - this.dragVertexEnd(evt, x, y); - break; + if (arrowhead === 'source') { + i = 2; + oppositeArrowhead = 'target'; + } else { + j = 2; + oppositeArrowhead = 'source'; + } - case 'label-move': - this.dragLabelEnd(evt, x, y); - break; + var end = this.model.get(oppositeArrowhead); - case 'arrowhead-move': - this.dragArrowheadEnd(evt, x, y); - break; + if (end.id) { + var view = args[i] = this.paper.findViewByModel(end.id); + var magnet = view.getMagnetFromLinkEnd(end); + if (magnet === view.el) { magnet = undefined; } + args[i + 1] = magnet; + } - case 'move': - this.dragEnd(evt, x, y); + function validateConnectionArgs(cellView, magnet) { + args[j] = cellView; + args[j + 1] = cellView.el === magnet ? undefined : magnet; + return args; } - this.notifyPointerup(evt, x, y); - this.checkMouseleave(evt); + return validateConnectionArgs; }, - mouseover: function(evt) { - - CellView.prototype.mouseover.apply(this, arguments); - this.notify('link:mouseover', evt); - }, + _markAvailableMagnets: function(data) { - mouseout: function(evt) { + function isMagnetAvailable(view, magnet) { + var paper = view.paper; + var validate = paper.options.validateConnection; + return validate.apply(paper, this.validateConnectionArgs(view, magnet)); + } - CellView.prototype.mouseout.apply(this, arguments); - this.notify('link:mouseout', evt); - }, + var paper = this.paper; + var elements = paper.model.getCells(); + data.marked = {}; - mouseenter: function(evt) { + for (var i = 0, n = elements.length; i < n; i++) { + var view = elements[i].findView(paper); - CellView.prototype.mouseenter.apply(this, arguments); - this.notify('link:mouseenter', evt); - }, + if (!view) { + continue; + } - mouseleave: function(evt) { + var magnets = Array.prototype.slice.call(view.el.querySelectorAll('[magnet]')); + if (view.el.getAttribute('magnet') !== 'false') { + // Element wrapping group is also a magnet + magnets.push(view.el); + } - CellView.prototype.mouseleave.apply(this, arguments); - this.notify('link:mouseleave', evt); - }, + var availableMagnets = magnets.filter(isMagnetAvailable.bind(data, view)); - mousewheel: function(evt, x, y, delta) { + if (availableMagnets.length > 0) { + // highlight all available magnets + for (var j = 0, m = availableMagnets.length; j < m; j++) { + view.highlight(availableMagnets[j], { magnetAvailability: true }); + } + // highlight the entire view + view.highlight(null, { elementAvailability: true }); - CellView.prototype.mousewheel.apply(this, arguments); - this.notify('link:mousewheel', evt, x, y, delta); + data.marked[view.model.id] = availableMagnets; + } + } }, - onevent: function(evt, eventName, x, y) { + _unmarkAvailableMagnets: function(data) { - // Backwards compatibility - var linkTool = V(evt.target).findParentByClass('link-tool', this.el); - if (linkTool) { - // No further action to be executed - evt.stopPropagation(); + var markedKeys = Object.keys(data.marked); + var id; + var markedMagnets; - // Allow `interactive.useLinkTools=false` - if (this.can('useLinkTools')) { - if (eventName === 'remove') { - // Built-in remove event - this.model.remove({ ui: true }); - // Do not trigger link pointerdown - return; + for (var i = 0, n = markedKeys.length; i < n; i++) { + id = markedKeys[i]; + markedMagnets = data.marked[id]; - } else { - // link:options and other custom events inside the link tools - this.notify(eventName, evt, x, y); + var view = this.paper.findViewByModel(id); + if (view) { + for (var j = 0, m = markedMagnets.length; j < m; j++) { + view.unhighlight(markedMagnets[j], { magnetAvailability: true }); } + view.unhighlight(null, { elementAvailability: true }); } - - this.notifyPointerdown(evt, x, y); - this.paper.delegateDragEvents(this, evt.data); - - } else { - CellView.prototype.onevent.apply(this, arguments); } + + data.marked = null; }, - onlabel: function(evt, x, y) { + startArrowheadMove: function(end, opt) { - this.notifyPointerdown(evt, x, y); + opt || (opt = {}); - this.dragLabelStart(evt, x, y); + // Allow to delegate events from an another view to this linkView in order to trigger arrowhead + // move without need to click on the actual arrowhead dom element. + var data = { + action: 'arrowhead-move', + arrowhead: end, + whenNotAllowed: opt.whenNotAllowed || 'revert', + initialMagnet: this[end + 'Magnet'] || (this[end + 'View'] ? this[end + 'View'].el : null), + initialEnd: clone(this.model.get(end)), + validateConnectionArgs: this._createValidateConnectionArgs(end) + }; - var stopPropagation = this.eventData(evt).stopPropagation; - if (stopPropagation) { evt.stopPropagation(); } - }, + this._beforeArrowheadMove(data); - // Drag Start Handlers + if (opt.ignoreBackwardsCompatibility !== true) { + this._dragData = data; + } - dragConnectionStart: function(evt, x, y) { + return data; + }, - if (!this.can('vertexAdd')) { return; } + onRemove: function() { + CellView.prototype.onRemove.apply(this, arguments); + this.unmountLabels(); + } - // Store the index at which the new vertex has just been placed. - // We'll be update the very same vertex position in `pointermove()`. - var vertexIdx = this.addVertex({ x: x, y: y }, { ui: true }); - this.eventData(evt, { - action: 'vertex-move', - vertexIdx: vertexIdx - }); - }, + }, { - dragLabelStart: function(evt, _x, _y) { + Flags: Flags$1, + }); - if (this.can('labelMove')) { + Object.defineProperty(LinkView.prototype, 'sourceBBox', { - var labelNode = evt.currentTarget; - var labelIdx = parseInt(labelNode.getAttribute('label-idx'), 10); + enumerable: true, - var positionAngle = this._getLabelPositionAngle(labelIdx); - var labelPositionArgs = this._getLabelPositionArgs(labelIdx); - var defaultLabelPositionArgs = this._getDefaultLabelPositionArgs(); - var positionArgs = this._mergeLabelPositionArgs(labelPositionArgs, defaultLabelPositionArgs); + get: function() { + var sourceView = this.sourceView; + if (!sourceView) { + var sourceDef = this.model.source(); + return new Rect(sourceDef.x, sourceDef.y); + } + var sourceMagnet = this.sourceMagnet; + if (sourceView.isNodeConnection(sourceMagnet)) { + return new Rect(this.sourceAnchor); + } + return sourceView.getNodeBBox(sourceMagnet || sourceView.el); + } - this.eventData(evt, { - action: 'label-move', - labelIdx: labelIdx, - positionAngle: positionAngle, - positionArgs: positionArgs, - stopPropagation: true - }); + }); - } else { + Object.defineProperty(LinkView.prototype, 'targetBBox', { - // Backwards compatibility: - // If labels can't be dragged no default action is triggered. - this.eventData(evt, { stopPropagation: true }); + enumerable: true, + + get: function() { + var targetView = this.targetView; + if (!targetView) { + var targetDef = this.model.target(); + return new Rect(targetDef.x, targetDef.y); + } + var targetMagnet = this.targetMagnet; + if (targetView.isNodeConnection(targetMagnet)) { + return new Rect(this.targetAnchor); } + return targetView.getNodeBBox(targetMagnet || targetView.el); + } + }); - this.paper.delegateDragEvents(this, evt.data); + var stroke = HighlighterView.extend({ + + tagName: 'path', + className: 'highlight-stroke', + attributes: { + 'pointer-events': 'none', + 'vector-effect': 'non-scaling-stroke', + 'fill': 'none' }, - dragVertexStart: function(evt, x, y) { + options: { + padding: 3, + rx: 0, + ry: 0, + useFirstSubpath: false, + attrs: { + 'stroke-width': 3, + 'stroke': '#FEB663' + } + }, - if (!this.can('vertexMove')) { return; } + getPathData: function getPathData(cellView, node) { + var ref = this; + var options = ref.options; + var useFirstSubpath = options.useFirstSubpath; + var d; + try { + var vNode = V(node); + d = vNode.convertToPathData().trim(); + if (vNode.tagName() === 'PATH' && useFirstSubpath) { + var secondSubpathIndex = d.search(/.M/i) + 1; + if (secondSubpathIndex > 0) { + d = d.substr(0, secondSubpathIndex); + } + } + } catch (error) { + // Failed to get path data from magnet element. + // Draw a rectangle around the node instead. + var nodeBBox = cellView.getNodeBoundingRect(node); + d = V.rectToPath(assign({}, options, nodeBBox.toJSON())); + } + return d; + }, - var vertexNode = evt.target; - var vertexIdx = parseInt(vertexNode.getAttribute('idx'), 10); - this.eventData(evt, { - action: 'vertex-move', - vertexIdx: vertexIdx + highlightConnection: function highlightConnection(cellView) { + this.vel.attr('d', cellView.getSerializedConnection()); + }, + + highlightNode: function highlightNode(cellView, node) { + var ref = this; + var vel = ref.vel; + var options = ref.options; + var padding = options.padding; + var layer = options.layer; + var highlightMatrix = cellView.getNodeMatrix(node); + // Add padding to the highlight element. + if (padding) { + if (!layer && node === cellView.el) { + // If the highlighter is appended to the cellView + // and we measure the size of the cellView wrapping group + // it's necessary to remove the highlighter first + vel.remove(); + } + var nodeBBox = cellView.getNodeBoundingRect(node); + var cx = nodeBBox.x + (nodeBBox.width / 2); + var cy = nodeBBox.y + (nodeBBox.height / 2); + nodeBBox = V.transformRect(nodeBBox, highlightMatrix); + var width = Math.max(nodeBBox.width, 1); + var height = Math.max(nodeBBox.height, 1); + var sx = (width + padding) / width; + var sy = (height + padding) / height; + var paddingMatrix = V.createSVGMatrix({ + a: sx, + b: 0, + c: 0, + d: sy, + e: cx - sx * cx, + f: cy - sy * cy + }); + highlightMatrix = highlightMatrix.multiply(paddingMatrix); + } + vel.attr({ + 'd': this.getPathData(cellView, node), + 'transform': V.matrixToTransformString(highlightMatrix) }); }, - dragVertexRemoveStart: function(evt, x, y) { - - if (!this.can('vertexRemove')) { return; } - - var removeNode = evt.target; - var vertexIdx = parseInt(removeNode.getAttribute('idx'), 10); - this.model.removeVertex(vertexIdx); - }, - - dragArrowheadStart: function(evt, x, y) { - - if (!this.can('arrowheadMove')) { return; } + highlight: function highlight(cellView, node) { + var ref = this; + var vel = ref.vel; + var options = ref.options; + vel.attr(options.attrs); + if (cellView.isNodeConnection(node)) { + this.highlightConnection(cellView); + } else { + this.highlightNode(cellView, node); + } + } - var arrowheadNode = evt.target; - var arrowheadType = arrowheadNode.getAttribute('end'); - var data = this.startArrowheadMove(arrowheadType, { ignoreBackwardsCompatibility: true }); + }); - this.eventData(evt, data); - }, + var MASK_CLIP = 20; - dragStart: function(evt, x, y) { + function forEachDescendant(vel, fn) { + var descendants = vel.children(); + while (descendants.length > 0) { + var descendant = descendants.shift(); + if (fn(descendant)) { + descendants.push.apply(descendants, descendant.children()); + } + } + } - if (!this.can('linkMove')) { return; } + var mask = HighlighterView.extend({ - this.eventData(evt, { - action: 'move', - dx: x, - dy: y - }); + tagName: 'rect', + className: 'highlight-mask', + attributes: { + 'pointer-events': 'none' }, - // Drag Handlers - dragLabel: function(evt, x, y) { - - var data = this.eventData(evt); - var label = { position: this.getLabelPosition(x, y, data.positionAngle, data.positionArgs) }; - if (this.paper.options.snapLabels) { delete label.position.offset; } - this.model.label(data.labelIdx, label); + options: { + padding: 3, + maskClip: MASK_CLIP, + deep: false, + attrs: { + 'stroke': '#FEB663', + 'stroke-width': 3, + 'stroke-linecap': 'butt', + 'stroke-linejoin': 'miter', + } }, - dragVertex: function(evt, x, y) { - - var data = this.eventData(evt); - this.model.vertex(data.vertexIdx, { x: x, y: y }, { ui: true }); - }, + VISIBLE: 'white', + INVISIBLE: 'black', - dragArrowhead: function(evt, x, y) { + MASK_ROOT_ATTRIBUTE_BLACKLIST: [ + 'marker-start', + 'marker-end', + 'marker-mid', + 'transform', + 'stroke-dasharray' + ], - if (this.paper.options.snapLinks) { + MASK_CHILD_ATTRIBUTE_BLACKLIST: [ + 'stroke', + 'fill', + 'stroke-width', + 'stroke-opacity', + 'stroke-dasharray', + 'fill-opacity', + 'marker-start', + 'marker-end', + 'marker-mid' + ], - this._snapArrowhead(evt, x, y); + // TODO: change the list to a function callback + MASK_REPLACE_TAGS: [ + 'FOREIGNOBJECT', + 'IMAGE', + 'USE', + 'TEXT', + 'TSPAN', + 'TEXTPATH' + ], - } else { + // TODO: change the list to a function callback + MASK_REMOVE_TAGS: [ + 'TEXT', + 'TSPAN', + 'TEXTPATH' + ], - this._connectArrowhead(this.getEventTarget(evt), x, y, this.eventData(evt)); + transformMaskChild: function transformMaskChild(cellView, childEl) { + var ref = this; + var MASK_CHILD_ATTRIBUTE_BLACKLIST = ref.MASK_CHILD_ATTRIBUTE_BLACKLIST; + var MASK_REPLACE_TAGS = ref.MASK_REPLACE_TAGS; + var MASK_REMOVE_TAGS = ref.MASK_REMOVE_TAGS; + var childTagName = childEl.tagName(); + // Do not include the element in the mask's image + if (!V.isSVGGraphicsElement(childEl) || MASK_REMOVE_TAGS.includes(childTagName)) { + childEl.remove(); + return false; } - }, - - drag: function(evt, x, y) { - - var data = this.eventData(evt); - this.model.translate(x - data.dx, y - data.dy, { ui: true }); - this.eventData(evt, { - dx: x, - dy: y + // Replace the element with a rectangle + if (MASK_REPLACE_TAGS.includes(childTagName)) { + // Note: clone() method does not change the children ids + var originalChild = cellView.vel.findOne(("#" + (childEl.id))); + if (originalChild) { + var originalNode = originalChild.node; + var childBBox = cellView.getNodeBoundingRect(originalNode); + if (cellView.model.isElement()) { + childBBox = V.transformRect(childBBox, cellView.getNodeMatrix(originalNode)); + } + var replacement = V('rect', childBBox.toJSON()); + var ref$1 = childBBox.center(); + var ox = ref$1.x; + var oy = ref$1.y; + var ref$2 = originalChild.rotate(); + var angle = ref$2.angle; + var cx = ref$2.cx; if ( cx === void 0 ) cx = ox; + var cy = ref$2.cy; if ( cy === void 0 ) cy = oy; + if (angle) { replacement.rotate(angle, cx, cy); } + // Note: it's not important to keep the same sibling index since all subnodes are filled + childEl.parent().append(replacement); + } + childEl.remove(); + return false; + } + // Keep the element, but clean it from certain attributes + MASK_CHILD_ATTRIBUTE_BLACKLIST.forEach(function (attrName) { + if (attrName === 'fill' && childEl.attr('fill') === 'none') { return; } + childEl.removeAttr(attrName); }); + return true; }, - // Drag End Handlers - - dragLabelEnd: function() { - // noop - }, - - dragVertexEnd: function() { - // noop + transformMaskRoot: function transformMaskRoot(_cellView, rootEl) { + var ref = this; + var MASK_ROOT_ATTRIBUTE_BLACKLIST = ref.MASK_ROOT_ATTRIBUTE_BLACKLIST; + MASK_ROOT_ATTRIBUTE_BLACKLIST.forEach(function (attrName) { + rootEl.removeAttr(attrName); + }); }, - dragArrowheadEnd: function(evt, x, y) { - - var data = this.eventData(evt); - var paper = this.paper; - - if (paper.options.snapLinks) { - this._snapArrowheadEnd(data); - } else { - this._connectArrowheadEnd(data, x, y); - } + getMaskShape: function getMaskShape(cellView, vel) { + var this$1 = this; - if (!paper.linkAllowed(this)) { - // If the changed link is not allowed, revert to its previous state. - this._disallow(data); + var ref = this; + var options = ref.options; + var MASK_REPLACE_TAGS = ref.MASK_REPLACE_TAGS; + var deep = options.deep; + var tagName = vel.tagName(); + var maskRoot; + if (tagName === 'G') { + if (!deep) { return null; } + maskRoot = vel.clone(); + forEachDescendant(maskRoot, function (maskChild) { return this$1.transformMaskChild(cellView, maskChild); }); } else { - this._finishEmbedding(data); - this._notifyConnectEvent(data, evt); + if (MASK_REPLACE_TAGS.includes(tagName)) { return null; } + maskRoot = vel.clone(); } - - this._afterArrowheadMove(data); + this.transformMaskRoot(cellView, maskRoot); + return maskRoot; }, - dragEnd: function() { - // noop + getMaskId: function getMaskId() { + return ("highlight-mask-" + (this.cid)); }, - _disallow: function(data) { - - switch (data.whenNotAllowed) { + getMask: function getMask(cellView, vNode) { - case 'remove': - this.model.remove({ ui: true }); - break; + var ref = this; + var VISIBLE = ref.VISIBLE; + var INVISIBLE = ref.INVISIBLE; + var options = ref.options; + var padding = options.padding; + var attrs = options.attrs; - case 'revert': - default: - this.model.set(data.arrowhead, data.initialEnd, { ui: true }); - break; + var strokeWidth = ('stroke-width' in attrs) ? attrs['stroke-width'] : 1; + var hasNodeFill = vNode.attr('fill') !== 'none'; + var magnetStrokeWidth = parseFloat(vNode.attr('stroke-width')); + if (isNaN(magnetStrokeWidth)) { magnetStrokeWidth = 1; } + // stroke of the invisible shape + var minStrokeWidth = magnetStrokeWidth + padding * 2; + // stroke of the visible shape + var maxStrokeWidth = minStrokeWidth + strokeWidth * 2; + var maskEl = this.getMaskShape(cellView, vNode); + if (!maskEl) { + var nodeBBox = cellView.getNodeBoundingRect(vNode.node); + // Make sure the rect is visible + nodeBBox.inflate(nodeBBox.width ? 0 : 0.5, nodeBBox.height ? 0 : 0.5); + maskEl = V('rect', nodeBBox.toJSON()); } + maskEl.attr(attrs); + return V('mask', { + 'id': this.getMaskId() + }).append([ + maskEl.clone().attr({ + 'fill': hasNodeFill ? VISIBLE : 'none', + 'stroke': VISIBLE, + 'stroke-width': maxStrokeWidth + }), + maskEl.clone().attr({ + 'fill': hasNodeFill ? INVISIBLE : 'none', + 'stroke': INVISIBLE, + 'stroke-width': minStrokeWidth + }) + ]); }, - _finishEmbedding: function(data) { - - // Reparent the link if embedding is enabled - if (this.paper.options.embeddingMode && this.model.reparent()) { - // Make sure we don't reverse to the original 'z' index (see afterArrowheadMove()). - data.z = null; + removeMask: function removeMask(paper) { + var maskNode = paper.svg.getElementById(this.getMaskId()); + if (maskNode) { + paper.defs.removeChild(maskNode); } }, - _notifyConnectEvent: function(data, evt) { - - var arrowhead = data.arrowhead; - var initialEnd = data.initialEnd; - var currentEnd = this.model.prop(arrowhead); - var endChanged = currentEnd && !Link.endsEqual(initialEnd, currentEnd); - if (endChanged) { - var paper = this.paper; - if (initialEnd.id) { - this.notify('link:disconnect', evt, paper.findViewByModel(initialEnd.id), data.initialMagnet, arrowhead); - } - if (currentEnd.id) { - this.notify('link:connect', evt, paper.findViewByModel(currentEnd.id), data.magnetUnderPointer, arrowhead); - } - } + addMask: function addMask(paper, maskEl) { + paper.defs.appendChild(maskEl.node); }, - _snapArrowhead: function(evt, x, y) { - + highlight: function highlight(cellView, node) { var ref = this; - var paper = ref.paper; - var ref$1 = paper.options; - var snapLinks = ref$1.snapLinks; - var connectionStrategy = ref$1.connectionStrategy; - var data = this.eventData(evt); - // checking view in close area of the pointer + var options = ref.options; + var vel = ref.vel; + var padding = options.padding; + var attrs = options.attrs; + var maskClip = options.maskClip; if ( maskClip === void 0 ) maskClip = MASK_CLIP; + var layer = options.layer; + var color = ('stroke' in attrs) ? attrs['stroke'] : '#000000'; + if (!layer && node === cellView.el) { + // If the highlighter is appended to the cellView + // and we measure the size of the cellView wrapping group + // it's necessary to remove the highlighter first + vel.remove(); + } + var highlighterBBox = cellView.getNodeBoundingRect(node).inflate(padding + maskClip); + var maskEl = this.getMask(cellView, V(node)); + this.addMask(cellView.paper, maskEl); + vel.attr(highlighterBBox.toJSON()); + vel.attr({ + 'transform': V.matrixToTransformString(cellView.getNodeMatrix(node)), + 'mask': ("url(#" + (maskEl.id) + ")"), + 'fill': color + }); + }, - var r = snapLinks.radius || 50; - var viewsInArea = paper.findViewsInArea({ x: x - r, y: y - r, width: 2 * r, height: 2 * r }); + unhighlight: function unhighlight(cellView) { + this.removeMask(cellView.paper); + } - var prevClosestView = data.closestView || null; - var prevClosestMagnet = data.closestMagnet || null; - var prevMagnetProxy = data.magnetProxy || null; + }); - data.closestView = data.closestMagnet = data.magnetProxy = null; + var opacity = HighlighterView.extend({ - var minDistance = Number.MAX_VALUE; - var pointer = new Point(x, y); + UPDATABLE: false, + MOUNTABLE: false, - viewsInArea.forEach(function(view) { - var candidates = []; - // skip connecting to the element in case '.': { magnet: false } attribute present - if (view.el.getAttribute('magnet') !== 'false') { - candidates.push({ - bbox: view.model.getBBox(), - magnet: view.el - }); - } + opacityClassName: addClassNamePrefix('highlight-opacity'), - view.$('[magnet]').toArray().forEach(function (magnet) { - candidates.push({ - bbox: view.getNodeBBox(magnet), - magnet: magnet - }); - }); + highlight: function(_cellView, node) { + V(node).addClass(this.opacityClassName); + }, - candidates.forEach(function (candidate) { - var magnet = candidate.magnet; - var bbox = candidate.bbox; - // find distance from the center of the model to pointer coordinates - var distance = bbox.center().squaredDistance(pointer); - // the connection is looked up in a circle area by `distance < r` - if (distance < minDistance) { - var isAlreadyValidated = prevClosestMagnet === magnet; - if (isAlreadyValidated || paper.options.validateConnection.apply( - paper, data.validateConnectionArgs(view, (view.el === magnet) ? null : magnet) - )) { - minDistance = distance; - data.closestView = view; - data.closestMagnet = magnet; - } - } - }); + unhighlight: function(_cellView, node) { + V(node).removeClass(this.opacityClassName); + } - }, this); + }); - var end; - var magnetProxy = null; - var closestView = data.closestView; - var closestMagnet = data.closestMagnet; - if (closestMagnet) { - magnetProxy = data.magnetProxy = closestView.findProxyNode(closestMagnet, 'highlighter'); - } - var endType = data.arrowhead; - var newClosestMagnet = (prevClosestMagnet !== closestMagnet); - if (prevClosestView && newClosestMagnet) { - prevClosestView.unhighlight(prevMagnetProxy, { - connecting: true, - snapping: true - }); - } + var className = addClassNamePrefix('highlighted'); - if (closestView) { - var prevEnd = data.prevEnd; - var prevX = data.prevX; - var prevY = data.prevY; - data.prevX = x; - data.prevY = y; + var addClass = HighlighterView.extend({ - if (!newClosestMagnet) { - if (typeof connectionStrategy !== 'function' || (prevX === x && prevY === y)) { - // the magnet has not changed and the link's end does not depend on the x and y - return; - } - } + UPDATABLE: false, + MOUNTABLE: false, - end = closestView.getLinkEnd(closestMagnet, x, y, this.model, endType); - if (!newClosestMagnet && isEqual(prevEnd, end)) { - // the source/target json has not changed - return; - } + options: { + className: className + }, - data.prevEnd = end; + highlight: function(_cellView, node) { + V(node).addClass(this.options.className); + }, - if (newClosestMagnet) { - closestView.highlight(magnetProxy, { - connecting: true, - snapping: true - }); - } + unhighlight: function(_cellView, node) { + V(node).removeClass(this.options.className); + } - } else { + }, { + // Backwards Compatibility + className: className + }); - end = { x: x, y: y }; - } - this.model.set(endType, end || { x: x, y: y }, { ui: true }); - if (prevClosestView) { - this.notify('link:snap:disconnect', evt, prevClosestView, prevClosestMagnet, endType); - } - if (closestView) { - this.notify('link:snap:connect', evt, closestView, closestMagnet, endType); - } - }, + var highlighters = ({ + stroke: stroke, + mask: mask, + opacity: opacity, + addClass: addClass + }); - _snapArrowheadEnd: function(data) { + function connectionRatio(view, _magnet, _refPoint, opt) { - // Finish off link snapping. - // Everything except view unhighlighting was already done on pointermove. - var closestView = data.closestView; - var closestMagnet = data.closestMagnet; - if (closestView && closestMagnet) { + var ratio = ('ratio' in opt) ? opt.ratio : 0.5; + return view.getPointAtRatio(ratio); + } - closestView.unhighlight(data.magnetProxy, { connecting: true, snapping: true }); - data.magnetUnderPointer = closestView.findMagnet(closestMagnet); - } + function connectionLength(view, _magnet, _refPoint, opt) { - data.closestView = data.closestMagnet = null; - }, + var length = ('length' in opt) ? opt.length : 20; + return view.getPointAtLength(length); + } - _connectArrowhead: function(target, x, y, data) { + function _connectionPerpendicular(view, _magnet, refPoint, opt) { - // checking views right under the pointer - var ref = this; - var paper = ref.paper; - var model = ref.model; + var OFFSET = 1e6; + var path = view.getConnection(); + var segmentSubdivisions = view.getConnectionSubdivisions(); + var verticalLine = new Line(refPoint.clone().offset(0, OFFSET), refPoint.clone().offset(0, -OFFSET)); + var horizontalLine = new Line(refPoint.clone().offset(OFFSET, 0), refPoint.clone().offset(-OFFSET, 0)); + var verticalIntersections = verticalLine.intersect(path, { segmentSubdivisions: segmentSubdivisions }); + var horizontalIntersections = horizontalLine.intersect(path, { segmentSubdivisions: segmentSubdivisions }); + var intersections = []; + if (verticalIntersections) { Array.prototype.push.apply(intersections, verticalIntersections); } + if (horizontalIntersections) { Array.prototype.push.apply(intersections, horizontalIntersections); } + if (intersections.length > 0) { return refPoint.chooseClosest(intersections); } + if ('fallbackAt' in opt) { + return getPointAtLink(view, opt.fallbackAt); + } + return connectionClosest(view, _magnet, refPoint, opt); + } - if (data.eventTarget !== target) { - // Unhighlight the previous view under pointer if there was one. - if (data.magnetProxy) { - data.viewUnderPointer.unhighlight(data.magnetProxy, { - connecting: true - }); - } + function _connectionClosest(view, _magnet, refPoint, _opt) { - var viewUnderPointer = data.viewUnderPointer = paper.findView(target); - if (viewUnderPointer) { - // If we found a view that is under the pointer, we need to find the closest - // magnet based on the real target element of the event. - var magnetUnderPointer = data.magnetUnderPointer = viewUnderPointer.findMagnet(target); - var magnetProxy = data.magnetProxy = viewUnderPointer.findProxyNode(magnetUnderPointer, 'highlighter'); + var closestPoint = view.getClosestPoint(refPoint); + if (!closestPoint) { return new Point(); } + return closestPoint; + } - if (magnetUnderPointer && this.paper.options.validateConnection.apply( - paper, - data.validateConnectionArgs(viewUnderPointer, magnetUnderPointer) - )) { - // If there was no magnet found, do not highlight anything and assume there - // is no view under pointer we're interested in reconnecting to. - // This can only happen if the overall element has the attribute `'.': { magnet: false }`. - if (magnetProxy) { - viewUnderPointer.highlight(magnetProxy, { - connecting: true - }); - } + function resolveRef(fn) { + return function(view, magnet, ref, opt) { + if (ref instanceof Element) { + var refView = this.paper.findView(ref); + var refPoint; + if (refView) { + if (refView.isNodeConnection(ref)) { + var distance = ('fixedAt' in opt) ? opt.fixedAt : '50%'; + refPoint = getPointAtLink(refView, distance); } else { - // This type of connection is not valid. Disregard this magnet. - data.magnetUnderPointer = null; - data.magnetProxy = null; + refPoint = refView.getNodeBBox(ref).center(); } } else { - // Make sure we'll unset previous magnet. - data.magnetUnderPointer = null; - data.magnetProxy = null; + // Something went wrong + refPoint = new Point(); } + return fn.call(this, view, magnet, refPoint, opt); } + return fn.apply(this, arguments); + }; + } - data.eventTarget = target; - - model.set(data.arrowhead, { x: x, y: y }, { ui: true }); - }, + function getPointAtLink(view, value) { + var parsedValue = parseFloat(value); + if (isPercentage(value)) { + return view.getPointAtRatio(parsedValue / 100); + } else { + return view.getPointAtLength(parsedValue); + } + } + var connectionPerpendicular = resolveRef(_connectionPerpendicular); + var connectionClosest = resolveRef(_connectionClosest); - _connectArrowheadEnd: function(data, x, y) { - if ( data === void 0 ) data = {}; + var linkAnchors = ({ + resolveRef: resolveRef, + connectionRatio: connectionRatio, + connectionLength: connectionLength, + connectionPerpendicular: connectionPerpendicular, + connectionClosest: connectionClosest + }); + function offsetPoint(p1, p2, offset) { + if (isPlainObject(offset)) { + var x = offset.x; + var y = offset.y; + if (isFinite(y)) { + var line = new Line(p2, p1); + var ref = line.parallel(y); + var start = ref.start; + var end = ref.end; + p2 = start; + p1 = end; + } + offset = x; + } + if (!isFinite(offset)) { return p1; } + var length = p1.distance(p2); + if (offset === 0 && length > 0) { return p1; } + return p1.move(p2, -Math.min(offset, length - 1)); + } - var ref = this; - var model = ref.model; - var viewUnderPointer = data.viewUnderPointer; - var magnetUnderPointer = data.magnetUnderPointer; - var magnetProxy = data.magnetProxy; - var arrowhead = data.arrowhead; + function stroke$1(magnet) { - if (!magnetUnderPointer || !magnetProxy || !viewUnderPointer) { return; } + var stroke = magnet.getAttribute('stroke-width'); + if (stroke === null) { return 0; } + return parseFloat(stroke) || 0; + } - viewUnderPointer.unhighlight(magnetProxy, { connecting: true }); + function alignLine(line, type, offset) { + if ( offset === void 0 ) offset = 0; - // The link end is taken from the magnet under the pointer, not the proxy. - var end = viewUnderPointer.getLinkEnd(magnetUnderPointer, x, y, model, arrowhead); - model.set(arrowhead, end, { ui: true }); - }, + var coordinate, a, b, direction; + var start = line.start; + var end = line.end; + switch (type) { + case 'left': + coordinate = 'x'; + a = end; + b = start; + direction = -1; + break; + case 'right': + coordinate = 'x'; + a = start; + b = end; + direction = 1; + break; + case 'top': + coordinate = 'y'; + a = end; + b = start; + direction = -1; + break; + case 'bottom': + coordinate = 'y'; + a = start; + b = end; + direction = 1; + break; + default: + return; + } + if (start[coordinate] < end[coordinate]) { + a[coordinate] = b[coordinate]; + } else { + b[coordinate] = a[coordinate]; + } + if (isFinite(offset)) { + a[coordinate] += direction * offset; + b[coordinate] += direction * offset; + } + } - _beforeArrowheadMove: function(data) { + // Connection Points - data.z = this.model.get('z'); - this.model.toFront(); + function anchorConnectionPoint(line, _view, _magnet, opt) { + var offset = opt.offset; + var alignOffset = opt.alignOffset; + var align = opt.align; + if (align) { alignLine(line, align, alignOffset); } + return offsetPoint(line.end, line.start, offset); + } - // Let the pointer propagate through the link view elements so that - // the `evt.target` is another element under the pointer, not the link itself. - var style = this.el.style; - data.pointerEvents = style.pointerEvents; - style.pointerEvents = 'none'; + function bboxIntersection(line, view, magnet, opt) { - if (this.paper.options.markAvailable) { - this._markAvailableMagnets(data); - } - }, + var bbox = view.getNodeBBox(magnet); + if (opt.stroke) { bbox.inflate(stroke$1(magnet) / 2); } + var intersections = line.intersect(bbox); + var cp = (intersections) + ? line.start.chooseClosest(intersections) + : line.end; + return offsetPoint(cp, line.start, opt.offset); + } - _afterArrowheadMove: function(data) { + function rectangleIntersection(line, view, magnet, opt) { - if (data.z !== null) { - this.model.set('z', data.z, { ui: true }); - data.z = null; - } + var angle = view.model.angle(); + if (angle === 0) { + return bboxIntersection(line, view, magnet, opt); + } - // Put `pointer-events` back to its original value. See `_beforeArrowheadMove()` for explanation. - this.el.style.pointerEvents = data.pointerEvents; + var bboxWORotation = view.getNodeUnrotatedBBox(magnet); + if (opt.stroke) { bboxWORotation.inflate(stroke$1(magnet) / 2); } + var center = bboxWORotation.center(); + var lineWORotation = line.clone().rotate(center, angle); + var intersections = lineWORotation.setLength(1e6).intersect(bboxWORotation); + var cp = (intersections) + ? lineWORotation.start.chooseClosest(intersections).rotate(center, -angle) + : line.end; + return offsetPoint(cp, line.start, opt.offset); + } - if (this.paper.options.markAvailable) { - this._unmarkAvailableMagnets(data); - } - }, + function findShapeNode(magnet) { + if (!magnet) { return null; } + var node = magnet; + do { + var tagName = node.tagName; + if (typeof tagName !== 'string') { return null; } + tagName = tagName.toUpperCase(); + if (tagName === 'G') { + node = node.firstElementChild; + } else if (tagName === 'TITLE') { + node = node.nextElementSibling; + } else { break; } + } while (node); + return node; + } - _createValidateConnectionArgs: function(arrowhead) { - // It makes sure the arguments for validateConnection have the following form: - // (source view, source magnet, target view, target magnet and link view) - var args = []; + var BNDR_SUBDIVISIONS = 'segmentSubdivisons'; + var BNDR_SHAPE_BBOX = 'shapeBBox'; - args[4] = arrowhead; - args[5] = this; + function boundaryIntersection(line, view, magnet, opt) { - var oppositeArrowhead; - var i = 0; - var j = 0; + var node, intersection; + var selector = opt.selector; + var anchor = line.end; - if (arrowhead === 'source') { - i = 2; - oppositeArrowhead = 'target'; - } else { - j = 2; - oppositeArrowhead = 'source'; - } + if (typeof selector === 'string') { + node = view.findBySelector(selector)[0]; + } else if (Array.isArray(selector)) { + node = getByPath(magnet, selector); + } else { + node = findShapeNode(magnet); + } - var end = this.model.get(oppositeArrowhead); + if (!V.isSVGGraphicsElement(node)) { + if (node === magnet || !V.isSVGGraphicsElement(magnet)) { return anchor; } + node = magnet; + } - if (end.id) { - var view = args[i] = this.paper.findViewByModel(end.id); - var magnet = view.getMagnetFromLinkEnd(end); - if (magnet === view.el) { magnet = undefined; } - args[i + 1] = magnet; - } + var localShape = view.getNodeShape(node); + var magnetMatrix = view.getNodeMatrix(node); + var translateMatrix = view.getRootTranslateMatrix(); + var rotateMatrix = view.getRootRotateMatrix(); + var targetMatrix = translateMatrix.multiply(rotateMatrix).multiply(magnetMatrix); + var localMatrix = targetMatrix.inverse(); + var localLine = V.transformLine(line, localMatrix); + var localRef = localLine.start.clone(); + var data = view.getNodeData(node); - function validateConnectionArgs(cellView, magnet) { - args[j] = cellView; - args[j + 1] = cellView.el === magnet ? undefined : magnet; - return args; - } + if (opt.insideout === false) { + if (!data[BNDR_SHAPE_BBOX]) { data[BNDR_SHAPE_BBOX] = localShape.bbox(); } + var localBBox = data[BNDR_SHAPE_BBOX]; + if (localBBox.containsPoint(localRef)) { return anchor; } + } - return validateConnectionArgs; - }, + // Caching segment subdivisions for paths + var pathOpt; + if (localShape instanceof Path) { + var precision = opt.precision || 2; + if (!data[BNDR_SUBDIVISIONS]) { data[BNDR_SUBDIVISIONS] = localShape.getSegmentSubdivisions({ precision: precision }); } + pathOpt = { + precision: precision, + segmentSubdivisions: data[BNDR_SUBDIVISIONS] + }; + } - _markAvailableMagnets: function(data) { + if (opt.extrapolate === true) { localLine.setLength(1e6); } - function isMagnetAvailable(view, magnet) { - var paper = view.paper; - var validate = paper.options.validateConnection; - return validate.apply(paper, this.validateConnectionArgs(view, magnet)); + intersection = localLine.intersect(localShape, pathOpt); + if (intersection) { + // More than one intersection + if (V.isArray(intersection)) { intersection = localRef.chooseClosest(intersection); } + } else if (opt.sticky === true) { + // No intersection, find the closest point instead + if (localShape instanceof Rect) { + intersection = localShape.pointNearestToPoint(localRef); + } else if (localShape instanceof Ellipse) { + intersection = localShape.intersectionWithLineFromCenterToPoint(localRef); + } else { + intersection = localShape.closestPoint(localRef, pathOpt); } + } - var paper = this.paper; - var elements = paper.model.getCells(); - data.marked = {}; + var cp = (intersection) ? V.transformPoint(intersection, targetMatrix) : anchor; + var cpOffset = opt.offset || 0; + if (opt.stroke) { cpOffset += stroke$1(node) / 2; } - for (var i = 0, n = elements.length; i < n; i++) { - var view = elements[i].findView(paper); + return offsetPoint(cp, line.start, cpOffset); + } - if (!view) { - continue; - } + var anchor = anchorConnectionPoint; + var bbox = bboxIntersection; + var rectangle = rectangleIntersection; + var boundary = boundaryIntersection; - var magnets = Array.prototype.slice.call(view.el.querySelectorAll('[magnet]')); - if (view.el.getAttribute('magnet') !== 'false') { - // Element wrapping group is also a magnet - magnets.push(view.el); - } + var connectionPoints = ({ + anchor: anchor, + bbox: bbox, + rectangle: rectangle, + boundary: boundary + }); - var availableMagnets = magnets.filter(isMagnetAvailable.bind(data, view)); + function bboxWrapper(method) { - if (availableMagnets.length > 0) { - // highlight all available magnets - for (var j = 0, m = availableMagnets.length; j < m; j++) { - view.highlight(availableMagnets[j], { magnetAvailability: true }); - } - // highlight the entire view - view.highlight(null, { elementAvailability: true }); + return function(view, magnet, ref, opt) { - data.marked[view.model.id] = availableMagnets; + var rotate = !!opt.rotate; + var bbox = (rotate) ? view.getNodeUnrotatedBBox(magnet) : view.getNodeBBox(magnet); + var anchor = bbox[method](); + + var dx = opt.dx; + if (dx) { + var dxPercentage = isPercentage(dx); + dx = parseFloat(dx); + if (isFinite(dx)) { + if (dxPercentage) { + dx /= 100; + dx *= bbox.width; + } + anchor.x += dx; } } - }, - - _unmarkAvailableMagnets: function(data) { - var markedKeys = Object.keys(data.marked); - var id; - var markedMagnets; - - for (var i = 0, n = markedKeys.length; i < n; i++) { - id = markedKeys[i]; - markedMagnets = data.marked[id]; - - var view = this.paper.findViewByModel(id); - if (view) { - for (var j = 0, m = markedMagnets.length; j < m; j++) { - view.unhighlight(markedMagnets[j], { magnetAvailability: true }); + var dy = opt.dy; + if (dy) { + var dyPercentage = isPercentage(dy); + dy = parseFloat(dy); + if (isFinite(dy)) { + if (dyPercentage) { + dy /= 100; + dy *= bbox.height; } - view.unhighlight(null, { elementAvailability: true }); + anchor.y += dy; } } - data.marked = null; - }, + return (rotate) ? anchor.rotate(view.model.getBBox().center(), -view.model.angle()) : anchor; + }; + } - startArrowheadMove: function(end, opt) { + function _perpendicular(view, magnet, refPoint, opt) { - opt || (opt = {}); + var angle = view.model.angle(); + var bbox = view.getNodeBBox(magnet); + var anchor = bbox.center(); + var topLeft = bbox.origin(); + var bottomRight = bbox.corner(); - // Allow to delegate events from an another view to this linkView in order to trigger arrowhead - // move without need to click on the actual arrowhead dom element. - var data = { - action: 'arrowhead-move', - arrowhead: end, - whenNotAllowed: opt.whenNotAllowed || 'revert', - initialMagnet: this[end + 'Magnet'] || (this[end + 'View'] ? this[end + 'View'].el : null), - initialEnd: clone(this.model.get(end)), - validateConnectionArgs: this._createValidateConnectionArgs(end) - }; + var padding = opt.padding; + if (!isFinite(padding)) { padding = 0; } - this._beforeArrowheadMove(data); + if ((topLeft.y + padding) <= refPoint.y && refPoint.y <= (bottomRight.y - padding)) { + var dy = (refPoint.y - anchor.y); + anchor.x += (angle === 0 || angle === 180) ? 0 : dy * 1 / Math.tan(toRad(angle)); + anchor.y += dy; + } else if ((topLeft.x + padding) <= refPoint.x && refPoint.x <= (bottomRight.x - padding)) { + var dx = (refPoint.x - anchor.x); + anchor.y += (angle === 90 || angle === 270) ? 0 : dx * Math.tan(toRad(angle)); + anchor.x += dx; + } - if (opt.ignoreBackwardsCompatibility !== true) { - this._dragData = data; - } + return anchor; + } - return data; - }, + function _midSide(view, magnet, refPoint, opt) { - onRemove: function() { - CellView.prototype.onRemove.apply(this, arguments); - this.unmountLabels(); + var rotate = !!opt.rotate; + var bbox, angle, center; + if (rotate) { + bbox = view.getNodeUnrotatedBBox(magnet); + center = view.model.getBBox().center(); + angle = view.model.angle(); + } else { + bbox = view.getNodeBBox(magnet); } - }, { - - Flags: Flags$1, - }); - - Object.defineProperty(LinkView.prototype, 'sourceBBox', { + var padding = opt.padding; + if (isFinite(padding)) { bbox.inflate(padding); } - enumerable: true, + if (rotate) { refPoint.rotate(center, angle); } - get: function() { - var sourceView = this.sourceView; - if (!sourceView) { - var sourceDef = this.model.source(); - return new Rect(sourceDef.x, sourceDef.y); - } - var sourceMagnet = this.sourceMagnet; - if (sourceView.isNodeConnection(sourceMagnet)) { - return new Rect(this.sourceAnchor); - } - return sourceView.getNodeBBox(sourceMagnet || sourceView.el); + var side = bbox.sideNearestToPoint(refPoint); + var anchor; + switch (side) { + case 'left': + anchor = bbox.leftMiddle(); + break; + case 'right': + anchor = bbox.rightMiddle(); + break; + case 'top': + anchor = bbox.topMiddle(); + break; + case 'bottom': + anchor = bbox.bottomMiddle(); + break; } - }); + return (rotate) ? anchor.rotate(center, -angle) : anchor; + } - Object.defineProperty(LinkView.prototype, 'targetBBox', { + // Can find anchor from model, when there is no selector or the link end + // is connected to a port + function _modelCenter(view, _magnet, _refPoint, opt, endType) { + return view.model.getPointFromConnectedLink(this.model, endType).offset(opt.dx, opt.dy); + } - enumerable: true, + //joint.anchors + var center = bboxWrapper('center'); + var top$2 = bboxWrapper('topMiddle'); + var bottom$2 = bboxWrapper('bottomMiddle'); + var left$2 = bboxWrapper('leftMiddle'); + var right$2 = bboxWrapper('rightMiddle'); + var topLeft = bboxWrapper('origin'); + var topRight = bboxWrapper('topRight'); + var bottomLeft = bboxWrapper('bottomLeft'); + var bottomRight = bboxWrapper('corner'); + var perpendicular = resolveRef(_perpendicular); + var midSide = resolveRef(_midSide); + var modelCenter = _modelCenter; - get: function() { - var targetView = this.targetView; - if (!targetView) { - var targetDef = this.model.target(); - return new Rect(targetDef.x, targetDef.y); - } - var targetMagnet = this.targetMagnet; - if (targetView.isNodeConnection(targetMagnet)) { - return new Rect(this.targetAnchor); - } - return targetView.getNodeBBox(targetMagnet || targetView.el); - } + var anchors = ({ + center: center, + top: top$2, + bottom: bottom$2, + left: left$2, + right: right$2, + topLeft: topLeft, + topRight: topRight, + bottomLeft: bottomLeft, + bottomRight: bottomRight, + perpendicular: perpendicular, + midSide: midSide, + modelCenter: modelCenter }); var sortingTypes = { @@ -29790,3371 +28186,4975 @@ var joint = (function (exports, Backbone, _, $) { evt = normalizeEvent(evt); if (this.guard(evt, view)) { return; } - var localPoint = this.snapToGrid(evt.clientX, evt.clientY); - view.onlabel(evt, localPoint.x, localPoint.y); + var localPoint = this.snapToGrid(evt.clientX, evt.clientY); + view.onlabel(evt, localPoint.x, localPoint.y); + } + }, + + getPointerArgs: function getPointerArgs(evt) { + var normalizedEvt = normalizeEvent(evt); + var ref = this.snapToGrid(normalizedEvt.clientX, normalizedEvt.clientY); + var x = ref.x; + var y = ref.y; + return [normalizedEvt, x, y]; + }, + + delegateDragEvents: function(view, data) { + + data || (data = {}); + this.eventData({ data: data }, { sourceView: view || null, mousemoved: 0 }); + this.delegateDocumentEvents(null, data); + this.undelegateEvents(); + }, + + // Guard the specified event. If the event is not interesting, guard returns `true`. + // Otherwise, it returns `false`. + guard: function(evt, view) { + + if (evt.type === 'mousedown' && evt.button === 2) { + // handled as `contextmenu` type + return true; + } + + if (this.options.guard && this.options.guard(evt, view)) { + return true; + } + + if (evt.data && evt.data.guarded !== undefined) { + return evt.data.guarded; + } + + if (view && view.model && (view.model instanceof Cell)) { + return false; + } + + if (this.svg === evt.target || this.el === evt.target || $.contains(this.svg, evt.target)) { + return false; + } + + return true; // Event guarded. Paper should not react on it in any way. + }, + + setGridSize: function(gridSize) { + + this.options.gridSize = gridSize; + + if (this.options.drawGrid) { + this.drawGrid(); + } + + return this; + }, + + clearGrid: function() { + + if (this.$grid) { + this.$grid.css('backgroundImage', 'none'); + } + return this; + }, + + _getGridRefs: function() { + + if (!this._gridCache) { + + this._gridCache = { + root: V('svg', { width: '100%', height: '100%' }, V('defs')), + patterns: {}, + add: function(id, vel) { + V(this.root.node.childNodes[0]).append(vel); + this.patterns[id] = vel; + this.root.append(V('rect', { width: '100%', height: '100%', fill: 'url(#' + id + ')' })); + }, + get: function(id) { + return this.patterns[id]; + }, + exist: function(id) { + return this.patterns[id] !== undefined; + } + }; + } + + return this._gridCache; + }, + + setGrid: function(drawGrid) { + + this.clearGrid(); + + this._gridCache = null; + this._gridSettings = []; + + var optionsList = Array.isArray(drawGrid) ? drawGrid : [drawGrid || {}]; + optionsList.forEach(function(item) { + this._gridSettings.push.apply(this._gridSettings, this._resolveDrawGridOption(item)); + }, this); + return this; + }, + + _resolveDrawGridOption: function(opt) { + + var namespace = this.constructor.gridPatterns; + if (isString(opt) && Array.isArray(namespace[opt])) { + return namespace[opt].map(function(item) { + return assign({}, item); + }); + } + + var options = opt || { args: [{}] }; + var isArray = Array.isArray(options); + var name = options.name; + + if (!isArray && !name && !options.markup) { + name = 'dot'; + } + + if (name && Array.isArray(namespace[name])) { + var pattern = namespace[name].map(function(item) { + return assign({}, item); + }); + + var args = Array.isArray(options.args) ? options.args : [options.args || {}]; + + defaults(args[0], omit(opt, 'args')); + for (var i = 0; i < args.length; i++) { + if (pattern[i]) { + assign(pattern[i], args[i]); + } + } + return pattern; + } + + return isArray ? options : [options]; + }, + + drawGrid: function(opt) { + + var gridSize = this.options.gridSize; + if (gridSize <= 1) { + return this.clearGrid(); + } + + var localOptions = Array.isArray(opt) ? opt : [opt]; + + var ctm = this.matrix(); + var refs = this._getGridRefs(); + + this._gridSettings.forEach(function(gridLayerSetting, index) { + + var id = 'pattern_' + index; + var options = merge(gridLayerSetting, localOptions[index], { + sx: ctm.a || 1, + sy: ctm.d || 1, + ox: ctm.e || 0, + oy: ctm.f || 0 + }); + + options.width = gridSize * (ctm.a || 1) * (options.scaleFactor || 1); + options.height = gridSize * (ctm.d || 1) * (options.scaleFactor || 1); + + if (!refs.exist(id)) { + refs.add(id, V('pattern', { id: id, patternUnits: 'userSpaceOnUse' }, V(options.markup))); + } + + var patternDefVel = refs.get(id); + + if (isFunction(options.update)) { + options.update(patternDefVel.node.childNodes[0], options); + } + + var x = options.ox % options.width; + if (x < 0) { x += options.width; } + + var y = options.oy % options.height; + if (y < 0) { y += options.height; } + + patternDefVel.attr({ + x: x, + y: y, + width: options.width, + height: options.height + }); + }); + + var patternUri = new XMLSerializer().serializeToString(refs.root.node); + patternUri = 'url(data:image/svg+xml;base64,' + btoa(patternUri) + ')'; + + this.$grid.css('backgroundImage', patternUri); + + return this; + }, + + updateBackgroundImage: function(opt) { + + opt = opt || {}; + + var backgroundPosition = opt.position || 'center'; + var backgroundSize = opt.size || 'auto auto'; + + var currentScale = this.scale(); + var currentTranslate = this.translate(); + + // backgroundPosition + if (isObject$1(backgroundPosition)) { + var x = currentTranslate.tx + (currentScale.sx * (backgroundPosition.x || 0)); + var y = currentTranslate.ty + (currentScale.sy * (backgroundPosition.y || 0)); + backgroundPosition = x + 'px ' + y + 'px'; + } + + // backgroundSize + if (isObject$1(backgroundSize)) { + backgroundSize = new Rect(backgroundSize).scale(currentScale.sx, currentScale.sy); + backgroundSize = backgroundSize.width + 'px ' + backgroundSize.height + 'px'; + } + + this.$background.css({ + backgroundSize: backgroundSize, + backgroundPosition: backgroundPosition + }); + }, + + drawBackgroundImage: function(img, opt) { + + // Clear the background image if no image provided + if (!(img instanceof HTMLImageElement)) { + this.$background.css('backgroundImage', ''); + return; + } + + if (!this._background || this._background.id !== opt.id) { + // Draw only the last image requested (see drawBackground()) + return; + } + + opt = opt || {}; + + var backgroundImage; + var backgroundSize = opt.size; + var backgroundRepeat = opt.repeat || 'no-repeat'; + var backgroundOpacity = opt.opacity || 1; + var backgroundQuality = Math.abs(opt.quality) || 1; + var backgroundPattern = this.constructor.backgroundPatterns[camelCase(backgroundRepeat)]; + + if (isFunction(backgroundPattern)) { + // 'flip-x', 'flip-y', 'flip-xy', 'watermark' and custom + img.width *= backgroundQuality; + img.height *= backgroundQuality; + var canvas = backgroundPattern(img, opt); + if (!(canvas instanceof HTMLCanvasElement)) { + throw new Error('dia.Paper: background pattern must return an HTML Canvas instance'); + } + + backgroundImage = canvas.toDataURL('image/png'); + backgroundRepeat = 'repeat'; + if (isObject$1(backgroundSize)) { + // recalculate the tile size if an object passed in + backgroundSize.width *= canvas.width / img.width; + backgroundSize.height *= canvas.height / img.height; + } else if (backgroundSize === undefined) { + // calculate the tile size if no provided + opt.size = { + width: canvas.width / backgroundQuality, + height: canvas.height / backgroundQuality + }; + } + } else { + // backgroundRepeat: + // no-repeat', 'round', 'space', 'repeat', 'repeat-x', 'repeat-y' + backgroundImage = img.src; + if (backgroundSize === undefined) { + // pass the image size for the backgroundSize if no size provided + opt.size = { + width: img.width, + height: img.height + }; + } + } + + this.$background.css({ + opacity: backgroundOpacity, + backgroundRepeat: backgroundRepeat, + backgroundImage: 'url(' + backgroundImage + ')' + }); + + this.updateBackgroundImage(opt); + }, + + updateBackgroundColor: function(color) { + + this.$el.css('backgroundColor', color || ''); + }, + + drawBackground: function(opt) { + + opt = opt || {}; + + this.updateBackgroundColor(opt.color); + + if (opt.image) { + opt = this._background = cloneDeep(opt); + guid(opt); + var img = document.createElement('img'); + img.onload = this.drawBackgroundImage.bind(this, img, opt); + img.src = opt.image; + } else { + this.drawBackgroundImage(null); + this._background = null; + } + + return this; + }, + + setInteractivity: function(value) { + + this.options.interactive = value; + + invoke(this._views, 'setInteractivity', value); + }, + + // Paper definitions. + // ------------------ + + isDefined: function(defId) { + + return !!this.svg.getElementById(defId); + }, + + defineFilter: function(filter$1) { + + if (!isObject$1(filter$1)) { + throw new TypeError('dia.Paper: defineFilter() requires 1. argument to be an object.'); + } + + var filterId = filter$1.id; + var name = filter$1.name; + // Generate a hash code from the stringified filter definition. This gives us + // a unique filter ID for different definitions. + if (!filterId) { + filterId = name + this.svg.id + hashCode(JSON.stringify(filter$1)); + } + // If the filter already exists in the document, + // we're done and we can just use it (reference it using `url()`). + // If not, create one. + if (!this.isDefined(filterId)) { + + var namespace = filter; + var filterSVGString = namespace[name] && namespace[name](filter$1.args || {}); + if (!filterSVGString) { + throw new Error('Non-existing filter ' + name); + } + + // Set the filter area to be 3x the bounding box of the cell + // and center the filter around the cell. + var filterAttrs = assign({ + filterUnits: 'objectBoundingBox', + x: -1, + y: -1, + width: 3, + height: 3 + }, filter$1.attrs, { + id: filterId + }); + + V(filterSVGString, filterAttrs).appendTo(this.defs); } - }, - getPointerArgs: function getPointerArgs(evt) { - var normalizedEvt = normalizeEvent(evt); - var ref = this.snapToGrid(normalizedEvt.clientX, normalizedEvt.clientY); - var x = ref.x; - var y = ref.y; - return [normalizedEvt, x, y]; + return filterId; }, - delegateDragEvents: function(view, data) { + defineGradient: function(gradient) { + if (!isObject$1(gradient)) { + throw new TypeError('dia.Paper: defineGradient() requires 1. argument to be an object.'); + } + var ref = this; + var svg = ref.svg; + var defs = ref.defs; + var type = gradient.type; + var id = gradient.id; if ( id === void 0 ) id = type + svg.id + hashCode(JSON.stringify(gradient)); + var stops = gradient.stops; + var attrs = gradient.attrs; if ( attrs === void 0 ) attrs = {}; + // If the gradient already exists in the document, + // we're done and we can just use it (reference it using `url()`). + if (this.isDefined(id)) { return id; } + // If not, create one. + var stopVEls = toArray(stops).map(function (ref) { + var offset = ref.offset; + var color = ref.color; + var opacity = ref.opacity; - data || (data = {}); - this.eventData({ data: data }, { sourceView: view || null, mousemoved: 0 }); - this.delegateDocumentEvents(null, data); - this.undelegateEvents(); + return V('stop').attr({ + 'offset': offset, + 'stop-color': color, + 'stop-opacity': Number.isFinite(opacity) ? opacity : 1 + }); + }); + var gradientVEl = V(type, attrs, stopVEls); + gradientVEl.id = id; + gradientVEl.appendTo(defs); + return id; }, - // Guard the specified event. If the event is not interesting, guard returns `true`. - // Otherwise, it returns `false`. - guard: function(evt, view) { - - if (evt.type === 'mousedown' && evt.button === 2) { - // handled as `contextmenu` type - return true; + definePattern: function(pattern) { + if (!isObject$1(pattern)) { + throw new TypeError('dia.Paper: definePattern() requires 1. argument to be an object.'); } - - if (this.options.guard && this.options.guard(evt, view)) { - return true; + var ref = this; + var svg = ref.svg; + var defs = ref.defs; + var id = pattern.id; if ( id === void 0 ) id = svg.id + hashCode(JSON.stringify(pattern)); + var markup = pattern.markup; + var attrs = pattern.attrs; if ( attrs === void 0 ) attrs = {}; + if (!markup) { + throw new TypeError('dia.Paper: definePattern() requires markup.'); } - - if (evt.data && evt.data.guarded !== undefined) { - return evt.data.guarded; + // If the gradient already exists in the document, + // we're done and we can just use it (reference it using `url()`). + if (this.isDefined(id)) { return id; } + // If not, create one. + var patternVEl = V('pattern', { + patternUnits: 'userSpaceOnUse' + }); + patternVEl.id = id; + patternVEl.attr(attrs); + if (typeof markup === 'string') { + patternVEl.append(V(markup)); + } else { + var ref$1 = parseDOMJSON(markup); + var fragment = ref$1.fragment; + patternVEl.append(fragment); } + patternVEl.appendTo(defs); + return id; + }, - if (view && view.model && (view.model instanceof Cell)) { - return false; + defineMarker: function(marker) { + if (!isObject$1(marker)) { + throw new TypeError('dia.Paper: defineMarker() requires 1. argument to be an object.'); } - - if (this.svg === evt.target || this.el === evt.target || $.contains(this.svg, evt.target)) { - return false; + var ref = this; + var svg = ref.svg; + var defs = ref.defs; + var id = marker.id; if ( id === void 0 ) id = svg.id + hashCode(JSON.stringify(marker)); + var markup = marker.markup; + var attrs = marker.attrs; if ( attrs === void 0 ) attrs = {}; + var markerUnits = marker.markerUnits; if ( markerUnits === void 0 ) markerUnits = 'userSpaceOnUse'; + // If the marker already exists in the document, + // we're done and we can just use it (reference it using `url()`). + if (this.isDefined(id)) { return id; } + // If not, create one. + var markerVEl = V('marker', { + orient: 'auto', + overflow: 'visible', + markerUnits: markerUnits + }); + markerVEl.id = id; + markerVEl.attr(attrs); + if (markup) { + if (typeof markup === 'string') { + markerVEl.append(V(markup)); + } else { + var ref$1 = parseDOMJSON(markup); + var fragment = ref$1.fragment; + markerVEl.append(fragment); + } + } else { + // marker object is a flat structure + var type = marker.type; if ( type === void 0 ) type = 'path'; + var markerContentVEl = V(type, omit(marker, 'type', 'id', 'markup', 'attrs', 'markerUnits')); + markerVEl.append(markerContentVEl); } + markerVEl.appendTo(defs); + return id; + } - return true; // Event guarded. Paper should not react on it in any way. - }, + }, { - setGridSize: function(gridSize) { + sorting: sortingTypes, - this.options.gridSize = gridSize; + Layers: LayersNames, - if (this.options.drawGrid) { - this.drawGrid(); - } + backgroundPatterns: { - return this; - }, + flipXy: function(img) { + // d b + // q p - clearGrid: function() { + var canvas = document.createElement('canvas'); + var imgWidth = img.width; + var imgHeight = img.height; - if (this.$grid) { - this.$grid.css('backgroundImage', 'none'); - } - return this; - }, + canvas.width = 2 * imgWidth; + canvas.height = 2 * imgHeight; - _getGridRefs: function() { + var ctx = canvas.getContext('2d'); + // top-left image + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // xy-flipped bottom-right image + ctx.setTransform(-1, 0, 0, -1, canvas.width, canvas.height); + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // x-flipped top-right image + ctx.setTransform(-1, 0, 0, 1, canvas.width, 0); + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // y-flipped bottom-left image + ctx.setTransform(1, 0, 0, -1, 0, canvas.height); + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - if (!this._gridCache) { + return canvas; + }, - this._gridCache = { - root: V('svg', { width: '100%', height: '100%' }, V('defs')), - patterns: {}, - add: function(id, vel) { - V(this.root.node.childNodes[0]).append(vel); - this.patterns[id] = vel; - this.root.append(V('rect', { width: '100%', height: '100%', fill: 'url(#' + id + ')' })); - }, - get: function(id) { - return this.patterns[id]; - }, - exist: function(id) { - return this.patterns[id] !== undefined; - } - }; - } + flipX: function(img) { + // d b + // d b - return this._gridCache; - }, + var canvas = document.createElement('canvas'); + var imgWidth = img.width; + var imgHeight = img.height; - setGrid: function(drawGrid) { + canvas.width = imgWidth * 2; + canvas.height = imgHeight; - this.clearGrid(); + var ctx = canvas.getContext('2d'); + // left image + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // flipped right image + ctx.translate(2 * imgWidth, 0); + ctx.scale(-1, 1); + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - this._gridCache = null; - this._gridSettings = []; + return canvas; + }, - var optionsList = Array.isArray(drawGrid) ? drawGrid : [drawGrid || {}]; - optionsList.forEach(function(item) { - this._gridSettings.push.apply(this._gridSettings, this._resolveDrawGridOption(item)); - }, this); - return this; - }, + flipY: function(img) { + // d d + // q q - _resolveDrawGridOption: function(opt) { + var canvas = document.createElement('canvas'); + var imgWidth = img.width; + var imgHeight = img.height; - var namespace = this.constructor.gridPatterns; - if (isString(opt) && Array.isArray(namespace[opt])) { - return namespace[opt].map(function(item) { - return assign({}, item); - }); - } + canvas.width = imgWidth; + canvas.height = imgHeight * 2; - var options = opt || { args: [{}] }; - var isArray = Array.isArray(options); - var name = options.name; + var ctx = canvas.getContext('2d'); + // top image + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // flipped bottom image + ctx.translate(0, 2 * imgHeight); + ctx.scale(1, -1); + ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - if (!isArray && !name && !options.markup) { - name = 'dot'; - } + return canvas; + }, - if (name && Array.isArray(namespace[name])) { - var pattern = namespace[name].map(function(item) { - return assign({}, item); - }); + watermark: function(img, opt) { + // d + // d - var args = Array.isArray(options.args) ? options.args : [options.args || {}]; + opt = opt || {}; - defaults(args[0], omit(opt, 'args')); - for (var i = 0; i < args.length; i++) { - if (pattern[i]) { - assign(pattern[i], args[i]); + var imgWidth = img.width; + var imgHeight = img.height; + + var canvas = document.createElement('canvas'); + canvas.width = imgWidth * 3; + canvas.height = imgHeight * 3; + + var ctx = canvas.getContext('2d'); + var angle = isNumber(opt.watermarkAngle) ? -opt.watermarkAngle : -20; + var radians = toRad(angle); + var stepX = canvas.width / 4; + var stepY = canvas.height / 4; + + for (var i = 0; i < 4; i++) { + for (var j = 0; j < 4; j++) { + if ((i + j) % 2 > 0) { + // reset the current transformations + ctx.setTransform(1, 0, 0, 1, (2 * i - 1) * stepX, (2 * j - 1) * stepY); + ctx.rotate(radians); + ctx.drawImage(img, -imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight); + } } } - return pattern; - } - return isArray ? options : [options]; + return canvas; + } }, - drawGrid: function(opt) { - - var gridSize = this.options.gridSize; - if (gridSize <= 1) { - return this.clearGrid(); - } + gridPatterns: { + dot: [{ + color: '#AAAAAA', + thickness: 1, + markup: 'rect', + update: function(el, opt) { + V(el).attr({ + width: opt.thickness * opt.sx, + height: opt.thickness * opt.sy, + fill: opt.color + }); + } + }], + fixedDot: [{ + color: '#AAAAAA', + thickness: 1, + markup: 'rect', + update: function(el, opt) { + var size = opt.sx <= 1 ? opt.thickness * opt.sx : opt.thickness; + V(el).attr({ width: size, height: size, fill: opt.color }); + } + }], + mesh: [{ + color: '#AAAAAA', + thickness: 1, + markup: 'path', + update: function(el, opt) { - var localOptions = Array.isArray(opt) ? opt : [opt]; + var d; + var width = opt.width; + var height = opt.height; + var thickness = opt.thickness; - var ctm = this.matrix(); - var refs = this._getGridRefs(); + if (width - thickness >= 0 && height - thickness >= 0) { + d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); + } else { + d = 'M 0 0 0 0'; + } - this._gridSettings.forEach(function(gridLayerSetting, index) { + V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); + } + }], + doubleMesh: [{ + color: '#AAAAAA', + thickness: 1, + markup: 'path', + update: function(el, opt) { - var id = 'pattern_' + index; - var options = merge(gridLayerSetting, localOptions[index], { - sx: ctm.a || 1, - sy: ctm.d || 1, - ox: ctm.e || 0, - oy: ctm.f || 0 - }); + var d; + var width = opt.width; + var height = opt.height; + var thickness = opt.thickness; - options.width = gridSize * (ctm.a || 1) * (options.scaleFactor || 1); - options.height = gridSize * (ctm.d || 1) * (options.scaleFactor || 1); + if (width - thickness >= 0 && height - thickness >= 0) { + d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); + } else { + d = 'M 0 0 0 0'; + } - if (!refs.exist(id)) { - refs.add(id, V('pattern', { id: id, patternUnits: 'userSpaceOnUse' }, V(options.markup))); + V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); } + }, { + color: '#000000', + thickness: 3, + scaleFactor: 4, + markup: 'path', + update: function(el, opt) { - var patternDefVel = refs.get(id); + var d; + var width = opt.width; + var height = opt.height; + var thickness = opt.thickness; - if (isFunction(options.update)) { - options.update(patternDefVel.node.childNodes[0], options); + if (width - thickness >= 0 && height - thickness >= 0) { + d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); + } else { + d = 'M 0 0 0 0'; + } + + V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); } + }] + } + }); - var x = options.ox % options.width; - if (x < 0) { x += options.width; } + var ToolView = View.extend({ + name: null, + tagName: 'g', + className: 'tool', + svgElement: true, + _visible: true, - var y = options.oy % options.height; - if (y < 0) { y += options.height; } + init: function() { + var name = this.name; + if (name) { this.vel.attr('data-tool-name', name); } + }, - patternDefVel.attr({ - x: x, - y: y, - width: options.width, - height: options.height - }); - }); + configure: function(view, toolsView) { + this.relatedView = view; + this.paper = view.paper; + this.parentView = toolsView; + this.simulateRelatedView(this.el); + // Delegate events in case the ToolView was removed from the DOM and reused. + this.delegateEvents(); + return this; + }, - var patternUri = new XMLSerializer().serializeToString(refs.root.node); - patternUri = 'url(data:image/svg+xml;base64,' + btoa(patternUri) + ')'; + simulateRelatedView: function(el) { + if (el) { el.setAttribute('model-id', this.relatedView.model.id); } + }, - this.$grid.css('backgroundImage', patternUri); + getName: function() { + return this.name; + }, - return this; + show: function() { + this.el.style.display = ''; + this._visible = true; }, - updateBackgroundImage: function(opt) { + hide: function() { + this.el.style.display = 'none'; + this._visible = false; + }, - opt = opt || {}; + isVisible: function() { + return !!this._visible; + }, - var backgroundPosition = opt.position || 'center'; - var backgroundSize = opt.size || 'auto auto'; + focus: function() { + var opacity = this.options.focusOpacity; + if (isFinite(opacity)) { this.el.style.opacity = opacity; } + this.parentView.focusTool(this); + }, - var currentScale = this.scale(); - var currentTranslate = this.translate(); + blur: function() { + this.el.style.opacity = ''; + this.parentView.blurTool(this); + }, - // backgroundPosition - if (isObject$1(backgroundPosition)) { - var x = currentTranslate.tx + (currentScale.sx * (backgroundPosition.x || 0)); - var y = currentTranslate.ty + (currentScale.sy * (backgroundPosition.y || 0)); - backgroundPosition = x + 'px ' + y + 'px'; - } + update: function() { + // to be overridden + }, - // backgroundSize - if (isObject$1(backgroundSize)) { - backgroundSize = new Rect(backgroundSize).scale(currentScale.sx, currentScale.sy); - backgroundSize = backgroundSize.width + 'px ' + backgroundSize.height + 'px'; + guard: function(evt) { + // Let the context-menu event bubble up to the relatedView + var ref = this; + var paper = ref.paper; + var relatedView = ref.relatedView; + if (!paper || !relatedView) { return true; } + return paper.guard(evt, relatedView); + } + }); + + var ToolsView = View.extend({ + tagName: 'g', + className: 'tools', + svgElement: true, + tools: null, + isRendered: false, + options: { + tools: null, + relatedView: null, + name: null, + // layer?: LayersNames.TOOLS + // z?: number + }, + + configure: function(options) { + options = assign(this.options, options); + var tools = options.tools; + if (!Array.isArray(tools)) { return this; } + var relatedView = options.relatedView; + if (!(relatedView instanceof CellView)) { return this; } + var views = this.tools = []; + for (var i = 0, n = tools.length; i < n; i++) { + var tool = tools[i]; + if (!(tool instanceof ToolView)) { continue; } + tool.configure(relatedView, this); + this.vel.append(tool.el); + views.push(tool); } + this.isRendered = false; + relatedView.requestUpdate(relatedView.getFlag('TOOLS')); + return this; + }, - this.$background.css({ - backgroundSize: backgroundSize, - backgroundPosition: backgroundPosition - }); + getName: function() { + return this.options.name; }, - drawBackgroundImage: function(img, opt) { + update: function(opt) { - // Clear the background image if no image provided - if (!(img instanceof HTMLImageElement)) { - this.$background.css('backgroundImage', ''); - return; + opt || (opt = {}); + var tools = this.tools; + if (!tools) { return this; } + var isRendered = this.isRendered; + for (var i = 0, n = tools.length; i < n; i++) { + var tool = tools[i]; + if (!isRendered) { + // First update executes render() + tool.render(); + } else if (opt.tool !== tool.cid && tool.isVisible()) { + tool.update(); + } } - - if (!this._background || this._background.id !== opt.id) { - // Draw only the last image requested (see drawBackground()) - return; + if (!isRendered) { + this.mount(); + // Make sure tools are visible (if they were hidden and the tool removed) + this.blurTool(); + this.isRendered = true; } + return this; + }, - opt = opt || {}; - - var backgroundImage; - var backgroundSize = opt.size; - var backgroundRepeat = opt.repeat || 'no-repeat'; - var backgroundOpacity = opt.opacity || 1; - var backgroundQuality = Math.abs(opt.quality) || 1; - var backgroundPattern = this.constructor.backgroundPatterns[camelCase(backgroundRepeat)]; + focusTool: function(focusedTool) { - if (isFunction(backgroundPattern)) { - // 'flip-x', 'flip-y', 'flip-xy', 'watermark' and custom - img.width *= backgroundQuality; - img.height *= backgroundQuality; - var canvas = backgroundPattern(img, opt); - if (!(canvas instanceof HTMLCanvasElement)) { - throw new Error('dia.Paper: background pattern must return an HTML Canvas instance'); + var tools = this.tools; + if (!tools) { return this; } + for (var i = 0, n = tools.length; i < n; i++) { + var tool = tools[i]; + if (focusedTool === tool) { + tool.show(); + } else { + tool.hide(); } + } + return this; + }, - backgroundImage = canvas.toDataURL('image/png'); - backgroundRepeat = 'repeat'; - if (isObject$1(backgroundSize)) { - // recalculate the tile size if an object passed in - backgroundSize.width *= canvas.width / img.width; - backgroundSize.height *= canvas.height / img.height; - } else if (backgroundSize === undefined) { - // calculate the tile size if no provided - opt.size = { - width: canvas.width / backgroundQuality, - height: canvas.height / backgroundQuality - }; - } - } else { - // backgroundRepeat: - // no-repeat', 'round', 'space', 'repeat', 'repeat-x', 'repeat-y' - backgroundImage = img.src; - if (backgroundSize === undefined) { - // pass the image size for the backgroundSize if no size provided - opt.size = { - width: img.width, - height: img.height - }; + blurTool: function(blurredTool) { + var tools = this.tools; + if (!tools) { return this; } + for (var i = 0, n = tools.length; i < n; i++) { + var tool = tools[i]; + if (tool !== blurredTool && !tool.isVisible()) { + tool.show(); + tool.update(); } } + return this; + }, - this.$background.css({ - opacity: backgroundOpacity, - backgroundRepeat: backgroundRepeat, - backgroundImage: 'url(' + backgroundImage + ')' - }); - - this.updateBackgroundImage(opt); + hide: function() { + return this.focusTool(null); }, - updateBackgroundColor: function(color) { + show: function() { + return this.blurTool(null); + }, - this.$el.css('backgroundColor', color || ''); + onRemove: function() { + var tools = this.tools; + if (!tools) { return this; } + for (var i = 0, n = tools.length; i < n; i++) { + tools[i].remove(); + } + this.tools = null; }, - drawBackground: function(opt) { + mount: function() { + var ref = this; + var options = ref.options; + var el = ref.el; + var relatedView = options.relatedView; + var layer = options.layer; if ( layer === void 0 ) layer = LayersNames.TOOLS; + var z = options.z; + if (relatedView) { + if (layer) { + relatedView.paper.getLayerView(layer).insertSortedNode(el, z); + } else { + relatedView.el.appendChild(el); + } + } + return this; + } - opt = opt || {}; + }); - this.updateBackgroundColor(opt.color); - if (opt.image) { - opt = this._background = cloneDeep(opt); - guid(opt); - var img = document.createElement('img'); - img.onload = this.drawBackgroundImage.bind(this, img, opt); - img.src = opt.image; - } else { - this.drawBackgroundImage(null); - this._background = null; - } - return this; - }, + var index$2 = ({ + Graph: Graph, + attributes: attributes, + LayersNames: LayersNames, + PaperLayer: PaperLayer, + Cell: Cell, + CellView: CellView, + Element: Element$1, + ElementView: ElementView, + Link: Link, + LinkView: LinkView, + Paper: Paper, + ToolView: ToolView, + ToolsView: ToolsView, + HighlighterView: HighlighterView + }); - setInteractivity: function(value) { + var DirectedGraph = { - this.options.interactive = value; + exportElement: function(element) { - invoke(this._views, 'setInteractivity', value); + // The width and height of the element. + return element.size(); }, - // Paper definitions. - // ------------------ + exportLink: function(link) { - isDefined: function(defId) { + var labelSize = link.get('labelSize') || {}; + var edge = { + // The number of ranks to keep between the source and target of the edge. + minLen: link.get('minLen') || 1, + // The weight to assign edges. Higher weight edges are generally + // made shorter and straighter than lower weight edges. + weight: link.get('weight') || 1, + // Where to place the label relative to the edge. + // l = left, c = center r = right. + labelpos: link.get('labelPosition') || 'c', + // How many pixels to move the label away from the edge. + // Applies only when labelpos is l or r. + labeloffset: link.get('labelOffset') || 0, + // The width of the edge label in pixels. + width: labelSize.width || 0, + // The height of the edge label in pixels. + height: labelSize.height || 0 + }; - return !!this.svg.getElementById(defId); + return edge; }, - defineFilter: function(filter$1) { + importElement: function(opt, v, gl) { - if (!isObject$1(filter$1)) { - throw new TypeError('dia.Paper: defineFilter() requires 1. argument to be an object.'); - } + var element = this.getCell(v); + var glNode = gl.node(v); - var filterId = filter$1.id; - var name = filter$1.name; - // Generate a hash code from the stringified filter definition. This gives us - // a unique filter ID for different definitions. - if (!filterId) { - filterId = name + this.svg.id + hashCode(JSON.stringify(filter$1)); + if (opt.setPosition) { + opt.setPosition(element, glNode); + } else { + element.set('position', { + x: glNode.x - glNode.width / 2, + y: glNode.y - glNode.height / 2 + }); } - // If the filter already exists in the document, - // we're done and we can just use it (reference it using `url()`). - // If not, create one. - if (!this.isDefined(filterId)) { + }, - var namespace = filter; - var filterSVGString = namespace[name] && namespace[name](filter$1.args || {}); - if (!filterSVGString) { - throw new Error('Non-existing filter ' + name); - } + importLink: function(opt, edgeObj, gl) { - // Set the filter area to be 3x the bounding box of the cell - // and center the filter around the cell. - var filterAttrs = assign({ - filterUnits: 'objectBoundingBox', - x: -1, - y: -1, - width: 3, - height: 3 - }, filter$1.attrs, { - id: filterId - }); + var SIMPLIFY_THRESHOLD = 0.001; - V(filterSVGString, filterAttrs).appendTo(this.defs); + var link = this.getCell(edgeObj.name); + var glEdge = gl.edge(edgeObj); + var points = glEdge.points || []; + var polyline = new Polyline(points); + + // check the `setLinkVertices` here for backwards compatibility + if (opt.setVertices || opt.setLinkVertices) { + if (isFunction(opt.setVertices)) { + opt.setVertices(link, points); + } else { + // simplify the `points` polyline + polyline.simplify({ threshold: SIMPLIFY_THRESHOLD }); + var polylinePoints = polyline.points.map(function (point) { return (point.toJSON()); }); // JSON of points after simplification + var numPolylinePoints = polylinePoints.length; // number of points after simplification + // set simplified polyline points as link vertices + // remove first and last polyline points (= source/target sonnectionPoints) + link.set('vertices', polylinePoints.slice(1, numPolylinePoints - 1)); + } } - return filterId; + if (opt.setLabels && ('x' in glEdge) && ('y' in glEdge)) { + var labelPosition = { x: glEdge.x, y: glEdge.y }; + if (isFunction(opt.setLabels)) { + opt.setLabels(link, labelPosition, points); + } else { + // convert the absolute label position to a relative position + // towards the closest point on the edge + var length = polyline.closestPointLength(labelPosition); + var closestPoint = polyline.pointAtLength(length); + var distance = (length / polyline.length()); + var offset = new Point(labelPosition).difference(closestPoint).toJSON(); + link.label(0, { + position: { + distance: distance, + offset: offset + } + }); + } + } }, - defineGradient: function(gradient) { - if (!isObject$1(gradient)) { - throw new TypeError('dia.Paper: defineGradient() requires 1. argument to be an object.'); - } - var ref = this; - var svg = ref.svg; - var defs = ref.defs; - var type = gradient.type; - var id = gradient.id; if ( id === void 0 ) id = type + svg.id + hashCode(JSON.stringify(gradient)); - var stops = gradient.stops; - var attrs = gradient.attrs; if ( attrs === void 0 ) attrs = {}; - // If the gradient already exists in the document, - // we're done and we can just use it (reference it using `url()`). - if (this.isDefined(id)) { return id; } - // If not, create one. - var stopVEls = toArray(stops).map(function (ref) { - var offset = ref.offset; - var color = ref.color; - var opacity = ref.opacity; + layout: function(graphOrCells, opt) { - return V('stop').attr({ - 'offset': offset, - 'stop-color': color, - 'stop-opacity': Number.isFinite(opacity) ? opacity : 1 - }); - }); - var gradientVEl = V(type, attrs, stopVEls); - gradientVEl.id = id; - gradientVEl.appendTo(defs); - return id; - }, + var graph; - definePattern: function(pattern) { - if (!isObject$1(pattern)) { - throw new TypeError('dia.Paper: definePattern() requires 1. argument to be an object.'); - } - var ref = this; - var svg = ref.svg; - var defs = ref.defs; - var id = pattern.id; if ( id === void 0 ) id = svg.id + hashCode(JSON.stringify(pattern)); - var markup = pattern.markup; - var attrs = pattern.attrs; if ( attrs === void 0 ) attrs = {}; - if (!markup) { - throw new TypeError('dia.Paper: definePattern() requires markup.'); - } - // If the gradient already exists in the document, - // we're done and we can just use it (reference it using `url()`). - if (this.isDefined(id)) { return id; } - // If not, create one. - var patternVEl = V('pattern', { - patternUnits: 'userSpaceOnUse' - }); - patternVEl.id = id; - patternVEl.attr(attrs); - if (typeof markup === 'string') { - patternVEl.append(V(markup)); + if (graphOrCells instanceof Graph) { + graph = graphOrCells; } else { - var ref$1 = parseDOMJSON(markup); - var fragment = ref$1.fragment; - patternVEl.append(fragment); + // Reset cells in dry mode so the graph reference is not stored on the cells. + // `sort: false` to prevent elements to change their order based on the z-index + graph = (new Graph()).resetCells(graphOrCells, { dry: true, sort: false }); } - patternVEl.appendTo(defs); - return id; - }, - defineMarker: function(marker) { - if (!isObject$1(marker)) { - throw new TypeError('dia.Paper: defineMarker() requires 1. argument to be an object.'); - } - var ref = this; - var svg = ref.svg; - var defs = ref.defs; - var id = marker.id; if ( id === void 0 ) id = svg.id + hashCode(JSON.stringify(marker)); - var markup = marker.markup; - var attrs = marker.attrs; if ( attrs === void 0 ) attrs = {}; - var markerUnits = marker.markerUnits; if ( markerUnits === void 0 ) markerUnits = 'userSpaceOnUse'; - // If the marker already exists in the document, - // we're done and we can just use it (reference it using `url()`). - if (this.isDefined(id)) { return id; } - // If not, create one. - var markerVEl = V('marker', { - orient: 'auto', - overflow: 'visible', - markerUnits: markerUnits + // This is not needed anymore. + graphOrCells = null; + + opt = defaults(opt || {}, { + resizeClusters: true, + clusterPadding: 10, + exportElement: this.exportElement, + exportLink: this.exportLink }); - markerVEl.id = id; - markerVEl.attr(attrs); - if (markup) { - if (typeof markup === 'string') { - markerVEl.append(V(markup)); - } else { - var ref$1 = parseDOMJSON(markup); - var fragment = ref$1.fragment; - markerVEl.append(fragment); + + /* eslint-disable no-undef */ + var dagreUtil = opt.dagre || (typeof dagre !== 'undefined' ? dagre : undefined); + /* eslint-enable no-undef */ + + if (dagreUtil === undefined) { throw new Error('The the "dagre" utility is a mandatory dependency.'); } + + // create a graphlib.Graph that represents the joint.dia.Graph + // var glGraph = graph.toGraphLib({ + var glGraph = DirectedGraph.toGraphLib(graph, { + graphlib: opt.graphlib, + directed: true, + // We are about to use edge naming feature. + multigraph: true, + // We are able to layout graphs with embeds. + compound: true, + setNodeLabel: opt.exportElement, + setEdgeLabel: opt.exportLink, + setEdgeName: function(link) { + // Graphlib edges have no ids. We use edge name property + // to store and retrieve ids instead. + return link.id; } - } else { - // marker object is a flat structure - var type = marker.type; if ( type === void 0 ) type = 'path'; - var markerContentVEl = V(type, omit(marker, 'type', 'id', 'markup', 'attrs', 'markerUnits')); - markerVEl.append(markerContentVEl); - } - markerVEl.appendTo(defs); - return id; - } + }); - }, { + var glLabel = {}; + var marginX = opt.marginX || 0; + var marginY = opt.marginY || 0; - sorting: sortingTypes, + // Dagre layout accepts options as lower case. + // Direction for rank nodes. Can be TB, BT, LR, or RL + if (opt.rankDir) { glLabel.rankdir = opt.rankDir; } + // Alignment for rank nodes. Can be UL, UR, DL, or DR + if (opt.align) { glLabel.align = opt.align; } + // Number of pixels that separate nodes horizontally in the layout. + if (opt.nodeSep) { glLabel.nodesep = opt.nodeSep; } + // Number of pixels that separate edges horizontally in the layout. + if (opt.edgeSep) { glLabel.edgesep = opt.edgeSep; } + // Number of pixels between each rank in the layout. + if (opt.rankSep) { glLabel.ranksep = opt.rankSep; } + // Type of algorithm to assign a rank to each node in the input graph. + // Possible values: network-simplex, tight-tree or longest-path + if (opt.ranker) { glLabel.ranker = opt.ranker; } + // Number of pixels to use as a margin around the left and right of the graph. + if (marginX) { glLabel.marginx = marginX; } + // Number of pixels to use as a margin around the top and bottom of the graph. + if (marginY) { glLabel.marginy = marginY; } - Layers: LayersNames, + // Set the option object for the graph label. + glGraph.setGraph(glLabel); - backgroundPatterns: { + // Executes the layout. + dagreUtil.layout(glGraph, { debugTiming: !!opt.debugTiming }); - flipXy: function(img) { - // d b - // q p + // Wrap all graph changes into a batch. + graph.startBatch('layout'); - var canvas = document.createElement('canvas'); - var imgWidth = img.width; - var imgHeight = img.height; + DirectedGraph.fromGraphLib(glGraph, { + importNode: this.importElement.bind(graph, opt), + importEdge: this.importLink.bind(graph, opt) + }); - canvas.width = 2 * imgWidth; - canvas.height = 2 * imgHeight; + // // Update the graph. + // graph.fromGraphLib(glGraph, { + // importNode: this.importElement.bind(graph, opt), + // importEdge: this.importLink.bind(graph, opt) + // }); - var ctx = canvas.getContext('2d'); - // top-left image - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - // xy-flipped bottom-right image - ctx.setTransform(-1, 0, 0, -1, canvas.width, canvas.height); - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - // x-flipped top-right image - ctx.setTransform(-1, 0, 0, 1, canvas.width, 0); - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - // y-flipped bottom-left image - ctx.setTransform(1, 0, 0, -1, 0, canvas.height); - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + if (opt.resizeClusters) { + // Resize and reposition cluster elements (parents of other elements) + // to fit their children. + // 1. filter clusters only + // 2. map id on cells + // 3. sort cells by their depth (the deepest first) + // 4. resize cell to fit their direct children only. + var clusters = glGraph.nodes() + .filter(function(v) { return glGraph.children(v).length > 0; }) + .map(graph.getCell.bind(graph)) + .sort(function(aCluster, bCluster) { + return bCluster.getAncestors().length - aCluster.getAncestors().length; + }); - return canvas; - }, + invoke(clusters, 'fitEmbeds', { padding: opt.clusterPadding }); + } + + graph.stopBatch('layout'); + + // Width and height of the graph extended by margins. + var glSize = glGraph.graph(); + // Return the bounding box of the graph after the layout. + return new Rect( + marginX, + marginY, + Math.abs(glSize.width - 2 * marginX), + Math.abs(glSize.height - 2 * marginY) + ); + }, + + fromGraphLib: function(glGraph, opt) { + + opt = opt || {}; + + var importNode = opt.importNode || noop; + var importEdge = opt.importEdge || noop; + var graph = (this instanceof Graph) ? this : new Graph; - flipX: function(img) { - // d b - // d b + // Import all nodes. + glGraph.nodes().forEach(function(node) { + importNode.call(graph, node, glGraph, graph, opt); + }); - var canvas = document.createElement('canvas'); - var imgWidth = img.width; - var imgHeight = img.height; + // Import all edges. + glGraph.edges().forEach(function(edge) { + importEdge.call(graph, edge, glGraph, graph, opt); + }); - canvas.width = imgWidth * 2; - canvas.height = imgHeight; + return graph; + }, - var ctx = canvas.getContext('2d'); - // left image - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - // flipped right image - ctx.translate(2 * imgWidth, 0); - ctx.scale(-1, 1); - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + // Create new graphlib graph from existing JointJS graph. + toGraphLib: function(graph, opt) { - return canvas; - }, + opt = opt || {}; - flipY: function(img) { - // d d - // q q + /* eslint-disable no-undef */ + var graphlibUtil = opt.graphlib || (typeof graphlib !== 'undefined' ? graphlib : undefined); + /* eslint-enable no-undef */ - var canvas = document.createElement('canvas'); - var imgWidth = img.width; - var imgHeight = img.height; + if (graphlibUtil === undefined) { throw new Error('The the "graphlib" utility is a mandatory dependency.'); } - canvas.width = imgWidth; - canvas.height = imgHeight * 2; + var glGraphType = pick(opt, 'directed', 'compound', 'multigraph'); + var glGraph = new graphlibUtil.Graph(glGraphType); + var setNodeLabel = opt.setNodeLabel || noop; + var setEdgeLabel = opt.setEdgeLabel || noop; + var setEdgeName = opt.setEdgeName || noop; + var collection = graph.get('cells'); - var ctx = canvas.getContext('2d'); - // top image - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); - // flipped bottom image - ctx.translate(0, 2 * imgHeight); - ctx.scale(1, -1); - ctx.drawImage(img, 0, 0, imgWidth, imgHeight); + for (var i = 0, n = collection.length; i < n; i++) { - return canvas; - }, + var cell = collection.at(i); + if (cell.isLink()) { - watermark: function(img, opt) { - // d - // d + var source = cell.get('source'); + var target = cell.get('target'); - opt = opt || {}; + // Links that end at a point are ignored. + if (!source.id || !target.id) { break; } - var imgWidth = img.width; - var imgHeight = img.height; + // Note that if we are creating a multigraph we can name the edges. If + // we try to name edges on a non-multigraph an exception is thrown. + glGraph.setEdge(source.id, target.id, setEdgeLabel(cell), setEdgeName(cell)); - var canvas = document.createElement('canvas'); - canvas.width = imgWidth * 3; - canvas.height = imgHeight * 3; + } else { - var ctx = canvas.getContext('2d'); - var angle = isNumber(opt.watermarkAngle) ? -opt.watermarkAngle : -20; - var radians = toRad(angle); - var stepX = canvas.width / 4; - var stepY = canvas.height / 4; + glGraph.setNode(cell.id, setNodeLabel(cell)); - for (var i = 0; i < 4; i++) { - for (var j = 0; j < 4; j++) { - if ((i + j) % 2 > 0) { - // reset the current transformations - ctx.setTransform(1, 0, 0, 1, (2 * i - 1) * stepX, (2 * j - 1) * stepY); - ctx.rotate(radians); - ctx.drawImage(img, -imgWidth / 2, -imgHeight / 2, imgWidth, imgHeight); + // For the compound graphs we have to take embeds into account. + if (glGraph.isCompound() && cell.has('parent')) { + var parentId = cell.get('parent'); + if (collection.has(parentId)) { + // Make sure the parent cell is included in the graph (this can + // happen when the layout is run on part of the graph only). + glGraph.setParent(cell.id, parentId); } } } - - return canvas; } - }, - gridPatterns: { - dot: [{ - color: '#AAAAAA', - thickness: 1, - markup: 'rect', - update: function(el, opt) { - V(el).attr({ - width: opt.thickness * opt.sx, - height: opt.thickness * opt.sy, - fill: opt.color - }); - } - }], - fixedDot: [{ - color: '#AAAAAA', - thickness: 1, - markup: 'rect', - update: function(el, opt) { - var size = opt.sx <= 1 ? opt.thickness * opt.sx : opt.thickness; - V(el).attr({ width: size, height: size, fill: opt.color }); - } - }], - mesh: [{ - color: '#AAAAAA', - thickness: 1, - markup: 'path', - update: function(el, opt) { + return glGraph; + } + }; - var d; - var width = opt.width; - var height = opt.height; - var thickness = opt.thickness; + Graph.prototype.toGraphLib = function(opt) { - if (width - thickness >= 0 && height - thickness >= 0) { - d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); - } else { - d = 'M 0 0 0 0'; - } + return DirectedGraph.toGraphLib(this, opt); + }; - V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); - } - }], - doubleMesh: [{ - color: '#AAAAAA', - thickness: 1, - markup: 'path', - update: function(el, opt) { + Graph.prototype.fromGraphLib = function(glGraph, opt) { - var d; - var width = opt.width; - var height = opt.height; - var thickness = opt.thickness; + return DirectedGraph.fromGraphLib.call(this, glGraph, opt); + }; - if (width - thickness >= 0 && height - thickness >= 0) { - d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); - } else { - d = 'M 0 0 0 0'; - } + var env = { - V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); - } - }, { - color: '#000000', - thickness: 3, - scaleFactor: 4, - markup: 'path', - update: function(el, opt) { + _results: {}, - var d; - var width = opt.width; - var height = opt.height; - var thickness = opt.thickness; + _tests: { - if (width - thickness >= 0 && height - thickness >= 0) { - d = ['M', width, 0, 'H0 M0 0 V0', height].join(' '); - } else { - d = 'M 0 0 0 0'; - } + svgforeignobject: function() { + return !!document.createElementNS && + /SVGForeignObject/.test(({}).toString.call(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'))); + } + }, - V(el).attr({ 'd': d, stroke: opt.color, 'stroke-width': opt.thickness }); - } - }] - } - }); + addTest: function(name, fn) { - var ToolsView = View.extend({ - tagName: 'g', - className: 'tools', - svgElement: true, - tools: null, - isRendered: false, - options: { - tools: null, - relatedView: null, - name: null, - // layer?: LayersNames.TOOLS - // z?: number + return this._tests[name] = fn; }, - configure: function(options) { - options = assign(this.options, options); - var tools = options.tools; - if (!Array.isArray(tools)) { return this; } - var relatedView = options.relatedView; - if (!(relatedView instanceof CellView)) { return this; } - var views = this.tools = []; - for (var i = 0, n = tools.length; i < n; i++) { - var tool = tools[i]; - if (!(tool instanceof ToolView)) { continue; } - tool.configure(relatedView, this); - this.vel.append(tool.el); - views.push(tool); + test: function(name) { + + var fn = this._tests[name]; + + if (!fn) { + throw new Error('Test not defined ("' + name + '"). Use `joint.env.addTest(name, fn) to add a new test.`'); } - this.isRendered = false; - relatedView.requestUpdate(relatedView.getFlag('TOOLS')); - return this; - }, - getName: function() { - return this.options.name; - }, + var result = this._results[name]; - update: function(opt) { + if (typeof result !== 'undefined') { + return result; + } - opt || (opt = {}); - var tools = this.tools; - if (!tools) { return this; } - var isRendered = this.isRendered; - for (var i = 0, n = tools.length; i < n; i++) { - var tool = tools[i]; - if (!isRendered) { - // First update executes render() - tool.render(); - } else if (opt.tool !== tool.cid && tool.isVisible()) { - tool.update(); - } + try { + result = fn(); + } catch (error) { + result = false; } - if (!isRendered) { - this.mount(); - // Make sure tools are visible (if they were hidden and the tool removed) - this.blurTool(); - this.isRendered = true; + + // Cache the test result. + this._results[name] = result; + + return result; + } + }; + + var Generic = Element$1.define('basic.Generic', { + attrs: { + '.': { fill: '#ffffff', stroke: 'none' } + } + }); + + var Rect$1 = Generic.define('basic.Rect', { + attrs: { + 'rect': { + fill: '#ffffff', + stroke: '#000000', + width: 100, + height: 60 + }, + 'text': { + fill: '#000000', + text: '', + 'font-size': 14, + 'ref-x': .5, + 'ref-y': .5, + 'text-anchor': 'middle', + 'y-alignment': 'middle', + 'font-family': 'Arial, helvetica, sans-serif' } - return this; - }, + } + }, { + markup: '' + }); - focusTool: function(focusedTool) { + var TextView = ElementView.extend({ - var tools = this.tools; - if (!tools) { return this; } - for (var i = 0, n = tools.length; i < n; i++) { - var tool = tools[i]; - if (focusedTool === tool) { - tool.show(); - } else { - tool.hide(); - } + presentationAttributes: ElementView.addPresentationAttributes({ + // The element view is not automatically re-scaled to fit the model size + // when the attribute 'attrs' is changed. + attrs: ['SCALE'] + }), + + confirmUpdate: function() { + var flags = ElementView.prototype.confirmUpdate.apply(this, arguments); + if (this.hasFlag(flags, 'SCALE')) { + this.resize(); + flags = this.removeFlag(flags, 'SCALE'); } - return this; - }, + return flags; + } + }); - blurTool: function(blurredTool) { - var tools = this.tools; - if (!tools) { return this; } - for (var i = 0, n = tools.length; i < n; i++) { - var tool = tools[i]; - if (tool !== blurredTool && !tool.isVisible()) { - tool.show(); - tool.update(); - } + var Text = Generic.define('basic.Text', { + attrs: { + 'text': { + 'font-size': 18, + fill: '#000000' } - return this; - }, + } + }, { + markup: '', + }); - hide: function() { - return this.focusTool(null); - }, + var Circle = Generic.define('basic.Circle', { + size: { width: 60, height: 60 }, + attrs: { + 'circle': { + fill: '#ffffff', + stroke: '#000000', + r: 30, + cx: 30, + cy: 30 + }, + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-y': .5, + 'y-alignment': 'middle', + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' + } + } + }, { + markup: '', + }); - show: function() { - return this.blurTool(null); - }, + var Ellipse$1 = Generic.define('basic.Ellipse', { + size: { width: 60, height: 40 }, + attrs: { + 'ellipse': { + fill: '#ffffff', + stroke: '#000000', + rx: 30, + ry: 20, + cx: 30, + cy: 20 + }, + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-y': .5, + 'y-alignment': 'middle', + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' + } + } + }, { + markup: '', + }); - onRemove: function() { - var tools = this.tools; - if (!tools) { return this; } - for (var i = 0, n = tools.length; i < n; i++) { - tools[i].remove(); + var Polygon$1 = Generic.define('basic.Polygon', { + size: { width: 60, height: 40 }, + attrs: { + 'polygon': { + fill: '#ffffff', + stroke: '#000000' + }, + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-dy': 20, + 'y-alignment': 'middle', + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' } - this.tools = null; - }, + } + }, { + markup: '', + }); - mount: function() { - var ref = this; - var options = ref.options; - var el = ref.el; - var relatedView = options.relatedView; - var layer = options.layer; if ( layer === void 0 ) layer = LayersNames.TOOLS; - var z = options.z; - if (relatedView) { - if (layer) { - relatedView.paper.getLayerView(layer).insertSortedNode(el, z); - } else { - relatedView.el.appendChild(el); - } + var Polyline$1 = Generic.define('basic.Polyline', { + size: { width: 60, height: 40 }, + attrs: { + 'polyline': { + fill: '#ffffff', + stroke: '#000000' + }, + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-dy': 20, + 'y-alignment': 'middle', + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' } - return this; } + }, { + markup: '', + }); + var Image = Generic.define('basic.Image', { + attrs: { + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-dy': 20, + 'y-alignment': 'middle', + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' + } + } + }, { + markup: '', }); + var Path$1 = Generic.define('basic.Path', { + size: { width: 60, height: 60 }, + attrs: { + 'path': { + fill: '#ffffff', + stroke: '#000000' + }, + 'text': { + 'font-size': 14, + text: '', + 'text-anchor': 'middle', + 'ref': 'path', + 'ref-x': .5, + 'ref-dy': 10, + fill: '#000000', + 'font-family': 'Arial, helvetica, sans-serif' + } + } + }, { + markup: '', + }); - var index$5 = ({ - Graph: Graph, - attributes: attributes, - LayersNames: LayersNames, - PaperLayer: PaperLayer, - Cell: Cell, - CellView: CellView, - Element: Element$1, - ElementView: ElementView, - Link: Link, - LinkView: LinkView, - Paper: Paper, - ToolView: ToolView, - ToolsView: ToolsView, - HighlighterView: HighlighterView + var Rhombus = Path$1.define('basic.Rhombus', { + attrs: { + 'path': { + d: 'M 30 0 L 60 30 30 60 0 30 z' + }, + 'text': { + 'ref-y': .5, + 'ref-dy': null, + 'y-alignment': 'middle' + } + } }); - var DirectedGraph = { - - exportElement: function(element) { + var svgForeignObjectSupported = env.test('svgforeignobject'); - // The width and height of the element. - return element.size(); + var TextBlock = Generic.define('basic.TextBlock', { + // see joint.css for more element styles + attrs: { + rect: { + fill: '#ffffff', + stroke: '#000000', + width: 80, + height: 100 + }, + text: { + fill: '#000000', + 'font-size': 14, + 'font-family': 'Arial, helvetica, sans-serif' + }, + '.content': { + text: '', + 'ref-x': .5, + 'ref-y': .5, + 'y-alignment': 'middle', + 'x-alignment': 'middle' + } }, - exportLink: function(link) { + content: '' + }, { + markup: [ + '', + '', + svgForeignObjectSupported + ? '
' + : '', + '' + ].join(''), - var labelSize = link.get('labelSize') || {}; - var edge = { - // The number of ranks to keep between the source and target of the edge. - minLen: link.get('minLen') || 1, - // The weight to assign edges. Higher weight edges are generally - // made shorter and straighter than lower weight edges. - weight: link.get('weight') || 1, - // Where to place the label relative to the edge. - // l = left, c = center r = right. - labelpos: link.get('labelPosition') || 'c', - // How many pixels to move the label away from the edge. - // Applies only when labelpos is l or r. - labeloffset: link.get('labelOffset') || 0, - // The width of the edge label in pixels. - width: labelSize.width || 0, - // The height of the edge label in pixels. - height: labelSize.height || 0 - }; + initialize: function() { - return edge; + this.listenTo(this, 'change:size', this.updateSize); + this.listenTo(this, 'change:content', this.updateContent); + this.updateSize(this, this.get('size')); + this.updateContent(this, this.get('content')); + Generic.prototype.initialize.apply(this, arguments); }, - importElement: function(opt, v, gl) { - - var element = this.getCell(v); - var glNode = gl.node(v); + updateSize: function(cell, size) { - if (opt.setPosition) { - opt.setPosition(element, glNode); - } else { - element.set('position', { - x: glNode.x - glNode.width / 2, - y: glNode.y - glNode.height / 2 - }); - } + // Selector `foreignObject' doesn't work across all browsers, we're using class selector instead. + // We have to clone size as we don't want attributes.div.style to be same object as attributes.size. + this.attr({ + '.fobj': assign({}, size), + div: { + style: assign({}, size) + } + }); }, - importLink: function(opt, edgeObj, gl) { + updateContent: function(cell, content) { - var SIMPLIFY_THRESHOLD = 0.001; + if (svgForeignObjectSupported) { - var link = this.getCell(edgeObj.name); - var glEdge = gl.edge(edgeObj); - var points = glEdge.points || []; - var polyline = new Polyline(points); + // Content element is a
element. + this.attr({ + '.content': { + html: sanitizeHTML(content) + } + }); - // check the `setLinkVertices` here for backwards compatibility - if (opt.setVertices || opt.setLinkVertices) { - if (isFunction(opt.setVertices)) { - opt.setVertices(link, points); - } else { - // simplify the `points` polyline - polyline.simplify({ threshold: SIMPLIFY_THRESHOLD }); - var polylinePoints = polyline.points.map(function (point) { return (point.toJSON()); }); // JSON of points after simplification - var numPolylinePoints = polylinePoints.length; // number of points after simplification - // set simplified polyline points as link vertices - // remove first and last polyline points (= source/target sonnectionPoints) - link.set('vertices', polylinePoints.slice(1, numPolylinePoints - 1)); - } - } + } else { - if (opt.setLabels && ('x' in glEdge) && ('y' in glEdge)) { - var labelPosition = { x: glEdge.x, y: glEdge.y }; - if (isFunction(opt.setLabels)) { - opt.setLabels(link, labelPosition, points); - } else { - // convert the absolute label position to a relative position - // towards the closest point on the edge - var length = polyline.closestPointLength(labelPosition); - var closestPoint = polyline.pointAtLength(length); - var distance = (length / polyline.length()); - var offset = new Point(labelPosition).difference(closestPoint).toJSON(); - link.label(0, { - position: { - distance: distance, - offset: offset - } - }); - } + // Content element is a element. + // SVG elements don't have innerHTML attribute. + this.attr({ + '.content': { + text: content + } + }); } }, - layout: function(graphOrCells, opt) { - - var graph; - - if (graphOrCells instanceof Graph) { - graph = graphOrCells; - } else { - // Reset cells in dry mode so the graph reference is not stored on the cells. - // `sort: false` to prevent elements to change their order based on the z-index - graph = (new Graph()).resetCells(graphOrCells, { dry: true, sort: false }); - } + // Here for backwards compatibility: + setForeignObjectSize: function() { - // This is not needed anymore. - graphOrCells = null; + this.updateSize.apply(this, arguments); + }, - opt = defaults(opt || {}, { - resizeClusters: true, - clusterPadding: 10, - exportElement: this.exportElement, - exportLink: this.exportLink - }); + // Here for backwards compatibility: + setDivContent: function() { - /* eslint-disable no-undef */ - var dagreUtil = opt.dagre || (typeof dagre !== 'undefined' ? dagre : undefined); - /* eslint-enable no-undef */ + this.updateContent.apply(this, arguments); + } + }); - if (dagreUtil === undefined) { throw new Error('The the "dagre" utility is a mandatory dependency.'); } + // TextBlockView implements the fallback for IE when no foreignObject exists and + // the text needs to be manually broken. + var TextBlockView = ElementView.extend({ - // create a graphlib.Graph that represents the joint.dia.Graph - // var glGraph = graph.toGraphLib({ - var glGraph = DirectedGraph.toGraphLib(graph, { - graphlib: opt.graphlib, - directed: true, - // We are about to use edge naming feature. - multigraph: true, - // We are able to layout graphs with embeds. - compound: true, - setNodeLabel: opt.exportElement, - setEdgeLabel: opt.exportLink, - setEdgeName: function(link) { - // Graphlib edges have no ids. We use edge name property - // to store and retrieve ids instead. - return link.id; - } - }); + presentationAttributes: svgForeignObjectSupported + ? ElementView.prototype.presentationAttributes + : ElementView.addPresentationAttributes({ + content: ['CONTENT'], + size: ['CONTENT'] + }), - var glLabel = {}; - var marginX = opt.marginX || 0; - var marginY = opt.marginY || 0; + initFlag: ['RENDER', 'CONTENT'], - // Dagre layout accepts options as lower case. - // Direction for rank nodes. Can be TB, BT, LR, or RL - if (opt.rankDir) { glLabel.rankdir = opt.rankDir; } - // Alignment for rank nodes. Can be UL, UR, DL, or DR - if (opt.align) { glLabel.align = opt.align; } - // Number of pixels that separate nodes horizontally in the layout. - if (opt.nodeSep) { glLabel.nodesep = opt.nodeSep; } - // Number of pixels that separate edges horizontally in the layout. - if (opt.edgeSep) { glLabel.edgesep = opt.edgeSep; } - // Number of pixels between each rank in the layout. - if (opt.rankSep) { glLabel.ranksep = opt.rankSep; } - // Type of algorithm to assign a rank to each node in the input graph. - // Possible values: network-simplex, tight-tree or longest-path - if (opt.ranker) { glLabel.ranker = opt.ranker; } - // Number of pixels to use as a margin around the left and right of the graph. - if (marginX) { glLabel.marginx = marginX; } - // Number of pixels to use as a margin around the top and bottom of the graph. - if (marginY) { glLabel.marginy = marginY; } + confirmUpdate: function() { + var flags = ElementView.prototype.confirmUpdate.apply(this, arguments); + if (this.hasFlag(flags, 'CONTENT')) { + this.updateContent(this.model); + flags = this.removeFlag(flags, 'CONTENT'); + } + return flags; + }, - // Set the option object for the graph label. - glGraph.setGraph(glLabel); + update: function(_, renderingOnlyAttrs) { - // Executes the layout. - dagreUtil.layout(glGraph, { debugTiming: !!opt.debugTiming }); + var model = this.model; - // Wrap all graph changes into a batch. - graph.startBatch('layout'); + if (!svgForeignObjectSupported) { - DirectedGraph.fromGraphLib(glGraph, { - importNode: this.importElement.bind(graph, opt), - importEdge: this.importLink.bind(graph, opt) - }); + // Update everything but the content first. + var noTextAttrs = omit(renderingOnlyAttrs || model.get('attrs'), '.content'); + ElementView.prototype.update.call(this, model, noTextAttrs); - // // Update the graph. - // graph.fromGraphLib(glGraph, { - // importNode: this.importElement.bind(graph, opt), - // importEdge: this.importLink.bind(graph, opt) - // }); + if (!renderingOnlyAttrs || has$2(renderingOnlyAttrs, '.content')) { + // Update the content itself. + this.updateContent(model, renderingOnlyAttrs); + } - if (opt.resizeClusters) { - // Resize and reposition cluster elements (parents of other elements) - // to fit their children. - // 1. filter clusters only - // 2. map id on cells - // 3. sort cells by their depth (the deepest first) - // 4. resize cell to fit their direct children only. - var clusters = glGraph.nodes() - .filter(function(v) { return glGraph.children(v).length > 0; }) - .map(graph.getCell.bind(graph)) - .sort(function(aCluster, bCluster) { - return bCluster.getAncestors().length - aCluster.getAncestors().length; - }); + } else { - invoke(clusters, 'fitEmbeds', { padding: opt.clusterPadding }); + ElementView.prototype.update.call(this, model, renderingOnlyAttrs); } - - graph.stopBatch('layout'); - - // Width and height of the graph extended by margins. - var glSize = glGraph.graph(); - // Return the bounding box of the graph after the layout. - return new Rect( - marginX, - marginY, - Math.abs(glSize.width - 2 * marginX), - Math.abs(glSize.height - 2 * marginY) - ); }, - fromGraphLib: function(glGraph, opt) { - - opt = opt || {}; + updateContent: function(cell, renderingOnlyAttrs) { - var importNode = opt.importNode || noop; - var importEdge = opt.importEdge || noop; - var graph = (this instanceof Graph) ? this : new Graph; + // Create copy of the text attributes + var textAttrs = merge({}, (renderingOnlyAttrs || cell.get('attrs'))['.content']); - // Import all nodes. - glGraph.nodes().forEach(function(node) { - importNode.call(graph, node, glGraph, graph, opt); - }); + textAttrs = omit(textAttrs, 'text'); - // Import all edges. - glGraph.edges().forEach(function(edge) { - importEdge.call(graph, edge, glGraph, graph, opt); + // Break the content to fit the element size taking into account the attributes + // set on the model. + var text = breakText(cell.get('content'), cell.get('size'), textAttrs, { + // measuring sandbox svg document + svgDocument: this.paper.svg }); - return graph; - }, - - // Create new graphlib graph from existing JointJS graph. - toGraphLib: function(graph, opt) { - - opt = opt || {}; - - /* eslint-disable no-undef */ - var graphlibUtil = opt.graphlib || (typeof graphlib !== 'undefined' ? graphlib : undefined); - /* eslint-enable no-undef */ - - if (graphlibUtil === undefined) { throw new Error('The the "graphlib" utility is a mandatory dependency.'); } - - var glGraphType = pick(opt, 'directed', 'compound', 'multigraph'); - var glGraph = new graphlibUtil.Graph(glGraphType); - var setNodeLabel = opt.setNodeLabel || noop; - var setEdgeLabel = opt.setEdgeLabel || noop; - var setEdgeName = opt.setEdgeName || noop; - var collection = graph.get('cells'); - - for (var i = 0, n = collection.length; i < n; i++) { - - var cell = collection.at(i); - if (cell.isLink()) { - - var source = cell.get('source'); - var target = cell.get('target'); + // Create a new attrs with same structure as the model attrs { text: { *textAttributes* }} + var attrs = setByPath({}, '.content', textAttrs, '/'); - // Links that end at a point are ignored. - if (!source.id || !target.id) { break; } + // Replace text attribute with the one we just processed. + attrs['.content'].text = text; - // Note that if we are creating a multigraph we can name the edges. If - // we try to name edges on a non-multigraph an exception is thrown. - glGraph.setEdge(source.id, target.id, setEdgeLabel(cell), setEdgeName(cell)); + // Update the view using renderingOnlyAttributes parameter. + ElementView.prototype.update.call(this, cell, attrs); + } + }); - } else { + var basic = ({ + Generic: Generic, + Rect: Rect$1, + TextView: TextView, + Text: Text, + Circle: Circle, + Ellipse: Ellipse$1, + Polygon: Polygon$1, + Polyline: Polyline$1, + Image: Image, + Path: Path$1, + Rhombus: Rhombus, + TextBlock: TextBlock, + TextBlockView: TextBlockView + }); - glGraph.setNode(cell.id, setNodeLabel(cell)); + // ELEMENTS - // For the compound graphs we have to take embeds into account. - if (glGraph.isCompound() && cell.has('parent')) { - var parentId = cell.get('parent'); - if (collection.has(parentId)) { - // Make sure the parent cell is included in the graph (this can - // happen when the layout is run on part of the graph only). - glGraph.setParent(cell.id, parentId); - } - } - } + var Rectangle = Element$1.define('standard.Rectangle', { + attrs: { + body: { + refWidth: '100%', + refHeight: '100%', + strokeWidth: 2, + stroke: '#000000', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' } - - return glGraph; } - }; + }, { + markup: [{ + tagName: 'rect', + selector: 'body', + }, { + tagName: 'text', + selector: 'label' + }] + }); - Graph.prototype.toGraphLib = function(opt) { + var Circle$1 = Element$1.define('standard.Circle', { + attrs: { + body: { + refCx: '50%', + refCy: '50%', + refR: '50%', + strokeWidth: 2, + stroke: '#333333', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' + } + } + }, { + markup: [{ + tagName: 'circle', + selector: 'body' + }, { + tagName: 'text', + selector: 'label' + }] + }); - return DirectedGraph.toGraphLib(this, opt); - }; + var Ellipse$2 = Element$1.define('standard.Ellipse', { + attrs: { + body: { + refCx: '50%', + refCy: '50%', + refRx: '50%', + refRy: '50%', + strokeWidth: 2, + stroke: '#333333', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' + } + } + }, { + markup: [{ + tagName: 'ellipse', + selector: 'body' + }, { + tagName: 'text', + selector: 'label' + }] + }); - Graph.prototype.fromGraphLib = function(glGraph, opt) { + var Path$2 = Element$1.define('standard.Path', { + attrs: { + body: { + refD: 'M 0 0 L 10 0 10 10 0 10 Z', + strokeWidth: 2, + stroke: '#333333', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' + } + } + }, { + markup: [{ + tagName: 'path', + selector: 'body' + }, { + tagName: 'text', + selector: 'label' + }] + }); - return DirectedGraph.fromGraphLib.call(this, glGraph, opt); - }; + var Polygon$2 = Element$1.define('standard.Polygon', { + attrs: { + body: { + refPoints: '0 0 10 0 10 10 0 10', + strokeWidth: 2, + stroke: '#333333', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' + } + } + }, { + markup: [{ + tagName: 'polygon', + selector: 'body' + }, { + tagName: 'text', + selector: 'label' + }] + }); - var Generic = Element$1.define('basic.Generic', { + var Polyline$2 = Element$1.define('standard.Polyline', { attrs: { - '.': { fill: '#ffffff', stroke: 'none' } + body: { + refPoints: '0 0 10 0 10 10 0 10 0 0', + strokeWidth: 2, + stroke: '#333333', + fill: '#FFFFFF' + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + fontSize: 14, + fill: '#333333' + } } + }, { + markup: [{ + tagName: 'polyline', + selector: 'body' + }, { + tagName: 'text', + selector: 'label' + }] }); - var Rect$1 = Generic.define('basic.Rect', { + var Image$1 = Element$1.define('standard.Image', { attrs: { - 'rect': { - fill: '#ffffff', - stroke: '#000000', - width: 100, - height: 60 + image: { + refWidth: '100%', + refHeight: '100%', + // xlinkHref: '[URL]' }, - 'text': { - fill: '#000000', - text: '', - 'font-size': 14, - 'ref-x': .5, - 'ref-y': .5, - 'text-anchor': 'middle', - 'y-alignment': 'middle', - 'font-family': 'Arial, helvetica, sans-serif' + label: { + textVerticalAnchor: 'top', + textAnchor: 'middle', + refX: '50%', + refY: '100%', + refY2: 10, + fontSize: 14, + fill: '#333333' } } }, { - markup: '' + markup: [{ + tagName: 'image', + selector: 'image' + }, { + tagName: 'text', + selector: 'label' + }] }); - var TextView = ElementView.extend({ - - presentationAttributes: ElementView.addPresentationAttributes({ - // The element view is not automatically re-scaled to fit the model size - // when the attribute 'attrs' is changed. - attrs: ['SCALE'] - }), - - confirmUpdate: function() { - var flags = ElementView.prototype.confirmUpdate.apply(this, arguments); - if (this.hasFlag(flags, 'SCALE')) { - this.resize(); - flags = this.removeFlag(flags, 'SCALE'); + var BorderedImage = Element$1.define('standard.BorderedImage', { + attrs: { + border: { + refWidth: '100%', + refHeight: '100%', + stroke: '#333333', + strokeWidth: 2 + }, + background: { + refWidth: -1, + refHeight: -1, + x: 0.5, + y: 0.5, + fill: '#FFFFFF' + }, + image: { + // xlinkHref: '[URL]' + refWidth: -1, + refHeight: -1, + x: 0.5, + y: 0.5 + }, + label: { + textVerticalAnchor: 'top', + textAnchor: 'middle', + refX: '50%', + refY: '100%', + refY2: 10, + fontSize: 14, + fill: '#333333' } - return flags; } + }, { + markup: [{ + tagName: 'rect', + selector: 'background', + attributes: { + 'stroke': 'none' + } + }, { + tagName: 'image', + selector: 'image' + }, { + tagName: 'rect', + selector: 'border', + attributes: { + 'fill': 'none' + } + }, { + tagName: 'text', + selector: 'label' + }] }); - var Text = Generic.define('basic.Text', { + var EmbeddedImage = Element$1.define('standard.EmbeddedImage', { attrs: { - 'text': { - 'font-size': 18, - fill: '#000000' + body: { + refWidth: '100%', + refHeight: '100%', + stroke: '#333333', + fill: '#FFFFFF', + strokeWidth: 2 + }, + image: { + // xlinkHref: '[URL]' + refWidth: '30%', + refHeight: -20, + x: 10, + y: 10, + preserveAspectRatio: 'xMidYMin' + }, + label: { + textVerticalAnchor: 'top', + textAnchor: 'left', + refX: '30%', + refX2: 20, // 10 + 10 + refY: 10, + fontSize: 14, + fill: '#333333' } } }, { - markup: '', + markup: [{ + tagName: 'rect', + selector: 'body' + }, { + tagName: 'image', + selector: 'image' + }, { + tagName: 'text', + selector: 'label' + }] }); - var Circle = Generic.define('basic.Circle', { - size: { width: 60, height: 60 }, + var InscribedImage = Element$1.define('standard.InscribedImage', { attrs: { - 'circle': { - fill: '#ffffff', - stroke: '#000000', - r: 30, - cx: 30, - cy: 30 + border: { + refRx: '50%', + refRy: '50%', + refCx: '50%', + refCy: '50%', + stroke: '#333333', + strokeWidth: 2 }, - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-y': .5, - 'y-alignment': 'middle', - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + background: { + refRx: '50%', + refRy: '50%', + refCx: '50%', + refCy: '50%', + fill: '#FFFFFF' + }, + image: { + // The image corners touch the border when its size is Math.sqrt(2) / 2 = 0.707.. ~= 70% + refWidth: '68%', + refHeight: '68%', + // The image offset is calculated as (100% - 68%) / 2 + refX: '16%', + refY: '16%', + preserveAspectRatio: 'xMidYMid' + // xlinkHref: '[URL]' + }, + label: { + textVerticalAnchor: 'top', + textAnchor: 'middle', + refX: '50%', + refY: '100%', + refY2: 10, + fontSize: 14, + fill: '#333333' } } }, { - markup: '', + markup: [{ + tagName: 'ellipse', + selector: 'background' + }, { + tagName: 'image', + selector: 'image' + }, { + tagName: 'ellipse', + selector: 'border', + attributes: { + 'fill': 'none' + } + }, { + tagName: 'text', + selector: 'label' + }] }); - var Ellipse$1 = Generic.define('basic.Ellipse', { - size: { width: 60, height: 40 }, + var HeaderedRectangle = Element$1.define('standard.HeaderedRectangle', { attrs: { - 'ellipse': { - fill: '#ffffff', + body: { + refWidth: '100%', + refHeight: '100%', + strokeWidth: 2, stroke: '#000000', - rx: 30, - ry: 20, - cx: 30, - cy: 20 + fill: '#FFFFFF' }, - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-y': .5, - 'y-alignment': 'middle', - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + header: { + refWidth: '100%', + height: 30, + strokeWidth: 2, + stroke: '#000000', + fill: '#FFFFFF' + }, + headerText: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: 15, + fontSize: 16, + fill: '#333333' + }, + bodyText: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '50%', + refY2: 15, + fontSize: 14, + fill: '#333333' } } }, { - markup: '', + markup: [{ + tagName: 'rect', + selector: 'body' + }, { + tagName: 'rect', + selector: 'header' + }, { + tagName: 'text', + selector: 'headerText' + }, { + tagName: 'text', + selector: 'bodyText' + }] }); - var Polygon$1 = Generic.define('basic.Polygon', { - size: { width: 60, height: 40 }, + var CYLINDER_TILT = 10; + + var Cylinder = Element$1.define('standard.Cylinder', { attrs: { - 'polygon': { - fill: '#ffffff', - stroke: '#000000' + body: { + lateralArea: CYLINDER_TILT, + fill: '#FFFFFF', + stroke: '#333333', + strokeWidth: 2 }, - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-dy': 20, - 'y-alignment': 'middle', - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + top: { + refCx: '50%', + cy: CYLINDER_TILT, + refRx: '50%', + ry: CYLINDER_TILT, + fill: '#FFFFFF', + stroke: '#333333', + strokeWidth: 2 + }, + label: { + textVerticalAnchor: 'middle', + textAnchor: 'middle', + refX: '50%', + refY: '100%', + refY2: 15, + fontSize: 14, + fill: '#333333' } } }, { - markup: '', + markup: [{ + tagName: 'path', + selector: 'body' + }, { + tagName: 'ellipse', + selector: 'top' + }, { + tagName: 'text', + selector: 'label' + }], + + topRy: function(t, opt) { + // getter + if (t === undefined) { return this.attr('body/lateralArea'); } + + // setter + var isPercentageSetter = isPercentage(t); + + var bodyAttrs = { lateralArea: t }; + var topAttrs = isPercentageSetter + ? { refCy: t, refRy: t, cy: null, ry: null } + : { refCy: null, refRy: null, cy: t, ry: t }; + + return this.attr({ body: bodyAttrs, top: topAttrs }, opt); + } + + }, { + attributes: { + lateralArea: { + set: function(t, refBBox) { + var isPercentageSetter = isPercentage(t); + if (isPercentageSetter) { t = parseFloat(t) / 100; } + + var x = refBBox.x; + var y = refBBox.y; + var w = refBBox.width; + var h = refBBox.height; + + // curve control point variables + var rx = w / 2; + var ry = isPercentageSetter ? (h * t) : t; + + var kappa = V.KAPPA; + var cx = kappa * rx; + var cy = kappa * (isPercentageSetter ? (h * t) : t); + + // shape variables + var xLeft = x; + var xCenter = x + (w / 2); + var xRight = x + w; + + var ySideTop = y + ry; + var yCurveTop = ySideTop - ry; + var ySideBottom = y + h - ry; + var yCurveBottom = y + h; + + // return calculated shape + var data = [ + 'M', xLeft, ySideTop, + 'L', xLeft, ySideBottom, + 'C', x, (ySideBottom + cy), (xCenter - cx), yCurveBottom, xCenter, yCurveBottom, + 'C', (xCenter + cx), yCurveBottom, xRight, (ySideBottom + cy), xRight, ySideBottom, + 'L', xRight, ySideTop, + 'C', xRight, (ySideTop - cy), (xCenter + cx), yCurveTop, xCenter, yCurveTop, + 'C', (xCenter - cx), yCurveTop, xLeft, (ySideTop - cy), xLeft, ySideTop, + 'Z' + ]; + return { d: data.join(' ') }; + } + } + } }); - var Polyline$1 = Generic.define('basic.Polyline', { - size: { width: 60, height: 40 }, + var foLabelMarkup = { + tagName: 'foreignObject', + selector: 'foreignObject', + attributes: { + 'overflow': 'hidden' + }, + children: [{ + tagName: 'div', + namespaceURI: 'http://www.w3.org/1999/xhtml', + selector: 'label', + style: { + width: '100%', + height: '100%', + position: 'static', + backgroundColor: 'transparent', + textAlign: 'center', + margin: 0, + padding: '0px 5px', + boxSizing: 'border-box', + display: 'flex', + alignItems: 'center', + justifyContent: 'center' + } + }] + }; + + var svgLabelMarkup = { + tagName: 'text', + selector: 'label', + attributes: { + 'text-anchor': 'middle' + } + }; + + var labelMarkup = (env.test('svgforeignobject')) ? foLabelMarkup : svgLabelMarkup; + + var TextBlock$1 = Element$1.define('standard.TextBlock', { attrs: { - 'polyline': { + body: { + refWidth: '100%', + refHeight: '100%', + stroke: '#333333', fill: '#ffffff', - stroke: '#000000' + strokeWidth: 2 }, - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-dy': 20, - 'y-alignment': 'middle', - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + foreignObject: { + refWidth: '100%', + refHeight: '100%' + }, + label: { + style: { + fontSize: 14 + } } } }, { - markup: '', + markup: [{ + tagName: 'rect', + selector: 'body' + }, labelMarkup] + }, { + attributes: { + text: { + set: function(text, refBBox, node, attrs) { + if (node instanceof HTMLElement) { + node.textContent = text; + } else { + // No foreign object + var style = attrs.style || {}; + var wrapValue = { text: text, width: -5, height: '100%' }; + var wrapAttrs = assign({ textVerticalAnchor: 'middle' }, style); + attributes.textWrap.set.call(this, wrapValue, refBBox, node, wrapAttrs); + return { fill: style.color || null }; + } + }, + position: function(text, refBBox, node) { + // No foreign object + if (node instanceof SVGElement) { return refBBox.center(); } + } + } + } }); - var Image = Generic.define('basic.Image', { + // LINKS + + var Link$1 = Link.define('standard.Link', { attrs: { - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-dy': 20, - 'y-alignment': 'middle', - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + line: { + connection: true, + stroke: '#333333', + strokeWidth: 2, + strokeLinejoin: 'round', + targetMarker: { + 'type': 'path', + 'd': 'M 10 -5 0 0 10 5 z' + } + }, + wrapper: { + connection: true, + strokeWidth: 10, + strokeLinejoin: 'round' } } }, { - markup: '', + markup: [{ + tagName: 'path', + selector: 'wrapper', + attributes: { + 'fill': 'none', + 'cursor': 'pointer', + 'stroke': 'transparent', + 'stroke-linecap': 'round' + } + }, { + tagName: 'path', + selector: 'line', + attributes: { + 'fill': 'none', + 'pointer-events': 'none' + } + }] }); - var Path$1 = Generic.define('basic.Path', { - size: { width: 60, height: 60 }, + var DoubleLink = Link.define('standard.DoubleLink', { attrs: { - 'path': { - fill: '#ffffff', - stroke: '#000000' + line: { + connection: true, + stroke: '#DDDDDD', + strokeWidth: 4, + strokeLinejoin: 'round', + targetMarker: { + type: 'path', + stroke: '#000000', + d: 'M 10 -3 10 -10 -2 0 10 10 10 3' + } }, - 'text': { - 'font-size': 14, - text: '', - 'text-anchor': 'middle', - 'ref': 'path', - 'ref-x': .5, - 'ref-dy': 10, - fill: '#000000', - 'font-family': 'Arial, helvetica, sans-serif' + outline: { + connection: true, + stroke: '#000000', + strokeWidth: 6, + strokeLinejoin: 'round' } } - }, { - markup: '', + markup: [{ + tagName: 'path', + selector: 'outline', + attributes: { + 'fill': 'none' + } + }, { + tagName: 'path', + selector: 'line', + attributes: { + 'fill': 'none' + } + }] }); - var Rhombus = Path$1.define('basic.Rhombus', { + var ShadowLink = Link.define('standard.ShadowLink', { attrs: { - 'path': { - d: 'M 30 0 L 60 30 30 60 0 30 z' + line: { + connection: true, + stroke: '#FF0000', + strokeWidth: 20, + strokeLinejoin: 'round', + targetMarker: { + 'type': 'path', + 'stroke': 'none', + 'd': 'M 0 -10 -10 0 0 10 z' + }, + sourceMarker: { + 'type': 'path', + 'stroke': 'none', + 'd': 'M -10 -10 0 0 -10 10 0 10 0 -10 z' + } }, - 'text': { - 'ref-y': .5, - 'ref-dy': null, - 'y-alignment': 'middle' + shadow: { + connection: true, + refX: 3, + refY: 6, + stroke: '#000000', + strokeOpacity: 0.2, + strokeWidth: 20, + strokeLinejoin: 'round', + targetMarker: { + 'type': 'path', + 'd': 'M 0 -10 -10 0 0 10 z', + 'stroke': 'none' + }, + sourceMarker: { + 'type': 'path', + 'stroke': 'none', + 'd': 'M -10 -10 0 0 -10 10 0 10 0 -10 z' + } } } + }, { + markup: [{ + tagName: 'path', + selector: 'shadow', + attributes: { + 'fill': 'none' + } + }, { + tagName: 'path', + selector: 'line', + attributes: { + 'fill': 'none' + } + }] }); - var svgForeignObjectSupported = env.test('svgforeignobject'); + var standard = ({ + Rectangle: Rectangle, + Circle: Circle$1, + Ellipse: Ellipse$2, + Path: Path$2, + Polygon: Polygon$2, + Polyline: Polyline$2, + Image: Image$1, + BorderedImage: BorderedImage, + EmbeddedImage: EmbeddedImage, + InscribedImage: InscribedImage, + HeaderedRectangle: HeaderedRectangle, + Cylinder: Cylinder, + TextBlock: TextBlock$1, + Link: Link$1, + DoubleLink: DoubleLink, + ShadowLink: ShadowLink + }); - var TextBlock = Generic.define('basic.TextBlock', { - // see joint.css for more element styles + /** + * @deprecated use the port api instead + */ + var Model = Generic.define('devs.Model', { + inPorts: [], + outPorts: [], + size: { + width: 80, + height: 80 + }, attrs: { - rect: { - fill: '#ffffff', - stroke: '#000000', - width: 80, - height: 100 - }, - text: { - fill: '#000000', - 'font-size': 14, - 'font-family': 'Arial, helvetica, sans-serif' + '.': { + magnet: false }, - '.content': { - text: '', + '.label': { + text: 'Model', 'ref-x': .5, - 'ref-y': .5, - 'y-alignment': 'middle', - 'x-alignment': 'middle' + 'ref-y': 10, + 'font-size': 18, + 'text-anchor': 'middle', + fill: '#000' + }, + '.body': { + 'ref-width': '100%', + 'ref-height': '100%', + stroke: '#000' } }, - - content: '' + ports: { + groups: { + 'in': { + position: { + name: 'left' + }, + attrs: { + '.port-label': { + fill: '#000' + }, + '.port-body': { + fill: '#fff', + stroke: '#000', + r: 10, + magnet: true + } + }, + label: { + position: { + name: 'left', + args: { + y: 10 + } + } + } + }, + 'out': { + position: { + name: 'right' + }, + attrs: { + '.port-label': { + fill: '#000' + }, + '.port-body': { + fill: '#fff', + stroke: '#000', + r: 10, + magnet: true + } + }, + label: { + position: { + name: 'right', + args: { + y: 10 + } + } + } + } + } + } }, { - markup: [ - '', - '', - svgForeignObjectSupported - ? '
' - : '', - '' - ].join(''), + markup: '', + portMarkup: '', + portLabelMarkup: '', initialize: function() { - this.listenTo(this, 'change:size', this.updateSize); - this.listenTo(this, 'change:content', this.updateContent); - this.updateSize(this, this.get('size')); - this.updateContent(this, this.get('content')); Generic.prototype.initialize.apply(this, arguments); - }, - - updateSize: function(cell, size) { - // Selector `foreignObject' doesn't work across all browsers, we're using class selector instead. - // We have to clone size as we don't want attributes.div.style to be same object as attributes.size. - this.attr({ - '.fobj': assign({}, size), - div: { - style: assign({}, size) - } - }); + this.on('change:inPorts change:outPorts', this.updatePortItems, this); + this.updatePortItems(); }, - updateContent: function(cell, content) { + updatePortItems: function(model, changed, opt) { - if (svgForeignObjectSupported) { + // Make sure all ports are unique. + var inPorts = uniq(this.get('inPorts')); + var outPorts = difference(uniq(this.get('outPorts')), inPorts); - // Content element is a
element. - this.attr({ - '.content': { - html: sanitizeHTML(content) - } - }); + var inPortItems = this.createPortItems('in', inPorts); + var outPortItems = this.createPortItems('out', outPorts); - } else { + this.prop('ports/items', inPortItems.concat(outPortItems), assign({ rewrite: true }, opt)); + }, - // Content element is a element. - // SVG elements don't have innerHTML attribute. - this.attr({ - '.content': { - text: content + createPortItem: function(group, port) { + + return { + id: port, + group: group, + attrs: { + '.port-label': { + text: port } - }); - } + } + }; }, - // Here for backwards compatibility: - setForeignObjectSize: function() { + createPortItems: function(group, ports) { - this.updateSize.apply(this, arguments); + return toArray(ports).map(this.createPortItem.bind(this, group)); }, - // Here for backwards compatibility: - setDivContent: function() { + _addGroupPort: function(port, group, opt) { - this.updateContent.apply(this, arguments); - } - }); + var ports = this.get(group); + return this.set(group, Array.isArray(ports) ? ports.concat(port) : [port], opt); + }, - // TextBlockView implements the fallback for IE when no foreignObject exists and - // the text needs to be manually broken. - var TextBlockView = ElementView.extend({ + addOutPort: function(port, opt) { - presentationAttributes: svgForeignObjectSupported - ? ElementView.prototype.presentationAttributes - : ElementView.addPresentationAttributes({ - content: ['CONTENT'], - size: ['CONTENT'] - }), + return this._addGroupPort(port, 'outPorts', opt); + }, - initFlag: ['RENDER', 'CONTENT'], + addInPort: function(port, opt) { - confirmUpdate: function() { - var flags = ElementView.prototype.confirmUpdate.apply(this, arguments); - if (this.hasFlag(flags, 'CONTENT')) { - this.updateContent(this.model); - flags = this.removeFlag(flags, 'CONTENT'); - } - return flags; + return this._addGroupPort(port, 'inPorts', opt); }, - update: function(_, renderingOnlyAttrs) { - - var model = this.model; + _removeGroupPort: function(port, group, opt) { - if (!svgForeignObjectSupported) { + return this.set(group, without(this.get(group), port), opt); + }, - // Update everything but the content first. - var noTextAttrs = omit(renderingOnlyAttrs || model.get('attrs'), '.content'); - ElementView.prototype.update.call(this, model, noTextAttrs); + removeOutPort: function(port, opt) { - if (!renderingOnlyAttrs || has$2(renderingOnlyAttrs, '.content')) { - // Update the content itself. - this.updateContent(model, renderingOnlyAttrs); - } + return this._removeGroupPort(port, 'outPorts', opt); + }, - } else { + removeInPort: function(port, opt) { - ElementView.prototype.update.call(this, model, renderingOnlyAttrs); - } + return this._removeGroupPort(port, 'inPorts', opt); }, - updateContent: function(cell, renderingOnlyAttrs) { - - // Create copy of the text attributes - var textAttrs = merge({}, (renderingOnlyAttrs || cell.get('attrs'))['.content']); + _changeGroup: function(group, properties, opt) { - textAttrs = omit(textAttrs, 'text'); + return this.prop('ports/groups/' + group, isObject$1(properties) ? properties : {}, opt); + }, - // Break the content to fit the element size taking into account the attributes - // set on the model. - var text = breakText(cell.get('content'), cell.get('size'), textAttrs, { - // measuring sandbox svg document - svgDocument: this.paper.svg - }); + changeInGroup: function(properties, opt) { - // Create a new attrs with same structure as the model attrs { text: { *textAttributes* }} - var attrs = setByPath({}, '.content', textAttrs, '/'); + return this._changeGroup('in', properties, opt); + }, - // Replace text attribute with the one we just processed. - attrs['.content'].text = text; + changeOutGroup: function(properties, opt) { - // Update the view using renderingOnlyAttributes parameter. - ElementView.prototype.update.call(this, cell, attrs); + return this._changeGroup('out', properties, opt); } }); - var basic = ({ - Generic: Generic, - Rect: Rect$1, - TextView: TextView, - Text: Text, - Circle: Circle, - Ellipse: Ellipse$1, - Polygon: Polygon$1, - Polyline: Polyline$1, - Image: Image, - Path: Path$1, - Rhombus: Rhombus, - TextBlock: TextBlock, - TextBlockView: TextBlockView - }); - - // ELEMENTS - - var Rectangle = Element$1.define('standard.Rectangle', { + var Atomic = Model.define('devs.Atomic', { + size: { + width: 80, + height: 80 + }, attrs: { - body: { - refWidth: '100%', - refHeight: '100%', - strokeWidth: 2, - stroke: '#000000', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' + '.label': { + text: 'Atomic' } } - }, { - markup: [{ - tagName: 'rect', - selector: 'body', - }, { - tagName: 'text', - selector: 'label' - }] }); - var Circle$1 = Element$1.define('standard.Circle', { + var Coupled = Model.define('devs.Coupled', { + size: { + width: 200, + height: 300 + }, attrs: { - body: { - refCx: '50%', - refCy: '50%', - refR: '50%', - strokeWidth: 2, - stroke: '#333333', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' + '.label': { + text: 'Coupled' } } - }, { - markup: [{ - tagName: 'circle', - selector: 'body' - }, { - tagName: 'text', - selector: 'label' - }] }); - var Ellipse$2 = Element$1.define('standard.Ellipse', { + var Link$2 = Link.define('devs.Link', { attrs: { - body: { - refCx: '50%', - refCy: '50%', - refRx: '50%', - refRy: '50%', - strokeWidth: 2, - stroke: '#333333', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' + '.connection': { + 'stroke-width': 2 } } - }, { - markup: [{ - tagName: 'ellipse', - selector: 'body' - }, { - tagName: 'text', - selector: 'label' - }] }); - var Path$2 = Element$1.define('standard.Path', { + var devs = ({ + Model: Model, + Atomic: Atomic, + Coupled: Coupled, + Link: Link$2 + }); + + var Gate = Generic.define('logic.Gate', { + size: { width: 80, height: 40 }, + attrs: { + '.': { magnet: false }, + '.body': { width: 100, height: 50 }, + circle: { r: 7, stroke: 'black', fill: 'transparent', 'stroke-width': 2 } + } + }, { + operation: function() { + return true; + } + }); + + var IO = Gate.define('logic.IO', { + size: { width: 60, height: 30 }, attrs: { - body: { - refD: 'M 0 0 L 10 0 10 10 0 10 Z', - strokeWidth: 2, - stroke: '#333333', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' + '.body': { fill: 'white', stroke: 'black', 'stroke-width': 2 }, + '.wire': { ref: '.body', 'ref-y': .5, stroke: 'black' }, + text: { + fill: 'black', + ref: '.body', 'ref-x': .5, 'ref-y': .5, 'y-alignment': 'middle', + 'text-anchor': 'middle', + 'font-weight': 'bold', + 'font-variant': 'small-caps', + 'text-transform': 'capitalize', + 'font-size': '14px' } } }, { - markup: [{ - tagName: 'path', - selector: 'body' - }, { - tagName: 'text', - selector: 'label' - }] + markup: '', }); - var Polygon$2 = Element$1.define('standard.Polygon', { + var Input = IO.define('logic.Input', { attrs: { - body: { - refPoints: '0 0 10 0 10 10 0 10', - strokeWidth: 2, - stroke: '#333333', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' - } + '.wire': { 'ref-dx': 0, d: 'M 0 0 L 23 0' }, + circle: { ref: '.body', 'ref-dx': 30, 'ref-y': 0.5, magnet: true, 'class': 'output', port: 'out' }, + text: { text: 'input' } } - }, { - markup: [{ - tagName: 'polygon', - selector: 'body' - }, { - tagName: 'text', - selector: 'label' - }] }); - var Polyline$2 = Element$1.define('standard.Polyline', { + var Output = IO.define('logic.Output', { attrs: { - body: { - refPoints: '0 0 10 0 10 10 0 10 0 0', - strokeWidth: 2, - stroke: '#333333', - fill: '#FFFFFF' - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - fontSize: 14, - fill: '#333333' - } + '.wire': { 'ref-x': 0, d: 'M 0 0 L -23 0' }, + circle: { ref: '.body', 'ref-x': -30, 'ref-y': 0.5, magnet: 'passive', 'class': 'input', port: 'in' }, + text: { text: 'output' } } - }, { - markup: [{ - tagName: 'polyline', - selector: 'body' - }, { - tagName: 'text', - selector: 'label' - }] }); - var Image$1 = Element$1.define('standard.Image', { + var Gate11 = Gate.define('logic.Gate11', { attrs: { - image: { - refWidth: '100%', - refHeight: '100%', - // xlinkHref: '[URL]' - }, - label: { - textVerticalAnchor: 'top', - textAnchor: 'middle', - refX: '50%', - refY: '100%', - refY2: 10, - fontSize: 14, - fill: '#333333' - } + '.input': { ref: '.body', 'ref-x': -2, 'ref-y': 0.5, magnet: 'passive', port: 'in' }, + '.output': { ref: '.body', 'ref-dx': 2, 'ref-y': 0.5, magnet: true, port: 'out' } } }, { - markup: [{ - tagName: 'image', - selector: 'image' - }, { - tagName: 'text', - selector: 'label' - }] + markup: '', }); - var BorderedImage = Element$1.define('standard.BorderedImage', { + var Gate21 = Gate.define('logic.Gate21', { attrs: { - border: { - refWidth: '100%', - refHeight: '100%', - stroke: '#333333', - strokeWidth: 2 - }, - background: { - refWidth: -1, - refHeight: -1, - x: 0.5, - y: 0.5, - fill: '#FFFFFF' - }, - image: { - // xlinkHref: '[URL]' - refWidth: -1, - refHeight: -1, - x: 0.5, - y: 0.5 - }, - label: { - textVerticalAnchor: 'top', - textAnchor: 'middle', - refX: '50%', - refY: '100%', - refY2: 10, - fontSize: 14, - fill: '#333333' - } + '.input1': { ref: '.body', 'ref-x': -2, 'ref-y': 0.3, magnet: 'passive', port: 'in1' }, + '.input2': { ref: '.body', 'ref-x': -2, 'ref-y': 0.7, magnet: 'passive', port: 'in2' }, + '.output': { ref: '.body', 'ref-dx': 2, 'ref-y': 0.5, magnet: true, port: 'out' } } }, { - markup: [{ - tagName: 'rect', - selector: 'background', - attributes: { - 'stroke': 'none' - } - }, { - tagName: 'image', - selector: 'image' - }, { - tagName: 'rect', - selector: 'border', - attributes: { - 'fill': 'none' - } - }, { - tagName: 'text', - selector: 'label' - }] + markup: '', }); - var EmbeddedImage = Element$1.define('standard.EmbeddedImage', { - attrs: { - body: { - refWidth: '100%', - refHeight: '100%', - stroke: '#333333', - fill: '#FFFFFF', - strokeWidth: 2 - }, - image: { - // xlinkHref: '[URL]' - refWidth: '30%', - refHeight: -20, - x: 10, - y: 10, - preserveAspectRatio: 'xMidYMin' - }, - label: { - textVerticalAnchor: 'top', - textAnchor: 'left', - refX: '30%', - refX2: 20, // 10 + 10 - refY: 10, - fontSize: 14, - fill: '#333333' - } + var Repeater = Gate11.define('logic.Repeater', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzIuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICA8L2c+Cjwvc3ZnPgo=' }} + }, { + operation: function(input) { + return input; } + }); + + var Not = Gate11.define('logic.Not', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzkuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI2NzEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzYiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA4MCwyNSBBIDQsNCAwIDEgMSA3MiwyNSBBIDQsNCAwIDEgMSA4MCwyNSB6IgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEsMCkiIC8+CiAgPC9nPgo8L3N2Zz4K' }} }, { - markup: [{ - tagName: 'rect', - selector: 'body' - }, { - tagName: 'image', - selector: 'image' - }, { - tagName: 'text', - selector: 'label' - }] + operation: function(input) { + return !input; + } }); - var InscribedImage = Element$1.define('standard.InscribedImage', { - attrs: { - border: { - refRx: '50%', - refRy: '50%', - refCx: '50%', - refCy: '50%', - stroke: '#333333', - strokeWidth: 2 - }, - background: { - refRx: '50%', - refRy: '50%', - refCx: '50%', - refCy: '50%', - fill: '#FFFFFF' - }, - image: { - // The image corners touch the border when its size is Math.sqrt(2) / 2 = 0.707.. ~= 70% - refWidth: '68%', - refHeight: '68%', - // The image offset is calculated as (100% - 68%) / 2 - refX: '16%', - refY: '16%', - preserveAspectRatio: 'xMidYMid' - // xlinkHref: '[URL]' - }, - label: { - textVerticalAnchor: 'top', - textAnchor: 'middle', - refX: '50%', - refY: '100%', - refY2: 10, - fontSize: 14, - fill: '#333333' - } + var Or = Gate21.define('logic.Or', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik9SIEFOU0kuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTAgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjcxNCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iMSA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMC41IDogMC4zMzMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODA2IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODE5IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjM3Mi4wNDcyNCA6IDM1MC43ODczOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI3NDQuMDk0NDggOiA1MjYuMTgxMDkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzc3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49Ijc1IDogNDAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTUwIDogNjAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDYwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTMyNzUiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNTAgOiAzMy4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTAwIDogNTAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTU1MzMiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjU1NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDE2LjY2NjY2NyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDI1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAyNSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iNCIKICAgICBpbmtzY2FwZTpjeD0iMTEzLjAwMDM5IgogICAgIGlua3NjYXBlOmN5PSIxMi44OTM3MzEiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImcyNTYwIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1wb2ludHM9InRydWUiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAwMDAiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMzk5IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg3NCIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMzciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii00IgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNzAsMjUgYyAyMCwwIDI1LDAgMjUsMCIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMSwxNSA1LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzIsMzUgNSwzNSIKICAgICAgIGlkPSJwYXRoMzk0NCIgLz4KICAgIDxnCiAgICAgICBpZD0iZzI1NjAiCiAgICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2LjUsLTM5LjUpIj4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=' }} + }, { + operation: function(input1, input2) { + return input1 || input2; } + }); + + var And = Gate21.define('logic.And', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IkFORCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI4IgogICAgIGlua3NjYXBlOmN4PSI1Ni42OTgzNDgiCiAgICAgaW5rc2NhcGU6Y3k9IjI1LjMyNjg5OSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTptZWRpdW07Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWluZGVudDowO3RleHQtYWxpZ246c3RhcnQ7dGV4dC1kZWNvcmF0aW9uOm5vbmU7bGluZS1oZWlnaHQ6bm9ybWFsO2xldHRlci1zcGFjaW5nOm5vcm1hbDt3b3JkLXNwYWNpbmc6bm9ybWFsO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlyZWN0aW9uOmx0cjtibG9jay1wcm9ncmVzc2lvbjp0Yjt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDozO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGU7Zm9udC1mYW1pbHk6Qml0c3RyZWFtIFZlcmEgU2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkJpdHN0cmVhbSBWZXJhIFNhbnMiCiAgICAgICBkPSJNIDMwLDUgTCAzMCw2LjQyODU3MTQgTCAzMCw0My41NzE0MjkgTCAzMCw0NSBMIDMxLjQyODU3MSw0NSBMIDUwLjQ3NjE5LDQ1IEMgNjEuNzQ0MDk4LDQ1IDcwLjQ3NjE5LDM1Ljk5OTk1NSA3MC40NzYxOSwyNSBDIDcwLjQ3NjE5LDE0LjAwMDA0NSA2MS43NDQwOTksNS4wMDAwMDAyIDUwLjQ3NjE5LDUgQyA1MC40NzYxOSw1IDUwLjQ3NjE5LDUgMzEuNDI4NTcxLDUgTCAzMCw1IHogTSAzMi44NTcxNDMsNy44NTcxNDI5IEMgNDAuODM0MjY0LDcuODU3MTQyOSA0NS45MTgzNjgsNy44NTcxNDI5IDQ4LjA5NTIzOCw3Ljg1NzE0MjkgQyA0OS4yODU3MTQsNy44NTcxNDI5IDQ5Ljg4MDk1Miw3Ljg1NzE0MjkgNTAuMTc4NTcxLDcuODU3MTQyOSBDIDUwLjMyNzM4MSw3Ljg1NzE0MjkgNTAuNDA5MjI3LDcuODU3MTQyOSA1MC40NDY0MjksNy44NTcxNDI5IEMgNTAuNDY1MDI5LDcuODU3MTQyOSA1MC40NzE1NDMsNy44NTcxNDI5IDUwLjQ3NjE5LDcuODU3MTQyOSBDIDYwLjIzNjg1Myw3Ljg1NzE0MyA2Ny4xNDI4NTcsMTUuNDk3MDk4IDY3LjE0Mjg1NywyNSBDIDY3LjE0Mjg1NywzNC41MDI5MDIgNTkuNzYwNjYyLDQyLjE0Mjg1NyA1MCw0Mi4xNDI4NTcgTCAzMi44NTcxNDMsNDIuMTQyODU3IEwgMzIuODU3MTQzLDcuODU3MTQyOSB6IgogICAgICAgaWQ9InBhdGgyODg0IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NzY2NjY3Nzc3NzY2NjIiAvPgogIDwvZz4KPC9zdmc+Cg==' }} + }, { - markup: [{ - tagName: 'ellipse', - selector: 'background' - }, { - tagName: 'image', - selector: 'image' - }, { - tagName: 'ellipse', - selector: 'border', - attributes: { - 'fill': 'none' - } - }, { - tagName: 'text', - selector: 'label' - }] + operation: function(input1, input2) { + return input1 && input2; + } }); - var HeaderedRectangle = Element$1.define('standard.HeaderedRectangle', { - attrs: { - body: { - refWidth: '100%', - refHeight: '100%', - strokeWidth: 2, - stroke: '#000000', - fill: '#FFFFFF' - }, - header: { - refWidth: '100%', - height: 30, - strokeWidth: 2, - stroke: '#000000', - fill: '#FFFFFF' - }, - headerText: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: 15, - fontSize: 16, - fill: '#333333' - }, - bodyText: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '50%', - refY2: 15, - fontSize: 14, - fill: '#333333' - } + var Nor = Gate21.define('logic.Nor', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjEiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjY3NzY0NCIKICAgICBpbmtzY2FwZTpjeT0iMjIuMTAyMzQ0IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjM3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItNCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc5LDI1IEMgOTksMjUgOTUsMjUgOTUsMjUiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBpZD0icGF0aDI2MDQiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI3NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjI1IgogICAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgICAgc29kaXBvZGk6cnk9IjQiCiAgICAgICAgIGQ9Ik0gNzksMjUgQSA0LDQgMCAxIDEgNzEsMjUgQSA0LDQgMCAxIDEgNzksMjUgeiIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI2LjUsMzkuNSkiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K' }} + }, { + operation: function(input1, input2) { + return !(input1 || input2); } + }); + + var Nand = Gate21.define('logic.Nand', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5BTkQgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTYiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjI4MzMwNyIKICAgICBpbmtzY2FwZTpjeT0iMTYuNDQyODQzIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzksMjUgQyA5MS44LDI1IDk1LDI1IDk1LDI1IgogICAgICAgaWQ9InBhdGgzMDU5IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMxLDE1IDUsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMiwzNSA1LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmb250LXNpemU6bWVkaXVtO2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1pbmRlbnQ6MDt0ZXh0LWFsaWduOnN0YXJ0O3RleHQtZGVjb3JhdGlvbjpub25lO2xpbmUtaGVpZ2h0Om5vcm1hbDtsZXR0ZXItc3BhY2luZzpub3JtYWw7d29yZC1zcGFjaW5nOm5vcm1hbDt0ZXh0LXRyYW5zZm9ybTpub25lO2RpcmVjdGlvbjpsdHI7YmxvY2stcHJvZ3Jlc3Npb246dGI7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MzttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO2ZvbnQtZmFtaWx5OkJpdHN0cmVhbSBWZXJhIFNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpCaXRzdHJlYW0gVmVyYSBTYW5zIgogICAgICAgZD0iTSAzMCw1IEwgMzAsNi40Mjg1NzE0IEwgMzAsNDMuNTcxNDI5IEwgMzAsNDUgTCAzMS40Mjg1NzEsNDUgTCA1MC40NzYxOSw0NSBDIDYxLjc0NDA5OCw0NSA3MC40NzYxOSwzNS45OTk5NTUgNzAuNDc2MTksMjUgQyA3MC40NzYxOSwxNC4wMDAwNDUgNjEuNzQ0MDk5LDUuMDAwMDAwMiA1MC40NzYxOSw1IEMgNTAuNDc2MTksNSA1MC40NzYxOSw1IDMxLjQyODU3MSw1IEwgMzAsNSB6IE0gMzIuODU3MTQzLDcuODU3MTQyOSBDIDQwLjgzNDI2NCw3Ljg1NzE0MjkgNDUuOTE4MzY4LDcuODU3MTQyOSA0OC4wOTUyMzgsNy44NTcxNDI5IEMgNDkuMjg1NzE0LDcuODU3MTQyOSA0OS44ODA5NTIsNy44NTcxNDI5IDUwLjE3ODU3MSw3Ljg1NzE0MjkgQyA1MC4zMjczODEsNy44NTcxNDI5IDUwLjQwOTIyNyw3Ljg1NzE0MjkgNTAuNDQ2NDI5LDcuODU3MTQyOSBDIDUwLjQ2NTAyOSw3Ljg1NzE0MjkgNTAuNDcxNTQzLDcuODU3MTQyOSA1MC40NzYxOSw3Ljg1NzE0MjkgQyA2MC4yMzY4NTMsNy44NTcxNDMgNjcuMTQyODU3LDE1LjQ5NzA5OCA2Ny4xNDI4NTcsMjUgQyA2Ny4xNDI4NTcsMzQuNTAyOTAyIDU5Ljc2MDY2Miw0Mi4xNDI4NTcgNTAsNDIuMTQyODU3IEwgMzIuODU3MTQzLDQyLjE0Mjg1NyBMIDMyLjg1NzE0Myw3Ljg1NzE0MjkgeiIKICAgICAgIGlkPSJwYXRoMjg4NCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2Njc2NjY2Nzc3Nzc2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDQwMDgiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg==' }} }, { - markup: [{ - tagName: 'rect', - selector: 'body' - }, { - tagName: 'rect', - selector: 'header' - }, { - tagName: 'text', - selector: 'headerText' - }, { - tagName: 'text', - selector: 'bodyText' - }] + operation: function(input1, input2) { + return !(input1 && input2); + } }); - var CYLINDER_TILT = 10; + var Xor = Gate21.define('logic.Xor', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhPUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iMjUuOTM4MTE2IgogICAgIGlua3NjYXBlOmN5PSIxNy4yMzAwNSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzAuMzg1NzE3LDE1IEwgNC45OTk5OTk4LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5NzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEuMzYyMDkxLDM1IEwgNC45OTk5OTk4LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPGcKICAgICAgIGlkPSJnMjU2MCIKICAgICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYuNSwtMzkuNSkiPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDM1MTYiCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi4yNSw4MS41MDAwMDUgQyAtMy44NDczNzQsODQuMTQ0NDA1IC00LjUsODQuNTAwMDA1IC00LjUsODQuNTAwMDA1IEwgLTguMTU2MjUsODQuNTAwMDA1IEwgLTYuMTU2MjUsODIuMDYyNTA1IEMgLTYuMTU2MjUsODIuMDYyNTA1IC0wLjUsNzUuMDYyNDUxIC0wLjUsNjQuNSBDIC0wLjUsNTMuOTM3NTQ5IC02LjE1NjI1LDQ2LjkzNzUgLTYuMTU2MjUsNDYuOTM3NSBMIC04LjE1NjI1LDQ0LjUgTCAtNC41LDQ0LjUgQyAtMy43MTg3NSw0NS40Mzc1IC0zLjA3ODEyNSw0Ni4xNTYyNSAtMi4yODEyNSw0Ny41IEMgLTAuNDA4NTMxLDUwLjU5OTgxNSAyLjUsNTYuNTI2NjQ2IDIuNSw2NC41IEMgMi41LDcyLjQ1MDY1IC0wLjM5NjY5Nyw3OC4zNzk0MjUgLTIuMjUsODEuNTAwMDA1IHoiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY3NjY2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K' }} + }, { + operation: function(input1, input2) { + return (!input1 || input2) && (input1 || !input2); + } + }); - var Cylinder = Element$1.define('standard.Cylinder', { - attrs: { - body: { - lateralArea: CYLINDER_TILT, - fill: '#FFFFFF', - stroke: '#333333', - strokeWidth: 2 - }, - top: { - refCx: '50%', - cy: CYLINDER_TILT, - refRx: '50%', - ry: CYLINDER_TILT, - fill: '#FFFFFF', - stroke: '#333333', - strokeWidth: 2 - }, - label: { - textVerticalAnchor: 'middle', - textAnchor: 'middle', - refX: '50%', - refY: '100%', - refY2: 15, - fontSize: 14, - fill: '#333333' - } + var Xnor = Gate21.define('logic.Xnor', { + attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhOT1IgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNTU3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTYuNjY2NjY3IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDI1IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0IgogICAgIGlua3NjYXBlOmN4PSI5NS43MjM2NiIKICAgICBpbmtzY2FwZTpjeT0iLTI2Ljc3NTAyMyIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyLjAwMDAwMDI0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc4LjMzMzMzMiwyNSBDIDkxLjY2NjY2NiwyNSA5NSwyNSA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMC4zODU3MTcsMTUgTCA0Ljk5OTk5OTgsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk3NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMS4zNjIwOTEsMzUgTCA0Ljk5OTk5OTgsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMzUxNiIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjI1LDgxLjUwMDAwNSBDIC0zLjg0NzM3NCw4NC4xNDQ0MDUgLTQuNSw4NC41MDAwMDUgLTQuNSw4NC41MDAwMDUgTCAtOC4xNTYyNSw4NC41MDAwMDUgTCAtNi4xNTYyNSw4Mi4wNjI1MDUgQyAtNi4xNTYyNSw4Mi4wNjI1MDUgLTAuNSw3NS4wNjI0NTEgLTAuNSw2NC41IEMgLTAuNSw1My45Mzc1NDkgLTYuMTU2MjUsNDYuOTM3NSAtNi4xNTYyNSw0Ni45Mzc1IEwgLTguMTU2MjUsNDQuNSBMIC00LjUsNDQuNSBDIC0zLjcxODc1LDQ1LjQzNzUgLTMuMDc4MTI1LDQ2LjE1NjI1IC0yLjI4MTI1LDQ3LjUgQyAtMC40MDg1MzEsNTAuNTk5ODE1IDIuNSw1Ni41MjY2NDYgMi41LDY0LjUgQyAyLjUsNzIuNDUwNjUgLTAuMzk2Njk3LDc4LjM3OTQyNSAtMi4yNSw4MS41MDAwMDUgeiIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Njc2NjY2NzYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDM1NTEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg==' }} + }, { + operation: function(input1, input2) { + return (!input1 || !input2) && (input1 || input2); } + }); + + var Wire = Link.define('logic.Wire', { + attrs: { + '.connection': { 'stroke-width': 2 }, + '.marker-vertex': { r: 7 } + }, + + router: { name: 'orthogonal' }, + connector: { name: 'rounded', args: { radius: 10 }} }, { - markup: [{ - tagName: 'path', - selector: 'body' - }, { - tagName: 'ellipse', - selector: 'top' - }, { - tagName: 'text', - selector: 'label' - }], + arrowheadMarkup: [ + '', + '', + '' + ].join(''), - topRy: function(t, opt) { - // getter - if (t === undefined) { return this.attr('body/lateralArea'); } + vertexMarkup: [ + '', + '', + '', + '', + '', + 'Remove vertex.', + '', + '', + '' + ].join('') + }); - // setter - var isPercentageSetter = isPercentage(t); + var logic = ({ + Gate: Gate, + IO: IO, + Input: Input, + Output: Output, + Gate11: Gate11, + Gate21: Gate21, + Repeater: Repeater, + Not: Not, + Or: Or, + And: And, + Nor: Nor, + Nand: Nand, + Xor: Xor, + Xnor: Xnor, + Wire: Wire + }); - var bodyAttrs = { lateralArea: t }; - var topAttrs = isPercentageSetter - ? { refCy: t, refRy: t, cy: null, ry: null } - : { refCy: null, refRy: null, cy: t, ry: t }; + var KingWhite = Generic.define('chess.KingWhite', { + size: { width: 42, height: 38 } + }, { + markup: ' ' + }); - return this.attr({ body: bodyAttrs, top: topAttrs }, opt); - } + var KingBlack = Generic.define('chess.KingBlack', { + size: { width: 42, height: 38 } + }, { + markup: ' ' + }); + var QueenWhite = Generic.define('chess.QueenWhite', { + size: { width: 42, height: 38 } }, { - attributes: { - lateralArea: { - set: function(t, refBBox) { - var isPercentageSetter = isPercentage(t); - if (isPercentageSetter) { t = parseFloat(t) / 100; } + markup: ' ' + }); - var x = refBBox.x; - var y = refBBox.y; - var w = refBBox.width; - var h = refBBox.height; + var QueenBlack = Generic.define('chess.QueenBlack', { + size: { width: 42, height: 38 } + }, { + markup: ' ' + }); - // curve control point variables - var rx = w / 2; - var ry = isPercentageSetter ? (h * t) : t; + var RookWhite = Generic.define('chess.RookWhite', { + size: { width: 32, height: 34 } + }, { + markup: ' ' + }); + + var RookBlack = Generic.define('chess.RookBlack', { + size: { width: 32, height: 34 } + }, { + markup: ' ' + }); + + var BishopWhite = Generic.define('chess.BishopWhite', { + size: { width: 38, height: 38 } + }, { + markup: ' ' + }); + + var BishopBlack = Generic.define('chess.BishopBlack', { + size: { width: 38, height: 38 } + }, { + markup: ' ' + }); + + var KnightWhite = Generic.define('chess.KnightWhite', { + size: { width: 38, height: 37 } + }, { + markup: ' ' + }); + + var KnightBlack = Generic.define('chess.KnightBlack', { + size: { width: 38, height: 37 } + }, { + markup: ' ' + }); - var kappa = V.KAPPA; - var cx = kappa * rx; - var cy = kappa * (isPercentageSetter ? (h * t) : t); + var PawnWhite = Generic.define('chess.PawnWhite', { + size: { width: 28, height: 33 } + }, { + markup: '' + }); - // shape variables - var xLeft = x; - var xCenter = x + (w / 2); - var xRight = x + w; + var PawnBlack = Generic.define('chess.PawnBlack', { + size: { width: 28, height: 33 } + }, { + markup: '' + }); - var ySideTop = y + ry; - var yCurveTop = ySideTop - ry; - var ySideBottom = y + h - ry; - var yCurveBottom = y + h; + var chess = ({ + KingWhite: KingWhite, + KingBlack: KingBlack, + QueenWhite: QueenWhite, + QueenBlack: QueenBlack, + RookWhite: RookWhite, + RookBlack: RookBlack, + BishopWhite: BishopWhite, + BishopBlack: BishopBlack, + KnightWhite: KnightWhite, + KnightBlack: KnightBlack, + PawnWhite: PawnWhite, + PawnBlack: PawnBlack + }); - // return calculated shape - var data = [ - 'M', xLeft, ySideTop, - 'L', xLeft, ySideBottom, - 'C', x, (ySideBottom + cy), (xCenter - cx), yCurveBottom, xCenter, yCurveBottom, - 'C', (xCenter + cx), yCurveBottom, xRight, (ySideBottom + cy), xRight, ySideBottom, - 'L', xRight, ySideTop, - 'C', xRight, (ySideTop - cy), (xCenter + cx), yCurveTop, xCenter, yCurveTop, - 'C', (xCenter - cx), yCurveTop, xLeft, (ySideTop - cy), xLeft, ySideTop, - 'Z' - ]; - return { d: data.join(' ') }; - } + var Entity = Element$1.define('erd.Entity', { + size: { width: 150, height: 60 }, + attrs: { + '.outer': { + fill: '#2ECC71', stroke: '#27AE60', 'stroke-width': 2, + points: '100,0 100,60 0,60 0,0' + }, + '.inner': { + fill: '#2ECC71', stroke: '#27AE60', 'stroke-width': 2, + points: '95,5 95,55 5,55 5,5', + display: 'none' + }, + text: { + text: 'Entity', + 'font-family': 'Arial', 'font-size': 14, + 'ref-x': .5, 'ref-y': .5, + 'y-alignment': 'middle', 'text-anchor': 'middle' } } + }, { + markup: '', }); - var foLabelMarkup = { - tagName: 'foreignObject', - selector: 'foreignObject', - attributes: { - 'overflow': 'hidden' - }, - children: [{ - tagName: 'div', - namespaceURI: 'http://www.w3.org/1999/xhtml', - selector: 'label', - style: { - width: '100%', - height: '100%', - position: 'static', - backgroundColor: 'transparent', - textAlign: 'center', - margin: 0, - padding: '0px 5px', - boxSizing: 'border-box', - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - } - }] - }; - - var svgLabelMarkup = { - tagName: 'text', - selector: 'label', - attributes: { - 'text-anchor': 'middle' + var WeakEntity = Entity.define('erd.WeakEntity', { + attrs: { + '.inner': { display: 'auto' }, + text: { text: 'Weak Entity' } } - }; - - var labelMarkup = (env.test('svgforeignobject')) ? foLabelMarkup : svgLabelMarkup; + }); - var TextBlock$1 = Element$1.define('standard.TextBlock', { + var Relationship = Element$1.define('erd.Relationship', { + size: { width: 80, height: 80 }, attrs: { - body: { - refWidth: '100%', - refHeight: '100%', - stroke: '#333333', - fill: '#ffffff', - strokeWidth: 2 + '.outer': { + fill: '#3498DB', stroke: '#2980B9', 'stroke-width': 2, + points: '40,0 80,40 40,80 0,40' }, - foreignObject: { - refWidth: '100%', - refHeight: '100%' + '.inner': { + fill: '#3498DB', stroke: '#2980B9', 'stroke-width': 2, + points: '40,5 75,40 40,75 5,40', + display: 'none' }, - label: { - style: { - fontSize: 14 - } - } - } - }, { - markup: [{ - tagName: 'rect', - selector: 'body' - }, labelMarkup] - }, { - attributes: { text: { - set: function(text, refBBox, node, attrs) { - if (node instanceof HTMLElement) { - node.textContent = text; - } else { - // No foreign object - var style = attrs.style || {}; - var wrapValue = { text: text, width: -5, height: '100%' }; - var wrapAttrs = assign({ textVerticalAnchor: 'middle' }, style); - attributes.textWrap.set.call(this, wrapValue, refBBox, node, wrapAttrs); - return { fill: style.color || null }; - } - }, - position: function(text, refBBox, node) { - // No foreign object - if (node instanceof SVGElement) { return refBBox.center(); } - } + text: 'Relationship', + 'font-family': 'Arial', 'font-size': 12, + 'ref-x': .5, 'ref-y': .5, + 'y-alignment': 'middle', 'text-anchor': 'middle' } } + }, { + markup: '', }); - // LINKS + var IdentifyingRelationship = Relationship.define('erd.IdentifyingRelationship', { + attrs: { + '.inner': { display: 'auto' }, + text: { text: 'Identifying' } + } + }); - var Link$1 = Link.define('standard.Link', { + var Attribute = Element$1.define('erd.Attribute', { + size: { width: 100, height: 50 }, attrs: { - line: { - connection: true, - stroke: '#333333', - strokeWidth: 2, - strokeLinejoin: 'round', - targetMarker: { - 'type': 'path', - 'd': 'M 10 -5 0 0 10 5 z' - } + 'ellipse': { + transform: 'translate(50, 25)' }, - wrapper: { - connection: true, - strokeWidth: 10, - strokeLinejoin: 'round' + '.outer': { + stroke: '#D35400', 'stroke-width': 2, + cx: 0, cy: 0, rx: 50, ry: 25, + fill: '#E67E22' + }, + '.inner': { + stroke: '#D35400', 'stroke-width': 2, + cx: 0, cy: 0, rx: 45, ry: 20, + fill: '#E67E22', display: 'none' + }, + text: { + 'font-family': 'Arial', 'font-size': 14, + 'ref-x': .5, 'ref-y': .5, + 'y-alignment': 'middle', 'text-anchor': 'middle' } } }, { - markup: [{ - tagName: 'path', - selector: 'wrapper', - attributes: { - 'fill': 'none', - 'cursor': 'pointer', - 'stroke': 'transparent', - 'stroke-linecap': 'round' - } - }, { - tagName: 'path', - selector: 'line', - attributes: { - 'fill': 'none', - 'pointer-events': 'none' - } - }] + markup: '', }); - var DoubleLink = Link.define('standard.DoubleLink', { + var Multivalued = Attribute.define('erd.Multivalued', { attrs: { - line: { - connection: true, - stroke: '#DDDDDD', - strokeWidth: 4, - strokeLinejoin: 'round', - targetMarker: { - type: 'path', - stroke: '#000000', - d: 'M 10 -3 10 -10 -2 0 10 10 10 3' - } - }, - outline: { - connection: true, - stroke: '#000000', - strokeWidth: 6, - strokeLinejoin: 'round' - } + '.inner': { display: 'block' }, + text: { text: 'multivalued' } } - }, { - markup: [{ - tagName: 'path', - selector: 'outline', - attributes: { - 'fill': 'none' - } - }, { - tagName: 'path', - selector: 'line', - attributes: { - 'fill': 'none' - } - }] }); - var ShadowLink = Link.define('standard.ShadowLink', { + var Derived = Attribute.define('erd.Derived', { attrs: { - line: { - connection: true, - stroke: '#FF0000', - strokeWidth: 20, - strokeLinejoin: 'round', - targetMarker: { - 'type': 'path', - 'stroke': 'none', - 'd': 'M 0 -10 -10 0 0 10 z' - }, - sourceMarker: { - 'type': 'path', - 'stroke': 'none', - 'd': 'M -10 -10 0 0 -10 10 0 10 0 -10 z' - } - }, - shadow: { - connection: true, - refX: 3, - refY: 6, - stroke: '#000000', - strokeOpacity: 0.2, - strokeWidth: 20, - strokeLinejoin: 'round', - targetMarker: { - 'type': 'path', - 'd': 'M 0 -10 -10 0 0 10 z', - 'stroke': 'none' - }, - sourceMarker: { - 'type': 'path', - 'stroke': 'none', - 'd': 'M -10 -10 0 0 -10 10 0 10 0 -10 z' - } - } + '.outer': { 'stroke-dasharray': '3,5' }, + text: { text: 'derived' } } - }, { - markup: [{ - tagName: 'path', - selector: 'shadow', - attributes: { - 'fill': 'none' - } - }, { - tagName: 'path', - selector: 'line', - attributes: { - 'fill': 'none' - } - }] }); - var standard = ({ - Rectangle: Rectangle, - Circle: Circle$1, - Ellipse: Ellipse$2, - Path: Path$2, - Polygon: Polygon$2, - Polyline: Polyline$2, - Image: Image$1, - BorderedImage: BorderedImage, - EmbeddedImage: EmbeddedImage, - InscribedImage: InscribedImage, - HeaderedRectangle: HeaderedRectangle, - Cylinder: Cylinder, - TextBlock: TextBlock$1, - Link: Link$1, - DoubleLink: DoubleLink, - ShadowLink: ShadowLink + var Key = Attribute.define('erd.Key', { + attrs: { + ellipse: { 'stroke-width': 4 }, + text: { text: 'key', 'font-weight': '800', 'text-decoration': 'underline' } + } }); - /** - * @deprecated use the port api instead - */ - var Model = Generic.define('devs.Model', { - inPorts: [], - outPorts: [], - size: { - width: 80, - height: 80 - }, + var Normal = Attribute.define('erd.Normal', { + attrs: { text: { text: 'Normal' }} + }); + + var ISA = Element$1.define('erd.ISA', { + type: 'erd.ISA', + size: { width: 100, height: 50 }, attrs: { - '.': { - magnet: false - }, - '.label': { - text: 'Model', - 'ref-x': .5, - 'ref-y': 10, - 'font-size': 18, - 'text-anchor': 'middle', - fill: '#000' + polygon: { + points: '0,0 50,50 100,0', + fill: '#F1C40F', stroke: '#F39C12', 'stroke-width': 2 }, - '.body': { - 'ref-width': '100%', - 'ref-height': '100%', - stroke: '#000' - } - }, - ports: { - groups: { - 'in': { - position: { - name: 'left' - }, - attrs: { - '.port-label': { - fill: '#000' - }, - '.port-body': { - fill: '#fff', - stroke: '#000', - r: 10, - magnet: true - } - }, - label: { - position: { - name: 'left', - args: { - y: 10 - } - } - } - }, - 'out': { - position: { - name: 'right' - }, - attrs: { - '.port-label': { - fill: '#000' - }, - '.port-body': { - fill: '#fff', - stroke: '#000', - r: 10, - magnet: true - } - }, - label: { - position: { - name: 'right', - args: { - y: 10 - } - } - } - } + text: { + text: 'ISA', 'font-size': 18, + 'ref-x': .5, 'ref-y': .3, + 'y-alignment': 'middle', 'text-anchor': 'middle' } } }, { - markup: '', - portMarkup: '', - portLabelMarkup: '', + markup: '', + }); - initialize: function() { + var Line$1 = Link.define('erd.Line', {}, { + cardinality: function(value) { + this.set('labels', [{ position: -20, attrs: { text: { dy: -8, text: value }}}]); + } + }); - Generic.prototype.initialize.apply(this, arguments); + var erd = ({ + Entity: Entity, + WeakEntity: WeakEntity, + Relationship: Relationship, + IdentifyingRelationship: IdentifyingRelationship, + Attribute: Attribute, + Multivalued: Multivalued, + Derived: Derived, + Key: Key, + Normal: Normal, + ISA: ISA, + Line: Line$1 + }); - this.on('change:inPorts change:outPorts', this.updatePortItems, this); - this.updatePortItems(); - }, + var State = Circle.define('fsa.State', { + attrs: { + circle: { 'stroke-width': 3 }, + text: { 'font-weight': '800' } + } + }); - updatePortItems: function(model, changed, opt) { + var StartState = Element$1.define('fsa.StartState', { + size: { width: 20, height: 20 }, + attrs: { + circle: { + transform: 'translate(10, 10)', + r: 10, + fill: '#000000' + } + } + }, { + markup: '', + }); - // Make sure all ports are unique. - var inPorts = uniq(this.get('inPorts')); - var outPorts = difference(uniq(this.get('outPorts')), inPorts); + var EndState = Element$1.define('fsa.EndState', { + size: { width: 20, height: 20 }, + attrs: { + '.outer': { + transform: 'translate(10, 10)', + r: 10, + fill: '#ffffff', + stroke: '#000000' + }, - var inPortItems = this.createPortItems('in', inPorts); - var outPortItems = this.createPortItems('out', outPorts); + '.inner': { + transform: 'translate(10, 10)', + r: 6, + fill: '#000000' + } + } + }, { + markup: '', + }); - this.prop('ports/items', inPortItems.concat(outPortItems), assign({ rewrite: true }, opt)); - }, + var Arrow = Link.define('fsa.Arrow', { + attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }}, + smooth: true + }); - createPortItem: function(group, port) { + var fsa = ({ + State: State, + StartState: StartState, + EndState: EndState, + Arrow: Arrow + }); - return { - id: port, - group: group, - attrs: { - '.port-label': { - text: port - } - } - }; - }, + var Member = Element$1.define('org.Member', { + size: { width: 180, height: 70 }, + attrs: { + rect: { width: 170, height: 60 }, - createPortItems: function(group, ports) { + '.card': { + fill: '#FFFFFF', stroke: '#000000', 'stroke-width': 2, + 'pointer-events': 'visiblePainted', rx: 10, ry: 10 + }, - return toArray(ports).map(this.createPortItem.bind(this, group)); - }, + image: { + width: 48, height: 48, + ref: '.card', 'ref-x': 10, 'ref-y': 5 + }, - _addGroupPort: function(port, group, opt) { + '.rank': { + 'text-decoration': 'underline', + ref: '.card', 'ref-x': 0.9, 'ref-y': 0.2, + 'font-family': 'Courier New', 'font-size': 14, + 'text-anchor': 'end' + }, - var ports = this.get(group); - return this.set(group, Array.isArray(ports) ? ports.concat(port) : [port], opt); - }, + '.name': { + 'font-weight': '800', + ref: '.card', 'ref-x': 0.9, 'ref-y': 0.6, + 'font-family': 'Courier New', 'font-size': 14, + 'text-anchor': 'end' + } + } + }, { + markup: '', + }); - addOutPort: function(port, opt) { + var Arrow$1 = Link.define('org.Arrow', { + source: { selector: '.card' }, target: { selector: '.card' }, + attrs: { '.connection': { stroke: '#585858', 'stroke-width': 3 }}, + z: -1 + }); - return this._addGroupPort(port, 'outPorts', opt); - }, + var org = ({ + Member: Member, + Arrow: Arrow$1 + }); - addInPort: function(port, opt) { + var Place = Generic.define('pn.Place', { + size: { width: 50, height: 50 }, + attrs: { + '.root': { + r: 25, + fill: '#ffffff', + stroke: '#000000', + transform: 'translate(25, 25)' + }, + '.label': { + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-y': -20, + ref: '.root', + fill: '#000000', + 'font-size': 12 + }, + '.tokens > circle': { + fill: '#000000', + r: 5 + }, + '.tokens.one > circle': { transform: 'translate(25, 25)' }, - return this._addGroupPort(port, 'inPorts', opt); - }, + '.tokens.two > circle:nth-child(1)': { transform: 'translate(19, 25)' }, + '.tokens.two > circle:nth-child(2)': { transform: 'translate(31, 25)' }, - _removeGroupPort: function(port, group, opt) { + '.tokens.three > circle:nth-child(1)': { transform: 'translate(18, 29)' }, + '.tokens.three > circle:nth-child(2)': { transform: 'translate(25, 19)' }, + '.tokens.three > circle:nth-child(3)': { transform: 'translate(32, 29)' }, - return this.set(group, without(this.get(group), port), opt); - }, + '.tokens.alot > text': { + transform: 'translate(25, 18)', + 'text-anchor': 'middle', + fill: '#000000' + } + } + }, { + markup: '', + }); - removeOutPort: function(port, opt) { + var PlaceView = ElementView.extend({ - return this._removeGroupPort(port, 'outPorts', opt); - }, + presentationAttributes: ElementView.addPresentationAttributes({ + tokens: ['TOKENS'] + }), - removeInPort: function(port, opt) { + initFlag: ElementView.prototype.initFlag.concat(['TOKENS']), - return this._removeGroupPort(port, 'inPorts', opt); + confirmUpdate: function() { + var ref; + + var args = [], len = arguments.length; + while ( len-- ) args[ len ] = arguments[ len ]; + var flags = (ref = ElementView.prototype.confirmUpdate).call.apply(ref, [ this ].concat( args )); + if (this.hasFlag(flags, 'TOKENS')) { + this.renderTokens(); + this.update(); + flags = this.removeFlag(flags, 'TOKENS'); + } + return flags; }, - _changeGroup: function(group, properties, opt) { + renderTokens: function() { - return this.prop('ports/groups/' + group, isObject$1(properties) ? properties : {}, opt); - }, + var vTokens = this.vel.findOne('.tokens').empty(); + ['one', 'two', 'three', 'alot'].forEach(function(className) { + vTokens.removeClass(className); + }); - changeInGroup: function(properties, opt) { + var tokens = this.model.get('tokens'); + if (!tokens) { return; } - return this._changeGroup('in', properties, opt); - }, + switch (tokens) { - changeOutGroup: function(properties, opt) { + case 1: + vTokens.addClass('one'); + vTokens.append(V('circle')); + break; - return this._changeGroup('out', properties, opt); - } - }); + case 2: + vTokens.addClass('two'); + vTokens.append([V('circle'), V('circle')]); + break; - var Atomic = Model.define('devs.Atomic', { - size: { - width: 80, - height: 80 - }, - attrs: { - '.label': { - text: 'Atomic' + case 3: + vTokens.addClass('three'); + vTokens.append([V('circle'), V('circle'), V('circle')]); + break; + + default: + vTokens.addClass('alot'); + vTokens.append(V('text').text(tokens + '')); + break; } } }); - var Coupled = Model.define('devs.Coupled', { - size: { - width: 200, - height: 300 - }, + var Transition = Generic.define('pn.Transition', { + size: { width: 12, height: 50 }, attrs: { + 'rect': { + width: 12, + height: 50, + fill: '#000000', + stroke: '#000000' + }, '.label': { - text: 'Coupled' + 'text-anchor': 'middle', + 'ref-x': .5, + 'ref-y': -20, + ref: 'rect', + fill: '#000000', + 'font-size': 12 } } + }, { + markup: '', }); - var Link$2 = Link.define('devs.Link', { - attrs: { - '.connection': { - 'stroke-width': 2 - } - } + var Link$3 = Link.define('pn.Link', { + attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }} }); - var devs = ({ - Model: Model, - Atomic: Atomic, - Coupled: Coupled, - Link: Link$2 + var pn = ({ + Place: Place, + PlaceView: PlaceView, + Transition: Transition, + Link: Link$3 }); - var Gate = Generic.define('logic.Gate', { - size: { width: 80, height: 40 }, + var Class = Generic.define('uml.Class', { attrs: { - '.': { magnet: false }, - '.body': { width: 100, height: 50 }, - circle: { r: 7, stroke: 'black', fill: 'transparent', 'stroke-width': 2 } - } - }, { - operation: function() { - return true; - } - }); + rect: { 'width': 200 }, - var IO = Gate.define('logic.IO', { - size: { width: 60, height: 30 }, - attrs: { - '.body': { fill: 'white', stroke: 'black', 'stroke-width': 2 }, - '.wire': { ref: '.body', 'ref-y': .5, stroke: 'black' }, - text: { - fill: 'black', - ref: '.body', 'ref-x': .5, 'ref-y': .5, 'y-alignment': 'middle', + '.uml-class-name-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#3498db' }, + '.uml-class-attrs-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#2980b9' }, + '.uml-class-methods-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#2980b9' }, + + '.uml-class-name-text': { + 'ref': '.uml-class-name-rect', + 'ref-y': .5, + 'ref-x': .5, 'text-anchor': 'middle', + 'y-alignment': 'middle', 'font-weight': 'bold', - 'font-variant': 'small-caps', - 'text-transform': 'capitalize', - 'font-size': '14px' + 'fill': 'black', + 'font-size': 12, + 'font-family': 'Times New Roman' + }, + '.uml-class-attrs-text': { + 'ref': '.uml-class-attrs-rect', 'ref-y': 5, 'ref-x': 5, + 'fill': 'black', 'font-size': 12, 'font-family': 'Times New Roman' + }, + '.uml-class-methods-text': { + 'ref': '.uml-class-methods-rect', 'ref-y': 5, 'ref-x': 5, + 'fill': 'black', 'font-size': 12, 'font-family': 'Times New Roman' } - } + }, + + name: [], + attributes: [], + methods: [] }, { - markup: '', - }); + markup: [ + '', + '', + '', + '', + '', + '' + ].join(''), - var Input = IO.define('logic.Input', { - attrs: { - '.wire': { 'ref-dx': 0, d: 'M 0 0 L 23 0' }, - circle: { ref: '.body', 'ref-dx': 30, 'ref-y': 0.5, magnet: true, 'class': 'output', port: 'out' }, - text: { text: 'input' } - } - }); + initialize: function() { - var Output = IO.define('logic.Output', { - attrs: { - '.wire': { 'ref-x': 0, d: 'M 0 0 L -23 0' }, - circle: { ref: '.body', 'ref-x': -30, 'ref-y': 0.5, magnet: 'passive', 'class': 'input', port: 'in' }, - text: { text: 'output' } + this.on('change:name change:attributes change:methods', function() { + this.updateRectangles(); + this.trigger('uml-update'); + }, this); + + this.updateRectangles(); + + Generic.prototype.initialize.apply(this, arguments); + }, + + getClassName: function() { + return this.get('name'); + }, + + updateRectangles: function() { + + var attrs = this.get('attrs'); + + var rects = [ + { type: 'name', text: this.getClassName() }, + { type: 'attrs', text: this.get('attributes') }, + { type: 'methods', text: this.get('methods') } + ]; + + var offsetY = 0; + + rects.forEach(function(rect) { + + var lines = Array.isArray(rect.text) ? rect.text : [rect.text]; + var rectHeight = lines.length * 20 + 20; + + attrs['.uml-class-' + rect.type + '-text'].text = lines.join('\n'); + attrs['.uml-class-' + rect.type + '-rect'].height = rectHeight; + attrs['.uml-class-' + rect.type + '-rect'].transform = 'translate(0,' + offsetY + ')'; + + offsetY += rectHeight; + }); } + }); - var Gate11 = Gate.define('logic.Gate11', { - attrs: { - '.input': { ref: '.body', 'ref-x': -2, 'ref-y': 0.5, magnet: 'passive', port: 'in' }, - '.output': { ref: '.body', 'ref-dx': 2, 'ref-y': 0.5, magnet: true, port: 'out' } + var ClassView = ElementView.extend({ + + initialize: function() { + + ElementView.prototype.initialize.apply(this, arguments); + + this.listenTo(this.model, 'uml-update', function() { + this.update(); + this.resize(); + }); } - }, { - markup: '', }); - var Gate21 = Gate.define('logic.Gate21', { + var Abstract = Class.define('uml.Abstract', { attrs: { - '.input1': { ref: '.body', 'ref-x': -2, 'ref-y': 0.3, magnet: 'passive', port: 'in1' }, - '.input2': { ref: '.body', 'ref-x': -2, 'ref-y': 0.7, magnet: 'passive', port: 'in2' }, - '.output': { ref: '.body', 'ref-dx': 2, 'ref-y': 0.5, magnet: true, port: 'out' } + '.uml-class-name-rect': { fill: '#e74c3c' }, + '.uml-class-attrs-rect': { fill: '#c0392b' }, + '.uml-class-methods-rect': { fill: '#c0392b' } } }, { - markup: '', - }); - var Repeater = Gate11.define('logic.Repeater', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzIuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICA8L2c+Cjwvc3ZnPgo=' }} - }, { - operation: function(input) { - return input; + getClassName: function() { + return ['<>', this.get('name')]; } - }); - var Not = Gate11.define('logic.Not', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzkuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI2NzEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzYiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA4MCwyNSBBIDQsNCAwIDEgMSA3MiwyNSBBIDQsNCAwIDEgMSA4MCwyNSB6IgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEsMCkiIC8+CiAgPC9nPgo8L3N2Zz4K' }} - }, { - operation: function(input) { - return !input; - } }); + var AbstractView = ClassView; - var Or = Gate21.define('logic.Or', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik9SIEFOU0kuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTAgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjcxNCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iMSA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMC41IDogMC4zMzMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODA2IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODE5IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjM3Mi4wNDcyNCA6IDM1MC43ODczOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI3NDQuMDk0NDggOiA1MjYuMTgxMDkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzc3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49Ijc1IDogNDAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTUwIDogNjAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDYwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTMyNzUiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNTAgOiAzMy4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTAwIDogNTAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTU1MzMiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjU1NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDE2LjY2NjY2NyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDI1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAyNSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iNCIKICAgICBpbmtzY2FwZTpjeD0iMTEzLjAwMDM5IgogICAgIGlua3NjYXBlOmN5PSIxMi44OTM3MzEiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImcyNTYwIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1wb2ludHM9InRydWUiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAwMDAiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMzk5IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg3NCIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMzciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii00IgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNzAsMjUgYyAyMCwwIDI1LDAgMjUsMCIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMSwxNSA1LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzIsMzUgNSwzNSIKICAgICAgIGlkPSJwYXRoMzk0NCIgLz4KICAgIDxnCiAgICAgICBpZD0iZzI1NjAiCiAgICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2LjUsLTM5LjUpIj4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=' }} - }, { - operation: function(input1, input2) { - return input1 || input2; + var Interface = Class.define('uml.Interface', { + attrs: { + '.uml-class-name-rect': { fill: '#f1c40f' }, + '.uml-class-attrs-rect': { fill: '#f39c12' }, + '.uml-class-methods-rect': { fill: '#f39c12' } } - }); - - var And = Gate21.define('logic.And', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IkFORCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI4IgogICAgIGlua3NjYXBlOmN4PSI1Ni42OTgzNDgiCiAgICAgaW5rc2NhcGU6Y3k9IjI1LjMyNjg5OSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTptZWRpdW07Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWluZGVudDowO3RleHQtYWxpZ246c3RhcnQ7dGV4dC1kZWNvcmF0aW9uOm5vbmU7bGluZS1oZWlnaHQ6bm9ybWFsO2xldHRlci1zcGFjaW5nOm5vcm1hbDt3b3JkLXNwYWNpbmc6bm9ybWFsO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlyZWN0aW9uOmx0cjtibG9jay1wcm9ncmVzc2lvbjp0Yjt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDozO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGU7Zm9udC1mYW1pbHk6Qml0c3RyZWFtIFZlcmEgU2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkJpdHN0cmVhbSBWZXJhIFNhbnMiCiAgICAgICBkPSJNIDMwLDUgTCAzMCw2LjQyODU3MTQgTCAzMCw0My41NzE0MjkgTCAzMCw0NSBMIDMxLjQyODU3MSw0NSBMIDUwLjQ3NjE5LDQ1IEMgNjEuNzQ0MDk4LDQ1IDcwLjQ3NjE5LDM1Ljk5OTk1NSA3MC40NzYxOSwyNSBDIDcwLjQ3NjE5LDE0LjAwMDA0NSA2MS43NDQwOTksNS4wMDAwMDAyIDUwLjQ3NjE5LDUgQyA1MC40NzYxOSw1IDUwLjQ3NjE5LDUgMzEuNDI4NTcxLDUgTCAzMCw1IHogTSAzMi44NTcxNDMsNy44NTcxNDI5IEMgNDAuODM0MjY0LDcuODU3MTQyOSA0NS45MTgzNjgsNy44NTcxNDI5IDQ4LjA5NTIzOCw3Ljg1NzE0MjkgQyA0OS4yODU3MTQsNy44NTcxNDI5IDQ5Ljg4MDk1Miw3Ljg1NzE0MjkgNTAuMTc4NTcxLDcuODU3MTQyOSBDIDUwLjMyNzM4MSw3Ljg1NzE0MjkgNTAuNDA5MjI3LDcuODU3MTQyOSA1MC40NDY0MjksNy44NTcxNDI5IEMgNTAuNDY1MDI5LDcuODU3MTQyOSA1MC40NzE1NDMsNy44NTcxNDI5IDUwLjQ3NjE5LDcuODU3MTQyOSBDIDYwLjIzNjg1Myw3Ljg1NzE0MyA2Ny4xNDI4NTcsMTUuNDk3MDk4IDY3LjE0Mjg1NywyNSBDIDY3LjE0Mjg1NywzNC41MDI5MDIgNTkuNzYwNjYyLDQyLjE0Mjg1NyA1MCw0Mi4xNDI4NTcgTCAzMi44NTcxNDMsNDIuMTQyODU3IEwgMzIuODU3MTQzLDcuODU3MTQyOSB6IgogICAgICAgaWQ9InBhdGgyODg0IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NzY2NjY3Nzc3NzY2NjIiAvPgogIDwvZz4KPC9zdmc+Cg==' }} - }, { - operation: function(input1, input2) { - return input1 && input2; + getClassName: function() { + return ['<>', this.get('name')]; } }); + var InterfaceView = ClassView; - var Nor = Gate21.define('logic.Nor', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjEiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjY3NzY0NCIKICAgICBpbmtzY2FwZTpjeT0iMjIuMTAyMzQ0IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjM3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItNCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc5LDI1IEMgOTksMjUgOTUsMjUgOTUsMjUiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBpZD0icGF0aDI2MDQiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI3NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjI1IgogICAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgICAgc29kaXBvZGk6cnk9IjQiCiAgICAgICAgIGQ9Ik0gNzksMjUgQSA0LDQgMCAxIDEgNzEsMjUgQSA0LDQgMCAxIDEgNzksMjUgeiIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI2LjUsMzkuNSkiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K' }} - }, { - operation: function(input1, input2) { - return !(input1 || input2); - } + var Generalization = Link.define('uml.Generalization', { + attrs: { '.marker-target': { d: 'M 20 0 L 0 10 L 20 20 z', fill: 'white' }} }); - var Nand = Gate21.define('logic.Nand', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5BTkQgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTYiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjI4MzMwNyIKICAgICBpbmtzY2FwZTpjeT0iMTYuNDQyODQzIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzksMjUgQyA5MS44LDI1IDk1LDI1IDk1LDI1IgogICAgICAgaWQ9InBhdGgzMDU5IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMxLDE1IDUsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMiwzNSA1LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmb250LXNpemU6bWVkaXVtO2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1pbmRlbnQ6MDt0ZXh0LWFsaWduOnN0YXJ0O3RleHQtZGVjb3JhdGlvbjpub25lO2xpbmUtaGVpZ2h0Om5vcm1hbDtsZXR0ZXItc3BhY2luZzpub3JtYWw7d29yZC1zcGFjaW5nOm5vcm1hbDt0ZXh0LXRyYW5zZm9ybTpub25lO2RpcmVjdGlvbjpsdHI7YmxvY2stcHJvZ3Jlc3Npb246dGI7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MzttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO2ZvbnQtZmFtaWx5OkJpdHN0cmVhbSBWZXJhIFNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpCaXRzdHJlYW0gVmVyYSBTYW5zIgogICAgICAgZD0iTSAzMCw1IEwgMzAsNi40Mjg1NzE0IEwgMzAsNDMuNTcxNDI5IEwgMzAsNDUgTCAzMS40Mjg1NzEsNDUgTCA1MC40NzYxOSw0NSBDIDYxLjc0NDA5OCw0NSA3MC40NzYxOSwzNS45OTk5NTUgNzAuNDc2MTksMjUgQyA3MC40NzYxOSwxNC4wMDAwNDUgNjEuNzQ0MDk5LDUuMDAwMDAwMiA1MC40NzYxOSw1IEMgNTAuNDc2MTksNSA1MC40NzYxOSw1IDMxLjQyODU3MSw1IEwgMzAsNSB6IE0gMzIuODU3MTQzLDcuODU3MTQyOSBDIDQwLjgzNDI2NCw3Ljg1NzE0MjkgNDUuOTE4MzY4LDcuODU3MTQyOSA0OC4wOTUyMzgsNy44NTcxNDI5IEMgNDkuMjg1NzE0LDcuODU3MTQyOSA0OS44ODA5NTIsNy44NTcxNDI5IDUwLjE3ODU3MSw3Ljg1NzE0MjkgQyA1MC4zMjczODEsNy44NTcxNDI5IDUwLjQwOTIyNyw3Ljg1NzE0MjkgNTAuNDQ2NDI5LDcuODU3MTQyOSBDIDUwLjQ2NTAyOSw3Ljg1NzE0MjkgNTAuNDcxNTQzLDcuODU3MTQyOSA1MC40NzYxOSw3Ljg1NzE0MjkgQyA2MC4yMzY4NTMsNy44NTcxNDMgNjcuMTQyODU3LDE1LjQ5NzA5OCA2Ny4xNDI4NTcsMjUgQyA2Ny4xNDI4NTcsMzQuNTAyOTAyIDU5Ljc2MDY2Miw0Mi4xNDI4NTcgNTAsNDIuMTQyODU3IEwgMzIuODU3MTQzLDQyLjE0Mjg1NyBMIDMyLjg1NzE0Myw3Ljg1NzE0MjkgeiIKICAgICAgIGlkPSJwYXRoMjg4NCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2Njc2NjY2Nzc3Nzc2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDQwMDgiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg==' }} - }, { - operation: function(input1, input2) { - return !(input1 && input2); + var Implementation = Link.define('uml.Implementation', { + attrs: { + '.marker-target': { d: 'M 20 0 L 0 10 L 20 20 z', fill: 'white' }, + '.connection': { 'stroke-dasharray': '3,3' } } }); - var Xor = Gate21.define('logic.Xor', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhPUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iMjUuOTM4MTE2IgogICAgIGlua3NjYXBlOmN5PSIxNy4yMzAwNSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzAuMzg1NzE3LDE1IEwgNC45OTk5OTk4LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5NzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEuMzYyMDkxLDM1IEwgNC45OTk5OTk4LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPGcKICAgICAgIGlkPSJnMjU2MCIKICAgICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYuNSwtMzkuNSkiPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDM1MTYiCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi4yNSw4MS41MDAwMDUgQyAtMy44NDczNzQsODQuMTQ0NDA1IC00LjUsODQuNTAwMDA1IC00LjUsODQuNTAwMDA1IEwgLTguMTU2MjUsODQuNTAwMDA1IEwgLTYuMTU2MjUsODIuMDYyNTA1IEMgLTYuMTU2MjUsODIuMDYyNTA1IC0wLjUsNzUuMDYyNDUxIC0wLjUsNjQuNSBDIC0wLjUsNTMuOTM3NTQ5IC02LjE1NjI1LDQ2LjkzNzUgLTYuMTU2MjUsNDYuOTM3NSBMIC04LjE1NjI1LDQ0LjUgTCAtNC41LDQ0LjUgQyAtMy43MTg3NSw0NS40Mzc1IC0zLjA3ODEyNSw0Ni4xNTYyNSAtMi4yODEyNSw0Ny41IEMgLTAuNDA4NTMxLDUwLjU5OTgxNSAyLjUsNTYuNTI2NjQ2IDIuNSw2NC41IEMgMi41LDcyLjQ1MDY1IC0wLjM5NjY5Nyw3OC4zNzk0MjUgLTIuMjUsODEuNTAwMDA1IHoiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY3NjY2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K' }} - }, { - operation: function(input1, input2) { - return (!input1 || input2) && (input1 || !input2); - } + var Aggregation = Link.define('uml.Aggregation', { + attrs: { '.marker-target': { d: 'M 40 10 L 20 20 L 0 10 L 20 0 z', fill: 'white' }} }); - var Xnor = Gate21.define('logic.Xnor', { - attrs: { image: { 'xlink:href': 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhOT1IgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNTU3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTYuNjY2NjY3IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDI1IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0IgogICAgIGlua3NjYXBlOmN4PSI5NS43MjM2NiIKICAgICBpbmtzY2FwZTpjeT0iLTI2Ljc3NTAyMyIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyLjAwMDAwMDI0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc4LjMzMzMzMiwyNSBDIDkxLjY2NjY2NiwyNSA5NSwyNSA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMC4zODU3MTcsMTUgTCA0Ljk5OTk5OTgsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk3NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMS4zNjIwOTEsMzUgTCA0Ljk5OTk5OTgsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMzUxNiIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjI1LDgxLjUwMDAwNSBDIC0zLjg0NzM3NCw4NC4xNDQ0MDUgLTQuNSw4NC41MDAwMDUgLTQuNSw4NC41MDAwMDUgTCAtOC4xNTYyNSw4NC41MDAwMDUgTCAtNi4xNTYyNSw4Mi4wNjI1MDUgQyAtNi4xNTYyNSw4Mi4wNjI1MDUgLTAuNSw3NS4wNjI0NTEgLTAuNSw2NC41IEMgLTAuNSw1My45Mzc1NDkgLTYuMTU2MjUsNDYuOTM3NSAtNi4xNTYyNSw0Ni45Mzc1IEwgLTguMTU2MjUsNDQuNSBMIC00LjUsNDQuNSBDIC0zLjcxODc1LDQ1LjQzNzUgLTMuMDc4MTI1LDQ2LjE1NjI1IC0yLjI4MTI1LDQ3LjUgQyAtMC40MDg1MzEsNTAuNTk5ODE1IDIuNSw1Ni41MjY2NDYgMi41LDY0LjUgQyAyLjUsNzIuNDUwNjUgLTAuMzk2Njk3LDc4LjM3OTQyNSAtMi4yNSw4MS41MDAwMDUgeiIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Njc2NjY2NzYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDM1NTEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg==' }} - }, { - operation: function(input1, input2) { - return (!input1 || !input2) && (input1 || input2); - } + var Composition = Link.define('uml.Composition', { + attrs: { '.marker-target': { d: 'M 40 10 L 20 20 L 0 10 L 20 0 z', fill: 'black' }} }); - var Wire = Link.define('logic.Wire', { + var Association = Link.define('uml.Association'); + + // Statechart + + var State$1 = Generic.define('uml.State', { attrs: { - '.connection': { 'stroke-width': 2 }, - '.marker-vertex': { r: 7 } + '.uml-state-body': { + 'width': 200, 'height': 200, 'rx': 10, 'ry': 10, + 'fill': '#ecf0f1', 'stroke': '#bdc3c7', 'stroke-width': 3 + }, + '.uml-state-separator': { + 'stroke': '#bdc3c7', 'stroke-width': 2 + }, + '.uml-state-name': { + 'ref': '.uml-state-body', 'ref-x': .5, 'ref-y': 5, 'text-anchor': 'middle', + 'fill': '#000000', 'font-family': 'Courier New', 'font-size': 14 + }, + '.uml-state-events': { + 'ref': '.uml-state-separator', 'ref-x': 5, 'ref-y': 5, + 'fill': '#000000', 'font-family': 'Courier New', 'font-size': 14 + } }, - router: { name: 'orthogonal' }, - connector: { name: 'rounded', args: { radius: 10 }} + name: 'State', + events: [] + }, { - arrowheadMarkup: [ - '', - '', + markup: [ + '', + '', + '', + '', + '', + '', + '', '' ].join(''), - vertexMarkup: [ - '', - '', - '', - '', - '', - 'Remove vertex.', - '', - '', - '' - ].join('') + initialize: function() { + + this.on({ + 'change:name': this.updateName, + 'change:events': this.updateEvents, + 'change:size': this.updatePath + }, this); + + this.updateName(); + this.updateEvents(); + this.updatePath(); + + Generic.prototype.initialize.apply(this, arguments); + }, + + updateName: function() { + + this.attr('.uml-state-name/text', this.get('name')); + }, + + updateEvents: function() { + + this.attr('.uml-state-events/text', this.get('events').join('\n')); + }, + + updatePath: function() { + + var d = 'M 0 20 L ' + this.get('size').width + ' 20'; + + // We are using `silent: true` here because updatePath() is meant to be called + // on resize and there's no need to to update the element twice (`change:size` + // triggers also an update). + this.attr('.uml-state-separator/d', d, { silent: true }); + } }); - var logic = ({ - Gate: Gate, - IO: IO, - Input: Input, - Output: Output, - Gate11: Gate11, - Gate21: Gate21, - Repeater: Repeater, - Not: Not, - Or: Or, - And: And, - Nor: Nor, - Nand: Nand, - Xor: Xor, - Xnor: Xnor, - Wire: Wire + var StartState$1 = Circle.define('uml.StartState', { + type: 'uml.StartState', + attrs: { circle: { 'fill': '#34495e', 'stroke': '#2c3e50', 'stroke-width': 2, 'rx': 1 }} }); - var KingWhite = Generic.define('chess.KingWhite', { - size: { width: 42, height: 38 } + var EndState$1 = Generic.define('uml.EndState', { + size: { width: 20, height: 20 }, + attrs: { + 'circle.outer': { + transform: 'translate(10, 10)', + r: 10, + fill: '#ffffff', + stroke: '#2c3e50' + }, + + 'circle.inner': { + transform: 'translate(10, 10)', + r: 6, + fill: '#34495e' + } + } }, { - markup: ' ' + markup: '', }); - var KingBlack = Generic.define('chess.KingBlack', { - size: { width: 42, height: 38 } - }, { - markup: ' ' + var Transition$1 = Link.define('uml.Transition', { + attrs: { + '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z', fill: '#34495e', stroke: '#2c3e50' }, + '.connection': { stroke: '#2c3e50' } + } }); - var QueenWhite = Generic.define('chess.QueenWhite', { - size: { width: 42, height: 38 } - }, { - markup: ' ' + var uml = ({ + Class: Class, + ClassView: ClassView, + Abstract: Abstract, + AbstractView: AbstractView, + Interface: Interface, + InterfaceView: InterfaceView, + Generalization: Generalization, + Implementation: Implementation, + Aggregation: Aggregation, + Composition: Composition, + Association: Association, + State: State$1, + StartState: StartState$1, + EndState: EndState$1, + Transition: Transition$1 }); - var QueenBlack = Generic.define('chess.QueenBlack', { - size: { width: 42, height: 38 } - }, { - markup: ' ' - }); - var RookWhite = Generic.define('chess.RookWhite', { - size: { width: 32, height: 34 } - }, { - markup: ' ' - }); - var RookBlack = Generic.define('chess.RookBlack', { - size: { width: 32, height: 34 } - }, { - markup: ' ' + var index$3 = ({ + basic: basic, + standard: standard, + devs: devs, + logic: logic, + chess: chess, + erd: erd, + fsa: fsa, + org: org, + pn: pn, + uml: uml }); - var BishopWhite = Generic.define('chess.BishopWhite', { - size: { width: 38, height: 38 } - }, { - markup: ' ' - }); + function abs2rel(value, max) { - var BishopBlack = Generic.define('chess.BishopBlack', { - size: { width: 38, height: 38 } - }, { - markup: ' ' - }); + if (max === 0) { return '0%'; } + return Math.round(value / max * 100) + '%'; + } - var KnightWhite = Generic.define('chess.KnightWhite', { - size: { width: 38, height: 37 } - }, { - markup: ' ' - }); + function pin(relative) { - var KnightBlack = Generic.define('chess.KnightBlack', { - size: { width: 38, height: 37 } - }, { - markup: ' ' - }); + return function(end, view, magnet, coords) { + var fn = (view.isNodeConnection(magnet)) ? pinnedLinkEnd : pinnedElementEnd; + return fn(relative, end, view, magnet, coords); + }; + } - var PawnWhite = Generic.define('chess.PawnWhite', { - size: { width: 28, height: 33 } - }, { - markup: '' - }); + function pinnedElementEnd(relative, end, view, magnet, coords) { - var PawnBlack = Generic.define('chess.PawnBlack', { - size: { width: 28, height: 33 } - }, { - markup: '' - }); + var angle = view.model.angle(); + var bbox = view.getNodeUnrotatedBBox(magnet); + var origin = view.model.getBBox().center(); + coords.rotate(origin, angle); + var dx = coords.x - bbox.x; + var dy = coords.y - bbox.y; - var chess = ({ - KingWhite: KingWhite, - KingBlack: KingBlack, - QueenWhite: QueenWhite, - QueenBlack: QueenBlack, - RookWhite: RookWhite, - RookBlack: RookBlack, - BishopWhite: BishopWhite, - BishopBlack: BishopBlack, - KnightWhite: KnightWhite, - KnightBlack: KnightBlack, - PawnWhite: PawnWhite, - PawnBlack: PawnBlack - }); + if (relative) { + dx = abs2rel(dx, bbox.width); + dy = abs2rel(dy, bbox.height); + } - var Entity = Element$1.define('erd.Entity', { - size: { width: 150, height: 60 }, - attrs: { - '.outer': { - fill: '#2ECC71', stroke: '#27AE60', 'stroke-width': 2, - points: '100,0 100,60 0,60 0,0' - }, - '.inner': { - fill: '#2ECC71', stroke: '#27AE60', 'stroke-width': 2, - points: '95,5 95,55 5,55 5,5', - display: 'none' - }, - text: { - text: 'Entity', - 'font-family': 'Arial', 'font-size': 14, - 'ref-x': .5, 'ref-y': .5, - 'y-alignment': 'middle', 'text-anchor': 'middle' + end.anchor = { + name: 'topLeft', + args: { + dx: dx, + dy: dy, + rotate: true } - } - }, { - markup: '', - }); + }; - var WeakEntity = Entity.define('erd.WeakEntity', { - attrs: { - '.inner': { display: 'auto' }, - text: { text: 'Weak Entity' } + return end; + } + + function pinnedLinkEnd(relative, end, view, _magnet, coords) { + + var connection = view.getConnection(); + if (!connection) { return end; } + var length = connection.closestPointLength(coords); + if (relative) { + var totalLength = connection.length(); + end.anchor = { + name: 'connectionRatio', + args: { + ratio: length / totalLength + } + }; + } else { + end.anchor = { + name: 'connectionLength', + args: { + length: length + } + }; } + return end; + } + + var useDefaults = noop; + var pinAbsolute = pin(false); + var pinRelative = pin(true); + + var index$4 = ({ + useDefaults: useDefaults, + pinAbsolute: pinAbsolute, + pinRelative: pinRelative }); - var Relationship = Element$1.define('erd.Relationship', { - size: { width: 80, height: 80 }, - attrs: { - '.outer': { - fill: '#3498DB', stroke: '#2980B9', 'stroke-width': 2, - points: '40,0 80,40 40,80 0,40' - }, - '.inner': { - fill: '#3498DB', stroke: '#2980B9', 'stroke-width': 2, - points: '40,5 75,40 40,75 5,40', - display: 'none' - }, - text: { - text: 'Relationship', - 'font-family': 'Arial', 'font-size': 12, - 'ref-x': .5, 'ref-y': .5, - 'y-alignment': 'middle', 'text-anchor': 'middle' - } + function getAnchor(coords, view, magnet) { + // take advantage of an existing logic inside of the + // pin relative connection strategy + var end = pinRelative.call( + this.paper, + {}, + view, + magnet, + coords, + this.model + ); + return end.anchor; + } + + function snapAnchor(coords, view, magnet, type, relatedView, toolView) { + var snapRadius = toolView.options.snapRadius; + var isSource = (type === 'source'); + var refIndex = (isSource ? 0 : -1); + var ref = this.model.vertex(refIndex) || this.getEndAnchor(isSource ? 'target' : 'source'); + if (ref) { + if (Math.abs(ref.x - coords.x) < snapRadius) { coords.x = ref.x; } + if (Math.abs(ref.y - coords.y) < snapRadius) { coords.y = ref.y; } } - }, { - markup: '', - }); + return coords; + } - var IdentifyingRelationship = Relationship.define('erd.IdentifyingRelationship', { - attrs: { - '.inner': { display: 'auto' }, - text: { text: 'Identifying' } + function getViewBBox(view, useModelGeometry) { + var model = view.model; + if (useModelGeometry) { return model.getBBox(); } + return (model.isLink()) ? view.getConnection().bbox() : view.getNodeUnrotatedBBox(view.el); + } + + // Vertex Handles + var VertexHandle = View.extend({ + tagName: 'circle', + svgElement: true, + className: 'marker-vertex', + events: { + mousedown: 'onPointerDown', + touchstart: 'onPointerDown', + dblclick: 'onDoubleClick', + dbltap: 'onDoubleClick' + }, + documentEvents: { + mousemove: 'onPointerMove', + touchmove: 'onPointerMove', + mouseup: 'onPointerUp', + touchend: 'onPointerUp', + touchcancel: 'onPointerUp' + }, + attributes: { + 'r': 6, + 'fill': '#33334F', + 'stroke': '#FFFFFF', + 'stroke-width': 2, + 'cursor': 'move' + }, + position: function(x, y) { + this.vel.attr({ cx: x, cy: y }); + }, + onPointerDown: function(evt) { + if (this.options.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + this.options.paper.undelegateEvents(); + this.delegateDocumentEvents(null, evt.data); + this.trigger('will-change', this, evt); + }, + onPointerMove: function(evt) { + this.trigger('changing', this, evt); + }, + onDoubleClick: function(evt) { + this.trigger('remove', this, evt); + }, + onPointerUp: function(evt) { + this.trigger('changed', this, evt); + this.undelegateDocumentEvents(); + this.options.paper.delegateEvents(); } }); - var Attribute = Element$1.define('erd.Attribute', { - size: { width: 100, height: 50 }, - attrs: { - 'ellipse': { - transform: 'translate(50, 25)' - }, - '.outer': { - stroke: '#D35400', 'stroke-width': 2, - cx: 0, cy: 0, rx: 50, ry: 25, - fill: '#E67E22' - }, - '.inner': { - stroke: '#D35400', 'stroke-width': 2, - cx: 0, cy: 0, rx: 45, ry: 20, - fill: '#E67E22', display: 'none' - }, - text: { - 'font-family': 'Arial', 'font-size': 14, - 'ref-x': .5, 'ref-y': .5, - 'y-alignment': 'middle', 'text-anchor': 'middle' + var Vertices = ToolView.extend({ + name: 'vertices', + options: { + handleClass: VertexHandle, + snapRadius: 20, + redundancyRemoval: true, + vertexAdding: true, + stopPropagation: true + }, + children: [{ + tagName: 'path', + selector: 'connection', + className: 'joint-vertices-path', + attributes: { + 'fill': 'none', + 'stroke': 'transparent', + 'stroke-width': 10, + 'cursor': 'cell' + } + }], + handles: null, + events: { + 'mousedown .joint-vertices-path': 'onPathPointerDown', + 'touchstart .joint-vertices-path': 'onPathPointerDown' + }, + onRender: function() { + if (this.options.vertexAdding) { + this.renderChildren(); + this.updatePath(); + } + this.resetHandles(); + this.renderHandles(); + return this; + }, + update: function() { + var relatedView = this.relatedView; + var vertices = relatedView.model.vertices(); + if (vertices.length === this.handles.length) { + this.updateHandles(); + } else { + this.resetHandles(); + this.renderHandles(); + } + if (this.options.vertexAdding) { + this.updatePath(); + } + return this; + }, + resetHandles: function() { + var handles = this.handles; + this.handles = []; + this.stopListening(); + if (!Array.isArray(handles)) { return; } + for (var i = 0, n = handles.length; i < n; i++) { + handles[i].remove(); + } + }, + renderHandles: function() { + var this$1 = this; + + var relatedView = this.relatedView; + var vertices = relatedView.model.vertices(); + for (var i = 0, n = vertices.length; i < n; i++) { + var vertex = vertices[i]; + var handle = new (this.options.handleClass)({ + index: i, + paper: this.paper, + guard: function (evt) { return this$1.guard(evt); } + }); + handle.render(); + handle.position(vertex.x, vertex.y); + this.simulateRelatedView(handle.el); + handle.vel.appendTo(this.el); + this.handles.push(handle); + this.startHandleListening(handle); + } + }, + updateHandles: function() { + var relatedView = this.relatedView; + var vertices = relatedView.model.vertices(); + for (var i = 0, n = vertices.length; i < n; i++) { + var vertex = vertices[i]; + var handle = this.handles[i]; + if (!handle) { return; } + handle.position(vertex.x, vertex.y); + } + }, + updatePath: function() { + var connection = this.childNodes.connection; + if (connection) { connection.setAttribute('d', this.relatedView.getSerializedConnection()); } + }, + startHandleListening: function(handle) { + var relatedView = this.relatedView; + if (relatedView.can('vertexMove')) { + this.listenTo(handle, 'will-change', this.onHandleWillChange); + this.listenTo(handle, 'changing', this.onHandleChanging); + this.listenTo(handle, 'changed', this.onHandleChanged); + } + if (relatedView.can('vertexRemove')) { + this.listenTo(handle, 'remove', this.onHandleRemove); + } + }, + getNeighborPoints: function(index) { + var linkView = this.relatedView; + var vertices = linkView.model.vertices(); + var prev = (index > 0) ? vertices[index - 1] : linkView.sourceAnchor; + var next = (index < vertices.length - 1) ? vertices[index + 1] : linkView.targetAnchor; + return { + prev: new Point(prev), + next: new Point(next) + }; + }, + onHandleWillChange: function(_handle, evt) { + this.focus(); + var ref = this; + var relatedView = ref.relatedView; + var options = ref.options; + relatedView.model.startBatch('vertex-move', { ui: true, tool: this.cid }); + if (!options.stopPropagation) { relatedView.notifyPointerdown.apply(relatedView, relatedView.paper.getPointerArgs(evt)); } + }, + onHandleChanging: function(handle, evt) { + var ref = this; + var options = ref.options; + var linkView = ref.relatedView; + var index = handle.options.index; + var ref$1 = linkView.paper.getPointerArgs(evt); + var normalizedEvent = ref$1[0]; + var x = ref$1[1]; + var y = ref$1[2]; + var vertex = { x: x, y: y }; + this.snapVertex(vertex, index); + linkView.model.vertex(index, vertex, { ui: true, tool: this.cid }); + handle.position(vertex.x, vertex.y); + if (!options.stopPropagation) { linkView.notifyPointermove(normalizedEvent, x, y); } + }, + onHandleChanged: function(_handle, evt) { + var ref = this; + var options = ref.options; + var linkView = ref.relatedView; + if (options.vertexAdding) { this.updatePath(); } + if (!options.redundancyRemoval) { return; } + var verticesRemoved = linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); + if (verticesRemoved) { this.render(); } + this.blur(); + linkView.model.stopBatch('vertex-move', { ui: true, tool: this.cid }); + if (this.eventData(evt).vertexAdded) { + linkView.model.stopBatch('vertex-add', { ui: true, tool: this.cid }); + } + var ref$1 = linkView.paper.getPointerArgs(evt); + var normalizedEvt = ref$1[0]; + var x = ref$1[1]; + var y = ref$1[2]; + if (!options.stopPropagation) { linkView.notifyPointerup(normalizedEvt, x, y); } + linkView.checkMouseleave(normalizedEvt); + }, + snapVertex: function(vertex, index) { + var snapRadius = this.options.snapRadius; + if (snapRadius > 0) { + var neighbors = this.getNeighborPoints(index); + var prev = neighbors.prev; + var next = neighbors.next; + if (Math.abs(vertex.x - prev.x) < snapRadius) { + vertex.x = prev.x; + } else if (Math.abs(vertex.x - next.x) < snapRadius) { + vertex.x = next.x; + } + if (Math.abs(vertex.y - prev.y) < snapRadius) { + vertex.y = neighbors.prev.y; + } else if (Math.abs(vertex.y - next.y) < snapRadius) { + vertex.y = next.y; + } } + }, + onHandleRemove: function(handle, evt) { + var index$1 = handle.options.index; + var linkView = this.relatedView; + linkView.model.removeVertex(index$1, { ui: true }); + if (this.options.vertexAdding) { this.updatePath(); } + linkView.checkMouseleave(normalizeEvent(evt)); + }, + onPathPointerDown: function(evt) { + if (this.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + var normalizedEvent = normalizeEvent(evt); + var vertex = this.paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY).toJSON(); + var relatedView = this.relatedView; + relatedView.model.startBatch('vertex-add', { ui: true, tool: this.cid }); + var index$1 = relatedView.getVertexIndex(vertex.x, vertex.y); + this.snapVertex(vertex, index$1); + relatedView.model.insertVertex(index$1, vertex, { ui: true, tool: this.cid }); + this.update(); + var handle = this.handles[index$1]; + this.eventData(normalizedEvent, { vertexAdded: true }); + handle.onPointerDown(normalizedEvent); + }, + onRemove: function() { + this.resetHandles(); } }, { - markup: '', + VertexHandle: VertexHandle // keep as class property }); - var Multivalued = Attribute.define('erd.Multivalued', { - attrs: { - '.inner': { display: 'block' }, - text: { text: 'multivalued' } - } - }); + var SegmentHandle = View.extend({ + tagName: 'g', + svgElement: true, + className: 'marker-segment', + events: { + mousedown: 'onPointerDown', + touchstart: 'onPointerDown' + }, + documentEvents: { + mousemove: 'onPointerMove', + touchmove: 'onPointerMove', + mouseup: 'onPointerUp', + touchend: 'onPointerUp', + touchcancel: 'onPointerUp' + }, + children: [{ + tagName: 'line', + selector: 'line', + attributes: { + 'stroke': '#33334F', + 'stroke-width': 2, + 'fill': 'none', + 'pointer-events': 'none' + } + }, { + tagName: 'rect', + selector: 'handle', + attributes: { + 'width': 20, + 'height': 8, + 'x': -10, + 'y': -4, + 'rx': 4, + 'ry': 4, + 'fill': '#33334F', + 'stroke': '#FFFFFF', + 'stroke-width': 2 + } + }], + onRender: function() { + this.renderChildren(); + }, + position: function(x, y, angle, view) { - var Derived = Attribute.define('erd.Derived', { - attrs: { - '.outer': { 'stroke-dasharray': '3,5' }, - text: { text: 'derived' } - } - }); + var matrix = V.createSVGMatrix().translate(x, y).rotate(angle); + var handle = this.childNodes.handle; + handle.setAttribute('transform', V.matrixToTransformString(matrix)); + handle.setAttribute('cursor', (angle % 180 === 0) ? 'row-resize' : 'col-resize'); - var Key = Attribute.define('erd.Key', { - attrs: { - ellipse: { 'stroke-width': 4 }, - text: { text: 'key', 'font-weight': '800', 'text-decoration': 'underline' } + var viewPoint = view.getClosestPoint(new Point(x, y)); + var line = this.childNodes.line; + line.setAttribute('x1', x); + line.setAttribute('y1', y); + line.setAttribute('x2', viewPoint.x); + line.setAttribute('y2', viewPoint.y); + }, + onPointerDown: function(evt) { + if (this.options.guard(evt)) { return; } + this.trigger('change:start', this, evt); + evt.stopPropagation(); + evt.preventDefault(); + this.options.paper.undelegateEvents(); + this.delegateDocumentEvents(null, evt.data); + }, + onPointerMove: function(evt) { + this.trigger('changing', this, evt); + }, + onPointerUp: function(evt) { + this.undelegateDocumentEvents(); + this.options.paper.delegateEvents(); + this.trigger('change:end', this, evt); + }, + show: function() { + this.el.style.display = ''; + }, + hide: function() { + this.el.style.display = 'none'; } }); - var Normal = Attribute.define('erd.Normal', { - attrs: { text: { text: 'Normal' }} - }); + var Segments = ToolView.extend({ + name: 'segments', + precision: .5, + options: { + handleClass: SegmentHandle, + segmentLengthThreshold: 40, + redundancyRemoval: true, + anchor: getAnchor, + snapRadius: 10, + snapHandle: true, + stopPropagation: true + }, + handles: null, + onRender: function() { + this.resetHandles(); + var relatedView = this.relatedView; + var vertices = relatedView.model.vertices(); + vertices.unshift(relatedView.sourcePoint); + vertices.push(relatedView.targetPoint); + for (var i = 0, n = vertices.length; i < n - 1; i++) { + var vertex = vertices[i]; + var nextVertex = vertices[i + 1]; + var handle = this.renderHandle(vertex, nextVertex); + this.simulateRelatedView(handle.el); + this.handles.push(handle); + handle.options.index = i; + } + return this; + }, + renderHandle: function(vertex, nextVertex) { + var this$1 = this; - var ISA = Element$1.define('erd.ISA', { - type: 'erd.ISA', - size: { width: 100, height: 50 }, - attrs: { - polygon: { - points: '0,0 50,50 100,0', - fill: '#F1C40F', stroke: '#F39C12', 'stroke-width': 2 - }, - text: { - text: 'ISA', 'font-size': 18, - 'ref-x': .5, 'ref-y': .3, - 'y-alignment': 'middle', 'text-anchor': 'middle' + var handle = new (this.options.handleClass)({ + paper: this.paper, + guard: function (evt) { return this$1.guard(evt); } + }); + handle.render(); + this.updateHandle(handle, vertex, nextVertex); + handle.vel.appendTo(this.el); + this.startHandleListening(handle); + return handle; + }, + update: function() { + this.render(); + return this; + }, + startHandleListening: function(handle) { + this.listenTo(handle, 'change:start', this.onHandleChangeStart); + this.listenTo(handle, 'changing', this.onHandleChanging); + this.listenTo(handle, 'change:end', this.onHandleChangeEnd); + }, + resetHandles: function() { + var handles = this.handles; + this.handles = []; + this.stopListening(); + if (!Array.isArray(handles)) { return; } + for (var i = 0, n = handles.length; i < n; i++) { + handles[i].remove(); } - } - }, { - markup: '', - }); + }, + shiftHandleIndexes: function(value) { + var handles = this.handles; + for (var i = 0, n = handles.length; i < n; i++) { handles[i].options.index += value; } + }, + resetAnchor: function(type, anchor) { + var relatedModel = this.relatedView.model; + if (anchor) { + relatedModel.prop([type, 'anchor'], anchor, { + rewrite: true, + ui: true, + tool: this.cid + }); + } else { + relatedModel.removeProp([type, 'anchor'], { + ui: true, + tool: this.cid + }); + } + }, + snapHandle: function(handle, position, data) { - var Line$1 = Link.define('erd.Line', {}, { - cardinality: function(value) { - this.set('labels', [{ position: -20, attrs: { text: { dy: -8, text: value }}}]); - } - }); + var index = handle.options.index; + var linkView = this.relatedView; + var link = linkView.model; + var vertices = link.vertices(); + var axis = handle.options.axis; + var prev = vertices[index - 2] || data.sourceAnchor; + var next = vertices[index + 1] || data.targetAnchor; + var snapRadius = this.options.snapRadius; + if (Math.abs(position[axis] - prev[axis]) < snapRadius) { + position[axis] = prev[axis]; + } else if (Math.abs(position[axis] - next[axis]) < snapRadius) { + position[axis] = next[axis]; + } + return position; + }, - var erd = ({ - Entity: Entity, - WeakEntity: WeakEntity, - Relationship: Relationship, - IdentifyingRelationship: IdentifyingRelationship, - Attribute: Attribute, - Multivalued: Multivalued, - Derived: Derived, - Key: Key, - Normal: Normal, - ISA: ISA, - Line: Line$1 - }); + onHandleChanging: function(handle, evt) { - var State = Circle.define('fsa.State', { - attrs: { - circle: { 'stroke-width': 3 }, - text: { 'font-weight': '800' } - } - }); + var ref = this; + var options = ref.options; + var data = this.eventData(evt); + var relatedView = this.relatedView; + var paper = relatedView.paper; + var index$1 = handle.options.index - 1; + var normalizedEvent = normalizeEvent(evt); + var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + var position = this.snapHandle(handle, coords.clone(), data); + var axis = handle.options.axis; + var offset = (this.options.snapHandle) ? 0 : (coords[axis] - position[axis]); + var link = relatedView.model; + var vertices = cloneDeep(link.vertices()); + var vertex = vertices[index$1]; + var nextVertex = vertices[index$1 + 1]; + var anchorFn = this.options.anchor; + if (typeof anchorFn !== 'function') { anchorFn = null; } - var StartState = Element$1.define('fsa.StartState', { - size: { width: 20, height: 20 }, - attrs: { - circle: { - transform: 'translate(10, 10)', - r: 10, - fill: '#000000' + // First Segment + var sourceView = relatedView.sourceView; + var sourceBBox = relatedView.sourceBBox; + var changeSourceAnchor = false; + var deleteSourceAnchor = false; + if (!vertex) { + vertex = relatedView.sourceAnchor.toJSON(); + vertex[axis] = position[axis]; + if (sourceBBox.containsPoint(vertex)) { + vertex[axis] = position[axis]; + changeSourceAnchor = true; + } else { + // we left the area of the source magnet for the first time + vertices.unshift(vertex); + this.shiftHandleIndexes(1); + deleteSourceAnchor = true; + } + } else if (index$1 === 0) { + if (sourceBBox.containsPoint(vertex)) { + vertices.shift(); + this.shiftHandleIndexes(-1); + changeSourceAnchor = true; + } else { + vertex[axis] = position[axis]; + deleteSourceAnchor = true; + } + } else { + vertex[axis] = position[axis]; } - } - }, { - markup: '', - }); - var EndState = Element$1.define('fsa.EndState', { - size: { width: 20, height: 20 }, - attrs: { - '.outer': { - transform: 'translate(10, 10)', - r: 10, - fill: '#ffffff', - stroke: '#000000' - }, + if (anchorFn && sourceView) { + if (changeSourceAnchor) { + var sourceAnchorPosition = data.sourceAnchor.clone(); + sourceAnchorPosition[axis] = position[axis]; + var sourceAnchor = anchorFn.call(relatedView, sourceAnchorPosition, sourceView, relatedView.sourceMagnet || sourceView.el, 'source', relatedView); + this.resetAnchor('source', sourceAnchor); + } + if (deleteSourceAnchor) { + this.resetAnchor('source', data.sourceAnchorDef); + } + } - '.inner': { - transform: 'translate(10, 10)', - r: 6, - fill: '#000000' + // Last segment + var targetView = relatedView.targetView; + var targetBBox = relatedView.targetBBox; + var changeTargetAnchor = false; + var deleteTargetAnchor = false; + if (!nextVertex) { + nextVertex = relatedView.targetAnchor.toJSON(); + nextVertex[axis] = position[axis]; + if (targetBBox.containsPoint(nextVertex)) { + changeTargetAnchor = true; + } else { + // we left the area of the target magnet for the first time + vertices.push(nextVertex); + deleteTargetAnchor = true; + } + } else if (index$1 === vertices.length - 2) { + if (targetBBox.containsPoint(nextVertex)) { + vertices.pop(); + changeTargetAnchor = true; + } else { + nextVertex[axis] = position[axis]; + deleteTargetAnchor = true; + } + } else { + nextVertex[axis] = position[axis]; } - } - }, { - markup: '', - }); - var Arrow = Link.define('fsa.Arrow', { - attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }}, - smooth: true - }); + if (anchorFn && targetView) { + if (changeTargetAnchor) { + var targetAnchorPosition = data.targetAnchor.clone(); + targetAnchorPosition[axis] = position[axis]; + var targetAnchor = anchorFn.call(relatedView, targetAnchorPosition, targetView, relatedView.targetMagnet || targetView.el, 'target', relatedView); + this.resetAnchor('target', targetAnchor); + } + if (deleteTargetAnchor) { + this.resetAnchor('target', data.targetAnchorDef); + } + } - var fsa = ({ - State: State, - StartState: StartState, - EndState: EndState, - Arrow: Arrow + link.vertices(vertices, { ui: true, tool: this.cid }); + this.updateHandle(handle, vertex, nextVertex, offset); + if (!options.stopPropagation) { relatedView.notifyPointermove(normalizedEvent, coords.x, coords.y); } + }, + onHandleChangeStart: function(handle, evt) { + var ref = this; + var options = ref.options; + var handles = ref.handles; + var linkView = ref.relatedView; + var model = linkView.model; + var paper = linkView.paper; + var index$1 = handle.options.index; + if (!Array.isArray(handles)) { return; } + for (var i = 0, n = handles.length; i < n; i++) { + if (i !== index$1) { handles[i].hide(); } + } + this.focus(); + this.eventData(evt, { + sourceAnchor: linkView.sourceAnchor.clone(), + targetAnchor: linkView.targetAnchor.clone(), + sourceAnchorDef: clone(model.prop(['source', 'anchor'])), + targetAnchorDef: clone(model.prop(['target', 'anchor'])) + }); + model.startBatch('segment-move', { ui: true, tool: this.cid }); + if (!options.stopPropagation) { linkView.notifyPointerdown.apply(linkView, paper.getPointerArgs(evt)); } + }, + onHandleChangeEnd: function(_handle, evt) { + var ref= this; + var options = ref.options; + var linkView = ref.relatedView; + var paper = linkView.paper; + var model = linkView.model; + if (options.redundancyRemoval) { + linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); + } + var normalizedEvent = normalizeEvent(evt); + var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + this.render(); + this.blur(); + model.stopBatch('segment-move', { ui: true, tool: this.cid }); + if (!options.stopPropagation) { linkView.notifyPointerup(normalizedEvent, coords.x, coords.y); } + linkView.checkMouseleave(normalizedEvent); + }, + updateHandle: function(handle, vertex, nextVertex, offset) { + var vertical = Math.abs(vertex.x - nextVertex.x) < this.precision; + var horizontal = Math.abs(vertex.y - nextVertex.y) < this.precision; + if (vertical || horizontal) { + var segmentLine = new Line(vertex, nextVertex); + var length = segmentLine.length(); + if (length < this.options.segmentLengthThreshold) { + handle.hide(); + } else { + var position = segmentLine.midpoint(); + var axis = (vertical) ? 'x' : 'y'; + position[axis] += offset || 0; + var angle = segmentLine.vector().vectorAngle(new Point(1, 0)); + handle.position(position.x, position.y, angle, this.relatedView); + handle.show(); + handle.options.axis = axis; + } + } else { + handle.hide(); + } + }, + onRemove: function() { + this.resetHandles(); + } + }, { + SegmentHandle: SegmentHandle // keep as class property }); - var Member = Element$1.define('org.Member', { - size: { width: 180, height: 70 }, - attrs: { - rect: { width: 170, height: 60 }, - - '.card': { - fill: '#FFFFFF', stroke: '#000000', 'stroke-width': 2, - 'pointer-events': 'visiblePainted', rx: 10, ry: 10 - }, - - image: { - width: 48, height: 48, - ref: '.card', 'ref-x': 10, 'ref-y': 5 - }, - - '.rank': { - 'text-decoration': 'underline', - ref: '.card', 'ref-x': 0.9, 'ref-y': 0.2, - 'font-family': 'Courier New', 'font-size': 14, - 'text-anchor': 'end' - }, - - '.name': { - 'font-weight': '800', - ref: '.card', 'ref-x': 0.9, 'ref-y': 0.6, - 'font-family': 'Courier New', 'font-size': 14, - 'text-anchor': 'end' + // End Markers + var Arrowhead = ToolView.extend({ + tagName: 'path', + xAxisVector: new Point(1, 0), + events: { + mousedown: 'onPointerDown', + touchstart: 'onPointerDown' + }, + documentEvents: { + mousemove: 'onPointerMove', + touchmove: 'onPointerMove', + mouseup: 'onPointerUp', + touchend: 'onPointerUp', + touchcancel: 'onPointerUp' + }, + onRender: function() { + this.update(); + }, + update: function() { + var ratio = this.ratio; + var view = this.relatedView; + var tangent = view.getTangentAtRatio(ratio); + var position, angle; + if (tangent) { + position = tangent.start; + angle = tangent.vector().vectorAngle(this.xAxisVector) || 0; + } else { + position = view.getPointAtRatio(ratio); + angle = 0; + } + if (!position) { return this; } + var matrix = V.createSVGMatrix().translate(position.x, position.y).rotate(angle); + this.vel.transform(matrix, { absolute: true }); + return this; + }, + onPointerDown: function(evt) { + if (this.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + var relatedView = this.relatedView; + relatedView.model.startBatch('arrowhead-move', { ui: true, tool: this.cid }); + if (relatedView.can('arrowheadMove')) { + relatedView.startArrowheadMove(this.arrowheadType); + this.delegateDocumentEvents(); + relatedView.paper.undelegateEvents(); } + this.focus(); + this.el.style.pointerEvents = 'none'; + }, + onPointerMove: function(evt) { + var normalizedEvent = normalizeEvent(evt); + var coords = this.paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + this.relatedView.pointermove(normalizedEvent, coords.x, coords.y); + }, + onPointerUp: function(evt) { + this.undelegateDocumentEvents(); + var relatedView = this.relatedView; + var paper = relatedView.paper; + var normalizedEvent = normalizeEvent(evt); + var coords = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + relatedView.pointerup(normalizedEvent, coords.x, coords.y); + paper.delegateEvents(); + this.blur(); + this.el.style.pointerEvents = ''; + relatedView.model.stopBatch('arrowhead-move', { ui: true, tool: this.cid }); } - }, { - markup: '', }); - var Arrow$1 = Link.define('org.Arrow', { - source: { selector: '.card' }, target: { selector: '.card' }, - attrs: { '.connection': { stroke: '#585858', 'stroke-width': 3 }}, - z: -1 + var TargetArrowhead = Arrowhead.extend({ + name: 'target-arrowhead', + ratio: 1, + arrowheadType: 'target', + attributes: { + 'd': 'M -10 -8 10 0 -10 8 Z', + 'fill': '#33334F', + 'stroke': '#FFFFFF', + 'stroke-width': 2, + 'cursor': 'move', + 'class': 'target-arrowhead' + } }); - var org = ({ - Member: Member, - Arrow: Arrow$1 + var SourceArrowhead = Arrowhead.extend({ + name: 'source-arrowhead', + ratio: 0, + arrowheadType: 'source', + attributes: { + 'd': 'M 10 -8 -10 0 10 8 Z', + 'fill': '#33334F', + 'stroke': '#FFFFFF', + 'stroke-width': 2, + 'cursor': 'move', + 'class': 'source-arrowhead' + } }); - var Place = Generic.define('pn.Place', { - size: { width: 50, height: 50 }, - attrs: { - '.root': { - r: 25, - fill: '#ffffff', - stroke: '#000000', - transform: 'translate(25, 25)' - }, - '.label': { - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-y': -20, - ref: '.root', - fill: '#000000', - 'font-size': 12 - }, - '.tokens > circle': { - fill: '#000000', - r: 5 - }, - '.tokens.one > circle': { transform: 'translate(25, 25)' }, - - '.tokens.two > circle:nth-child(1)': { transform: 'translate(19, 25)' }, - '.tokens.two > circle:nth-child(2)': { transform: 'translate(31, 25)' }, - - '.tokens.three > circle:nth-child(1)': { transform: 'translate(18, 29)' }, - '.tokens.three > circle:nth-child(2)': { transform: 'translate(25, 19)' }, - '.tokens.three > circle:nth-child(3)': { transform: 'translate(32, 29)' }, - - '.tokens.alot > text': { - transform: 'translate(25, 18)', - 'text-anchor': 'middle', - fill: '#000000' + var Button = ToolView.extend({ + name: 'button', + events: { + 'mousedown': 'onPointerDown', + 'touchstart': 'onPointerDown' + }, + options: { + distance: 0, + offset: 0, + rotate: false + }, + onRender: function() { + this.renderChildren(this.options.markup); + this.update(); + }, + update: function() { + this.position(); + return this; + }, + position: function() { + var ref = this; + var view = ref.relatedView; + var vel = ref.vel; + var matrix = view.model.isLink() ? this.getLinkMatrix() : this.getElementMatrix(); + vel.transform(matrix, { absolute: true }); + }, + getElementMatrix: function getElementMatrix() { + var ref = this; + var view = ref.relatedView; + var options = ref.options; + var x = options.x; if ( x === void 0 ) x = 0; + var y = options.y; if ( y === void 0 ) y = 0; + var offset = options.offset; if ( offset === void 0 ) offset = {}; + var useModelGeometry = options.useModelGeometry; + var rotate = options.rotate; + var bbox = getViewBBox(view, useModelGeometry); + var angle = view.model.angle(); + if (!rotate) { bbox = bbox.bbox(angle); } + var offsetX = offset.x; if ( offsetX === void 0 ) offsetX = 0; + var offsetY = offset.y; if ( offsetY === void 0 ) offsetY = 0; + if (isPercentage(x)) { + x = parseFloat(x) / 100 * bbox.width; + } + if (isPercentage(y)) { + y = parseFloat(y) / 100 * bbox.height; + } + var matrix = V.createSVGMatrix().translate(bbox.x + bbox.width / 2, bbox.y + bbox.height / 2); + if (rotate) { matrix = matrix.rotate(angle); } + matrix = matrix.translate(x + offsetX - bbox.width / 2, y + offsetY - bbox.height / 2); + return matrix; + }, + getLinkMatrix: function getLinkMatrix() { + var ref = this; + var view = ref.relatedView; + var options = ref.options; + var offset = options.offset; if ( offset === void 0 ) offset = 0; + var distance = options.distance; if ( distance === void 0 ) distance = 0; + var rotate = options.rotate; + var tangent, position, angle; + if (isPercentage(distance)) { + tangent = view.getTangentAtRatio(parseFloat(distance) / 100); + } else { + tangent = view.getTangentAtLength(distance); + } + if (tangent) { + position = tangent.start; + angle = tangent.vector().vectorAngle(new Point(1, 0)) || 0; + } else { + position = view.getConnection().start; + angle = 0; + } + var matrix = V.createSVGMatrix() + .translate(position.x, position.y) + .rotate(angle) + .translate(0, offset); + if (!rotate) { matrix = matrix.rotate(-angle); } + return matrix; + }, + onPointerDown: function(evt) { + if (this.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + var actionFn = this.options.action; + if (typeof actionFn === 'function') { + actionFn.call(this.relatedView, evt, this.relatedView, this); } } - }, { - markup: '', }); - var PlaceView = ElementView.extend({ - - presentationAttributes: ElementView.addPresentationAttributes({ - tokens: ['TOKENS'] - }), - - initFlag: ElementView.prototype.initFlag.concat(['TOKENS']), - confirmUpdate: function() { - var ref; + var Remove = Button.extend({ + children: [{ + tagName: 'circle', + selector: 'button', + attributes: { + 'r': 7, + 'fill': '#FF1D00', + 'cursor': 'pointer' + } + }, { + tagName: 'path', + selector: 'icon', + attributes: { + 'd': 'M -3 -3 3 3 M -3 3 3 -3', + 'fill': 'none', + 'stroke': '#FFFFFF', + 'stroke-width': 2, + 'pointer-events': 'none' + } + }], + options: { + distance: 60, + offset: 0, + action: function(evt, view, tool) { + view.model.remove({ ui: true, tool: tool.cid }); + } + } + }); - var args = [], len = arguments.length; - while ( len-- ) args[ len ] = arguments[ len ]; - var flags = (ref = ElementView.prototype.confirmUpdate).call.apply(ref, [ this ].concat( args )); - if (this.hasFlag(flags, 'TOKENS')) { - this.renderTokens(); - this.update(); - flags = this.removeFlag(flags, 'TOKENS'); + var Connect = Button.extend({ + name: 'connect', + documentEvents: { + mousemove: 'drag', + touchmove: 'drag', + mouseup: 'dragend', + touchend: 'dragend', + touchcancel: 'dragend' + }, + children: [{ + tagName: 'circle', + selector: 'button', + attributes: { + 'r': 7, + 'fill': '#333333', + 'cursor': 'pointer' } - return flags; + }, { + tagName: 'path', + selector: 'icon', + attributes: { + 'd': 'M -4 -1 L 0 -1 L 0 -4 L 4 0 L 0 4 0 1 -4 1 z', + 'fill': '#FFFFFF', + 'stroke': 'none', + 'stroke-width': 2, + 'pointer-events': 'none' + } + }], + options: { + distance: 80, + offset: 0, + magnet: function (view) { return view.el; }, + action: function (evt, _view, tool) { return tool.dragstart(evt); }, }, + getMagnetNode: function() { + var assign; - renderTokens: function() { - - var vTokens = this.vel.findOne('.tokens').empty(); - ['one', 'two', 'three', 'alot'].forEach(function(className) { - vTokens.removeClass(className); - }); - - var tokens = this.model.get('tokens'); - if (!tokens) { return; } - - switch (tokens) { - - case 1: - vTokens.addClass('one'); - vTokens.append(V('circle')); - break; - - case 2: - vTokens.addClass('two'); - vTokens.append([V('circle'), V('circle')]); + var ref = this; + var options = ref.options; + var relatedView = ref.relatedView; + var magnet = options.magnet; + var magnetNode; + switch (typeof magnet) { + case 'function': { + magnetNode = magnet.call(this, relatedView, this); break; - - case 3: - vTokens.addClass('three'); - vTokens.append([V('circle'), V('circle'), V('circle')]); + } + case 'string': { + (assign = relatedView.findBySelector(magnet), magnetNode = assign[0]); break; - - default: - vTokens.addClass('alot'); - vTokens.append(V('text').text(tokens + '')); + } + default: { + magnetNode = magnet; break; + } } + if (!magnetNode) { magnetNode = relatedView.el; } + if (magnetNode instanceof SVGElement) { return magnetNode; } + throw new Error('Connect: magnet must be an SVGElement'); + }, + dragstart: function(evt) { + var ref = this; + var paper = ref.paper; + var relatedView = ref.relatedView; + var normalizedEvent = normalizeEvent(evt); + var ref$1 = paper.clientToLocalPoint(normalizedEvent.clientX, normalizedEvent.clientY); + var x = ref$1.x; + var y = ref$1.y; + relatedView.dragLinkStart(normalizedEvent, this.getMagnetNode(), x, y); + paper.undelegateEvents(); + this.delegateDocumentEvents(null, evt.data); + this.focus(); + }, + drag: function(evt) { + var ref = this; + var paper = ref.paper; + var relatedView = ref.relatedView; + var normalizedEvent = normalizeEvent(evt); + var ref$1 = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + var x = ref$1.x; + var y = ref$1.y; + relatedView.dragLink(normalizedEvent, x, y); + }, + dragend: function(evt) { + var ref = this; + var paper = ref.paper; + var relatedView = ref.relatedView; + var normalizedEvent = normalizeEvent(evt); + var ref$1 = paper.snapToGrid(normalizedEvent.clientX, normalizedEvent.clientY); + var x = ref$1.x; + var y = ref$1.y; + relatedView.dragLinkEnd(normalizedEvent, x, y); + this.undelegateDocumentEvents(); + paper.delegateEvents(); + this.blur(); + relatedView.checkMouseleave(normalizedEvent); } }); - var Transition = Generic.define('pn.Transition', { - size: { width: 12, height: 50 }, - attrs: { - 'rect': { - width: 12, - height: 50, - fill: '#000000', - stroke: '#000000' - }, - '.label': { - 'text-anchor': 'middle', - 'ref-x': .5, - 'ref-y': -20, - ref: 'rect', - fill: '#000000', - 'font-size': 12 + + var Boundary = ToolView.extend({ + name: 'boundary', + tagName: 'rect', + options: { + padding: 10, + useModelGeometry: false, + }, + attributes: { + 'fill': 'none', + 'stroke': '#33334F', + 'stroke-width': .5, + 'stroke-dasharray': '5, 5', + 'pointer-events': 'none' + }, + onRender: function() { + this.update(); + }, + update: function() { + var ref = this; + var view = ref.relatedView; + var options = ref.options; + var vel = ref.vel; + var useModelGeometry = options.useModelGeometry; + var rotate = options.rotate; + var padding = normalizeSides(options.padding); + var bbox = getViewBBox(view, useModelGeometry).moveAndExpand({ + x: -padding.left, + y: -padding.top, + width: padding.left + padding.right, + height: padding.top + padding.bottom + }); + var model = view.model; + if (model.isElement()) { + var angle = model.angle(); + if (angle) { + if (rotate) { + var origin = model.getBBox().center(); + vel.rotate(angle, origin.x, origin.y, { absolute: true }); + } else { + bbox = bbox.bbox(angle); + } + } } + vel.attr(bbox.toJSON()); + return this; } - }, { - markup: '', - }); - - var Link$3 = Link.define('pn.Link', { - attrs: { '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z' }} - }); - - var pn = ({ - Place: Place, - PlaceView: PlaceView, - Transition: Transition, - Link: Link$3 }); - var Class = Generic.define('uml.Class', { - attrs: { - rect: { 'width': 200 }, - - '.uml-class-name-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#3498db' }, - '.uml-class-attrs-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#2980b9' }, - '.uml-class-methods-rect': { 'stroke': 'black', 'stroke-width': 2, 'fill': '#2980b9' }, - - '.uml-class-name-text': { - 'ref': '.uml-class-name-rect', - 'ref-y': .5, - 'ref-x': .5, - 'text-anchor': 'middle', - 'y-alignment': 'middle', - 'font-weight': 'bold', - 'fill': 'black', - 'font-size': 12, - 'font-family': 'Times New Roman' + var Anchor = ToolView.extend({ + tagName: 'g', + type: null, + children: [{ + tagName: 'circle', + selector: 'anchor', + attributes: { + 'cursor': 'pointer' + } + }, { + tagName: 'rect', + selector: 'area', + attributes: { + 'pointer-events': 'none', + 'fill': 'none', + 'stroke': '#33334F', + 'stroke-dasharray': '2,4', + 'rx': 5, + 'ry': 5 + } + }], + events: { + mousedown: 'onPointerDown', + touchstart: 'onPointerDown', + dblclick: 'onPointerDblClick', + dbltap: 'onPointerDblClick' + }, + documentEvents: { + mousemove: 'onPointerMove', + touchmove: 'onPointerMove', + mouseup: 'onPointerUp', + touchend: 'onPointerUp', + touchcancel: 'onPointerUp' + }, + options: { + snap: snapAnchor, + anchor: getAnchor, + resetAnchor: true, + customAnchorAttributes: { + 'stroke-width': 4, + 'stroke': '#33334F', + 'fill': '#FFFFFF', + 'r': 5 }, - '.uml-class-attrs-text': { - 'ref': '.uml-class-attrs-rect', 'ref-y': 5, 'ref-x': 5, - 'fill': 'black', 'font-size': 12, 'font-family': 'Times New Roman' + defaultAnchorAttributes: { + 'stroke-width': 2, + 'stroke': '#FFFFFF', + 'fill': '#33334F', + 'r': 6 }, - '.uml-class-methods-text': { - 'ref': '.uml-class-methods-rect', 'ref-y': 5, 'ref-x': 5, - 'fill': 'black', 'font-size': 12, 'font-family': 'Times New Roman' + areaPadding: 6, + snapRadius: 10, + restrictArea: true, + redundancyRemoval: true + }, + onRender: function() { + this.renderChildren(); + this.toggleArea(false); + this.update(); + }, + update: function() { + var type = this.type; + var relatedView = this.relatedView; + var view = relatedView.getEndView(type); + if (view) { + this.updateAnchor(); + this.updateArea(); + this.el.style.display = ''; + } else { + this.el.style.display = 'none'; + } + return this; + }, + updateAnchor: function() { + var childNodes = this.childNodes; + if (!childNodes) { return; } + var anchorNode = childNodes.anchor; + if (!anchorNode) { return; } + var relatedView = this.relatedView; + var type = this.type; + var position = relatedView.getEndAnchor(type); + var options = this.options; + var customAnchor = relatedView.model.prop([type, 'anchor']); + anchorNode.setAttribute('transform', 'translate(' + position.x + ',' + position.y + ')'); + var anchorAttributes = (customAnchor) ? options.customAnchorAttributes : options.defaultAnchorAttributes; + for (var attrName in anchorAttributes) { + anchorNode.setAttribute(attrName, anchorAttributes[attrName]); + } + }, + updateArea: function() { + var childNodes = this.childNodes; + if (!childNodes) { return; } + var areaNode = childNodes.area; + if (!areaNode) { return; } + var relatedView = this.relatedView; + var type = this.type; + var view = relatedView.getEndView(type); + var model = view.model; + var magnet = relatedView.getEndMagnet(type); + var padding = this.options.areaPadding; + if (!isFinite(padding)) { padding = 0; } + var bbox, angle, center; + if (view.isNodeConnection(magnet)) { + bbox = view.getBBox(); + angle = 0; + center = bbox.center(); + } else { + bbox = view.getNodeUnrotatedBBox(magnet); + angle = model.angle(); + center = bbox.center(); + if (angle) { center.rotate(model.getBBox().center(), -angle); } + // TODO: get the link's magnet rotation into account } + bbox.inflate(padding); + areaNode.setAttribute('x', -bbox.width / 2); + areaNode.setAttribute('y', -bbox.height / 2); + areaNode.setAttribute('width', bbox.width); + areaNode.setAttribute('height', bbox.height); + areaNode.setAttribute('transform', 'translate(' + center.x + ',' + center.y + ') rotate(' + angle + ')'); }, - - name: [], - attributes: [], - methods: [] - }, { - markup: [ - '', - '', - '', - '', - '', - '' - ].join(''), - - initialize: function() { - - this.on('change:name change:attributes change:methods', function() { - this.updateRectangles(); - this.trigger('uml-update'); - }, this); - - this.updateRectangles(); - - Generic.prototype.initialize.apply(this, arguments); + toggleArea: function(visible) { + this.childNodes.area.style.display = (visible) ? '' : 'none'; }, - - getClassName: function() { - return this.get('name'); + onPointerDown: function(evt) { + if (this.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + this.paper.undelegateEvents(); + this.delegateDocumentEvents(); + this.focus(); + this.toggleArea(this.options.restrictArea); + this.relatedView.model.startBatch('anchor-move', { ui: true, tool: this.cid }); }, + resetAnchor: function(anchor) { + var type = this.type; + var relatedModel = this.relatedView.model; + if (anchor) { + relatedModel.prop([type, 'anchor'], anchor, { + rewrite: true, + ui: true, + tool: this.cid + }); + } else { + relatedModel.removeProp([type, 'anchor'], { + ui: true, + tool: this.cid + }); + } + }, + onPointerMove: function(evt) { - updateRectangles: function() { - - var attrs = this.get('attrs'); - - var rects = [ - { type: 'name', text: this.getClassName() }, - { type: 'attrs', text: this.get('attributes') }, - { type: 'methods', text: this.get('methods') } - ]; - - var offsetY = 0; - - rects.forEach(function(rect) { - - var lines = Array.isArray(rect.text) ? rect.text : [rect.text]; - var rectHeight = lines.length * 20 + 20; - - attrs['.uml-class-' + rect.type + '-text'].text = lines.join('\n'); - attrs['.uml-class-' + rect.type + '-rect'].height = rectHeight; - attrs['.uml-class-' + rect.type + '-rect'].transform = 'translate(0,' + offsetY + ')'; - - offsetY += rectHeight; - }); - } - - }); - - var ClassView = ElementView.extend({ - - initialize: function() { - - ElementView.prototype.initialize.apply(this, arguments); + var relatedView = this.relatedView; + var type = this.type; + var view = relatedView.getEndView(type); + var model = view.model; + var magnet = relatedView.getEndMagnet(type); + var normalizedEvent = normalizeEvent(evt); + var coords = this.paper.clientToLocalPoint(normalizedEvent.clientX, normalizedEvent.clientY); + var snapFn = this.options.snap; + if (typeof snapFn === 'function') { + coords = snapFn.call(relatedView, coords, view, magnet, type, relatedView, this); + coords = new Point(coords); + } - this.listenTo(this.model, 'uml-update', function() { - this.update(); - this.resize(); - }); - } - }); + if (this.options.restrictArea) { + if (view.isNodeConnection(magnet)) { + // snap coords to the link's connection + var pointAtConnection = view.getClosestPoint(coords); + if (pointAtConnection) { coords = pointAtConnection; } + } else { + // snap coords within node bbox + var bbox = view.getNodeUnrotatedBBox(magnet); + var angle = model.angle(); + var origin = model.getBBox().center(); + var rotatedCoords = coords.clone().rotate(origin, angle); + if (!bbox.containsPoint(rotatedCoords)) { + coords = bbox.pointNearestToPoint(rotatedCoords).rotate(origin, -angle); + } + } + } - var Abstract = Class.define('uml.Abstract', { - attrs: { - '.uml-class-name-rect': { fill: '#e74c3c' }, - '.uml-class-attrs-rect': { fill: '#c0392b' }, - '.uml-class-methods-rect': { fill: '#c0392b' } - } - }, { + var anchor; + var anchorFn = this.options.anchor; + if (typeof anchorFn === 'function') { + anchor = anchorFn.call(relatedView, coords, view, magnet, type, relatedView); + } - getClassName: function() { - return ['<>', this.get('name')]; - } + this.resetAnchor(anchor); + this.update(); + }, - }); - var AbstractView = ClassView; + onPointerUp: function(evt) { + this.paper.delegateEvents(); + this.undelegateDocumentEvents(); + this.blur(); + this.toggleArea(false); + var linkView = this.relatedView; + if (this.options.redundancyRemoval) { linkView.removeRedundantLinearVertices({ ui: true, tool: this.cid }); } + linkView.model.stopBatch('anchor-move', { ui: true, tool: this.cid }); + }, - var Interface = Class.define('uml.Interface', { - attrs: { - '.uml-class-name-rect': { fill: '#f1c40f' }, - '.uml-class-attrs-rect': { fill: '#f39c12' }, - '.uml-class-methods-rect': { fill: '#f39c12' } - } - }, { - getClassName: function() { - return ['<>', this.get('name')]; + onPointerDblClick: function() { + var anchor = this.options.resetAnchor; + if (anchor === false) { return; } // reset anchor disabled + if (anchor === true) { anchor = null; } // remove the current anchor + this.resetAnchor(cloneDeep(anchor)); + this.update(); } }); - var InterfaceView = ClassView; - - var Generalization = Link.define('uml.Generalization', { - attrs: { '.marker-target': { d: 'M 20 0 L 0 10 L 20 20 z', fill: 'white' }} - }); - var Implementation = Link.define('uml.Implementation', { - attrs: { - '.marker-target': { d: 'M 20 0 L 0 10 L 20 20 z', fill: 'white' }, - '.connection': { 'stroke-dasharray': '3,3' } - } + var SourceAnchor = Anchor.extend({ + name: 'source-anchor', + type: 'source' }); - var Aggregation = Link.define('uml.Aggregation', { - attrs: { '.marker-target': { d: 'M 40 10 L 20 20 L 0 10 L 20 0 z', fill: 'white' }} + var TargetAnchor = Anchor.extend({ + name: 'target-anchor', + type: 'target' }); - var Composition = Link.define('uml.Composition', { - attrs: { '.marker-target': { d: 'M 40 10 L 20 20 L 0 10 L 20 0 z', fill: 'black' }} + var index$5 = ({ + Vertices: Vertices, + Segments: Segments, + SourceArrowhead: SourceArrowhead, + TargetArrowhead: TargetArrowhead, + SourceAnchor: SourceAnchor, + TargetAnchor: TargetAnchor, + Button: Button, + Remove: Remove, + Connect: Connect, + Boundary: Boundary }); - var Association = Link.define('uml.Association'); - - // Statechart - - var State$1 = Generic.define('uml.State', { - attrs: { - '.uml-state-body': { - 'width': 200, 'height': 200, 'rx': 10, 'ry': 10, - 'fill': '#ecf0f1', 'stroke': '#bdc3c7', 'stroke-width': 3 - }, - '.uml-state-separator': { - 'stroke': '#bdc3c7', 'stroke-width': 2 - }, - '.uml-state-name': { - 'ref': '.uml-state-body', 'ref-x': .5, 'ref-y': 5, 'text-anchor': 'middle', - 'fill': '#000000', 'font-family': 'Courier New', 'font-size': 14 - }, - '.uml-state-events': { - 'ref': '.uml-state-separator', 'ref-x': 5, 'ref-y': 5, - 'fill': '#000000', 'font-family': 'Courier New', 'font-size': 14 + var Control = ToolView.extend({ + tagName: 'g', + children: [{ + tagName: 'circle', + selector: 'handle', + attributes: { + 'cursor': 'pointer', + 'stroke-width': 2, + 'stroke': '#FFFFFF', + 'fill': '#33334F', + 'r': 6 + } + }, { + tagName: 'rect', + selector: 'extras', + attributes: { + 'pointer-events': 'none', + 'fill': 'none', + 'stroke': '#33334F', + 'stroke-dasharray': '2,4', + 'rx': 5, + 'ry': 5 } + }], + events: { + mousedown: 'onPointerDown', + touchstart: 'onPointerDown', + dblclick: 'onPointerDblClick', + dbltap: 'onPointerDblClick' }, - - name: 'State', - events: [] - - }, { - markup: [ - '', - '', - '', - '', - '', - '', - '', - '' - ].join(''), - - initialize: function() { - - this.on({ - 'change:name': this.updateName, - 'change:events': this.updateEvents, - 'change:size': this.updatePath - }, this); - - this.updateName(); - this.updateEvents(); - this.updatePath(); - - Generic.prototype.initialize.apply(this, arguments); + documentEvents: { + mousemove: 'onPointerMove', + touchmove: 'onPointerMove', + mouseup: 'onPointerUp', + touchend: 'onPointerUp', + touchcancel: 'onPointerUp' }, - - updateName: function() { - - this.attr('.uml-state-name/text', this.get('name')); + options: { + handleAttributes: null, + selector: 'root', + padding: 6, }, - updateEvents: function() { - - this.attr('.uml-state-events/text', this.get('events').join('\n')); + getPosition: function() { + // To be overridden }, - - updatePath: function() { - - var d = 'M 0 20 L ' + this.get('size').width + ' 20'; - - // We are using `silent: true` here because updatePath() is meant to be called - // on resize and there's no need to to update the element twice (`change:size` - // triggers also an update). - this.attr('.uml-state-separator/d', d, { silent: true }); + setPosition: function() { + // To be overridden + }, + resetPosition: function() { + // To be overridden + }, + onRender: function() { + this.renderChildren(); + this.toggleExtras(false); + this.update(); + }, + update: function() { + var ref = this.childNodes; + var handle = ref.handle; + var extras = ref.extras; + if (handle) { + this.updateHandle(handle); + } else { + throw new Error('Control: markup selector `handle` is required'); + } + if (extras) { + this.updateExtras(extras); + } + return this; + }, + updateHandle: function(handleNode) { + var ref = this; + var relatedView = ref.relatedView; + var options = ref.options; + var model = relatedView.model; + var relativePos = this.getPosition(relatedView, this); + var absolutePos = model.getAbsolutePointFromRelative(relativePos); + handleNode.setAttribute('transform', ("translate(" + (absolutePos.x) + "," + (absolutePos.y) + ")")); + var handleAttributes = options.handleAttributes; + if (handleAttributes) { + for (var attrName in handleAttributes) { + handleNode.setAttribute(attrName, handleAttributes[attrName]); + } + } + }, + updateExtras: function(extrasNode) { + var ref = this; + var relatedView = ref.relatedView; + var options = ref.options; + var ref$1 = this.options; + var selector = ref$1.selector; + if (!selector) { + this.toggleExtras(false); + return; + } + var ref$2 = relatedView.findBySelector(selector); + var magnet = ref$2[0]; + if (!magnet) { throw new Error('Control: invalid selector.'); } + var padding = options.padding; + if (!isFinite(padding)) { padding = 0; } + var bbox = relatedView.getNodeUnrotatedBBox(magnet); + var model = relatedView.model; + var angle = model.angle(); + var center = bbox.center(); + if (angle) { center.rotate(model.getBBox().center(), -angle); } + bbox.inflate(padding); + extrasNode.setAttribute('x', -bbox.width / 2); + extrasNode.setAttribute('y', -bbox.height / 2); + extrasNode.setAttribute('width', bbox.width); + extrasNode.setAttribute('height', bbox.height); + extrasNode.setAttribute('transform', ("translate(" + (center.x) + "," + (center.y) + ") rotate(" + angle + ")")); + }, + toggleExtras: function(visible) { + var ref = this.childNodes; + var extras = ref.extras; + if (!extras) { return; } + extras.style.display = (visible) ? '' : 'none'; + }, + onPointerDown: function(evt) { + var ref = this; + var relatedView = ref.relatedView; + var paper = ref.paper; + if (this.guard(evt)) { return; } + evt.stopPropagation(); + evt.preventDefault(); + paper.undelegateEvents(); + this.delegateDocumentEvents(); + this.focus(); + this.toggleExtras(true); + relatedView.model.startBatch('control-move', { ui: true, tool: this.cid }); + }, + onPointerMove: function(evt) { + var ref = this; + var relatedView = ref.relatedView; + var paper = ref.paper; + var model = relatedView.model; + var ref$1 = normalizeEvent(evt); + var clientX = ref$1.clientX; + var clientY = ref$1.clientY; + var coords = paper.clientToLocalPoint(clientX, clientY); + var relativeCoords = model.getRelativePointFromAbsolute(coords); + this.setPosition(relatedView, relativeCoords, this); + this.update(); + }, + onPointerUp: function(_evt) { + var ref = this; + var relatedView = ref.relatedView; + var paper = ref.paper; + paper.delegateEvents(); + this.undelegateDocumentEvents(); + this.blur(); + this.toggleExtras(false); + relatedView.model.stopBatch('control-move', { ui: true, tool: this.cid }); + }, + onPointerDblClick: function() { + var ref = this; + var relatedView = ref.relatedView; + this.resetPosition(relatedView, this); + this.update(); } - }); - var StartState$1 = Circle.define('uml.StartState', { - type: 'uml.StartState', - attrs: { circle: { 'fill': '#34495e', 'stroke': '#2c3e50', 'stroke-width': 2, 'rx': 1 }} }); - var EndState$1 = Generic.define('uml.EndState', { - size: { width: 20, height: 20 }, - attrs: { - 'circle.outer': { - transform: 'translate(10, 10)', - r: 10, - fill: '#ffffff', - stroke: '#2c3e50' - }, - 'circle.inner': { - transform: 'translate(10, 10)', - r: 6, - fill: '#34495e' - } - } - }, { - markup: '', - }); - var Transition$1 = Link.define('uml.Transition', { - attrs: { - '.marker-target': { d: 'M 10 0 L 0 5 L 10 10 z', fill: '#34495e', stroke: '#2c3e50' }, - '.connection': { stroke: '#2c3e50' } - } + var index$6 = ({ + Button: Button, + Remove: Remove, + Connect: Connect, + Boundary: Boundary, + Control: Control }); - var uml = ({ - Class: Class, - ClassView: ClassView, - Abstract: Abstract, - AbstractView: AbstractView, - Interface: Interface, - InterfaceView: InterfaceView, - Generalization: Generalization, - Implementation: Implementation, - Aggregation: Aggregation, - Composition: Composition, - Association: Association, - State: State$1, - StartState: StartState$1, - EndState: EndState$1, - Transition: Transition$1 - }); + var version = "3.5.1"; + var Vectorizer = V; + var layout = { PortLabel: PortLabel, Port: Port }; + var setTheme = function(theme, opt) { + opt = opt || {}; - var index$6 = ({ - basic: basic, - standard: standard, - devs: devs, - logic: logic, - chess: chess, - erd: erd, - fsa: fsa, - org: org, - pn: pn, - uml: uml - }); + invoke(views, 'setTheme', theme, opt); + + // Update the default theme on the view prototype. + View.prototype.defaultTheme = theme; + }; var layout$1 = { DirectedGraph: DirectedGraph, PortLabel: PortLabel, Port: Port }; @@ -33167,21 +33167,21 @@ var joint = (function (exports, Backbone, _, $) { exports.anchors = anchors; exports.config = config; exports.connectionPoints = connectionPoints; - exports.connectionStrategies = index$2; + exports.connectionStrategies = index$4; exports.connectors = connectors; - exports.dia = index$5; - exports.elementTools = index$4; + exports.dia = index$2; + exports.elementTools = index$6; exports.env = env; exports.format = format$1; exports.g = g; exports.highlighters = highlighters; exports.layout = layout$1; exports.linkAnchors = linkAnchors; - exports.linkTools = index$3; + exports.linkTools = index$5; exports.mvc = index$1; exports.routers = routers; exports.setTheme = setTheme; - exports.shapes = index$6; + exports.shapes = index$3; exports.ui = ui; exports.util = index; exports.version = version; diff --git a/dist/joint.nowrap.min.js b/dist/joint.nowrap.min.js index 2058d8593..141557e73 100644 --- a/dist/joint.nowrap.min.js +++ b/dist/joint.nowrap.min.js @@ -1,8 +1,8 @@ -/*! JointJS v3.5.0 (2022-02-01) - JavaScript diagramming library +/*! JointJS v3.5.1 (2022-02-02) - JavaScript diagramming library This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -var joint=function(t,s,i,S){"use strict";s=s&&s.hasOwnProperty("default")?s.default:s,i=i&&i.hasOwnProperty("default")?i.default:i,S=S&&S.hasOwnProperty("default")?S.default:S;var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function n(t,e){return t(e={exports:{}},e.exports),e.exports}var r=function(t){return t&&t.Math==Math&&t},u=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof e&&e)||function(){return this}()||Function("return this")(),a=function(t){try{return!!t()}catch(t){return!0}},l=!a(function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}),o={}.propertyIsEnumerable,c=Object.getOwnPropertyDescriptor,h={f:c&&!o.call({1:2},1)?function(t){var e=c(this,t);return!!e&&e.enumerable}:o},C=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},g={}.toString,d=function(t){return g.call(t).slice(8,-1)},f="".split,w=a(function(){return!Object("z").propertyIsEnumerable(0)})?function(t){return"String"==d(t)?f.call(t,""):Object(t)}:Object,p=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},m=function(t){return w(p(t))},I=function(t){return"object"==typeof t?null!==t:"function"==typeof t},v=function(t,e){if(!I(t))return t;var i,n;if(e&&"function"==typeof(i=t.toString)&&!I(n=i.call(t)))return n;if("function"==typeof(i=t.valueOf)&&!I(n=i.call(t)))return n;if(!e&&"function"==typeof(i=t.toString)&&!I(n=i.call(t)))return n;throw TypeError("Can't convert object to primitive value")},A={}.hasOwnProperty,y=function(t,e){return A.call(t,e)},b=u.document,N=I(b)&&I(b.createElement),x=function(t){return N?b.createElement(t):{}},M=!l&&!a(function(){return 7!=Object.defineProperty(x("div"),"a",{get:function(){return 7}}).a}),k=Object.getOwnPropertyDescriptor,D={f:l?k:function(t,e){if(t=m(t),e=v(e,!0),M)try{return k(t,e)}catch(t){}if(y(t,e))return C(!h.f.call(t,e),t[e])}},T=function(t){if(!I(t))throw TypeError(String(t)+" is not an object");return t},L=Object.defineProperty,z={f:l?L:function(t,e,i){if(T(t),e=v(e,!0),T(i),M)try{return L(t,e,i)}catch(t){}if("get"in i||"set"in i)throw TypeError("Accessors not supported");return"value"in i&&(t[e]=i.value),t}},P=l?function(t,e,i){return z.f(t,e,C(1,i))}:function(t,e,i){return t[e]=i,t},j=function(e,i){try{P(u,e,i)}catch(t){u[e]=i}return i},Z="__core-js_shared__",G=u[Z]||j(Z,{}),B=Function.toString;"function"!=typeof G.inspectSource&&(G.inspectSource=function(t){return B.call(t)});var O,E,R,Y=G.inspectSource,W=u.WeakMap,U="function"==typeof W&&/native code/.test(Y(W)),F=n(function(t){(t.exports=function(t,e){return G[t]||(G[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.8.3",mode:"global",copyright:"\xa9 2021 Denis Pushkarev (zloirock.ru)"})}),V=0,X=Math.random(),H=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++V+X).toString(36)},J=F("keys"),_=function(t){return J[t]||(J[t]=H(t))},Q={},K=u.WeakMap;if(U){var q=G.state||(G.state=new K),$=q.get,tt=q.has,et=q.set;O=function(t,e){return e.facade=t,et.call(q,t,e),e},E=function(t){return $.call(q,t)||{}},R=function(t){return tt.call(q,t)}}else{var it=_("state");Q[it]=!0,O=function(t,e){return e.facade=t,P(t,it,e),e},E=function(t){return y(t,it)?t[it]:{}},R=function(t){return y(t,it)}}var nt,rt={set:O,get:E,has:R,enforce:function(t){return R(t)?E(t):O(t,{})},getterFor:function(i){return function(t){var e;if(!I(t)||(e=E(t)).type!==i)throw TypeError("Incompatible receiver, "+i+" required");return e}}},ot=n(function(t){var e=rt.get,l=rt.enforce,c=String(String).split("String");(t.exports=function(t,e,i,n){var r,o=!!n&&!!n.unsafe,a=!!n&&!!n.enumerable,s=!!n&&!!n.noTargetGet;"function"==typeof i&&("string"!=typeof e||y(i,"name")||P(i,"name",e),(r=l(i)).source||(r.source=c.join("string"==typeof e?e:""))),t!==u?(o?!s&&t[e]&&(a=!0):delete t[e],a?t[e]=i:P(t,e,i)):a?t[e]=i:j(e,i)})(Function.prototype,"toString",function(){return"function"==typeof this&&e(this).source||Y(this)})}),at=u,st=function(t){return"function"==typeof t?t:void 0},lt=function(t,e){return arguments.length<2?st(at[t])||st(u[t]):at[t]&&at[t][e]||u[t]&&u[t][e]},ct=Math.ceil,ut=Math.floor,ht=function(t){return isNaN(t=+t)?0:(0r;)y(n,i=e[r++])&&(~vt(o,i)||o.push(i));return o},Ct=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],wt=Ct.concat("length","prototype"),yt={f:Object.getOwnPropertyNames||function(t){return At(t,wt)}},bt={f:Object.getOwnPropertySymbols},Nt=lt("Reflect","ownKeys")||function(t){var e=yt.f(T(t)),i=bt.f;return i?e.concat(i(t)):e},xt=function(t,e){for(var i=Nt(e),n=z.f,r=D.f,o=0;o"+t+""},_t=function(){try{nt=document.domain&&new ActiveXObject("htmlfile")}catch(t){}var t,e;_t=nt?function(t){t.write(Jt("")),t.close();var e=t.parentWindow.Object;return t=null,e}(nt):((e=x("iframe")).style.display="none",Ut.appendChild(e),e.src=String("javascript:"),(t=e.contentWindow.document).open(),t.write(Jt("document.F=Object")),t.close(),t.F);for(var i=Ct.length;i--;)delete _t[Ft][Ct[i]];return _t()};Q[Xt]=!0;var Qt=Object.create||function(t,e){var i;return null!==t?(Ht[Ft]=T(t),i=new Ht,Ht[Ft]=null,i[Xt]=t):i=_t(),void 0===e?i:Wt(i,e)},Kt=Rt("unscopables"),qt=Array.prototype;null==qt[Kt]&&z.f(qt,Kt,{configurable:!0,value:Qt(null)});var $t=function(t){qt[Kt][t]=!0},te=Object.defineProperty,ee={},ie=function(t){throw t},ne=function(t,e){if(y(ee,t))return ee[t];e||(e={});var i=[][t],n=!!y(e,"ACCESSORS")&&e.ACCESSORS,r=y(e,0)?e[0]:ie,o=y(e,1)?e[1]:void 0;return ee[t]=!!i&&!a(function(){if(n&&!l)return!0;var t={length:-1};n?te(t,1,{enumerable:!0,get:ie}):t[1]=1,i.call(t,r,o)})},re=It.includes,oe=ne("indexOf",{ACCESSORS:!0,1:0});jt({target:"Array",proto:!0,forced:!oe},{includes:function(t){return re(this,t,1=i.length?{value:void 0,done:!0}:(t=Xe(i,n),e.index+=t.length,{value:t,done:!1})});var Qe=function(e,t,i,n){try{return n?t(T(i)[0],i[1]):t(i)}catch(t){throw function(t){var e=t.return;if(void 0!==e)T(e.call(t)).value}(e),t}},Ke=Rt("iterator"),qe=Array.prototype,$e=function(t,e,i){var n=v(e);n in t?z.f(t,n,C(0,i)):t[n]=i},ti={};ti[Rt("toStringTag")]="z";var ei="[object z]"===String(ti),ii=Rt("toStringTag"),ni="Arguments"==d(function(){return arguments}()),ri=ei?d:function(t){var e,i,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(i=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),ii))?i:ni?d(e):"Object"==(n=d(e))&&"function"==typeof e.callee?"Arguments":n},oi=Rt("iterator"),ai=Rt("iterator"),si=!1;try{var li=0,ci={next:function(){return{done:!!li++}},return:function(){si=!0}};ci[ai]=function(){return this},Array.from(ci,function(){throw 2})}catch(t){}var ui=!function(t,e){if(!e&&!si)return!1;var i=!1;try{var n={};n[ai]=function(){return{next:function(){return{done:i=!0}}}},t(n)}catch(t){}return i}(function(t){Array.from(t)});jt({target:"Array",stat:!0,forced:ui},{from:function(t){var e,i,n,r,o,a,s,l=ce(t),c="function"==typeof this?this:Array,u=arguments.length,h=1>8-r%1*8)){if(255<(i=n.charCodeAt(r+=.75)))throw new l("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");e=e<<8|i}return a}),t.atob||(t.atob=function(t){var e=String(t).replace(/=+$/,"");if(e.length%4==1)throw new l("'atob' failed: The string to be decoded is not correctly encoded.");for(var i,n,r=0,o=0,a="";n=e.charAt(o++);~n&&(i=r%4?64*i+n:n,r++%4)?a+=String.fromCharCode(255&i>>(-2*r&6)):0)n=s.indexOf(n);return a})}()});jt({target:"Number",stat:!0},{isNaN:function(t){return t!=t}});at.Number.isNaN;var pi=u.isFinite,mi=Number.isFinite||function(t){return"number"==typeof t&&pi(t)};jt({target:"Number",stat:!0},{isFinite:mi});at.Number.isFinite;var Ii=Rt("match"),vi=function(t){if(I(e=t)&&(void 0!==(i=e[Ii])?i:"RegExp"==d(e)))throw TypeError("The method doesn't accept regular expressions");var e,i;return t},Ai=Rt("match"),Ci=function(e){var i=/./;try{"/./"[e](i)}catch(t){try{return i[Ai]=!1,"/./"[e](i)}catch(t){}}return!1};jt({target:"String",proto:!0,forced:!Ci("includes")},{includes:function(t){return!!~String(p(this)).indexOf(vi(t),1n)&&!(new nn(t,i).length()>n)},divideAt:function(t){var e=this.pointAt(t);return[new nn(this.start,e),new nn(e,this.end)]},divideAtLength:function(t){var e=this.pointAtLength(t);return[new nn(this.start,e),new nn(e,this.end)]},equals:function(t){return!!t&&this.start.x===t.start.x&&this.start.y===t.start.y&&this.end.x===t.end.x&&this.end.y===t.end.y},intersect:function(t,e){if(t&&t.intersectionWithLine){var i=t.intersectionWithLine(this,e);return i&&t instanceof nn&&(i=i[0]),i}return null},intersectionWithLine:function(t){var e=new qi(this.end.x-this.start.x,this.end.y-this.start.y),i=new qi(t.end.x-t.start.x,t.end.y-t.start.y),n=e.x*i.y-e.y*i.x,r=new qi(t.start.x-this.start.x,t.start.y-this.start.y),o=r.x*i.y-r.y*i.x,a=r.x*e.y-r.y*e.x;if(0===n||o*n<0||a*n<0)return null;if(0s.x+o/2,h=ns.x?r-30:r+30)-c)/(a*a*(n-l))+l:e=a*a/(r-c)-a*a*(n-l)*((i=r>s.y?n+30:n-30)-l)/(o*o*(r-c))+c,new qi(i,e).theta(t)},toString:function(){return new qi(this.x,this.y).toString()+" "+this.a+" "+this.b}};var cn=ln,un=Math.abs,hn=Math.cos,gn=Math.sin,dn=Math.min,fn=Math.max,pn=Math.round,mn=Math.pow,In=function(t,e,i,n){if(!(this instanceof In))return new In(t,e,i,n);Object(t)===t&&(e=t.y,i=t.width,n=t.height,t=t.x),this.x=void 0===t?0:t,this.y=void 0===e?0:e,this.width=void 0===i?0:i,this.height=void 0===n?0:n};In.fromEllipse=function(t){return t=new ln(t),new In(t.x-t.a,t.y-t.b,2*t.a,2*t.b)},In.fromPointUnion=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(0===t.length)return null;var i,n,r,o,a=new qi;i=n=1/0,r=o=-1/0;for(var s=0;s=this.x&&t.x<=this.x+this.width&&t.y>=this.y&&t.y<=this.y+this.height},containsRect:function(t){var e=new In(this).normalize(),i=new In(t).normalize(),n=e.width,r=e.height,o=i.width,a=i.height;if(!(n&&r&&o&&a))return!1;var s=e.x,l=e.y,c=i.x,u=i.y;return o+=c,n+=s,a+=u,r+=l,s<=c&&o<=n&&l<=u&&a<=r},corner:function(){return new qi(this.x+this.width,this.y+this.height)},equals:function(t){var e=new In(this).normalize(),i=new In(t).normalize();return e.x===i.x&&e.y===i.y&&e.width===i.width&&e.height===i.height},inflate:function(t,e){return void 0===t&&(t=0),void 0===e&&(e=t),this.x-=t,this.y-=e,this.width+=2*t,this.height+=2*e,this},intersect:function(t){var e=this.origin(),i=this.corner(),n=t.origin(),r=t.corner();if(r.x<=e.x||r.y<=e.y||n.x>=i.x||n.y>=i.y)return null;var o=fn(e.x,n.x),a=fn(e.y,n.y);return new In(o,a,dn(i.x,r.x)-o,dn(i.y,r.y)-a)},intersectionWithLine:function(t){var e,i,n=[this.topLine(),this.rightLine(),this.bottomLine(),this.leftLine()],r=[],o=[],a=n.length;for(i=0;iu&&(n=(this.x+this.width-u)/(d.x-u)),d.y>h&&(s=(this.y+this.height-h)/(d.y-h));var f=t.topRight();f.x>u&&(r=(this.x+this.width-u)/(f.x-u)),f.yh&&(c=(this.y+this.height-h)/(p.y-h)),{sx:dn(i,n,r,o),sy:dn(a,s,l,c)}},maxRectUniformScaleToFit:function(t,e){var i=this.maxRectScaleToFit(t,e);return dn(i.sx,i.sy)},moveAndExpand:function(t){return this.x+=t.x||0,this.y+=t.y||0,this.width+=t.width||0,this.height+=t.height||0,this},normalize:function(){var t=this.x,e=this.y,i=this.width,n=this.height;return this.width<0&&(t=this.x+this.width,i=-this.width),this.height<0&&(e=this.y+this.height,n=-this.height),this.x=t,this.y=e,this.width=i,this.height=n,this},offset:function(t,e){return qi.prototype.offset.call(this,t,e)},origin:function(){return new qi(this.x,this.y)},pointNearestToPoint:function(t){if(t=new qi(t),this.containsPoint(t))switch(this.sideNearestToPoint(t)){case"right":return new qi(this.x+this.width,t.y);case"left":return new qi(this.x,t.y);case"bottom":return new qi(t.x,this.y+this.height);case"top":return new qi(t.x,this.y)}return t.adhereToRect(this)},rightLine:function(){return new nn(this.topRight(),this.bottomRight())},rightMiddle:function(){return new qi(this.x+this.width,this.y+this.height/2)},round:function(t){var e=1;if(t)switch(t){case 1:e=10;break;case 2:e=100;break;case 3:e=1e3;break;default:e=mn(10,t)}return this.x=pn(this.x*e)/e,this.y=pn(this.y*e)/e,this.width=pn(this.width*e)/e,this.height=pn(this.height*e)/e,this},scale:function(t,e,i){return i=this.origin().scale(t,e,i),this.x=i.x,this.y=i.y,this.width*=t,this.height*=e,this},sideNearestToPoint:function(t){var e=(t=new qi(t)).x-this.x,i=this.x+this.width-t.x,n=t.y-this.y,r=e,o="left";return in.x&&(n=t[e]);var a=[];for(e=0;eg.y||r>h.y&&r<=g.y){var d=h.x-n>g.x-n?h.x-n:g.x-n;0<=d&&(u.x=n+d,u.y=r,c.start=t,c.end=u,l.intersect(c)&&s++)}o=a}return s%2==1},close:function(){var t=this.start,e=this.end,i=this.points;return t&&e&&!t.equals(e)&&i.push(t.clone()),this},lengthPoints:function(){return this.points},convexHull:function(){return new yn(wn(this.points))},equals:function(t){if(!t)return!1;var e=this.points,i=t.points,n=e.length;if(i.length!==n)return!1;for(var r=0;rr+a&&n.x>r+a||i.xo+s&&n.y>o+s||i.ye.x&&t.ye.y}function Qn(t,e){return hr(t,e,{interior:!1})}function Kn(t,e){return gr(t,e,{interior:!1})}function qn(t,e){return dr(t,e,{interior:!1})}function $n(t,e){return pr(t,e,{interior:!1})}function tr(t,e){return hr(t,e,{interior:!0})}function er(t,e){return gr(t,e,{interior:!0})}function ir(t,e){return dr(t,e,{interior:!0})}function nr(t,e){return pr(t,e,{interior:!0})}function rr(t,e){return mr(t,e,{interior:!0})}function or(t,i,n){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?tr(e,i):Qn(e,i)})}function ar(t,i,n){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?er(e,i):Kn(e,i)})}function sr(t,e,i){return cr(t,Un.fromRect(e),i)}function lr(t,e,i){return fr(t,e,i,{interior:!1})}function cr(t,e,i){return fr(t,e,i,{interior:!0})}function ur(t,i,n,r){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?cr(i,e,r):lr(i,e,r)})}function hr(t,e,i){void 0===i&&(i={});var n,r=i.interior;if(void 0===r&&(r=!1),r){if(t.containsPoint(e.start))return!0;var o=t.start,a=t.end,s=t.points;n=a.equals(o)?s:s.concat([o])}else n=t.points;for(var l=n.length,c=new nn,u=0;u'+t+"";return L.parseXML(e,{async:!1}).documentElement}var i=document.createElementNS(T.svg,"svg");return i.setAttributeNS(T.xmlns,"xmlns:xlink",T.xlink),i.setAttribute("version","1.1"),i},L.createSVGStyle=function(t){return L("style",{type:"text/css"},[L.createCDATASection(t)]).node},L.createCDATASection=function(t){return void 0===t&&(t=""),document.implementation.createDocument(null,"xml",null).createCDATASection(t)},L.idCounter=0,L.uniqueId=function(){return"v-"+ ++L.idCounter},L.toNode=function(t){return L.isV(t)?t.node:t.nodeName&&t||t[0]},L.ensureId=function(t){return(t=L.toNode(t)).id||(t.id=L.uniqueId())},L.sanitizeText=function(t){return(t||"").replace(/ /g,"\xa0")},L.isUndefined=function(t){return void 0===t},L.isString=function(t){return"string"==typeof t},L.isObject=function(t){return t&&"object"==typeof t},L.isArray=Array.isArray,L.parseXML=function(t,e){var i;e=e||{};try{var n=new DOMParser;L.isUndefined(e.async)||(n.async=e.async),i=n.parseFromString(t,"text/xml")}catch(t){i=void 0}if(!i||i.getElementsByTagName("parsererror").length)throw new Error("Invalid XML: "+t);return i},L.qualifyAttr=function(t){if(-1===t.indexOf(":"))return{ns:null,local:t};var e=t.split(":");return{ns:T[e[0]],local:e[1]}},L.transformRegex=/(\w+)\(([^,)]+),?([^)]+)?\)/gi,L.transformSeparatorRegex=/[ ,]+/,L.transformationListRegex=/^(\w+)\((.*)\)/,L.transformStringToMatrix=function(t){var e=L.createSVGMatrix(),i=t&&t.match(L.transformRegex);if(!i)return e;for(var n=0,r=i.length;n=t.start&&et.start&&i<=t.end||t.start>=e&&t.end=e?t.end+=i:t.start>=e&&(t.start+=i,t.end+=i)}),t},L.convertLineToPathData=function(t){return["M",(t=L(t)).attr("x1"),t.attr("y1"),"L",t.attr("x2"),t.attr("y2")].join(" ")},L.convertPolygonToPathData=function(t){var e=L.getPointsFromSvgNode(t);return 0===e.length?null:L.svgPointsToPath(e)+" Z"},L.convertPolylineToPathData=function(t){var e=L.getPointsFromSvgNode(t);return 0===e.length?null:L.svgPointsToPath(e)},L.svgPointsToPath=function(t){for(var e=0,i=t.length;e=o[r]&&(a.push([e].concat(n.splice(0,o[r]))),o[r]););}),a}(t)),!t||!t.length)return[["M",0,0]];for(var e,i=[],n=0,r=0,o=0,a=0,s=t.length,l=0;lg){var M=N,S=l,k=c;f=t(l=w+n*V(N=b+g*(s&&bd)T=d-1;else if(void 0!==o){var L;if(void 0===m)m=(L="auto"===i.lineHeight?{value:1.5,unit:"em"}:Rr(i.lineHeight,["em"])||{value:1,unit:"em"}).value,"em"===L.unit&&(m*=l.getBBox().height);m*A.length>o&&(T=Math.floor(o/m)-1)}if(null!==T){A.splice(T+1);var z=n.ellipsis;if(!z||T<0)break;"string"!=typeof z&&(z="\u2026");var P=A[T];if(!P&&!N)break;var j,Z,G=P.length;do{if(Z=P[G],j=P.substring(0,G),Z?Z.match(u)&&(j+=Z):j+="string"==typeof u?u:" ",j+=z,c.data=j,s.getComputedTextLength()<=r){A[T]=j;break}G--}while(0<=G);break}}}return n.svgDocument?a.removeChild(l):document.body.removeChild(a),A.join(h)},Wr=function(t){var e=S(S.parseHTML("
"+t+"
",null,!1));return e.find("*").each(function(){var i=this;S.each(i.attributes,function(){var t=this.name,e=this.value;(t.startsWith("on")||e.startsWith("javascript:")||e.startsWith("data:")||e.startsWith("vbscript:"))&&S(i).removeAttr(t)})}),e.html()},Ur=function(t,e){if(window.navigator.msSaveBlob)window.navigator.msSaveBlob(t,e);else{var i=window.URL.createObjectURL(t),n=document.createElement("a");n.href=i,n.download=e,document.body.appendChild(n),n.click(),document.body.removeChild(n),window.URL.revokeObjectURL(i)}},Fr=function(t){t=t.replace(/\s/g,"");var e,i=(t=decodeURIComponent(t)).indexOf(","),n=t.slice(0,i),r=n.split(":")[1].split(";")[0],o=t.slice(i+1);e=0<=n.indexOf("base64")?atob(o):unescape(encodeURIComponent(o));for(var a=new Uint8Array(e.length),s=0;s')({color:t.color||"blue",opacity:Number.isFinite(t.opacity)?t.opacity:1,outerRadius:e+i,innerRadius:e})},highlight:function(t){return Kr('')({color:t.color||"red",width:Number.isFinite(t.width)?t.width:1,blur:Number.isFinite(t.blur)?t.blur:0,opacity:Number.isFinite(t.opacity)?t.opacity:1})},blur:function(t){var e=Number.isFinite(t.x)?t.x:2;return Kr('')({stdDeviation:Number.isFinite(t.y)?[e,t.y]:e})},dropShadow:function(t){var e="SVGFEDropShadowElement"in window?'':'';return Kr(e)({dx:t.dx||0,dy:t.dy||0,opacity:Number.isFinite(t.opacity)?t.opacity:1,color:t.color||"black",blur:Number.isFinite(t.blur)?t.blur:4})},grayscale:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({a:.2126+.7874*(1-e),b:.7152-.7152*(1-e),c:.0722-.0722*(1-e),d:.2126-.2126*(1-e),e:.7152+.2848*(1-e),f:.0722-.0722*(1-e),g:.2126-.2126*(1-e),h:.0722+.9278*(1-e)})},sepia:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({a:.393+.607*(1-e),b:.769-.769*(1-e),c:.189-.189*(1-e),d:.349-.349*(1-e),e:.686+.314*(1-e),f:.168-.168*(1-e),g:.272-.272*(1-e),h:.534-.534*(1-e),i:.131+.869*(1-e)})},saturate:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:1-e})},hueRotate:function(t){return Kr('')({angle:t.angle||0})},invert:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:e,amount2:1-e})},brightness:function(t){return Kr('')({amount:Number.isFinite(t.amount)?t.amount:1})},contrast:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:e,amount2:.5-e/2})}},Qr={number:function(t,e,o){o=o||{currency:["$",""],decimal:".",thousands:",",grouping:[3]};var i=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i.exec(t),n=i[1]||" ",r=i[2]||">",a=i[3]||"",s=i[4]||"",l=i[5],c=+i[6],u=i[7],h=i[8],g=i[9],d=1,f="",p="",m=!1;switch(h&&(h=+h.substring(1)),(l||"0"===n&&"="===r)&&(l=n="0",r="=",u&&(c-=Math.floor((c-1)/4))),g){case"n":u=!0,g="g";break;case"%":d=100,p="%",g="f";break;case"p":d=100,p="%",g="r";break;case"b":case"o":case"x":case"X":"#"===s&&(f="0"+g.toLowerCase());break;case"c":case"d":m=!0,h=0;break;case"s":d=-1,g="r"}"$"===s&&(f=o.currency[0],p=o.currency[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):"e"!=g&&"f"!=g||(h=Math.max(0,Math.min(20,h))));var I=l&&u;if(m&&e%1)return"";var v=e<0||0===e&&1/e<0?(e=-e,"-"):a,A=p;if(d<0){var C=this.prefix(e,h);e=C.scale(e),A=C.symbol+p}else e*=d;var w=(e=this.convert(g,e,h)).lastIndexOf("."),y=w<0?e:e.substring(0,w),b=w<0?"":o.decimal+e.substring(w+1);function N(t){for(var e=t.length,i=[],n=0,r=o.grouping[0];0"===r?M+v+e:"^"===r?M.substring(0,x>>=1)+v+e+M.substring(x):v+(I?e:M+e))+A},string:function(t,e){for(var i,n="{",r=!1,o=[];-1!==(i=t.indexOf(n));){var a,s,l;if(a=t.slice(0,i),r){l=(s=a.split(":")).shift().split("."),a=e;for(var c=0;c|\$\{ ?([^{} ]+) ?\}|\{\{([^{} ]+)\}\}/g;return function(n){return n=n||{},t.replace(e,function(t){for(var e=Array.from(arguments).slice(1,4).find(function(t){return!!t}).split("."),i=n[e.shift()];void 0!==i&&e.length;)i=i[e.shift()];return void 0!==i?i:""})}},qr=i.assign,$r=i.defaults,to=qr,eo=i.defaultsDeep,io=i.assign,no=i.defaults,ro=i.defaultsDeep,oo=i.invokeMap||i.invoke,ao=i.sortedIndexBy||i.sortedIndex,so=i.uniqBy||i.uniq,lo=i.clone,co=i.cloneDeep,uo=i.isEmpty,ho=i.isEqual,go=i.isFunction,fo=i.isPlainObject,po=i.toArray,mo=i.debounce,Io=i.groupBy,vo=i.sortBy,Ao=i.flattenDeep,Co=i.without,wo=i.difference,yo=i.intersection,bo=i.union,No=i.has,xo=i.result,Mo=i.omit,So=i.pick,ko=i.bindAll,Do=i.forIn,To=i.camelCase,Lo=i.uniqueId,zo=function(){if(i.mergeWith){var t=Array.from(arguments),e=t[t.length-1],n=go(e)?e:Bo;return t.push(function(t,e){var i=n(t,e);return void 0!==i?i:Array.isArray(t)&&!Array.isArray(e)?e:void 0}),i.mergeWith.apply(this,t)}return i.merge.apply(this,arguments)},Po=function(t){var e=Object.prototype.toString;return!0===t||!1===t||!!t&&"object"==typeof t&&"[object Boolean]"===e.call(t)},jo=function(t){return!!t&&("object"==typeof t||"function"==typeof t)},Zo=function(t){var e=Object.prototype.toString;return"number"==typeof t||!!t&&"object"==typeof t&&"[object Number]"===e.call(t)},Go=function(t){var e=Object.prototype.toString;return"string"==typeof t||!!t&&"object"==typeof t&&"[object String]"===e.call(t)},Bo=function(){};function Oo(t){t=so(t);var a=po(t).reduce(function(t,e){return t[e.id]=e.clone(),t},{});return po(t).forEach(function(t){var e=a[t.id];if(e.isLink()){var i=e.source(),n=e.target();i.id&&a[i.id]&&e.prop("source/id",a[i.id].id),n.id&&a[n.id]&&e.prop("target/id",a[n.id].id)}var r=t.get("parent");r&&a[r]&&e.set("parent",a[r].id);var o=po(t.get("embeds")).reduce(function(t,e){return a[e]&&t.push(a[e].id),t},[]);uo(o)||e.set("embeds",o)}),a}var Eo={x:"x",y:"y",width:"w",height:"h",minimum:"s",maximum:"l",diagonal:"d"},Ro=Object.keys(Eo).map(function(t){return Eo[t]}).join(""),Yo="[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?",Wo=/\s/g,Uo=new RegExp("^("+Yo+"\\*)?(["+Ro+"])([-+]{1,2}"+Yo+")?$","g");function Fo(t){throw new Error("Invalid calc() expression: "+t)}function Vo(t,e){var i=Uo.exec(t.replace(Wo,""));i||Fo(t),Uo.lastIndex=0;var n=i[1];void 0===n&&(n=1);var r=i[2],o=i[3];void 0===o&&(o=0);var a=e.x,s=e.y,l=e.width,c=e.height,u=0;switch(r){case Eo.width:u=l;break;case Eo.height:u=c;break;case Eo.x:u=a;break;case Eo.y:u=s;break;case Eo.minimum:u=Math.min(c,l);break;case Eo.maximum:u=Math.max(c,l);break;case Eo.diagonal:u=Math.sqrt(c*c+l*l)}return parseFloat(n)*u+function(t){if(!t)return 0;switch(t[0]){case"+":return parseFloat(t.substr(1));case"-":return-parseFloat(t.substr(1))}return parseFloat(t)}(o)}function Xo(t){return"string"==typeof t&&t.includes("calc")}var Ho="calc(",Jo=Ho.length;function _o(t,e){for(var i=t,n=0;;){var r=i.indexOf(Ho,n);if(-1===r)return i;var o=r+Jo,a=1;t:for(;;){switch(i[o]){case"(":a++;break;case")":if(0===--a)break t;break;case void 0:Fo(i)}o++}var s=i.slice(r+Jo,o);Xo(s)&&(s=_o(s,e));var l=String(Vo(s,e));i=i.slice(0,r)+l+i.slice(o+1),n=r+l.length}}function Qo(o,a){return function(t,e){var i=Er(t);t=parseFloat(t),i&&(t/=100);var n={};if(isFinite(t)){var r=i||0<=t&&t<=1?t*e[a]:Math.max(t+e[a],0);n[o]=r}return n}}function Ko(a,s,l){return function(t,e){var i,n=Er(t);if(t=parseFloat(t),n&&(t/=100),isFinite(t)){var r=e[l]();i=n||0e.width?sa:la)(t,e)})},refRCircumscribed:{set:function(t,e){var i=Er(t);t=parseFloat(t),i&&(t/=100);var n,r=Math.sqrt(e.height*e.height+e.width*e.width);return isFinite(t)&&(n=i||0<=t&&t<=1?t*r:Math.max(t+r,0)),{r:n}}},refCx:{set:Qo("cx","width")},refCy:{set:Qo("cy","height")},xAlignment:{offset:qo("x","width","right")},yAlignment:{offset:qo("y","height","bottom")},resetOffset:{offset:function(t,e){return t?{x:-e.x,y:-e.y}:{x:0,y:0}}},refDResetOffset:{set:ta({resetOffset:!0})},refDKeepOffset:{set:ta({resetOffset:!1})},refPointsResetOffset:{set:ea({resetOffset:!0})},refPointsKeepOffset:{set:ea({resetOffset:!1})},connection:{qualify:ra,set:function(t){var e,i=t.stubs;if(void 0===i&&(i=0),isFinite(i)&&0!==i){var n;n=i<0?(this.getConnectionLength()+i)/2:i;var r=this.getConnection(),o=r.divideAtLength(n),a=r.divideAtLength(-n);o&&a&&(e=o[0].serialize()+" "+a[1].serialize())}return{d:e||this.getSerializedConnection()}}},atConnectionLengthKeepGradient:{qualify:ra,set:ia("getTangentAtLength",{rotate:!0})},atConnectionLengthIgnoreGradient:{qualify:ra,set:ia("getTangentAtLength",{rotate:!1})},atConnectionRatioKeepGradient:{qualify:ra,set:ia("getTangentAtRatio",{rotate:!0})},atConnectionRatioIgnoreGradient:{qualify:ra,set:ia("getTangentAtRatio",{rotate:!1})}};["transform","d","points","width","height","cx","cy","r","rx","ry","x1","x2","y1","y2","x","y","dx","dy"].forEach(function(n){ca[n]={qualify:Xo,set:function(t,e){var i;return(i={})[n]=_o(t,e),i}}}),ca.refR=ca.refRInscribed,ca.refD=ca.refDResetOffset,ca.refPoints=ca.refPointsResetOffset,ca.atConnectionLength=ca.atConnectionLengthKeepGradient,ca.atConnectionRatio=ca.atConnectionRatioKeepGradient,ca.refX2=ca.refX,ca.refY2=ca.refY,ca.refWidth2=ca.refWidth,ca.refHeight2=ca.refHeight,ca["ref-x"]=ca.refX,ca["ref-y"]=ca.refY,ca["ref-dy"]=ca.refDy,ca["ref-dx"]=ca.refDx,ca["ref-width"]=ca.refWidth,ca["ref-height"]=ca.refHeight,ca["x-alignment"]=ca.xAlignment,ca["y-alignment"]=ca.yAlignment;var ua=ca,ha=s.Model.extend({constructor:function(t,e){var i,n=t||{};"function"==typeof this.preinitialize&&this.preinitialize.apply(this,arguments),this.cid=Lo("c"),this.attributes={},e&&e.collection&&(this.collection=e.collection),e&&e.parse&&(n=this.parse(n,e)||{}),(i=xo(this,"defaults"))&&(n=zo({},i,n)),this.set(n,e),this.changed={},this.initialize.apply(this,arguments)},translate:function(t,e,i){throw new Error("Must define a translate() method.")},toJSON:function(){var e=xo(this.constructor.prototype,"defaults").attrs||{},t=this.attributes.attrs,o={};Do(t,function(t,n){var r=e[n];Do(t,function(t,i){jo(t)&&!Array.isArray(t)?Do(t,function(t,e){r&&r[i]&&ho(r[i][e],t)||(o[n]=o[n]||{},(o[n][i]||(o[n][i]={}))[e]=t)}):r&&ho(r[i],t)||(o[n]=o[n]||{},o[n][i]=t)})});var i=co(Mo(this.attributes,"attrs"));return i.attrs=o,i},initialize:function(t){t&&t.id||this.set("id",this.generateId(),{silent:!0}),this._transitionIds={},this.processPorts(),this.on("change:attrs",this.processPorts,this)},generateId:function(){return Pr()},processPorts:function(){var t=this.ports,i={};Do(this.get("attrs"),function(t,e){t&&t.port&&(void 0!==t.port.id?i[t.port.id]=t.port:i[t.port]={id:t.port})});var n={};(Do(t,function(t,e){i[e]||(n[e]=!0)}),this.graph&&!uo(n))&&(this.graph.getConnectedLinks(this,{inbound:!0}).forEach(function(t){n[t.get("target").port]&&t.remove()}),this.graph.getConnectedLinks(this,{outbound:!0}).forEach(function(t){n[t.get("source").port]&&t.remove()}));this.ports=i},remove:function(t){void 0===t&&(t={});var e=this.graph,i=this.collection;if(!e)return i&&i.remove(this,t),this;e.startBatch("remove");var n=this.getParentCell();n&&n.unembed(this,t);for(var r=this.getEmbeddedCells(),o=0,a=r.length;oc[2]?(a=".3em",r=u,o=0,"start"):lh[2]?(a=".3em",r=-c,o=0,"end"):l','','','','','','',''].join(""),toolMarkup:['','','','',"Remove link.","",'','','',"Link options.","",""].join(""),doubleToolMarkup:void 0,vertexMarkup:['','','','',"Remove vertex.","",""].join(""),arrowheadMarkup:['','',""].join(""),defaultLabel:void 0,labelMarkup:void 0,_builtins:{defaultLabel:{markup:[{tagName:"rect",selector:"rect"},{tagName:"text",selector:"text"}],attrs:{text:{fill:"#000000",fontSize:14,textAnchor:"middle",yAlignment:"middle",pointerEvents:"none"},rect:{ref:"text",fill:"#ffffff",rx:3,ry:3,refWidth:1,refHeight:1,refX:0,refY:0}},position:{distance:.5}}},defaults:{type:"link",source:{},target:{}},isLink:function(){return!0},disconnect:function(t){return this.set({source:{x:0,y:0},target:{x:0,y:0}},t)},source:function(t,e,i){return void 0===t?lo(this.get("source")):(r=t instanceof ha?((n=lo(e)||{}).id=t.id,i):fo(t)?(n=t,e):((n=lo(e)||{}).x=t.x,n.y=t.y,i),this.set("source",n,r));var n,r},target:function(t,e,i){return void 0===t?lo(this.get("target")):(r=t instanceof ha?((n=lo(e)||{}).id=t.id,i):fo(t)?(n=t,e):((n=lo(e)||{}).x=t.x,n.y=t.y,i),this.set("target",n,r));var n,r},router:function(t,e,i){if(void 0===t){var n=this.get("router");return n?"object"==typeof n?lo(n):n:this.get("manhattan")?{name:"orthogonal"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},a=r?e:i;return this.set("router",o,a)},connector:function(t,e,i){if(void 0===t){var n=this.get("connector");return n?"object"==typeof n?lo(n):n:this.get("smooth")?{name:"smooth"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},a=r?e:i;return this.set("connector",o,a)},label:function(t,e,i){var n=this.labels();return(t=isFinite(t)&&null!==t?0|t:0)<0&&(t=n.length+t),arguments.length<=1?this.prop(["labels",t]):this.prop(["labels",t],e,i)},labels:function(t,e){return 0===arguments.length?(t=this.get("labels"),Array.isArray(t)?t.slice():[]):(Array.isArray(t)||(t=[]),this.set("labels",t,e))},hasLabels:function(){var t=this.attributes.labels;return Array.isArray(t)&&0e[1].x?"right":"left";switch(n){case"left":return new qi(-1,0);case"right":default:return new qi(1,0)}}(t,e);case Ra.VERTICAL:return function(t,e,i){var n,r=t.sourceBBox;n=r.width&&r.height?r.sideNearestToPoint(e[0]):r.y>e[1].y?"bottom":"top";switch(n){case"top":return new qi(0,-1);case"bottom":default:return new qi(0,1)}}(t,e);case Ra.CLOSEST_POINT:return Va(t,e,n);case Ra.OUTWARDS:return Ha(t,e,n);case Ra.AUTO:default:return Ua(t,e,n)}}(r,h,a,l),d=h[0].distance(h[1])*l.coeff,f=Qa(g,h[1].difference(h[0]).normalize());if(f>Math.PI/4){var p=d+(f-Math.PI/4)*l.angleTangentCoefficient;c=g.clone().scale(p,p)}else c=g.clone().scale(d,d)}if(l.targetTangent)u=l.targetTangent;else{var m=function(t,e,i,n){if(n.targetDirection)switch(n.targetDirection){case Ya.UP:return new qi(0,-1);case Ya.DOWN:return new qi(0,1);case Ya.LEFT:return new qi(-1,0);case Ya.RIGHT:return new qi(0,1);case Ya.AUTO:return Fa(t,e,n);case Ya.CLOSEST_POINT:return Xa(t,e,n);case Ya.OUTWARDS:return Ja(t,e,n);default:return n.targetDirection}switch(i){case Ra.HORIZONTAL:return function(t,e,i){var n,r=t.targetBBox;n=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.x>e[e.length-2].x?"left":"right";switch(n){case"left":return new qi(-1,0);case"right":default:return new qi(1,0)}}(t,e);case Ra.VERTICAL:return function(t,e,i){var n,r=t.targetBBox;n=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.y>e[e.length-2].y?"top":"bottom";switch(n){case"top":return new qi(0,-1);case"bottom":default:return new qi(0,1)}}(t,e);case Ra.CLOSEST_POINT:return Xa(t,e,n);case Ra.OUTWARDS:return Ja(t,e,n);case Ra.AUTO:default:return Fa(t,e,n)}}(r,h,a,l),I=h.length-1,v=h[I-1].distance(h[I])*l.coeff,A=Qa(m,h[I-1].difference(h[I]).normalize());if(A>Math.PI/4){var C=v+(A-Math.PI/4)*l.angleTangentCoefficient;u=m.clone().scale(C,C)}else u=m.clone().scale(v,v)}var w=function(t,e,i,n){for(var r=n.tau,o=n.coeff,a=[],s=[],l=[],c=t.length-1,u=0;u=Math.abs(t.y-e.y)){var c=(t.x+e.x)/2;l=Tn.createSegment("C",c,t.y,c,e.y,e.x,e.y),r.appendSegment(l)}else{var u=(t.y+e.y)/2;l=Tn.createSegment("C",t.x,u,e.x,u,e.x,e.y),r.appendSegment(l)}}return o?r:r.serialize()},curve:Wa},$a={},ts=s.View.extend({options:{},theme:null,themeClassNamePrefix:Nr("theme-"),requireSetThemeOverride:!1,defaultTheme:br.defaultTheme,children:null,childNodes:null,DETACHABLE:!0,UPDATE_PRIORITY:2,FLAG_INSERT:1<<30,FLAG_REMOVE:1<<29,constructor:function(t){this.requireSetThemeOverride=t&&!!t.theme,this.options=io({},this.options,t),s.View.call(this,t)},initialize:function(){($a[this.cid]=this).setTheme(this.options.theme||this.defaultTheme),this.init()},unmount:function(){this.svgElement?this.vel.remove():this.$el.remove()},renderChildren:function(t){if(t||(t=xo(this,"children")),t){var e=this.svgElement,i=yr.namespace[e?"svg":"xhtml"],n=Mr(t,i);(e?this.vel:this.$el).empty().append(n.fragment),this.childNodes=n.selectors}return this},findAttribute:function(t,e){for(var i=e;i&&1===i.nodeType;){var n=i.getAttribute(t);if(n)return n;if(i===this.el)return null;i=i.parentNode}return null},_ensureElement:function(){if(this.el)this.setElement(xo(this,"el"));else{var t=xo(this,"tagName"),e=io({},xo(this,"attributes")),i=io({},xo(this,"style"));this.id&&(e.id=xo(this,"id")),this.setElement(this._createElement(t)),this._setAttributes(e),this._setStyle(i)}this._ensureElClassName()},_setAttributes:function(t){this.svgElement?this.vel.attr(t):this.$el.attr(t)},_setStyle:function(t){this.$el.css(t)},_createElement:function(t){return this.svgElement?document.createElementNS(yr.namespace.svg,t):document.createElement(t)},_setElement:function(t){this.$el=t instanceof s.$?t:s.$(t),this.el=this.$el[0],this.svgElement&&(this.vel=yr(this.el))},_ensureElClassName:function(){var t=xo(this,"className");if(t){var e=Nr(t);this.svgElement?this.vel.removeClass(t).addClass(e):this.$el.removeClass(t).addClass(e)}},init:function(){},onRender:function(){},confirmUpdate:function(){return 0},setTheme:function(t,e){return e=e||{},this.theme&&this.requireSetThemeOverride&&!e.override||(this.removeThemeClassName(),this.addThemeClassName(t),this.onSetTheme(this.theme,t),this.theme=t),this},addThemeClassName:function(t){if(!(t=t||this.theme))return this;var e=this.themeClassNamePrefix+t;return this.svgElement?this.vel.addClass(e):this.$el.addClass(e),this},removeThemeClassName:function(t){t=t||this.theme;var e=this.themeClassNamePrefix+t;return this.svgElement?this.vel.removeClass(e):this.$el.removeClass(e),this},onSetTheme:function(t,e){},remove:function(){return this.onRemove(),this.undelegateDocumentEvents(),$a[this.cid]=null,s.View.prototype.remove.apply(this,arguments),this},onRemove:function(){},getEventNamespace:function(){return".joint-event-ns-"+this.cid},delegateElementEvents:function(t,e,i){if(!e)return this;i||(i={});var n=this.getEventNamespace();for(var r in e){var o=e[r];"function"!=typeof o&&(o=this[o]),o&&S(t).on(r+n,i,o.bind(this))}return this},undelegateElementEvents:function(t){return S(t).off(this.getEventNamespace()),this},delegateDocumentEvents:function(t,e){return t||(t=xo(this,"documentEvents")),this.delegateElementEvents(document,t,e)},undelegateDocumentEvents:function(){return this.undelegateElementEvents(document)},eventData:function(t,e){if(!t)throw new Error("eventData(): event object required.");var i=t.data,n="__"+this.cid+"__";return void 0===e?i&&i[n]||{}:(i||(i=t.data={}),i[n]||(i[n]={}),io(i[n],e),this)},stopPropagation:function(t){return this.eventData(t,{propagationStopped:!0}),this},isPropagationStopped:function(t){return!!this.eventData(t).propagationStopped}},{extend:function(){var t=Array.from(arguments),e=t[0]&&io({},t[0])||{},i=t[1]&&io({},t[1])||{},n=e.render||this.prototype&&this.prototype.render||null;return e.render=function(){return"function"==typeof n&&n.apply(this,arguments),this.render.__render__===n&&this.onRender(),this},e.render.__render__=n,s.View.extend.call(this,e,i)}});if(S.event&&!("dbltap"in S.event.special)){var es=br.doubleTapInterval;S.event.special.dbltap={bindType:"touchend",delegateType:"touchend",handle:function(t){for(var e,i=[],n=arguments.length-1;0e.y?"N":"S":t.y===e.y?t.x>e.x?"W":"E":null}function Ds(t){return new In(t.x,t.y,0,0)}function Ts(t){var e=Xr(t.padding||t.elementPadding||20);return{x:-e.left,y:-e.top,width:e.left+e.right,height:e.top+e.bottom}}function Ls(t,e){return t.sourceBBox.clone().moveAndExpand(Ts(e))}function zs(t,e){return t.targetBBox.clone().moveAndExpand(Ts(e))}function Ps(t,e,i){var n=new qi(t.x,e.y),r=new qi(e.x,t.y),o=ks(t,n),a=ks(t,r),s=Ns[i],l=o===i||o!==s&&(a===s||a!==i)?n:r;return{points:[l],direction:ks(l,e)}}function js(t,e,i){var n=Ms(t,e,i);return{points:[n],direction:ks(n,e)}}function Zs(e,t,i,n){var r,o={},a=[new qi(e.x,t.y),new qi(t.x,e.y)],s=a.filter(function(t){return!i.containsPoint(t)}),l=s.filter(function(t){return ks(t,e)!==n});if(0c.center().distance(t),h=u?e:t,g=u?t:e;return a=Ms(o=r?(o=qi.fromPolar(c.width+c.height,xs[r],h),c.pointNearestToPoint(o).move(o,-1)):c.pointNearestToPoint(h).move(h,1),g,c),o.round().equals(a.round())?(a=qi.fromPolar(c.width+c.height,Pi(o.theta(h))+Math.PI/2,g),s=Ms(o,a=c.pointNearestToPoint(a).move(g,1).round(),c),l.points=u?[a,s,o]:[o,s,a]):l.points=u?[a,o]:[o,a],l.direction=ks(u?o:a,e),l}function Os(t,e,i){var n,r,o,a,s,l=Ls(i,e),c=zs(i,e),u=(r=e,(n=i).sourceAnchor?n.sourceAnchor:Ls(n,r).center()),h=(a=e,(o=i).targetAnchor?o.targetAnchor:zs(o,a).center());l=l.union(Ds(u)),c=c.union(Ds(h)),(t=po(t).map(qi)).unshift(u),t.push(h);for(var g=[],d=0,f=t.length-1;dn.maxAllowedDirectionChange)){var X=_s(O.clone().offset(k.gridOffsetX,k.gridOffsetY),A,l),H=Qs(X);if(!C.isClose(H)&&!i(X)){if(0<=j.indexOf(H))if(!X.equals(d))if(Xs(V,Vs(X,d,P,A,n))>n.maxAllowedDirectionChange)continue;var J=R+k.cost+(W?0:n.penalties[D]);(!C.isOpen(H)||J "+e),i=this.getSelector(t.parentNode,i)}return i},addLinkFromMagnet:function(t,e,i){var n=this.paper,r=n.model,o=n.getDefaultLink(this,t);return o.set({source:this.getLinkEnd(t,e,i,o,"source"),target:{x:e,y:i}}).addTo(r,{async:!1,ui:!0}),o.findView(n)},getLinkEnd:function(t){for(var e,i=[],n=arguments.length-1;0r.options.clickThreshold||this.notify("element:magnet:pointerclick",t,e,i,n)}},{Flags:Gl});io(Bl.prototype,Da);var Ol={RENDER:"RENDER",UPDATE:"UPDATE",TOOLS:"TOOLS",LEGACY_TOOLS:"LEGACY_TOOLS",LABELS:"LABELS",VERTICES:"VERTICES",SOURCE:"SOURCE",TARGET:"TARGET",CONNECTOR:"CONNECTOR"},El=Zl.extend({className:function(){var t=Zl.prototype.className.apply(this).split(" ");return t.push("link"),t.join(" ")},options:{shortLinkLength:105,doubleLinkTools:!1,longLinkLength:155,linkToolsOffset:40,doubleLinkToolsOffset:65,sampleInterval:50},_labelCache:null,_labelSelectors:null,_markerCache:null,_V:null,_dragData:null,metrics:null,decimalsRounding:2,initialize:function(){Zl.prototype.initialize.apply(this,arguments),this._labelCache={},this._labelSelectors={},this._markerCache={},this._V={},this.cleanNodesCache()},presentationAttributes:{markup:[Ol.RENDER],attrs:[Ol.UPDATE],router:[Ol.UPDATE],connector:[Ol.CONNECTOR],smooth:[Ol.UPDATE],manhattan:[Ol.UPDATE],toolMarkup:[Ol.LEGACY_TOOLS],labels:[Ol.LABELS],labelMarkup:[Ol.LABELS],vertices:[Ol.VERTICES,Ol.UPDATE],vertexMarkup:[Ol.VERTICES],source:[Ol.SOURCE,Ol.UPDATE],target:[Ol.TARGET,Ol.UPDATE]},initFlag:[Ol.RENDER,Ol.SOURCE,Ol.TARGET,Ol.TOOLS],UPDATE_PRIORITY:1,confirmUpdate:function(t,e){if(e||(e={}),this.hasFlag(t,Ol.SOURCE)){if(!this.updateEndProperties("source"))return t;t=this.removeFlag(t,Ol.SOURCE)}if(this.hasFlag(t,Ol.TARGET)){if(!this.updateEndProperties("target"))return t;t=this.removeFlag(t,Ol.TARGET)}var i=this.paper,n=this.sourceView,r=this.targetView;if(i&&(n&&!i.isViewMounted(n)||r&&!i.isViewMounted(r)))return t;if(this.hasFlag(t,Ol.RENDER))return this.render(),this.updateHighlighters(!0),this.updateTools(e),t=this.removeFlag(t,[Ol.RENDER,Ol.UPDATE,Ol.VERTICES,Ol.LABELS,Ol.TOOLS,Ol.LEGACY_TOOLS,Ol.CONNECTOR]);var o=!1;this.hasFlag(t,Ol.VERTICES)&&(this.renderVertexMarkers(),t=this.removeFlag(t,Ol.VERTICES));var a=this.model,s=a.attributes,l=this.hasFlag(t,Ol.LABELS),c=this.hasFlag(t,Ol.LEGACY_TOOLS);l&&(this.onLabelsChange(a,s.labels,e),t=this.removeFlag(t,Ol.LABELS),o=!0),c&&(this.renderTools(),t=this.removeFlag(t,Ol.LEGACY_TOOLS));var u=this.hasFlag(t,Ol.UPDATE),h=this.hasFlag(t,Ol.CONNECTOR);return(u||h)&&(u?e.translateBy&&a.isRelationshipEmbeddedIn(e.translateBy)?this.translate(e.tx,e.ty):this.update():(this.updatePath(),this.updateDOM()),this.updateTools(e),t=this.removeFlag(t,[Ol.UPDATE,Ol.TOOLS,Ol.CONNECTOR]),o=!(c=l=!1)),l&&this.updateLabelPositions(),c&&this.updateToolsPosition(),o&&this.updateHighlighters(),this.hasFlag(t,Ol.TOOLS)&&(this.updateTools(e),t=this.removeFlag(t,Ol.TOOLS)),t},requestConnectionUpdate:function(t){this.requestUpdate(this.getFlag(Ol.UPDATE),t)},isLabelsRenderRequired:function(t){void 0===t&&(t={});var e=this.model.previous("labels");if(!e)return!0;if("propertyPathArray"in t&&"propertyValue"in t){var i=t.propertyPathArray||[],n=i.length;if(1=this.options.longLinkLength){var r=this.options.doubleLinkToolsOffset||e;n=this.getPointAtLength(i-r),this._tool2Cache.attr("transform","translate("+n.x+", "+n.y+") "+t),this._tool2Cache.attr("visibility","visible")}else this.options.doubleLinkTools&&this._tool2Cache.attr("visibility","hidden")}return this},updateArrowheadMarkers:function(){if(!this._V.markerArrowheads)return this;if("none"===S.css(this._V.markerArrowheads.node,"display"))return this;var t=this.getConnectionLength()'}),Kl=Bl.extend({presentationAttributes:Bl.addPresentationAttributes({attrs:["SCALE"]}),confirmUpdate:function(){var t=Bl.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"SCALE")&&(this.resize(),t=this.removeFlag(t,"SCALE")),t}}),ql=_l.define("basic.Text",{attrs:{text:{"font-size":18,fill:"#000000"}}},{markup:''}),$l=_l.define("basic.Circle",{size:{width:60,height:60},attrs:{circle:{fill:"#ffffff",stroke:"#000000",r:30,cx:30,cy:30},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),tc=_l.define("basic.Ellipse",{size:{width:60,height:40},attrs:{ellipse:{fill:"#ffffff",stroke:"#000000",rx:30,ry:20,cx:30,cy:20},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),ec=_l.define("basic.Polygon",{size:{width:60,height:40},attrs:{polygon:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),ic=_l.define("basic.Polyline",{size:{width:60,height:40},attrs:{polyline:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),nc=_l.define("basic.Image",{attrs:{text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),rc=_l.define("basic.Path",{size:{width:60,height:60},attrs:{path:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle",ref:"path","ref-x":.5,"ref-dy":10,fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),oc=rc.define("basic.Rhombus",{attrs:{path:{d:"M 30 0 L 60 30 30 60 0 30 z"},text:{"ref-y":.5,"ref-dy":null,"y-alignment":"middle"}}}),ac=Tl.test("svgforeignobject"),sc=_l.define("basic.TextBlock",{attrs:{rect:{fill:"#ffffff",stroke:"#000000",width:80,height:100},text:{fill:"#000000","font-size":14,"font-family":"Arial, helvetica, sans-serif"},".content":{text:"","ref-x":.5,"ref-y":.5,"y-alignment":"middle","x-alignment":"middle"}},content:""},{markup:['','',ac?'
':'',""].join(""),initialize:function(){this.listenTo(this,"change:size",this.updateSize),this.listenTo(this,"change:content",this.updateContent),this.updateSize(this,this.get("size")),this.updateContent(this,this.get("content")),_l.prototype.initialize.apply(this,arguments)},updateSize:function(t,e){this.attr({".fobj":io({},e),div:{style:io({},e)}})},updateContent:function(t,e){ac?this.attr({".content":{html:Wr(e)}}):this.attr({".content":{text:e}})},setForeignObjectSize:function(){this.updateSize.apply(this,arguments)},setDivContent:function(){this.updateContent.apply(this,arguments)}}),lc=Bl.extend({presentationAttributes:ac?Bl.prototype.presentationAttributes:Bl.addPresentationAttributes({content:["CONTENT"],size:["CONTENT"]}),initFlag:["RENDER","CONTENT"],confirmUpdate:function(){var t=Bl.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"CONTENT")&&(this.updateContent(this.model),t=this.removeFlag(t,"CONTENT")),t},update:function(t,e){var i=this.model;if(ac)Bl.prototype.update.call(this,i,e);else{var n=Mo(e||i.get("attrs"),".content");Bl.prototype.update.call(this,i,n),e&&!No(e,".content")||this.updateContent(i,e)}},updateContent:function(t,e){var i=zo({},(e||t.get("attrs"))[".content"]);i=Mo(i,"text");var n=Yr(t.get("content"),t.get("size"),i,{svgDocument:this.paper.svg}),r=Tr({},".content",i,"/");r[".content"].text=n,Bl.prototype.update.call(this,t,r)}}),cc={Generic:_l,Rect:Ql,TextView:Kl,Text:ql,Circle:$l,Ellipse:tc,Polygon:ec,Polyline:ic,Image:nc,Path:rc,Rhombus:oc,TextBlock:sc,TextBlockView:lc},uc=Ta.define("standard.Rectangle",{attrs:{body:{refWidth:"100%",refHeight:"100%",strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"text",selector:"label"}]}),hc=Ta.define("standard.Circle",{attrs:{body:{refCx:"50%",refCy:"50%",refR:"50%",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"circle",selector:"body"},{tagName:"text",selector:"label"}]}),gc=Ta.define("standard.Ellipse",{attrs:{body:{refCx:"50%",refCy:"50%",refRx:"50%",refRy:"50%",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"ellipse",selector:"body"},{tagName:"text",selector:"label"}]}),dc=Ta.define("standard.Path",{attrs:{body:{refD:"M 0 0 L 10 0 10 10 0 10 Z",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"path",selector:"body"},{tagName:"text",selector:"label"}]}),fc=Ta.define("standard.Polygon",{attrs:{body:{refPoints:"0 0 10 0 10 10 0 10",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"polygon",selector:"body"},{tagName:"text",selector:"label"}]}),pc=Ta.define("standard.Polyline",{attrs:{body:{refPoints:"0 0 10 0 10 10 0 10 0 0",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"polyline",selector:"body"},{tagName:"text",selector:"label"}]}),mc=Ta.define("standard.Image",{attrs:{image:{refWidth:"100%",refHeight:"100%"},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"image",selector:"image"},{tagName:"text",selector:"label"}]}),Ic=Ta.define("standard.BorderedImage",{attrs:{border:{refWidth:"100%",refHeight:"100%",stroke:"#333333",strokeWidth:2},background:{refWidth:-1,refHeight:-1,x:.5,y:.5,fill:"#FFFFFF"},image:{refWidth:-1,refHeight:-1,x:.5,y:.5},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"background",attributes:{stroke:"none"}},{tagName:"image",selector:"image"},{tagName:"rect",selector:"border",attributes:{fill:"none"}},{tagName:"text",selector:"label"}]}),vc=Ta.define("standard.EmbeddedImage",{attrs:{body:{refWidth:"100%",refHeight:"100%",stroke:"#333333",fill:"#FFFFFF",strokeWidth:2},image:{refWidth:"30%",refHeight:-20,x:10,y:10,preserveAspectRatio:"xMidYMin"},label:{textVerticalAnchor:"top",textAnchor:"left",refX:"30%",refX2:20,refY:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"image",selector:"image"},{tagName:"text",selector:"label"}]}),Ac=Ta.define("standard.InscribedImage",{attrs:{border:{refRx:"50%",refRy:"50%",refCx:"50%",refCy:"50%",stroke:"#333333",strokeWidth:2},background:{refRx:"50%",refRy:"50%",refCx:"50%",refCy:"50%",fill:"#FFFFFF"},image:{refWidth:"68%",refHeight:"68%",refX:"16%",refY:"16%",preserveAspectRatio:"xMidYMid"},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"ellipse",selector:"background"},{tagName:"image",selector:"image"},{tagName:"ellipse",selector:"border",attributes:{fill:"none"}},{tagName:"text",selector:"label"}]}),Cc=Ta.define("standard.HeaderedRectangle",{attrs:{body:{refWidth:"100%",refHeight:"100%",strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},header:{refWidth:"100%",height:30,strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},headerText:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:15,fontSize:16,fill:"#333333"},bodyText:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",refY2:15,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"rect",selector:"header"},{tagName:"text",selector:"headerText"},{tagName:"text",selector:"bodyText"}]}),wc=Ta.define("standard.Cylinder",{attrs:{body:{lateralArea:10,fill:"#FFFFFF",stroke:"#333333",strokeWidth:2},top:{refCx:"50%",cy:10,refRx:"50%",ry:10,fill:"#FFFFFF",stroke:"#333333",strokeWidth:2},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"100%",refY2:15,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"path",selector:"body"},{tagName:"ellipse",selector:"top"},{tagName:"text",selector:"label"}],topRy:function(t,e){if(void 0===t)return this.attr("body/lateralArea");var i={lateralArea:t},n=Er(t)?{refCy:t,refRy:t,cy:null,ry:null}:{refCy:null,refRy:null,cy:t,ry:t};return this.attr({body:i,top:n},e)}},{attributes:{lateralArea:{set:function(t,e){var i=Er(t);i&&(t=parseFloat(t)/100);var n=e.x,r=e.y,o=e.width,a=e.height,s=o/2,l=i?a*t:t,c=yr.KAPPA,u=c*s,h=c*(i?a*t:t),g=n+o/2,d=n+o,f=r+l,p=f-l,m=r+a-l,I=r+a;return{d:["M",n,f,"L",n,m,"C",n,m+h,g-u,I,g,I,"C",g+u,I,d,m+h,d,m,"L",d,f,"C",d,f-h,g+u,p,g,p,"C",g-u,p,n,f-h,n,f,"Z"].join(" ")}}}}}),yc=Tl.test("svgforeignobject")?{tagName:"foreignObject",selector:"foreignObject",attributes:{overflow:"hidden"},children:[{tagName:"div",namespaceURI:"http://www.w3.org/1999/xhtml",selector:"label",style:{width:"100%",height:"100%",position:"static",backgroundColor:"transparent",textAlign:"center",margin:0,padding:"0px 5px",boxSizing:"border-box",display:"flex",alignItems:"center",justifyContent:"center"}}]}:{tagName:"text",selector:"label",attributes:{"text-anchor":"middle"}},bc={Rectangle:uc,Circle:hc,Ellipse:gc,Path:dc,Polygon:fc,Polyline:pc,Image:mc,BorderedImage:Ic,EmbeddedImage:vc,InscribedImage:Ac,HeaderedRectangle:Cc,Cylinder:wc,TextBlock:Ta.define("standard.TextBlock",{attrs:{body:{refWidth:"100%",refHeight:"100%",stroke:"#333333",fill:"#ffffff",strokeWidth:2},foreignObject:{refWidth:"100%",refHeight:"100%"},label:{style:{fontSize:14}}}},{markup:[{tagName:"rect",selector:"body"},yc]},{attributes:{text:{set:function(t,e,i,n){if(!(i instanceof HTMLElement)){var r=n.style||{},o={text:t,width:-5,height:"100%"},a=io({textVerticalAnchor:"middle"},r);return ua.textWrap.set.call(this,o,e,i,a),{fill:r.color||null}}i.textContent=t},position:function(t,e,i){if(i instanceof SVGElement)return e.center()}}}}),Link:Ma.define("standard.Link",{attrs:{line:{connection:!0,stroke:"#333333",strokeWidth:2,strokeLinejoin:"round",targetMarker:{type:"path",d:"M 10 -5 0 0 10 5 z"}},wrapper:{connection:!0,strokeWidth:10,strokeLinejoin:"round"}}},{markup:[{tagName:"path",selector:"wrapper",attributes:{fill:"none",cursor:"pointer",stroke:"transparent","stroke-linecap":"round"}},{tagName:"path",selector:"line",attributes:{fill:"none","pointer-events":"none"}}]}),DoubleLink:Ma.define("standard.DoubleLink",{attrs:{line:{connection:!0,stroke:"#DDDDDD",strokeWidth:4,strokeLinejoin:"round",targetMarker:{type:"path",stroke:"#000000",d:"M 10 -3 10 -10 -2 0 10 10 10 3"}},outline:{connection:!0,stroke:"#000000",strokeWidth:6,strokeLinejoin:"round"}}},{markup:[{tagName:"path",selector:"outline",attributes:{fill:"none"}},{tagName:"path",selector:"line",attributes:{fill:"none"}}]}),ShadowLink:Ma.define("standard.ShadowLink",{attrs:{line:{connection:!0,stroke:"#FF0000",strokeWidth:20,strokeLinejoin:"round",targetMarker:{type:"path",stroke:"none",d:"M 0 -10 -10 0 0 10 z"},sourceMarker:{type:"path",stroke:"none",d:"M -10 -10 0 0 -10 10 0 10 0 -10 z"}},shadow:{connection:!0,refX:3,refY:6,stroke:"#000000",strokeOpacity:.2,strokeWidth:20,strokeLinejoin:"round",targetMarker:{type:"path",d:"M 0 -10 -10 0 0 10 z",stroke:"none"},sourceMarker:{type:"path",stroke:"none",d:"M -10 -10 0 0 -10 10 0 10 0 -10 z"}}}},{markup:[{tagName:"path",selector:"shadow",attributes:{fill:"none"}},{tagName:"path",selector:"line",attributes:{fill:"none"}}]})},Nc=_l.define("devs.Model",{inPorts:[],outPorts:[],size:{width:80,height:80},attrs:{".":{magnet:!1},".label":{text:"Model","ref-x":.5,"ref-y":10,"font-size":18,"text-anchor":"middle",fill:"#000"},".body":{"ref-width":"100%","ref-height":"100%",stroke:"#000"}},ports:{groups:{in:{position:{name:"left"},attrs:{".port-label":{fill:"#000"},".port-body":{fill:"#fff",stroke:"#000",r:10,magnet:!0}},label:{position:{name:"left",args:{y:10}}}},out:{position:{name:"right"},attrs:{".port-label":{fill:"#000"},".port-body":{fill:"#fff",stroke:"#000",r:10,magnet:!0}},label:{position:{name:"right",args:{y:10}}}}}}},{markup:'',portMarkup:'',portLabelMarkup:'',initialize:function(){_l.prototype.initialize.apply(this,arguments),this.on("change:inPorts change:outPorts",this.updatePortItems,this),this.updatePortItems()},updatePortItems:function(t,e,i){var n=so(this.get("inPorts")),r=wo(so(this.get("outPorts")),n),o=this.createPortItems("in",n),a=this.createPortItems("out",r);this.prop("ports/items",o.concat(a),io({rewrite:!0},i))},createPortItem:function(t,e){return{id:e,group:t,attrs:{".port-label":{text:e}}}},createPortItems:function(t,e){return po(e).map(this.createPortItem.bind(this,t))},_addGroupPort:function(t,e,i){var n=this.get(e);return this.set(e,Array.isArray(n)?n.concat(t):[t],i)},addOutPort:function(t,e){return this._addGroupPort(t,"outPorts",e)},addInPort:function(t,e){return this._addGroupPort(t,"inPorts",e)},_removeGroupPort:function(t,e,i){return this.set(e,Co(this.get(e),t),i)},removeOutPort:function(t,e){return this._removeGroupPort(t,"outPorts",e)},removeInPort:function(t,e){return this._removeGroupPort(t,"inPorts",e)},_changeGroup:function(t,e,i){return this.prop("ports/groups/"+t,jo(e)?e:{},i)},changeInGroup:function(t,e){return this._changeGroup("in",t,e)},changeOutGroup:function(t,e){return this._changeGroup("out",t,e)}}),xc=Nc.define("devs.Atomic",{size:{width:80,height:80},attrs:{".label":{text:"Atomic"}}}),Mc=Nc.define("devs.Coupled",{size:{width:200,height:300},attrs:{".label":{text:"Coupled"}}}),Sc={Model:Nc,Atomic:xc,Coupled:Mc,Link:Ma.define("devs.Link",{attrs:{".connection":{"stroke-width":2}}})},kc=_l.define("logic.Gate",{size:{width:80,height:40},attrs:{".":{magnet:!1},".body":{width:100,height:50},circle:{r:7,stroke:"black",fill:"transparent","stroke-width":2}}},{operation:function(){return!0}}),Dc=kc.define("logic.IO",{size:{width:60,height:30},attrs:{".body":{fill:"white",stroke:"black","stroke-width":2},".wire":{ref:".body","ref-y":.5,stroke:"black"},text:{fill:"black",ref:".body","ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle","font-weight":"bold","font-variant":"small-caps","text-transform":"capitalize","font-size":"14px"}}},{markup:''}),Tc=Dc.define("logic.Input",{attrs:{".wire":{"ref-dx":0,d:"M 0 0 L 23 0"},circle:{ref:".body","ref-dx":30,"ref-y":.5,magnet:!0,class:"output",port:"out"},text:{text:"input"}}}),Lc=Dc.define("logic.Output",{attrs:{".wire":{"ref-x":0,d:"M 0 0 L -23 0"},circle:{ref:".body","ref-x":-30,"ref-y":.5,magnet:"passive",class:"input",port:"in"},text:{text:"output"}}}),zc=kc.define("logic.Gate11",{attrs:{".input":{ref:".body","ref-x":-2,"ref-y":.5,magnet:"passive",port:"in"},".output":{ref:".body","ref-dx":2,"ref-y":.5,magnet:!0,port:"out"}}},{markup:''}),Pc=kc.define("logic.Gate21",{attrs:{".input1":{ref:".body","ref-x":-2,"ref-y":.3,magnet:"passive",port:"in1"},".input2":{ref:".body","ref-x":-2,"ref-y":.7,magnet:"passive",port:"in2"},".output":{ref:".body","ref-dx":2,"ref-y":.5,magnet:!0,port:"out"}}},{markup:''}),jc=zc.define("logic.Repeater",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzIuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICA8L2c+Cjwvc3ZnPgo="}}},{operation:function(t){return t}}),Zc=zc.define("logic.Not",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzkuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI2NzEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzYiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA4MCwyNSBBIDQsNCAwIDEgMSA3MiwyNSBBIDQsNCAwIDEgMSA4MCwyNSB6IgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEsMCkiIC8+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t){return!t}}),Gc=Pc.define("logic.Or",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik9SIEFOU0kuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTAgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjcxNCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iMSA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMC41IDogMC4zMzMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODA2IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODE5IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjM3Mi4wNDcyNCA6IDM1MC43ODczOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI3NDQuMDk0NDggOiA1MjYuMTgxMDkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzc3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49Ijc1IDogNDAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTUwIDogNjAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDYwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTMyNzUiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNTAgOiAzMy4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTAwIDogNTAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTU1MzMiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjU1NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDE2LjY2NjY2NyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDI1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAyNSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iNCIKICAgICBpbmtzY2FwZTpjeD0iMTEzLjAwMDM5IgogICAgIGlua3NjYXBlOmN5PSIxMi44OTM3MzEiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImcyNTYwIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1wb2ludHM9InRydWUiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAwMDAiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMzk5IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg3NCIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMzciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii00IgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNzAsMjUgYyAyMCwwIDI1LDAgMjUsMCIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMSwxNSA1LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzIsMzUgNSwzNSIKICAgICAgIGlkPSJwYXRoMzk0NCIgLz4KICAgIDxnCiAgICAgICBpZD0iZzI1NjAiCiAgICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2LjUsLTM5LjUpIj4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo="}}},{operation:function(t,e){return t||e}}),Bc=Pc.define("logic.And",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IkFORCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI4IgogICAgIGlua3NjYXBlOmN4PSI1Ni42OTgzNDgiCiAgICAgaW5rc2NhcGU6Y3k9IjI1LjMyNjg5OSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTptZWRpdW07Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWluZGVudDowO3RleHQtYWxpZ246c3RhcnQ7dGV4dC1kZWNvcmF0aW9uOm5vbmU7bGluZS1oZWlnaHQ6bm9ybWFsO2xldHRlci1zcGFjaW5nOm5vcm1hbDt3b3JkLXNwYWNpbmc6bm9ybWFsO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlyZWN0aW9uOmx0cjtibG9jay1wcm9ncmVzc2lvbjp0Yjt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDozO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGU7Zm9udC1mYW1pbHk6Qml0c3RyZWFtIFZlcmEgU2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkJpdHN0cmVhbSBWZXJhIFNhbnMiCiAgICAgICBkPSJNIDMwLDUgTCAzMCw2LjQyODU3MTQgTCAzMCw0My41NzE0MjkgTCAzMCw0NSBMIDMxLjQyODU3MSw0NSBMIDUwLjQ3NjE5LDQ1IEMgNjEuNzQ0MDk4LDQ1IDcwLjQ3NjE5LDM1Ljk5OTk1NSA3MC40NzYxOSwyNSBDIDcwLjQ3NjE5LDE0LjAwMDA0NSA2MS43NDQwOTksNS4wMDAwMDAyIDUwLjQ3NjE5LDUgQyA1MC40NzYxOSw1IDUwLjQ3NjE5LDUgMzEuNDI4NTcxLDUgTCAzMCw1IHogTSAzMi44NTcxNDMsNy44NTcxNDI5IEMgNDAuODM0MjY0LDcuODU3MTQyOSA0NS45MTgzNjgsNy44NTcxNDI5IDQ4LjA5NTIzOCw3Ljg1NzE0MjkgQyA0OS4yODU3MTQsNy44NTcxNDI5IDQ5Ljg4MDk1Miw3Ljg1NzE0MjkgNTAuMTc4NTcxLDcuODU3MTQyOSBDIDUwLjMyNzM4MSw3Ljg1NzE0MjkgNTAuNDA5MjI3LDcuODU3MTQyOSA1MC40NDY0MjksNy44NTcxNDI5IEMgNTAuNDY1MDI5LDcuODU3MTQyOSA1MC40NzE1NDMsNy44NTcxNDI5IDUwLjQ3NjE5LDcuODU3MTQyOSBDIDYwLjIzNjg1Myw3Ljg1NzE0MyA2Ny4xNDI4NTcsMTUuNDk3MDk4IDY3LjE0Mjg1NywyNSBDIDY3LjE0Mjg1NywzNC41MDI5MDIgNTkuNzYwNjYyLDQyLjE0Mjg1NyA1MCw0Mi4xNDI4NTcgTCAzMi44NTcxNDMsNDIuMTQyODU3IEwgMzIuODU3MTQzLDcuODU3MTQyOSB6IgogICAgICAgaWQ9InBhdGgyODg0IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NzY2NjY3Nzc3NzY2NjIiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return t&&e}}),Oc=Pc.define("logic.Nor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjEiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjY3NzY0NCIKICAgICBpbmtzY2FwZTpjeT0iMjIuMTAyMzQ0IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjM3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItNCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc5LDI1IEMgOTksMjUgOTUsMjUgOTUsMjUiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBpZD0icGF0aDI2MDQiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI3NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjI1IgogICAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgICAgc29kaXBvZGk6cnk9IjQiCiAgICAgICAgIGQ9Ik0gNzksMjUgQSA0LDQgMCAxIDEgNzEsMjUgQSA0LDQgMCAxIDEgNzksMjUgeiIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI2LjUsMzkuNSkiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t,e){return!(t||e)}}),Ec=Pc.define("logic.Nand",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5BTkQgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTYiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjI4MzMwNyIKICAgICBpbmtzY2FwZTpjeT0iMTYuNDQyODQzIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzksMjUgQyA5MS44LDI1IDk1LDI1IDk1LDI1IgogICAgICAgaWQ9InBhdGgzMDU5IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMxLDE1IDUsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMiwzNSA1LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmb250LXNpemU6bWVkaXVtO2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1pbmRlbnQ6MDt0ZXh0LWFsaWduOnN0YXJ0O3RleHQtZGVjb3JhdGlvbjpub25lO2xpbmUtaGVpZ2h0Om5vcm1hbDtsZXR0ZXItc3BhY2luZzpub3JtYWw7d29yZC1zcGFjaW5nOm5vcm1hbDt0ZXh0LXRyYW5zZm9ybTpub25lO2RpcmVjdGlvbjpsdHI7YmxvY2stcHJvZ3Jlc3Npb246dGI7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MzttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO2ZvbnQtZmFtaWx5OkJpdHN0cmVhbSBWZXJhIFNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpCaXRzdHJlYW0gVmVyYSBTYW5zIgogICAgICAgZD0iTSAzMCw1IEwgMzAsNi40Mjg1NzE0IEwgMzAsNDMuNTcxNDI5IEwgMzAsNDUgTCAzMS40Mjg1NzEsNDUgTCA1MC40NzYxOSw0NSBDIDYxLjc0NDA5OCw0NSA3MC40NzYxOSwzNS45OTk5NTUgNzAuNDc2MTksMjUgQyA3MC40NzYxOSwxNC4wMDAwNDUgNjEuNzQ0MDk5LDUuMDAwMDAwMiA1MC40NzYxOSw1IEMgNTAuNDc2MTksNSA1MC40NzYxOSw1IDMxLjQyODU3MSw1IEwgMzAsNSB6IE0gMzIuODU3MTQzLDcuODU3MTQyOSBDIDQwLjgzNDI2NCw3Ljg1NzE0MjkgNDUuOTE4MzY4LDcuODU3MTQyOSA0OC4wOTUyMzgsNy44NTcxNDI5IEMgNDkuMjg1NzE0LDcuODU3MTQyOSA0OS44ODA5NTIsNy44NTcxNDI5IDUwLjE3ODU3MSw3Ljg1NzE0MjkgQyA1MC4zMjczODEsNy44NTcxNDI5IDUwLjQwOTIyNyw3Ljg1NzE0MjkgNTAuNDQ2NDI5LDcuODU3MTQyOSBDIDUwLjQ2NTAyOSw3Ljg1NzE0MjkgNTAuNDcxNTQzLDcuODU3MTQyOSA1MC40NzYxOSw3Ljg1NzE0MjkgQyA2MC4yMzY4NTMsNy44NTcxNDMgNjcuMTQyODU3LDE1LjQ5NzA5OCA2Ny4xNDI4NTcsMjUgQyA2Ny4xNDI4NTcsMzQuNTAyOTAyIDU5Ljc2MDY2Miw0Mi4xNDI4NTcgNTAsNDIuMTQyODU3IEwgMzIuODU3MTQzLDQyLjE0Mjg1NyBMIDMyLjg1NzE0Myw3Ljg1NzE0MjkgeiIKICAgICAgIGlkPSJwYXRoMjg4NCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2Njc2NjY2Nzc3Nzc2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDQwMDgiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return!(t&&e)}}),Rc=Pc.define("logic.Xor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhPUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iMjUuOTM4MTE2IgogICAgIGlua3NjYXBlOmN5PSIxNy4yMzAwNSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzAuMzg1NzE3LDE1IEwgNC45OTk5OTk4LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5NzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEuMzYyMDkxLDM1IEwgNC45OTk5OTk4LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPGcKICAgICAgIGlkPSJnMjU2MCIKICAgICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYuNSwtMzkuNSkiPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDM1MTYiCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi4yNSw4MS41MDAwMDUgQyAtMy44NDczNzQsODQuMTQ0NDA1IC00LjUsODQuNTAwMDA1IC00LjUsODQuNTAwMDA1IEwgLTguMTU2MjUsODQuNTAwMDA1IEwgLTYuMTU2MjUsODIuMDYyNTA1IEMgLTYuMTU2MjUsODIuMDYyNTA1IC0wLjUsNzUuMDYyNDUxIC0wLjUsNjQuNSBDIC0wLjUsNTMuOTM3NTQ5IC02LjE1NjI1LDQ2LjkzNzUgLTYuMTU2MjUsNDYuOTM3NSBMIC04LjE1NjI1LDQ0LjUgTCAtNC41LDQ0LjUgQyAtMy43MTg3NSw0NS40Mzc1IC0zLjA3ODEyNSw0Ni4xNTYyNSAtMi4yODEyNSw0Ny41IEMgLTAuNDA4NTMxLDUwLjU5OTgxNSAyLjUsNTYuNTI2NjQ2IDIuNSw2NC41IEMgMi41LDcyLjQ1MDY1IC0wLjM5NjY5Nyw3OC4zNzk0MjUgLTIuMjUsODEuNTAwMDA1IHoiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY3NjY2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t,e){return(!t||e)&&(t||!e)}}),Yc=Pc.define("logic.Xnor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhOT1IgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNTU3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTYuNjY2NjY3IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDI1IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0IgogICAgIGlua3NjYXBlOmN4PSI5NS43MjM2NiIKICAgICBpbmtzY2FwZTpjeT0iLTI2Ljc3NTAyMyIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyLjAwMDAwMDI0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc4LjMzMzMzMiwyNSBDIDkxLjY2NjY2NiwyNSA5NSwyNSA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMC4zODU3MTcsMTUgTCA0Ljk5OTk5OTgsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk3NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMS4zNjIwOTEsMzUgTCA0Ljk5OTk5OTgsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMzUxNiIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjI1LDgxLjUwMDAwNSBDIC0zLjg0NzM3NCw4NC4xNDQ0MDUgLTQuNSw4NC41MDAwMDUgLTQuNSw4NC41MDAwMDUgTCAtOC4xNTYyNSw4NC41MDAwMDUgTCAtNi4xNTYyNSw4Mi4wNjI1MDUgQyAtNi4xNTYyNSw4Mi4wNjI1MDUgLTAuNSw3NS4wNjI0NTEgLTAuNSw2NC41IEMgLTAuNSw1My45Mzc1NDkgLTYuMTU2MjUsNDYuOTM3NSAtNi4xNTYyNSw0Ni45Mzc1IEwgLTguMTU2MjUsNDQuNSBMIC00LjUsNDQuNSBDIC0zLjcxODc1LDQ1LjQzNzUgLTMuMDc4MTI1LDQ2LjE1NjI1IC0yLjI4MTI1LDQ3LjUgQyAtMC40MDg1MzEsNTAuNTk5ODE1IDIuNSw1Ni41MjY2NDYgMi41LDY0LjUgQyAyLjUsNzIuNDUwNjUgLTAuMzk2Njk3LDc4LjM3OTQyNSAtMi4yNSw4MS41MDAwMDUgeiIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Njc2NjY2NzYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDM1NTEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return(!t||!e)&&(t||e)}}),Wc={Gate:kc,IO:Dc,Input:Tc,Output:Lc,Gate11:zc,Gate21:Pc,Repeater:jc,Not:Zc,Or:Gc,And:Bc,Nor:Oc,Nand:Ec,Xor:Rc,Xnor:Yc,Wire:Ma.define("logic.Wire",{attrs:{".connection":{"stroke-width":2},".marker-vertex":{r:7}},router:{name:"orthogonal"},connector:{name:"rounded",args:{radius:10}}},{arrowheadMarkup:['','',""].join(""),vertexMarkup:['','','','','',"Remove vertex.","","",""].join("")})},Uc={KingWhite:_l.define("chess.KingWhite",{size:{width:42,height:38}},{markup:' '}),KingBlack:_l.define("chess.KingBlack",{size:{width:42,height:38}},{markup:' '}),QueenWhite:_l.define("chess.QueenWhite",{size:{width:42,height:38}},{markup:' '}),QueenBlack:_l.define("chess.QueenBlack",{size:{width:42,height:38}},{markup:' '}),RookWhite:_l.define("chess.RookWhite",{size:{width:32,height:34}},{markup:' '}),RookBlack:_l.define("chess.RookBlack",{size:{width:32,height:34}},{markup:' '}),BishopWhite:_l.define("chess.BishopWhite",{size:{width:38,height:38}},{markup:' '}),BishopBlack:_l.define("chess.BishopBlack",{size:{width:38,height:38}},{markup:' '}),KnightWhite:_l.define("chess.KnightWhite",{size:{width:38,height:37}},{markup:' '}),KnightBlack:_l.define("chess.KnightBlack",{size:{width:38,height:37}},{markup:' '}),PawnWhite:_l.define("chess.PawnWhite",{size:{width:28,height:33}},{markup:''}),PawnBlack:_l.define("chess.PawnBlack",{size:{width:28,height:33}},{markup:''})},Fc=Ta.define("erd.Entity",{size:{width:150,height:60},attrs:{".outer":{fill:"#2ECC71",stroke:"#27AE60","stroke-width":2,points:"100,0 100,60 0,60 0,0"},".inner":{fill:"#2ECC71",stroke:"#27AE60","stroke-width":2,points:"95,5 95,55 5,55 5,5",display:"none"},text:{text:"Entity","font-family":"Arial","font-size":14,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Vc=Fc.define("erd.WeakEntity",{attrs:{".inner":{display:"auto"},text:{text:"Weak Entity"}}}),Xc=Ta.define("erd.Relationship",{size:{width:80,height:80},attrs:{".outer":{fill:"#3498DB",stroke:"#2980B9","stroke-width":2,points:"40,0 80,40 40,80 0,40"},".inner":{fill:"#3498DB",stroke:"#2980B9","stroke-width":2,points:"40,5 75,40 40,75 5,40",display:"none"},text:{text:"Relationship","font-family":"Arial","font-size":12,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Hc=Xc.define("erd.IdentifyingRelationship",{attrs:{".inner":{display:"auto"},text:{text:"Identifying"}}}),Jc=Ta.define("erd.Attribute",{size:{width:100,height:50},attrs:{ellipse:{transform:"translate(50, 25)"},".outer":{stroke:"#D35400","stroke-width":2,cx:0,cy:0,rx:50,ry:25,fill:"#E67E22"},".inner":{stroke:"#D35400","stroke-width":2,cx:0,cy:0,rx:45,ry:20,fill:"#E67E22",display:"none"},text:{"font-family":"Arial","font-size":14,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),_c=Jc.define("erd.Multivalued",{attrs:{".inner":{display:"block"},text:{text:"multivalued"}}}),Qc=Jc.define("erd.Derived",{attrs:{".outer":{"stroke-dasharray":"3,5"},text:{text:"derived"}}}),Kc=Jc.define("erd.Key",{attrs:{ellipse:{"stroke-width":4},text:{text:"key","font-weight":"800","text-decoration":"underline"}}}),qc=Jc.define("erd.Normal",{attrs:{text:{text:"Normal"}}}),$c={Entity:Fc,WeakEntity:Vc,Relationship:Xc,IdentifyingRelationship:Hc,Attribute:Jc,Multivalued:_c,Derived:Qc,Key:Kc,Normal:qc,ISA:Ta.define("erd.ISA",{type:"erd.ISA",size:{width:100,height:50},attrs:{polygon:{points:"0,0 50,50 100,0",fill:"#F1C40F",stroke:"#F39C12","stroke-width":2},text:{text:"ISA","font-size":18,"ref-x":.5,"ref-y":.3,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Line:Ma.define("erd.Line",{},{cardinality:function(t){this.set("labels",[{position:-20,attrs:{text:{dy:-8,text:t}}}])}})},tu={State:$l.define("fsa.State",{attrs:{circle:{"stroke-width":3},text:{"font-weight":"800"}}}),StartState:Ta.define("fsa.StartState",{size:{width:20,height:20},attrs:{circle:{transform:"translate(10, 10)",r:10,fill:"#000000"}}},{markup:''}),EndState:Ta.define("fsa.EndState",{size:{width:20,height:20},attrs:{".outer":{transform:"translate(10, 10)",r:10,fill:"#ffffff",stroke:"#000000"},".inner":{transform:"translate(10, 10)",r:6,fill:"#000000"}}},{markup:''}),Arrow:Ma.define("fsa.Arrow",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}},smooth:!0})},eu={Member:Ta.define("org.Member",{size:{width:180,height:70},attrs:{rect:{width:170,height:60},".card":{fill:"#FFFFFF",stroke:"#000000","stroke-width":2,"pointer-events":"visiblePainted",rx:10,ry:10},image:{width:48,height:48,ref:".card","ref-x":10,"ref-y":5},".rank":{"text-decoration":"underline",ref:".card","ref-x":.9,"ref-y":.2,"font-family":"Courier New","font-size":14,"text-anchor":"end"},".name":{"font-weight":"800",ref:".card","ref-x":.9,"ref-y":.6,"font-family":"Courier New","font-size":14,"text-anchor":"end"}}},{markup:''}),Arrow:Ma.define("org.Arrow",{source:{selector:".card"},target:{selector:".card"},attrs:{".connection":{stroke:"#585858","stroke-width":3}},z:-1})},iu={Place:_l.define("pn.Place",{size:{width:50,height:50},attrs:{".root":{r:25,fill:"#ffffff",stroke:"#000000",transform:"translate(25, 25)"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:".root",fill:"#000000","font-size":12},".tokens > circle":{fill:"#000000",r:5},".tokens.one > circle":{transform:"translate(25, 25)"},".tokens.two > circle:nth-child(1)":{transform:"translate(19, 25)"},".tokens.two > circle:nth-child(2)":{transform:"translate(31, 25)"},".tokens.three > circle:nth-child(1)":{transform:"translate(18, 29)"},".tokens.three > circle:nth-child(2)":{transform:"translate(25, 19)"},".tokens.three > circle:nth-child(3)":{transform:"translate(32, 29)"},".tokens.alot > text":{transform:"translate(25, 18)","text-anchor":"middle",fill:"#000000"}}},{markup:''}),PlaceView:Bl.extend({presentationAttributes:Bl.addPresentationAttributes({tokens:["TOKENS"]}),initFlag:Bl.prototype.initFlag.concat(["TOKENS"]),confirmUpdate:function(){for(var t,e=[],i=arguments.length;i--;)e[i]=arguments[i];var n=(t=Bl.prototype.confirmUpdate).call.apply(t,[this].concat(e));return this.hasFlag(n,"TOKENS")&&(this.renderTokens(),this.update(),n=this.removeFlag(n,"TOKENS")),n},renderTokens:function(){var e=this.vel.findOne(".tokens").empty();["one","two","three","alot"].forEach(function(t){e.removeClass(t)});var t=this.model.get("tokens");if(t)switch(t){case 1:e.addClass("one"),e.append(yr("circle"));break;case 2:e.addClass("two"),e.append([yr("circle"),yr("circle")]);break;case 3:e.addClass("three"),e.append([yr("circle"),yr("circle"),yr("circle")]);break;default:e.addClass("alot"),e.append(yr("text").text(t+""))}}}),Transition:_l.define("pn.Transition",{size:{width:12,height:50},attrs:{rect:{width:12,height:50,fill:"#000000",stroke:"#000000"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:"rect",fill:"#000000","font-size":12}}},{markup:''}),Link:Ma.define("pn.Link",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}}})},nu=_l.define("uml.Class",{attrs:{rect:{width:200},".uml-class-name-rect":{stroke:"black","stroke-width":2,fill:"#3498db"},".uml-class-attrs-rect":{stroke:"black","stroke-width":2,fill:"#2980b9"},".uml-class-methods-rect":{stroke:"black","stroke-width":2,fill:"#2980b9"},".uml-class-name-text":{ref:".uml-class-name-rect","ref-y":.5,"ref-x":.5,"text-anchor":"middle","y-alignment":"middle","font-weight":"bold",fill:"black","font-size":12,"font-family":"Times New Roman"},".uml-class-attrs-text":{ref:".uml-class-attrs-rect","ref-y":5,"ref-x":5,fill:"black","font-size":12,"font-family":"Times New Roman"},".uml-class-methods-text":{ref:".uml-class-methods-rect","ref-y":5,"ref-x":5,fill:"black","font-size":12,"font-family":"Times New Roman"}},name:[],attributes:[],methods:[]},{markup:['','','',"",'',""].join(""),initialize:function(){this.on("change:name change:attributes change:methods",function(){this.updateRectangles(),this.trigger("uml-update")},this),this.updateRectangles(),_l.prototype.initialize.apply(this,arguments)},getClassName:function(){return this.get("name")},updateRectangles:function(){var n=this.get("attrs"),t=[{type:"name",text:this.getClassName()},{type:"attrs",text:this.get("attributes")},{type:"methods",text:this.get("methods")}],r=0;t.forEach(function(t){var e=Array.isArray(t.text)?t.text:[t.text],i=20*e.length+20;n[".uml-class-"+t.type+"-text"].text=e.join("\n"),n[".uml-class-"+t.type+"-rect"].height=i,n[".uml-class-"+t.type+"-rect"].transform="translate(0,"+r+")",r+=i})}}),ru=Bl.extend({initialize:function(){Bl.prototype.initialize.apply(this,arguments),this.listenTo(this.model,"uml-update",function(){this.update(),this.resize()})}}),ou=nu.define("uml.Abstract",{attrs:{".uml-class-name-rect":{fill:"#e74c3c"},".uml-class-attrs-rect":{fill:"#c0392b"},".uml-class-methods-rect":{fill:"#c0392b"}}},{getClassName:function(){return["<>",this.get("name")]}}),au=ru,su=nu.define("uml.Interface",{attrs:{".uml-class-name-rect":{fill:"#f1c40f"},".uml-class-attrs-rect":{fill:"#f39c12"},".uml-class-methods-rect":{fill:"#f39c12"}}},{getClassName:function(){return["<>",this.get("name")]}}),lu={basic:cc,standard:bc,devs:Sc,logic:Wc,chess:Uc,erd:$c,fsa:tu,org:eu,pn:iu,uml:{Class:nu,ClassView:ru,Abstract:ou,AbstractView:au,Interface:su,InterfaceView:ru,Generalization:Ma.define("uml.Generalization",{attrs:{".marker-target":{d:"M 20 0 L 0 10 L 20 20 z",fill:"white"}}}),Implementation:Ma.define("uml.Implementation",{attrs:{".marker-target":{d:"M 20 0 L 0 10 L 20 20 z",fill:"white"},".connection":{"stroke-dasharray":"3,3"}}}),Aggregation:Ma.define("uml.Aggregation",{attrs:{".marker-target":{d:"M 40 10 L 20 20 L 0 10 L 20 0 z",fill:"white"}}}),Composition:Ma.define("uml.Composition",{attrs:{".marker-target":{d:"M 40 10 L 20 20 L 0 10 L 20 0 z",fill:"black"}}}),Association:Ma.define("uml.Association"),State:_l.define("uml.State",{attrs:{".uml-state-body":{width:200,height:200,rx:10,ry:10,fill:"#ecf0f1",stroke:"#bdc3c7","stroke-width":3},".uml-state-separator":{stroke:"#bdc3c7","stroke-width":2},".uml-state-name":{ref:".uml-state-body","ref-x":.5,"ref-y":5,"text-anchor":"middle",fill:"#000000","font-family":"Courier New","font-size":14},".uml-state-events":{ref:".uml-state-separator","ref-x":5,"ref-y":5,fill:"#000000","font-family":"Courier New","font-size":14}},name:"State",events:[]},{markup:['','','',"",'','','',""].join(""),initialize:function(){this.on({"change:name":this.updateName,"change:events":this.updateEvents,"change:size":this.updatePath},this),this.updateName(),this.updateEvents(),this.updatePath(),_l.prototype.initialize.apply(this,arguments)},updateName:function(){this.attr(".uml-state-name/text",this.get("name"))},updateEvents:function(){this.attr(".uml-state-events/text",this.get("events").join("\n"))},updatePath:function(){var t="M 0 20 L "+this.get("size").width+" 20";this.attr(".uml-state-separator/d",t,{silent:!0})}}),StartState:$l.define("uml.StartState",{type:"uml.StartState",attrs:{circle:{fill:"#34495e",stroke:"#2c3e50","stroke-width":2,rx:1}}}),EndState:_l.define("uml.EndState",{size:{width:20,height:20},attrs:{"circle.outer":{transform:"translate(10, 10)",r:10,fill:"#ffffff",stroke:"#2c3e50"},"circle.inner":{transform:"translate(10, 10)",r:6,fill:"#34495e"}}},{markup:''}),Transition:Ma.define("uml.Transition",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z",fill:"#34495e",stroke:"#2c3e50"},".connection":{stroke:"#2c3e50"}}})}},cu={DirectedGraph:Jl,PortLabel:xa,Port:Aa};return t.V=yr,t.Vectorizer=Ll,t.anchors=gl,t.config=br,t.connectionPoints=ps,t.connectionStrategies=bs,t.connectors=qa,t.dia=Hl,t.elementTools=Dl,t.env=Tl,t.format={},t.g=wr,t.highlighters=cs,t.layout=cu,t.linkAnchors=ul,t.linkTools=kl,t.mvc=is,t.routers=ol,t.setTheme=function(t,e){oo($a,"setTheme",t,e=e||{}),ts.prototype.defaultTheme=t},t.shapes=lu,t.ui={},t.util=fa,t.version="3.5.0",t}({},Backbone,_,$);if(void 0!==joint)var g=joint.g,V=joint.V,Vectorizer=joint.V; \ No newline at end of file +var joint=function(t,s,i,S){"use strict";s=s&&s.hasOwnProperty("default")?s.default:s,i=i&&i.hasOwnProperty("default")?i.default:i,S=S&&S.hasOwnProperty("default")?S.default:S;var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function n(t,e){return t(e={exports:{}},e.exports),e.exports}var r=function(t){return t&&t.Math==Math&&t},u=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof e&&e)||function(){return this}()||Function("return this")(),a=function(t){try{return!!t()}catch(t){return!0}},l=!a(function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}),o={}.propertyIsEnumerable,c=Object.getOwnPropertyDescriptor,h={f:c&&!o.call({1:2},1)?function(t){var e=c(this,t);return!!e&&e.enumerable}:o},C=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},g={}.toString,d=function(t){return g.call(t).slice(8,-1)},f="".split,w=a(function(){return!Object("z").propertyIsEnumerable(0)})?function(t){return"String"==d(t)?f.call(t,""):Object(t)}:Object,p=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},m=function(t){return w(p(t))},I=function(t){return"object"==typeof t?null!==t:"function"==typeof t},v=function(t,e){if(!I(t))return t;var i,n;if(e&&"function"==typeof(i=t.toString)&&!I(n=i.call(t)))return n;if("function"==typeof(i=t.valueOf)&&!I(n=i.call(t)))return n;if(!e&&"function"==typeof(i=t.toString)&&!I(n=i.call(t)))return n;throw TypeError("Can't convert object to primitive value")},A={}.hasOwnProperty,y=function(t,e){return A.call(t,e)},b=u.document,N=I(b)&&I(b.createElement),x=function(t){return N?b.createElement(t):{}},M=!l&&!a(function(){return 7!=Object.defineProperty(x("div"),"a",{get:function(){return 7}}).a}),k=Object.getOwnPropertyDescriptor,D={f:l?k:function(t,e){if(t=m(t),e=v(e,!0),M)try{return k(t,e)}catch(t){}if(y(t,e))return C(!h.f.call(t,e),t[e])}},T=function(t){if(!I(t))throw TypeError(String(t)+" is not an object");return t},L=Object.defineProperty,z={f:l?L:function(t,e,i){if(T(t),e=v(e,!0),T(i),M)try{return L(t,e,i)}catch(t){}if("get"in i||"set"in i)throw TypeError("Accessors not supported");return"value"in i&&(t[e]=i.value),t}},P=l?function(t,e,i){return z.f(t,e,C(1,i))}:function(t,e,i){return t[e]=i,t},j=function(e,i){try{P(u,e,i)}catch(t){u[e]=i}return i},Z="__core-js_shared__",G=u[Z]||j(Z,{}),B=Function.toString;"function"!=typeof G.inspectSource&&(G.inspectSource=function(t){return B.call(t)});var O,E,R,Y=G.inspectSource,W=u.WeakMap,U="function"==typeof W&&/native code/.test(Y(W)),F=n(function(t){(t.exports=function(t,e){return G[t]||(G[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.8.3",mode:"global",copyright:"\xa9 2021 Denis Pushkarev (zloirock.ru)"})}),V=0,X=Math.random(),H=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++V+X).toString(36)},J=F("keys"),_=function(t){return J[t]||(J[t]=H(t))},Q={},K=u.WeakMap;if(U){var q=G.state||(G.state=new K),$=q.get,tt=q.has,et=q.set;O=function(t,e){return e.facade=t,et.call(q,t,e),e},E=function(t){return $.call(q,t)||{}},R=function(t){return tt.call(q,t)}}else{var it=_("state");Q[it]=!0,O=function(t,e){return e.facade=t,P(t,it,e),e},E=function(t){return y(t,it)?t[it]:{}},R=function(t){return y(t,it)}}var nt,rt={set:O,get:E,has:R,enforce:function(t){return R(t)?E(t):O(t,{})},getterFor:function(i){return function(t){var e;if(!I(t)||(e=E(t)).type!==i)throw TypeError("Incompatible receiver, "+i+" required");return e}}},ot=n(function(t){var e=rt.get,l=rt.enforce,c=String(String).split("String");(t.exports=function(t,e,i,n){var r,o=!!n&&!!n.unsafe,a=!!n&&!!n.enumerable,s=!!n&&!!n.noTargetGet;"function"==typeof i&&("string"!=typeof e||y(i,"name")||P(i,"name",e),(r=l(i)).source||(r.source=c.join("string"==typeof e?e:""))),t!==u?(o?!s&&t[e]&&(a=!0):delete t[e],a?t[e]=i:P(t,e,i)):a?t[e]=i:j(e,i)})(Function.prototype,"toString",function(){return"function"==typeof this&&e(this).source||Y(this)})}),at=u,st=function(t){return"function"==typeof t?t:void 0},lt=function(t,e){return arguments.length<2?st(at[t])||st(u[t]):at[t]&&at[t][e]||u[t]&&u[t][e]},ct=Math.ceil,ut=Math.floor,ht=function(t){return isNaN(t=+t)?0:(0r;)y(n,i=e[r++])&&(~vt(o,i)||o.push(i));return o},Ct=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],wt=Ct.concat("length","prototype"),yt={f:Object.getOwnPropertyNames||function(t){return At(t,wt)}},bt={f:Object.getOwnPropertySymbols},Nt=lt("Reflect","ownKeys")||function(t){var e=yt.f(T(t)),i=bt.f;return i?e.concat(i(t)):e},xt=function(t,e){for(var i=Nt(e),n=z.f,r=D.f,o=0;o"+t+""},_t=function(){try{nt=document.domain&&new ActiveXObject("htmlfile")}catch(t){}var t,e;_t=nt?function(t){t.write(Jt("")),t.close();var e=t.parentWindow.Object;return t=null,e}(nt):((e=x("iframe")).style.display="none",Ut.appendChild(e),e.src=String("javascript:"),(t=e.contentWindow.document).open(),t.write(Jt("document.F=Object")),t.close(),t.F);for(var i=Ct.length;i--;)delete _t[Ft][Ct[i]];return _t()};Q[Xt]=!0;var Qt=Object.create||function(t,e){var i;return null!==t?(Ht[Ft]=T(t),i=new Ht,Ht[Ft]=null,i[Xt]=t):i=_t(),void 0===e?i:Wt(i,e)},Kt=Rt("unscopables"),qt=Array.prototype;null==qt[Kt]&&z.f(qt,Kt,{configurable:!0,value:Qt(null)});var $t=function(t){qt[Kt][t]=!0},te=Object.defineProperty,ee={},ie=function(t){throw t},ne=function(t,e){if(y(ee,t))return ee[t];e||(e={});var i=[][t],n=!!y(e,"ACCESSORS")&&e.ACCESSORS,r=y(e,0)?e[0]:ie,o=y(e,1)?e[1]:void 0;return ee[t]=!!i&&!a(function(){if(n&&!l)return!0;var t={length:-1};n?te(t,1,{enumerable:!0,get:ie}):t[1]=1,i.call(t,r,o)})},re=It.includes,oe=ne("indexOf",{ACCESSORS:!0,1:0});jt({target:"Array",proto:!0,forced:!oe},{includes:function(t){return re(this,t,1=i.length?{value:void 0,done:!0}:(t=Xe(i,n),e.index+=t.length,{value:t,done:!1})});var Qe=function(e,t,i,n){try{return n?t(T(i)[0],i[1]):t(i)}catch(t){throw function(t){var e=t.return;if(void 0!==e)T(e.call(t)).value}(e),t}},Ke=Rt("iterator"),qe=Array.prototype,$e=function(t,e,i){var n=v(e);n in t?z.f(t,n,C(0,i)):t[n]=i},ti={};ti[Rt("toStringTag")]="z";var ei="[object z]"===String(ti),ii=Rt("toStringTag"),ni="Arguments"==d(function(){return arguments}()),ri=ei?d:function(t){var e,i,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(i=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),ii))?i:ni?d(e):"Object"==(n=d(e))&&"function"==typeof e.callee?"Arguments":n},oi=Rt("iterator"),ai=Rt("iterator"),si=!1;try{var li=0,ci={next:function(){return{done:!!li++}},return:function(){si=!0}};ci[ai]=function(){return this},Array.from(ci,function(){throw 2})}catch(t){}var ui=!function(t,e){if(!e&&!si)return!1;var i=!1;try{var n={};n[ai]=function(){return{next:function(){return{done:i=!0}}}},t(n)}catch(t){}return i}(function(t){Array.from(t)});jt({target:"Array",stat:!0,forced:ui},{from:function(t){var e,i,n,r,o,a,s,l=ce(t),c="function"==typeof this?this:Array,u=arguments.length,h=1>8-r%1*8)){if(255<(i=n.charCodeAt(r+=.75)))throw new l("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");e=e<<8|i}return a}),t.atob||(t.atob=function(t){var e=String(t).replace(/=+$/,"");if(e.length%4==1)throw new l("'atob' failed: The string to be decoded is not correctly encoded.");for(var i,n,r=0,o=0,a="";n=e.charAt(o++);~n&&(i=r%4?64*i+n:n,r++%4)?a+=String.fromCharCode(255&i>>(-2*r&6)):0)n=s.indexOf(n);return a})}()});jt({target:"Number",stat:!0},{isNaN:function(t){return t!=t}});at.Number.isNaN;var pi=u.isFinite,mi=Number.isFinite||function(t){return"number"==typeof t&&pi(t)};jt({target:"Number",stat:!0},{isFinite:mi});at.Number.isFinite;var Ii=Rt("match"),vi=function(t){if(I(e=t)&&(void 0!==(i=e[Ii])?i:"RegExp"==d(e)))throw TypeError("The method doesn't accept regular expressions");var e,i;return t},Ai=Rt("match"),Ci=function(e){var i=/./;try{"/./"[e](i)}catch(t){try{return i[Ai]=!1,"/./"[e](i)}catch(t){}}return!1};jt({target:"String",proto:!0,forced:!Ci("includes")},{includes:function(t){return!!~String(p(this)).indexOf(vi(t),1n)&&!(new nn(t,i).length()>n)},divideAt:function(t){var e=this.pointAt(t);return[new nn(this.start,e),new nn(e,this.end)]},divideAtLength:function(t){var e=this.pointAtLength(t);return[new nn(this.start,e),new nn(e,this.end)]},equals:function(t){return!!t&&this.start.x===t.start.x&&this.start.y===t.start.y&&this.end.x===t.end.x&&this.end.y===t.end.y},intersect:function(t,e){if(t&&t.intersectionWithLine){var i=t.intersectionWithLine(this,e);return i&&t instanceof nn&&(i=i[0]),i}return null},intersectionWithLine:function(t){var e=new qi(this.end.x-this.start.x,this.end.y-this.start.y),i=new qi(t.end.x-t.start.x,t.end.y-t.start.y),n=e.x*i.y-e.y*i.x,r=new qi(t.start.x-this.start.x,t.start.y-this.start.y),o=r.x*i.y-r.y*i.x,a=r.x*e.y-r.y*e.x;if(0===n||o*n<0||a*n<0)return null;if(0s.x+o/2,h=ns.x?r-30:r+30)-c)/(a*a*(n-l))+l:e=a*a/(r-c)-a*a*(n-l)*((i=r>s.y?n+30:n-30)-l)/(o*o*(r-c))+c,new qi(i,e).theta(t)},toString:function(){return new qi(this.x,this.y).toString()+" "+this.a+" "+this.b}};var cn=ln,un=Math.abs,hn=Math.cos,gn=Math.sin,dn=Math.min,fn=Math.max,pn=Math.round,mn=Math.pow,In=function(t,e,i,n){if(!(this instanceof In))return new In(t,e,i,n);Object(t)===t&&(e=t.y,i=t.width,n=t.height,t=t.x),this.x=void 0===t?0:t,this.y=void 0===e?0:e,this.width=void 0===i?0:i,this.height=void 0===n?0:n};In.fromEllipse=function(t){return t=new ln(t),new In(t.x-t.a,t.y-t.b,2*t.a,2*t.b)},In.fromPointUnion=function(){for(var t=[],e=arguments.length;e--;)t[e]=arguments[e];if(0===t.length)return null;var i,n,r,o,a=new qi;i=n=1/0,r=o=-1/0;for(var s=0;s=this.x&&t.x<=this.x+this.width&&t.y>=this.y&&t.y<=this.y+this.height},containsRect:function(t){var e=new In(this).normalize(),i=new In(t).normalize(),n=e.width,r=e.height,o=i.width,a=i.height;if(!(n&&r&&o&&a))return!1;var s=e.x,l=e.y,c=i.x,u=i.y;return o+=c,n+=s,a+=u,r+=l,s<=c&&o<=n&&l<=u&&a<=r},corner:function(){return new qi(this.x+this.width,this.y+this.height)},equals:function(t){var e=new In(this).normalize(),i=new In(t).normalize();return e.x===i.x&&e.y===i.y&&e.width===i.width&&e.height===i.height},inflate:function(t,e){return void 0===t&&(t=0),void 0===e&&(e=t),this.x-=t,this.y-=e,this.width+=2*t,this.height+=2*e,this},intersect:function(t){var e=this.origin(),i=this.corner(),n=t.origin(),r=t.corner();if(r.x<=e.x||r.y<=e.y||n.x>=i.x||n.y>=i.y)return null;var o=fn(e.x,n.x),a=fn(e.y,n.y);return new In(o,a,dn(i.x,r.x)-o,dn(i.y,r.y)-a)},intersectionWithLine:function(t){var e,i,n=[this.topLine(),this.rightLine(),this.bottomLine(),this.leftLine()],r=[],o=[],a=n.length;for(i=0;iu&&(n=(this.x+this.width-u)/(d.x-u)),d.y>h&&(s=(this.y+this.height-h)/(d.y-h));var f=t.topRight();f.x>u&&(r=(this.x+this.width-u)/(f.x-u)),f.yh&&(c=(this.y+this.height-h)/(p.y-h)),{sx:dn(i,n,r,o),sy:dn(a,s,l,c)}},maxRectUniformScaleToFit:function(t,e){var i=this.maxRectScaleToFit(t,e);return dn(i.sx,i.sy)},moveAndExpand:function(t){return this.x+=t.x||0,this.y+=t.y||0,this.width+=t.width||0,this.height+=t.height||0,this},normalize:function(){var t=this.x,e=this.y,i=this.width,n=this.height;return this.width<0&&(t=this.x+this.width,i=-this.width),this.height<0&&(e=this.y+this.height,n=-this.height),this.x=t,this.y=e,this.width=i,this.height=n,this},offset:function(t,e){return qi.prototype.offset.call(this,t,e)},origin:function(){return new qi(this.x,this.y)},pointNearestToPoint:function(t){if(t=new qi(t),this.containsPoint(t))switch(this.sideNearestToPoint(t)){case"right":return new qi(this.x+this.width,t.y);case"left":return new qi(this.x,t.y);case"bottom":return new qi(t.x,this.y+this.height);case"top":return new qi(t.x,this.y)}return t.adhereToRect(this)},rightLine:function(){return new nn(this.topRight(),this.bottomRight())},rightMiddle:function(){return new qi(this.x+this.width,this.y+this.height/2)},round:function(t){var e=1;if(t)switch(t){case 1:e=10;break;case 2:e=100;break;case 3:e=1e3;break;default:e=mn(10,t)}return this.x=pn(this.x*e)/e,this.y=pn(this.y*e)/e,this.width=pn(this.width*e)/e,this.height=pn(this.height*e)/e,this},scale:function(t,e,i){return i=this.origin().scale(t,e,i),this.x=i.x,this.y=i.y,this.width*=t,this.height*=e,this},sideNearestToPoint:function(t){var e=(t=new qi(t)).x-this.x,i=this.x+this.width-t.x,n=t.y-this.y,r=e,o="left";return in.x&&(n=t[e]);var a=[];for(e=0;eg.y||r>h.y&&r<=g.y){var d=h.x-n>g.x-n?h.x-n:g.x-n;0<=d&&(u.x=n+d,u.y=r,c.start=t,c.end=u,l.intersect(c)&&s++)}o=a}return s%2==1},close:function(){var t=this.start,e=this.end,i=this.points;return t&&e&&!t.equals(e)&&i.push(t.clone()),this},lengthPoints:function(){return this.points},convexHull:function(){return new yn(wn(this.points))},equals:function(t){if(!t)return!1;var e=this.points,i=t.points,n=e.length;if(i.length!==n)return!1;for(var r=0;rr+a&&n.x>r+a||i.xo+s&&n.y>o+s||i.ye.x&&t.ye.y}function Qn(t,e){return hr(t,e,{interior:!1})}function Kn(t,e){return gr(t,e,{interior:!1})}function qn(t,e){return dr(t,e,{interior:!1})}function $n(t,e){return pr(t,e,{interior:!1})}function tr(t,e){return hr(t,e,{interior:!0})}function er(t,e){return gr(t,e,{interior:!0})}function ir(t,e){return dr(t,e,{interior:!0})}function nr(t,e){return pr(t,e,{interior:!0})}function rr(t,e){return mr(t,e,{interior:!0})}function or(t,i,n){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?tr(e,i):Qn(e,i)})}function ar(t,i,n){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?er(e,i):Kn(e,i)})}function sr(t,e,i){return cr(t,Un.fromRect(e),i)}function lr(t,e,i){return fr(t,e,i,{interior:!1})}function cr(t,e,i){return fr(t,e,i,{interior:!0})}function ur(t,i,n,r){return t.getSubpaths().some(function(t){var e=t.toPolylines(n)[0];return"Z"===t.getSegment(-1).type?cr(i,e,r):lr(i,e,r)})}function hr(t,e,i){void 0===i&&(i={});var n,r=i.interior;if(void 0===r&&(r=!1),r){if(t.containsPoint(e.start))return!0;var o=t.start,a=t.end,s=t.points;n=a.equals(o)?s:s.concat([o])}else n=t.points;for(var l=n.length,c=new nn,u=0;u'+t+"";return L.parseXML(e,{async:!1}).documentElement}var i=document.createElementNS(T.svg,"svg");return i.setAttributeNS(T.xmlns,"xmlns:xlink",T.xlink),i.setAttribute("version","1.1"),i},L.createSVGStyle=function(t){return L("style",{type:"text/css"},[L.createCDATASection(t)]).node},L.createCDATASection=function(t){return void 0===t&&(t=""),document.implementation.createDocument(null,"xml",null).createCDATASection(t)},L.idCounter=0,L.uniqueId=function(){return"v-"+ ++L.idCounter},L.toNode=function(t){return L.isV(t)?t.node:t.nodeName&&t||t[0]},L.ensureId=function(t){return(t=L.toNode(t)).id||(t.id=L.uniqueId())},L.sanitizeText=function(t){return(t||"").replace(/ /g,"\xa0")},L.isUndefined=function(t){return void 0===t},L.isString=function(t){return"string"==typeof t},L.isObject=function(t){return t&&"object"==typeof t},L.isArray=Array.isArray,L.parseXML=function(t,e){var i;e=e||{};try{var n=new DOMParser;L.isUndefined(e.async)||(n.async=e.async),i=n.parseFromString(t,"text/xml")}catch(t){i=void 0}if(!i||i.getElementsByTagName("parsererror").length)throw new Error("Invalid XML: "+t);return i},L.qualifyAttr=function(t){if(-1===t.indexOf(":"))return{ns:null,local:t};var e=t.split(":");return{ns:T[e[0]],local:e[1]}},L.transformRegex=/(\w+)\(([^,)]+),?([^)]+)?\)/gi,L.transformSeparatorRegex=/[ ,]+/,L.transformationListRegex=/^(\w+)\((.*)\)/,L.transformStringToMatrix=function(t){var e=L.createSVGMatrix(),i=t&&t.match(L.transformRegex);if(!i)return e;for(var n=0,r=i.length;n=t.start&&et.start&&i<=t.end||t.start>=e&&t.end=e?t.end+=i:t.start>=e&&(t.start+=i,t.end+=i)}),t},L.convertLineToPathData=function(t){return["M",(t=L(t)).attr("x1"),t.attr("y1"),"L",t.attr("x2"),t.attr("y2")].join(" ")},L.convertPolygonToPathData=function(t){var e=L.getPointsFromSvgNode(t);return 0===e.length?null:L.svgPointsToPath(e)+" Z"},L.convertPolylineToPathData=function(t){var e=L.getPointsFromSvgNode(t);return 0===e.length?null:L.svgPointsToPath(e)},L.svgPointsToPath=function(t){for(var e=0,i=t.length;e=o[r]&&(a.push([e].concat(n.splice(0,o[r]))),o[r]););}),a}(t)),!t||!t.length)return[["M",0,0]];for(var e,i=[],n=0,r=0,o=0,a=0,s=t.length,l=0;lg){var M=N,S=l,k=c;f=t(l=w+n*V(N=b+g*(s&&bd)T=d-1;else if(void 0!==o){var L;if(void 0===m)m=(L="auto"===i.lineHeight?{value:1.5,unit:"em"}:Rr(i.lineHeight,["em"])||{value:1,unit:"em"}).value,"em"===L.unit&&(m*=l.getBBox().height);m*A.length>o&&(T=Math.floor(o/m)-1)}if(null!==T){A.splice(T+1);var z=n.ellipsis;if(!z||T<0)break;"string"!=typeof z&&(z="\u2026");var P=A[T];if(!P&&!N)break;var j,Z,G=P.length;do{if(Z=P[G],j=P.substring(0,G),Z?Z.match(u)&&(j+=Z):j+="string"==typeof u?u:" ",j+=z,c.data=j,s.getComputedTextLength()<=r){A[T]=j;break}G--}while(0<=G);break}}}return n.svgDocument?a.removeChild(l):document.body.removeChild(a),A.join(h)},Wr=function(t){var e=S(S.parseHTML("
"+t+"
",null,!1));return e.find("*").each(function(){var i=this;S.each(i.attributes,function(){var t=this.name,e=this.value;(t.startsWith("on")||e.startsWith("javascript:")||e.startsWith("data:")||e.startsWith("vbscript:"))&&S(i).removeAttr(t)})}),e.html()},Ur=function(t,e){if(window.navigator.msSaveBlob)window.navigator.msSaveBlob(t,e);else{var i=window.URL.createObjectURL(t),n=document.createElement("a");n.href=i,n.download=e,document.body.appendChild(n),n.click(),document.body.removeChild(n),window.URL.revokeObjectURL(i)}},Fr=function(t){t=t.replace(/\s/g,"");var e,i=(t=decodeURIComponent(t)).indexOf(","),n=t.slice(0,i),r=n.split(":")[1].split(";")[0],o=t.slice(i+1);e=0<=n.indexOf("base64")?atob(o):unescape(encodeURIComponent(o));for(var a=new Uint8Array(e.length),s=0;s')({color:t.color||"blue",opacity:Number.isFinite(t.opacity)?t.opacity:1,outerRadius:e+i,innerRadius:e})},highlight:function(t){return Kr('')({color:t.color||"red",width:Number.isFinite(t.width)?t.width:1,blur:Number.isFinite(t.blur)?t.blur:0,opacity:Number.isFinite(t.opacity)?t.opacity:1})},blur:function(t){var e=Number.isFinite(t.x)?t.x:2;return Kr('')({stdDeviation:Number.isFinite(t.y)?[e,t.y]:e})},dropShadow:function(t){var e="SVGFEDropShadowElement"in window?'':'';return Kr(e)({dx:t.dx||0,dy:t.dy||0,opacity:Number.isFinite(t.opacity)?t.opacity:1,color:t.color||"black",blur:Number.isFinite(t.blur)?t.blur:4})},grayscale:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({a:.2126+.7874*(1-e),b:.7152-.7152*(1-e),c:.0722-.0722*(1-e),d:.2126-.2126*(1-e),e:.7152+.2848*(1-e),f:.0722-.0722*(1-e),g:.2126-.2126*(1-e),h:.0722+.9278*(1-e)})},sepia:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({a:.393+.607*(1-e),b:.769-.769*(1-e),c:.189-.189*(1-e),d:.349-.349*(1-e),e:.686+.314*(1-e),f:.168-.168*(1-e),g:.272-.272*(1-e),h:.534-.534*(1-e),i:.131+.869*(1-e)})},saturate:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:1-e})},hueRotate:function(t){return Kr('')({angle:t.angle||0})},invert:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:e,amount2:1-e})},brightness:function(t){return Kr('')({amount:Number.isFinite(t.amount)?t.amount:1})},contrast:function(t){var e=Number.isFinite(t.amount)?t.amount:1;return Kr('')({amount:e,amount2:.5-e/2})}},Qr={number:function(t,e,o){o=o||{currency:["$",""],decimal:".",thousands:",",grouping:[3]};var i=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i.exec(t),n=i[1]||" ",r=i[2]||">",a=i[3]||"",s=i[4]||"",l=i[5],c=+i[6],u=i[7],h=i[8],g=i[9],d=1,f="",p="",m=!1;switch(h&&(h=+h.substring(1)),(l||"0"===n&&"="===r)&&(l=n="0",r="=",u&&(c-=Math.floor((c-1)/4))),g){case"n":u=!0,g="g";break;case"%":d=100,p="%",g="f";break;case"p":d=100,p="%",g="r";break;case"b":case"o":case"x":case"X":"#"===s&&(f="0"+g.toLowerCase());break;case"c":case"d":m=!0,h=0;break;case"s":d=-1,g="r"}"$"===s&&(f=o.currency[0],p=o.currency[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):"e"!=g&&"f"!=g||(h=Math.max(0,Math.min(20,h))));var I=l&&u;if(m&&e%1)return"";var v=e<0||0===e&&1/e<0?(e=-e,"-"):a,A=p;if(d<0){var C=this.prefix(e,h);e=C.scale(e),A=C.symbol+p}else e*=d;var w=(e=this.convert(g,e,h)).lastIndexOf("."),y=w<0?e:e.substring(0,w),b=w<0?"":o.decimal+e.substring(w+1);function N(t){for(var e=t.length,i=[],n=0,r=o.grouping[0];0"===r?M+v+e:"^"===r?M.substring(0,x>>=1)+v+e+M.substring(x):v+(I?e:M+e))+A},string:function(t,e){for(var i,n="{",r=!1,o=[];-1!==(i=t.indexOf(n));){var a,s,l;if(a=t.slice(0,i),r){l=(s=a.split(":")).shift().split("."),a=e;for(var c=0;c|\$\{ ?([^{} ]+) ?\}|\{\{([^{} ]+)\}\}/g;return function(n){return n=n||{},t.replace(e,function(t){for(var e=Array.from(arguments).slice(1,4).find(function(t){return!!t}).split("."),i=n[e.shift()];void 0!==i&&e.length;)i=i[e.shift()];return void 0!==i?i:""})}},qr=i.assign,$r=i.defaults,to=qr,eo=i.defaultsDeep,io=i.assign,no=i.defaults,ro=i.defaultsDeep,oo=i.invokeMap||i.invoke,ao=i.sortedIndexBy||i.sortedIndex,so=i.uniqBy||i.uniq,lo=i.clone,co=i.cloneDeep,uo=i.isEmpty,ho=i.isEqual,go=i.isFunction,fo=i.isPlainObject,po=i.toArray,mo=i.debounce,Io=i.groupBy,vo=i.sortBy,Ao=i.flattenDeep,Co=i.without,wo=i.difference,yo=i.intersection,bo=i.union,No=i.has,xo=i.result,Mo=i.omit,So=i.pick,ko=i.bindAll,Do=i.forIn,To=i.camelCase,Lo=i.uniqueId,zo=function(){if(i.mergeWith){var t=Array.from(arguments),e=t[t.length-1],n=go(e)?e:Bo;return t.push(function(t,e){var i=n(t,e);return void 0!==i?i:Array.isArray(t)&&!Array.isArray(e)?e:void 0}),i.mergeWith.apply(this,t)}return i.merge.apply(this,arguments)},Po=function(t){var e=Object.prototype.toString;return!0===t||!1===t||!!t&&"object"==typeof t&&"[object Boolean]"===e.call(t)},jo=function(t){return!!t&&("object"==typeof t||"function"==typeof t)},Zo=function(t){var e=Object.prototype.toString;return"number"==typeof t||!!t&&"object"==typeof t&&"[object Number]"===e.call(t)},Go=function(t){var e=Object.prototype.toString;return"string"==typeof t||!!t&&"object"==typeof t&&"[object String]"===e.call(t)},Bo=function(){};function Oo(t){t=so(t);var a=po(t).reduce(function(t,e){return t[e.id]=e.clone(),t},{});return po(t).forEach(function(t){var e=a[t.id];if(e.isLink()){var i=e.source(),n=e.target();i.id&&a[i.id]&&e.prop("source/id",a[i.id].id),n.id&&a[n.id]&&e.prop("target/id",a[n.id].id)}var r=t.get("parent");r&&a[r]&&e.set("parent",a[r].id);var o=po(t.get("embeds")).reduce(function(t,e){return a[e]&&t.push(a[e].id),t},[]);uo(o)||e.set("embeds",o)}),a}var Eo={x:"x",y:"y",width:"w",height:"h",minimum:"s",maximum:"l",diagonal:"d"},Ro=Object.keys(Eo).map(function(t){return Eo[t]}).join(""),Yo="[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?",Wo=/\s/g,Uo=new RegExp("^("+Yo+"\\*)?(["+Ro+"])([-+]{1,2}"+Yo+")?$","g");function Fo(t){throw new Error("Invalid calc() expression: "+t)}function Vo(t,e){var i=Uo.exec(t.replace(Wo,""));i||Fo(t),Uo.lastIndex=0;var n=i[1];void 0===n&&(n=1);var r=i[2],o=i[3];void 0===o&&(o=0);var a=e.x,s=e.y,l=e.width,c=e.height,u=0;switch(r){case Eo.width:u=l;break;case Eo.height:u=c;break;case Eo.x:u=a;break;case Eo.y:u=s;break;case Eo.minimum:u=Math.min(c,l);break;case Eo.maximum:u=Math.max(c,l);break;case Eo.diagonal:u=Math.sqrt(c*c+l*l)}return parseFloat(n)*u+function(t){if(!t)return 0;switch(t[0]){case"+":return parseFloat(t.substr(1));case"-":return-parseFloat(t.substr(1))}return parseFloat(t)}(o)}function Xo(t){return"string"==typeof t&&t.includes("calc")}var Ho="calc(",Jo=Ho.length;function _o(t,e){for(var i=t,n=0;;){var r=i.indexOf(Ho,n);if(-1===r)return i;var o=r+Jo,a=1;t:for(;;){switch(i[o]){case"(":a++;break;case")":if(0===--a)break t;break;case void 0:Fo(i)}o++}var s=i.slice(r+Jo,o);Xo(s)&&(s=_o(s,e));var l=String(Vo(s,e));i=i.slice(0,r)+l+i.slice(o+1),n=r+l.length}}function Qo(o,a){return function(t,e){var i=Er(t);t=parseFloat(t),i&&(t/=100);var n={};if(isFinite(t)){var r=i||0<=t&&t<=1?t*e[a]:Math.max(t+e[a],0);n[o]=r}return n}}function Ko(a,s,l){return function(t,e){var i,n=Er(t);if(t=parseFloat(t),n&&(t/=100),isFinite(t)){var r=e[l]();i=n||0e.width?sa:la)(t,e)})},refRCircumscribed:{set:function(t,e){var i=Er(t);t=parseFloat(t),i&&(t/=100);var n,r=Math.sqrt(e.height*e.height+e.width*e.width);return isFinite(t)&&(n=i||0<=t&&t<=1?t*r:Math.max(t+r,0)),{r:n}}},refCx:{set:Qo("cx","width")},refCy:{set:Qo("cy","height")},xAlignment:{offset:qo("x","width","right")},yAlignment:{offset:qo("y","height","bottom")},resetOffset:{offset:function(t,e){return t?{x:-e.x,y:-e.y}:{x:0,y:0}}},refDResetOffset:{set:ta({resetOffset:!0})},refDKeepOffset:{set:ta({resetOffset:!1})},refPointsResetOffset:{set:ea({resetOffset:!0})},refPointsKeepOffset:{set:ea({resetOffset:!1})},connection:{qualify:ra,set:function(t){var e,i=t.stubs;if(void 0===i&&(i=0),isFinite(i)&&0!==i){var n;n=i<0?(this.getConnectionLength()+i)/2:i;var r=this.getConnection(),o=r.divideAtLength(n),a=r.divideAtLength(-n);o&&a&&(e=o[0].serialize()+" "+a[1].serialize())}return{d:e||this.getSerializedConnection()}}},atConnectionLengthKeepGradient:{qualify:ra,set:ia("getTangentAtLength",{rotate:!0})},atConnectionLengthIgnoreGradient:{qualify:ra,set:ia("getTangentAtLength",{rotate:!1})},atConnectionRatioKeepGradient:{qualify:ra,set:ia("getTangentAtRatio",{rotate:!0})},atConnectionRatioIgnoreGradient:{qualify:ra,set:ia("getTangentAtRatio",{rotate:!1})}};["transform","d","points","width","height","cx","cy","r","rx","ry","x1","x2","y1","y2","x","y","dx","dy"].forEach(function(n){ca[n]={qualify:Xo,set:function(t,e){var i;return(i={})[n]=_o(t,e),i}}}),ca.refR=ca.refRInscribed,ca.refD=ca.refDResetOffset,ca.refPoints=ca.refPointsResetOffset,ca.atConnectionLength=ca.atConnectionLengthKeepGradient,ca.atConnectionRatio=ca.atConnectionRatioKeepGradient,ca.refX2=ca.refX,ca.refY2=ca.refY,ca.refWidth2=ca.refWidth,ca.refHeight2=ca.refHeight,ca["ref-x"]=ca.refX,ca["ref-y"]=ca.refY,ca["ref-dy"]=ca.refDy,ca["ref-dx"]=ca.refDx,ca["ref-width"]=ca.refWidth,ca["ref-height"]=ca.refHeight,ca["x-alignment"]=ca.xAlignment,ca["y-alignment"]=ca.yAlignment;var ua=ca,ha=s.Model.extend({constructor:function(t,e){var i,n=t||{};"function"==typeof this.preinitialize&&this.preinitialize.apply(this,arguments),this.cid=Lo("c"),this.attributes={},e&&e.collection&&(this.collection=e.collection),e&&e.parse&&(n=this.parse(n,e)||{}),(i=xo(this,"defaults"))&&(n=zo({},i,n)),this.set(n,e),this.changed={},this.initialize.apply(this,arguments)},translate:function(t,e,i){throw new Error("Must define a translate() method.")},toJSON:function(){var e=xo(this.constructor.prototype,"defaults").attrs||{},t=this.attributes.attrs,o={};Do(t,function(t,n){var r=e[n];Do(t,function(t,i){jo(t)&&!Array.isArray(t)?Do(t,function(t,e){r&&r[i]&&ho(r[i][e],t)||(o[n]=o[n]||{},(o[n][i]||(o[n][i]={}))[e]=t)}):r&&ho(r[i],t)||(o[n]=o[n]||{},o[n][i]=t)})});var i=co(Mo(this.attributes,"attrs"));return i.attrs=o,i},initialize:function(t){t&&t.id||this.set("id",this.generateId(),{silent:!0}),this._transitionIds={},this.processPorts(),this.on("change:attrs",this.processPorts,this)},generateId:function(){return Pr()},processPorts:function(){var t=this.ports,i={};Do(this.get("attrs"),function(t,e){t&&t.port&&(void 0!==t.port.id?i[t.port.id]=t.port:i[t.port]={id:t.port})});var n={};(Do(t,function(t,e){i[e]||(n[e]=!0)}),this.graph&&!uo(n))&&(this.graph.getConnectedLinks(this,{inbound:!0}).forEach(function(t){n[t.get("target").port]&&t.remove()}),this.graph.getConnectedLinks(this,{outbound:!0}).forEach(function(t){n[t.get("source").port]&&t.remove()}));this.ports=i},remove:function(t){void 0===t&&(t={});var e=this.graph,i=this.collection;if(!e)return i&&i.remove(this,t),this;e.startBatch("remove");var n=this.getParentCell();n&&n.unembed(this,t);for(var r=this.getEmbeddedCells(),o=0,a=r.length;oc[2]?(a=".3em",r=u,o=0,"start"):lh[2]?(a=".3em",r=-c,o=0,"end"):l','','','','','','',''].join(""),toolMarkup:['','','','',"Remove link.","",'','','',"Link options.","",""].join(""),doubleToolMarkup:void 0,vertexMarkup:['','','','',"Remove vertex.","",""].join(""),arrowheadMarkup:['','',""].join(""),defaultLabel:void 0,labelMarkup:void 0,_builtins:{defaultLabel:{markup:[{tagName:"rect",selector:"rect"},{tagName:"text",selector:"text"}],attrs:{text:{fill:"#000000",fontSize:14,textAnchor:"middle",yAlignment:"middle",pointerEvents:"none"},rect:{ref:"text",fill:"#ffffff",rx:3,ry:3,refWidth:1,refHeight:1,refX:0,refY:0}},position:{distance:.5}}},defaults:{type:"link",source:{},target:{}},isLink:function(){return!0},disconnect:function(t){return this.set({source:{x:0,y:0},target:{x:0,y:0}},t)},source:function(t,e,i){return void 0===t?lo(this.get("source")):(r=t instanceof ha?((n=lo(e)||{}).id=t.id,i):fo(t)?(n=t,e):((n=lo(e)||{}).x=t.x,n.y=t.y,i),this.set("source",n,r));var n,r},target:function(t,e,i){return void 0===t?lo(this.get("target")):(r=t instanceof ha?((n=lo(e)||{}).id=t.id,i):fo(t)?(n=t,e):((n=lo(e)||{}).x=t.x,n.y=t.y,i),this.set("target",n,r));var n,r},router:function(t,e,i){if(void 0===t){var n=this.get("router");return n?"object"==typeof n?lo(n):n:this.get("manhattan")?{name:"orthogonal"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},a=r?e:i;return this.set("router",o,a)},connector:function(t,e,i){if(void 0===t){var n=this.get("connector");return n?"object"==typeof n?lo(n):n:this.get("smooth")?{name:"smooth"}:null}var r="object"==typeof t||"function"==typeof t,o=r?t:{name:t,args:e},a=r?e:i;return this.set("connector",o,a)},label:function(t,e,i){var n=this.labels();return(t=isFinite(t)&&null!==t?0|t:0)<0&&(t=n.length+t),arguments.length<=1?this.prop(["labels",t]):this.prop(["labels",t],e,i)},labels:function(t,e){return 0===arguments.length?(t=this.get("labels"),Array.isArray(t)?t.slice():[]):(Array.isArray(t)||(t=[]),this.set("labels",t,e))},hasLabels:function(){var t=this.attributes.labels;return Array.isArray(t)&&0 "+e),i=this.getSelector(t.parentNode,i)}return i},addLinkFromMagnet:function(t,e,i){var n=this.paper,r=n.model,o=n.getDefaultLink(this,t);return o.set({source:this.getLinkEnd(t,e,i,o,"source"),target:{x:e,y:i}}).addTo(r,{async:!1,ui:!0}),o.findView(n)},getLinkEnd:function(t){for(var e,i=[],n=arguments.length-1;0r.options.clickThreshold||this.notify("element:magnet:pointerclick",t,e,i,n)}},{Flags:Ua});io(Fa.prototype,Da);var Va={N:"S",S:"N",E:"W",W:"E"},Xa={N:-Math.PI/2*3,S:-Math.PI/2,E:0,W:Math.PI};function Ha(t,e,i){var n=new qi(t.x,e.y);return i.containsPoint(n)&&(n=new qi(e.x,t.y)),n}function Ja(t,e){return t["W"===e||"E"===e?"width":"height"]}function _a(t,e){return t.x===e.x?t.y>e.y?"N":"S":t.y===e.y?t.x>e.x?"W":"E":null}function Qa(t){return new In(t.x,t.y,0,0)}function Ka(t){var e=Xr(t.padding||t.elementPadding||20);return{x:-e.left,y:-e.top,width:e.left+e.right,height:e.top+e.bottom}}function qa(t,e){return t.sourceBBox.clone().moveAndExpand(Ka(e))}function $a(t,e){return t.targetBBox.clone().moveAndExpand(Ka(e))}function ts(t,e,i){var n=new qi(t.x,e.y),r=new qi(e.x,t.y),o=_a(t,n),a=_a(t,r),s=Va[i],l=o===i||o!==s&&(a===s||a!==i)?n:r;return{points:[l],direction:_a(l,e)}}function es(t,e,i){var n=Ha(t,e,i);return{points:[n],direction:_a(n,e)}}function is(e,t,i,n){var r,o={},a=[new qi(e.x,t.y),new qi(t.x,e.y)],s=a.filter(function(t){return!i.containsPoint(t)}),l=s.filter(function(t){return _a(t,e)!==n});if(0c.center().distance(t),h=u?e:t,g=u?t:e;return a=Ha(o=r?(o=qi.fromPolar(c.width+c.height,Xa[r],h),c.pointNearestToPoint(o).move(o,-1)):c.pointNearestToPoint(h).move(h,1),g,c),o.round().equals(a.round())?(a=qi.fromPolar(c.width+c.height,Pi(o.theta(h))+Math.PI/2,g),s=Ha(o,a=c.pointNearestToPoint(a).move(g,1).round(),c),l.points=u?[a,s,o]:[o,s,a]):l.points=u?[a,o]:[o,a],l.direction=_a(u?o:a,e),l}function os(t,e,i){var n,r,o,a,s,l=qa(i,e),c=$a(i,e),u=(r=e,(n=i).sourceAnchor?n.sourceAnchor:qa(n,r).center()),h=(a=e,(o=i).targetAnchor?o.targetAnchor:$a(o,a).center());l=l.union(Qa(u)),c=c.union(Qa(h)),(t=po(t).map(qi)).unshift(u),t.push(h);for(var g=[],d=0,f=t.length-1;dn.maxAllowedDirectionChange)){var X=ms(O.clone().offset(k.gridOffsetX,k.gridOffsetY),A,l),H=Is(X);if(!C.isClose(H)&&!i(X)){if(0<=j.indexOf(H))if(!X.equals(d))if(ds(V,gs(X,d,P,A,n))>n.maxAllowedDirectionChange)continue;var J=R+k.cost+(W?0:n.penalties[D]);(!C.isOpen(H)||Je[1].x?"right":"left";switch(n){case"left":return new qi(-1,0);case"right":default:return new qi(1,0)}}(t,e);case js.VERTICAL:return function(t,e,i){var n,r=t.sourceBBox;n=r.width&&r.height?r.sideNearestToPoint(e[0]):r.y>e[1].y?"bottom":"top";switch(n){case"top":return new qi(0,-1);case"bottom":default:return new qi(0,1)}}(t,e);case js.CLOSEST_POINT:return Es(t,e,n);case js.OUTWARDS:return Ys(t,e,n);case js.AUTO:default:return Bs(t,e,n)}}(r,h,a,l),d=h[0].distance(h[1])*l.coeff,f=Fs(g,h[1].difference(h[0]).normalize());if(f>Math.PI/4){var p=d+(f-Math.PI/4)*l.angleTangentCoefficient;c=g.clone().scale(p,p)}else c=g.clone().scale(d,d)}if(l.targetTangent)u=l.targetTangent;else{var m=function(t,e,i,n){if(n.targetDirection)switch(n.targetDirection){case Zs.UP:return new qi(0,-1);case Zs.DOWN:return new qi(0,1);case Zs.LEFT:return new qi(-1,0);case Zs.RIGHT:return new qi(0,1);case Zs.AUTO:return Os(t,e,n);case Zs.CLOSEST_POINT:return Rs(t,e,n);case Zs.OUTWARDS:return Ws(t,e,n);default:return n.targetDirection}switch(i){case js.HORIZONTAL:return function(t,e,i){var n,r=t.targetBBox;n=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.x>e[e.length-2].x?"left":"right";switch(n){case"left":return new qi(-1,0);case"right":default:return new qi(1,0)}}(t,e);case js.VERTICAL:return function(t,e,i){var n,r=t.targetBBox;n=r.width&&r.height?r.sideNearestToPoint(e[e.length-1]):r.y>e[e.length-2].y?"top":"bottom";switch(n){case"top":return new qi(0,-1);case"bottom":default:return new qi(0,1)}}(t,e);case js.CLOSEST_POINT:return Rs(t,e,n);case js.OUTWARDS:return Ws(t,e,n);case js.AUTO:default:return Os(t,e,n)}}(r,h,a,l),I=h.length-1,v=h[I-1].distance(h[I])*l.coeff,A=Fs(m,h[I-1].difference(h[I]).normalize());if(A>Math.PI/4){var C=v+(A-Math.PI/4)*l.angleTangentCoefficient;u=m.clone().scale(C,C)}else u=m.clone().scale(v,v)}var w=function(t,e,i,n){for(var r=n.tau,o=n.coeff,a=[],s=[],l=[],c=t.length-1,u=0;u=Math.abs(t.y-e.y)){var c=(t.x+e.x)/2;l=Tn.createSegment("C",c,t.y,c,e.y,e.x,e.y),r.appendSegment(l)}else{var u=(t.y+e.y)/2;l=Tn.createSegment("C",t.x,u,e.x,u,e.x,e.y),r.appendSegment(l)}}return o?r:r.serialize()},curve:Gs},Hs={RENDER:"RENDER",UPDATE:"UPDATE",TOOLS:"TOOLS",LEGACY_TOOLS:"LEGACY_TOOLS",LABELS:"LABELS",VERTICES:"VERTICES",SOURCE:"SOURCE",TARGET:"TARGET",CONNECTOR:"CONNECTOR"},Js=Wa.extend({className:function(){var t=Wa.prototype.className.apply(this).split(" ");return t.push("link"),t.join(" ")},options:{shortLinkLength:105,doubleLinkTools:!1,longLinkLength:155,linkToolsOffset:40,doubleLinkToolsOffset:65,sampleInterval:50},_labelCache:null,_labelSelectors:null,_markerCache:null,_V:null,_dragData:null,metrics:null,decimalsRounding:2,initialize:function(){Wa.prototype.initialize.apply(this,arguments),this._labelCache={},this._labelSelectors={},this._markerCache={},this._V={},this.cleanNodesCache()},presentationAttributes:{markup:[Hs.RENDER],attrs:[Hs.UPDATE],router:[Hs.UPDATE],connector:[Hs.CONNECTOR],smooth:[Hs.UPDATE],manhattan:[Hs.UPDATE],toolMarkup:[Hs.LEGACY_TOOLS],labels:[Hs.LABELS],labelMarkup:[Hs.LABELS],vertices:[Hs.VERTICES,Hs.UPDATE],vertexMarkup:[Hs.VERTICES],source:[Hs.SOURCE,Hs.UPDATE],target:[Hs.TARGET,Hs.UPDATE]},initFlag:[Hs.RENDER,Hs.SOURCE,Hs.TARGET,Hs.TOOLS],UPDATE_PRIORITY:1,confirmUpdate:function(t,e){if(e||(e={}),this.hasFlag(t,Hs.SOURCE)){if(!this.updateEndProperties("source"))return t;t=this.removeFlag(t,Hs.SOURCE)}if(this.hasFlag(t,Hs.TARGET)){if(!this.updateEndProperties("target"))return t;t=this.removeFlag(t,Hs.TARGET)}var i=this.paper,n=this.sourceView,r=this.targetView;if(i&&(n&&!i.isViewMounted(n)||r&&!i.isViewMounted(r)))return t;if(this.hasFlag(t,Hs.RENDER))return this.render(),this.updateHighlighters(!0),this.updateTools(e),t=this.removeFlag(t,[Hs.RENDER,Hs.UPDATE,Hs.VERTICES,Hs.LABELS,Hs.TOOLS,Hs.LEGACY_TOOLS,Hs.CONNECTOR]);var o=!1;this.hasFlag(t,Hs.VERTICES)&&(this.renderVertexMarkers(),t=this.removeFlag(t,Hs.VERTICES));var a=this.model,s=a.attributes,l=this.hasFlag(t,Hs.LABELS),c=this.hasFlag(t,Hs.LEGACY_TOOLS);l&&(this.onLabelsChange(a,s.labels,e),t=this.removeFlag(t,Hs.LABELS),o=!0),c&&(this.renderTools(),t=this.removeFlag(t,Hs.LEGACY_TOOLS));var u=this.hasFlag(t,Hs.UPDATE),h=this.hasFlag(t,Hs.CONNECTOR);return(u||h)&&(u?e.translateBy&&a.isRelationshipEmbeddedIn(e.translateBy)?this.translate(e.tx,e.ty):this.update():(this.updatePath(),this.updateDOM()),this.updateTools(e),t=this.removeFlag(t,[Hs.UPDATE,Hs.TOOLS,Hs.CONNECTOR]),o=!(c=l=!1)),l&&this.updateLabelPositions(),c&&this.updateToolsPosition(),o&&this.updateHighlighters(),this.hasFlag(t,Hs.TOOLS)&&(this.updateTools(e),t=this.removeFlag(t,Hs.TOOLS)),t},requestConnectionUpdate:function(t){this.requestUpdate(this.getFlag(Hs.UPDATE),t)},isLabelsRenderRequired:function(t){void 0===t&&(t={});var e=this.model.previous("labels");if(!e)return!0;if("propertyPathArray"in t&&"propertyValue"in t){var i=t.propertyPathArray||[],n=i.length;if(1=this.options.longLinkLength){var r=this.options.doubleLinkToolsOffset||e;n=this.getPointAtLength(i-r),this._tool2Cache.attr("transform","translate("+n.x+", "+n.y+") "+t),this._tool2Cache.attr("visibility","visible")}else this.options.doubleLinkTools&&this._tool2Cache.attr("visibility","hidden")}return this},updateArrowheadMarkers:function(){if(!this._V.markerArrowheads)return this;if("none"===S.css(this._V.markerArrowheads.node,"display"))return this;var t=this.getConnectionLength()'}),Ml=Fa.extend({presentationAttributes:Fa.addPresentationAttributes({attrs:["SCALE"]}),confirmUpdate:function(){var t=Fa.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"SCALE")&&(this.resize(),t=this.removeFlag(t,"SCALE")),t}}),Sl=Nl.define("basic.Text",{attrs:{text:{"font-size":18,fill:"#000000"}}},{markup:''}),kl=Nl.define("basic.Circle",{size:{width:60,height:60},attrs:{circle:{fill:"#ffffff",stroke:"#000000",r:30,cx:30,cy:30},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Dl=Nl.define("basic.Ellipse",{size:{width:60,height:40},attrs:{ellipse:{fill:"#ffffff",stroke:"#000000",rx:30,ry:20,cx:30,cy:20},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-y":.5,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Tl=Nl.define("basic.Polygon",{size:{width:60,height:40},attrs:{polygon:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Ll=Nl.define("basic.Polyline",{size:{width:60,height:40},attrs:{polyline:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),zl=Nl.define("basic.Image",{attrs:{text:{"font-size":14,text:"","text-anchor":"middle","ref-x":.5,"ref-dy":20,"y-alignment":"middle",fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),Pl=Nl.define("basic.Path",{size:{width:60,height:60},attrs:{path:{fill:"#ffffff",stroke:"#000000"},text:{"font-size":14,text:"","text-anchor":"middle",ref:"path","ref-x":.5,"ref-dy":10,fill:"#000000","font-family":"Arial, helvetica, sans-serif"}}},{markup:''}),jl=Pl.define("basic.Rhombus",{attrs:{path:{d:"M 30 0 L 60 30 30 60 0 30 z"},text:{"ref-y":.5,"ref-dy":null,"y-alignment":"middle"}}}),Zl=bl.test("svgforeignobject"),Gl=Nl.define("basic.TextBlock",{attrs:{rect:{fill:"#ffffff",stroke:"#000000",width:80,height:100},text:{fill:"#000000","font-size":14,"font-family":"Arial, helvetica, sans-serif"},".content":{text:"","ref-x":.5,"ref-y":.5,"y-alignment":"middle","x-alignment":"middle"}},content:""},{markup:['','',Zl?'
':'',""].join(""),initialize:function(){this.listenTo(this,"change:size",this.updateSize),this.listenTo(this,"change:content",this.updateContent),this.updateSize(this,this.get("size")),this.updateContent(this,this.get("content")),Nl.prototype.initialize.apply(this,arguments)},updateSize:function(t,e){this.attr({".fobj":io({},e),div:{style:io({},e)}})},updateContent:function(t,e){Zl?this.attr({".content":{html:Wr(e)}}):this.attr({".content":{text:e}})},setForeignObjectSize:function(){this.updateSize.apply(this,arguments)},setDivContent:function(){this.updateContent.apply(this,arguments)}}),Bl=Fa.extend({presentationAttributes:Zl?Fa.prototype.presentationAttributes:Fa.addPresentationAttributes({content:["CONTENT"],size:["CONTENT"]}),initFlag:["RENDER","CONTENT"],confirmUpdate:function(){var t=Fa.prototype.confirmUpdate.apply(this,arguments);return this.hasFlag(t,"CONTENT")&&(this.updateContent(this.model),t=this.removeFlag(t,"CONTENT")),t},update:function(t,e){var i=this.model;if(Zl)Fa.prototype.update.call(this,i,e);else{var n=Mo(e||i.get("attrs"),".content");Fa.prototype.update.call(this,i,n),e&&!No(e,".content")||this.updateContent(i,e)}},updateContent:function(t,e){var i=zo({},(e||t.get("attrs"))[".content"]);i=Mo(i,"text");var n=Yr(t.get("content"),t.get("size"),i,{svgDocument:this.paper.svg}),r=Tr({},".content",i,"/");r[".content"].text=n,Fa.prototype.update.call(this,t,r)}}),Ol={Generic:Nl,Rect:xl,TextView:Ml,Text:Sl,Circle:kl,Ellipse:Dl,Polygon:Tl,Polyline:Ll,Image:zl,Path:Pl,Rhombus:jl,TextBlock:Gl,TextBlockView:Bl},El=Ta.define("standard.Rectangle",{attrs:{body:{refWidth:"100%",refHeight:"100%",strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"text",selector:"label"}]}),Rl=Ta.define("standard.Circle",{attrs:{body:{refCx:"50%",refCy:"50%",refR:"50%",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"circle",selector:"body"},{tagName:"text",selector:"label"}]}),Yl=Ta.define("standard.Ellipse",{attrs:{body:{refCx:"50%",refCy:"50%",refRx:"50%",refRy:"50%",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"ellipse",selector:"body"},{tagName:"text",selector:"label"}]}),Wl=Ta.define("standard.Path",{attrs:{body:{refD:"M 0 0 L 10 0 10 10 0 10 Z",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"path",selector:"body"},{tagName:"text",selector:"label"}]}),Ul=Ta.define("standard.Polygon",{attrs:{body:{refPoints:"0 0 10 0 10 10 0 10",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"polygon",selector:"body"},{tagName:"text",selector:"label"}]}),Fl=Ta.define("standard.Polyline",{attrs:{body:{refPoints:"0 0 10 0 10 10 0 10 0 0",strokeWidth:2,stroke:"#333333",fill:"#FFFFFF"},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",fontSize:14,fill:"#333333"}}},{markup:[{tagName:"polyline",selector:"body"},{tagName:"text",selector:"label"}]}),Vl=Ta.define("standard.Image",{attrs:{image:{refWidth:"100%",refHeight:"100%"},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"image",selector:"image"},{tagName:"text",selector:"label"}]}),Xl=Ta.define("standard.BorderedImage",{attrs:{border:{refWidth:"100%",refHeight:"100%",stroke:"#333333",strokeWidth:2},background:{refWidth:-1,refHeight:-1,x:.5,y:.5,fill:"#FFFFFF"},image:{refWidth:-1,refHeight:-1,x:.5,y:.5},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"background",attributes:{stroke:"none"}},{tagName:"image",selector:"image"},{tagName:"rect",selector:"border",attributes:{fill:"none"}},{tagName:"text",selector:"label"}]}),Hl=Ta.define("standard.EmbeddedImage",{attrs:{body:{refWidth:"100%",refHeight:"100%",stroke:"#333333",fill:"#FFFFFF",strokeWidth:2},image:{refWidth:"30%",refHeight:-20,x:10,y:10,preserveAspectRatio:"xMidYMin"},label:{textVerticalAnchor:"top",textAnchor:"left",refX:"30%",refX2:20,refY:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"image",selector:"image"},{tagName:"text",selector:"label"}]}),Jl=Ta.define("standard.InscribedImage",{attrs:{border:{refRx:"50%",refRy:"50%",refCx:"50%",refCy:"50%",stroke:"#333333",strokeWidth:2},background:{refRx:"50%",refRy:"50%",refCx:"50%",refCy:"50%",fill:"#FFFFFF"},image:{refWidth:"68%",refHeight:"68%",refX:"16%",refY:"16%",preserveAspectRatio:"xMidYMid"},label:{textVerticalAnchor:"top",textAnchor:"middle",refX:"50%",refY:"100%",refY2:10,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"ellipse",selector:"background"},{tagName:"image",selector:"image"},{tagName:"ellipse",selector:"border",attributes:{fill:"none"}},{tagName:"text",selector:"label"}]}),_l=Ta.define("standard.HeaderedRectangle",{attrs:{body:{refWidth:"100%",refHeight:"100%",strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},header:{refWidth:"100%",height:30,strokeWidth:2,stroke:"#000000",fill:"#FFFFFF"},headerText:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:15,fontSize:16,fill:"#333333"},bodyText:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"50%",refY2:15,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"rect",selector:"body"},{tagName:"rect",selector:"header"},{tagName:"text",selector:"headerText"},{tagName:"text",selector:"bodyText"}]}),Ql=Ta.define("standard.Cylinder",{attrs:{body:{lateralArea:10,fill:"#FFFFFF",stroke:"#333333",strokeWidth:2},top:{refCx:"50%",cy:10,refRx:"50%",ry:10,fill:"#FFFFFF",stroke:"#333333",strokeWidth:2},label:{textVerticalAnchor:"middle",textAnchor:"middle",refX:"50%",refY:"100%",refY2:15,fontSize:14,fill:"#333333"}}},{markup:[{tagName:"path",selector:"body"},{tagName:"ellipse",selector:"top"},{tagName:"text",selector:"label"}],topRy:function(t,e){if(void 0===t)return this.attr("body/lateralArea");var i={lateralArea:t},n=Er(t)?{refCy:t,refRy:t,cy:null,ry:null}:{refCy:null,refRy:null,cy:t,ry:t};return this.attr({body:i,top:n},e)}},{attributes:{lateralArea:{set:function(t,e){var i=Er(t);i&&(t=parseFloat(t)/100);var n=e.x,r=e.y,o=e.width,a=e.height,s=o/2,l=i?a*t:t,c=yr.KAPPA,u=c*s,h=c*(i?a*t:t),g=n+o/2,d=n+o,f=r+l,p=f-l,m=r+a-l,I=r+a;return{d:["M",n,f,"L",n,m,"C",n,m+h,g-u,I,g,I,"C",g+u,I,d,m+h,d,m,"L",d,f,"C",d,f-h,g+u,p,g,p,"C",g-u,p,n,f-h,n,f,"Z"].join(" ")}}}}}),Kl=bl.test("svgforeignobject")?{tagName:"foreignObject",selector:"foreignObject",attributes:{overflow:"hidden"},children:[{tagName:"div",namespaceURI:"http://www.w3.org/1999/xhtml",selector:"label",style:{width:"100%",height:"100%",position:"static",backgroundColor:"transparent",textAlign:"center",margin:0,padding:"0px 5px",boxSizing:"border-box",display:"flex",alignItems:"center",justifyContent:"center"}}]}:{tagName:"text",selector:"label",attributes:{"text-anchor":"middle"}},ql={Rectangle:El,Circle:Rl,Ellipse:Yl,Path:Wl,Polygon:Ul,Polyline:Fl,Image:Vl,BorderedImage:Xl,EmbeddedImage:Hl,InscribedImage:Jl,HeaderedRectangle:_l,Cylinder:Ql,TextBlock:Ta.define("standard.TextBlock",{attrs:{body:{refWidth:"100%",refHeight:"100%",stroke:"#333333",fill:"#ffffff",strokeWidth:2},foreignObject:{refWidth:"100%",refHeight:"100%"},label:{style:{fontSize:14}}}},{markup:[{tagName:"rect",selector:"body"},Kl]},{attributes:{text:{set:function(t,e,i,n){if(!(i instanceof HTMLElement)){var r=n.style||{},o={text:t,width:-5,height:"100%"},a=io({textVerticalAnchor:"middle"},r);return ua.textWrap.set.call(this,o,e,i,a),{fill:r.color||null}}i.textContent=t},position:function(t,e,i){if(i instanceof SVGElement)return e.center()}}}}),Link:Ma.define("standard.Link",{attrs:{line:{connection:!0,stroke:"#333333",strokeWidth:2,strokeLinejoin:"round",targetMarker:{type:"path",d:"M 10 -5 0 0 10 5 z"}},wrapper:{connection:!0,strokeWidth:10,strokeLinejoin:"round"}}},{markup:[{tagName:"path",selector:"wrapper",attributes:{fill:"none",cursor:"pointer",stroke:"transparent","stroke-linecap":"round"}},{tagName:"path",selector:"line",attributes:{fill:"none","pointer-events":"none"}}]}),DoubleLink:Ma.define("standard.DoubleLink",{attrs:{line:{connection:!0,stroke:"#DDDDDD",strokeWidth:4,strokeLinejoin:"round",targetMarker:{type:"path",stroke:"#000000",d:"M 10 -3 10 -10 -2 0 10 10 10 3"}},outline:{connection:!0,stroke:"#000000",strokeWidth:6,strokeLinejoin:"round"}}},{markup:[{tagName:"path",selector:"outline",attributes:{fill:"none"}},{tagName:"path",selector:"line",attributes:{fill:"none"}}]}),ShadowLink:Ma.define("standard.ShadowLink",{attrs:{line:{connection:!0,stroke:"#FF0000",strokeWidth:20,strokeLinejoin:"round",targetMarker:{type:"path",stroke:"none",d:"M 0 -10 -10 0 0 10 z"},sourceMarker:{type:"path",stroke:"none",d:"M -10 -10 0 0 -10 10 0 10 0 -10 z"}},shadow:{connection:!0,refX:3,refY:6,stroke:"#000000",strokeOpacity:.2,strokeWidth:20,strokeLinejoin:"round",targetMarker:{type:"path",d:"M 0 -10 -10 0 0 10 z",stroke:"none"},sourceMarker:{type:"path",stroke:"none",d:"M -10 -10 0 0 -10 10 0 10 0 -10 z"}}}},{markup:[{tagName:"path",selector:"shadow",attributes:{fill:"none"}},{tagName:"path",selector:"line",attributes:{fill:"none"}}]})},$l=Nl.define("devs.Model",{inPorts:[],outPorts:[],size:{width:80,height:80},attrs:{".":{magnet:!1},".label":{text:"Model","ref-x":.5,"ref-y":10,"font-size":18,"text-anchor":"middle",fill:"#000"},".body":{"ref-width":"100%","ref-height":"100%",stroke:"#000"}},ports:{groups:{in:{position:{name:"left"},attrs:{".port-label":{fill:"#000"},".port-body":{fill:"#fff",stroke:"#000",r:10,magnet:!0}},label:{position:{name:"left",args:{y:10}}}},out:{position:{name:"right"},attrs:{".port-label":{fill:"#000"},".port-body":{fill:"#fff",stroke:"#000",r:10,magnet:!0}},label:{position:{name:"right",args:{y:10}}}}}}},{markup:'',portMarkup:'',portLabelMarkup:'',initialize:function(){Nl.prototype.initialize.apply(this,arguments),this.on("change:inPorts change:outPorts",this.updatePortItems,this),this.updatePortItems()},updatePortItems:function(t,e,i){var n=so(this.get("inPorts")),r=wo(so(this.get("outPorts")),n),o=this.createPortItems("in",n),a=this.createPortItems("out",r);this.prop("ports/items",o.concat(a),io({rewrite:!0},i))},createPortItem:function(t,e){return{id:e,group:t,attrs:{".port-label":{text:e}}}},createPortItems:function(t,e){return po(e).map(this.createPortItem.bind(this,t))},_addGroupPort:function(t,e,i){var n=this.get(e);return this.set(e,Array.isArray(n)?n.concat(t):[t],i)},addOutPort:function(t,e){return this._addGroupPort(t,"outPorts",e)},addInPort:function(t,e){return this._addGroupPort(t,"inPorts",e)},_removeGroupPort:function(t,e,i){return this.set(e,Co(this.get(e),t),i)},removeOutPort:function(t,e){return this._removeGroupPort(t,"outPorts",e)},removeInPort:function(t,e){return this._removeGroupPort(t,"inPorts",e)},_changeGroup:function(t,e,i){return this.prop("ports/groups/"+t,jo(e)?e:{},i)},changeInGroup:function(t,e){return this._changeGroup("in",t,e)},changeOutGroup:function(t,e){return this._changeGroup("out",t,e)}}),tc=$l.define("devs.Atomic",{size:{width:80,height:80},attrs:{".label":{text:"Atomic"}}}),ec=$l.define("devs.Coupled",{size:{width:200,height:300},attrs:{".label":{text:"Coupled"}}}),ic={Model:$l,Atomic:tc,Coupled:ec,Link:Ma.define("devs.Link",{attrs:{".connection":{"stroke-width":2}}})},nc=Nl.define("logic.Gate",{size:{width:80,height:40},attrs:{".":{magnet:!1},".body":{width:100,height:50},circle:{r:7,stroke:"black",fill:"transparent","stroke-width":2}}},{operation:function(){return!0}}),rc=nc.define("logic.IO",{size:{width:60,height:30},attrs:{".body":{fill:"white",stroke:"black","stroke-width":2},".wire":{ref:".body","ref-y":.5,stroke:"black"},text:{fill:"black",ref:".body","ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle","font-weight":"bold","font-variant":"small-caps","text-transform":"capitalize","font-size":"14px"}}},{markup:''}),oc=rc.define("logic.Input",{attrs:{".wire":{"ref-dx":0,d:"M 0 0 L 23 0"},circle:{ref:".body","ref-dx":30,"ref-y":.5,magnet:!0,class:"output",port:"out"},text:{text:"input"}}}),ac=rc.define("logic.Output",{attrs:{".wire":{"ref-x":0,d:"M 0 0 L -23 0"},circle:{ref:".body","ref-x":-30,"ref-y":.5,magnet:"passive",class:"input",port:"in"},text:{text:"output"}}}),sc=nc.define("logic.Gate11",{attrs:{".input":{ref:".body","ref-x":-2,"ref-y":.5,magnet:"passive",port:"in"},".output":{ref:".body","ref-dx":2,"ref-y":.5,magnet:!0,port:"out"}}},{markup:''}),lc=nc.define("logic.Gate21",{attrs:{".input1":{ref:".body","ref-x":-2,"ref-y":.3,magnet:"passive",port:"in1"},".input2":{ref:".body","ref-x":-2,"ref-y":.7,magnet:"passive",port:"in2"},".output":{ref:".body","ref-dx":2,"ref-y":.5,magnet:!0,port:"out"}}},{markup:''}),cc=sc.define("logic.Repeater",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzIuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICA8L2c+Cjwvc3ZnPgo="}}},{operation:function(t){return t}}),uc=sc.define("logic.Not",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PVCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjgiCiAgICAgaW5rc2NhcGU6Y3g9Ijg0LjY4NTM1MiIKICAgICBpbmtzY2FwZTpjeT0iMTUuMjg4NjI4IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzkuMTU2OTEsMjUgTCA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAyOS4wNDM0NzgsMjUgTCA1LjA0MzQ3ODEsMjUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgZD0iTSAyOC45Njg3NSwyLjU5Mzc1IEwgMjguOTY4NzUsNSBMIDI4Ljk2ODc1LDQ1IEwgMjguOTY4NzUsNDcuNDA2MjUgTCAzMS4xMjUsNDYuMzQzNzUgTCA3Mi4xNTYyNSwyNi4zNDM3NSBMIDcyLjE1NjI1LDIzLjY1NjI1IEwgMzEuMTI1LDMuNjU2MjUgTCAyOC45Njg3NSwyLjU5Mzc1IHogTSAzMS45Njg3NSw3LjQwNjI1IEwgNjguMDkzNzUsMjUgTCAzMS45Njg3NSw0Mi41OTM3NSBMIDMxLjk2ODc1LDcuNDA2MjUgeiIKICAgICAgIGlkPSJwYXRoMjYzOCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDI2NzEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzYiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA4MCwyNSBBIDQsNCAwIDEgMSA3MiwyNSBBIDQsNCAwIDEgMSA4MCwyNSB6IgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEsMCkiIC8+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t){return!t}}),hc=lc.define("logic.Or",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik9SIEFOU0kuc3ZnIgogICBpbmtzY2FwZTpvdXRwdXRfZXh0ZW5zaW9uPSJvcmcuaW5rc2NhcGUub3V0cHV0LnN2Zy5pbmtzY2FwZSI+CiAgPGRlZnMKICAgICBpZD0iZGVmczQiPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTAgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjcxNCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iMSA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMC41IDogMC4zMzMzMzMzMyA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODA2IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyODE5IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjM3Mi4wNDcyNCA6IDM1MC43ODczOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI3NDQuMDk0NDggOiA1MjYuMTgxMDkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzc3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49Ijc1IDogNDAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTUwIDogNjAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDYwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTMyNzUiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNTAgOiAzMy4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iMTAwIDogNTAgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDUwIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTU1MzMiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzIgOiAyMS4zMzMzMzMgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNjQgOiAzMiA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMzIgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjU1NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDE2LjY2NjY2NyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDI1IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAyNSA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iNCIKICAgICBpbmtzY2FwZTpjeD0iMTEzLjAwMDM5IgogICAgIGlua3NjYXBlOmN5PSIxMi44OTM3MzEiCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4IgogICAgIGlua3NjYXBlOmN1cnJlbnQtbGF5ZXI9ImcyNTYwIgogICAgIHNob3dncmlkPSJmYWxzZSIKICAgICBpbmtzY2FwZTpncmlkLWJib3g9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1wb2ludHM9InRydWUiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAwMDAiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxMzk5IgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijg3NCIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iMzciCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii00IgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Im0gNzAsMjUgYyAyMCwwIDI1LDAgMjUsMCIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMSwxNSA1LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzIsMzUgNSwzNSIKICAgICAgIGlkPSJwYXRoMzk0NCIgLz4KICAgIDxnCiAgICAgICBpZD0iZzI1NjAiCiAgICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2LjUsLTM5LjUpIj4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo="}}},{operation:function(t,e){return t||e}}),gc=lc.define("logic.And",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IkFORCBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI4IgogICAgIGlua3NjYXBlOmN4PSI1Ni42OTgzNDgiCiAgICAgaW5rc2NhcGU6Y3k9IjI1LjMyNjg5OSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZvbnQtc2l6ZTptZWRpdW07Zm9udC1zdHlsZTpub3JtYWw7Zm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zdHJldGNoOm5vcm1hbDt0ZXh0LWluZGVudDowO3RleHQtYWxpZ246c3RhcnQ7dGV4dC1kZWNvcmF0aW9uOm5vbmU7bGluZS1oZWlnaHQ6bm9ybWFsO2xldHRlci1zcGFjaW5nOm5vcm1hbDt3b3JkLXNwYWNpbmc6bm9ybWFsO3RleHQtdHJhbnNmb3JtOm5vbmU7ZGlyZWN0aW9uOmx0cjtibG9jay1wcm9ncmVzc2lvbjp0Yjt3cml0aW5nLW1vZGU6bHItdGI7dGV4dC1hbmNob3I6c3RhcnQ7ZmlsbDojMDAwMDAwO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTpub25lO3N0cm9rZS13aWR0aDozO21hcmtlcjpub25lO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGU7Zm9udC1mYW1pbHk6Qml0c3RyZWFtIFZlcmEgU2FuczstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkJpdHN0cmVhbSBWZXJhIFNhbnMiCiAgICAgICBkPSJNIDMwLDUgTCAzMCw2LjQyODU3MTQgTCAzMCw0My41NzE0MjkgTCAzMCw0NSBMIDMxLjQyODU3MSw0NSBMIDUwLjQ3NjE5LDQ1IEMgNjEuNzQ0MDk4LDQ1IDcwLjQ3NjE5LDM1Ljk5OTk1NSA3MC40NzYxOSwyNSBDIDcwLjQ3NjE5LDE0LjAwMDA0NSA2MS43NDQwOTksNS4wMDAwMDAyIDUwLjQ3NjE5LDUgQyA1MC40NzYxOSw1IDUwLjQ3NjE5LDUgMzEuNDI4NTcxLDUgTCAzMCw1IHogTSAzMi44NTcxNDMsNy44NTcxNDI5IEMgNDAuODM0MjY0LDcuODU3MTQyOSA0NS45MTgzNjgsNy44NTcxNDI5IDQ4LjA5NTIzOCw3Ljg1NzE0MjkgQyA0OS4yODU3MTQsNy44NTcxNDI5IDQ5Ljg4MDk1Miw3Ljg1NzE0MjkgNTAuMTc4NTcxLDcuODU3MTQyOSBDIDUwLjMyNzM4MSw3Ljg1NzE0MjkgNTAuNDA5MjI3LDcuODU3MTQyOSA1MC40NDY0MjksNy44NTcxNDI5IEMgNTAuNDY1MDI5LDcuODU3MTQyOSA1MC40NzE1NDMsNy44NTcxNDI5IDUwLjQ3NjE5LDcuODU3MTQyOSBDIDYwLjIzNjg1Myw3Ljg1NzE0MyA2Ny4xNDI4NTcsMTUuNDk3MDk4IDY3LjE0Mjg1NywyNSBDIDY3LjE0Mjg1NywzNC41MDI5MDIgNTkuNzYwNjYyLDQyLjE0Mjg1NyA1MCw0Mi4xNDI4NTcgTCAzMi44NTcxNDMsNDIuMTQyODU3IEwgMzIuODU3MTQzLDcuODU3MTQyOSB6IgogICAgICAgaWQ9InBhdGgyODg0IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2NjY2NzY2NjY3Nzc3NzY2NjIiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return t&&e}}),dc=lc.define("logic.Nor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5PUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjEiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjY3NzY0NCIKICAgICBpbmtzY2FwZTpjeT0iMjIuMTAyMzQ0IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjM3IgogICAgIGlua3NjYXBlOndpbmRvdy15PSItNCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc5LDI1IEMgOTksMjUgOTUsMjUgOTUsMjUiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEsMTUgNSwxNSIKICAgICAgIGlkPSJwYXRoMzA2MSIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjk5OTk5OTg4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMyLDM1IDUsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc3R5bGU9ImZpbGw6bm9uZTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWpvaW46bWl0ZXI7bWFya2VyOm5vbmU7c3Ryb2tlLW9wYWNpdHk6MTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlIgogICAgICAgICBpZD0icGF0aDI2MDQiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI3NSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjI1IgogICAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgICAgc29kaXBvZGk6cnk9IjQiCiAgICAgICAgIGQ9Ik0gNzksMjUgQSA0LDQgMCAxIDEgNzEsMjUgQSA0LDQgMCAxIDEgNzksMjUgeiIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTI2LjUsMzkuNSkiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t,e){return!(t||e)}}),fc=lc.define("logic.Nand",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9Ik5BTkQgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6em9vbT0iMTYiCiAgICAgaW5rc2NhcGU6Y3g9Ijc4LjI4MzMwNyIKICAgICBpbmtzY2FwZTpjeT0iMTYuNDQyODQzIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9InRydWUiCiAgICAgaW5rc2NhcGU6Z3JpZC1iYm94PSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtcG9pbnRzPSJ0cnVlIgogICAgIGdyaWR0b2xlcmFuY2U9IjEwMDAwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM5OSIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI4NzQiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjMzIgogICAgIGlua3NjYXBlOndpbmRvdy15PSIwIgogICAgIGlua3NjYXBlOnNuYXAtYmJveD0idHJ1ZSI+CiAgICA8aW5rc2NhcGU6Z3JpZAogICAgICAgaWQ9IkdyaWRGcm9tUHJlMDQ2U2V0dGluZ3MiCiAgICAgICB0eXBlPSJ4eWdyaWQiCiAgICAgICBvcmlnaW54PSIwcHgiCiAgICAgICBvcmlnaW55PSIwcHgiCiAgICAgICBzcGFjaW5neD0iMXB4IgogICAgICAgc3BhY2luZ3k9IjFweCIKICAgICAgIGNvbG9yPSIjMDAwMGZmIgogICAgICAgZW1wY29sb3I9IiMwMDAwZmYiCiAgICAgICBvcGFjaXR5PSIwLjIiCiAgICAgICBlbXBvcGFjaXR5PSIwLjQiCiAgICAgICBlbXBzcGFjaW5nPSI1IgogICAgICAgdmlzaWJsZT0idHJ1ZSIKICAgICAgIGVuYWJsZWQ9InRydWUiIC8+CiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+CiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhNyI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gNzksMjUgQyA5MS44LDI1IDk1LDI1IDk1LDI1IgogICAgICAgaWQ9InBhdGgzMDU5IgogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDMxLDE1IDUsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMiwzNSA1LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmb250LXNpemU6bWVkaXVtO2ZvbnQtc3R5bGU6bm9ybWFsO2ZvbnQtdmFyaWFudDpub3JtYWw7Zm9udC13ZWlnaHQ6bm9ybWFsO2ZvbnQtc3RyZXRjaDpub3JtYWw7dGV4dC1pbmRlbnQ6MDt0ZXh0LWFsaWduOnN0YXJ0O3RleHQtZGVjb3JhdGlvbjpub25lO2xpbmUtaGVpZ2h0Om5vcm1hbDtsZXR0ZXItc3BhY2luZzpub3JtYWw7d29yZC1zcGFjaW5nOm5vcm1hbDt0ZXh0LXRyYW5zZm9ybTpub25lO2RpcmVjdGlvbjpsdHI7YmxvY2stcHJvZ3Jlc3Npb246dGI7d3JpdGluZy1tb2RlOmxyLXRiO3RleHQtYW5jaG9yOnN0YXJ0O2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MzttYXJrZXI6bm9uZTt2aXNpYmlsaXR5OnZpc2libGU7ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO2ZvbnQtZmFtaWx5OkJpdHN0cmVhbSBWZXJhIFNhbnM7LWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjpCaXRzdHJlYW0gVmVyYSBTYW5zIgogICAgICAgZD0iTSAzMCw1IEwgMzAsNi40Mjg1NzE0IEwgMzAsNDMuNTcxNDI5IEwgMzAsNDUgTCAzMS40Mjg1NzEsNDUgTCA1MC40NzYxOSw0NSBDIDYxLjc0NDA5OCw0NSA3MC40NzYxOSwzNS45OTk5NTUgNzAuNDc2MTksMjUgQyA3MC40NzYxOSwxNC4wMDAwNDUgNjEuNzQ0MDk5LDUuMDAwMDAwMiA1MC40NzYxOSw1IEMgNTAuNDc2MTksNSA1MC40NzYxOSw1IDMxLjQyODU3MSw1IEwgMzAsNSB6IE0gMzIuODU3MTQzLDcuODU3MTQyOSBDIDQwLjgzNDI2NCw3Ljg1NzE0MjkgNDUuOTE4MzY4LDcuODU3MTQyOSA0OC4wOTUyMzgsNy44NTcxNDI5IEMgNDkuMjg1NzE0LDcuODU3MTQyOSA0OS44ODA5NTIsNy44NTcxNDI5IDUwLjE3ODU3MSw3Ljg1NzE0MjkgQyA1MC4zMjczODEsNy44NTcxNDI5IDUwLjQwOTIyNyw3Ljg1NzE0MjkgNTAuNDQ2NDI5LDcuODU3MTQyOSBDIDUwLjQ2NTAyOSw3Ljg1NzE0MjkgNTAuNDcxNTQzLDcuODU3MTQyOSA1MC40NzYxOSw3Ljg1NzE0MjkgQyA2MC4yMzY4NTMsNy44NTcxNDMgNjcuMTQyODU3LDE1LjQ5NzA5OCA2Ny4xNDI4NTcsMjUgQyA2Ny4xNDI4NTcsMzQuNTAyOTAyIDU5Ljc2MDY2Miw0Mi4xNDI4NTcgNTAsNDIuMTQyODU3IEwgMzIuODU3MTQzLDQyLjE0Mjg1NyBMIDMyLjg1NzE0Myw3Ljg1NzE0MjkgeiIKICAgICAgIGlkPSJwYXRoMjg4NCIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2Njc2NjY2Nzc3Nzc2NjYyIgLz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDQwMDgiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return!(t&&e)}}),pc=lc.define("logic.Xor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhPUiBBTlNJLnN2ZyIKICAgaW5rc2NhcGU6b3V0cHV0X2V4dGVuc2lvbj0ib3JnLmlua3NjYXBlLm91dHB1dC5zdmcuaW5rc2NhcGUiPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM0Ij4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSI1MCA6IDE1IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIyNSA6IDEwIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3MTQiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEgOiAwLjUgOiAxIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjAuNSA6IDAuMzMzMzMzMzMgOiAxIgogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgwNiIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjgxOSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzNzIuMDQ3MjQgOiAzNTAuNzg3MzkgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNzQ0LjA5NDQ4IDogNTI2LjE4MTA5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MjYuMTgxMDkgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMjc3NyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI3NSA6IDQwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjE1MCA6IDYwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA2MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUzMjc1IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjUwIDogMzMuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjEwMCA6IDUwIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiA1MCA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmU1NTMzIgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjMyIDogMjEuMzMzMzMzIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjY0IDogMzIgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDMyIDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI1NTciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxNi42NjY2NjcgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAyNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMjUgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxLjAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAuMCIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjUuNjU2ODU0MiIKICAgICBpbmtzY2FwZTpjeD0iMjUuOTM4MTE2IgogICAgIGlua3NjYXBlOmN5PSIxNy4yMzAwNSIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJtIDcwLDI1IGMgMjAsMCAyNSwwIDI1LDAiCiAgICAgICBpZD0icGF0aDMwNTkiCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5ODg7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzAuMzg1NzE3LDE1IEwgNC45OTk5OTk4LDE1IgogICAgICAgaWQ9InBhdGgzMDYxIiAvPgogICAgPHBhdGgKICAgICAgIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuOTk5OTk5NzY7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgIGQ9Ik0gMzEuMzYyMDkxLDM1IEwgNC45OTk5OTk4LDM1IgogICAgICAgaWQ9InBhdGgzOTQ0IiAvPgogICAgPGcKICAgICAgIGlkPSJnMjU2MCIKICAgICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYuNSwtMzkuNSkiPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDM1MTYiCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi4yNSw4MS41MDAwMDUgQyAtMy44NDczNzQsODQuMTQ0NDA1IC00LjUsODQuNTAwMDA1IC00LjUsODQuNTAwMDA1IEwgLTguMTU2MjUsODQuNTAwMDA1IEwgLTYuMTU2MjUsODIuMDYyNTA1IEMgLTYuMTU2MjUsODIuMDYyNTA1IC0wLjUsNzUuMDYyNDUxIC0wLjUsNjQuNSBDIC0wLjUsNTMuOTM3NTQ5IC02LjE1NjI1LDQ2LjkzNzUgLTYuMTU2MjUsNDYuOTM3NSBMIC04LjE1NjI1LDQ0LjUgTCAtNC41LDQ0LjUgQyAtMy43MTg3NSw0NS40Mzc1IC0zLjA3ODEyNSw0Ni4xNTYyNSAtMi4yODEyNSw0Ny41IEMgLTAuNDA4NTMxLDUwLjU5OTgxNSAyLjUsNTYuNTI2NjQ2IDIuNSw2NC41IEMgMi41LDcyLjQ1MDY1IC0wLjM5NjY5Nyw3OC4zNzk0MjUgLTIuMjUsODEuNTAwMDA1IHoiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY3NjY2Njc2MiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIHN0eWxlPSJmaWxsOiMwMDAwMDA7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOm5vbmU7c3Ryb2tlLXdpZHRoOjM7c3Ryb2tlLWxpbmVjYXA6YnV0dDtzdHJva2UtbGluZWpvaW46bWl0ZXI7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgZD0iTSAtMi40MDYyNSw0NC41IEwgLTAuNDA2MjUsNDYuOTM3NSBDIC0wLjQwNjI1LDQ2LjkzNzUgNS4yNSw1My45Mzc1NDkgNS4yNSw2NC41IEMgNS4yNSw3NS4wNjI0NTEgLTAuNDA2MjUsODIuMDYyNSAtMC40MDYyNSw4Mi4wNjI1IEwgLTIuNDA2MjUsODQuNSBMIDAuNzUsODQuNSBMIDE0Ljc1LDg0LjUgQyAxNy4xNTgwNzYsODQuNTAwMDAxIDIyLjQzOTY5OSw4NC41MjQ1MTQgMjguMzc1LDgyLjA5Mzc1IEMgMzQuMzEwMzAxLDc5LjY2Mjk4NiA0MC45MTE1MzYsNzQuNzUwNDg0IDQ2LjA2MjUsNjUuMjE4NzUgTCA0NC43NSw2NC41IEwgNDYuMDYyNSw2My43ODEyNSBDIDM1Ljc1OTM4Nyw0NC43MTU1OSAxOS41MDY1NzQsNDQuNSAxNC43NSw0NC41IEwgMC43NSw0NC41IEwgLTIuNDA2MjUsNDQuNSB6IE0gMy40Njg3NSw0Ny41IEwgMTQuNzUsNDcuNSBDIDE5LjQzNDE3Myw0Ny41IDMzLjAzNjg1LDQ3LjM2OTc5MyA0Mi43MTg3NSw2NC41IEMgMzcuOTUxOTY0LDcyLjkyOTA3NSAzMi4xOTc0NjksNzcuMTgzOTEgMjcsNzkuMzEyNSBDIDIxLjYzOTMzOSw4MS41MDc5MjQgMTcuMTU4MDc1LDgxLjUwMDAwMSAxNC43NSw4MS41IEwgMy41LDgxLjUgQyA1LjM3MzU4ODQsNzguMzkxNTY2IDguMjUsNzIuNDUwNjUgOC4yNSw2NC41IEMgOC4yNSw1Ni41MjY2NDYgNS4zNDE0Njg2LDUwLjU5OTgxNSAzLjQ2ODc1LDQ3LjUgeiIKICAgICAgICAgaWQ9InBhdGg0OTczIgogICAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9ImNjc2NjY2NzY2NjY2NjY2Njc2Njc2MiIC8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K"}}},{operation:function(t,e){return(!t||e)&&(t||!e)}}),mc=lc.define("logic.Xnor",{attrs:{image:{"xlink:href":"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgo8c3ZnCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnNvZGlwb2RpPSJodHRwOi8vc29kaXBvZGkuc291cmNlZm9yZ2UubmV0L0RURC9zb2RpcG9kaS0wLmR0ZCIKICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiCiAgIHdpZHRoPSIxMDAiCiAgIGhlaWdodD0iNTAiCiAgIGlkPSJzdmcyIgogICBzb2RpcG9kaTp2ZXJzaW9uPSIwLjMyIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIwLjQ2IgogICB2ZXJzaW9uPSIxLjAiCiAgIHNvZGlwb2RpOmRvY25hbWU9IlhOT1IgQU5TSS5zdmciCiAgIGlua3NjYXBlOm91dHB1dF9leHRlbnNpb249Im9yZy5pbmtzY2FwZS5vdXRwdXQuc3ZnLmlua3NjYXBlIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogMTUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfej0iNTAgOiAxNSA6IDEiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMjUgOiAxMCA6IDEiCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNzE0IiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDAuNSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxIDogMC41IDogMSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIwLjUgOiAwLjMzMzMzMzMzIDogMSIKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MDYiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI4MTkiCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iMzcyLjA0NzI0IDogMzUwLjc4NzM5IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9Ijc0NC4wOTQ0OCA6IDUyNi4xODEwOSA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTI2LjE4MTA5IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgICA8aW5rc2NhcGU6cGVyc3BlY3RpdmUKICAgICAgIGlkPSJwZXJzcGVjdGl2ZTI3NzciCiAgICAgICBpbmtzY2FwZTpwZXJzcDNkLW9yaWdpbj0iNzUgOiA0MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxNTAgOiA2MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNjAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlMzI3NSIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSI1MCA6IDMzLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSIxMDAgOiA1MCA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF95PSIwIDogMTAwMCA6IDAiCiAgICAgICBpbmtzY2FwZTp2cF94PSIwIDogNTAgOiAxIgogICAgICAgc29kaXBvZGk6dHlwZT0iaW5rc2NhcGU6cGVyc3AzZCIgLz4KICAgIDxpbmtzY2FwZTpwZXJzcGVjdGl2ZQogICAgICAgaWQ9InBlcnNwZWN0aXZlNTUzMyIKICAgICAgIGlua3NjYXBlOnBlcnNwM2Qtb3JpZ2luPSIzMiA6IDIxLjMzMzMzMyA6IDEiCiAgICAgICBpbmtzY2FwZTp2cF96PSI2NCA6IDMyIDogMSIKICAgICAgIGlua3NjYXBlOnZwX3k9IjAgOiAxMDAwIDogMCIKICAgICAgIGlua3NjYXBlOnZwX3g9IjAgOiAzMiA6IDEiCiAgICAgICBzb2RpcG9kaTp0eXBlPSJpbmtzY2FwZTpwZXJzcDNkIiAvPgogICAgPGlua3NjYXBlOnBlcnNwZWN0aXZlCiAgICAgICBpZD0icGVyc3BlY3RpdmUyNTU3IgogICAgICAgaW5rc2NhcGU6cGVyc3AzZC1vcmlnaW49IjI1IDogMTYuNjY2NjY3IDogMSIKICAgICAgIGlua3NjYXBlOnZwX3o9IjUwIDogMjUgOiAxIgogICAgICAgaW5rc2NhcGU6dnBfeT0iMCA6IDEwMDAgOiAwIgogICAgICAgaW5rc2NhcGU6dnBfeD0iMCA6IDI1IDogMSIKICAgICAgIHNvZGlwb2RpOnR5cGU9Imlua3NjYXBlOnBlcnNwM2QiIC8+CiAgPC9kZWZzPgogIDxzb2RpcG9kaTpuYW1lZHZpZXcKICAgICBpZD0iYmFzZSIKICAgICBwYWdlY29sb3I9IiNmZmZmZmYiCiAgICAgYm9yZGVyY29sb3I9IiM2NjY2NjYiCiAgICAgYm9yZGVyb3BhY2l0eT0iMS4wIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp6b29tPSI0IgogICAgIGlua3NjYXBlOmN4PSI5NS43MjM2NiIKICAgICBpbmtzY2FwZTpjeT0iLTI2Ljc3NTAyMyIKICAgICBpbmtzY2FwZTpkb2N1bWVudC11bml0cz0icHgiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ibGF5ZXIxIgogICAgIHNob3dncmlkPSJ0cnVlIgogICAgIGlua3NjYXBlOmdyaWQtYmJveD0idHJ1ZSIKICAgICBpbmtzY2FwZTpncmlkLXBvaW50cz0idHJ1ZSIKICAgICBncmlkdG9sZXJhbmNlPSIxMDAwMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjEzOTkiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iODc0IgogICAgIGlua3NjYXBlOndpbmRvdy14PSIzMyIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMCIKICAgICBpbmtzY2FwZTpzbmFwLWJib3g9InRydWUiPgogICAgPGlua3NjYXBlOmdyaWQKICAgICAgIGlkPSJHcmlkRnJvbVByZTA0NlNldHRpbmdzIgogICAgICAgdHlwZT0ieHlncmlkIgogICAgICAgb3JpZ2lueD0iMHB4IgogICAgICAgb3JpZ2lueT0iMHB4IgogICAgICAgc3BhY2luZ3g9IjFweCIKICAgICAgIHNwYWNpbmd5PSIxcHgiCiAgICAgICBjb2xvcj0iIzAwMDBmZiIKICAgICAgIGVtcGNvbG9yPSIjMDAwMGZmIgogICAgICAgb3BhY2l0eT0iMC4yIgogICAgICAgZW1wb3BhY2l0eT0iMC40IgogICAgICAgZW1wc3BhY2luZz0iNSIKICAgICAgIHZpc2libGU9InRydWUiCiAgICAgICBlbmFibGVkPSJ0cnVlIiAvPgogIDwvc29kaXBvZGk6bmFtZWR2aWV3PgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTciPgogICAgPHJkZjpSREY+CiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxnCiAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgaW5rc2NhcGU6Z3JvdXBtb2RlPSJsYXllciIKICAgICBpZD0ibGF5ZXIxIj4KICAgIDxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyLjAwMDAwMDI0O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICBkPSJNIDc4LjMzMzMzMiwyNSBDIDkxLjY2NjY2NiwyNSA5NSwyNSA5NSwyNSIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2MiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk4ODtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMC4zODU3MTcsMTUgTCA0Ljk5OTk5OTgsMTUiCiAgICAgICBpZD0icGF0aDMwNjEiIC8+CiAgICA8cGF0aAogICAgICAgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS45OTk5OTk3NjtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgZD0iTSAzMS4zNjIwOTEsMzUgTCA0Ljk5OTk5OTgsMzUiCiAgICAgICBpZD0icGF0aDM5NDQiIC8+CiAgICA8ZwogICAgICAgaWQ9ImcyNTYwIgogICAgICAgaW5rc2NhcGU6bGFiZWw9IkxheWVyIDEiCiAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNi41LC0zOS41KSI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMzUxNiIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjI1LDgxLjUwMDAwNSBDIC0zLjg0NzM3NCw4NC4xNDQ0MDUgLTQuNSw4NC41MDAwMDUgLTQuNSw4NC41MDAwMDUgTCAtOC4xNTYyNSw4NC41MDAwMDUgTCAtNi4xNTYyNSw4Mi4wNjI1MDUgQyAtNi4xNTYyNSw4Mi4wNjI1MDUgLTAuNSw3NS4wNjI0NTEgLTAuNSw2NC41IEMgLTAuNSw1My45Mzc1NDkgLTYuMTU2MjUsNDYuOTM3NSAtNi4xNTYyNSw0Ni45Mzc1IEwgLTguMTU2MjUsNDQuNSBMIC00LjUsNDQuNSBDIC0zLjcxODc1LDQ1LjQzNzUgLTMuMDc4MTI1LDQ2LjE1NjI1IC0yLjI4MTI1LDQ3LjUgQyAtMC40MDg1MzEsNTAuNTk5ODE1IDIuNSw1Ni41MjY2NDYgMi41LDY0LjUgQyAyLjUsNzIuNDUwNjUgLTAuMzk2Njk3LDc4LjM3OTQyNSAtMi4yNSw4MS41MDAwMDUgeiIKICAgICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJjY2Njc2NjY2NzYyIgLz4KICAgICAgPHBhdGgKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2Utb3BhY2l0eToxIgogICAgICAgICBkPSJNIC0yLjQwNjI1LDQ0LjUgTCAtMC40MDYyNSw0Ni45Mzc1IEMgLTAuNDA2MjUsNDYuOTM3NSA1LjI1LDUzLjkzNzU0OSA1LjI1LDY0LjUgQyA1LjI1LDc1LjA2MjQ1MSAtMC40MDYyNSw4Mi4wNjI1IC0wLjQwNjI1LDgyLjA2MjUgTCAtMi40MDYyNSw4NC41IEwgMC43NSw4NC41IEwgMTQuNzUsODQuNSBDIDE3LjE1ODA3Niw4NC41MDAwMDEgMjIuNDM5Njk5LDg0LjUyNDUxNCAyOC4zNzUsODIuMDkzNzUgQyAzNC4zMTAzMDEsNzkuNjYyOTg2IDQwLjkxMTUzNiw3NC43NTA0ODQgNDYuMDYyNSw2NS4yMTg3NSBMIDQ0Ljc1LDY0LjUgTCA0Ni4wNjI1LDYzLjc4MTI1IEMgMzUuNzU5Mzg3LDQ0LjcxNTU5IDE5LjUwNjU3NCw0NC41IDE0Ljc1LDQ0LjUgTCAwLjc1LDQ0LjUgTCAtMi40MDYyNSw0NC41IHogTSAzLjQ2ODc1LDQ3LjUgTCAxNC43NSw0Ny41IEMgMTkuNDM0MTczLDQ3LjUgMzMuMDM2ODUsNDcuMzY5NzkzIDQyLjcxODc1LDY0LjUgQyAzNy45NTE5NjQsNzIuOTI5MDc1IDMyLjE5NzQ2OSw3Ny4xODM5MSAyNyw3OS4zMTI1IEMgMjEuNjM5MzM5LDgxLjUwNzkyNCAxNy4xNTgwNzUsODEuNTAwMDAxIDE0Ljc1LDgxLjUgTCAzLjUsODEuNSBDIDUuMzczNTg4NCw3OC4zOTE1NjYgOC4yNSw3Mi40NTA2NSA4LjI1LDY0LjUgQyA4LjI1LDU2LjUyNjY0NiA1LjM0MTQ2ODYsNTAuNTk5ODE1IDMuNDY4NzUsNDcuNSB6IgogICAgICAgICBpZD0icGF0aDQ5NzMiCiAgICAgICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NzY2NjY3NjY2NjY2NjY2NzY2NzYyIgLz4KICAgIDwvZz4KICAgIDxwYXRoCiAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICBzdHlsZT0iZmlsbDpub25lO2ZpbGwtb3BhY2l0eToxO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDozO3N0cm9rZS1saW5lam9pbjptaXRlcjttYXJrZXI6bm9uZTtzdHJva2Utb3BhY2l0eToxO3Zpc2liaWxpdHk6dmlzaWJsZTtkaXNwbGF5OmlubGluZTtvdmVyZmxvdzp2aXNpYmxlO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGUiCiAgICAgICBpZD0icGF0aDM1NTEiCiAgICAgICBzb2RpcG9kaTpjeD0iNzUiCiAgICAgICBzb2RpcG9kaTpjeT0iMjUiCiAgICAgICBzb2RpcG9kaTpyeD0iNCIKICAgICAgIHNvZGlwb2RpOnJ5PSI0IgogICAgICAgZD0iTSA3OSwyNSBBIDQsNCAwIDEgMSA3MSwyNSBBIDQsNCAwIDEgMSA3OSwyNSB6IiAvPgogIDwvZz4KPC9zdmc+Cg=="}}},{operation:function(t,e){return(!t||!e)&&(t||e)}}),Ic={Gate:nc,IO:rc,Input:oc,Output:ac,Gate11:sc,Gate21:lc,Repeater:cc,Not:uc,Or:hc,And:gc,Nor:dc,Nand:fc,Xor:pc,Xnor:mc,Wire:Ma.define("logic.Wire",{attrs:{".connection":{"stroke-width":2},".marker-vertex":{r:7}},router:{name:"orthogonal"},connector:{name:"rounded",args:{radius:10}}},{arrowheadMarkup:['','',""].join(""),vertexMarkup:['','','','','',"Remove vertex.","","",""].join("")})},vc={KingWhite:Nl.define("chess.KingWhite",{size:{width:42,height:38}},{markup:' '}),KingBlack:Nl.define("chess.KingBlack",{size:{width:42,height:38}},{markup:' '}),QueenWhite:Nl.define("chess.QueenWhite",{size:{width:42,height:38}},{markup:' '}),QueenBlack:Nl.define("chess.QueenBlack",{size:{width:42,height:38}},{markup:' '}),RookWhite:Nl.define("chess.RookWhite",{size:{width:32,height:34}},{markup:' '}),RookBlack:Nl.define("chess.RookBlack",{size:{width:32,height:34}},{markup:' '}),BishopWhite:Nl.define("chess.BishopWhite",{size:{width:38,height:38}},{markup:' '}),BishopBlack:Nl.define("chess.BishopBlack",{size:{width:38,height:38}},{markup:' '}),KnightWhite:Nl.define("chess.KnightWhite",{size:{width:38,height:37}},{markup:' '}),KnightBlack:Nl.define("chess.KnightBlack",{size:{width:38,height:37}},{markup:' '}),PawnWhite:Nl.define("chess.PawnWhite",{size:{width:28,height:33}},{markup:''}),PawnBlack:Nl.define("chess.PawnBlack",{size:{width:28,height:33}},{markup:''})},Ac=Ta.define("erd.Entity",{size:{width:150,height:60},attrs:{".outer":{fill:"#2ECC71",stroke:"#27AE60","stroke-width":2,points:"100,0 100,60 0,60 0,0"},".inner":{fill:"#2ECC71",stroke:"#27AE60","stroke-width":2,points:"95,5 95,55 5,55 5,5",display:"none"},text:{text:"Entity","font-family":"Arial","font-size":14,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Cc=Ac.define("erd.WeakEntity",{attrs:{".inner":{display:"auto"},text:{text:"Weak Entity"}}}),wc=Ta.define("erd.Relationship",{size:{width:80,height:80},attrs:{".outer":{fill:"#3498DB",stroke:"#2980B9","stroke-width":2,points:"40,0 80,40 40,80 0,40"},".inner":{fill:"#3498DB",stroke:"#2980B9","stroke-width":2,points:"40,5 75,40 40,75 5,40",display:"none"},text:{text:"Relationship","font-family":"Arial","font-size":12,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),yc=wc.define("erd.IdentifyingRelationship",{attrs:{".inner":{display:"auto"},text:{text:"Identifying"}}}),bc=Ta.define("erd.Attribute",{size:{width:100,height:50},attrs:{ellipse:{transform:"translate(50, 25)"},".outer":{stroke:"#D35400","stroke-width":2,cx:0,cy:0,rx:50,ry:25,fill:"#E67E22"},".inner":{stroke:"#D35400","stroke-width":2,cx:0,cy:0,rx:45,ry:20,fill:"#E67E22",display:"none"},text:{"font-family":"Arial","font-size":14,"ref-x":.5,"ref-y":.5,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Nc=bc.define("erd.Multivalued",{attrs:{".inner":{display:"block"},text:{text:"multivalued"}}}),xc=bc.define("erd.Derived",{attrs:{".outer":{"stroke-dasharray":"3,5"},text:{text:"derived"}}}),Mc=bc.define("erd.Key",{attrs:{ellipse:{"stroke-width":4},text:{text:"key","font-weight":"800","text-decoration":"underline"}}}),Sc=bc.define("erd.Normal",{attrs:{text:{text:"Normal"}}}),kc={Entity:Ac,WeakEntity:Cc,Relationship:wc,IdentifyingRelationship:yc,Attribute:bc,Multivalued:Nc,Derived:xc,Key:Mc,Normal:Sc,ISA:Ta.define("erd.ISA",{type:"erd.ISA",size:{width:100,height:50},attrs:{polygon:{points:"0,0 50,50 100,0",fill:"#F1C40F",stroke:"#F39C12","stroke-width":2},text:{text:"ISA","font-size":18,"ref-x":.5,"ref-y":.3,"y-alignment":"middle","text-anchor":"middle"}}},{markup:''}),Line:Ma.define("erd.Line",{},{cardinality:function(t){this.set("labels",[{position:-20,attrs:{text:{dy:-8,text:t}}}])}})},Dc={State:kl.define("fsa.State",{attrs:{circle:{"stroke-width":3},text:{"font-weight":"800"}}}),StartState:Ta.define("fsa.StartState",{size:{width:20,height:20},attrs:{circle:{transform:"translate(10, 10)",r:10,fill:"#000000"}}},{markup:''}),EndState:Ta.define("fsa.EndState",{size:{width:20,height:20},attrs:{".outer":{transform:"translate(10, 10)",r:10,fill:"#ffffff",stroke:"#000000"},".inner":{transform:"translate(10, 10)",r:6,fill:"#000000"}}},{markup:''}),Arrow:Ma.define("fsa.Arrow",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}},smooth:!0})},Tc={Member:Ta.define("org.Member",{size:{width:180,height:70},attrs:{rect:{width:170,height:60},".card":{fill:"#FFFFFF",stroke:"#000000","stroke-width":2,"pointer-events":"visiblePainted",rx:10,ry:10},image:{width:48,height:48,ref:".card","ref-x":10,"ref-y":5},".rank":{"text-decoration":"underline",ref:".card","ref-x":.9,"ref-y":.2,"font-family":"Courier New","font-size":14,"text-anchor":"end"},".name":{"font-weight":"800",ref:".card","ref-x":.9,"ref-y":.6,"font-family":"Courier New","font-size":14,"text-anchor":"end"}}},{markup:''}),Arrow:Ma.define("org.Arrow",{source:{selector:".card"},target:{selector:".card"},attrs:{".connection":{stroke:"#585858","stroke-width":3}},z:-1})},Lc={Place:Nl.define("pn.Place",{size:{width:50,height:50},attrs:{".root":{r:25,fill:"#ffffff",stroke:"#000000",transform:"translate(25, 25)"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:".root",fill:"#000000","font-size":12},".tokens > circle":{fill:"#000000",r:5},".tokens.one > circle":{transform:"translate(25, 25)"},".tokens.two > circle:nth-child(1)":{transform:"translate(19, 25)"},".tokens.two > circle:nth-child(2)":{transform:"translate(31, 25)"},".tokens.three > circle:nth-child(1)":{transform:"translate(18, 29)"},".tokens.three > circle:nth-child(2)":{transform:"translate(25, 19)"},".tokens.three > circle:nth-child(3)":{transform:"translate(32, 29)"},".tokens.alot > text":{transform:"translate(25, 18)","text-anchor":"middle",fill:"#000000"}}},{markup:''}),PlaceView:Fa.extend({presentationAttributes:Fa.addPresentationAttributes({tokens:["TOKENS"]}),initFlag:Fa.prototype.initFlag.concat(["TOKENS"]),confirmUpdate:function(){for(var t,e=[],i=arguments.length;i--;)e[i]=arguments[i];var n=(t=Fa.prototype.confirmUpdate).call.apply(t,[this].concat(e));return this.hasFlag(n,"TOKENS")&&(this.renderTokens(),this.update(),n=this.removeFlag(n,"TOKENS")),n},renderTokens:function(){var e=this.vel.findOne(".tokens").empty();["one","two","three","alot"].forEach(function(t){e.removeClass(t)});var t=this.model.get("tokens");if(t)switch(t){case 1:e.addClass("one"),e.append(yr("circle"));break;case 2:e.addClass("two"),e.append([yr("circle"),yr("circle")]);break;case 3:e.addClass("three"),e.append([yr("circle"),yr("circle"),yr("circle")]);break;default:e.addClass("alot"),e.append(yr("text").text(t+""))}}}),Transition:Nl.define("pn.Transition",{size:{width:12,height:50},attrs:{rect:{width:12,height:50,fill:"#000000",stroke:"#000000"},".label":{"text-anchor":"middle","ref-x":.5,"ref-y":-20,ref:"rect",fill:"#000000","font-size":12}}},{markup:''}),Link:Ma.define("pn.Link",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z"}}})},zc=Nl.define("uml.Class",{attrs:{rect:{width:200},".uml-class-name-rect":{stroke:"black","stroke-width":2,fill:"#3498db"},".uml-class-attrs-rect":{stroke:"black","stroke-width":2,fill:"#2980b9"},".uml-class-methods-rect":{stroke:"black","stroke-width":2,fill:"#2980b9"},".uml-class-name-text":{ref:".uml-class-name-rect","ref-y":.5,"ref-x":.5,"text-anchor":"middle","y-alignment":"middle","font-weight":"bold",fill:"black","font-size":12,"font-family":"Times New Roman"},".uml-class-attrs-text":{ref:".uml-class-attrs-rect","ref-y":5,"ref-x":5,fill:"black","font-size":12,"font-family":"Times New Roman"},".uml-class-methods-text":{ref:".uml-class-methods-rect","ref-y":5,"ref-x":5,fill:"black","font-size":12,"font-family":"Times New Roman"}},name:[],attributes:[],methods:[]},{markup:['','','',"",'',""].join(""),initialize:function(){this.on("change:name change:attributes change:methods",function(){this.updateRectangles(),this.trigger("uml-update")},this),this.updateRectangles(),Nl.prototype.initialize.apply(this,arguments)},getClassName:function(){return this.get("name")},updateRectangles:function(){var n=this.get("attrs"),t=[{type:"name",text:this.getClassName()},{type:"attrs",text:this.get("attributes")},{type:"methods",text:this.get("methods")}],r=0;t.forEach(function(t){var e=Array.isArray(t.text)?t.text:[t.text],i=20*e.length+20;n[".uml-class-"+t.type+"-text"].text=e.join("\n"),n[".uml-class-"+t.type+"-rect"].height=i,n[".uml-class-"+t.type+"-rect"].transform="translate(0,"+r+")",r+=i})}}),Pc=Fa.extend({initialize:function(){Fa.prototype.initialize.apply(this,arguments),this.listenTo(this.model,"uml-update",function(){this.update(),this.resize()})}}),jc=zc.define("uml.Abstract",{attrs:{".uml-class-name-rect":{fill:"#e74c3c"},".uml-class-attrs-rect":{fill:"#c0392b"},".uml-class-methods-rect":{fill:"#c0392b"}}},{getClassName:function(){return["<>",this.get("name")]}}),Zc=Pc,Gc=zc.define("uml.Interface",{attrs:{".uml-class-name-rect":{fill:"#f1c40f"},".uml-class-attrs-rect":{fill:"#f39c12"},".uml-class-methods-rect":{fill:"#f39c12"}}},{getClassName:function(){return["<>",this.get("name")]}}),Bc={basic:Ol,standard:ql,devs:ic,logic:Ic,chess:vc,erd:kc,fsa:Dc,org:Tc,pn:Lc,uml:{Class:zc,ClassView:Pc,Abstract:jc,AbstractView:Zc,Interface:Gc,InterfaceView:Pc,Generalization:Ma.define("uml.Generalization",{attrs:{".marker-target":{d:"M 20 0 L 0 10 L 20 20 z",fill:"white"}}}),Implementation:Ma.define("uml.Implementation",{attrs:{".marker-target":{d:"M 20 0 L 0 10 L 20 20 z",fill:"white"},".connection":{"stroke-dasharray":"3,3"}}}),Aggregation:Ma.define("uml.Aggregation",{attrs:{".marker-target":{d:"M 40 10 L 20 20 L 0 10 L 20 0 z",fill:"white"}}}),Composition:Ma.define("uml.Composition",{attrs:{".marker-target":{d:"M 40 10 L 20 20 L 0 10 L 20 0 z",fill:"black"}}}),Association:Ma.define("uml.Association"),State:Nl.define("uml.State",{attrs:{".uml-state-body":{width:200,height:200,rx:10,ry:10,fill:"#ecf0f1",stroke:"#bdc3c7","stroke-width":3},".uml-state-separator":{stroke:"#bdc3c7","stroke-width":2},".uml-state-name":{ref:".uml-state-body","ref-x":.5,"ref-y":5,"text-anchor":"middle",fill:"#000000","font-family":"Courier New","font-size":14},".uml-state-events":{ref:".uml-state-separator","ref-x":5,"ref-y":5,fill:"#000000","font-family":"Courier New","font-size":14}},name:"State",events:[]},{markup:['','','',"",'','','',""].join(""),initialize:function(){this.on({"change:name":this.updateName,"change:events":this.updateEvents,"change:size":this.updatePath},this),this.updateName(),this.updateEvents(),this.updatePath(),Nl.prototype.initialize.apply(this,arguments)},updateName:function(){this.attr(".uml-state-name/text",this.get("name"))},updateEvents:function(){this.attr(".uml-state-events/text",this.get("events").join("\n"))},updatePath:function(){var t="M 0 20 L "+this.get("size").width+" 20";this.attr(".uml-state-separator/d",t,{silent:!0})}}),StartState:kl.define("uml.StartState",{type:"uml.StartState",attrs:{circle:{fill:"#34495e",stroke:"#2c3e50","stroke-width":2,rx:1}}}),EndState:Nl.define("uml.EndState",{size:{width:20,height:20},attrs:{"circle.outer":{transform:"translate(10, 10)",r:10,fill:"#ffffff",stroke:"#2c3e50"},"circle.inner":{transform:"translate(10, 10)",r:6,fill:"#34495e"}}},{markup:''}),Transition:Ma.define("uml.Transition",{attrs:{".marker-target":{d:"M 10 0 L 0 5 L 10 10 z",fill:"#34495e",stroke:"#2c3e50"},".connection":{stroke:"#2c3e50"}}})}};function Oc(t,e){return 0===e?"0%":Math.round(t/e*100)+"%"}function Ec(r){return function(t,e,i,n){return(e.isNodeConnection(i)?Yc:Rc)(r,t,e,i,n)}}function Rc(t,e,i,n,r){var o=i.model.angle(),a=i.getNodeUnrotatedBBox(n),s=i.model.getBBox().center();r.rotate(s,o);var l=r.x-a.x,c=r.y-a.y;return t&&(l=Oc(l,a.width),c=Oc(c,a.height)),e.anchor={name:"topLeft",args:{dx:l,dy:c,rotate:!0}},e}function Yc(t,e,i,n,r){var o=i.getConnection();if(!o)return e;var a=o.closestPointLength(r);if(t){var s=o.length();e.anchor={name:"connectionRatio",args:{ratio:a/s}}}else e.anchor={name:"connectionLength",args:{length:a}};return e}var Wc=Bo,Uc=Ec(!1),Fc=Ec(!0),Vc={useDefaults:Wc,pinAbsolute:Uc,pinRelative:Fc};function Xc(t,e,i){return Fc.call(this.paper,{},e,i,t,this.model).anchor}function Hc(t,e){var i=t.model;return e?i.getBBox():i.isLink()?t.getConnection().bbox():t.getNodeUnrotatedBBox(t.el)}var Jc=ja.extend({tagName:"circle",svgElement:!0,className:"marker-vertex",events:{mousedown:"onPointerDown",touchstart:"onPointerDown",dblclick:"onDoubleClick",dbltap:"onDoubleClick"},documentEvents:{mousemove:"onPointerMove",touchmove:"onPointerMove",mouseup:"onPointerUp",touchend:"onPointerUp",touchcancel:"onPointerUp"},attributes:{r:6,fill:"#33334F",stroke:"#FFFFFF","stroke-width":2,cursor:"move"},position:function(t,e){this.vel.attr({cx:t,cy:e})},onPointerDown:function(t){this.options.guard(t)||(t.stopPropagation(),t.preventDefault(),this.options.paper.undelegateEvents(),this.delegateDocumentEvents(null,t.data),this.trigger("will-change",this,t))},onPointerMove:function(t){this.trigger("changing",this,t)},onDoubleClick:function(t){this.trigger("remove",this,t)},onPointerUp:function(t){this.trigger("changed",this,t),this.undelegateDocumentEvents(),this.options.paper.delegateEvents()}}),_c=Al.extend({name:"vertices",options:{handleClass:Jc,snapRadius:20,redundancyRemoval:!0,vertexAdding:!0,stopPropagation:!0},children:[{tagName:"path",selector:"connection",className:"joint-vertices-path",attributes:{fill:"none",stroke:"transparent","stroke-width":10,cursor:"cell"}}],handles:null,events:{"mousedown .joint-vertices-path":"onPathPointerDown","touchstart .joint-vertices-path":"onPathPointerDown"},onRender:function(){return this.options.vertexAdding&&(this.renderChildren(),this.updatePath()),this.resetHandles(),this.renderHandles(),this},update:function(){return this.relatedView.model.vertices().length===this.handles.length?this.updateHandles():(this.resetHandles(),this.renderHandles()),this.options.vertexAdding&&this.updatePath(),this},resetHandles:function(){var t=this.handles;if(this.handles=[],this.stopListening(),Array.isArray(t))for(var e=0,i=t.length;e