diff --git a/dist/css/ueb-style.css b/dist/css/ueb-style.css
index 51ec9df..aaacaa6 100644
--- a/dist/css/ueb-style.css
+++ b/dist/css/ueb-style.css
@@ -1 +1 @@
-@font-face{font-family:"Roboto";font-style:light;src:url("../font/roboto-light.woff2") format("woff2"),url("../font/roboto-light.woff") format("woff")}@font-face{font-family:"Roboto";font-style:regular;src:url("../font/roboto-regular.woff2") format("woff2"),url("../font/roboto-regular.woff") format("woff")}ueb-blueprint{display:block;position:relative;font-family:Roboto,Noto,Oxygen,Ubuntu,"Open Sans","Helvetica Neue",sans-serif;font-size:var(--ueb-font-size);user-select:none}.ueb-viewport-header{display:flex;position:absolute;top:0;right:0;left:0;height:1.5em;background:rgba(0,0,0,.5);z-index:1}.ueb-viewport-zoom{margin-left:auto;color:#4d4d4db7;font-size:20px}.ueb-viewport-body{position:relative;height:var(--ueb-height, 30rem);overflow:hidden;scrollbar-width:0}ueb-blueprint[data-focused=true] .ueb-viewport-body{overflow:scroll}.ueb-grid{--ueb-grid-line-actual-width: calc(var(--ueb-grid-line-width) / var(--ueb-scale));position:absolute;min-width:100%;min-height:100%;width:calc((100% + var(--ueb-additional-x)*1px)/var(--ueb-scale));height:calc((100% + var(--ueb-additional-y)*1px)/var(--ueb-scale));background-color:#262626;background-image:linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(to right, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to right, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent);background-size:100% var(--ueb-grid-line-actual-width),var(--ueb-grid-line-actual-width) 100%,calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size);background-position:calc(var(--ueb-translate-x)*1px) calc(var(--ueb-translate-y)*1px);background-repeat:repeat-x,repeat-y,repeat,repeat,repeat,repeat;transform:scale(var(--ueb-scale), var(--ueb-scale));transform-origin:0 0;overflow:hidden}ueb-blueprint[data-drag-scrolling=true] .ueb-grid{cursor:grabbing}ueb-blueprint[data-drag-scrolling=false] .ueb-grid{cursor:default}.ueb-zoom--.ueb,.ueb{--ueb-scale: 1;--ueb-grid-actual-size: var(--ueb-grid-size)}.ueb-zoom-7.ueb{--ueb-scale: 2}.ueb-zoom-6.ueb{--ueb-scale: 1.875}.ueb-zoom-5.ueb{--ueb-scale: 1.75}.ueb-zoom-4.ueb{--ueb-scale: 1.675}.ueb-zoom-3.ueb{--ueb-scale: 1.5}.ueb-zoom-2.ueb{--ueb-scale: 1.375}.ueb-zoom-1.ueb{--ueb-scale: 1.25}.ueb-zoom--1.ueb{--ueb-scale: 0.875}.ueb-zoom--2.ueb{--ueb-scale: 0.75}.ueb-zoom--3.ueb{--ueb-scale: 0.675}.ueb-zoom--4.ueb{--ueb-scale: 0.5;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--5.ueb{--ueb-scale: 0.375;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--6.ueb{--ueb-scale: 0.333333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--7.ueb{--ueb-scale: 0.3;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--8.ueb{--ueb-scale: 0.266666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--9.ueb{--ueb-scale: 0.233333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--10.ueb{--ueb-scale: 0.2;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--11.ueb{--ueb-scale: 0.166666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-zoom--12.ueb{--ueb-scale: 0.133333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-grid-content{position:relative;width:0;height:0;transform:translateX(calc(var(--ueb-translate-x) * 1px)) translateY(calc(var(--ueb-translate-y) * 1px))}.ueb-positioned,ueb-blueprint[data-selecting=true] ueb-selector{--ueb-computed-min-x: min(var(--ueb-from-x), var(--ueb-to-x));--ueb-computed-max-x: max(var(--ueb-from-x), var(--ueb-to-x));--ueb-computed-min-y: min(var(--ueb-from-y), var(--ueb-to-y));--ueb-computed-max-y: max(var(--ueb-from-y), var(--ueb-to-y));--ueb-computed-width: max(var(--ueb-from-x) - var(--ueb-to-x), var(--ueb-to-x) - var(--ueb-from-x));--ueb-computed-height: max(var(--ueb-from-y) - var(--ueb-to-y), var(--ueb-to-y) - var(--ueb-from-y));position:absolute;top:calc(var(--ueb-computed-min-y)*1px);left:calc(var(--ueb-computed-min-x)*1px);width:calc(var(--ueb-computed-width)*1px);height:calc(var(--ueb-computed-height)*1px)}ueb-selector{display:block;position:absolute;visibility:hidden;top:0;left:0;width:0;height:0;background-image:repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(1px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale)));background-size:100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%,calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%;background-position:0 calc(1px/var(--ueb-scale)),0 0,0 calc(100% - 1px/var(--ueb-scale)),0 100%,calc(1px/var(--ueb-scale)) 0,0 0,calc(100% - 1px/var(--ueb-scale)) 0,100% 0;background-repeat:no-repeat}ueb-blueprint[data-selecting=true] ueb-selector{visibility:visible}ueb-selector>*{visibility:visible}ueb-node{display:block;position:absolute;transform:translateX(calc(var(--ueb-position-x) * 1px)) translateY(calc(var(--ueb-position-y) * 1px));border-radius:var(--ueb-node-radius);box-shadow:0 0 1px 0 #000,1px 4px 6px 0 rgba(0,0,0,.3)}ueb-blueprint[data-drag-scrolling=false][data-selecting=false] ueb-node{cursor:move}.ueb-node-border{margin:-3px;padding:3px;border-radius:calc(var(--ueb-node-radius)*1.4)}.ueb-selected>.ueb-node-border{background-image:linear-gradient(to right, #f1b000 0%, #f1b000 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%),linear-gradient(to right, #cc6700 0%, #cc6700 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%);background-size:100% 7px,7px 100%,100% 7px,7px 100%;background-position:top,right,bottom,left;background-repeat:repeat-x,repeat-y,repeat-x,repeat-y;outline:3px solid #cc6700;outline-offset:-6px}.ueb-node-content{position:relative;padding:1px;box-shadow:inset 0 0 2px 0 #000;border-radius:var(--ueb-node-radius);background:rgba(10,10,10,.8);overflow:hidden}.ueb-node-header{padding:.3em .7em;box-shadow:inset 0 1px 2px 0 #313631,inset 0 2px 0 0 #92c381;border-radius:var(--ueb-node-radius) var(--ueb-node-radius) 0 0;background:linear-gradient(170deg, #5f815a 0%, #5f815a 50%, transparent 100%);color:silver;font-weight:600;white-space:nowrap}.ueb-node-name{background:radial-gradient(closest-side, rgba(0, 0, 0, 0.5) 0%, transparent 90%);margin:-0.1em -1.6em;padding:.1em 1.6em}.ueb-node-body{display:flex;padding:1px 0;color:#fff;font-weight:100;white-space:nowrap}.ueb-node-inputs{margin-right:auto;padding-left:8px}.ueb-node-outputs{padding-right:8px}.ueb-node-expand{display:none}ueb-node[data-advanced-display] .ueb-node-expand{display:block}ueb-pin{display:block;margin:6px 0;padding:2px 2px}ueb-pin>*{display:inline-block;vertical-align:middle}ueb-node[data-advanced-display=Hidden] ueb-pin[data-advanced-view=true]{display:none}ueb-blueprint[data-drag-scrolling=false][data-selecting=false] ueb-pin:hover{background:var(--ueb-pin-background);cursor:crosshair}.ueb-node-outputs ueb-pin{text-align:right}.ueb-pin-icon-exec{display:inline-block;vertical-align:text-top}.ueb-pin-icon-value{display:inline-block;position:relative;width:1em;height:1em;vertical-align:baseline;margin:0 .4em -1px .1em}.ueb-pin-icon-value::before{content:"";display:block;position:absolute;top:0;right:0;bottom:0;left:0;border:2px solid var(--ueb-pin-color);border-radius:50%}ueb-pin.ueb-pin-fill .ueb-pin-icon-value::before{background:var(--ueb-pin-color)}.ueb-pin-icon-value::after{content:"";display:block;position:absolute;top:calc(50% - .3em);left:calc(100% + 1px);width:0;height:0;border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid var(--ueb-pin-color)}.ueb-pin-content>span{display:inline-block;vertical-align:middle}.ueb-pin-exec .ueb-pin-name{display:none}.ueb-pin-input{margin-left:3px;border:1px solid #a0a0a0;border-radius:3px;padding:1px 0 0 3px;color:silver}.ueb-pin-input:hover,.ueb-pin-input:active,.ueb-pin-input:focus,.ueb-pin-input:focus-within{background:#ffffff46;outline:none}.ueb-pin-input-content{display:block;outline:none;border:none;margin:0 3px 1px 0;padding:0;min-width:10px;max-width:400px;max-height:16em;background:none;cursor:text;overflow:auto}.ueb-pin-input-content::-webkit-scrollbar{width:10px;height:10px}.ueb-pin-input-content::-webkit-scrollbar-thumb{background:#575757;border-radius:10px;margin:4px}ueb-link{--ueb-from-input-coefficient: calc(2 * var(--ueb-from-input) - 1);--ueb-y-opposite: clamp(0, var(--ueb-from-y) - var(--ueb-to-y) - 1, 1);display:block;min-width:calc(var(--ueb-link-min-width)*1px);visibility:hidden}ueb-link svg{--ueb-y-opposite-coefficient: calc(2* var(--ueb-y-opposite) - 1);position:absolute;top:0;left:0;width:100%;height:100%;min-height:1px;transform:scaleY(calc(var(--ueb-y-opposite-coefficient) * var(--ueb-from-input-coefficient)));overflow:visible}ueb-link svg path{visibility:visible;stroke:var(--ueb-pin-color);stroke-width:1}ueb-link.ueb-link-dragging svg path,ueb-link svg g:hover path{stroke-width:5;transition:stroke-width .8s}ueb-link-message{display:block;visibility:visible;position:absolute;top:calc(100%*(1 - var(--ueb-y-opposite)) + 22px);left:calc((1 - var(--ueb-from-input))*100% + (var(--ueb-from-input-coefficient))*var(--ueb-start-percentage) + 15px);border:1px solid #000;padding:4px 8px;border-radius:2px;background:linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%);color:var(--ueb-pin-dim-color);white-space:nowrap;z-index:1000000}.ueb{--ueb-pin-color: white;--ueb-pin-dim-color: #afafaf}.ueb-pin-boolean{--ueb-pin-color: #4d0000}.ueb-pin-class{--ueb-pin-color: #5800bb}.ueb-pin-float{--ueb-pin-color: #9ffb44}.ueb-pin-int{--ueb-pin-color: #1fe0ad}.ueb-pin-name{--ueb-pin-color: #cb81fc}.ueb-pin-object{--ueb-pin-color: #006603}.ueb-pin-rotator{--ueb-pin-color: #9eb1fc}.ueb-pin-string{--ueb-pin-color: #fc00d2;--ueb-pin-background: linear-gradient(90deg, #fc00d220, #fc00d280 15%, #fc00d250 85%, transparent)}.ueb-pin-vector{--ueb-pin-color: #fcc823}/*# sourceMappingURL=ueb-style.css.map */
+@font-face{font-family:"Roboto";font-style:light;src:url("../font/roboto-light.woff2") format("woff2"),url("../font/roboto-light.woff") format("woff")}@font-face{font-family:"Roboto";font-style:regular;src:url("../font/roboto-regular.woff2") format("woff2"),url("../font/roboto-regular.woff") format("woff")}ueb-blueprint{display:block;position:relative;font-family:Roboto,Noto,Oxygen,Ubuntu,"Open Sans","Helvetica Neue",sans-serif;font-size:var(--ueb-font-size);user-select:none}.ueb-viewport-header{display:flex;position:absolute;top:0;right:0;left:0;height:1.5em;background:rgba(0,0,0,.5);z-index:1}.ueb-viewport-zoom{margin-left:auto;color:#4d4d4db7;font-size:20px}.ueb-viewport-body{position:relative;height:var(--ueb-height, 30rem);overflow:hidden;scrollbar-width:0}ueb-blueprint[data-focused=true] .ueb-viewport-body{overflow:scroll}.ueb-grid{--ueb-grid-line-actual-width: calc(var(--ueb-grid-line-width) / var(--ueb-scale));position:absolute;min-width:100%;min-height:100%;width:calc((100% + var(--ueb-additional-x)*1px)/var(--ueb-scale));height:calc((100% + var(--ueb-additional-y)*1px)/var(--ueb-scale));background-color:#262626;background-image:linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(var(--ueb-grid-axis-line-color), var(--ueb-grid-axis-line-color)),linear-gradient(to right, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-set-line-color), var(--ueb-grid-set-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to right, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent),linear-gradient(to bottom, var(--ueb-grid-line-color), var(--ueb-grid-line-color) var(--ueb-grid-line-actual-width), transparent var(--ueb-grid-line-actual-width), transparent);background-size:100% var(--ueb-grid-line-actual-width),var(--ueb-grid-line-actual-width) 100%,calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)) calc(var(--ueb-grid-set)*var(--ueb-grid-actual-size)),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size),var(--ueb-grid-actual-size) var(--ueb-grid-actual-size);background-position:calc(var(--ueb-translate-x)*1px) calc(var(--ueb-translate-y)*1px);background-repeat:repeat-x,repeat-y,repeat,repeat,repeat,repeat;transform:scale(var(--ueb-scale), var(--ueb-scale));transform-origin:0 0;overflow:hidden}ueb-blueprint[data-drag-scrolling=true] .ueb-grid{cursor:grabbing}ueb-blueprint[data-drag-scrolling=false] .ueb-grid{cursor:default}.ueb-zoom--.ueb,.ueb{--ueb-scale: 1;--ueb-grid-actual-size: var(--ueb-grid-size)}.ueb-zoom-7.ueb{--ueb-scale: 2}.ueb-zoom-6.ueb{--ueb-scale: 1.875}.ueb-zoom-5.ueb{--ueb-scale: 1.75}.ueb-zoom-4.ueb{--ueb-scale: 1.675}.ueb-zoom-3.ueb{--ueb-scale: 1.5}.ueb-zoom-2.ueb{--ueb-scale: 1.375}.ueb-zoom-1.ueb{--ueb-scale: 1.25}.ueb-zoom--1.ueb{--ueb-scale: 0.875}.ueb-zoom--2.ueb{--ueb-scale: 0.75}.ueb-zoom--3.ueb{--ueb-scale: 0.675}.ueb-zoom--4.ueb{--ueb-scale: 0.5;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--5.ueb{--ueb-scale: 0.375;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 2)}.ueb-zoom--6.ueb{--ueb-scale: 0.333333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--7.ueb{--ueb-scale: 0.3;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--8.ueb{--ueb-scale: 0.266666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--9.ueb{--ueb-scale: 0.233333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--10.ueb{--ueb-scale: 0.2;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 3)}.ueb-zoom--11.ueb{--ueb-scale: 0.166666;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-zoom--12.ueb{--ueb-scale: 0.133333;--ueb-grid-actual-size: calc(var(--ueb-grid-size) * 6)}.ueb-grid-content{position:relative;width:0;height:0;transform:translateX(calc(var(--ueb-translate-x) * 1px)) translateY(calc(var(--ueb-translate-y) * 1px))}.ueb-positioned,ueb-blueprint[data-selecting=true] ueb-selector{--ueb-computed-min-x: min(var(--ueb-from-x), var(--ueb-to-x));--ueb-computed-max-x: max(var(--ueb-from-x), var(--ueb-to-x));--ueb-computed-min-y: min(var(--ueb-from-y), var(--ueb-to-y));--ueb-computed-max-y: max(var(--ueb-from-y), var(--ueb-to-y));--ueb-computed-width: max(var(--ueb-from-x) - var(--ueb-to-x), var(--ueb-to-x) - var(--ueb-from-x));--ueb-computed-height: max(var(--ueb-from-y) - var(--ueb-to-y), var(--ueb-to-y) - var(--ueb-from-y));position:absolute;top:calc(var(--ueb-computed-min-y)*1px);left:calc(var(--ueb-computed-min-x)*1px);width:calc(var(--ueb-computed-width)*1px);height:calc(var(--ueb-computed-height)*1px)}ueb-selector{display:block;position:absolute;visibility:hidden;top:0;left:0;width:0;height:0;background-image:repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(90deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(1px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(180deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, transparent, transparent calc(1px / var(--ueb-scale)), white calc(2px / var(--ueb-scale)), white calc(7px / var(--ueb-scale)), transparent calc(7px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale))),repeating-linear-gradient(0deg, black, black calc(8px / var(--ueb-scale)), transparent calc(9px / var(--ueb-scale)), transparent calc(11px / var(--ueb-scale)));background-size:100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),100% calc(1px/var(--ueb-scale)),100% calc(3px/var(--ueb-scale)),calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%,calc(1px/var(--ueb-scale)) 100%,calc(3px/var(--ueb-scale)) 100%;background-position:0 calc(1px/var(--ueb-scale)),0 0,0 calc(100% - 1px/var(--ueb-scale)),0 100%,calc(1px/var(--ueb-scale)) 0,0 0,calc(100% - 1px/var(--ueb-scale)) 0,100% 0;background-repeat:no-repeat}ueb-blueprint[data-selecting=true] ueb-selector{visibility:visible}ueb-selector>*{visibility:visible}ueb-node{display:block;position:absolute;transform:translateX(calc(var(--ueb-position-x) * 1px)) translateY(calc(var(--ueb-position-y) * 1px));border-radius:var(--ueb-node-radius);box-shadow:0 0 1px 0 #000,1px 4px 6px 0 rgba(0,0,0,.3)}ueb-blueprint[data-drag-scrolling=false][data-selecting=false] ueb-node{cursor:move}.ueb-node-border{margin:-3px;padding:3px;border-radius:calc(var(--ueb-node-radius)*1.4)}.ueb-selected>.ueb-node-border{background-image:linear-gradient(to right, #f1b000 0%, #f1b000 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%),linear-gradient(to right, #cc6700 0%, #cc6700 100%),linear-gradient(to bottom, #f1b000 0%, #cc6700 100%);background-size:100% 7px,7px 100%,100% 7px,7px 100%;background-position:top,right,bottom,left;background-repeat:repeat-x,repeat-y,repeat-x,repeat-y;outline:3px solid #cc6700;outline-offset:-6px}.ueb-node-content{position:relative;padding:1px;box-shadow:inset 0 0 2px 0 #000;border-radius:var(--ueb-node-radius);background:rgba(10,10,10,.8);overflow:hidden}.ueb-node-header{padding:.3em .7em;box-shadow:inset 0 1px 2px 0 #313631,inset 0 2px 0 0 #92c381;border-radius:var(--ueb-node-radius) var(--ueb-node-radius) 0 0;background:linear-gradient(170deg, #5f815a 0%, #5f815a 50%, transparent 100%);color:silver;font-weight:600;white-space:nowrap}.ueb-node-name{background:radial-gradient(closest-side, rgba(0, 0, 0, 0.5) 0%, transparent 90%);margin:-0.1em -1.6em;padding:.1em 1.6em}.ueb-node-body{display:flex;padding:1px 0;color:#fff;font-weight:100;white-space:nowrap}.ueb-node-inputs{margin-right:auto;padding-left:8px}.ueb-node-outputs{padding-right:8px}.ueb-node-expand{display:none}ueb-node[data-advanced-display] .ueb-node-expand{display:block}ueb-pin{display:block;margin:6px 0;padding:2px 2px}ueb-pin>*{display:inline-block;vertical-align:middle}ueb-node[data-advanced-display=Hidden] ueb-pin[data-advanced-view=true]{display:none}ueb-blueprint[data-drag-scrolling=false][data-selecting=false] ueb-pin:hover{background:var(--ueb-pin-background);cursor:crosshair}.ueb-node-outputs ueb-pin{text-align:right}.ueb-pin-icon-exec{display:inline-block;vertical-align:text-top}.ueb-pin-icon-value{display:inline-block;position:relative;width:1em;height:1em;vertical-align:baseline;margin:0 .4em -1px .1em}.ueb-pin-icon-value::before{content:"";display:block;position:absolute;top:0;right:0;bottom:0;left:0;border:2px solid var(--ueb-pin-color);border-radius:50%}ueb-pin.ueb-pin-fill .ueb-pin-icon-value::before{background:var(--ueb-pin-color)}.ueb-pin-icon-value::after{content:"";display:block;position:absolute;top:calc(50% - .3em);left:calc(100% + 1px);width:0;height:0;border-top:.3em solid transparent;border-bottom:.3em solid transparent;border-left:.3em solid var(--ueb-pin-color)}.ueb-pin-content>span{display:inline-block;vertical-align:middle}.ueb-pin-exec .ueb-pin-name{display:none}.ueb-pin-input{margin-left:3px;border:1px solid #a0a0a0;border-radius:3px;padding:1px 0 0 3px;color:silver}.ueb-pin-input:hover,.ueb-pin-input:active,.ueb-pin-input:focus,.ueb-pin-input:focus-within{background:#ffffff46;outline:none}.ueb-pin-input-content{display:block;outline:none;border:none;margin:0 3px 1px 0;padding:0;min-width:10px;max-width:400px;max-height:16em;background:none;color:inherit;cursor:text;overflow:auto}.ueb-pin-input-content::-webkit-scrollbar{width:10px;height:10px}.ueb-pin-input-content::-webkit-scrollbar-thumb{background:#575757;border-radius:10px;margin:4px}ueb-link{--ueb-from-input-coefficient: calc(2 * var(--ueb-from-input) - 1);--ueb-y-opposite: clamp(0, var(--ueb-from-y) - var(--ueb-to-y) - 1, 1);display:block;min-width:calc(var(--ueb-link-min-width)*1px);visibility:hidden}ueb-link svg{--ueb-y-opposite-coefficient: calc(2* var(--ueb-y-opposite) - 1);position:absolute;top:0;left:0;width:100%;height:100%;min-height:1px;transform:scaleY(calc(var(--ueb-y-opposite-coefficient) * var(--ueb-from-input-coefficient)));overflow:visible}ueb-link svg path{visibility:visible;stroke:var(--ueb-pin-color);stroke-width:1}ueb-link.ueb-link-dragging svg path,ueb-link svg g:hover path{stroke-width:5;transition:stroke-width .8s}ueb-link-message{display:block;visibility:visible;position:absolute;top:calc(100%*(1 - var(--ueb-y-opposite)) + 22px);left:calc((1 - var(--ueb-from-input))*100% + (var(--ueb-from-input-coefficient))*var(--ueb-start-percentage) + 15px);border:1px solid #000;padding:4px 8px;border-radius:2px;background:linear-gradient(to bottom, #2a2a2a 0, #151515 50%, #2a2a2a 100%);color:var(--ueb-pin-dim-color);white-space:nowrap;z-index:1000000}.ueb{--ueb-pin-color: white;--ueb-pin-dim-color: #afafaf}.ueb-pin-boolean{--ueb-pin-color: #4d0000}.ueb-pin-class{--ueb-pin-color: #5800bb}.ueb-pin-float{--ueb-pin-color: #9ffb44}.ueb-pin-int{--ueb-pin-color: #1fe0ad}.ueb-pin-name{--ueb-pin-color: #cb81fc}.ueb-pin-object{--ueb-pin-color: #006603}.ueb-pin-rotator{--ueb-pin-color: #9eb1fc}.ueb-pin-string{--ueb-pin-color: #fc00d2;--ueb-pin-background: linear-gradient(90deg, #fc00d220, #fc00d280 15%, #fc00d250 85%, transparent)}.ueb-pin-vector{--ueb-pin-color: #fcc823}/*# sourceMappingURL=ueb-style.css.map */
diff --git a/dist/css/ueb-style.css.map b/dist/css/ueb-style.css.map
index 3c18732..453b71d 100644
--- a/dist/css/ueb-style.css.map
+++ b/dist/css/ueb-style.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["../../scss/style.scss","../../scss/ueb-node.scss","../../scss/ueb-pin.scss","../../scss/ueb-link.scss","../../scss/ueb-type-color.scss"],"names":[],"mappings":"AAAA,WACI,qBACA,iBACA,IACI,kGAIR,WACI,qBACA,mBACA,IACI,sGAIR,cACI,cACA,kBACA,8EACA,+BACA,iBAGJ,qBACI,aACA,kBACA,MACA,QACA,OACA,aACA,0BACA,UAGJ,mBACI,iBACA,gBACA,eAGJ,mBACI,kBACA,gCACA,gBACA,kBAGJ,oDACI,gBAGJ,UACI,kFACA,kBACA,eACA,gBACA,kEACA,mEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,oDACA,qBACA,gBAGJ,kDACI,gBAGJ,mDACI,eAGJ,qBAEI,eACA,6CAGJ,gBACI,eAGJ,gBACI,mBAGJ,gBACI,kBAGJ,gBACI,mBAGJ,gBACI,iBAGJ,gBACI,mBAGJ,gBACI,kBAGJ,iBACI,mBAGJ,iBACI,kBAGJ,iBACI,mBAGJ,iBACI,iBACA,uDAGJ,iBACI,mBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,iBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,sBACA,uDAGJ,kBACI,iBACA,uDAGJ,kBACI,sBACA,uDAGJ,kBACI,sBACA,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,gEACI,8DACA,8DACA,8DACA,8DACA,oGACA,qGACA,kBACA,wCACA,yCACA,0CACA,4CAGJ,aACI,cACA,kBACA,kBACA,MACA,OACA,QACA,SACA,iBAEI,wlDAmDJ,gBAEI,gQAWJ,oBAEI,wJAOJ,4BAGJ,gDACI,mBAIJ,eACI,mBC7TJ,SACI,cACA,kBACA,sGACA,qCACA,uDAGJ,wEACI,YAGJ,iBACI,YACA,YACA,+CAGJ,+BACI,iBACI,kNAIJ,oDACA,0CACA,sDACA,0BACA,oBAGJ,kBACI,kBACA,YACA,gCACA,qCACA,6BACA,gBAGJ,iBACI,kBACA,6DACA,gEACA,8EACA,aACA,gBACA,mBAGJ,eACI,iFACA,qBACA,mBAGJ,eACI,aACA,cACA,WACA,gBACA,mBAGJ,iBACI,kBACA,iBAGJ,kBACI,kBAGJ,iBACI,aAGJ,iDACI,cC9EJ,QACI,cACA,aACA,gBAEA,UACI,qBACA,sBAIR,wEACI,aAGJ,6EACI,qCACA,iBAGJ,0BACI,iBAGJ,mBACI,qBACA,wBAGJ,oBACI,qBACA,kBACA,UACA,WACA,wBACA,wBAGJ,4BACI,WACA,cACA,kBACA,MACA,QACA,SACA,OACA,sCACA,kBAGJ,iDACI,gCAGJ,2BACI,WACA,cACA,kBACA,qBACA,sBACA,QACA,SACA,kCACA,qCACA,4CAGJ,sBACI,qBACA,sBAGJ,4BACI,aAGJ,eACI,gBACA,yBACA,kBACA,oBACA,aAEA,4FAII,qBACA,aAIR,uBACI,cACA,aACA,YACA,mBACA,UACA,eACA,gBACA,gBACA,gBACA,YACA,cAEA,0CACI,WACA,YAGJ,gDACI,mBACA,mBACA,WCjHR,SACI,kEAEA,uEACA,cACA,8CAKA,kBAGJ,aACI,iEACA,kBACA,MACA,OACA,WACA,YACA,eACA,8FACA,iBAGJ,kBACI,mBACA,4BACA,eAGJ,8DAEI,eACA,4BAGJ,iBACI,cACA,mBACA,kBACA,kDACA,qHAOA,sBACA,gBACA,kBACA,4EACA,+BACA,mBACA,gBCvDJ,KAGI,uBACA,6BAGJ,iBAEI,yBAGJ,eAEI,yBAGJ,eAEI,yBAGJ,aAEI,yBAGJ,cAEI,yBAGJ,gBAGI,yBAGJ,iBAEI,yBAGJ,gBAEI,yBACA,mGAGJ,gBAEI","file":"ueb-style.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["../../scss/style.scss","../../scss/ueb-node.scss","../../scss/ueb-pin.scss","../../scss/ueb-link.scss","../../scss/ueb-type-color.scss"],"names":[],"mappings":"AAAA,WACI,qBACA,iBACA,IACI,kGAIR,WACI,qBACA,mBACA,IACI,sGAIR,cACI,cACA,kBACA,8EACA,+BACA,iBAGJ,qBACI,aACA,kBACA,MACA,QACA,OACA,aACA,0BACA,UAGJ,mBACI,iBACA,gBACA,eAGJ,mBACI,kBACA,gCACA,gBACA,kBAGJ,oDACI,gBAGJ,UACI,kFACA,kBACA,eACA,gBACA,kEACA,mEACA,yBACA,iBAEI,s3BA0BJ,gBAEI,sZAQJ,sFACA,gEACA,oDACA,qBACA,gBAGJ,kDACI,gBAGJ,mDACI,eAGJ,qBAEI,eACA,6CAGJ,gBACI,eAGJ,gBACI,mBAGJ,gBACI,kBAGJ,gBACI,mBAGJ,gBACI,iBAGJ,gBACI,mBAGJ,gBACI,kBAGJ,iBACI,mBAGJ,iBACI,kBAGJ,iBACI,mBAGJ,iBACI,iBACA,uDAGJ,iBACI,mBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,iBACA,uDAGJ,iBACI,sBACA,uDAGJ,iBACI,sBACA,uDAGJ,kBACI,iBACA,uDAGJ,kBACI,sBACA,uDAGJ,kBACI,sBACA,uDAGJ,kBACI,kBACA,QACA,SACA,wGAGJ,gEACI,8DACA,8DACA,8DACA,8DACA,oGACA,qGACA,kBACA,wCACA,yCACA,0CACA,4CAGJ,aACI,cACA,kBACA,kBACA,MACA,OACA,QACA,SACA,iBAEI,wlDAmDJ,gBAEI,gQAWJ,oBAEI,wJAOJ,4BAGJ,gDACI,mBAIJ,eACI,mBC7TJ,SACI,cACA,kBACA,sGACA,qCACA,uDAGJ,wEACI,YAGJ,iBACI,YACA,YACA,+CAGJ,+BACI,iBACI,kNAIJ,oDACA,0CACA,sDACA,0BACA,oBAGJ,kBACI,kBACA,YACA,gCACA,qCACA,6BACA,gBAGJ,iBACI,kBACA,6DACA,gEACA,8EACA,aACA,gBACA,mBAGJ,eACI,iFACA,qBACA,mBAGJ,eACI,aACA,cACA,WACA,gBACA,mBAGJ,iBACI,kBACA,iBAGJ,kBACI,kBAGJ,iBACI,aAGJ,iDACI,cC9EJ,QACI,cACA,aACA,gBAEA,UACI,qBACA,sBAIR,wEACI,aAGJ,6EACI,qCACA,iBAGJ,0BACI,iBAGJ,mBACI,qBACA,wBAGJ,oBACI,qBACA,kBACA,UACA,WACA,wBACA,wBAGJ,4BACI,WACA,cACA,kBACA,MACA,QACA,SACA,OACA,sCACA,kBAGJ,iDACI,gCAGJ,2BACI,WACA,cACA,kBACA,qBACA,sBACA,QACA,SACA,kCACA,qCACA,4CAGJ,sBACI,qBACA,sBAGJ,4BACI,aAGJ,eACI,gBACA,yBACA,kBACA,oBACA,aAEA,4FAII,qBACA,aAIR,uBACI,cACA,aACA,YACA,mBACA,UACA,eACA,gBACA,gBACA,gBACA,cACA,YACA,cAEA,0CACI,WACA,YAGJ,gDACI,mBACA,mBACA,WClHR,SACI,kEAEA,uEACA,cACA,8CAKA,kBAGJ,aACI,iEACA,kBACA,MACA,OACA,WACA,YACA,eACA,8FACA,iBAGJ,kBACI,mBACA,4BACA,eAGJ,8DAEI,eACA,4BAGJ,iBACI,cACA,mBACA,kBACA,kDACA,qHAOA,sBACA,gBACA,kBACA,4EACA,+BACA,mBACA,gBCvDJ,KAGI,uBACA,6BAGJ,iBAEI,yBAGJ,eAEI,yBAGJ,eAEI,yBAGJ,aAEI,yBAGJ,cAEI,yBAGJ,gBAGI,yBAGJ,iBAEI,yBAGJ,gBAEI,yBACA,mGAGJ,gBAEI","file":"ueb-style.css"}
\ No newline at end of file
diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js
index 7000f32..23eef2f 100755
--- a/dist/ueblueprint.js
+++ b/dist/ueblueprint.js
@@ -5,27 +5,27 @@
*/
const html = String.raw;
-// @ts-check
-
-/**
- * @typedef {import("../element/IElement").default} IElement
- */
-class ITemplate {
-
- /**
- * @param {IElement} entity
- */
- render(entity) {
- return ""
- }
-
- /**
- * @param {IElement} element
- */
- apply(element) {
- // TODO replace with the safer element.setHTML(...) when it will be availableBreack
- element.innerHTML = this.render(element);
- }
+// @ts-check
+
+/**
+ * @typedef {import("../element/IElement").default} IElement
+ */
+class ITemplate {
+
+ /**
+ * @param {IElement} entity
+ */
+ render(entity) {
+ return ""
+ }
+
+ /**
+ * @param {IElement} element
+ */
+ apply(element) {
+ // TODO replace with the safer element.setHTML(...) when it will be availableBreack
+ element.innerHTML = this.render(element);
+ }
}
// @ts-check
@@ -354,71 +354,71 @@ class FastSelectionModel {
}
}
-// @ts-check
-
-/**
- * @typedef {import("../Blueprint").default} Blueprint
- * @typedef {import("../entity/IEntity").default} IEntity
- * @typedef {import("../input/IContext").default} IContext
- * @typedef {import("../template/ITemplate").default} ITemplate
- */
-
-class IElement extends HTMLElement {
-
- static tagName = ""
-
- /** @type {Blueprint} */
- blueprint
-
- /** @type {IEntity} */
- entity
-
- /** @type {ITemplate} */
- template
-
- /** @type {IContext[]} */
- inputObjects = []
-
- constructor(entity, template) {
- super();
- this.blueprint = null;
- this.entity = entity;
- this.template = template;
- this.inputObjects = [];
- }
-
- getTemplate() {
- return this.template
- }
-
- connectedCallback() {
- this.blueprint = this.closest("ueb-blueprint");
- this.template.apply(this);
- this.inputObjects = this.createInputObjects();
- }
-
- disconnectedCallback() {
- this.inputObjects.forEach(v => v.unlistenDOMElement());
- }
-
- /** @param {IElement} element */
- isSameGraph(element) {
- return this.blueprint && this.blueprint == element?.blueprint
- }
-
- /**
- * @template {IContext} T
- * @param {new (...args: any[]) => T} type
- * @returns {T}
- */
- getInputObject(type) {
- return /** @type {T} */ (this.inputObjects.find(object => object.constructor == type))
- }
-
- // Subclasses will want to override
- createInputObjects() {
- return []
- }
+// @ts-check
+
+/**
+ * @typedef {import("../Blueprint").default} Blueprint
+ * @typedef {import("../entity/IEntity").default} IEntity
+ * @typedef {import("../input/IContext").default} IContext
+ * @typedef {import("../template/ITemplate").default} ITemplate
+ */
+
+class IElement extends HTMLElement {
+
+ static tagName = ""
+
+ /** @type {Blueprint} */
+ blueprint
+
+ /** @type {IEntity} */
+ entity
+
+ /** @type {ITemplate} */
+ template
+
+ /** @type {IContext[]} */
+ inputObjects = []
+
+ constructor(entity, template) {
+ super();
+ this.blueprint = null;
+ this.entity = entity;
+ this.template = template;
+ this.inputObjects = [];
+ }
+
+ getTemplate() {
+ return this.template
+ }
+
+ connectedCallback() {
+ this.blueprint = this.closest("ueb-blueprint");
+ this.template.apply(this);
+ this.inputObjects = this.createInputObjects();
+ }
+
+ disconnectedCallback() {
+ this.inputObjects.forEach(v => v.unlistenDOMElement());
+ }
+
+ /** @param {IElement} element */
+ isSameGraph(element) {
+ return this.blueprint && this.blueprint == element?.blueprint
+ }
+
+ /**
+ * @template {IContext} T
+ * @param {new (...args: any[]) => T} type
+ * @returns {T}
+ */
+ getInputObject(type) {
+ return /** @type {T} */ (this.inputObjects.find(object => object.constructor == type))
+ }
+
+ // Subclasses will want to override
+ createInputObjects() {
+ return []
+ }
}
// @ts-check
@@ -469,43 +469,43 @@ class SelectorTemplate extends ITemplate {
}
}
-// @ts-check
-
-class SelectorElement extends IElement {
-
- static tagName = "ueb-selector"
-
- constructor() {
- super({}, new SelectorTemplate());
- this.selectionModel = null;
- /** @type {SelectorTemplate} */
- this.template;
- }
-
- /**
- * Create a selection rectangle starting from the specified position
- * @param {Number[]} initialPosition - Selection rectangle initial position (relative to the .ueb-grid element)
- */
- startSelecting(initialPosition) {
- this.template.applyStartSelecting(this, initialPosition);
- this.selectionModel = new FastSelectionModel(initialPosition, this.blueprint.getNodes(), this.blueprint.nodeBoundariesSupplier, this.blueprint.nodeSelectToggleFunction);
- }
-
- /**
- * Move selection rectagle to the specified final position. The initial position was specified by startSelecting()
- * @param {Number[]} finalPosition - Selection rectangle final position (relative to the .ueb-grid element)
- */
- doSelecting(finalPosition) {
- this.template.applyDoSelecting(this, finalPosition);
- this.selectionModel.selectTo(finalPosition);
- }
-
- finishSelecting() {
- this.template.applyFinishSelecting(this);
- this.selectionModel = null;
- }
-}
-
+// @ts-check
+
+class SelectorElement extends IElement {
+
+ static tagName = "ueb-selector"
+
+ constructor() {
+ super({}, new SelectorTemplate());
+ this.selectionModel = null;
+ /** @type {SelectorTemplate} */
+ this.template;
+ }
+
+ /**
+ * Create a selection rectangle starting from the specified position
+ * @param {Number[]} initialPosition - Selection rectangle initial position (relative to the .ueb-grid element)
+ */
+ startSelecting(initialPosition) {
+ this.template.applyStartSelecting(this, initialPosition);
+ this.selectionModel = new FastSelectionModel(initialPosition, this.blueprint.getNodes(), this.blueprint.nodeBoundariesSupplier, this.blueprint.nodeSelectToggleFunction);
+ }
+
+ /**
+ * Move selection rectagle to the specified final position. The initial position was specified by startSelecting()
+ * @param {Number[]} finalPosition - Selection rectangle final position (relative to the .ueb-grid element)
+ */
+ doSelecting(finalPosition) {
+ this.template.applyDoSelecting(this, finalPosition);
+ this.selectionModel.selectTo(finalPosition);
+ }
+
+ finishSelecting() {
+ this.template.applyFinishSelecting(this);
+ this.selectionModel = null;
+ }
+}
+
customElements.define(SelectorElement.tagName, SelectorElement);
// @ts-check
@@ -658,10 +658,14 @@ class Configuration {
deleteNodesKeyboardKey = "Delete"
editTextEventName = {
begin: "ueb-edit-text-begin",
- end: "ueb-edit-text-end"
+ end: "ueb-edit-text-end",
}
enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 0 (1:1) zoom
expandGridSize = 400
+ focusEventName = {
+ begin: "blueprint-focus",
+ end: "blueprint-unfocus",
+ }
fontSize = "12px"
gridAxisLineColor = "black"
gridExpandThreshold = 0.25 // remaining size factor threshold to cause an expansion event
@@ -693,13 +697,13 @@ class Configuration {
selectAllKeyboardKey = "(bCtrl=True,Key=A)"
trackingMouseEventName = {
begin: "ueb-tracking-mouse-begin",
- end: "ueb-tracking-mouse-end"
+ end: "ueb-tracking-mouse-end",
}
ModifierKeys = [
"Ctrl",
"Shift",
"Alt",
- "Meta"
+ "Meta",
]
Keys = {
/* UE name: JS name */
@@ -794,57 +798,66 @@ class Configuration {
}
}
-// @ts-check
-
-/**
- * @typedef {import("../Blueprint").default} Blueprint
- */
-class IContext {
-
- /** @type {HTMLElement} */
- target
-
- /** @type {Blueprint} */
- blueprint
-
- /** @type {Object} */
- options
-
- constructor(target, blueprint, options) {
- this.target = target;
- this.blueprint = blueprint;
- this.options = options;
- let self = this;
- this.listenHandler = _ => {
- self.listenEvents();
- };
- this.unlistenHandler = _ => {
- self.unlistenEvents();
- };
- if (options?.listenOnFocus ?? false) {
- this.blueprint.addEventListener("blueprint-focus", this.listenHandler);
- this.blueprint.addEventListener("blueprint-unfocus", this.unlistenHandler);
- }
- if (options?.unlistenOnEditText ?? false) {
- this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler);
- this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler);
- }
- }
-
- unlistenDOMElement() {
- this.unlistenEvents();
- this.blueprint.removeEventListener("blueprint-focus", this.listenHandler);
- this.blueprint.removeEventListener("blueprint-unfocus", this.unlistenHandler);
- this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler);
- this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler);
- }
-
- /* Subclasses will probabily override the following methods */
- listenEvents() {
- }
-
- unlistenEvents() {
- }
+// @ts-check
+
+/**
+ * @typedef {import("../Blueprint").default} Blueprint
+ */
+class IContext {
+
+ /** @type {HTMLElement} */
+ #target
+ get target() {
+ return this.#target
+ }
+
+ /** @type {Blueprint} */
+ #blueprint
+ get blueprint() {
+ return this.#blueprint
+ }
+
+ /** @type {Object} */
+ options
+
+ /**
+ * @param {HTMLElement} target
+ * @param {Blueprint} blueprint
+ * @param {Object} options
+ */
+ constructor(target, blueprint, options) {
+ this.#target = target;
+ this.#blueprint = blueprint;
+ this.options = options;
+ this.options.listenOnFocus = this.options?.listenOnFocus ?? false;
+ this.options.unlistenOnTextEdit = this.options?.unlistenOnTextEdit ?? false;
+ let self = this;
+ this.listenHandler = _ => self.listenEvents();
+ this.unlistenHandler = _ => self.unlistenEvents();
+ if (this.options.listenOnFocus) {
+ this.blueprint.addEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler);
+ this.blueprint.addEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler);
+ }
+ if (options?.unlistenOnTextEdit ?? false) {
+ this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler);
+ this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler);
+ }
+ }
+
+ unlistenDOMElement() {
+ this.unlistenEvents();
+ this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler);
+ this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler);
+ this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler);
+ this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler);
+ }
+
+ /* Subclasses will probabily override the following methods */
+ listenEvents() {
+ }
+
+ unlistenEvents() {
+ }
}
// @ts-check
@@ -996,66 +1009,66 @@ class Utility {
}
}
-// @ts-check
-
-class IEntity {
-
- static attributes = {}
-
- constructor(options = {}) {
- /**
- * @param {String[]} prefix
- * @param {Object} target
- * @param {Object} properties
- */
- const defineAllAttributes = (prefix, target, properties) => {
- let fullKey = prefix.concat("");
- const last = fullKey.length - 1;
- for (let property of Object.getOwnPropertyNames(properties)) {
- fullKey[last] = property;
- // Not instanceof because all objects are instenceof Object, exact match needed
- if (properties[property]?.constructor === Object) {
- target[property] = {};
- defineAllAttributes(fullKey, target[property], properties[property]);
- continue
- }
- /*
- * The value can either be:
- * - Array: can contain multiple values, its property is assigned multiple times like (X=1, X=4, X="Hello World").
- * - TypeInitialization: contains the maximum amount of information about the attribute.
- * - A type: the default value will be default constructed object without arguments.
- * - A proper value.
- */
- const value = Utility.objectGet(options, fullKey);
- if (value !== undefined) {
- target[property] = value;
- continue
- }
- let defaultValue = properties[property];
- if (defaultValue instanceof TypeInitialization) {
- if (!defaultValue.showDefault) {
- target[property] = undefined; // to preserve the order
- continue
- }
- defaultValue = defaultValue.value;
- }
- if (defaultValue instanceof Array) {
- target[property] = [];
- continue
- }
- if (defaultValue instanceof Function) {
- defaultValue = TypeInitialization.sanitize(new defaultValue());
- }
- target[property] = TypeInitialization.sanitize(defaultValue);
- }
- };
- // @ts-expect-error
- defineAllAttributes([], this, this.constructor.attributes);
- }
-
- empty() {
- return true
- }
+// @ts-check
+
+class IEntity {
+
+ static attributes = {}
+
+ constructor(options = {}) {
+ /**
+ * @param {String[]} prefix
+ * @param {Object} target
+ * @param {Object} properties
+ */
+ const defineAllAttributes = (prefix, target, properties) => {
+ let fullKey = prefix.concat("");
+ const last = fullKey.length - 1;
+ for (let property of Object.getOwnPropertyNames(properties)) {
+ fullKey[last] = property;
+ // Not instanceof because all objects are instenceof Object, exact match needed
+ if (properties[property]?.constructor === Object) {
+ target[property] = {};
+ defineAllAttributes(fullKey, target[property], properties[property]);
+ continue
+ }
+ /*
+ * The value can either be:
+ * - Array: can contain multiple values, its property is assigned multiple times like (X=1, X=4, X="Hello World").
+ * - TypeInitialization: contains the maximum amount of information about the attribute.
+ * - A type: the default value will be default constructed object without arguments.
+ * - A proper value.
+ */
+ const value = Utility.objectGet(options, fullKey);
+ if (value !== undefined) {
+ target[property] = value;
+ continue
+ }
+ let defaultValue = properties[property];
+ if (defaultValue instanceof TypeInitialization) {
+ if (!defaultValue.showDefault) {
+ target[property] = undefined; // to preserve the order
+ continue
+ }
+ defaultValue = defaultValue.value;
+ }
+ if (defaultValue instanceof Array) {
+ target[property] = [];
+ continue
+ }
+ if (defaultValue instanceof Function) {
+ defaultValue = TypeInitialization.sanitize(new defaultValue());
+ }
+ target[property] = TypeInitialization.sanitize(defaultValue);
+ }
+ };
+ // @ts-expect-error
+ defineAllAttributes([], this, this.constructor.attributes);
+ }
+
+ empty() {
+ return true
+ }
}
// @ts-check
@@ -1128,32 +1141,32 @@ class GuidEntity extends IEntity {
}
}
-// @ts-check
-
-class IdentifierEntity extends IEntity {
-
- static attributes = {
- value: String,
- }
-
- constructor(options = {}) {
- // Not instanceof to pick also primitive string
- if (options.constructor === String) {
- options = {
- value: options
- };
- }
- super(options);
- /** @type {String} */ this.value;
- }
-
- valueOf() {
- return this.value
- }
-
- toString() {
- return this.value
- }
+// @ts-check
+
+class IdentifierEntity extends IEntity {
+
+ static attributes = {
+ value: String,
+ }
+
+ constructor(options = {}) {
+ // Not instanceof to pick also primitive string
+ if (options.constructor === String) {
+ options = {
+ value: options
+ };
+ }
+ super(options);
+ /** @type {String} */ this.value;
+ }
+
+ valueOf() {
+ return this.value
+ }
+
+ toString() {
+ return this.value
+ }
}
// @ts-check
@@ -1186,28 +1199,28 @@ class IntegerEntity extends IEntity {
}
}
-// @ts-check
-
-class KeyBindingEntity extends IEntity {
-
- static attributes = {
- ActionName: "",
- bShift: false,
- bCtrl: false,
- bAlt: false,
- bCmd: false,
- Key: IdentifierEntity,
- }
-
- constructor(options = {}) {
- super(options);
- /** @type {String} */ this.ActionName;
- /** @type {Boolean} */ this.bShift;
- /** @type {Boolean} */ this.bCtrl;
- /** @type {Boolean} */ this.bAlt;
- /** @type {Boolean} */ this.bCmd;
- /** @type {IdentifierEntity} */ this.Key;
- }
+// @ts-check
+
+class KeyBindingEntity extends IEntity {
+
+ static attributes = {
+ ActionName: "",
+ bShift: false,
+ bCtrl: false,
+ bAlt: false,
+ bCmd: false,
+ Key: IdentifierEntity,
+ }
+
+ constructor(options = {}) {
+ super(options);
+ /** @type {String} */ this.ActionName;
+ /** @type {Boolean} */ this.bShift;
+ /** @type {Boolean} */ this.bCtrl;
+ /** @type {Boolean} */ this.bAlt;
+ /** @type {Boolean} */ this.bCmd;
+ /** @type {IdentifierEntity} */ this.Key;
+ }
}
// @ts-check
@@ -1734,84 +1747,84 @@ class SerializerFactory {
}
}
-// @ts-check
-
-class ISerializer {
-
- static grammar = Parsimmon.createLanguage(new Grammar())
-
- constructor(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) {
- this.entityType = entityType;
- this.prefix = prefix ?? "";
- this.separator = separator ?? ",";
- this.trailingSeparator = trailingSeparator ?? false;
- this.attributeValueConjunctionSign = attributeValueConjunctionSign ?? "=";
- this.attributeKeyPrinter = attributeKeyPrinter ?? (k => k.join("."));
- }
-
- writeValue(value) {
- if (value === null) {
- return "()"
- }
- const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).write(v);
- // This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically)
- switch (value?.constructor) {
- case Function:
- return this.writeValue(value())
- case Boolean:
- return Utility.FirstCapital(value.toString())
- case Number:
- return value.toString()
- case String:
- return `"${value}"`
- }
- if (value instanceof Array) {
- return `(${value.map(v => serialize(v) + ",").join("")})`
- }
- if (value instanceof IEntity) {
- return serialize(value)
- }
- }
-
- /**
- * @param {String[]} key
- * @param {Object} object
- * @returns {String}
- */
- subWrite(key, object) {
- let result = "";
- let fullKey = key.concat("");
- const last = fullKey.length - 1;
- for (const property of Object.getOwnPropertyNames(object)) {
- fullKey[last] = property;
- const value = object[property];
- if (object[property]?.constructor === Object) {
- // Recursive call when finding an object
- result += (result.length ? this.separator : "")
- + this.subWrite(fullKey, value);
- } else if (value !== undefined && this.showProperty(object, fullKey, value)) {
- result += (result.length ? this.separator : "")
- + this.prefix
- + this.attributeKeyPrinter(fullKey)
- + this.attributeValueConjunctionSign
- + this.writeValue(value);
- }
- }
- if (this.trailingSeparator && result.length && fullKey.length === 1) {
- // append separator at the end if asked and there was printed content
- result += this.separator;
- }
- return result
- }
-
- showProperty(object, attributeKey, attributeValue) {
- const attributes = this.entityType.attributes;
- const attribute = Utility.objectGet(attributes, attributeKey);
- if (attribute instanceof TypeInitialization) {
- return !Utility.equals(attribute.value, attributeValue) || attribute.showDefault
- }
- return true
- }
+// @ts-check
+
+class ISerializer {
+
+ static grammar = Parsimmon.createLanguage(new Grammar())
+
+ constructor(entityType, prefix, separator, trailingSeparator, attributeValueConjunctionSign, attributeKeyPrinter) {
+ this.entityType = entityType;
+ this.prefix = prefix ?? "";
+ this.separator = separator ?? ",";
+ this.trailingSeparator = trailingSeparator ?? false;
+ this.attributeValueConjunctionSign = attributeValueConjunctionSign ?? "=";
+ this.attributeKeyPrinter = attributeKeyPrinter ?? (k => k.join("."));
+ }
+
+ writeValue(value) {
+ if (value === null) {
+ return "()"
+ }
+ const serialize = v => SerializerFactory.getSerializer(Utility.getType(v)).write(v);
+ // This is an exact match (and not instanceof) to hit also primitive types (by accessing value.constructor they are converted to objects automatically)
+ switch (value?.constructor) {
+ case Function:
+ return this.writeValue(value())
+ case Boolean:
+ return Utility.FirstCapital(value.toString())
+ case Number:
+ return value.toString()
+ case String:
+ return `"${value}"`
+ }
+ if (value instanceof Array) {
+ return `(${value.map(v => serialize(v) + ",").join("")})`
+ }
+ if (value instanceof IEntity) {
+ return serialize(value)
+ }
+ }
+
+ /**
+ * @param {String[]} key
+ * @param {Object} object
+ * @returns {String}
+ */
+ subWrite(key, object) {
+ let result = "";
+ let fullKey = key.concat("");
+ const last = fullKey.length - 1;
+ for (const property of Object.getOwnPropertyNames(object)) {
+ fullKey[last] = property;
+ const value = object[property];
+ if (object[property]?.constructor === Object) {
+ // Recursive call when finding an object
+ result += (result.length ? this.separator : "")
+ + this.subWrite(fullKey, value);
+ } else if (value !== undefined && this.showProperty(object, fullKey, value)) {
+ result += (result.length ? this.separator : "")
+ + this.prefix
+ + this.attributeKeyPrinter(fullKey)
+ + this.attributeValueConjunctionSign
+ + this.writeValue(value);
+ }
+ }
+ if (this.trailingSeparator && result.length && fullKey.length === 1) {
+ // append separator at the end if asked and there was printed content
+ result += this.separator;
+ }
+ return result
+ }
+
+ showProperty(object, attributeKey, attributeValue) {
+ const attributes = this.entityType.attributes;
+ const attribute = Utility.objectGet(attributes, attributeKey);
+ if (attribute instanceof TypeInitialization) {
+ return !Utility.equals(attribute.value, attributeValue) || attribute.showDefault
+ }
+ return true
+ }
}
// @ts-check
@@ -1877,10 +1890,12 @@ End Object\n`;
class Copy extends IContext {
+ /** @type {(e: ClipboardEvent) => void} */
#copyHandler
constructor(target, blueprint, options = {}) {
options.listenOnFocus = true;
+ options.unlistenOnTextEdit = true;
super(target, blueprint, options);
this.serializer = new ObjectSerializer();
let self = this;
@@ -1901,96 +1916,96 @@ class Copy extends IContext {
}
}
-// @ts-check
-
-class IKeyboardShortcut extends IContext {
-
- /** @type {KeyBindingEntity[]} */
- #activationKeys
-
- constructor(target, blueprint, options = {}) {
- options.listenOnFocus = true;
- options.activationKeys ??= [];
- if (!(options.activationKeys instanceof Array)) {
- options.activationKeys = [options.activationKeys];
- }
- options.activationKeys = options.activationKeys.map(v => {
- if (v instanceof KeyBindingEntity) {
- return v
- }
- if (v.constructor === String) {
- // @ts-expect-error
- const parsed = ISerializer.grammar.KeyBinding.parse(v);
- if (parsed.status) {
- return parsed.value
- }
- }
- throw new Error("Unexpected key value")
- });
-
- super(target, blueprint, options);
-
- this.#activationKeys = this.options.activationKeys ?? [];
-
- const wantsShift = keyEntry => keyEntry.bShift || keyEntry.Key == "LeftShift" || keyEntry.Key == "RightShift";
- const wantsCtrl = keyEntry => keyEntry.bCtrl || keyEntry.Key == "LeftControl" || keyEntry.Key == "RightControl";
- const wantsAlt = keyEntry => keyEntry.bAlt || keyEntry.Key == "LeftAlt" || keyEntry.Key == "RightAlt";
-
- let self = this;
- /** @param {KeyboardEvent} e */
- this.keyDownHandler = e => {
- if (
- self.#activationKeys.some(keyEntry =>
- wantsShift(keyEntry) == e.shiftKey
- && wantsCtrl(keyEntry) == e.ctrlKey
- && wantsAlt(keyEntry) == e.altKey
- && this.blueprint.settings.Keys[keyEntry.Key] == e.code
- )) {
- if (options.consumeEvent) {
- e.stopImmediatePropagation();
- }
- self.fire();
- document.removeEventListener("keydown", self.keyDownHandler);
- document.addEventListener("keyup", self.keyUpHandler);
- }
- };
-
- /** @param {KeyboardEvent} e */
- this.keyUpHandler = e => {
- if (
- self.#activationKeys.some(keyEntry =>
- keyEntry.bShift && e.key == "Shift"
- || keyEntry.bCtrl && e.key == "Control"
- || keyEntry.bAlt && e.key == "Alt"
- || keyEntry.bCmd && e.key == "Meta"
- || this.blueprint.settings.Keys[keyEntry.Key] == e.code
- )) {
- if (options.consumeEvent) {
- e.stopImmediatePropagation();
- }
- self.unfire();
- document.removeEventListener("keyup", this.keyUpHandler);
- document.addEventListener("keydown", this.keyDownHandler);
- }
- };
-
- }
-
- listenEvents() {
- document.addEventListener("keydown", this.keyDownHandler);
- }
-
- unlistenEvents() {
- document.removeEventListener("keydown", this.keyDownHandler);
- }
-
- // Subclasses will want to override
-
- fire() {
- }
-
- unfire() {
- }
+// @ts-check
+
+class IKeyboardShortcut extends IContext {
+
+ /** @type {KeyBindingEntity[]} */
+ #activationKeys
+
+ constructor(target, blueprint, options = {}) {
+ options.listenOnFocus = true;
+ options.activationKeys ??= [];
+ if (!(options.activationKeys instanceof Array)) {
+ options.activationKeys = [options.activationKeys];
+ }
+ options.activationKeys = options.activationKeys.map(v => {
+ if (v instanceof KeyBindingEntity) {
+ return v
+ }
+ if (v.constructor === String) {
+ // @ts-expect-error
+ const parsed = ISerializer.grammar.KeyBinding.parse(v);
+ if (parsed.status) {
+ return parsed.value
+ }
+ }
+ throw new Error("Unexpected key value")
+ });
+
+ super(target, blueprint, options);
+
+ this.#activationKeys = this.options.activationKeys ?? [];
+
+ const wantsShift = keyEntry => keyEntry.bShift || keyEntry.Key == "LeftShift" || keyEntry.Key == "RightShift";
+ const wantsCtrl = keyEntry => keyEntry.bCtrl || keyEntry.Key == "LeftControl" || keyEntry.Key == "RightControl";
+ const wantsAlt = keyEntry => keyEntry.bAlt || keyEntry.Key == "LeftAlt" || keyEntry.Key == "RightAlt";
+
+ let self = this;
+ /** @param {KeyboardEvent} e */
+ this.keyDownHandler = e => {
+ if (
+ self.#activationKeys.some(keyEntry =>
+ wantsShift(keyEntry) == e.shiftKey
+ && wantsCtrl(keyEntry) == e.ctrlKey
+ && wantsAlt(keyEntry) == e.altKey
+ && this.blueprint.settings.Keys[keyEntry.Key] == e.code
+ )) {
+ if (options.consumeEvent) {
+ e.stopImmediatePropagation();
+ }
+ self.fire();
+ document.removeEventListener("keydown", self.keyDownHandler);
+ document.addEventListener("keyup", self.keyUpHandler);
+ }
+ };
+
+ /** @param {KeyboardEvent} e */
+ this.keyUpHandler = e => {
+ if (
+ self.#activationKeys.some(keyEntry =>
+ keyEntry.bShift && e.key == "Shift"
+ || keyEntry.bCtrl && e.key == "Control"
+ || keyEntry.bAlt && e.key == "Alt"
+ || keyEntry.bCmd && e.key == "Meta"
+ || this.blueprint.settings.Keys[keyEntry.Key] == e.code
+ )) {
+ if (options.consumeEvent) {
+ e.stopImmediatePropagation();
+ }
+ self.unfire();
+ document.removeEventListener("keyup", this.keyUpHandler);
+ document.addEventListener("keydown", this.keyDownHandler);
+ }
+ };
+
+ }
+
+ listenEvents() {
+ document.addEventListener("keydown", this.keyDownHandler);
+ }
+
+ unlistenEvents() {
+ document.removeEventListener("keydown", this.keyDownHandler);
+ }
+
+ // Subclasses will want to override
+
+ fire() {
+ }
+
+ unfire() {
+ }
}
// @ts-check
@@ -2015,72 +2030,72 @@ class KeyboardCanc extends IKeyboardShortcut {
}
}
-// @ts-check
-
-class IPointing extends IContext {
-
- constructor(target, blueprint, options) {
- super(target, blueprint, options);
- this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement;
- }
-
- /**
- * @param {MouseEvent} mouseEvent
- */
- locationFromEvent(mouseEvent) {
- return this.blueprint.compensateTranslation(
- Utility.convertLocation(
- [mouseEvent.clientX, mouseEvent.clientY],
- this.movementSpace))
- }
+// @ts-check
+
+class IPointing extends IContext {
+
+ constructor(target, blueprint, options) {
+ super(target, blueprint, options);
+ this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement;
+ }
+
+ /**
+ * @param {MouseEvent} mouseEvent
+ */
+ locationFromEvent(mouseEvent) {
+ return this.blueprint.compensateTranslation(
+ Utility.convertLocation(
+ [mouseEvent.clientX, mouseEvent.clientY],
+ this.movementSpace))
+ }
}
-// @ts-check
-
-class IMouseWheel extends IPointing {
-
- /** @type {(e: WheelEvent) => void} */
- #mouseWheelHandler
-
- /** @type {(e: WheelEvent) => void} */
- #mouseParentWheelHandler
-
- /**
- * @param {HTMLElement} target
- * @param {import("../../Blueprint").default} blueprint
- * @param {Object} options
- */
- constructor(target, blueprint, options) {
- options.listenOnFocus = true;
- super(target, blueprint, options);
- this.looseTarget = options?.looseTarget ?? true;
- let self = this;
-
- this.#mouseWheelHandler = e => {
- e.preventDefault();
- const location = self.locationFromEvent(e);
- self.wheel(Math.sign(e.deltaY), location);
- };
- this.#mouseParentWheelHandler = e => e.preventDefault();
-
- if (this.blueprint.focused) {
- this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false);
- }
- }
-
- listenEvents() {
- this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false);
- this.movementSpace.parentElement?.addEventListener("wheel", this.#mouseParentWheelHandler);
- }
-
- unlistenEvents() {
- this.movementSpace.removeEventListener("wheel", this.#mouseWheelHandler, false);
- this.movementSpace.parentElement?.removeEventListener("wheel", this.#mouseParentWheelHandler);
- }
-
- /* Subclasses will override the following method */
- wheel(variation, location) {
- }
+// @ts-check
+
+class IMouseWheel extends IPointing {
+
+ /** @type {(e: WheelEvent) => void} */
+ #mouseWheelHandler
+
+ /** @type {(e: WheelEvent) => void} */
+ #mouseParentWheelHandler
+
+ /**
+ * @param {HTMLElement} target
+ * @param {import("../../Blueprint").default} blueprint
+ * @param {Object} options
+ */
+ constructor(target, blueprint, options) {
+ options.listenOnFocus = true;
+ super(target, blueprint, options);
+ this.looseTarget = options?.looseTarget ?? true;
+ let self = this;
+
+ this.#mouseWheelHandler = e => {
+ e.preventDefault();
+ const location = self.locationFromEvent(e);
+ self.wheel(Math.sign(e.deltaY), location);
+ };
+ this.#mouseParentWheelHandler = e => e.preventDefault();
+
+ if (this.blueprint.focused) {
+ this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false);
+ }
+ }
+
+ listenEvents() {
+ this.movementSpace.addEventListener("wheel", this.#mouseWheelHandler, false);
+ this.movementSpace.parentElement?.addEventListener("wheel", this.#mouseParentWheelHandler);
+ }
+
+ unlistenEvents() {
+ this.movementSpace.removeEventListener("wheel", this.#mouseWheelHandler, false);
+ this.movementSpace.parentElement?.removeEventListener("wheel", this.#mouseParentWheelHandler);
+ }
+
+ /* Subclasses will override the following method */
+ wheel(variation, location) {
+ }
}
// @ts-check
@@ -2112,34 +2127,34 @@ class Zoom extends IMouseWheel {
}
}
-// @ts-check
-
-class KeyboardEnableZoom extends IKeyboardShortcut {
-
- /** @type {Zoom} */
- #zoomInputObject
-
- /**
- * @param {HTMLElement} target
- * @param {import("../../Blueprint").default} blueprint
- * @param {Object} options
- */
- constructor(target, blueprint, options = {}) {
- options = {
- ...options,
- activationKeys: blueprint.settings.enableZoomIn
- };
- super(target, blueprint, options);
- }
-
- fire() {
- this.#zoomInputObject = this.blueprint.getInputObject(Zoom);
- this.#zoomInputObject.enableZoonIn = true;
- }
-
- unfire() {
- this.#zoomInputObject.enableZoonIn = false;
- }
+// @ts-check
+
+class KeyboardEnableZoom extends IKeyboardShortcut {
+
+ /** @type {Zoom} */
+ #zoomInputObject
+
+ /**
+ * @param {HTMLElement} target
+ * @param {import("../../Blueprint").default} blueprint
+ * @param {Object} options
+ */
+ constructor(target, blueprint, options = {}) {
+ options = {
+ ...options,
+ activationKeys: blueprint.settings.enableZoomIn
+ };
+ super(target, blueprint, options);
+ }
+
+ fire() {
+ this.#zoomInputObject = this.blueprint.getInputObject(Zoom);
+ this.#zoomInputObject.enableZoonIn = true;
+ }
+
+ unfire() {
+ this.#zoomInputObject.enableZoonIn = false;
+ }
}
// @ts-check
@@ -2333,362 +2348,362 @@ class LinkTemplate extends ITemplate {
}
}
-// @ts-check
-
-/**
- * @typedef {import("./PinElement").default} PinElement
- * @typedef {import("./LinkMessageElement").default} LinkMessageElement
- */
-class LinkElement extends IElement {
-
- static tagName = "ueb-link"
- /** @type {PinElement} */
- #source
- /** @type {PinElement} */
- #destination
- #nodeDeleteHandler
- #nodeDragSourceHandler
- #nodeDragDestinatonHandler
- sourceLocation = [0, 0]
- /** @type {SVGPathElement} */
- pathElement
- /** @type {LinkMessageElement} */
- linkMessageElement
- originatesFromInput = false
- destinationLocation = [0, 0]
-
- /**
- * @param {PinElement} source
- * @param {PinElement} destination
- */
- constructor(source, destination) {
- super({}, new LinkTemplate());
- /** @type {import("../template/LinkTemplate").default} */
- this.template;
- const self = this;
- this.#nodeDeleteHandler = _ => self.remove();
- this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value);
- this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value);
- if (source) {
- this.setSourcePin(source);
- }
- if (destination) {
- this.setDestinationPin(destination);
- }
- if (source && destination) {
- this.#linkPins();
- }
- }
-
- #linkPins() {
- this.#source.linkTo(this.#destination);
- this.#destination.linkTo(this.#source);
- }
-
- #unlinkPins() {
- if (this.#source && this.#destination) {
- this.#source.unlinkFrom(this.#destination);
- this.#destination.unlinkFrom(this.#source);
- }
- }
-
- disconnectedCallback() {
- super.disconnectedCallback();
- this.#unlinkPins();
- }
-
- /**
- * @returns {Number[]}
- */
- getSourceLocation() {
- return this.sourceLocation
- }
-
- /**
- * @param {Number[]} offset
- */
- addSourceLocation(offset) {
- const location = [
- this.sourceLocation[0] + offset[0],
- this.sourceLocation[1] + offset[1]
- ];
- this.sourceLocation = location;
- this.template.applyFullLocation(this);
- }
-
- /**
- * @param {Number[]} location
- */
- setSourceLocation(location = null) {
- if (location == null) {
- location = this.#source.template.getLinkLocation(this.#source);
- }
- this.sourceLocation = location;
- this.template.applySourceLocation(this);
- }
-
- /**
- * @returns {Number[]}
- */
- getDestinationLocation() {
- return this.destinationLocation
- }
-
- /**
- * @param {Number[]} offset
- */
- addDestinationLocation(offset) {
- const location = [
- this.destinationLocation[0] + offset[0],
- this.destinationLocation[1] + offset[1]
- ];
- this.setDestinationLocation(location);
- }
-
- /**
- * @param {Number[]} location
- */
- setDestinationLocation(location = null) {
- if (location == null) {
- location = this.#destination.template.getLinkLocation(this.#destination);
- }
- this.destinationLocation = location;
- this.template.applyFullLocation(this);
- }
-
- /**
- * @returns {PinElement}
- */
- getSourcePin() {
- return this.#source
- }
-
- /**
- * @param {PinElement} pin
- */
- setSourcePin(pin) {
- if (this.#source) {
- const settings = this.#source.blueprint.settings;
- const nodeElement = this.#source.getNodeElement();
- nodeElement.removeEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler);
- nodeElement.removeEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler);
- if (this.#destination) {
- this.#unlinkPins();
- }
- }
- this.#source = pin;
- if (this.#source) {
- const nodeElement = this.#source.getNodeElement();
- const settings = this.#source.blueprint.settings;
- this.originatesFromInput = pin.isInput();
- nodeElement.addEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler);
- nodeElement.addEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler);
- this.setSourceLocation();
- if (this.#destination) {
- this.#linkPins();
- }
- }
- }
-
- /**
- * @returns {PinElement}
- */
- getDestinationPin() {
- return this.#destination
- }
-
- /**
- * @param {PinElement} pin
- */
- setDestinationPin(pin) {
- if (this.#destination) {
- const nodeElement = this.#destination.getNodeElement();
- nodeElement.removeEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler);
- nodeElement.removeEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler);
- if (this.#source) {
- this.#unlinkPins();
- }
- }
- this.#destination = pin;
- if (this.#destination) {
- const nodeElement = this.#destination.getNodeElement();
- nodeElement.addEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler);
- nodeElement.addEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler);
- this.setDestinationLocation();
- if (this.#source) {
- this.#linkPins();
- }
- }
- }
-
- /**
- * @param {LinkMessageElement} linkMessage
- */
- setLinkMessage(linkMessage) {
- if (linkMessage) {
- this.template.applyLinkMessage(this, linkMessage);
- } else if (this.linkMessageElement) {
- this.linkMessageElement.remove();
- this.linkMessageElement = null;
- }
- }
-
- startDragging() {
- this.template.applyStartDragging(this);
- }
-
- finishDragging() {
- this.template.applyFinishDragging(this);
- }
-}
-
+// @ts-check
+
+/**
+ * @typedef {import("./PinElement").default} PinElement
+ * @typedef {import("./LinkMessageElement").default} LinkMessageElement
+ */
+class LinkElement extends IElement {
+
+ static tagName = "ueb-link"
+ /** @type {PinElement} */
+ #source
+ /** @type {PinElement} */
+ #destination
+ #nodeDeleteHandler
+ #nodeDragSourceHandler
+ #nodeDragDestinatonHandler
+ sourceLocation = [0, 0]
+ /** @type {SVGPathElement} */
+ pathElement
+ /** @type {LinkMessageElement} */
+ linkMessageElement
+ originatesFromInput = false
+ destinationLocation = [0, 0]
+
+ /**
+ * @param {PinElement} source
+ * @param {PinElement} destination
+ */
+ constructor(source, destination) {
+ super({}, new LinkTemplate());
+ /** @type {import("../template/LinkTemplate").default} */
+ this.template;
+ const self = this;
+ this.#nodeDeleteHandler = _ => self.remove();
+ this.#nodeDragSourceHandler = e => self.addSourceLocation(e.detail.value);
+ this.#nodeDragDestinatonHandler = e => self.addDestinationLocation(e.detail.value);
+ if (source) {
+ this.setSourcePin(source);
+ }
+ if (destination) {
+ this.setDestinationPin(destination);
+ }
+ if (source && destination) {
+ this.#linkPins();
+ }
+ }
+
+ #linkPins() {
+ this.#source.linkTo(this.#destination);
+ this.#destination.linkTo(this.#source);
+ }
+
+ #unlinkPins() {
+ if (this.#source && this.#destination) {
+ this.#source.unlinkFrom(this.#destination);
+ this.#destination.unlinkFrom(this.#source);
+ }
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ this.#unlinkPins();
+ }
+
+ /**
+ * @returns {Number[]}
+ */
+ getSourceLocation() {
+ return this.sourceLocation
+ }
+
+ /**
+ * @param {Number[]} offset
+ */
+ addSourceLocation(offset) {
+ const location = [
+ this.sourceLocation[0] + offset[0],
+ this.sourceLocation[1] + offset[1]
+ ];
+ this.sourceLocation = location;
+ this.template.applyFullLocation(this);
+ }
+
+ /**
+ * @param {Number[]} location
+ */
+ setSourceLocation(location = null) {
+ if (location == null) {
+ location = this.#source.template.getLinkLocation(this.#source);
+ }
+ this.sourceLocation = location;
+ this.template.applySourceLocation(this);
+ }
+
+ /**
+ * @returns {Number[]}
+ */
+ getDestinationLocation() {
+ return this.destinationLocation
+ }
+
+ /**
+ * @param {Number[]} offset
+ */
+ addDestinationLocation(offset) {
+ const location = [
+ this.destinationLocation[0] + offset[0],
+ this.destinationLocation[1] + offset[1]
+ ];
+ this.setDestinationLocation(location);
+ }
+
+ /**
+ * @param {Number[]} location
+ */
+ setDestinationLocation(location = null) {
+ if (location == null) {
+ location = this.#destination.template.getLinkLocation(this.#destination);
+ }
+ this.destinationLocation = location;
+ this.template.applyFullLocation(this);
+ }
+
+ /**
+ * @returns {PinElement}
+ */
+ getSourcePin() {
+ return this.#source
+ }
+
+ /**
+ * @param {PinElement} pin
+ */
+ setSourcePin(pin) {
+ if (this.#source) {
+ const settings = this.#source.blueprint.settings;
+ const nodeElement = this.#source.getNodeElement();
+ nodeElement.removeEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler);
+ nodeElement.removeEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler);
+ if (this.#destination) {
+ this.#unlinkPins();
+ }
+ }
+ this.#source = pin;
+ if (this.#source) {
+ const nodeElement = this.#source.getNodeElement();
+ const settings = this.#source.blueprint.settings;
+ this.originatesFromInput = pin.isInput();
+ nodeElement.addEventListener(settings.nodeDeleteEventName, this.#nodeDeleteHandler);
+ nodeElement.addEventListener(settings.nodeDragLocalEventName, this.#nodeDragSourceHandler);
+ this.setSourceLocation();
+ if (this.#destination) {
+ this.#linkPins();
+ }
+ }
+ }
+
+ /**
+ * @returns {PinElement}
+ */
+ getDestinationPin() {
+ return this.#destination
+ }
+
+ /**
+ * @param {PinElement} pin
+ */
+ setDestinationPin(pin) {
+ if (this.#destination) {
+ const nodeElement = this.#destination.getNodeElement();
+ nodeElement.removeEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler);
+ nodeElement.removeEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler);
+ if (this.#source) {
+ this.#unlinkPins();
+ }
+ }
+ this.#destination = pin;
+ if (this.#destination) {
+ const nodeElement = this.#destination.getNodeElement();
+ nodeElement.addEventListener(this.blueprint.settings.nodeDeleteEventName, this.#nodeDeleteHandler);
+ nodeElement.addEventListener(this.blueprint.settings.nodeDragLocalEventName, this.#nodeDragDestinatonHandler);
+ this.setDestinationLocation();
+ if (this.#source) {
+ this.#linkPins();
+ }
+ }
+ }
+
+ /**
+ * @param {LinkMessageElement} linkMessage
+ */
+ setLinkMessage(linkMessage) {
+ if (linkMessage) {
+ this.template.applyLinkMessage(this, linkMessage);
+ } else if (this.linkMessageElement) {
+ this.linkMessageElement.remove();
+ this.linkMessageElement = null;
+ }
+ }
+
+ startDragging() {
+ this.template.applyStartDragging(this);
+ }
+
+ finishDragging() {
+ this.template.applyFinishDragging(this);
+ }
+}
+
customElements.define(LinkElement.tagName, LinkElement);
-// @ts-check
-
-/**
- * This class manages the ui gesture of mouse click and drag. Tha actual operations are implemented by the subclasses.
- */
-class IMouseClickDrag extends IPointing {
-
- /** @type {(e: MouseEvent) => void} */
- #mouseDownHandler
-
- /** @type {(e: MouseEvent) => void} */
- #mouseStartedMovingHandler
-
- /** @type {(e: MouseEvent) => void} */
- #mouseMoveHandler
-
- /** @type {(e: MouseEvent) => void} */
- #mouseUpHandler
-
- #trackingMouse = false
-
- started = false
-
- constructor(target, blueprint, options) {
- super(target, blueprint, options);
- this.clickButton = options?.clickButton ?? 0;
- this.exitAnyButton = options?.exitAnyButton ?? true;
- this.moveEverywhere = options?.moveEverywhere ?? false;
- this.looseTarget = options?.looseTarget ?? false;
- this.consumeEvent = options?.consumeEvent ?? true;
- this.clickedPosition = [0, 0];
-
- const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace;
- let self = this;
-
- this.#mouseDownHandler = e => {
- this.blueprint.setFocused(true);
- switch (e.button) {
- case self.clickButton:
- // Either doesn't matter or consider the click only when clicking on the parent, not descandants
- if (self.looseTarget || e.target == e.currentTarget) {
- if (this.consumeEvent) {
- e.stopImmediatePropagation(); // Captured, don't call anyone else
- }
- // Attach the listeners
- movementListenedElement.addEventListener("mousemove", self.#mouseStartedMovingHandler);
- document.addEventListener("mouseup", self.#mouseUpHandler);
- self.clickedPosition = self.locationFromEvent(e);
- self.clicked(self.clickedPosition);
- }
- break
- default:
- if (!self.exitAnyButton) {
- self.#mouseUpHandler(e);
- }
- break
- }
- };
-
- this.#mouseStartedMovingHandler = e => {
- if (this.consumeEvent) {
- e.stopImmediatePropagation(); // Captured, don't call anyone else
- }
- // Delegate from now on to self.#mouseMoveHandler
- movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler);
- movementListenedElement.addEventListener("mousemove", self.#mouseMoveHandler);
- // Handler calls e.preventDefault() when it receives the event, this means dispatchEvent returns false
- const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.begin);
- self.#trackingMouse = this.target.dispatchEvent(dragEvent) == false;
- // Do actual actions
- self.startDrag();
- self.started = true;
- };
-
- this.#mouseMoveHandler = e => {
- if (this.consumeEvent) {
- e.stopImmediatePropagation(); // Captured, don't call anyone else
- }
- const location = self.locationFromEvent(e);
- const movement = [e.movementX, e.movementY];
- self.dragTo(location, movement);
- if (self.#trackingMouse) {
- self.blueprint.mousePosition = self.locationFromEvent(e);
- }
- };
-
- this.#mouseUpHandler = e => {
- if (!self.exitAnyButton || e.button == self.clickButton) {
- if (this.consumeEvent) {
- e.stopImmediatePropagation(); // Captured, don't call anyone else
- }
- // Remove the handlers of "mousemove" and "mouseup"
- movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler);
- movementListenedElement.removeEventListener("mousemove", self.#mouseMoveHandler);
- document.removeEventListener("mouseup", self.#mouseUpHandler);
- if (self.started) {
- self.endDrag();
- }
- self.unclicked();
- if (self.#trackingMouse) {
- const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.end);
- this.target.dispatchEvent(dragEvent);
- self.#trackingMouse = false;
- }
- self.started = false;
- }
- };
-
- this.target.addEventListener("mousedown", this.#mouseDownHandler);
- if (this.clickButton == 2) {
- this.target.addEventListener("contextmenu", e => e.preventDefault());
- }
- }
-
- getEvent(eventName) {
- return new CustomEvent(eventName, {
- detail: {
- tracker: this
- },
- bubbles: true,
- cancelable: true
- })
- }
-
- unlistenDOMElement() {
- super.unlistenDOMElement();
- this.target.removeEventListener("mousedown", this.#mouseDownHandler);
- if (this.clickButton == 2) ;
- }
-
- /* Subclasses will override the following methods */
- clicked(location) {
- }
-
- startDrag(location) {
- }
-
- dragTo(location, movement) {
- }
-
- endDrag() {
- }
-
- unclicked(location) {
- }
+// @ts-check
+
+/**
+ * This class manages the ui gesture of mouse click and drag. Tha actual operations are implemented by the subclasses.
+ */
+class IMouseClickDrag extends IPointing {
+
+ /** @type {(e: MouseEvent) => void} */
+ #mouseDownHandler
+
+ /** @type {(e: MouseEvent) => void} */
+ #mouseStartedMovingHandler
+
+ /** @type {(e: MouseEvent) => void} */
+ #mouseMoveHandler
+
+ /** @type {(e: MouseEvent) => void} */
+ #mouseUpHandler
+
+ #trackingMouse = false
+
+ started = false
+
+ constructor(target, blueprint, options) {
+ super(target, blueprint, options);
+ this.clickButton = options?.clickButton ?? 0;
+ this.exitAnyButton = options?.exitAnyButton ?? true;
+ this.moveEverywhere = options?.moveEverywhere ?? false;
+ this.looseTarget = options?.looseTarget ?? false;
+ this.consumeEvent = options?.consumeEvent ?? true;
+ this.clickedPosition = [0, 0];
+
+ const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace;
+ let self = this;
+
+ this.#mouseDownHandler = e => {
+ this.blueprint.setFocused(true);
+ switch (e.button) {
+ case self.clickButton:
+ // Either doesn't matter or consider the click only when clicking on the parent, not descandants
+ if (self.looseTarget || e.target == e.currentTarget) {
+ if (this.consumeEvent) {
+ e.stopImmediatePropagation(); // Captured, don't call anyone else
+ }
+ // Attach the listeners
+ movementListenedElement.addEventListener("mousemove", self.#mouseStartedMovingHandler);
+ document.addEventListener("mouseup", self.#mouseUpHandler);
+ self.clickedPosition = self.locationFromEvent(e);
+ self.clicked(self.clickedPosition);
+ }
+ break
+ default:
+ if (!self.exitAnyButton) {
+ self.#mouseUpHandler(e);
+ }
+ break
+ }
+ };
+
+ this.#mouseStartedMovingHandler = e => {
+ if (this.consumeEvent) {
+ e.stopImmediatePropagation(); // Captured, don't call anyone else
+ }
+ // Delegate from now on to self.#mouseMoveHandler
+ movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler);
+ movementListenedElement.addEventListener("mousemove", self.#mouseMoveHandler);
+ // Handler calls e.preventDefault() when it receives the event, this means dispatchEvent returns false
+ const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.begin);
+ self.#trackingMouse = this.target.dispatchEvent(dragEvent) == false;
+ // Do actual actions
+ self.startDrag();
+ self.started = true;
+ };
+
+ this.#mouseMoveHandler = e => {
+ if (this.consumeEvent) {
+ e.stopImmediatePropagation(); // Captured, don't call anyone else
+ }
+ const location = self.locationFromEvent(e);
+ const movement = [e.movementX, e.movementY];
+ self.dragTo(location, movement);
+ if (self.#trackingMouse) {
+ self.blueprint.mousePosition = self.locationFromEvent(e);
+ }
+ };
+
+ this.#mouseUpHandler = e => {
+ if (!self.exitAnyButton || e.button == self.clickButton) {
+ if (this.consumeEvent) {
+ e.stopImmediatePropagation(); // Captured, don't call anyone else
+ }
+ // Remove the handlers of "mousemove" and "mouseup"
+ movementListenedElement.removeEventListener("mousemove", self.#mouseStartedMovingHandler);
+ movementListenedElement.removeEventListener("mousemove", self.#mouseMoveHandler);
+ document.removeEventListener("mouseup", self.#mouseUpHandler);
+ if (self.started) {
+ self.endDrag();
+ }
+ self.unclicked();
+ if (self.#trackingMouse) {
+ const dragEvent = self.getEvent(this.blueprint.settings.trackingMouseEventName.end);
+ this.target.dispatchEvent(dragEvent);
+ self.#trackingMouse = false;
+ }
+ self.started = false;
+ }
+ };
+
+ this.target.addEventListener("mousedown", this.#mouseDownHandler);
+ if (this.clickButton == 2) {
+ this.target.addEventListener("contextmenu", e => e.preventDefault());
+ }
+ }
+
+ getEvent(eventName) {
+ return new CustomEvent(eventName, {
+ detail: {
+ tracker: this
+ },
+ bubbles: true,
+ cancelable: true
+ })
+ }
+
+ unlistenDOMElement() {
+ super.unlistenDOMElement();
+ this.target.removeEventListener("mousedown", this.#mouseDownHandler);
+ if (this.clickButton == 2) ;
+ }
+
+ /* Subclasses will override the following methods */
+ clicked(location) {
+ }
+
+ startDrag(location) {
+ }
+
+ dragTo(location, movement) {
+ }
+
+ endDrag() {
+ }
+
+ unclicked(location) {
+ }
}
// @ts-check
@@ -2831,193 +2846,193 @@ class MouseMoveNodes extends IMouseClickDrag {
}
}
-// @ts-check
-
-/**
- * @typedef {import("../template/SelectableDraggableTemplate").default} SelectableDraggableTemplate
- * @typedef {import("../entity/IntegerEntity").default} IntegerEntity
- */
-class ISelectableDraggableElement extends IElement {
-
- constructor(...args) {
- super(...args);
- this.dragObject = null;
- this.location = [0, 0];
- this.selected = false;
- /** @type {SelectableDraggableTemplate} */
- this.template;
-
- let self = this;
- this.dragHandler = (e) => {
- self.addLocation(e.detail.value);
- };
- }
-
- createInputObjects() {
- return [
- new MouseMoveNodes(this, this.blueprint, {
- looseTarget: true
- }),
- ]
- }
-
- /**
- * @param {Number[]} value
- */
- setLocation(value = [0, 0]) {
- const d = [value[0] - this.location[0], value[1] - this.location[1]];
- this.location = value;
- this.template.applyLocation(this);
- if (this.blueprint) {
- const dragLocalEvent = new CustomEvent(this.blueprint.settings.nodeDragLocalEventName, {
- detail: {
- value: d
- },
- bubbles: false,
- cancelable: true
- });
- this.dispatchEvent(dragLocalEvent);
- }
- }
-
- addLocation(value) {
- this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]);
- }
-
- setSelected(value = true) {
- if (this.selected == value) {
- return
- }
- this.selected = value;
- if (this.selected) {
- this.blueprint.addEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler);
- } else {
- this.blueprint.removeEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler);
- }
- this.template.applySelected(this);
- }
-
- dispatchDragEvent(value) {
- if (!this.selected) {
- this.blueprint.unselectAll();
- this.setSelected(true);
- }
- const dragEvent = new CustomEvent(this.blueprint.settings.nodeDragEventName, {
- detail: {
- value: value
- },
- bubbles: true,
- cancelable: true
- });
- this.dispatchEvent(dragEvent);
- }
-
- snapToGrid() {
- let snappedLocation = this.blueprint.snapToGrid(this.location);
- if (this.location[0] != snappedLocation[0] || this.location[1] != snappedLocation[1]) {
- this.setLocation(snappedLocation);
- }
- }
+// @ts-check
+
+/**
+ * @typedef {import("../template/SelectableDraggableTemplate").default} SelectableDraggableTemplate
+ * @typedef {import("../entity/IntegerEntity").default} IntegerEntity
+ */
+class ISelectableDraggableElement extends IElement {
+
+ constructor(...args) {
+ super(...args);
+ this.dragObject = null;
+ this.location = [0, 0];
+ this.selected = false;
+ /** @type {SelectableDraggableTemplate} */
+ this.template;
+
+ let self = this;
+ this.dragHandler = (e) => {
+ self.addLocation(e.detail.value);
+ };
+ }
+
+ createInputObjects() {
+ return [
+ new MouseMoveNodes(this, this.blueprint, {
+ looseTarget: true
+ }),
+ ]
+ }
+
+ /**
+ * @param {Number[]} value
+ */
+ setLocation(value = [0, 0]) {
+ const d = [value[0] - this.location[0], value[1] - this.location[1]];
+ this.location = value;
+ this.template.applyLocation(this);
+ if (this.blueprint) {
+ const dragLocalEvent = new CustomEvent(this.blueprint.settings.nodeDragLocalEventName, {
+ detail: {
+ value: d
+ },
+ bubbles: false,
+ cancelable: true
+ });
+ this.dispatchEvent(dragLocalEvent);
+ }
+ }
+
+ addLocation(value) {
+ this.setLocation([this.location[0] + value[0], this.location[1] + value[1]]);
+ }
+
+ setSelected(value = true) {
+ if (this.selected == value) {
+ return
+ }
+ this.selected = value;
+ if (this.selected) {
+ this.blueprint.addEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler);
+ } else {
+ this.blueprint.removeEventListener(this.blueprint.settings.nodeDragEventName, this.dragHandler);
+ }
+ this.template.applySelected(this);
+ }
+
+ dispatchDragEvent(value) {
+ if (!this.selected) {
+ this.blueprint.unselectAll();
+ this.setSelected(true);
+ }
+ const dragEvent = new CustomEvent(this.blueprint.settings.nodeDragEventName, {
+ detail: {
+ value: value
+ },
+ bubbles: true,
+ cancelable: true
+ });
+ this.dispatchEvent(dragEvent);
+ }
+
+ snapToGrid() {
+ let snappedLocation = this.blueprint.snapToGrid(this.location);
+ if (this.location[0] != snappedLocation[0] || this.location[1] != snappedLocation[1]) {
+ this.setLocation(snappedLocation);
+ }
+ }
}
-// @ts-check
-
-/**
- * @typedef {import("../element/LinkMessageElement").default} LinkMessageElement
- */
-class LinkMessageTemplate extends ITemplate {
-
- /**
- * @param {LinkMessageElement} linkMessage
- */
- render(linkMessage) {
- return html`
-
-
- `
- }
-
- /**
- * Applies the style to the element.
- * @param {LinkMessageElement} linkMessage
- */
- apply(linkMessage) {
- super.apply(linkMessage);
- const linkMessageSetup = _ => linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message(
- linkMessage.linkElement.getSourcePin(),
- linkMessage.linkElement.getDestinationPin()
- );
- linkMessage.linkElement = linkMessage.closest(LinkElement.tagName);
- if (linkMessage.linkElement) {
- linkMessageSetup();
- } else {
- window.customElements.whenDefined(linkMessage.constructor.tagName).then(linkMessage);
- }
- }
-
-}
-
-// @ts-check
-
-/**
- * @typedef {import("./PinElement").default} PinElement
- * @typedef {import("./LinkElement").default} LinkElement
- * @typedef {(sourcePin: PinElement, destinationPin: PinElement) => String} LinkRetrieval
- */
-class LinkMessageElement extends IElement {
-
- static tagName = "ueb-link-message"
- static convertType = _ => new LinkMessageElement(
- "ueb-icon-conver-type",
- /** @type {LinkRetrieval} */
- (s, d) => `Convert ${s.getType()} to ${d.getType()}.`
- )
- static correct = _ => new LinkMessageElement(
- "ueb-icon-correct",
- /** @type {LinkRetrieval} */
- (s, d) => ""
- )
- static directionsIncompatible = _ => new LinkMessageElement(
- "ueb-icon-directions-incompatible",
- /** @type {LinkRetrieval} */
- (s, d) => "Directions are not compatbile."
- )
- static placeNode = _ => new LinkMessageElement(
- "ueb-icon-place-node",
- /** @type {LinkRetrieval} */
- (s, d) => "Place a new node."
- )
- static replaceLink = _ => new LinkMessageElement(
- "ueb-icon-replace-link",
- /** @type {LinkRetrieval} */
- (s, d) => "Replace existing input connections."
- )
- static sameNode = _ => new LinkMessageElement(
- "ueb-icon-same-node",
- /** @type {LinkRetrieval} */
- (s, d) => "Both are on the same node."
- )
- static typesIncompatible = _ => new LinkMessageElement(
- "ueb-icon-types-incompatible",
- /** @type {LinkRetrieval} */
- (s, d) => `${s.getType()} is not compatible with ${d.getType()}.`
- )
-
- /** @type {String} */
- icon
- /** @type {String} */
- message
- /** @type {LinkElement} */
- linkElement
-
- constructor(icon, message) {
- super({}, new LinkMessageTemplate());
- this.icon = icon;
- this.message = message;
- }
-
+// @ts-check
+
+/**
+ * @typedef {import("../element/LinkMessageElement").default} LinkMessageElement
+ */
+class LinkMessageTemplate extends ITemplate {
+
+ /**
+ * @param {LinkMessageElement} linkMessage
+ */
+ render(linkMessage) {
+ return html`
+
+
+ `
+ }
+
+ /**
+ * Applies the style to the element.
+ * @param {LinkMessageElement} linkMessage
+ */
+ apply(linkMessage) {
+ super.apply(linkMessage);
+ const linkMessageSetup = _ => linkMessage.querySelector(".ueb-link-message").innerText = linkMessage.message(
+ linkMessage.linkElement.getSourcePin(),
+ linkMessage.linkElement.getDestinationPin()
+ );
+ linkMessage.linkElement = linkMessage.closest(LinkElement.tagName);
+ if (linkMessage.linkElement) {
+ linkMessageSetup();
+ } else {
+ window.customElements.whenDefined(linkMessage.constructor.tagName).then(linkMessage);
+ }
+ }
+
}
+// @ts-check
+
+/**
+ * @typedef {import("./PinElement").default} PinElement
+ * @typedef {import("./LinkElement").default} LinkElement
+ * @typedef {(sourcePin: PinElement, destinationPin: PinElement) => String} LinkRetrieval
+ */
+class LinkMessageElement extends IElement {
+
+ static tagName = "ueb-link-message"
+ static convertType = _ => new LinkMessageElement(
+ "ueb-icon-conver-type",
+ /** @type {LinkRetrieval} */
+ (s, d) => `Convert ${s.getType()} to ${d.getType()}.`
+ )
+ static correct = _ => new LinkMessageElement(
+ "ueb-icon-correct",
+ /** @type {LinkRetrieval} */
+ (s, d) => ""
+ )
+ static directionsIncompatible = _ => new LinkMessageElement(
+ "ueb-icon-directions-incompatible",
+ /** @type {LinkRetrieval} */
+ (s, d) => "Directions are not compatbile."
+ )
+ static placeNode = _ => new LinkMessageElement(
+ "ueb-icon-place-node",
+ /** @type {LinkRetrieval} */
+ (s, d) => "Place a new node."
+ )
+ static replaceLink = _ => new LinkMessageElement(
+ "ueb-icon-replace-link",
+ /** @type {LinkRetrieval} */
+ (s, d) => "Replace existing input connections."
+ )
+ static sameNode = _ => new LinkMessageElement(
+ "ueb-icon-same-node",
+ /** @type {LinkRetrieval} */
+ (s, d) => "Both are on the same node."
+ )
+ static typesIncompatible = _ => new LinkMessageElement(
+ "ueb-icon-types-incompatible",
+ /** @type {LinkRetrieval} */
+ (s, d) => `${s.getType()} is not compatible with ${d.getType()}.`
+ )
+
+ /** @type {String} */
+ icon
+ /** @type {String} */
+ message
+ /** @type {LinkElement} */
+ linkElement
+
+ constructor(icon, message) {
+ super({}, new LinkMessageTemplate());
+ this.icon = icon;
+ this.message = message;
+ }
+
+}
+
customElements.define(LinkMessageElement.tagName, LinkMessageElement);
// @ts-check
@@ -3225,217 +3240,189 @@ class PinTemplate extends ITemplate {
}
}
-// @ts-check
-
-/**
- * @typedef {import("../element/PinElement").default} PinElement
- */
-class ExecPinTemplate extends PinTemplate {
-
- /**
- * @param {PinElement} pin
- */
- renderIcon(pin) {
- return html`
-
- `
- }
-}
-
-// @ts-check
-
-/**
- * @typedef {import("../element/PinElement").default} PinElement
- */
-class StringPinTemplate extends PinTemplate {
-
- hasInput() {
- return true
- }
-
- /**
- * @param {PinElement} pin
- */
- renderInput(pin) {
- return html`
-
-
-
- `
- }
+// @ts-check
+
+/**
+ * @typedef {import("../element/PinElement").default} PinElement
+ */
+class ExecPinTemplate extends PinTemplate {
+
+ /**
+ * @param {PinElement} pin
+ */
+ renderIcon(pin) {
+ return html`
+
+ `
+ }
}
// @ts-check
-class KeyboardIgnoreSelectAll extends KeyboardSelectAll {
+/**
+ * @typedef {import("../element/PinElement").default} PinElement
+ */
+class StringPinTemplate extends PinTemplate {
+
+ hasInput() {
+ return true
+ }
/**
- * @param {HTMLElement} target
- * @param {any} blueprint
- * @param {Object} options
+ * @param {PinElement} pin
*/
- constructor(target, blueprint, options = {}) {
- options = {
- ...options,
- activationKeys: blueprint.settings.selectAllKeyboardKey
- };
- super(target, blueprint, options);
- }
-
- fire() {
- }
-
- unfire() {
-
+ renderInput(pin) {
+ return html`
+
+
+
+ `
}
}
-// @ts-check
-
-/**
- * @typedef {import("../entity/GuidEntity").default} GuidEntity
- * @typedef {import("../entity/PinEntity").default} PinEntity
- * @typedef {import("./NodeElement").default} NodeElement
- */
-class PinElement extends IElement {
-
- static tagName = "ueb-pin"
-
- static #typeTemplateMap = {
- "exec": ExecPinTemplate,
- "string": StringPinTemplate,
- }
-
- /** @type {NodeElement} */
- nodeElement
-
- /** @type {HTMLElement} */
- clickableElement
-
- /** @type {String} */
- #color
-
- constructor(entity) {
- super(
- entity,
- new (PinElement.#typeTemplateMap[entity.getType()] ?? PinTemplate)()
- );
-
- /** @type {PinEntity} */
- this.entity;
-
- /** @type {PinTemplate} */
- this.template;
- }
-
- connectedCallback() {
- super.connectedCallback();
- this.#color = window.getComputedStyle(this).getPropertyValue("--ueb-pin-color");
- }
-
- createInputObjects() {
- return [
- new MouseCreateLink(this.clickableElement, this.blueprint, {
- moveEverywhere: true,
- looseTarget: true
- }),
- new KeyboardIgnoreSelectAll(this, this.blueprint, {
- consumeEvent: true
- })
- ]
- }
-
- /** @return {GuidEntity} */
- GetPinId() {
- return this.entity.PinId
- }
-
- /** @return {String} */
- GetPinIdValue() {
- return this.GetPinId().value
- }
-
- /**
- * @returns {String}
- */
- getPinName() {
- return this.entity.PinName
- }
-
- /**
- * @returns {String}
- */
- getPinDisplayName() {
- return this.entity.PinName
- }
-
- isInput() {
- return this.entity.isInput()
- }
-
- isOutput() {
- return this.entity.isOutput()
- }
-
- isLinked() {
- return this.entity.isLinked()
- }
-
- getType() {
- return this.entity.getType()
- }
-
- getClickableElement() {
- return this.clickableElement
- }
-
- getColor() {
- return this.#color
- }
-
- /**
- * Returns The exact location where the link originates from or arrives at.
- * @returns {Number[]} The location array
- */
- getLinkLocation() {
- return this.template.getLinkLocation(this)
- }
-
- /**
- * @returns {NodeElement}
- */
- getNodeElement() {
- return this.closest("ueb-node")
- }
-
- getLinks() {
- return this.entity.LinkedTo?.map(pinReference =>
- pinReference
- ) ?? []
- }
-
- /**
- * @param {PinElement} targetPinElement
- */
- linkTo(targetPinElement) {
- this.entity.linkTo(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity);
- this.template.applyConnected(this);
- }
-
- /**
- * @param {PinElement} targetPinElement
- */
- unlinkFrom(targetPinElement) {
- this.entity.unlinkFrom(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity);
- this.template.applyConnected(this);
- }
-}
-
+// @ts-check
+
+/**
+ * @typedef {import("../entity/GuidEntity").default} GuidEntity
+ * @typedef {import("../entity/PinEntity").default} PinEntity
+ * @typedef {import("./NodeElement").default} NodeElement
+ */
+class PinElement extends IElement {
+
+ static tagName = "ueb-pin"
+
+ static #typeTemplateMap = {
+ "exec": ExecPinTemplate,
+ "string": StringPinTemplate,
+ }
+
+ /** @type {NodeElement} */
+ nodeElement
+
+ /** @type {HTMLElement} */
+ clickableElement
+
+ /** @type {String} */
+ #color
+
+ constructor(entity) {
+ super(
+ entity,
+ new (PinElement.#typeTemplateMap[entity.getType()] ?? PinTemplate)()
+ );
+
+ /** @type {PinEntity} */
+ this.entity;
+
+ /** @type {PinTemplate} */
+ this.template;
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.#color = window.getComputedStyle(this).getPropertyValue("--ueb-pin-color");
+ }
+
+ createInputObjects() {
+ return [
+ new MouseCreateLink(this.clickableElement, this.blueprint, {
+ moveEverywhere: true,
+ looseTarget: true
+ })
+ ]
+ }
+
+ /** @return {GuidEntity} */
+ GetPinId() {
+ return this.entity.PinId
+ }
+
+ /** @return {String} */
+ GetPinIdValue() {
+ return this.GetPinId().value
+ }
+
+ /**
+ * @returns {String}
+ */
+ getPinName() {
+ return this.entity.PinName
+ }
+
+ /**
+ * @returns {String}
+ */
+ getPinDisplayName() {
+ return this.entity.PinName
+ }
+
+ isInput() {
+ return this.entity.isInput()
+ }
+
+ isOutput() {
+ return this.entity.isOutput()
+ }
+
+ isLinked() {
+ return this.entity.isLinked()
+ }
+
+ getType() {
+ return this.entity.getType()
+ }
+
+ getClickableElement() {
+ return this.clickableElement
+ }
+
+ getColor() {
+ return this.#color
+ }
+
+ /**
+ * Returns The exact location where the link originates from or arrives at.
+ * @returns {Number[]} The location array
+ */
+ getLinkLocation() {
+ return this.template.getLinkLocation(this)
+ }
+
+ /**
+ * @returns {NodeElement}
+ */
+ getNodeElement() {
+ return this.closest("ueb-node")
+ }
+
+ getLinks() {
+ return this.entity.LinkedTo?.map(pinReference =>
+ pinReference
+ ) ?? []
+ }
+
+ /**
+ * @param {PinElement} targetPinElement
+ */
+ linkTo(targetPinElement) {
+ this.entity.linkTo(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity);
+ this.template.applyConnected(this);
+ }
+
+ /**
+ * @param {PinElement} targetPinElement
+ */
+ unlinkFrom(targetPinElement) {
+ this.entity.unlinkFrom(targetPinElement.nodeElement.getNodeName(), targetPinElement.entity);
+ this.template.applyConnected(this);
+ }
+}
+
customElements.define(PinElement.tagName, PinElement);
// @ts-check
@@ -3534,83 +3521,85 @@ class NodeTemplate extends SelectableDraggableTemplate {
}
}
-// @ts-check
-
-class NodeElement extends ISelectableDraggableElement {
-
- static tagName = "ueb-node"
-
- /**
- * @param {ObjectEntity} entity
- */
- constructor(entity) {
- super(entity, new NodeTemplate());
- /** @type {ObjectEntity} */
- this.entity;
- /** @type {NodeTemplate} */
- this.template;
- this.dragLinkObjects = [];
- super.setLocation([this.entity.NodePosX.value, this.entity.NodePosY.value]);
- }
-
- static fromSerializedObject(str) {
- let entity = SerializerFactory.getSerializer(ObjectEntity).read(str);
- return new NodeElement(entity)
- }
-
- disconnectedCallback() {
- super.disconnectedCallback();
- this.dispatchDeleteEvent();
- }
-
- getNodeName() {
- return this.entity.getName()
- }
-
- getPinElements() {
- return this.template.getPinElements(this)
- }
-
- /**
- * @returns {PinEntity[]}
- */
- getPinEntities() {
- return this.entity.CustomProperties.filter(v => v instanceof PinEntity)
- }
-
- connectedCallback() {
- this.getAttribute("type")?.trim();
- super.connectedCallback();
- }
-
- setLocation(value = [0, 0]) {
- let nodeType = this.entity.NodePosX.constructor;
- // @ts-expect-error
- this.entity.NodePosX = new nodeType(value[0]);
- // @ts-expect-error
- this.entity.NodePosY = new nodeType(value[1]);
- super.setLocation(value);
- }
-
- dispatchDeleteEvent(value) {
- let deleteEvent = new CustomEvent(this.blueprint.settings.nodeDeleteEventName, {
- bubbles: true,
- cancelable: true,
- });
- this.dispatchEvent(deleteEvent);
- }
-}
-
+// @ts-check
+
+class NodeElement extends ISelectableDraggableElement {
+
+ static tagName = "ueb-node"
+
+ /**
+ * @param {ObjectEntity} entity
+ */
+ constructor(entity) {
+ super(entity, new NodeTemplate());
+ /** @type {ObjectEntity} */
+ this.entity;
+ /** @type {NodeTemplate} */
+ this.template;
+ this.dragLinkObjects = [];
+ super.setLocation([this.entity.NodePosX.value, this.entity.NodePosY.value]);
+ }
+
+ static fromSerializedObject(str) {
+ let entity = SerializerFactory.getSerializer(ObjectEntity).read(str);
+ return new NodeElement(entity)
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ this.dispatchDeleteEvent();
+ }
+
+ getNodeName() {
+ return this.entity.getName()
+ }
+
+ getPinElements() {
+ return this.template.getPinElements(this)
+ }
+
+ /**
+ * @returns {PinEntity[]}
+ */
+ getPinEntities() {
+ return this.entity.CustomProperties.filter(v => v instanceof PinEntity)
+ }
+
+ connectedCallback() {
+ this.getAttribute("type")?.trim();
+ super.connectedCallback();
+ }
+
+ setLocation(value = [0, 0]) {
+ let nodeType = this.entity.NodePosX.constructor;
+ // @ts-expect-error
+ this.entity.NodePosX = new nodeType(value[0]);
+ // @ts-expect-error
+ this.entity.NodePosY = new nodeType(value[1]);
+ super.setLocation(value);
+ }
+
+ dispatchDeleteEvent(value) {
+ let deleteEvent = new CustomEvent(this.blueprint.settings.nodeDeleteEventName, {
+ bubbles: true,
+ cancelable: true,
+ });
+ this.dispatchEvent(deleteEvent);
+ }
+}
+
customElements.define(NodeElement.tagName, NodeElement);
// @ts-check
class Paste extends IContext {
+ /** @type {(e: ClipboardEvent) => void} */
#pasteHandle
constructor(target, blueprint, options = {}) {
options.listenOnFocus = true;
+ options.unlistenOnTextEdit = true;
super(target, blueprint, options);
this.serializer = new ObjectSerializer();
let self = this;
diff --git a/js/Configuration.js b/js/Configuration.js
index 4545384..4149eeb 100755
--- a/js/Configuration.js
+++ b/js/Configuration.js
@@ -4,10 +4,14 @@ export default class Configuration {
deleteNodesKeyboardKey = "Delete"
editTextEventName = {
begin: "ueb-edit-text-begin",
- end: "ueb-edit-text-end"
+ end: "ueb-edit-text-end",
}
enableZoomIn = ["LeftControl", "RightControl"] // Button to enable more than 0 (1:1) zoom
expandGridSize = 400
+ focusEventName = {
+ begin: "blueprint-focus",
+ end: "blueprint-unfocus",
+ }
fontSize = "12px"
gridAxisLineColor = "black"
gridExpandThreshold = 0.25 // remaining size factor threshold to cause an expansion event
@@ -39,13 +43,13 @@ export default class Configuration {
selectAllKeyboardKey = "(bCtrl=True,Key=A)"
trackingMouseEventName = {
begin: "ueb-tracking-mouse-begin",
- end: "ueb-tracking-mouse-end"
+ end: "ueb-tracking-mouse-end",
}
ModifierKeys = [
"Ctrl",
"Shift",
"Alt",
- "Meta"
+ "Meta",
]
Keys = {
/* UE name: JS name */
diff --git a/js/element/PinElement.js b/js/element/PinElement.js
index d7e388c..7bd5214 100644
--- a/js/element/PinElement.js
+++ b/js/element/PinElement.js
@@ -5,7 +5,6 @@ import MouseCreateLink from "../input/mouse/MouseCreateLink"
import PinTemplate from "../template/PinTemplate"
import ExecPinTemplate from "../template/ExecPinTemplate"
import StringPinTemplate from "../template/StringPinTemplate"
-import KeyboardIgnoreSelectAll from "../input/keybaord/KeyboardIgnoreSelectAll"
/**
* @typedef {import("../entity/GuidEntity").default} GuidEntity
@@ -53,9 +52,6 @@ export default class PinElement extends IElement {
new MouseCreateLink(this.clickableElement, this.blueprint, {
moveEverywhere: true,
looseTarget: true
- }),
- new KeyboardIgnoreSelectAll(this, this.blueprint, {
- consumeEvent: true
})
]
}
diff --git a/js/input/IContext.js b/js/input/IContext.js
index 6ff5a55..8e0404a 100644
--- a/js/input/IContext.js
+++ b/js/input/IContext.js
@@ -6,30 +6,39 @@
export default class IContext {
/** @type {HTMLElement} */
- target
+ #target
+ get target() {
+ return this.#target
+ }
/** @type {Blueprint} */
- blueprint
+ #blueprint
+ get blueprint() {
+ return this.#blueprint
+ }
/** @type {Object} */
options
+ /**
+ * @param {HTMLElement} target
+ * @param {Blueprint} blueprint
+ * @param {Object} options
+ */
constructor(target, blueprint, options) {
- this.target = target
- this.blueprint = blueprint
+ this.#target = target
+ this.#blueprint = blueprint
this.options = options
+ this.options.listenOnFocus = this.options?.listenOnFocus ?? false
+ this.options.unlistenOnTextEdit = this.options?.unlistenOnTextEdit ?? false
let self = this
- this.listenHandler = _ => {
- self.listenEvents()
+ this.listenHandler = _ => self.listenEvents()
+ this.unlistenHandler = _ => self.unlistenEvents()
+ if (this.options.listenOnFocus) {
+ this.blueprint.addEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler)
+ this.blueprint.addEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler)
}
- this.unlistenHandler = _ => {
- self.unlistenEvents()
- }
- if (options?.listenOnFocus ?? false) {
- this.blueprint.addEventListener("blueprint-focus", this.listenHandler)
- this.blueprint.addEventListener("blueprint-unfocus", this.unlistenHandler)
- }
- if (options?.unlistenOnEditText ?? false) {
+ if (options?.unlistenOnTextEdit ?? false) {
this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler)
this.blueprint.addEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler)
}
@@ -37,8 +46,8 @@ export default class IContext {
unlistenDOMElement() {
this.unlistenEvents()
- this.blueprint.removeEventListener("blueprint-focus", this.listenHandler)
- this.blueprint.removeEventListener("blueprint-unfocus", this.unlistenHandler)
+ this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.begin, this.listenHandler)
+ this.blueprint.removeEventListener(this.blueprint.settings.focusEventName.end, this.unlistenHandler)
this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.begin, this.unlistenHandler)
this.blueprint.removeEventListener(this.blueprint.settings.editTextEventName.end, this.listenHandler)
}
diff --git a/js/input/common/Copy.js b/js/input/common/Copy.js
index 56f709e..402a5df 100755
--- a/js/input/common/Copy.js
+++ b/js/input/common/Copy.js
@@ -5,10 +5,12 @@ import ObjectSerializer from "../../serialization/ObjectSerializer"
export default class Copy extends IContext {
+ /** @type {(e: ClipboardEvent) => void} */
#copyHandler
constructor(target, blueprint, options = {}) {
options.listenOnFocus = true
+ options.unlistenOnTextEdit = true
super(target, blueprint, options)
this.serializer = new ObjectSerializer()
let self = this
diff --git a/js/input/common/Paste.js b/js/input/common/Paste.js
index a54ce3b..c38304b 100755
--- a/js/input/common/Paste.js
+++ b/js/input/common/Paste.js
@@ -6,10 +6,12 @@ import ObjectSerializer from "../../serialization/ObjectSerializer"
export default class Paste extends IContext {
+ /** @type {(e: ClipboardEvent) => void} */
#pasteHandle
constructor(target, blueprint, options = {}) {
options.listenOnFocus = true
+ options.unlistenOnTextEdit = true
super(target, blueprint, options)
this.serializer = new ObjectSerializer()
let self = this
diff --git a/js/input/keybaord/KeyboardIgnoreSelectAll.js b/js/input/keybaord/KeyboardIgnoreSelectAll.js
deleted file mode 100755
index f873ef2..0000000
--- a/js/input/keybaord/KeyboardIgnoreSelectAll.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// @ts-check
-
-import KeyboardSelectAll from "./KeyboardSelectAll"
-
-export default class KeyboardIgnoreSelectAll extends KeyboardSelectAll {
-
- /**
- * @param {HTMLElement} target
- * @param {any} blueprint
- * @param {Object} options
- */
- constructor(target, blueprint, options = {}) {
- options = {
- ...options,
- activationKeys: blueprint.settings.selectAllKeyboardKey
- }
- super(target, blueprint, options)
- }
-
- fire() {
- }
-
- unfire() {
-
- }
-}
diff --git a/js/template/StringPinTemplate.js b/js/template/StringPinTemplate.js
index baf110d..3f5816f 100644
--- a/js/template/StringPinTemplate.js
+++ b/js/template/StringPinTemplate.js
@@ -20,8 +20,8 @@ export default class StringPinTemplate extends PinTemplate {
return html`
`
diff --git a/scss/ueb-pin.scss b/scss/ueb-pin.scss
index 9299b7c..49ec64b 100644
--- a/scss/ueb-pin.scss
+++ b/scss/ueb-pin.scss
@@ -100,6 +100,7 @@ ueb-pin.ueb-pin-fill .ueb-pin-icon-value::before {
max-width : 400px;
max-height: 16em;
background: none;
+ color : inherit;
cursor : text;
overflow : auto;