From e8815c1d95afe888e9e0797c462e0da1963d2d25 Mon Sep 17 00:00:00 2001 From: Sumner Date: Wed, 17 Jul 2019 14:59:28 +0200 Subject: [PATCH 1/2] major refractor --- build/js/d3sm.min.v0.0.3.js | 2 - build/js/d3sm.min.v0.0.4.js | 2 - build/js/d3sm.v0.0.3.js | 10613 --- build/js/d3sm.v0.0.4.js | 10613 --- d3sm-legacy.js | 2171 - data/1-col-heatmap.js | 1138 - data/2-col-heatmap.js | 6924 -- data/d3sm-data.js | 933 - data/d3sm-logo.afdesign | Bin 47050 -> 0 bytes data/d3sm.icon.png | Bin 2013 -> 0 bytes data/d3sm.png | Bin 73103 -> 0 bytes data/d3sm.svg | 1 - data/data.js | 13 - data/datasets.js | 135 - data/icon.afdesign | Bin 23157 -> 0 bytes data/upset.js | 72292 ---------------- data/upset0.js | 5583 -- data/upset1.js | 23910 ----- demos/axes/index.html | 279 - demos/axes/js/v001.js | 196 - demos/axes/js/v003.js | 0 .../index.html | 195 - .../js/v001.js | 125 - demos/basic-violins/index.html | 172 - demos/box-whiskers-points/index.html | 154 - demos/box-whiskers/index.html | 155 - demos/bubble-heatmap/index.html | 161 - demos/bubble-heatmap/js/v000.js | 96 - demos/bundle/index.html | 52 - demos/filter-select/index.html | 155 - demos/filter-select/js/v002.txt | 0 demos/filter-table/index.html | 120 - demos/filter-table/js/v003.js | 222 - demos/heatmap/index.html | 70 - demos/heatmap/js/v000.js | 98 - demos/heatmap/js/v003.js | 132 - demos/index.html | 37 +- demos/scatter/index.html | 141 - demos/scatter/js/v001.js | 103 - demos/scatter/js/v002.js | 117 - demos/scatter/js/v003.js | 187 - demos/tooltip-design/index.html | 86 - demos/upset/index.html | 131 - demos/upset/js/v003.js | 300 - demos/vertical-violins/index.html | 147 - dist/d3sm.esm.js | 10601 +-- dist/d3sm.min.js | 2 +- dist/d3sm.umd.js | 10601 +-- index.html | 6 +- needs-to-be-bundled/distribution.js | 241 - needs-to-be-bundled/graph.js | 806 - needs-to-be-bundled/scatter.js | 265 - needs-to-be-bundled/tooltip.js | 79 - rollup.config.js | 34 +- src/entry.js | 27 + src/modules/aux/index.js | 9 + src/{scripts/modules => modules/aux}/lasso.js | 64 +- .../aux}/multi-plot-zoom.js | 29 +- .../modules => modules/aux}/plot-zoom.js | 29 +- src/{scripts => }/modules/axis.js | 77 +- .../modules => modules/charts}/bar.js | 22 +- .../modules => modules/charts}/box-whisker.js | 38 +- .../charts}/bubble-heatmap.js | 60 +- .../modules => modules/charts}/heatmap.js | 54 +- src/modules/charts/index.js | 14 + .../modules => modules/charts}/scatter.js | 13 +- .../modules => modules/charts}/upset.js | 46 +- .../modules => modules/charts}/violin.js | 83 +- src/{scripts => }/modules/color-function.js | 6 +- src/{scripts => }/modules/d3-prototypes.js | 4 +- src/{scripts => }/modules/grouping-spacer.js | 10 +- .../legends}/categorical-legend.js | 23 +- src/modules/legends/index.js | 9 + .../legends}/numeric-legend.js | 38 +- src/{scripts => }/modules/tooltip.js | 31 +- .../utils/array.js} | 17 +- src/modules/utils/colors.js | 32 + src/modules/utils/console.js | 97 + src/modules/utils/index.js | 16 + src/modules/utils/math.js | 160 + src/modules/utils/misc.js | 139 + src/modules/utils/paths.js | 40 + src/modules/utils/selections.js | 120 + src/modules/utils/strings.js | 41 + src/scripts/main.js | 116 - src/scripts/modules/data-toggle.js | 210 - src/scripts/modules/filter-table.js | 118 - src/scripts/modules/helpers.js | 273 - src/scripts/modules/lasso-leg.js | 748 - src/scripts/modules/lasso-widget.js | 606 - src/scripts/modules/points.js | 95 - src/scripts/modules/select-filter.js | 110 - src/scripts/modules/utils.js | 599 - src/styles/main.css | 3 - 94 files changed, 9872 insertions(+), 154950 deletions(-) delete mode 100644 build/js/d3sm.min.v0.0.3.js delete mode 100644 build/js/d3sm.min.v0.0.4.js delete mode 100644 build/js/d3sm.v0.0.3.js delete mode 100644 build/js/d3sm.v0.0.4.js delete mode 100644 d3sm-legacy.js delete mode 100644 data/1-col-heatmap.js delete mode 100644 data/2-col-heatmap.js delete mode 100644 data/d3sm-data.js delete mode 100644 data/d3sm-logo.afdesign delete mode 100644 data/d3sm.icon.png delete mode 100644 data/d3sm.png delete mode 100644 data/d3sm.svg delete mode 100644 data/data.js delete mode 100644 data/datasets.js delete mode 100644 data/icon.afdesign delete mode 100644 data/upset.js delete mode 100644 data/upset0.js delete mode 100644 data/upset1.js delete mode 100644 demos/axes/index.html delete mode 100644 demos/axes/js/v001.js delete mode 100644 demos/axes/js/v003.js delete mode 100644 demos/bar-chart-same-data-complex-grouping/index.html delete mode 100644 demos/bar-chart-same-data-complex-grouping/js/v001.js delete mode 100644 demos/basic-violins/index.html delete mode 100644 demos/box-whiskers-points/index.html delete mode 100644 demos/box-whiskers/index.html delete mode 100644 demos/bubble-heatmap/index.html delete mode 100644 demos/bubble-heatmap/js/v000.js delete mode 100644 demos/bundle/index.html delete mode 100644 demos/filter-select/index.html delete mode 100644 demos/filter-select/js/v002.txt delete mode 100644 demos/filter-table/index.html delete mode 100644 demos/filter-table/js/v003.js delete mode 100644 demos/heatmap/index.html delete mode 100644 demos/heatmap/js/v000.js delete mode 100644 demos/heatmap/js/v003.js delete mode 100644 demos/scatter/index.html delete mode 100644 demos/scatter/js/v001.js delete mode 100644 demos/scatter/js/v002.js delete mode 100644 demos/scatter/js/v003.js delete mode 100644 demos/tooltip-design/index.html delete mode 100644 demos/upset/index.html delete mode 100644 demos/upset/js/v003.js delete mode 100644 demos/vertical-violins/index.html delete mode 100644 needs-to-be-bundled/distribution.js delete mode 100644 needs-to-be-bundled/graph.js delete mode 100644 needs-to-be-bundled/scatter.js delete mode 100644 needs-to-be-bundled/tooltip.js create mode 100644 src/entry.js create mode 100644 src/modules/aux/index.js rename src/{scripts/modules => modules/aux}/lasso.js (87%) rename src/{scripts/modules => modules/aux}/multi-plot-zoom.js (88%) rename src/{scripts/modules => modules/aux}/plot-zoom.js (87%) rename src/{scripts => }/modules/axis.js (91%) rename src/{scripts/modules => modules/charts}/bar.js (96%) rename src/{scripts/modules => modules/charts}/box-whisker.js (94%) rename src/{scripts/modules => modules/charts}/bubble-heatmap.js (91%) rename src/{scripts/modules => modules/charts}/heatmap.js (92%) create mode 100644 src/modules/charts/index.js rename src/{scripts/modules => modules/charts}/scatter.js (97%) rename src/{scripts/modules => modules/charts}/upset.js (84%) rename src/{scripts/modules => modules/charts}/violin.js (92%) rename src/{scripts => }/modules/color-function.js (98%) rename src/{scripts => }/modules/d3-prototypes.js (95%) rename src/{scripts => }/modules/grouping-spacer.js (97%) rename src/{scripts/modules => modules/legends}/categorical-legend.js (93%) create mode 100644 src/modules/legends/index.js rename src/{scripts/modules => modules/legends}/numeric-legend.js (75%) rename src/{scripts => }/modules/tooltip.js (85%) rename src/{scripts/modules/array-functions.js => modules/utils/array.js} (92%) create mode 100644 src/modules/utils/colors.js create mode 100644 src/modules/utils/console.js create mode 100644 src/modules/utils/index.js create mode 100644 src/modules/utils/math.js create mode 100644 src/modules/utils/misc.js create mode 100644 src/modules/utils/paths.js create mode 100644 src/modules/utils/selections.js create mode 100644 src/modules/utils/strings.js delete mode 100644 src/scripts/main.js delete mode 100644 src/scripts/modules/data-toggle.js delete mode 100644 src/scripts/modules/filter-table.js delete mode 100644 src/scripts/modules/helpers.js delete mode 100644 src/scripts/modules/lasso-leg.js delete mode 100644 src/scripts/modules/lasso-widget.js delete mode 100644 src/scripts/modules/points.js delete mode 100644 src/scripts/modules/select-filter.js delete mode 100644 src/scripts/modules/utils.js delete mode 100644 src/styles/main.css diff --git a/build/js/d3sm.min.v0.0.3.js b/build/js/d3sm.min.v0.0.3.js deleted file mode 100644 index fcd6774..0000000 --- a/build/js/d3sm.min.v0.0.3.js +++ /dev/null @@ -1,2 +0,0 @@ -var d3sm=function(t){"use strict";function e(t,e,n){return n.indexOf(t)===e}function n(t){var e=document.createElementNS("http://www.w3.org/2000/svg","g");t=void 0==t?"translate(0,0)":t,e.setAttributeNS(null,"transform",t);var n=e.transform.baseVal.consolidate().matrix;return[n.e,n.f]}function r(t,e){(t=String(t).replace(/[^0-9a-f]/gi,"")).length<6&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]),e=e||0;var n,r,o="#";for(r=0;r<3;r++)n=parseInt(t.substr(2*r,2),16),o+=("00"+(n=Math.round(Math.min(Math.max(0,n+n*e),255)).toString(16))).substr(n.length);return o}function o(t,e){var n=d3.median(t),r=t.filter(function(t){return tn}),a=void 0==(a=d3.median(r))?n:a,i=void 0==(i=d3.min(r))?a:i,l=void 0==(l=d3.median(o))?n:l,c=void 0==(c=d3.max(o))?l:c,u="q0",s="q1",d="q2",f="q3",h="q4",g={};return void 0!=e&&5==e.length&&(u=e[0],s=e[1],d=e[2],f=e[3],h=e[4]),g[u]=i,g[s]=a,g[d]=n,g[f]=l,g[h]=c,g}function a(){return Array.prototype.slice.call(arguments).join("-")}function i(t,e){var n=function(t,e,n){n&&(e=-e);var r=(""+t).split("e");return+(r[0]+"e"+(r[1]?+r[1]+e:e))};return n(Math.round(n(t,e,!1)),e,!0)}function l(t){var e=t.parentElement,n=e.tagName.toLowerCase();return"svg"===n?e:"html"!==n?l(e):void 0}function c(t,e,n){var r=void 0==n?"":"."+n,o=t.select(e+r).empty()?t.append(e):t.select(e+r);return o.classed(r.replace(".",""),!0).attr("transform",void 0==o.attr("transform")?"translate(0,0)":o.attr("transform"))}function u(t,e,n){for(var r=[t],o=(e-t)/(n-1),a=0;a1?o:t*o),l=(i=i<0?0:i)/e;return a&&void 0!=n&&l=r.length?r.push(e.length-1):r[n]+=e.length-1;e.map(function(e,o){Array.isArray(e)&&t(e,n,r)});return r}(t);o=(e-n*r)/i.map(function(t,e){return 1*t/(e+1)}).reduce(function(t,e){return t+e},0);return isNaN(o)?0:o}function b(t,e,n,r,o,a,i){if("up"!=t&&"top"!=t&&1!=t||(t=!0),"down"!=t&&"bottom"!=t&&0!=t||(t=!1),i=void 0==i?"horizontal":i,a=void 0==a?1:a,"horizontal"!=i){var l=o*a,c=(r=t?r:-r,t?e+r:e),u=t?e:e+r,s=t?c:u;return f="M "+c+" "+o/2+" L "+u+" "+o/2+" M "+s+" "+(o/2-l/2)+" L "+s+" "+(o/2+l/2)+" "}var d=r*a,f="M "+r/2+" "+(c=t?n+o:n)+" L "+r/2+" "+(u=t?n:n+o)+" h "+-d/2+" 0 h "+d+" 0 ";return f}function k(){var t,e,n,r=!0,o=d3.scaleLinear(),a="category",i="d3sm-groupped-item",l=1e3,c=d3.easeSin,u="spacer",s=function(t){t.attr("transform",function(t,e){return"translate("+(r?window.outerWidth:0)+","+(r?0:window.outerWidth)+")"})},d=function(t){m("groupingSpacer","exiting with",{current:t,currentNode:t.node()}),t.selectAll("g").classed("to-remove",!0),t.transition().duration(.9*l).ease(c).attr("transform",function(t,e){return"translate("+(r?window.outerWidth:0)+","+(r?0:window.outerWidth)+")"}).remove()};function f(t,h,g){void 0==g&&(g=0);var p=t.selectAll("g."+u+'[level="'+g+'"]').data(h),m=p.enter().append("g").attr("level",g).attr("class",u),v=p.exit();p=p.merge(m),"function"==typeof d?v.each(function(t,e){d(d3.select(this))}):v.remove();var y=n/(g+1),x=0;return p.each(function(t,n){var h=d3.select(this);if(void 0==h.attr("transform")&&"function"==typeof s&&s(h),h.transition().duration(l).ease(c).attr("transform",function(t,e){return"translate("+(r?"scale"==a?o(t):x:0)+","+(r?0:"scale"==a?o(t):x)+")"}),Array.isArray(t)){x+=f(h,t,g+1);var m=h.selectAll("g."+u+'[level="'+g+'"] > g.'+i+"."+u);"function"==typeof d?m.each(function(t,e){d(d3.select(this))}):m.remove()}else{x+=e;var v=h.select("g."+u+'[level="'+g+'"] > g.'+i+"."+u);v.empty()&&(v=h.append("g").attr("class",i).classed(u,!0)),v.attr("parent-index",n);m=h.selectAll("g."+u+'[level="'+(g+1)+'"]');"function"==typeof d?m.each(function(t,e){d(d3.select(this))}):m.remove()}x+=n==p.size()-1?0:y}),x}return f.horizontalQ=function(t){return arguments.length?(r=t,f):r},f.scale=function(t){return arguments.length?(o=t,f):o},f.moveby=function(t){return arguments.length?(a=t,f):a},f.numberOfObjects=function(e){return arguments.length?(t=e,f):t},f.objectClass=function(t){return arguments.length?(i=t,f):i},f.objectSize=function(t){return arguments.length?(e=t,f):e},f.spacerSize=function(t){return arguments.length?(n=t,f):n},f.transitionDuration=function(t){return arguments.length?(l=t,f):l},f.easeFunc=function(t){return arguments.length?(c=t,f):c},f.namespace=function(t){return arguments.length?(u=t,f):u},f.enterFunction=function(t){return arguments.length?(s=t,f):s},f.exitFunction=function(t){return arguments.length?(d=t,f):d},f}var w=function(){return function(t,e){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return function(t,e){var n=[],r=!0,o=!1,a=void 0;try{for(var i,l=t[Symbol.iterator]();!(r=(i=l.next()).done)&&(n.push(i.value),!e||n.length!==e);r=!0);}catch(t){o=!0,a=t}finally{try{!r&&l.return&&l.return()}finally{if(o)throw a}}return n}(t,e);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),S=function(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);ewindow.innerWidth-window.scrollX&&(d=d3.event.pageX-b.width-15),f+b.height>window.innerHeight-window.scrollY&&(f=d3.event.pageY-b.height-15),"relative"==h.style("position")?h.style("position","absolute").style("left",d+"px").style("top",f+"px"):h.style("left",d+"px").style("top",f+"px"),h.attr("z-index",1e4)}return a.keys=function(t){return arguments.length?(e=t,a):e},a.values=function(t){return arguments.length?(n=t,a):n},a.header=function(t){return arguments.length?(r=t,a):r},a.data=function(t){return arguments.length?(o=t,a):o},a.selection=function(e){return arguments.length?(t=e,a):t},a}function A(t){var e,n="d3sm-select-filter",r="Select options:",o=void 0,i=void 0;function l(){var o=c(t,"div","input-group").classed(a(n,"container"),!0),l=(c(c(o,"div","select-prepend").classed("input-group-prepend",!0),"span","input-group-text").text(r),c(o,"select","custom-select").classed(a(n,"select"),!0)),s=c(c(o,"div","select-append").classed("input-group-prepend",!0),"a","filter-button").classed("btn btn-outline-secondary",!0),d=(c(s,"i","fa fa-filter"),c(o,"div","filter-input-group").classed("input-group",!0).classed("d-none",!0)),f=(c(c(c(d,"div","input-group-prepend"),"span","input-group-text").classed("search-button",!0),"i","fa fa-search"),c(d,"input","form-control").attr("placeholder","all").attr("type","text")),h=c(c(d,"div","input-group-append"),"a","close-button").classed("btn btn-outline-secondary",!0),g=(c(h,"i","fa fa-close"),d3.keys(e)),p=l.selectAll("option");p=(p=p.data(d3.keys(e))).merge(p.enter().append("option")).attr("value",function(t,e){return t}).text(function(t,e){return t});var m=h;s.on("click",function(t,e){var n=d.classed("d-none");d.classed("d-none",!n)}),m.on("click",function(t,e){f.property("value","").dispatch("input")}),f.on("input",function(t,n){var r,o=f.property("value"),a=new RegExp(o,"gi");""==o?r=g:(r=[],d3.keys(e).map(function(t,e){var n=t.match(a);null==n||""==n.join("")||r.push(t)})),(p=(p=l.selectAll("option")).data(r)).exit().remove(),p=p.merge(p.enter().append("option")).attr("value",function(t,e){return t}).text(function(t,e){return t});var c=u();i!=c&&(i=c,l.dispatch("change"))})}function u(){var n=t.select("select").property("value");return void 0==n||""==n?void 0==o?d3.keys(e)[0]:o:n}return l.data=function(t){return arguments.length?(e=t,l):e},l.namespace=function(t){return arguments.length?(n=t,l):n},l.selectionName=function(t){return arguments.length?(r=t,l):r},l.defaultValue=function(t){return arguments.length?(o=t,l):o},l.currentOption=u,l}function M(t){var e=t.attr("transform").split("translate("),n=w(e,2),r=(n[0],n[1].split(",")),o=w(r,2),a=o[0],i=o[1];return i.split(")"),[parseFloat(a),parseFloat(i)]}function E(t){var e,n,r,o,i,l,u,s,f,h,g="d3sm-lasso",p=!1,m=[],v=[],y=d3.line().x(function(t,e){return void 0!=s?s(t[0]):t[0]}).y(function(t,e){return void 0!=f?f(t[1]):t[1]}).curve(d3.curveLinearClosed),x=0,b=10,k="#17a2b8",w="10s",S=.3,j="5, 10",z="black",A=2,E="white",O="black",L=3,C=1e3,F=d3.easeExp;function Q(){var t,e;p&&((t=(t=c(n,"g","lasso-container").selectAll('path[instance="'+x+'"]')).data(v)).exit().remove(),e=t.enter().append("path"),V(t=t.merge(e).transition().duration(C).ease(F)))}function B(){var t=c(n,"g","lasso-container");t.selectAll('path[instance="'+x+'"]').remove();t.remove(),n.selectAll(r).classed("in-lasso",!1),Y()}function q(t){t.preventDefault(),t.stopPropagation();var r=c(n,"g","lasso-container");m=[],e.node().addEventListener("mousemove",D),e.node().addEventListener("mouseup",function(t){e.node().removeEventListener("mousemove",D),v.push(m),v=d(v)}),V(h=r.append("path").data([m]))}function V(t){t.attr("class",a(g,"lasso-path")).style("opacity",S).attr("fill",k).attr("d",y).attr("instance",x).style("stroke-dasharray",j).attr("stroke",z).attr("stroke-width",A).style("animation","lassoDash "+w+" linear").style("animation-iteration-count","infinite")}function D(t){if(void 0!=u&&u.dispatch(a(g,"drag")),1==t.which){d3.event=t;var r=d3.mouse(n.node());r=d3.mouse(e.node());if(void 0!=s&&(r[0]=s.invert(r[0])),void 0!=f&&(r[1]=f.invert(r[1])),r[0]=r[0]-i[0]-l[0],r[1]=r[1]-i[1]-l[1],m.length){var o=m[m.length-1],c=[r[0],r[1]],d=[o[0],o[1]];s&&(d[0]=s(d[0]),c[0]=s(c[0])),f&&(d[1]=f(d[1]),c[1]=f(c[1])),function(t,e){var n=t[0]-e[0],r=t[1]-e[1];return Math.sqrt(n*n+r*r)}(d,c)>b&&_(r)}else _(r)}}function _(t){if(void 0!=t){if(m.push(t),h.attr("d",y),m.length<3)return;K(v.concat([m]))}else K(v)}function K(t){return void 0==t&&(t=v),n.selectAll(r).each(function(e,n){var r=d3.select(this),o=r.absolutePosition(),a=[[o.left-i[0]-l[0],o.top-i[1]-l[1]],[o.right-i[0]-l[0],o.top-i[1]-l[1]],[o.left-i[0]-l[0],o.bottom-i[1]-l[1]],[o.right-i[0]-l[0],o.bottom-i[1]-l[1]]];void 0!=s&&(a[0][0]=s.invert(a[0][0]),a[1][0]=s.invert(a[1][0]),a[2][0]=s.invert(a[2][0]),a[3][0]=s.invert(a[3][0])),void 0!=f&&(a[0][1]=f.invert(a[0][1]),a[1][1]=f.invert(a[1][1]),a[2][1]=f.invert(a[2][1]),a[3][1]=f.invert(a[3][1]));var c=!1;for(n=0;nwindow.innerWidth&&o.style("left",d3.event.pageX-15-300+"px")}function ot(t,e){var n=d3.select(this).style("fill","black");if(d3.select(n.node().parentNode).select("line."+a(B,"tick")).attr("stroke",K).attr("stroke-width",Y),M){var o=d3.select(n.node().parentNode).select("line."+a(B,"guideline")),i=o.attr("minor");o.attr("stroke",function(t,e){return"true"==i?r(I,.8):I}).attr("stroke-width",function(t,e){return"true"==i?.8*J:J})}d3.select("#"+a(B,"guideline-tooltip")).remove()}return nt.label=function(t){return arguments.length?(m=t,nt):m},nt.tickTickLabelSpacer=function(t){return arguments.length?(X=t,nt):X},nt.tickLabelMargin=function(t){return arguments.length?(R=t,nt):R},nt.selection=function(e){return arguments.length?(t=e,nt):t},nt.orient=function(t){return arguments.length?(b=t,nt):b},nt.spaceX=function(t){return arguments.length?(w=t,nt):w},nt.spaceY=function(t){return arguments.length?(j=t,nt):j},nt.overflowQ=function(t){return arguments.length?(z=t,nt):z},nt.categoricalQ=function(t){return arguments.length?(A=t,nt):A},nt.guideLinesQ=function(t){return arguments.length?(M=t,nt):M},nt.grouping=function(t){return arguments.length?(e=t,nt):e},nt.scale=function(t){return arguments.length?(E=t,nt):E},nt.domainPadding=function(t){return arguments.length?(O=t,nt):O},nt.objectSpacer=function(t){return arguments.length?(L=t,nt):L},nt.minObjectSize=function(t){return arguments.length?(C=t,nt):C},nt.maxObjectSize=function(t){return arguments.length?(F=t,nt):F},nt.namespace=function(t){return arguments.length?(B=t,nt):B},nt.backgroundFill=function(t){return arguments.length?(Q=t,nt):Q},nt.objectClass=function(t){return arguments.length?(q=t,nt):q},nt.tickLabels=function(t){return arguments.length?(n=t,nt):n},nt.tickValues=function(t){return arguments.length?(o=t,nt):o},nt.numberOfTicks=function(t){return arguments.length?(V=t,nt):V},nt.lineStroke=function(t){return arguments.length?(D=t,nt):D},nt.lineStrokeWidth=function(t){return arguments.length?(_=t,nt):_},nt.tickStroke=function(t){return arguments.length?(K=t,nt):K},nt.tickStrokeWidth=function(t){return arguments.length?(Y=t,nt):Y},nt.tickLength=function(t){return arguments.length?(P=t,nt):P},nt.tickLabelFontSize=function(t){return arguments.length?(W=t,nt):W},nt.tickLabelMinFontSize=function(t){return arguments.length?(N=t,nt):N},nt.tickLabelMaxFontSize=function(t){return arguments.length?(T=t,nt):T},nt.tickLabelTextAnchor=function(t){return arguments.length?(l=t,nt):l},nt.tickLabelRotation=function(t){return arguments.length?(d=t,nt):d},nt.tickLabelFunc=function(t){return arguments.length?(G=t,nt):G},nt.tickLabelOnClick=function(t){return arguments.length?(H=t,nt):H},nt.guidelineSpace=function(t){return arguments.length?(h=t,nt):h},nt.guideLineStroke=function(t){return arguments.length?(I=t,nt):I},nt.guideLineStrokeWidth=function(t){return arguments.length?(J=t,nt):J},nt.transitionDuration=function(t){return arguments.length?(U=t,nt):U},nt.easeFunc=function(t){return arguments.length?($=t,nt):$},nt.objectSize=function(t){return arguments.length?(g=t,nt):g},nt.spacerSize=function(t){return arguments.length?(p=t,nt):p},nt.roundTo=function(t){return arguments.length?(tt=t,nt):tt},nt.reverseScaleQ=function(t){return arguments.length?(et=t,nt):et},nt.tickLabelOnHoverFunc=function(t){return arguments.length?(Z=t,nt):Z},nt},O.bar=function(t){var e,n,r,o,a,i,l,u,s="horizontal",d=!1,h=function(t,n){return e[t]},g=function(t,n){return d3.descending(e[t],e[n])},p=d3.scaleLinear(),m=.5,b=.05,w=50,A=100,M=2,E=j(),O="transparent",L="d3sm-bar",C="bar",F=1e3,Q=d3.easeExp,B=z(),q=1;function V(){var j="horizontal"==s||"bottom"==s||"top"==s,z=!j,V=v(t,L,{x:0,y:0,width:n,height:r},O);a=d3.keys(e),i=a.map(h);var D=void 0==o?a.sort(g):o,_=(a=f(D)).length,K=[Math.min.apply(Math,S(i))-m,Math.max.apply(Math,S(i))+m];p.domain(K).range(j?[0,r]:"right"==s?[0,n]:[n,0]);var Y=j?n:r;l=void 0==l?y(Y,_,w,A,b,d):l,u=void 0==u?x(a,Y,l,_,b,d):u;var P=k().horizontalQ(j).scale(p).moveby("category").numberOfObjects(_).objectClass(C).objectSize(l).spacerSize(u).transitionDuration(F).easeFunc(Q).namespace(L),X=P.exitFunction();P.exitFunction(function(t){void 0==l&&console.log(t.nodes(),l),X(t),t.selectAll("g").classed("to-remove",!0),t.selectAll("* > rect").transition().duration(F).attr("transform",function(t,e){return"translate(0,"+(z?0:p(K[1]))+")"}).attr("width",j?l:0).attr("height",z?l:0).remove()}),P(V,D,0);var R=[];V.selectAll("g:not(.to-remove)."+C).each(function(t,e){R.push(Number(d3.select(this).attr("parent-index")))}),E="index"==E.colorBy()?E.dataExtent([0,Math.max.apply(Math,R)]):E.dataExtent(K),V.selectAll("g."+C+":not(.to-remove)").each(function(t,n){var r=d3.select(this),o=(e[t],h(t,n)),a=(n=void 0==r.attr("parent-index")?n:r.attr("parent-index"),E(t,o,n,"fill")),i=E(t,o,n,"stroke"),u=c(r,"rect","bar-rect");void 0==u.attr("transform")&&u.attr("transform",function(t,e){return"translate(0,"+(z?0:p(K[1]))+")"}).attr("width",j?l:0).attr("height",z?l:0),u.transition().duration(F).ease(Q).attr("transform",function(t,e){return"translate("+(j?l-l*q:"right"==s?p(K[1])-p(o):l-l*q)+","+(z?l-l*q:p(K[1])-p(o))+")"}).attr("width",j?l*q:p(o)).attr("height",z?l*q:p(o)).attr("fill",a).attr("stroke",i).attr("stroke-width",M),r.on("mouseover",function(t,e){V.selectAll("g."+C).style("opacity",.2),r.style("opacity",1),u.attr("stroke-width",2*M)}),r.on("mouseout",function(){V.selectAll("g."+C).style("opacity",1),u.attr("stroke-width",M)})}),B.selection(V.selectAll(".bar-rect")).data(e),B()}return V.selection=function(e){return arguments.length?(t=e,V):t},V.data=function(t){return arguments.length?(e=t,V):e},V.orient=function(t){return arguments.length?(s=t,V):s},V.spaceX=function(t){return arguments.length?(n=t,V):n},V.spaceY=function(t){return arguments.length?(r=t,V):r},V.overflowQ=function(t){return arguments.length?(d=t,V):d},V.grouping=function(t){return arguments.length?(o=t,V):o},V.valueExtractor=function(t){return arguments.length?(h=t,V):h},V.sortingFunction=function(t){return arguments.length?(g=t,V):g},V.scale=function(t){return arguments.length?(p=t,V):p},V.domainPadding=function(t){return arguments.length?(m=t,V):m},V.objectSpacer=function(t){return arguments.length?(b=t,V):b},V.minObjectSize=function(t){return arguments.length?(w=t,V):w},V.maxObjectSize=function(t){return arguments.length?(A=t,V):A},V.barStrokeWidth=function(t){return arguments.length?(M=t,V):M},V.colorFunction=function(t){return arguments.length?(E=t,V):E},V.backgroundFill=function(t){return arguments.length?(O=t,V):O},V.namespace=function(t){return arguments.length?(L=t,V):L},V.objectClass=function(t){return arguments.length?(C=t,V):C},V.transitionDuration=function(t){return arguments.length?(F=t,V):F},V.easeFunc=function(t){return arguments.length?(Q=t,V):Q},V.barKeys=function(t){return arguments.length?(a=t,V):a},V.barValues=function(t){return arguments.length?(i=t,V):i},V.objectSize=function(t){return arguments.length?(l=t,V):l},V.spacerSize=function(t){return arguments.length?(u=t,V):u},V.tooltip=function(t){return arguments.length?(B=t,V):B},V.barPercent=function(t){return arguments.length?(q=t,V):q},V},O.bubbleHeatmap=function(t){var e,n,r,o,i,l,u,s,f,h,g,p,b="x",w="y",A="r",M="v",E=function(t,n){return e[t][b]},O=function(t,n){return e[t][w]},L=function(t,n){return e[t][A]},C=function(t,n){return e[t][M]},F=!1,Q=d3.scaleLinear(),B=.5,q=0,V=50,D=100,_=2,K="transparent",Y="d3sm-bubble",P="bubble",X=1e3,R=d3.easeExp,W=function(t,e){return E(t)-E(e)},N=function(t,e){return O(t)-O(e)},T=j().colorBy("value"),G=z();function H(){var b=v(t,Y,{x:0,y:0,width:n,height:r},K);(o=d3.keys(e)).sort(function(t,e){return W(t,e)||N(t,e)}),m("bubbleHeatmap","cells are sorted by",o),i=d(o.map(E)),l=d(o.map(O)),u=d(o.map(L)),s=d(o.map(C)),m("bubbleHeatmap","x and y keys are",{x:i,y:l});var w=i.length,j=l.length,z=[Math.min.apply(Math,S(u))-B,Math.max.apply(Math,S(u))+B];g=y(r,j,V,D,q,F),f=y(n,w,V,D,q,F),p=x(l,r,g,j,q,F),h=x(i,n,f,w,q,F),m("bubbleHeatmap","size of",{x:f,y:g}),Q.domain(z).range([Math.min(V/2,Math.min(g,f)/2),Math.min(g,f)/2]);var A=k().horizontalQ(!1).moveby("category").numberOfObjects(j).objectClass(a(P,"row")).objectSize(g).spacerSize(p).transitionDuration(X).easeFunc(R).namespace("row"),M=k().horizontalQ(!0).moveby("category").numberOfObjects(w).objectClass(P).objectSize(f).spacerSize(h).transitionDuration(X).easeFunc(R);A(b,l,0),b.selectAll("g."+a(P,"row")).each(function(t,e){M(d3.select(this),i,0)});var H=b.selectAll("g:not(.to-remove)."+P).data(o),Z=[];H.each(function(t,e){Z.push(Number(d3.select(this).attr("parent-index")))}),T="index"==T.colorBy()?T.dataExtent([0,Math.max.apply(Math,Z)]):T.dataExtent([0,Math.max.apply(Math,S(s))]),H.each(function(t,n){m("bubbleHeatmap","each cell",{key:t,index:n,node:d3.select(this).node()});var r=d3.select(this),o=(e[t],C(t,n)),i=L(t,n),l=(n=void 0==r.attr("parent-index")?n:r.attr("parent-index"),T(t,o,n,"fill")),u=T(t,o,n,"stroke");m("bubbleHeatmap","radius",{radius:i,scaled:Q(i),extent:z,range:Q.range()}),c(r,"circle",a(P,"circle")).attr("cx",f/2).attr("cy",g/2).attr("r",Q(i)).attr("fill",l).attr("stroke",u).attr("stroke-width",_)}),G.selection(H.selectAll("circle."+a(P,"circle"))).data(e),G()}return H.selection=function(e){return arguments.length?(t=e,H):t},H.data=function(t){return arguments.length?(e=t,H):e},H.spaceX=function(t){return arguments.length?(n=t,H):n},H.spaceY=function(t){return arguments.length?(r=t,H):r},H.xKey=function(t){return arguments.length?(b=t,H):b},H.yKey=function(t){return arguments.length?(w=t,H):w},H.rKey=function(t){return arguments.length?(A=t,H):A},H.vKey=function(t){return arguments.length?(M=t,H):M},H.cellKeys=function(t){return arguments.length?(o=t,H):o},H.xValues=function(t){return arguments.length?(i=t,H):i},H.yValues=function(t){return arguments.length?(l=t,H):l},H.rValues=function(t){return arguments.length?(u=t,H):u},H.vValues=function(t){return arguments.length?(s=t,H):s},H.xExtractor=function(t){return arguments.length?(E=t,H):E},H.yExtractor=function(t){return arguments.length?(O=t,H):O},H.rExtractor=function(t){return arguments.length?(L=t,H):L},H.vExtractor=function(t){return arguments.length?(C=t,H):C},H.overflowQ=function(t){return arguments.length?(F=t,H):F},H.scale=function(t){return arguments.length?(Q=t,H):Q},H.domainPadding=function(t){return arguments.length?(B=t,H):B},H.objectSpacer=function(t){return arguments.length?q=t:e},H.minObjectSize=function(t){return arguments.length?(V=t,H):V},H.maxObjectSize=function(t){return arguments.length?(D=t,H):D},H.bubbleStrokeWidth=function(t){return arguments.length?(_=t,H):_},H.backgroundFill=function(t){return arguments.length?(K=t,H):K},H.namespace=function(t){return arguments.length?(Y=t,H):Y},H.objectClass=function(t){return arguments.length?(P=t,H):P},H.transitionDuration=function(t){return arguments.length?(X=t,H):X},H.easeFunc=function(t){return arguments.length?(R=t,H):R},H.tooltip=function(t){return arguments.length?(G=t,H):G},H.colorFunction=function(t){return arguments.length?(T=t,H):T},H.xSize=function(t){return arguments.length?(f=t,H):f},H.xSpacerSize=function(t){return arguments.length?(h=t,H):h},H.ySize=function(t){return arguments.length?(g=t,H):g},H.ySpacerSize=function(t){return arguments.length?(p=t,H):p},H},O.heatmap=function(t){var e,n,r,o,i,l,u,s,f,h,g,p="x",b="y",w="v",A=function(t,n){return e[t][p]},M=function(t,n){return e[t][b]},E=function(t,n){return e[t][w]},O=!1,L=0,C=50,F=50,Q=100,B=100,q=2,V="transparent",D="d3sm-heatmap",_="heatmap",K=1e3,Y=d3.easeExp,P=function(t,e){return i.indexOf(A(t))-i.indexOf(A(e))},X=function(t,e){return l.indexOf(M(t))-l.indexOf(M(e))},R=j().colorBy("category"),W=z();function N(){var p=v(t,D,{x:0,y:0,width:n,height:r},V);o=d3.keys(e),i=d(o.map(A)),l=d(o.map(M)),u=d(o.map(E)),o.sort(function(t,e){return P(t,e)||X(t,e)}),m("heatmap","cells are sorted by",o),m("heatmap","x and y keys are",{x:i,y:l});var b=i.length,w=l.length;h=y(r,w,C,B,L,O),s=y(n,b,F,Q,L,O),g=x(l,r,h,w,L,O),f=x(i,n,s,b,L,O),m("heatmap","size of",{x:s,y:h});var j=k().horizontalQ(!1).moveby("category").numberOfObjects(w).objectClass(a(_,"row")).objectSize(h+g).spacerSize(0).transitionDuration(K).easeFunc(Y).namespace("row"),z=k().horizontalQ(!0).moveby("category").numberOfObjects(b).objectClass(_).objectSize(s+f).spacerSize(0).transitionDuration(K).easeFunc(Y);j(p,l,0),p.selectAll("g."+a(_,"row")).each(function(t,e){z(d3.select(this),i,0)});var N=p.selectAll("g:not(.to-remove)."+_);if(o.length!=l.length*i.length){var T={};o.map(function(t,e){T[A(t)+"::"+M(t)]=t});for(var G=[],H=0;Hr?r:n}).attr("fill",h).attr("stroke",v).attr("stroke-width",Q).attr("transform",function(t,e){return"translate("+(g?l/2:A(u))+","+(j?l/2:A(R[1])-A(u))+")"}),k.transition().duration(_).ease(K).attr("d",function(t,e){var n=g?A(i)-A(a):l;return b(!1,0,0,j?A(i)-A(a):l,n,C,d)}).attr("transform",function(t,e){return"translate("+(g?0:A(i))+","+(j?0:A(R[1])-A(i))+")"}).attr("stroke","black").attr("stroke-width",B).attr("fill","none"),x.transition().duration(_).ease(K).attr("d",function(t,e){var n=g?A(f)-A(s):l;return b(!0,0,0,j?A(f)-A(s):l,n,C,d)}).attr("transform",function(t,e){return"translate("+(g?0:A(s))+","+(j?0:A(R[1])-A(f))+")"}).attr("stroke","black").attr("stroke-width",B).attr("fill","none")}),Y.selection(z.selectAll("g:not(.to-remove)."+D)).data(e),Y()}return P.selection=function(e){return arguments.length?(t=e,P):t},P.data=function(t){return arguments.length?(e=t,P):e},P.orient=function(t){return arguments.length?(d=t,P):d},P.spaceX=function(t){return arguments.length?(n=t,P):n},P.spaceY=function(t){return arguments.length?(r=t,P):r},P.overflowQ=function(t){return arguments.length?(h=t,P):h},P.grouping=function(t){return arguments.length?(o=t,P):o},P.quartilesKey=function(t){return arguments.length?(g=t,P):g},P.quartilesKeys=function(t){return arguments.length?(p=t,P):p},P.valueExtractor=function(t){return arguments.length?(m=t,P):m},P.sortingFunction=function(t){return arguments.length?(w=t,P):w},P.scale=function(t){return arguments.length?(A=t,P):A},P.domainPadding=function(t){return arguments.length?(M=t,P):M},P.objectSpacer=function(t){return arguments.length?(E=t,P):E},P.minObjectSize=function(t){return arguments.length?(O=t,P):O},P.maxObjectSize=function(t){return arguments.length?(L=t,P):L},P.whiskerWidthPercent=function(t){return arguments.length?(C=t,P):C},P.colorFunction=function(t){return arguments.length?(F=t,P):F},P.boxStrokeWidth=function(t){return arguments.length?(Q=t,P):Q},P.whiskerStrokeWidth=function(t){return arguments.length?(B=t,P):B},P.backgroundFill=function(t){return arguments.length?(q=t,P):q},P.namespace=function(t){return arguments.length?(V=t,P):V},P.objectClass=function(t){return arguments.length?(D=t,P):D},P.transitionDuration=function(t){return arguments.length?(_=t,P):_},P.easeFunc=function(t){return arguments.length?(K=t,P):K},P.boxKeys=function(t){return arguments.length?(a=t,P):a},P.boxValues=function(t){return arguments.length?(i=t,P):i},P.objectSize=function(t){return arguments.length?(l=t,P):l},P.spacerSize=function(t){return arguments.length?(u=t,P):u},P.tooltip=function(t){return arguments.length?(Y=t,P):Y},P},O.colorFunction=j,O.datatoggle=function(t){var e,n,r,o=function(){},i="d3sm-databar",l=!1,u=!1;d.xAxisSelectQ=function(t){return arguments.length?(l=t,d):l},d.yAxisSelectQ=function(t){return arguments.length?(u=t,d):u},d.xAxisOptions=function(t){return arguments.length?(e=t,d):e},d.yAxisOptions=function(t){return arguments.length?(n=t,d):n},d.data=function(t){return arguments.length?(r=t,d):r},d.updateFunction=function(t){return arguments.length?(o=t,d):o},d.namespace=function(t){return arguments.length?(i=t,d):i},d.currentKeys=function(){var t={};return d3.keys(s).map(function(e,n){t[e]=s[e].currentOption()}),t};var s={};function d(){var e=c(t,"div","d-inline-flex flex-row flex-wrap").selectAll("div."+a(i,"select-filter"));e.exit().remove();var n=(e=e.data(d3.keys(r))).enter().append("div").attr("class","select-filter");return(e=e.merge(n).style("margin-right","10px")).each(function(t,e){var n=A(d3.select(this)).data(r[t]).namespace(a(i,t)).selectionName(t);n(),s[t]=n}),t.selectAll("select").on("change",function(){o()}),d}return d},O.groupingSpacer=k,O.tooltip=z,O.scatter=function(t){var e,n,r,o,a,i,l,c=d3.scaleLinear(),u=.5,s=function(t,n){return e[t].x},d=d3.scaleLinear(),f=.5,h=function(t,n){return e[t].y},g=d3.scaleLinear(),p=.5,m=function(t,e){return 2},y=2,x=10,b=2,k=j(),w="transparent",A="d3sm-scatter",M="scatter-point",E=1e3,O=d3.easeExp,L=z();function C(){var j=v(t,A,{x:0,y:0,width:n,height:r},w);o=d3.keys(e),a=o.map(s),i=o.map(h),l=o.map(m),o.length;var z=[Math.min.apply(Math,S(a))-u,Math.max.apply(Math,S(a))+u],C=[Math.min.apply(Math,S(i))-f,Math.max.apply(Math,S(i))+f],F=[Math.min.apply(Math,S(l))-p,Math.max.apply(Math,S(l))+p];c.domain(z).range([0,n]),d.domain(C).range([r,0]),g.domain(F).range([y,x]);var Q=j.selectAll("."+M),B=(Q=Q.data(o)).enter().append("circle").attr("class",M).attr("cx",0).attr("cy",r).attr("r",0),q=Q.exit();(Q=Q.merge(B)).each(function(t,n){var r=d3.select(this),o=e[t],u=a[n],s=i[n],f=l[n],h=k(t,o,n,"fill"),p=k(t,o,n,"stroke");r.transition().duration(E).ease(O).attr("cx",c(u)).attr("cy",d(s)).attr("r",g(f)).attr("fill",h).attr("stroke",p).attr("stroke-width",b),r.on("mouseover",function(t,e){Q.style("opacity",.2),r.style("opacity",1),r.transition().duration(E/2).ease(O).attr("stroke-width",2*b).attr("r",1.5*g(f))}),r.node().addEventListener("mouseout",function(){j.selectAll("."+M).style("opacity",1),r.transition().duration(E/2).ease(O).attr("stroke-width",b).attr("r",g(f))})}),q.transition().duration(E).ease(O).attr("cx",0).attr("cy",r).attr("r",0).remove(),L.selection(Q).data(e),L()}return C.selection=function(e){return arguments.length?(t=e,C):t},C.data=function(t){return arguments.length?(e=t,C):e},C.spaceX=function(t){return arguments.length?(n=t,C):n},C.spaceY=function(t){return arguments.length?(r=t,C):r},C.scaleX=function(t){return arguments.length?(c=t,C):c},C.domainPaddingX=function(t){return arguments.length?(u=t,C):u},C.valueExtractorX=function(t){return arguments.length?(s=t,C):s},C.scaleY=function(t){return arguments.length?(d=t,C):d},C.domainPaddingY=function(t){return arguments.length?(f=t,C):f},C.valueExtractorY=function(t){return arguments.length?(h=t,C):h},C.scaleR=function(t){return arguments.length?(g=t,C):g},C.domainPaddingR=function(t){return arguments.length?(p=t,C):p},C.valueExtractorR=function(t){return arguments.length?(m=t,C):m},C.minRadius=function(t){return arguments.length?(y=t,C):y},C.maxRadius=function(t){return arguments.length?(x=t,C):x},C.pointStrokeWidth=function(t){return arguments.length?(b=t,C):b},C.colorFunction=function(t){return arguments.length?(k=t,C):k},C.backgroundFill=function(t){return arguments.length?(w=t,C):w},C.namespace=function(t){return arguments.length?(A=t,C):A},C.objectClass=function(t){return arguments.length?(M=t,C):M},C.transitionDuration=function(t){return arguments.length?(E=t,C):E},C.easeFunc=function(t){return arguments.length?(O=t,C):O},C.pointKeys=function(t){return arguments.length?(o=t,C):o},C.valuesX=function(t){return arguments.length?(a=t,C):a},C.valuesY=function(t){return arguments.length?(i=t,C):i},C.valuesR=function(t){return arguments.length?(l=t,C):l},C.tooltip=function(t){return arguments.length?(L=t,C):L},C},O.plotZoom=function(t,e,r){var o,i=20,l=void 0==t.orient?"horizontal":t.orient(),c=t.spaceX(),u=t.spaceY(),s=t.selection(),d=e.selection(),f=r.selection();function h(){var e=s.select("."+a(t.namespace(),"object-container")),r=n(e.attr("transform")),o=e.attr("transform","translate(0,0)");c=s.node().getBBox().width-.9*t.spaceX(),u=s.node().getBBox().height-.9*t.spaceY(),o.attr("transform","translate("+r[0]+","+r[1]+")"),m("plotZoom","setLocks",{xLock:c,yLock:u})}function g(){var g,p;h(),"2D"==l&&(g=!0,p=!0),"horizontal"==l&&(g=!0,p=!1),"vertical"==l&&(p=!0,g=!1);var m=d3.event.transform,v=s.node().getBBox(),y=d.node().getBBox(),x=d.node().getBBox();if(v.width,v.x,v.height,v.y,y.width,y.height,x.width,x.height,"wheel"==o){d3.event.preventDefault();var b=d3.event.deltaY*i,k=d3.event.shiftKey;(m="2D"==l?k?{k:1,x:b,y:0}:{k:1,x:0,y:b}:g?{k:1,x:b,y:0}:{k:1,x:0,y:b}).applyX=function(t){return t*this.k+-1*this.x},m.applyY=function(t){return t*this.k+-1*this.y}}var w=s.select("."+a(t.namespace(),"object-container")),S=d.select("."+a(e.namespace(),"object-container")),j=f.select("."+a(r.namespace(),"object-container")),z=n(w.attr("transform")),A=(n(S.attr("transform")),n(j.attr("transform")),g?m.applyX(z[0]):0);g&&(A=A<-c?(m.x=0,-c):(m.x=0,Math.min(A,0)));var M=p?m.applyY(z[1]):0;p&&(M=M<-u?(m.y=0,-u):(m.y=0,Math.min(M,0))),w.attr("transform","translate("+A+","+M+")"),g&&S.attr("transform","translate("+A+",0)"),p&&j.attr("transform","translate(0,"+M+")")}return d3.select(s.thisSVG()),g.eventType=function(t){return arguments.length?(o=t,g):o},g.wheelSpeed=function(t){return arguments.length?(i=t,g):i},g.orient=function(t){return arguments.length?(l=t,g):l},g.xLock=function(t){return arguments.length?(c=t,g):c},g.yLock=function(t){return arguments.length?(u=t,g):u},g.setLocks=h,g.reset=function(){var n=s.select("."+a(t.namespace(),"object-container")),o=d.select("."+a(e.namespace(),"object-container")),i=f.select("."+a(r.namespace(),"object-container"));n.attr("transform","translate(0,0)"),o.attr("transform","translate(0,0)"),i.attr("transform","translate(0,0)")},g},O.multiPlotZoom=function(t){var e,r=20,o=void 0==t.orient?"horizontal":t.orient(),i=t.spaceX(),l=t.spaceY(),c=t.selection(),u=(d3.select(c.thisSVG()),[]),s=[];function d(){var e=c.select("."+a(t.namespace(),"object-container")),r=n(e.attr("transform")),o=e.attr("transform","translate(0,0)");i=c.node().getBBox().width-t.spaceX(),l=c.node().getBBox().height-t.spaceY(),o.attr("transform","translate("+r[0]+","+r[1]+")"),m("plotZoom","setLocks",{xLock:i,yLock:l})}function f(){d();var f,h,g=u.map(function(t,e){return t.selection()}),p=s.map(function(t,e){return t.selection()});"2D"==o&&(f=!0,h=!0),"horizontal"==o&&(f=!0,h=!1),"vertical"==o&&(h=!0,f=!1);var m=d3.event.transform,v=c.node().getBBox();if(g.map(function(t,e){return t.node().getBBox()}),g.map(function(t,e){return t.node().getBBox()}),v.width,v.x,v.height,v.y,"wheel"==e){d3.event.preventDefault();var y=d3.event.deltaY*r,x=d3.event.shiftKey;(m="2D"==o?x?{k:1,x:y,y:0}:{k:1,x:0,y:y}:f?{k:1,x:y,y:0}:{k:1,x:0,y:y}).applyX=function(t){return t*this.k+-1*this.x},m.applyY=function(t){return t*this.k+-1*this.y}}var b=c.select("."+a(t.namespace(),"object-container")),k=g.map(function(t,e){return t.select("."+a(u[e].namespace(),"object-container"))}),w=p.map(function(t,e){return t.select("."+a(s[e].namespace(),"object-container"))}),S=n(b.attr("transform")),j=(k.map(function(t,e){return n(t.attr("transform"))}),w.map(function(t,e){return n(t.attr("transform"))}),f?m.applyX(S[0]):0);f&&(j=j<-i?(m.x=0,-i):(m.x=0,Math.min(j,0)));var z=h?m.applyY(S[1]):0;h&&(z=z<-l?(m.y=0,-l):(m.y=0,Math.min(z,0))),b.attr("transform","translate("+j+","+z+")"),f&&k.map(function(t,e){t.attr("transform","translate("+j+",0)")}),h&&w.map(function(t,e){t.attr("transform","translate(0,"+z+")")})}return f.eventType=function(t){return arguments.length?(e=t,f):e},f.wheelSpeed=function(t){return arguments.length?(r=t,f):r},f.orient=function(t){return arguments.length?(o=t,f):o},f.xLock=function(t){return arguments.length?(i=t,f):i},f.yLock=function(t){return arguments.length?(l=t,f):l},f.xComponents=function(t){return arguments.length?(u=t,f):u},f.yComponents=function(t){return arguments.length?(s=t,f):s},f.setLocks=d,f.reset=function(){var e=c.select("."+a(t.namespace(),"object-container")),n=xAxisSel.select("."+a(xAxis.namespace(),"object-container")),r=yAxisSel.select("."+a(yAxis.namespace(),"object-container"));e.attr("transform","translate(0,0)"),n.attr("transform","translate(0,0)"),r.attr("transform","translate(0,0)")},f},O.violin=function(t){var e,n,a,i,l,u,d,g,p="horizontal",m=!0,b=!0,w=function(t,n){return e[t]},A=function(t,n){return d3.descending(e[t],e[n])},M=d3.scaleLinear(),E=.5,O=.05,L=50,C=100,F=2,Q=j(),B=function(t,e,n,o,a){var i=d3.scaleLinear().domain([o,a]).range([-.25,.05]),l=r(n.replace("#",""),i(t)),c="stroke"==e?0:.25;return r(l.replace("#",""),c)},q=3,V=2,D="transparent",_="d3sm-violin",K="violin",Y=1e3,P=d3.easeExp,X=["Q0","Q1","Q2","Q3","Q4"],R=z().keys([X[4],X[3],X[2],X[1],X[0]]),W=z(),N=function(t,e){return e.points},T=function(t,e){return e[t].value};function G(){var r,u,w,j="horizontal"==p,G=v(t,_,{x:0,y:0,width:n,height:a},D),H=void 0==i?d3.keys(e).sort(A):i;l=f(H);var Z=function(){var t,e,n=!0,r=["Q0","Q1","Q2","Q3","Q4"];function a(a,i){var l=i[a],c=t(a,l),u=d3.keys(c),s=u.map(function(t,n){return e(t,c)}),d=o(s,r),f=d3.histogram()(s),h=f.map(function(t){return t.length}),g=n?{x:0,y:d3.min(s)}:{x:d3.min(s),y:0},p=n?{x:0,y:d3.max(s)}:{x:d3.max(s),y:0},m=f.map(function(t,e){return n?{y:t.length?d3.median(t):d3.median([t.x0,t.x1]),x:h[e]}:{x:t.length?d3.median(t):d3.median([t.x0,t.x1]),y:h[e]}});m=[g].concat(m).concat([p]),l.binned=f,l.frequencies=h,l.contour=m,l.quartiles=d,l.pointKeys=u,l.pointValues=s}return a.horizontalQ=function(t){return arguments.length?(n=t,a):n},a.quartileKeys=function(t){return arguments.length?(r=t,a):r},a.violinPointsExtractor=function(e){return arguments.length?(t=e,a):t},a.violinPointValueExtractor=function(t){return arguments.length?(e=t,a):e},a}().horizontalQ(j).quartileKeys(X).violinPointsExtractor(N).violinPointValueExtractor(T);l.map(function(t,n){Z(t,e)});var I=l.length,J=(r=[]).concat.apply(r,S(l.map(function(t,n){return e[t].quartiles[X[0]]}))),U=(u=[]).concat.apply(u,S(l.map(function(t,n){return e[t].quartiles[X[X.length-1]]}))),$=[Math.min.apply(Math,S(J))-E,Math.max.apply(Math,S(U))+E];M.domain($).range(j?[0,a]:[0,n]);var tt=j?n:a;d=y(tt,I,L,C,O,m),g=x(H,tt,d,I,O,m),k().horizontalQ(j).scale(M).moveby("category").numberOfObjects(I).objectClass(K).objectSize(d).spacerSize(g).transitionDuration(Y).easeFunc(P).namespace(_)(G,H,0);var et=[];G.selectAll("g:not(.to-remove)."+K).each(function(t,e){s(l,t)&&et.push(Number(d3.select(this).attr("parent-index")))}),Q="index"==Q.colorBy()?Q.dataExtent([0,Math.max.apply(Math,et)]):Q.dataExtent($);var nt=Math.max.apply(Math,S((w=[]).concat.apply(w,S(l.map(function(t,n){return d3.max(e[t].frequencies)}))))),rt=d3.scaleLinear().domain([0,nt]).range([0,d/2]),ot=d3.line().x(function(t,e){return j?-rt(t.x):M(t.x)}).y(function(t,e){return j?M($[1])-M(t.y):-rt(t.y)}).curve(d3.curveBasis),at=d3.line().x(function(t,e){return j?rt(t.x):M(t.x)}).y(function(t,e){return j?M($[1])-M(t.y):rt(t.y)}).curve(d3.curveBasis);G.selectAll("g:not(.to-remove)."+K).each(function(t,n){var r=d3.select(this),o=e[t];if(s(l,t)){n=void 0==r.attr("parent-index")?n:r.attr("parent-index");var a=Q(t,o,n,"fill"),i=Q(t,o,n,"stroke"),u=c(r,"g","area"),f=c(u,"path","left"),g=c(u,"path","right"),p=c(r,"g","quarts"),m=(c(p,"line","q3"),c(p,"line","q1"),o.quartiles[X[3]],o.quartiles[X[2]]);if(o.quartiles[X[1]],r.attr("transform",j?"translate("+d/2+",0)":"translate(0,"+d/2+")"),f.transition().duration(Y).attr("d",function(t,e){return ot(o.contour)}).attr("fill",a).attr("stroke",i).attr("stroke-width",F),g.transition().duration(Y).attr("d",function(t,e){return at(o.contour)}).attr("fill",a).attr("stroke",i).attr("stroke-width",F),u.node().addEventListener("mouseover",function(t,e){G.selectAll("g."+K).style("opacity",.2),r.style("opacity",1),f.attr("stroke-width",2*F),g.attr("stroke-width",2*F)}),u.node().addEventListener("mouseout",function(t,e){G.selectAll("g."+K).style("opacity",1),f.attr("stroke-width",F),g.attr("stroke-width",F)}),b){var v=c(r,"g","points"),y=v.selectAll(".point").data(o.pointKeys);y.on("mouseover",null),y.exit().transition().ease(P).duration(Y).attr("r",0).attr("cy",j?M($[1])-M(m):rt(0)).attr("cx",j?rt(0):M(m)).remove();var x=y.enter().append("circle").attr("class","point").attr("r",0).attr("cx",j?0:M(m)).attr("cy",j?M(m):0);y=y.merge(x),z().selection(y).data(N(t,o)).header(W.header()).keys(W.keys()).values(W.values())();var k=d3.min(o.pointValues),w=d3.max(o.pointValues);y.transition().duration(Y).ease(P).attr("r",q).attr("cy",function(t,e){var n=o.pointValues[e];if(j)return M($[1])-M(n);var r=h(o.binned,n),a=Math.random(),i=rt(a*o.frequencies[r]*.5);return Math.random()>.5?i:-i}).attr("cx",function(t,e){var n=o.pointValues[e];if(j){var r=h(o.binned,n),a=Math.random(),i=rt(a*o.frequencies[r]*.5);return Math.random()>.5?i:-i}return M(n)}).attr("stroke",function(t,e){return t=o.pointValues[e],B(t,"stroke",i,k,w)}).attr("fill",function(t,e){return t=o.pointValues[e],B(t,"fill",i,k,w)}).attr("stroke-width",V),v.selectAll("circle.point").on("mouseover",function(t,e){G.selectAll("g."+K).style("opacity",.2),r.style("opacity",1),f.attr("stroke-width",2*F),g.attr("stroke-width",2*F),G.selectAll(".point").style("opacity",.2),d3.select(this).style("opacity",1).attr("r",2*q).attr("stroke-width",2*V)}),v.selectAll("circle.point").on("mouseout",function(t,e){var n=document.createEvent("SVGEvents");n.initEvent("mouseout",!0,!0),u.node().dispatchEvent(n),G.selectAll(".point").style("opacity",1),d3.select(this).attr("stroke-width",V).attr("r",q)})}else cV.selectAll(".point").transition().duration(Y).ease(P).attr("r",0).attr("cy",j?M($[1])-M(m):rt(0)).attr("cx",j?rt(0):M(m)).remove()}}),R.selection(G.selectAll("g:not(.to-remove)."+K+" .area")),void 0==R.data()&&R.data(e),R(),void 0==R.values()&&R.values([function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]}])}return G.violinPointsExtractor=function(t){return arguments.length?(N=t,G):N},G.violinPointValueExtractor=function(t){return arguments.length?(T=t,G):T},G.selection=function(e){return arguments.length?(t=e,G):t},G.data=function(t){return arguments.length?(e=t,G):e},G.orient=function(t){return arguments.length?(p=t,G):p},G.spaceX=function(t){return arguments.length?(n=t,G):n},G.spaceY=function(t){return arguments.length?(a=t,G):a},G.overflowQ=function(t){return arguments.length?(m=t,G):m},G.pointsQ=function(t){return arguments.length?(b=t,G):b},G.grouping=function(t){return arguments.length?(i=t,G):i},G.valueExtractor=function(t){return arguments.length?(w=t,G):w},G.sortingFunction=function(t){return arguments.length?(A=t,G):A},G.scale=function(t){return arguments.length?(M=t,G):M},G.domainPadding=function(t){return arguments.length?(E=t,G):E},G.objectSpacer=function(t){return arguments.length?(O=t,G):O},G.minObjectSize=function(t){return arguments.length?(L=t,G):L},G.maxObjectSize=function(t){return arguments.length?(C=t,G):C},G.objectStrokeWidth=function(t){return arguments.length?(F=t,G):F},G.colorFunction=function(t){return arguments.length?(Q=t,G):Q},G.pointColorFunc=function(t){return arguments.length?(B=t,G):B},G.pointRadius=function(t){return arguments.length?(q=t,G):q},G.pointStrokeWidth=function(t){return arguments.length?(V=t,G):V},G.backgroundFill=function(t){return arguments.length?(D=t,G):D},G.namespace=function(t){return arguments.length?(_=t,G):_},G.objectClass=function(t){return arguments.length?(K=t,G):K},G.transitionDuration=function(t){return arguments.length?(Y=t,G):Y},G.easeFunc=function(t){return arguments.length?(P=t,G):P},G.quartileKey=function(t){return arguments.length?(quartileKey=t,G):quartileKey},G.quartileKeys=function(t){return arguments.length?(X=t,G):X},G.violinKeys=function(t){return arguments.length?(l=t,G):l},G.violinValues=function(t){return arguments.length?(u=t,G):u},G.objectSize=function(t){return arguments.length?(d=t,G):d},G.spacerSize=function(t){return arguments.length?(g=t,G):g},G.tooltip=function(t){return arguments.length?(R=t,G):R},G.pointsTooltip=function(t){return arguments.length?(W=t,G):W},G},O.numericLegend=function(t){var e,n,r=0,o=1,l=j(),u="d3sm-linear-vertical-gradient",s=12,d="transparent",f="black",h=2;function g(){var g=v(t,u,{x:0,y:0,width:e,height:n},d),p=c(c(t,"defs"),"linearGradient").attr("x1","0%").attr("y1","100%").attr("x2","0%").attr("y2","0%").attr("id",a(u,"numerical-legend-gradient"));l.dataExtent([r,o]).colorBy("value").valueExtractor(function(t,e,n){return e}),p.selectAll("stop").data(l.colors()).enter().append("stop").attr("offset",function(t,e){return e/(l.colors().length-1)}).attr("stop-color",function(t){return t});var m=c(g,"rect","legend").attr("transform","translate(0,"+s+")").style("fill","url(#"+a(u,"numerical-legend-gradient")+")").attr("x",0).attr("y",0).attr("width",e).attr("height",n-2*s).on("mousemove",function(t,e){!function(t,e,n,d){var g=d3.scaleLinear().domain([0,n.attr("height")]).range([o,r]),p=d3.mouse(n.node()),m=i(g(p[1]),h),v=l(void 0,m,void 0,"stroke"),y=l(void 0,m,void 0,"fill");c(c(d3.select("body"),"div",a(u,"legend-tooltip")).attr("id",a(u,"legend-tooltip")).style("position","absolute").style("left",d3.event.pageX+15+"px").style("top",d3.event.pageY+15+"px").style("background-color",y).style("border-color",v).style("min-width",s*(String(o).split(".")[0].length+3)+"px").style("min-height",s*(String(o).split(".")[0].length+3)+"px").style("border-radius","50%").style("border-radius","5000px").style("display","flex").style("justify-content","center").style("text-align","middle").style("padding","2px").style("border-style","solid").style("border-width",2),"div").text(m).style("color",f).style("align-self","center")}(0,0,m,d3.select(this))}).on("mouseout",function(t,e){d3.select("#"+a(u,"legend-tooltip")).remove()});c(g,"text","min").text(i(r,2)).attr("text-anchor","middle").attr("font-size",s+"px").attr("transform",function(t,r){return"translate("+e/2+","+(n-s/4)+")"}),c(g,"text","max").text(i(o,2)).attr("text-anchor","middle").attr("font-size",s+"px").attr("transform",function(t,n){return"translate("+e/2+","+s+")"})}return g.min=function(t){return arguments.length?(r=t,g):r},g.max=function(t){return arguments.length?(o=t,g):o},g.spaceX=function(t){return arguments.length?(e=t,g):e},g.spaceY=function(t){return arguments.length?(n=t,g):n},g.namespace=function(t){return arguments.length?(u=t,g):u},g.fontSize=function(t){return arguments.length?(s=t,g):s},g.backgroundFill=function(t){return arguments.length?(d=t,g):d},g.colorFunction=function(t){return arguments.length?(l=t,g):l},g.textColor=function(t){return arguments.length?(f=t,g):f},g.roundTo=function(t){return arguments.length?(h=t,g):h},g},O.categoricLegend=function(t){var e,n,r,o,a,i="horizontal",l=!1,u=function(t,e){return n[t]},s=function(t,e){return d3.ascending(t,e)},d=.05,h=10,g=100,p=2,m=j(),b="transparent",w="d3sm-legend",S="legend",z=1e3,A=d3.easeExp;function M(){var n="horizontal"==i,u=!n,j=v(t,w,{x:0,y:0,width:r,height:o},b);m.dataExtent([0,e.length-1]).colorBy("categories").categoryExtractor(function(t,e,n){return e});var M=Math.min(r,o),E=e.length,O=void 0==a?e.sort(s):a,L=f(O),C=n?r:o,F=y(C,E,h,g,d,l),Q=x(L,C,F,E,d,l);k().horizontalQ(n).scale(scale).moveby("category").numberOfObjects(E).objectClass(S).objectSize(F).spacerSize(Q).transitionDuration(z).easeFunc(A).namespace(w)(j,O,0),M=Math.min(F,r,o)/2-p,j.selectAll("g:not(.to-remove)."+S).each(function(t,e){var o=d3.select(this),a=c(o,"circle"),i=m(void 0,t,e,"fill"),l=m(void 0,t,e,"stroke"),s=n?M+p:(r-2*M)/2+M,d=u?M+p:(r-2*M)/2+M;a.attr("r",M).attr("cx",s).attr("cy",d).attr("fill",i).attr("stroke",l).attr("stroke-width",p);var f=c(o,"text");f.text(t).attr("text-anchor","middle").attr("transform",function(t,e){return"translate("+s+","+(d+f.node().getBoundingClientRect().height/4)+")"})})}return M.categories=function(t){return arguments.length?(e=t,M):e},M.selection=function(e){return arguments.length?(t=e,M):t},M.data=function(t){return arguments.length?(n=t,M):n},M.orient=function(t){return arguments.length?(i=t,M):i},M.spaceX=function(t){return arguments.length?(r=t,M):r},M.spaceY=function(t){return arguments.length?(o=t,M):o},M.overflowQ=function(t){return arguments.length?(l=t,M):l},M.grouping=function(t){return arguments.length?(a=t,M):a},M.valueExtractor=function(t){return arguments.length?(u=t,M):u},M.sortingFunction=function(t){return arguments.length?(s=t,M):s},M.objectSpacer=function(t){return arguments.length?(d=t,M):d},M.minObjectSize=function(t){return arguments.length?(h=t,M):h},M.maxObjectSize=function(t){return arguments.length?(g=t,M):g},M.bubbleStrokeWidth=function(t){return arguments.length?(p=t,M):p},M.colorFunction=function(t){return arguments.length?(m=t,M):m},M.backgroundFill=function(t){return arguments.length?(b=t,M):b},M.namespace=function(t){return arguments.length?(w=t,M):w},M.objectClass=function(t){return arguments.length?(S=t,M):S},M.transitionDuration=function(t){return arguments.length?(z=t,M):z},M.easeFunc=function(t){return arguments.length?(A=t,M):A},M},O.lasso=E,O.lassoWidget=function(t){var e,n,r,o,i,l,u,s,d="d3sm-lasso",f=(t=t,d3.line().x(function(t,e){return t[0]}).y(function(t,e){return t[1]}).curve(d3.curveLinearClosed),j());function h(t,e){console.log(t,e)}function g(){d3.select("html").select("style."+d+"lasso-widget").empty()&&d3.select("html").append("style").classed(d+"lasso-widget",!0).html("."+a(d,"data-table")+"{ height:100px; overflow:auto; }")}function p(t){console.log(t)}function m(){p((t.select("."+a(d,"tab-list")),t.select("."+a(d,"tab-content")).selectAll("."+a(d,"data-table")),{}))}function v(e,n){var r,o;t.select("#"+a(d,"tab",e)),t.select("#"+a(d,"tab","pane",e)),function(){var e=t.select("."+a(d,"tab-list")),n=(t.select("."+a(d,"tab-content")),e.selectAll("."+a(d,"tab")));if(0==n.size());else{var r=n.nodes()[n.size()-1];d3.select(r).select("a").attr("href")}}(),r=t.select("."+a(d,"tab-list")),o=t.select("."+a(d,"tab-content")),r=r.selectAll("."+a(d,"tab")),o=o.selectAll("."+a(d,"tab","pane")),r.each(function(t,e){d3.select(this).datum(e).attr("id",a(d,"tab",e)).select("a").attr("href","#"+a(d,"tab","pane",e)).text(function(t,n){var r=d3.select(this).text();return"Group"==r.split(" ")[0]?"Group "+e:r})}),o.each(function(t,e){d3.select(this).datum(e).attr("id",a(d,"tab","pane",e)),c(d3.select(this),"p","lead").text(function(t,n){var r=d3.select(this).text();return"Group"==r.split(" ")[0]?"Group "+e:r}),c(d3.select(this),"button","remove-btn").on("click",v)})}function y(t,i){var h=c(t,"p","lead").text("Group "+i),g=c(c(t,"div","table-responsive").attr("class",a(d,"data-table")),"table","table").classed("table-sm",!0).classed("table-hover",!0),p=(c(g,"caption","caption").html("List of selected"),c(t,"div","text-right")),m=i%f.colors().length;i%2!=0&&(m=f.colors().length-1-m);var y,k,w,S=function(t){var e=c(t,"button","lasso-btn").classed("btn btn-info",!0).classed(d,!0);return c(e,"i","fa").classed("fa-hand-pointer-o",!0),c(e,"span").text("Lasso select"),e}(p),j=(y=i,k=f.colors()[m],E().namespace(d).svg(e).objectClass(o).chartContainer(n).objectContainer(r).instance(y).color(k).yScale(s).xScale(u));!function(t,e,r,o){r.eventCatcher(t),t.node().addEventListener("click",x),t.datum([r]).on(a(d,"render"),function(){d3.select(this).datum()[0].draw()}).on(a(d,"drag"),function(t){var e=n.selectAll(".in-lasso-"+t[0].instance()).nodes(),a=e.map(function(t,e){var n=l(d3.select(t).datum());return n.__node=t,n});b(o,a,r)}),e.on("click",function(){r.allPoints([]),r.currentPoints([]),t.dispatch(a(d,"drag"))})}(S,function(t){var e=c(t,"button","clear-btn").classed("btn btn-light",!0).classed(d,!0);return c(e,"i","fa").classed("fa-eraser",!0),c(e,"span").text("Clear selection"),e}(p),j,g),c(w=c(p,"button","remove-btn").classed("btn btn-danger",!0).on("click",v),"i","fa").classed("fa-trash-o",!0),c(w,"span").text("Remove group"),h.on("mouseover",function(){j.draw()}),h.on("mouseout",function(){j.remove()})}function x(){var e=d3.select(this),n=e.datum()[0];n.toggle();var r=n.activeQ();function o(t){t.target!=e.node()&&t.target!=e.select("span").node()&&t.target!=e.select("i").node()&&(n.toggle(!1),e.classed("btn-info",!0),e.classed("btn-warning",!1),e.select("span").text("Lasso select"))}n.activeQ()?(e.classed("btn-info",!r),e.classed("btn-warning",r),e.select("span").text("Lasso select (active)"),t.selectAll("."+d+".lasso-btn").dispatch(a(d,"render")),d3.select("html").node().addEventListener("mousedown",o)):(e.classed("btn-info",!r),e.classed("btn-warning",r),e.select("span").text("Lasso select"),d3.select("html").node().removeEventListener("mousedown",o))}function b(t,e,n){var r=c(c(t,"thead"),"tr"),o=c(t,"tbody"),a=function(t,e){var n=d3.keys(e[0]).filter(function(t){return"__node"!=t});n.length>0&&n.push("remove");var r=t.selectAll("th");return(r=r.data(n)).exit().remove(),r=r.merge(r.enter().append("th").attr("scope","col")).text(function(t,e){return t}),n}(r,e);(function(t,e){var n=t.selectAll("tr");return(n=n.data(e)).exit().remove(),n=n.merge(n.enter().append("tr"))})(o,e).each(function(r,o){var i=d3.select(this);!function(t,e,n,r,o,a){(t=t.data(e)).exit().remove(),(t=t.merge(t.enter().append("td"))).html(function(t,e){return"remove"!=t?n[t]:""}).classed("text-left",function(t,e){return"remove"==t}),t.select("i.fa-close").on("click",function(t,e){var i=n.__node,l=d3.select(i);l.classed("in-lasso",!1),l.classed("in-lasso-"+o.instance(),!1),r.map(function(t,e){t.__node==i&&(r.splice(e,1),b(a,r,o))})})}(i.selectAll("td"),a,r,e,n,t),i.on("mouseover",function(t,e){n.applyObjectAttributes(d3.select(t.__node),!0)}).on("mouseout",function(t,e){n.applyObjectAttributes(d3.select(t.__node),!1)})})}function k(e,n){var r=t.select("."+a(d,"tab-list")),o=t.select("."+a(d,"tab-content")),l=function(){for(var e=t.select("."+a(d,"tab-list")).selectAll("li").size()-3,n="Group "+e,r=[];r.includes(n);)n="Group "+(e+=1);return e}();if(r.selectAll("."+a(d,"tab")).size()!=i){!function(t,e){var n=t.insert("li",".right-aligned-tabs").classed("nav-item",!0).classed("alert",!0).classed("alert-secondary",!0).classed("alert-dismissable",!0).classed("fade",!0).classed("show",!0).attr("role","alert").attr("id",a(d,"tab",e)).classed(a(d,"tab"),!0);c(n,"a").attr("data-toggle","tab").text("Group "+e).attr("href","#"+a(d,"tab","pane",e)).on("dblclick",function(t,e){var n=d3.select(this);n.attr("contenteditable",!0),d3.select(n.node().parentNode).classed("alert-secondary",!1).classed("alert-warning",!0)}).on("blur",function(t,e){var n=d3.select(this);n.attr("contenteditable",!1),d3.select(n.node().parentNode).classed("alert-secondary",!0).classed("alert-warning",!1)}).on("input",function(t,e){var n=d3.select(this),r=n.text();d3.select(n.attr("href")).select("p.lead").text(r)})}(r,l);var u=function(t,e){return t.append("div").datum(e).attr("role","tabpanel").classed("tab-pane",!0).classed("text-left",!0).classed(a(d,"tab","pane"),!0).attr("id",a(d,"tab","pane",e))}(o,l);y(u,l),u.attr("id"),t.select("."+a(d,"tab-content"))}else h("only "+i+" allowed.","warning")}return w=c(t,"div","card"),S=c(c(w,"div","card-header"),"ul","nav").classed("nav-tabs card-header-tabs",!0).classed(a(d,"tab-list"),!0).attr("role","tablist"),z=c(c(w,"div","card-body"),"div","tab-content").classed(a(d,"tab-content"),!0),A=c(S,"div","right-aligned-tabs").classed("nav ml-auto ",!0),function(t){c(c(c(t,"li",a(d,"plus-tab")).classed("ml-auto",!0).classed("nav-item",!0).on("click",k),"a","nav-link"),"i","fa").classed("fa-plus fa-2x text-success",!0)}(A),function(t){c(c(c(t,"li",a(d,"send-tab")).classed("ml-auto",!0).classed("nav-item",!0).on("click",m),"a","nav-link"),"i","fa").classed("fa-paper-plane-o fa-2x text-primary",!0)}(A),function(t){c(c(c(t,"li",a(d,"close-tab")).classed("ml-auto",!0).classed("nav-item",!0).on("click",function(){d3.mouse(d3.select("html").node())}),"a","nav-link"),"i","fa").classed("fa-window-close-o fa-2x text-danger",!0)}(A),c(c(z,"div","tab-pane").classed(a(d,"default-tab"),!0).classed("active",!0).classed("text-left",!0),"div").html("Click to add a new group.
Click to submit for re-analysis.
Click to close the dataselect widget."),g.objectClass=function(t){return arguments.length?(o="."+t.replace(".",""),g):o},g.svg=function(t){return arguments.length?(e=t,g):e},g.submit=function(t){return arguments.length?(p=t,g):p},g.maxNumberOfGroups=function(t){return arguments.length?(i=t,g):i},g.onError=function(t){return arguments.length?(h=t,g):h},g.objectContainer=function(t){return arguments.length?(r=t,g):r},g.chartContainer=function(t){return arguments.length?(n=t,g):n},g.dataExtractor=function(t){return arguments.length?(l=t,g):l},g.xScale=function(t){return arguments.length?(u=t,g):u},g.yScale=function(t){return arguments.length?(s=t,g):s},g;var w,S,z,A},O.selectFilter=A,O.upset=function(t){var e,n,r,o,i,l,u,s,f,h,g,p="horizontal",m=!1,b=20,w=50,S=2,j="transparent",z="d3sm-upset",A="upset",M=1e3,E=d3.easeExp,O=function(t,n){return e[t].set},L=function(t,n){return e[t].intersection},C=function(t,n){return e[t].elements},F=.05,Q=.05,B=function(t,e){return i.indexOf(O(t))-i.indexOf(O(e))},q=function(t,e){return l.indexOf(L(t))-l.indexOf(L(e))};function V(){var S="horizontal"==p,C=!S,V=v(t,z,{x:0,y:0,width:n,height:r},j);o=d3.keys(e),i=d(o.map(O)).sort(),l=d(o.map(L)).sort().sort(function(t,e){return t.split(";").length-e.split(";").length}),S?o.sort(function(t,e){return q(t,e)||B(t,e)}):o.sort(function(t,e){return B(t,e)||q(t,e)});var D=S?l:i,_=S?i:l,K=S?D.length:_.length,Y=S?_.length:D.length;h=y(n,K,b,w,F,m),s=y(r,Y,b,w,Q,m),g=x(D,n,h,K,F,m),f=x(_,r,s,Y,Q,m);var P=k().horizontalQ(!1).moveby("category").numberOfObjects(Y).objectSize(s).spacerSize(f).transitionDuration(M).easeFunc(E),X=k().horizontalQ(!0).moveby("category").numberOfObjects(K).objectClass(A).objectSize(h).spacerSize(g).transitionDuration(M).easeFunc(E);C?(X.objectClass(A),P.namespace("across").objectClass(a(A,"across")),P(V,_,0),V.selectAll("g."+a(A,"across")).each(function(t,e){X(d3.select(this),D,0)})):(X.namespace("across").objectClass(a(A,"across")),P.objectClass(A),X(V,D,0),V.selectAll("g."+a(A,"across")).each(function(t,e){P(d3.select(this),_,0)}));var R=V.selectAll("g:not(.to-remove)."+A),W={};o.map(function(t,e){W[O(t)+"::"+L(t)]=t}),R.data(o),R.each(function(t,n){var r=d3.select(this);if(void 0!=t){e[t];var o=O(t,n),i=L(t,n);r.classed(i,!0),r.classed(o,!0),c(r,"circle",a(A,"circle")).attr("cx",h/2).attr("cy",s/2).attr("r",void 0==u?Math.min(h,s)/2:u).attr("fill",i.includes(o)?"black":"rgb(233,233,233)").attr("stroke","black").attr("in-intersection",i.includes(o))}})}return V.selection=function(e){return arguments.length?(t=e,V):t},V.data=function(t){return arguments.length?(e=t,V):e},V.orient=function(t){return arguments.length?(p=t,V):p},V.spaceX=function(t){return arguments.length?(n=t,V):n},V.spaceY=function(t){return arguments.length?(r=t,V):r},V.overflowQ=function(t){return arguments.length?(m=t,V):m},V.minObjectSize=function(t){return arguments.length?(b=t,V):b},V.maxObjectSize=function(t){return arguments.length?(w=t,V):w},V.circleStrokeWidth=function(t){return arguments.length?(S=t,V):S},V.backgroundFill=function(t){return arguments.length?(j=t,V):j},V.namespace=function(t){return arguments.length?(z=t,V):z},V.objectClass=function(t){return arguments.length?(A=t,V):A},V.transitionDuration=function(t){return arguments.length?(M=t,V):M},V.easeFunc=function(t){return arguments.length?(E=t,V):E},V.cellKeys=function(t){return arguments.length?(o=t,V):o},V.setValues=function(t){return arguments.length?(i=t,V):i},V.intersectionValues=function(t){return arguments.length?(l=t,V):l},V.xObjectSpacer=function(t){return arguments.length?(F=t,V):F},V.yObjectSpacer=function(t){return arguments.length?(Q=t,V):Q},V.radius=function(t){return arguments.length?(u=t,V):u},V.setExtractor=function(t){return arguments.length?(O=t,V):O},V.intersectionExtractor=function(t){return arguments.length?(L=t,V):L},V.elementExtractor=function(t){return arguments.length?(C=t,V):C},V.setKeySortingFunction=function(t){return arguments.length?(B=t,V):B},V.intersectionKeySortingFunction=function(t){return arguments.length?(q=t,V):q},V.yObjectSize=function(t){return arguments.length?(s=t,V):s},V.ySpacerSize=function(t){return arguments.length?(f=t,V):f},V.xObjectSize=function(t){return arguments.length?(h=t,V):h},V.xSpacerSize=function(t){return arguments.length?(g=t,V):g},V.intersectionTotals=function(){var t={};return l.map(function(e,n){t[e]={total:0}}),o.map(function(e,n){var r=C(e,n);0==t[L(e,n)].total&&(Array.isArray(r)?(t[L(e,n)].total+=r.length,t[L(e,n)].values=r):t[L(e,n)].total+=r)}),t},V.setTotals=function(){var t={};return i.map(function(e,n){t[e]={total:0}}),o.map(function(e,n){var r=C(e,n);Array.isArray(r)?t[O(e,n)].total+=r.length:t[O(e,n)].total+=r}),t},V},O.filterTable=function(t){var e,n="d3sm-filter-table",r=function(t,e,n){return n};function o(){var o=c(t,"div","filter-table").classed(a(n,"container"),!0),u=c(o,"div","filter-input-group").classed("input-group",!0),s=(c(c(c(u,"div","input-group-prepend"),"span","input-group-text").classed("search-button",!0),"i","fa fa-search"),c(u,"input","form-control").attr("placeholder","filter").attr("type","text")),d=c(c(u,"div","input-group-append"),"a","close-button").classed("btn btn-outline-secondary",!0),f=(c(d,"i","fa fa-close"),d),h=c(c(o,"div","table-responsive"),"table","table").classed("table-hover",!0).classed("table-bordered",!0).classed("table-striped",!0),g=c(c(h,"thead").classed("thead-dark",!0),"tr"),p=c(h,"tbody"),m=d3.keys(e),v=d3.keys(e[m[0]]);(function(t,e){var n=t.selectAll("th");(n=n.data(e)).exit().remove(),(n=n.merge(n.enter().append("th"))).attr("scope","col").text(function(t,e){return t})})(g,v),l(i(p,m),v),s.on("input",function(t,n){var o,a=s.property("value"),c=new RegExp(a,"gi");""==a?o=m:(o=[],m.map(function(t,n){var a=e[t],i=v.map(function(t,e){return r(a,t,a[t])}).join("").match(c);null==i||""==i.join("")||o.push(t)})),l(i(p,o),v)}),f.on("click",function(t,e){s.property("value","").dispatch("input")})}function i(t,e){var n=t.selectAll("tr");return(n=n.data(e)).exit().remove(),n=n.merge(n.enter().append("tr"))}function l(t,n){return t.each(function(t,o){var a=e[t],i=d3.select(this).selectAll("td");(i=i.data(n)).exit().remove(),(i=i.merge(i.enter().append("td"))).attr("scope",function(t,e){return 0==e}).html(function(t,e){return r(a,t,a[t])})}),t}return o.data=function(t){return arguments.length?(e=t,o):e},o.namespace=function(t){return arguments.length?(n=t,o):n},o.fieldFunction=function(t){return arguments.length?(r=t,o):r},o},O.uniqueElements=e,O.getTranslation=n,O.modifyHexidecimalColorLuminance=r,O.tickRange=u,O.quartiles=o,O.extractViolinValues=function(t,e,n,r,a,i){var l={};return t.map(function(t,c){var u=n(t,c,e),s=d3.histogram()(u),d=s.map(function(t){return t.length}),f=r?{y:d3.min(u),x:0}:{x:d3.min(u),y:0},h=r?{y:d3.max(u),x:0}:{x:d3.max(u),y:0},g=s.map(function(t,e){return r?{y:t.length?d3.median(t):d3.median([t.x0,t.x1]),x:d[e]}:{x:t.length?d3.median(t):d3.median([t.x0,t.x1]),y:d[e]}}),p=o(u,i),m={values:u,binned:s,frequencies:d,points:[f].concat(g).concat([h])};m[a]=p,l[t]=m}),l},O.hypenate=a,O.round=i,O.getContainingSVG=l,O.interpolateColors=function(){return d3.interpolateRgbBasis(arguments)},O.truncateText=function(t,e,n,r,o,a){var i=t.node().getBoundingClientRect();for(t.text(e);Math.max(i.width,i.height)>o-r&&(e=(e=String(e)).slice(0,e.length-1),t.text(e+"..."),i=t.node().getBoundingClientRect(),0!=e.length););},O.safeSelect=c,O.whichBin=h,O.unique=d,O.flatten=f,O.setupStandardChartContainers=function(t,e,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{top:.01,bottom:.01,left:.01,right:.01},o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{w:.8,h:.6},a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{y:.1,x:.1},i=arguments.length>6&&void 0!==arguments[6]?arguments[6]:{x:0,margin:0,pos:"left"};void 0==n&&(n={w:window.innerWidth,h:window.Height});var l={w:n.w*o.w,h:n.h*o.h},c={top:r.top*l.h,bottom:r.bottom*l.h,left:r.left*l.w,right:r.right*l.w},u=l.w-c.left-c.right,s=l.h-c.top-c.bottom,d={x:s*a.x,y:u*a.y},f={x:u-d.y-i.x-2*i.margin,y:s-d.x},h={x:i.margin+c.left+("left"==i.pos?0:f.x+d.y),y:c.top,w:i.x,h:f.y},g={x:d.y+c.left+("left"==i.pos?i.x+2*i.margin:0),y:c.top,w:d.y,h:f.y},p={x:d.y+c.left+("left"==i.pos?i.x+2*i.margin:0),y:c.top,w:f.x,h:f.y},m={x:d.y+c.left+("left"==i.pos?i.x+2*i.margin:0),y:c.top+f.y,w:f.x,h:d.x};return n=d3sm.safeSelect(t,"svg",e).style("width",l.w+"px").style("height",l.h+"px"),a=d3sm.safeSelect(n,"g",d3sm.hypenate(e,"axes")),i=d3sm.safeSelect(n,"g",d3sm.hypenate(e,"legend")).attr("transform","translate("+h.x+","+h.y+")"),{svg:{selection:n,rect:l},plot:{selection:d3sm.safeSelect(n,"g",d3sm.hypenate(e,"plot")).attr("transform","translate("+p.x+","+p.y+")"),rect:p},xAxis:{selection:d3sm.safeSelect(a,"g",d3sm.hypenate(e,"x-axis")).attr("transform","translate("+m.x+","+m.y+")"),rect:m},yAxis:{selection:d3sm.safeSelect(a,"g",d3sm.hypenate(e,"y-axis")).attr("transform","translate("+g.x+","+g.y+")"),rect:g},legend:{selection:i,rect:h}}},O.log=m,O.warn=function(t,e,n){!0===window.d3sm.debugQ&&console.warn("%c[d3sm::"+t+"]:\t"+e,["background: #ffd53e","border-radius: 5000px","padding: 0px 2px","font-size: 14px"].join(";")),console.table(n)},O.info=function(t,e,n){window.d3sm.debugQ&&console.info("%c[d3sm::"+t+"]:\t"+e,["background: #009ccd","border-radius: 5000px","padding: 0px 2px","font-size: 14px"].join(";")),console.table(n)},O.error=function(t,e,n){window.d3sm.debugQ&&console.error("[d3sm::"+t+"]:\t"+e+"\t%o",n)},O.consoleGroup=g,O.consoleGroupEnd=p,O.resizeDebounce=function(t,e){var n=function(t,e,n){var r;return function(){var o=this,a=arguments,i=n&&!r;clearTimeout(r),r=setTimeout(function(){r=null,n||t.apply(o,a)},e),i&&t.apply(o,a)}}(function(){t()},e);window.addEventListener("resize",n)},O.debugQ=!1,window.d3sm=O,t.default=O,t}({}); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZDNzbS5taW4udjAuMC4zLmpzIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL2hlbHBlcnMuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL2FycmF5LWZ1bmN0aW9ucy5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvdXRpbHMuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL2dyb3VwaW5nLXNwYWNlci5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvY29sb3ItZnVuY3Rpb24uanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL3Rvb2x0aXAuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL3NlbGVjdC1maWx0ZXIuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL2xhc3NvLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9kMy1wcm90b3R5cGVzLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbWFpbi5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvYXhpcy5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvYmFyLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9idWJibGUtaGVhdG1hcC5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvaGVhdG1hcC5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvYm94LXdoaXNrZXIuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL2RhdGEtdG9nZ2xlLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9zY2F0dGVyLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9wbG90LXpvb20uanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL211bHRpLXBsb3Qtem9vbS5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvdmlvbGluLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9udW1lcmljLWxlZ2VuZC5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvY2F0ZWdvcmljYWwtbGVnZW5kLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9sYXNzby13aWRnZXQuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL3Vwc2V0LmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9maWx0ZXItdGFibGUuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gaW1wb3J0IHtoYXNRfSBmcm9tICcuL2FycmF5LWZ1bmN0aW9ucyc7XG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSEVMUEVSUyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqXG4qIEhlbHBlciBmdW5jdGlvbiBmb3IgQXJyYXkuZmlsdGVyIHRvIGdldCB1bmlxdWUgZWxlbWVudHMgb2YgdGhlIGFycmF5XG4qIEBwYXJhbSB7Kn0gdmFsdWUgY3VycmVudCB2YWx1ZSBhcyBtYXBwaW5nIG92ZXIgYXJyYXkgKHNlbGYpXG4qIEBwYXJhbSB7bnVtYmVyfSBpbmRleCBjdXJyZW50IGluZGV4IGluIHRoZSBhcnJheVxuKiBAcGFyYW0ge0FycmF5fSBzZWxmIHBhc3NlZCBhcnJheSBmcm9tIEFycmF5LmZpbHRlciBtZXRob2RcbiogQHJldHVybnMge2Jvb2xlYW59IHdoZXRoZXIgb3Igbm90IHZhbHVlIGlzIHRoZSBmaXJzdCBvZiBpdHMga2luZCAoaS5lLiBpbmRleE9mKHZhbHVlKSA9PSBpbmRleClcbiovXG5leHBvcnQgZnVuY3Rpb24gdW5pcXVlRWxlbWVudHModmFsdWUsIGluZGV4LCBzZWxmKSB7IHJldHVybiBzZWxmLmluZGV4T2YodmFsdWUpID09PSBpbmRleDsgfVxuXG4vKipcbiogRXh0cmFjdHMgeCBhbmQgeSBvZiB0cmFuc2xhdGUgZnJvbSB0cmFuc2Zvcm0gcHJvcGVydHlcbiogQHBhcmFtIHtzdHJpbmd9IHRyYW5zZm9ybSB0cmFuc2Zvcm0gcHJvcGVydHkgb2Ygc3ZnIGVsZW1lbnRcbiogQHJldHVybnMge251bWJlcltdfSB4LCB5IG9mIHRyYW5zbGF0ZSh4LCB5KVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRUcmFuc2xhdGlvbih0cmFuc2Zvcm0pIHtcbiAgLy8gQ3JlYXRlIGEgZHVtbXkgZyBmb3IgY2FsY3VsYXRpb24gcHVycG9zZXMgb25seS4gVGhpcyB3aWxsIG5ldmVyXG4gIC8vIGJlIGFwcGVuZGVkIHRvIHRoZSBET00gYW5kIHdpbGwgYmUgZGlzY2FyZGVkIG9uY2UgdGhpcyBmdW5jdGlvblxuICAvLyByZXR1cm5zLlxuICB2YXIgZyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUygnaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnLCAnZycpO1xuICAvLyBTZXQgdGhlIHRyYW5zZm9ybSBhdHRyaWJ1dGUgdG8gdGhlIHByb3ZpZGVkIHN0cmluZyB2YWx1ZS5cbiAgdHJhbnNmb3JtID0gdHJhbnNmb3JtID09IHVuZGVmaW5lZCA/ICd0cmFuc2xhdGUoMCwwKScgOiB0cmFuc2Zvcm07XG4gIGcuc2V0QXR0cmlidXRlTlMobnVsbCwgJ3RyYW5zZm9ybScsIHRyYW5zZm9ybSk7XG4gIC8vIGNvbnNvbGlkYXRlIHRoZSBTVkdUcmFuc2Zvcm1MaXN0IGNvbnRhaW5pbmcgYWxsIHRyYW5zZm9ybWF0aW9uc1xuICAvLyB0byBhIHNpbmdsZSBTVkdUcmFuc2Zvcm0gb2YgdHlwZSBTVkdfVFJBTlNGT1JNX01BVFJJWCBhbmQgZ2V0XG4gIC8vIGl0cyBTVkdNYXRyaXguXG4gIHZhciBtYXRyaXggPSBnLnRyYW5zZm9ybS5iYXNlVmFsLmNvbnNvbGlkYXRlKCkubWF0cml4O1xuICAvLyBBcyBwZXIgZGVmaW5pdGlvbiB2YWx1ZXMgZSBhbmQgZiBhcmUgdGhlIG9uZXMgZm9yIHRoZSB0cmFuc2xhdGlvbi5cbiAgcmV0dXJuIFttYXRyaXguZSwgbWF0cml4LmZdO1xufVxuXG5cbi8qKlxuKiBNb2RpZmllcyBsdW1pbmFuY2Ugb2YgaGV4aWRlY2ltYWwgbnVtYmVyXG4qIEBwYXJhbSB7c3RyaW5nfSBoZXggc2hvdWxkIGJlIGhleGlkZWNpbWFsIHZhbHVlIHdpdGggb3Igd2l0aG91dCB0aGUgcHJvY2VlZGluZyBvY3RvdHJvcGVcbiogQHBhcmFtIHtudW1iZXJ9IGx1bSB2YWx1ZSB0byBpbmNyZWFzZSBvciBkZWNyZWFzZSBsdW1pbm9zaXR5IGJ5XG4qIEByZXR1cm5zIHtzdHJpbmd9IHVwZGF0ZWQgaGV4aWRlY2ltYWwgdmFsdWUgd2l0aG91dCB0aGUgcHJvY2VlZGluZyBvY3RvdHJvcGVcbiovXG5leHBvcnQgZnVuY3Rpb24gbW9kaWZ5SGV4aWRlY2ltYWxDb2xvckx1bWluYW5jZShoZXgsIGx1bSkge1xuICAvLyB2YWxpZGF0ZSBoZXggc3RyaW5nXG4gIHZhciBoZXggPSBTdHJpbmcoaGV4KS5yZXBsYWNlKC9bXjAtOWEtZl0vZ2ksICcnKTtcblxuICBpZiAoaGV4Lmxlbmd0aCA8IDYpIHtcbiAgICBoZXggPSBoZXhbMF0raGV4WzBdK2hleFsxXStoZXhbMV0raGV4WzJdK2hleFsyXTtcblx0fVxuXHRsdW0gPSBsdW0gfHwgMDtcblxuXHQvLyBjb252ZXJ0IHRvIGRlY2ltYWwgYW5kIGNoYW5nZSBsdW1pbm9zaXR5XG5cdHZhciByZ2IgPSAnIycsIGMsIGk7XG5cdGZvciAoaSA9IDA7IGkgPCAzOyBpKyspIHtcblx0XHRjID0gcGFyc2VJbnQoaGV4LnN1YnN0cihpKjIsMiksIDE2KTtcblx0XHRjID0gTWF0aC5yb3VuZChNYXRoLm1pbihNYXRoLm1heCgwLCBjICsgKGMgKiBsdW0pKSwgMjU1KSkudG9TdHJpbmcoMTYpO1xuXHRcdHJnYiArPSAoJzAwJytjKS5zdWJzdHIoYy5sZW5ndGgpO1xuXHR9XG5cblx0cmV0dXJuIHJnYjtcbn1cblxuXG4vKipcbiogQGRlcHJlY2F0ZWQgQHNlZXtAbGluayB0aWNrUmFuZ2V9XG4qIEBwYXJhbSB7bnVtYmVyfSBtaW5cbiogQHBhcmFtIHtudW1iZXJ9IG1heFxuKiBAcGFyYW0ge251bWJlcn0gcGFydHNcbiogQHJldHVybnMge251bWJlcltdfSBhcnJheSBvZiBsZW5ndGggcGFydHMgZXZlbmx5IHBhcnRpdGlvbmVkIGJldHdlZW4gbWluIGFuZCBtYXhcbiovXG5leHBvcnQgZnVuY3Rpb24gcGFydGl0aW9uUmFuZ2VJbnRvKG1pbiwgbWF4LCBwYXJ0cykge1xuICB2YXIgZGlmZiA9IG1heCAtIG1pblxuICByZXR1cm4gQXJyYXkocGFydHMpLm1hcChmdW5jdGlvbiAoZSwgaSkgeyByZXR1cm4gbWluICsgZGlmZiAvIHBhcnRzICogaSB9KVxufVxuXG5cbi8qKlxuKiBDYWxjdWxhdGVkIHRoZSBxdWFydGlsZXMgb2YgdGhlIHBhc3NlZCBkYXRhIGFuZCBzdG9yZXMgdGhlbSB3aXRoIHFLZXlzXG4qIEBwYXJhbSB7bnVtYmVyW119IGRhdGEgbGlzdCBvZiBudW1lcmljYWwgdmFsdWVzXG4qIEBwYXJhbSB7c3RyaW5nW119IFtxS2V5cz1bJ3EwJywgJ3ExJywgJ3EyJywgJ3EzJywgJ3E0J11dIGhvdyByZXR1cm5lZCBvYmplY3Qgd2l0aCBxdWFydGlsZXMgc2hvdWxkIGJlIHN0b3JlZFxuKiBAcmV0dXJucyB7T2JqZWN0fSB3aXRoIGtleXMgcUtleXMgZ2l2aW5nIG9ubHkgdGhlIG51bWVyaWNhbCB2YWx1ZXMgZm9yIHRoZSBxdWFydGlsZXNcbiovXG5leHBvcnQgZnVuY3Rpb24gcXVhcnRpbGVzKGRhdGEsIHFLZXlzKSB7XG4gIHZhclxuICBxMiA9IGQzLm1lZGlhbihkYXRhKSxcbiAgbG93ZXIgPSBkYXRhLmZpbHRlcih4ID0+IHggPCBxMiksXG4gIHVwcGVyID0gZGF0YS5maWx0ZXIoeCA9PiB4ID4gcTIpLFxuXG4gIHExID0gZDMubWVkaWFuKGxvd2VyKSxcbiAgcTEgPSBxMSA9PSB1bmRlZmluZWQgPyBxMiA6IHExLFxuXG4gIHEwID0gZDMubWluKGxvd2VyKSxcbiAgcTAgPSBxMCA9PSB1bmRlZmluZWQgPyBxMSA6IHEwLFxuXG4gIHEzID0gZDMubWVkaWFuKHVwcGVyKSxcbiAgcTMgPSBxMyA9PSB1bmRlZmluZWQgPyBxMiA6IHEzLFxuXG4gIHE0ID0gZDMubWF4KHVwcGVyKSxcbiAgcTQgPSBxNCA9PSB1bmRlZmluZWQgPyBxMyA6IHE0LFxuXG4gIGswID0gJ3EwJywgazEgPSAncTEnLCBrMiA9ICdxMicsIGszID0gJ3EzJywgazQgPSAncTQnLFxuICBvYmogPSB7fVxuICBpZiAocUtleXMhPXVuZGVmaW5lZCAmJiBxS2V5cy5sZW5ndGggPT0gNSkgeyBrMCA9IHFLZXlzWzBdOyBrMSA9IHFLZXlzWzFdOyBrMiA9IHFLZXlzWzJdOyBrMyA9IHFLZXlzWzNdOyBrNCA9IHFLZXlzWzRdOyB9XG4gIG9ialtrMF0gPSBxMDsgb2JqW2sxXSA9IHExOyBvYmpbazJdID0gcTI7IG9ialtrM10gPSBxMzsgb2JqW2s0XSA9IHE0O1xuXG4gIHJldHVybiBvYmpcbn1cblxuXG4vKipcbiogSGVscGVyIGZ1bmN0aW9uIHRvIGdldCBhbGwgdmFsdWVzIG5lZWRlZCBpbiBtYWtpbmcgdmlvbGluIHBsb3RzXG4qIEBwYXJhbSB7c3RyaW5nW119IHZpb2xpbktleXNcbiogQHBhcmFtIHtudW1iZXJbXX0gZGF0YVxuKiBAcGFyYW0ge0Z1bmN0aW9ufSB2YWx1ZUV4dHJhY3RvckZ1bmN0aW9uIGhvdyB0byBnZXQgdmFsdWVzIGZyb20gZGF0YVt2aW9saW5LZXlzW2ldXVxuKiBAcGFyYW0ge2Jvb2xlYW59IGhvcml6b250YWxRIHdoZXRoZXIgb3Igbm90IHZpb2xpbnMgd2lsbCBiZSByZW5kZXJlZCBob3Jpem9udGFsbHkgb3IgdmVydGljYWxseVxuKiBAcGFyYW0ge3N0cmluZ30gcUtleSBob3cgdGhlIG9iamVjdCBjb250YWluaW5nIHRoZSBxdWFydGlsZXMgc2hvdWxkIGJlIGxhYmVsZWQgYXNcbiogQHBhcmFtIHtzdHJpbmdbXX0gcUtleXMgaG93IGVhY2ggcXVhcnRpbGUgc2hvdWxkIGJlIGxhYmVsZWQgYXNcbiogQHJldHVybnMge09iamVjdH0gcmVxdWlyZWQgZm9yIEBzZWV7QGxpbmsgdmlvbGlufSBjb250YWluaW5nIGtleXMgdmFsdWVzLCBiaW5ubmVkLCBmcmVxdWVuY2llcywgcG9pbnRzLCBhbmQgcXVhcnRpbGVzXG4qIEBzZWV7QGxpbmsgcXVhcnRpbGVzfVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0VmlvbGluVmFsdWVzKFxuICB2aW9saW5LZXlzLFxuICBkYXRhLFxuICB2YWx1ZUV4dHJhY3RvckZ1bmN0aW9uLFxuICBob3Jpem9udGFsUSxcbiAgcUtleSxcbiAgcUtleXNcbil7XG4gIHZhciBvYmogPSB7fVxuICB2aW9saW5LZXlzLm1hcChmdW5jdGlvbihrLCBpKXtcbiAgICAgdmFyIGQgPSB2YWx1ZUV4dHJhY3RvckZ1bmN0aW9uKGssIGksIGRhdGEpLFxuICAgICBiaW5uZWQgPSBkMy5oaXN0b2dyYW0oKShkKSxcbiAgICAgZnJlcXVlbmNpZXMgPSBiaW5uZWQubWFwKHg9PngubGVuZ3RoKSxcbiAgICAgbWluUG9pbnQgPSBob3Jpem9udGFsUSA/IHt5OiBkMy5taW4oZCksIHg6IDB9IDoge3g6IGQzLm1pbihkKSwgeTogMH0sXG4gICAgIG1heFBvaW50ID0gaG9yaXpvbnRhbFEgPyB7eTogZDMubWF4KGQpLCB4OiAwfSA6IHt4OiBkMy5tYXgoZCksIHk6IDB9LFxuICAgICBwb2ludHMgPSBiaW5uZWQubWFwKGZ1bmN0aW9uKGJpbiwgaSkge1xuICAgICAgIHJldHVybiBob3Jpem9udGFsUVxuICAgICAgID8ge3k6IChiaW4ubGVuZ3RoKSA/IGQzLm1lZGlhbihiaW4pOiBkMy5tZWRpYW4oW2Jpbi54MCwgYmluLngxXSksIHg6IGZyZXF1ZW5jaWVzW2ldfVxuICAgICAgIDoge3g6IChiaW4ubGVuZ3RoKSA/IGQzLm1lZGlhbihiaW4pOiBkMy5tZWRpYW4oW2Jpbi54MCwgYmluLngxXSksIHk6IGZyZXF1ZW5jaWVzW2ldfVxuICAgICB9KSxcbiAgICAgcXVhcnRzID0gcXVhcnRpbGVzKGQsIHFLZXlzKSxcbiAgICAgbyA9IHtcbiAgICAgICB2YWx1ZXM6IGQsXG4gICAgICAgYmlubmVkOiBiaW5uZWQsXG4gICAgICAgZnJlcXVlbmNpZXM6IGZyZXF1ZW5jaWVzLFxuICAgICAgIHBvaW50czogW21pblBvaW50XS5jb25jYXQocG9pbnRzKS5jb25jYXQoW21heFBvaW50XSlcbiAgICAgfVxuICAgICBvW3FLZXldID0gcXVhcnRzO1xuICAgICBvYmpba10gPSBvO1xuICAgfSk7XG4gICByZXR1cm4gb2JqO1xufVxuXG4vKipcbiogSHlwZW5hdGVzIGFsbCBzdHJpbmdzIHRvZ2V0aGVyXG4qIEBwYXJhbSB7c3RyaW5nW119IGFyZ3VtZW50c1xuKiBAcmV0dXJucyB7c3RyaW5nfSBcImFyZzEtYXJnMi0uLi4tYXJnblwiXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIGh5cGVuYXRlKCl7IHJldHVybiBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpLmpvaW4oJy0nKSB9XG5cblxuLyoqXG4qIFJvdW5kcyBkZWNpbWFscyBvZiBudW1iZXIgdG8gcHJlY2lzaW9uXG4qIEBwYXJhbSB7bnVtYmVyfSBudW1iZXJcbiogQHBhcmFtIHtudW1iZXJ9IHByZWNpc2lvblxuKiBAcmV0dXJucyB7bnVtYmVyfSByb3VuZGVkIHRvIHByZWNpc2lvblxuKi9cbmV4cG9ydCBmdW5jdGlvbiByb3VuZChudW1iZXIsIHByZWNpc2lvbikge1xuICB2YXIgc2hpZnQgPSBmdW5jdGlvbiAobnVtYmVyLCBwcmVjaXNpb24sIHJldmVyc2VTaGlmdCkge1xuICAgIGlmIChyZXZlcnNlU2hpZnQpIHtcbiAgICAgIHByZWNpc2lvbiA9IC1wcmVjaXNpb247XG4gICAgfVxuICAgIHZhciBudW1BcnJheSA9ICgnJyArIG51bWJlcikuc3BsaXQoJ2UnKTtcbiAgICByZXR1cm4gKyhudW1BcnJheVswXSArICdlJyArIChudW1BcnJheVsxXSA/ICgrbnVtQXJyYXlbMV0gKyBwcmVjaXNpb24pIDogcHJlY2lzaW9uKSk7XG4gIH07XG4gIHJldHVybiBzaGlmdChNYXRoLnJvdW5kKHNoaWZ0KG51bWJlciwgcHJlY2lzaW9uLCBmYWxzZSkpLCBwcmVjaXNpb24sIHRydWUpO1xufVxuXG4vKipcbiogcmVjdXJzaXZlbHkgYXNjZW5kcyBlbGVtZW50LnBhcmVudEVsZW1lbnQgdG8gZmluZCBhIHN2ZyB0YWdcbiogQHBhcmFtIHtFbGVtZW50fSBlbGVtZW50XG4qIEByZXR1cm5zIHtFbGVtZW50IHwgdW5kZWZpbmVkfVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRDb250YWluaW5nU1ZHKGVsZW1lbnQpIHtcbiAgdmFyIHBhcmVudCA9IGVsZW1lbnQucGFyZW50RWxlbWVudFxuICB2YXIgdGFnID0gcGFyZW50LnRhZ05hbWUudG9Mb3dlckNhc2UoKVxuICBpZiAodGFnID09PSAnc3ZnJykgeyByZXR1cm4gcGFyZW50OyB9XG4gIGlmICh0YWcgPT09ICdodG1sJykgeyByZXR1cm4gdW5kZWZpbmVkOyB9XG4gIHJldHVybiBnZXRDb250YWluaW5nU1ZHKHBhcmVudCk7XG59XG5cbi8qKlxuKiBNYXBzIGFyZ3VtZW50cyBpbiB0byBkMy5pbnRlcnBvbGF0ZVJnYkJhc2lzXG4qIEBwYXJhbSBhcmd1bWVudHNcbiogQHJldHVybnMge0Z1bmN0aW9ufVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBpbnRlcnBvbGF0ZUNvbG9ycygpe3JldHVybiBkMy5pbnRlcnBvbGF0ZVJnYkJhc2lzKGFyZ3VtZW50cyl9XG5cblxuLyoqXG4qIFRyeXMgdG8gcmVkdWNlIHRleHQgdG8gZml0IGluIHNwZWNpZmllZCBhcmVhLCBtYWRlIGZvciB0aWNrIGxhYmVscyBhcyBjYWxsZWQgYnlcbiogQHNlZXtAbGluayBheGlzfVxuKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gdCBjb250YWluZXIgZm9yIHNwZWNpZmljIGF4aXMgdGlja1xuKiBAcGFyYW0ge3N0cmluZ30gdGV4dCB0byBiZSB0aGUgbGFiZWwgb2YgdGhlIHBhc3NlZCBheGlzIHRpY2tcbiogQHBhcmFtIHtib29sZWFufSBvcmllbnQgb2YgdGhlIGF4aXMsIHRydWUgaXMgaG9yaXpvbnRhbCwgZmFsc2UgaXMgdmVydGljYWxcbiogQHBhcmFtIHtudW1iZXJ9IHRpY2tMZW5ndGggaXMgdGhlIGxlbmd0aCBvZiB0aGUgdGV4dFxuKiBAcGFyYW0ge251bWJlcn0gc3BhY2UgaXMgdGhlIGFtb3VudCBvZiBhdmFpbGJsZSBzcGFjZSBmb3IgdGhlIHRleHQgYW5kIHRoZSB0aWNrIHRvIGZpdCBpblxuKiBAcGFyYW0ge2Jvb2xlYW59IG92ZXJmbG93USB3aGV0aGVyIG9yIG5vdCBhbGxvd2VkIHRvIGdvIG92ZXIgdGhlIGFsbG90ZWQgc3BhY2VcbiogQHJldHVybnMge25vbmV9XG4qL1xuZXhwb3J0IGZ1bmN0aW9uIHRydW5jYXRlVGV4dCh0LCB0ZXh0LCBvcmllbnQsIHRpY2tMZW5ndGgsIHNwYWNlLCBvdmVyZmxvd1EpIHtcbiAgdmFyIHJlY3QgPSB0Lm5vZGUoKS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuICB0LnRleHQodGV4dClcbiAgd2hpbGUgKE1hdGgubWF4KHJlY3Qud2lkdGgsIHJlY3QuaGVpZ2h0KSA+IHNwYWNlIC0gdGlja0xlbmd0aCkge1xuICAgIHRleHQgPSBTdHJpbmcodGV4dClcbiAgICB0ZXh0ID0gdGV4dC5zbGljZSgwLCB0ZXh0Lmxlbmd0aCAtIDEpXG4gICAgdC50ZXh0KHRleHQgKyAnLi4uJylcbiAgICByZWN0ID0gdC5ub2RlKCkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICBpZiAodGV4dC5sZW5ndGggPT0gMCkgYnJlYWtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gdHJ1bmNhdGVTdHJpbmcoc3RyaW5nLCBzcGFjZSwgZm9udCkge1xuICB2YXIgY2hhcnMgPSBzcGFjZSAvIGZvbnQ7XG4gIGlmIChjaGFycyA8IHN0cmluZy5sZW5ndGgpIHtcbiAgICByZXR1cm4gc3RyaW5nLnNsaWNlKDAsIE1hdGgucm91bmQoY2hhcnMtNSkpICsgJy4uLidcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gc3RyaW5nXG4gIH1cbn1cblxuXG4vKipcbiogVHJ5cyB0byB1c2UgZDMuc2VsZWN0aW9uIHRvIGdldCBlbGVtZW50LCBpZiBpdCBkb2VzbnQgZXhpc3QsIG1ha2VzIG9uZVxuKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gc2VsIHNlbGVjdGlvbiBpbiB3aGljaCB0byB0cnkgYW5kIGZpbmQgb2JqZWN0XG4qIEBwYXJhbSB7c3RyaW5nfSB0YWcgdGFnIG9mIHdoaWNoIHRvIHRyeSBhbmQgc2VsZWN0XG4qIEBwYXJhbSB7c3RyaW5nfSBbY2xzPScnXSBjbGFzcyBvZiB0YWcgdG8gdHJ5IGFuZCBncmFiXG4qIEByZXR1cm5zIHtkMy5zZWxlY3Rpb259IG9mIGVpdGhlciBhcHBlbmQgb3Igc2VsZWN0ZWQgdGFnLmNscyB3aXRoaW4gc2VsXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIHNhZmVTZWxlY3Qoc2VsLCB0YWcsIGNscykge1xuICB2YXIgY2xzU3RyID0gY2xzID09IHVuZGVmaW5lZCA/ICcnIDogJy4nK2NscztcbiAgdmFyIHNTZWwgPSBzZWwuc2VsZWN0KHRhZytjbHNTdHIpLmVtcHR5KClcbiAgPyBzZWwuYXBwZW5kKHRhZylcbiAgOiBzZWwuc2VsZWN0KHRhZytjbHNTdHIpXG4gIHJldHVybiBzU2VsXG4gIC5jbGFzc2VkKGNsc1N0ci5yZXBsYWNlKCcuJywgJycpLCB0cnVlKVxuICAuYXR0cigndHJhbnNmb3JtJywgc1NlbC5hdHRyKCd0cmFuc2Zvcm0nKSA9PSB1bmRlZmluZWQgPyAndHJhbnNsYXRlKDAsMCknIDogc1NlbC5hdHRyKCd0cmFuc2Zvcm0nKSlcbn1cblxuLyoqXG4qIGV2ZW5seSBwYXJ0aXRpb25zIHRoZSByYW5nZSBbbWluLCBtYXhdIGludG8gbiBwYXJ0c1xuKiBAcGFyYW0ge251bWJlcn0gbWluXG4qIEBwYXJhbSB7bnVtYmVyfSBtYXhcbiogQHBhcmFtIHtudW1iZXJ9IG5cbiogQHJldHVybnMge251bWJlcltdfSBhcnJheSBvZiBsZW5ndGggbiBldmVubHkgcGFydGl0aW9uZWQgYmV0d2VlbiBtaW4gYW5kIG1heFxuKi9cbmV4cG9ydCBmdW5jdGlvbiB0aWNrUmFuZ2UobWluLCBtYXgsIG4pIHtcbiAgdmFyIGEgPSBbbWluXVxuICB2YXIgZCA9IG1heC1taW5cbiAgdmFyIHMgPSBkIC8gKG4tMSlcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBuLTI7IGkrKykgeyBhLnB1c2gobWluICsgcyAqIChpKzEpKSB9XG4gIGEucHVzaChtYXgpXG4gIHJldHVybiBhXG59XG5cblxuZXhwb3J0IGZ1bmN0aW9uIGV1Y2xpZGVhbkRpc3RhbmNlKHAxLCBwMil7XG4gIHZhciBhID0gIHAxWzBdIC0gcDJbMF0sIGIgPSAgcDFbMV0gLSBwMlsxXVxuICByZXR1cm4gTWF0aC5zcXJ0KGEqYSArIGIqYilcbn1cbiIsImltcG9ydCB7dW5pcXVlRWxlbWVudHN9IGZyb20gJy4vaGVscGVycyc7XG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBST1RPVFlQRVMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqXG4qIFRoaXMgZnVuY3Rpb24gdGVzdHMgdG8gc2VlIGlmIGFsbCBlbGVtZW50cyBvZiB0aGUgcGFzc2VkIGFycmF5IGFyZSB0cnVlLlxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiB2YWx1ZXNcbiogQHBhcmFtIHtGdW5jdGlvbn0gW2Z1bmMgZnVuY3Rpb24odmFsdWUpe3JldHVybiB2YWx1ZSA9PSB0cnVlO31dIGlzIGFwcGxpZWQgdG8gZWFjaCB2YWx1ZSBvZiB0aGUgYXJyYXkgYW5kIHNob3VsZCByZXR1cm4gYSBib29sZWFuLlxuKiBAcmV0dXJucyB7Ym9vbGVhbn0gaWYgYWxsIHZhbHVlcyBhcmUgdHJ1ZSBieSBmdW5jdGlvblxuKi9cbmV4cG9ydCBmdW5jdGlvbiBhbGwoIGFycmF5LCBmdW5jICkge1xuICBpZiAoZnVuYyA9PSB1bmRlZmluZWQpIHsgcmV0dXJuIGFycmF5LmV2ZXJ5KCBmdW5jdGlvbih2YWx1ZSkgeyByZXR1cm4gdmFsdWUgPT09IHRydWU7IH0pOyB9XG4gIHJldHVybiBhcnJheS5ldmVyeSggZnVuY3Rpb24odmFsdWUpIHsgcmV0dXJuIGZ1bmModmFsdWUpOyB9ICk7XG59XG5cbi8qKlxuKiBDb3VudHMgdGhlIG51bWJlciBvZiBvY2N1cmFuY2VzIG9mIGVhY2ggZWxlbWVudCBpbiB0aGUgZ2l2ZW4gYXJyYXkuXG4qIEBwYXJhbSB7QXJyYXl9IGFycmF5IG9mIGVsZW1lbnRzXG4qIEByZXR1cm5zIHtPYmplY3R9IG9mIGtleTogdmFsdWUgcGFpcnMgd2hlcmUga2V5IGlzIGFuIGVsZW1lbnQgaW4gdGhlIGFycmF5IGFuZCB2YWx1ZSBpcyB0aGUgbnVtYmVyIG9mIHRpbWVzIGl0IG9jY3Vycy5cbiovXG5leHBvcnQgZnVuY3Rpb24gdGFsbHkoIGFycmF5ICkge1xuICB2YXIgdGFsbGllcyA9IHt9O1xuICBhcnJheS5tYXAoIGZ1bmN0aW9uICggZWxlbWVudCApIHtcbiAgICBpZiAoIGhhc1EoT2JqZWN0LmtleXModGFsbGllcyksIGVsZW1lbnQpICkgeyB0YWxsaWVzW2VsZW1lbnRdID0gMTsgfVxuICAgIGVsc2UgeyB0YWxsaWVzW2VsZW1lbnRdICs9IDE7IH1cbiAgfSk7XG4gIHJldHVybiB0YWxsaWVzO1xufVxuLyoqXG4qIFNob3J0LWhhbmQgZm9yIGFycmF5LmluY2x1ZGVzKGl0ZW0pO1xuKiBAcGFyYW0ge0FycmF5fSBhcnJheVxuKiBAcGFyYW0geyp9IGl0ZW0gdG8gdGVzdCBpZiBjb250YWluZWQgaW4gIHthcnJheX1cbiogQHJldHVybnMge2Jvb2xlYW59XG4qL1xuZXhwb3J0IGZ1bmN0aW9uIGhhc1EoIGFycmF5LCBpdGVtICkgeyByZXR1cm4gYXJyYXkuaW5jbHVkZXMoaXRlbSk7IH1cblxuLyoqXG4qIFJldHVybnMgZmlyc3QgaXRlbSBpbiBhcnJheVxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtc1xuKiBAcmV0dXJucyB7Kn0gYXJyYXlbMF1cbiovXG5leHBvcnQgZnVuY3Rpb24gZmlyc3QoIGFycmF5ICkgeyByZXR1cm4gYXJyYXlbMF07IH1cblxuLyoqXG4qIFJldHVybnMgbGFzdCBpdGVtIGluIGFycmF5XG4qIEBwYXJhbSB7QXJyYXl9IGFycmF5IG9mIGl0ZW1zXG4qIEByZXR1cm5zIHsqfSBhcnJheVthcnJheS5sZW5ndGgtMV1cbiovXG5leHBvcnQgZnVuY3Rpb24gbGFzdCggYXJyYXkgKSB7IHJldHVybiBhcnJheVthcnJheS5sZW5ndGgtMV07IH1cblxuLyoqXG4qIENhbGN1bGF0ZXMgdGhlIHRvdGFsIHZhbHVlIG9mIG51bWJlcnMgaW4gcGFzc2VkIGFycmF5XG4qIEBwYXJhbSB7bnVtYmVyW119IGFycmF5IG9mIG51bWVyaWNhbCB2YWx1ZXNcbiogQHJldHVybnMge251bWJlcn0gc3VtIG92ZXIgZWxlbWVudHMgaW4gYXJyYXlcbiovXG5leHBvcnQgZnVuY3Rpb24gdG90YWwoIGFycmF5ICkgeyByZXR1cm4gYXJyYXkucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCkgfTtcblxuLyoqXG4qIFJlbW92ZXMgZHVwbGljYXRlcyBpbiBhcnJheVxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtc1xuKiBAcmV0dXJucyB7QXJyYXl9IG9mIGl0ZW1zIHN1Y2ggdGhhdCBpdGVtX2kgIT0gaXRlbV9qIGZvciBhbGwgaSA8IGpcbiogQHNlZXtAbGluayB1bmlxdWVFbGVtZW50c30gZm9yIHRoZSBmaWx0ZXJpbmcgZnVuY3Rpb25cbiovXG5leHBvcnQgZnVuY3Rpb24gdW5pcXVlKCBhcnJheSApIHsgcmV0dXJuIGFycmF5LmZpbHRlciggdW5pcXVlRWxlbWVudHMgKTsgfVxuXG4vKipcbiogRmlsdGVycyBwYXNzZWQgYXJyYXkgZm9yIHNwZWNpZmllZCBpbmRpY2llc1xuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtc1xuKiBAcGFyYW0ge251bWJlcltdfSBwb3NpdGlvbnMgb2YgaW50ZWdlcnMgc3VjaCB0aGF0IGkgPCBhcnJheS5sZW5ndGhcbiogQHJldHVybnMge0FycmF5fSBvZiBpdGVtcyBzdWNoIHRoYXQgZm9yIGFueSBpdGVtX2ksIHBvc2l0aW9ucy5pbmNsdWRlcyhpKSA9PT0gdHJ1ZVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXQoIGFycmF5LCBwb3NpdGlvbnMgKSB7XG4gIHJldHVybiBhcnJheS5maWx0ZXIoIGZ1bmN0aW9uKCB2YWx1ZSwgaW5kZXggKSB7IHJldHVybiBoYXNRKHBvc2l0aW9ucywgaW5kZXgpOyB9ICk7XG59XG5cbi8qKlxuKiBEZXRlcm1pbmVzIGlmIGFsbCBlbGVtZW50cyBpbiBwYXNzZWQgYXJyYXkgYXJlIGFycmF5cyB0aGVtc2VsdmVzLlxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtc1xuKiBAcmV0dXJucyB7Ym9vbGVhbn0gdHJ1ZSBpZiBBcnJheS5pc0FycmF5KGUpIGlzIHRydWUgZm9yIGFsbCBlIGluIGFycmF5XG4qIEBzZWV7QGxpbmsgYWxsfVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBsaXN0T2ZMaXN0c1EoIGFycmF5ICkge1xuICByZXR1cm4gYWxsKCBhcnJheS5tYXAoIGZ1bmN0aW9uKCBlbGVtZW50LCBpbmRleCApIHsgcmV0dXJuIEFycmF5LmlzQXJyYXkoZWxlbWVudCkgfSApIClcbn1cblxuLyoqXG4qIEJ1aWx0IG9uIHRvcCBvZiBAc2Vle0BsaW5rIGdldH0sIG1hcHBpbmcgaWYgcG9zaXRpb25zIGlzIGEgbGlzdCBvZiBsaXN0cyAoQHNlZXtAbGluayBsaXN0T2ZMaXN0c1F9KVxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtc1xuKiBAcGFyYW0ge251bWJlcltdIHwgW11udW1iZXJbXSB9IHBvc2l0aW9ucyBvZiBpbnRlZ2VycyBvciBsaXN0IG9mIHBvc2l0aW9ucyBvZiBpbnRlZ2Vyc1xuKiBAcmV0dXJucyB7Ym9vbGVhbn0gcmV0dXJucyBzcGVjaWZpZWQgcG9zaXRpb25zIGZyb20gYXJyYXkuIElmIG5lc3RlZCBwb3NpdGlvbnMgcGFzc2VkLCByZXR1cm5zIHJlcXVlc3RlZCBpdGVtcyBpbiBzYW1lIHN0cnVjdHVyZS5cbiovXG5leHBvcnQgZnVuY3Rpb24gY3V0KCBhcnJheSwgcG9zaXRpb25zICkge1xuICBpZiAoIGxpc3RPZkxpc3RzUShhcnJheSkgKSB7IHJldHVybiBwb3NpdGlvbnMubWFwKGZ1bmN0aW9uKHBvcywgaSkgeyByZXR1cm4gYXJyYXkuZ2V0KHBvcyk7IH0pOyB9XG4gIHJldHVybiBnZXQoIGFycmF5LCBwb3NpdGlvbnMgKTtcbn1cblxuLyoqXG4qIEdpdmVuIGFuIGFycmF5IG9mIG9iamVjdHMsIGNvbnN0cnVjdHMgbmV3IG9iamVjdHMgd2hlcmUgZWFjaCB2YWx1ZSBpcyBhIGxpc3RcbiogYmFzZWQgb24gdGhlIGNvcnJlc29uZGluZyBrZXksIHdoaWNoIGlzIGV4dHJhY3RlZCBieSB0aGUgcGFyYW1ldGVyIGJ5XG4qIEBwYXJhbSB7T2JqZWN0c1tdfSBhcnJheSBvZiBvYmplY3RzXG4qIEBwYXJhbSB7c3RyaW5nfSBieSBrZXkgd2l0aGluIGFsbCBvYmplY3RzIG9mIHBhc3NlZCBhcnJheVxuKiBAcGFyYW0ge3N0cmluZ1tdfSBbZ3JvdXBzXSBzYXZlcyBzb21lIGNvbXB1dGF0aW9uIGlmIGFsbCBrbm93biB2YWx1ZXMgZXh0cmFjdGVkIGJ5IG1hcHBpbmcgb3ZlciB0aGUgcGFyYW1ldGVyIGJ5IGFyZSBwYXNzZWRcbiogQHJldHVybnMge09iamVjdH0gb2Yga2V5IHZhbHVlIHBhaXJzLCB3aGVyZSBrZXlzIGFyZSBhbGwgdmFsdWVzIG9mIHRoZSBrZXkgYnkgZnJvbSBhbiBvYmplY3QgaW4gdGhlIHBhc3NlZCBhcnJheSBhbmQgdGhlIHZhbHVlIGFyZSB0aG9zZSBjb3JyZXNwb25kaW5nIG9iamVjdHMuXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIGdyb3VwQnkgKGFycmF5LCBieSwgZ3JvdXBzKSB7XG4gIGlmIChncm91cHMgPT0gdW5kZWZpbmVkKSB7XG4gICAgZ3JvdXBzID0gdW5pcXVlKGFycmF5Lm1hcChmdW5jdGlvbihlbGVtZW50cywgaW5kZXgpeyByZXR1cm4gZWxlbWVudFtieV07IH0pKTtcbiAgICBncm91cHMubWFwKGZ1bmN0aW9uKHZhbHVlLCBpbmRleCl7Z3JvdXBwZWRbdmFsdWVdID0gW119KVxuICB9XG5cbiAgdmFyIGdyb3VwcGVkID0ge307XG4gIGFycmF5Lm1hcChmdW5jdGlvbihlbGVtZW50LCBpbmRleCl7Z3JvdXBwZWRbZWxlbWVudFtieV1dLnB1c2goZWxlbWVudCl9KTtcbiAgcmV0dXJuIGdyb3VwcGVkXG59XG5cbi8qKlxuKiBUZXN0cyBpZiB0d28gYXJyYXlzIGFyZSBlcXVpdmFsZW50XG4qIEBwYXJhbSB7QXJyYXl9IGFycmF5XG4qIEBwYXJhbSB7QXJyYXl9IG90aGVyXG4qIEByZXR1cm5zIHtib29sZWFufSBpZiBldmVyeSBlbGVtZW50IG9mIGFycmF5IG1hdGNoZXMgdGhhdCBvZiBvdGhlclxuKi9cbmV4cG9ydCBmdW5jdGlvbiBhcnJheUVxdWFscyhhcnJheSwgb3RoZXIpIHtcbiAgaWYgKCFvdGhlcilcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgLy8gY29tcGFyZSBsZW5ndGhzIC0gY2FuIHNhdmUgYSBsb3Qgb2YgdGltZVxuICBpZiAoYXJyYXkubGVuZ3RoICE9IG90aGVyLmxlbmd0aClcbiAgICAgIHJldHVybiBmYWxzZTtcblxuICBmb3IgKHZhciBpID0gMCwgbD1hcnJheS5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgIC8vIENoZWNrIGlmIHdlIGhhdmUgbmVzdGVkIGFycmF5c1xuICAgICAgaWYgKGFycmF5W2ldIGluc3RhbmNlb2YgQXJyYXkgJiYgb3RoZXJbaV0gaW5zdGFuY2VvZiBBcnJheSkge1xuICAgICAgICAgIC8vIHJlY3Vyc2UgaW50byB0aGUgbmVzdGVkIGFycmF5c1xuICAgICAgICAgIGlmICghYXJyYXlFcXVhbHMoYXJyYXlbaV0sb3RoZXJbaV0pKVxuICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICBlbHNlIGlmIChhcnJheVtpXSAhPSBvdGhlcltpXSkge1xuICAgICAgICAgIC8vIFdhcm5pbmcgLSB0d28gZGlmZmVyZW50IG9iamVjdCBpbnN0YW5jZXMgd2lsbCBuZXZlciBiZSBlcXVhbDoge3g6MjB9ICE9IHt4OjIwfVxuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgfVxuICByZXR1cm4gdHJ1ZTtcbn1cblxuXG5cbi8qKlxuKiBSZWN1cnNpdmVseSB0YWxsaWVzIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgYXQgZWFjaCBsZXZlbCBvZiB0aGUgcGFzc2VkLCBwdXRhdGl2ZWx5IG5lc3RlZCBhcnJheVxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtcyB3aGljaCBtYXkgaW5jbHVkZSBuZXN0ZWQgYXJyYXlzXG4qIEBwYXJhbSB7bnVtYmVyfSBbbGV2ZWw9MF0gY3VycmVudCBkZXB0aCBpbiB0aGUgcmVjdXJzaW9uXG4qIEBwYXJhbSB7QXJyYXl9IFtsZXZlbERhdGE9W11dIGtlZXBzIHRyYWNrIG9mIGl0ZW1zIHNlZW4gc28gZmFyIGF0IGVhY2ggZGVwdGhcbiogQHJldHVybnMge0FycmF5fSBzdGF0aW5nIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgKGFycmF5IGluY2x1c2l2ZSkgZm91bmQgYXQgZWFjaCBsZXZlbCBvZiB0aGUgYXJyYXlcbiovXG5leHBvcnQgZnVuY3Rpb24gZWxlbWVudHNBdExldmVscyhhcnJheSwgbGV2ZWwsIGxldmVsRGF0YSkge1xuICBsZXZlbCA9IGxldmVsID09IHVuZGVmaW5lZCA/IDAgOiBsZXZlbCArIDE7XG4gIGxldmVsRGF0YSA9IGxldmVsRGF0YSA9PSB1bmRlZmluZWQgPyBbXSA6IGxldmVsRGF0YTtcbiAgaWYgKCBsZXZlbCA+PSBsZXZlbERhdGEubGVuZ3RoICkgeyBsZXZlbERhdGEucHVzaChhcnJheS5sZW5ndGgpfSBlbHNlIHtsZXZlbERhdGFbbGV2ZWxdICs9IGFycmF5Lmxlbmd0aCB9XG4gIGFycmF5Lm1hcChmdW5jdGlvbihlLCBpKSB7aWYgKEFycmF5LmlzQXJyYXkoZSkpeyBlbGVtZW50c0F0TGV2ZWxzKGUsIGxldmVsLCBsZXZlbERhdGEpIH19KVxuICByZXR1cm4gbGV2ZWxEYXRhXG59XG5cblxuLyoqXG4qIFJlY3Vyc2l2ZWx5IHRhbGxpZXMgdGhlIG51bWJlciBvZiBlbGVtZW50cyBvZiB0aGUgcGFzc2VkLCBwdXRhdGl2ZWx5IG5lc3RlZCBhcnJheVxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtcyB3aGljaCBtYXkgaW5jbHVkZSBuZXN0ZWQgYXJyYXlzXG4qIEBwYXJhbSB7bnVtYmVyfSBbZWxlbWVudHM9MF0gY3VycmVudCBudW1iZXIgb2YgZWxlbWVudHMgc2VlbiBzbyBmYXJcbiogQHJldHVybnMge251bWJlcn0gbnVtYmVyIG9mIGVsZW1lbnRzIChhcnJheSBpbmNsdXNpdmUpIGZvdW5kIGluIHBhc3NlZCBhcnJheVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBudW1iZXJPZkVsZW1lbnRzKCBhcnJheSwgZWxlbWVudHMgKSB7XG4gIGVsZW1lbnRzID0gZWxlbWVudHMgPT0gdW5kZWZpbmVkID8gMCA6IGVsZW1lbnRzO1xuICBhcnJheS5tYXAoZnVuY3Rpb24oZSwgaSkge1xuICAgIGlmICggQXJyYXkuaXNBcnJheShlKSApIHsgZWxlbWVudHMgPSBudW1iZXJPZkVsZW1lbnRzKGUsIGVsZW1lbnRzKSB9XG4gICAgZWxzZSB7IGVsZW1lbnRzICs9IDEgfVxuICB9KVxuICByZXR1cm4gZWxlbWVudHNcbn1cblxuLyoqXG4qIENvbmNhdHMgYWxsIG5lc3RlZCBhcnJheXMgaW4gcGFzc2VkIGFycmF5IHRvIGZvcm0gYSBzaW5nbGUgYXJyYXlcbiogQHBhcmFtIHtBcnJheX0gYXJyYXkgb2YgcHV0YXRpdmVseSBuZXN0ZWQgYXJyYXlzXG4qIEBwYXJhbSB7QXJyYXl9IFtmbGF0PVtdXSBjdXJyZW50IGZsYXR0ZW5lZCBhcnJheVxuKiBAcmV0dXJucyB7QXJyYXl9IHdpdGggZXZlcnkgZWxlbWVudCBpbiB0aGUgc2FtZSBsZXZlbFxuKi9cbmV4cG9ydCBmdW5jdGlvbiBmbGF0dGVuKCBhcnJheSwgZmxhdCApIHtcbiAgZmxhdCA9IGZsYXQgPT0gdW5kZWZpbmVkID8gW10gOiBmbGF0O1xuICBhcnJheS5tYXAoZnVuY3Rpb24oZSwgaSl7XG4gICAgaWYgKEFycmF5LmlzQXJyYXkoZSkpIHtmbGF0ID0gZmxhdC5jb25jYXQoZmxhdHRlbihlKSl9XG4gICAgZWxzZSB7ZmxhdC5wdXNoKGUpfVxuICB9KVxuICByZXR1cm4gZmxhdDtcbn1cblxuLyoqXG4qIFNlYXJjaCBvZiBsaXN0IG9mIGxpc3RzIHRvIGZpbmQgd2hpY2ggLSBpZiBhbnkgLSBwYXNzZWQgdmFsdWUgaXMgaW5cbiogQHBhcmFtIHtBcnJheVtdfSBiaW5zIGxpc3Qgb2YgbGlzdHMgb2YgdmFsdWVzXG4qIEBwYXJhbSB7Kn0gdmFsdWUgaXRlbSB0byB0ZXN0IGlmIGluIGFueSBvZiB0aGUgYmluc1xuKiBAcmV0dXJucyB7bnVtYmVyfSBpbmRpY2F0aW5nIHRoZSBpbmRleCBvZiB0aGUgYmluIGluIHdoaWNoIHZhbHVlIHdhcyBmb3VuZFxuKi9cbmV4cG9ydCBmdW5jdGlvbiB3aGljaEJpbihiaW5zLCB2YWx1ZSkge1xuICB2YXIgaSA9IC0xXG4gIGZvciAodmFyIGogPSAwOyBqIDwgYmlucy5sZW5ndGg7IGorKykgeyBpZiAoaGFzUShiaW5zW2pdLHZhbHVlKSkge3JldHVybiBqfSB9XG4gIHJldHVybiBpXG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHt0b3RhbH0gZnJvbSAnLi9hcnJheS1mdW5jdGlvbnMnO1xuXG5cbi8qKlxuICogY2FsbHMgY29uc29sZS5ncm91cCBpZiBkM3NtLmRlYnVnUSA9PSB0cnVlXG4gKiBAcGFyYW0ge3N0cmluZ30gbmFtZSBvZiB0aGUgZ3JvdXBcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb25zb2xlR3JvdXAobmFtZSkge1xuICBpZiAod2luZG93LmQzc20uZGVidWdRID09PSB0cnVlKXtcbiAgICBjb25zb2xlLmdyb3VwKG5hbWUpXG4gIH1cbn1cblxuLyoqXG4gKiBjYWxscyBjb25zb2xlLmdyb3VwRW5kIGlmIGQzc20uZGVidWdRID09IHRydWVcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb25zb2xlR3JvdXBFbmQoKSB7XG4gIGlmICh3aW5kb3cuZDNzbS5kZWJ1Z1EgPT09IHRydWUpe1xuICAgIGNvbnNvbGUuZ3JvdXBFbmQoKVxuICB9XG59XG5cbi8qKlxuICogQ2FsbHMgY29uc29sZS5sb2cgaWYgZDNzbS5kZWJ1Z1EgPT0gdHJ1ZVxuICogQHBhcmFtIHtzdHJpbmd9IGZ1bmMgbmFtZSBvZiB0aGUgZnVuY3Rpb24gbG9nZ2luZ1xuICogQHBhcmFtIHtzdHJpbmd9IG1zZyB0byBsb2dcbiAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhIHRvIGJlIGxvZ2dlZCBhbG9uZyBzaWRlIHRoZSBtZXNzYWdlXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfVxuICovXG5leHBvcnQgZnVuY3Rpb24gbG9nKGZ1bmMsIG1zZywgZGF0YSkge1xuICBpZiAod2luZG93LmQzc20uZGVidWdRID09PSB0cnVlKXtcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgIGAlY1tkM3NtOjoke2Z1bmN9XTpcXHQke21zZ31gLFxuICAgICAgW1xuICAgICAgICAnYmFja2dyb3VuZDogIzZjZDFlZicsXG4gICAgICAgICdib3JkZXItcmFkaXVzOiA1MDAwcHgnLFxuICAgICAgICAncGFkZGluZzogMHB4IDJweCcsXG4gICAgICAgICdmb250LXNpemU6IDE0cHgnXG4gICAgICBdLmpvaW4oJzsnKVxuICAgIClcbiAgICBjb25zb2xlLnRhYmxlKGRhdGEpXG4gICAgLy8gY29uc29sZS50cmFjZSgpXG4gIH1cbn1cblxuLyoqXG4gKiBDYWxscyBjb25zb2xlLndhcm4gaWYgZDNzbS5kZWJ1Z1EgPT0gdHJ1ZVxuICogQHBhcmFtIHtzdHJpbmd9IGZ1bmMgbmFtZSBvZiB0aGUgZnVuY3Rpb24gd2FybmluZ1xuICogQHBhcmFtIHtzdHJpbmd9IG1zZyB0byBkaXNwbGF5XG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YSB0byBiZSBkaXNwbGF5ZWQgYWxvbmcgc2lkZSB0aGUgbWVzc2FnZVxuICogQHJldHVybnMge3VuZGVmaW5lZH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdhcm4oZnVuYywgbXNnLCBkYXRhKSB7XG4gIGlmICh3aW5kb3cuZDNzbS5kZWJ1Z1EgPT09IHRydWUpXG4gICAgY29uc29sZS53YXJuKFxuICAgICAgYCVjW2Qzc206OiR7ZnVuY31dOlxcdCR7bXNnfWAsXG4gICAgICBbXG4gICAgICAgICdiYWNrZ3JvdW5kOiAjZmZkNTNlJyxcbiAgICAgICAgJ2JvcmRlci1yYWRpdXM6IDUwMDBweCcsXG4gICAgICAgICdwYWRkaW5nOiAwcHggMnB4JyxcbiAgICAgICAgJ2ZvbnQtc2l6ZTogMTRweCdcbiAgICAgIF0uam9pbignOycpXG4gICAgKVxuICAgIGNvbnNvbGUudGFibGUoZGF0YSlcbn1cbi8qKlxuICogQ2FsbHMgdGhlIGNvbnNvbGUuaW5mbyBpZiBkM3NtLmRlYnVnUSA9PSB0cnVlXG4gKiBAcGFyYW0ge3N0cmluZ30gZnVuYyBuYW1lIG9mIHRoZSBmdW5jdGlvbiBwcm92aWRpbmcgaW5mb1xuICogQHBhcmFtIHtzdHJpbmd9IG1zZyB0byBkaXNwbGF5XG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YSB0byBiZSBkaXNwbGF5ZWQgYWxvbmcgc2lkZSB0aGUgbWVzc2FnZVxuICogQHJldHVybnMge3VuZGVmaW5lZH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGluZm8oZnVuYywgbXNnLCBkYXRhKSB7XG4gIGlmICh3aW5kb3cuZDNzbS5kZWJ1Z1EpXG4gICAgY29uc29sZS5pbmZvKFxuICAgICAgYCVjW2Qzc206OiR7ZnVuY31dOlxcdCR7bXNnfWAsXG4gICAgICBbXG4gICAgICAgICdiYWNrZ3JvdW5kOiAjMDA5Y2NkJyxcbiAgICAgICAgJ2JvcmRlci1yYWRpdXM6IDUwMDBweCcsXG4gICAgICAgICdwYWRkaW5nOiAwcHggMnB4JyxcbiAgICAgICAgJ2ZvbnQtc2l6ZTogMTRweCdcbiAgICAgIF0uam9pbignOycpXG4gICAgKVxuICAgIGNvbnNvbGUudGFibGUoZGF0YSlcbn1cblxuXG4vKipcbiAqIENhbGxzIGNvbnNvbGUuZXJyb3IgaWYgZDNzbS5kZWJ1Z1EgPT0gdHJ1ZVxuICogQHBhcmFtIHtzdHJpbmd9IGZ1bmMgbmFtZSBvZiB0aGUgZnVuY3Rpb24gd2hpY2ggc2VuZHMgdGhlIGVycm9yXG4gKiBAcGFyYW0ge3N0cmluZ30gbXNnIHRvIGRpc3BsYXlcbiAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhIHRvIGJlIGRpc3BsYXllZCBhbG9uZyBzaWRlIHRoZSBtZXNzYWdlXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfVxuICovXG5leHBvcnQgZnVuY3Rpb24gZXJyb3IoZnVuYywgbXNnLCBkYXRhKSB7XG4gIGlmICh3aW5kb3cuZDNzbS5kZWJ1Z1EpXG4gICAgY29uc29sZS5lcnJvcihgW2Qzc206OiR7ZnVuY31dOlxcdCR7bXNnfVxcdCVvYCxkYXRhKVxufVxuXG5cblxuXG5cbi8qKlxuKiBGdW5jdGlvbiBmb3Igc2V0dGluZyB1cCBjb250YWluZXJzIGZvciBtb3N0IHBsb3RzIHdpdGggdGhlIHkgYXhpcyBjb250YWluZXJcbiogcG9zaXRpb25lZCBvbiB0aGUgbGVmdCBhbmQgdGhlIHggYXhpcyBjb250YWluZXIgcG9zaXRpb25lZCBvbiB0aGUgYm90dG9tXG4qIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBzZWxlY3Rpb24gc2VsZWN0aW9uIG9mIGNvbnRhaW5lciBpbiB3aGljaCB0aGUgc3ZnIGlzIG9yIHNob3VsZCBiZSBtYWRlXG4qIEBwYXJhbSB7c3RyaW5nfSBuYW1lc3BhY2UgbmFtZXNwYWNlIG9mIHRoZSBjaGFydFxuKiBAcGFyYW0ge09iamVjdH0gW3NwYWNlPXt3OndpbmRvdy5pbm5lcldpZHRoLCBoOndpbmRvdy5pbm5lckhlaWdodH1dIHRoZSB3aWR0aCAodykgYW5kIGhlaWdodCAoaCkgYXZhaWxibGVcbiogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZS53PXdpbmRvdy5pbm5lcldpZHRoXSB0aGUgYXZhaWxhYmxlIHdpZHRoIGluIHdoaWNoIHRvIHJlbmRlciB0aGUgY2hhcnRcbiogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZS5oPXdpbmRvdy5pbm5lckhlaWdodF0gdGhlIGF2YWlsYWJsZSBoZWlnaHQgaW4gd2hpY2ggdG8gcmVuZGVyIHRoZSBjaGFydFxuXG4qIEBwYXJhbSB7T2JqZWN0fSBbbWFyZ2lucz17dG9wOiAwLjAxLCBib3R0b206IDAuMDEsIGxlZnQ6IDAuMDEsIHJpZ2h0OiAwLjAxfV0gdGhlIG1hcmdpbnMgZm9yIHRoZSBjaGFydFxuKiBAcGFyYW0ge251bWJlcn0gW21hcmdpbnMudG9wPTAuMDFdIHRoZSB0b3AgbWFyZ2luIG9mIHRoZSBjaGFydFxuKiBAcGFyYW0ge251bWJlcn0gW21hcmdpbnMuYm90dG9tPTAuMDFdIHRoZSBib3R0b20gbWFyZ2luIG9mIHRoZSBjaGFydFxuKiBAcGFyYW0ge251bWJlcn0gW21hcmdpbnMubGVmdD0wLjAxXSB0aGUgbGVmdCBtYXJnaW4gb2YgdGhlIGNoYXJ0XG4qIEBwYXJhbSB7bnVtYmVyfSBbbWFyZ2lucy5yaWdodD0wLjAxXSB0aGUgcmlnaHQgbWFyZ2luIG9mIHRoZSBjaGFydFxuXG5cbiogQHBhcmFtIHtPYmplY3R9IFtwZXJjZW50YWdlcyA9IHtheGVzOnt4OjAuMSx5OjAuMX0sc3BhY2U6e3c6MC44LGg6MC42fX1dIHBlcmNlbnRhZ2VzIG9mIHRoZSBwYXJhbWF0ZXIgc3BhY2Ugb2Ygd2hpY2ggdG8gbWFrZSB0aGUgeCBhbmQgeSBheGVzIGFzIHdlbGwgYXMgdGhlIHBlcmNlbnQgb2YgdGhlIGF2YWlsYmxlIHNwYWNlIGluIHdoaWNoIHRvIHJlbmRlciB0aGUgcGxvdFxuKiBAcGFyYW0ge09iamVjdH0gW3BlcmNlbnRhZ2VzLmF4ZXM9e3g6MC4xLHk6MC4xfV0gdGhlIHBlcmNlbnRhZ2VzIG9mIHRoZSBwYXJhbWF0ZXIgc3BhY2UsIG9mIHdoaWNoIHRoZSB4IGFuZCB5IGF4ZXMgd2lsbCB0YWtlIHVwXG4qIEBwYXJhbSB7bnVtYmVyfSBbcGVyY2VudGFnZXMuYXhlcy54QXhpc1BlcmNlbnQ9MC4xXSB0aGUgcGVyY2VudGFnZXMgb2YgdGhlIHBhcmFtYXRlciBzcGFjZSwgb2Ygd2hpY2ggdGhlIHggYXhpcyB3aWxsIHRha2UgdXBcbiogQHBhcmFtIHtudW1iZXJ9IFtwZXJjZW50YWdlcy5heGVzLnlBeGlzUGVyY2VudD0wLjFdIHRoZSBwZXJjZW50YWdlcyBvZiB0aGUgcGFyYW1hdGVyIHNwYWNlLCBvZiB3aGljaCB0aGUgeSBheGlzIHdpbGwgdGFrZSB1cFxuXG4qIEBwYXJhbSB7T2JqZWN0fSBbcGVyY2VudGFnZXMuc3BhY2U9e3c6MC44LGg6MC42fV0gdGhlIHBlcmNlbnRhZ2VzIG9mIHRoZSBwYXJhbWF0ZXIgc3BhY2UsIG9mIHdoaWNoIHRoZSBTVkcncyB3aWR0aCBhbmQgaGVpZ2h0IHdpbGwgYmUgc2V0XG4qIEBwYXJhbSB7bnVtYmVyfSBbcGVyY2VudGFnZXMuc3BhY2UucGVyY2VudE9mU3BhY2VGb3JXaWR0aD0wLjFdIHRoZSBwZXJjZW50YWdlcyBvZiB0aGUgcGFyYW1hdGVyIHNwYWNlLCBvZiB3aGljaCB0aGUgU1ZHJ3Mgd2lkdGggd2lsbCBiZSBzZXRcbiogQHBhcmFtIHtudW1iZXJ9IFtwZXJjZW50YWdlcy5zcGFjZS5wZXJjZW50T2ZTcGFjZUZvckhlaWdodD0wLjFdIHRoZSBwZXJjZW50YWdlcyBvZiB0aGUgcGFyYW1hdGVyIHNwYWNlLCBvZiB3aGljaCB0aGUgU1ZHJ3MgaGVpZ2h0IHdpbGwgYmUgc2V0XG5cbiogQHJldHVybnMge09iamVjdH0gcmV0dXJucyB0aGUgc2VsZWN0aW9uIGFuZCBcImJvdW5kaW5nUmVjdHNcIiBvZiB0aGUgcGxvdCBjb250YWluZXIsIHgtYXhpcyBjb250YWluZXIgYW5kIHktYXhpcyBjb250YWluZXJcbiogYXNcbipcbioge1xuKlxuKiAgIHBsb3Q6IHtzZWxlY3Rpb246IHBsb3RTZWxlY3Rpb24sIHJlY3Q6IHBsb3RSZWN0fSxcbipcbiogICB4QXhpczp7c2VsZWN0aW9uOnhBeGlzU2VsZWN0aW9uLCByZWN0OnhBeGlzUmVjdH0sXG4qXG4qICAgeUF4aXM6IHtzZWxlY3Rpb246eUF4aXNTZWxlY3Rpb24sIHJlY3Q6eUF4aXNSZWN0fVxuKlxuKiB9XG4qXG4qIHdoZXJlIGVhY2ggcmVjdCBoYXMgZm9ybTpcbipcbioge3g6ICMsIHk6ICMsIGg6ICMsIHc6ICN9XG4qXG4qIGRlcGljdGluZyB0aGUgc3RhcnRpbmcgeCBhbmQgeSBjb29yZGluYXRlIG9mIHRoZSBjb3Jlc3BvbmRpbmcgY29udGFpbmVyIChhbHNvIHRoZWlyIGRlZmF1bHQgdHJhbnNmb3JtIHZhbHVlcykgYXMgd2VsbCB0aGVpciBoZWlnaHQgKGgpIGFucyB3aWR0aCAodylcbiovXG4vLyBleHBvcnQgZnVuY3Rpb24gc2V0dXBTdGFuZGFyZENoYXJ0Q29udGFpbmVycyggc2VsZWN0aW9uLCBuYW1lc3BhY2UsIHNwYWNlLCBtYXJnaW5zLCBwZXJjZW50YWdlcykge1xuLy8gZXhwb3J0IGZ1bmN0aW9uIHNldHVwU3RhbmRhcmRDaGFydENvbnRhaW5lcnMoXG4vLyAgIHNlbGVjdGlvbixcbi8vICAgbmFtZXNwYWNlLFxuLy8gICBzcGFjZT17dzphdmFpbGFibGVXaWR0aD13aW5kb3cuaW5uZXJXaWR0aCwgaDphdmFpbGFibGVIZWlnaHQ9d2luZG93LmlubmVySGVpZ2h0fSxcbi8vICAgbWFyZ2lucz17dG9wOjAuMDEsIGJvdHRvbTowLjAxLCBsZWZ0OjAuMDEsIHJpZ2h0OjAuMDF9LFxuLy8gICBwZXJjZW50YWdlcz17YXhlczoge3g6IHhBeGlzUGVyY2VudD0wLjEsIHk6IHlBeGlzUGVyY2VudD0wLjF9LCBzcGFjZToge3c6IHBlcmNlbnRPZlNwYWNlRm9yV2lkdGgsIGg6IHBlcmNlbnRPZlNwYWNlRm9ySGVpZ2h0fX1cbi8vICkge1xuLy8gICBpZiAoc3BhY2UgPT0gdW5kZWZpbmVkKSB7IHNwYWNlID0ge3c6IHdpbmRvdy5pbm5lcldpZHRoLCBoOiB3aW5kb3cuaW5uZXJIZWlnaHR9IH1cbi8vICAgaWYgKG1hcmdpbnMgPT0gdW5kZWZpbmVkKSB7IG1hcmdpbnMgPSB7dG9wOiAwLjAxLCBib3R0b206IDAuMDEsIGxlZnQ6IDAuMDEsIHJpZ2h0OiAwLjAxfSB9XG4vLyAgIGlmIChwZXJjZW50YWdlcyA9PSB1bmRlZmluZWQpIHsgcGVyY2VudGFnZXMgPSB7fTsgfVxuLy8gICBpZiAocGVyY2VudGFnZXMuYXhlcyA9PSB1bmRlZmluZWQpIHsgcGVyY2VudGFnZXMuYXhlcyA9IHsgeDowLjEsIHk6MC4xIH0gfVxuLy8gICBpZiAocGVyY2VudGFnZXMuc3BhY2UgPT0gdW5kZWZpbmVkKSB7IHBlcmNlbnRhZ2VzLnNwYWNlID0geyB3OiAwLjgsIGg6IDAuNiB9IH1cbi8vXG4vLyAgIC8vIFNWRyB3aWR0aCBhbmQgaGVpZ2h0XG4vLyAgIHZhciBzdmdTcGFjZSA9ICB7XG4vLyAgICAgdzogc3BhY2UudyAqIHBlcmNlbnRhZ2VzLnNwYWNlLncsXG4vLyAgICAgaDogc3BhY2UuaCAqIHBlcmNlbnRhZ2VzLnNwYWNlLmhcbi8vICAgfSxcbi8vXG4vLyAgIC8vIFNwYWNlIGFmdGVyIHJlbW92aW5nIG1hcmdpbnNcbi8vICAgY2hhcnRTcGFjZSA9IHtcbi8vICAgICB3OiBzdmdTcGFjZS53IC0gKG1hcmdpbnMubGVmdCAqIHNwYWNlLncpIC0gKG1hcmdpbnMucmlnaHQgKiBzcGFjZS53KSxcbi8vICAgICBoOiBzdmdTcGFjZS5oIC0gKG1hcmdpbnMudG9wICogc3BhY2UuaCkgLSAobWFyZ2lucy5ib3R0b20gKiBzcGFjZS5oKVxuLy8gICB9LFxuLy9cbi8vICAgLy8gbWFpbiBkaW1lbnNpb24gb2YgeCBhbmQgeSBheGllc1xuLy8gICAvLyBlLmcuIGRlZmluZXMgaG93IHRhbGwgeCBheGlzIGlzIGFzIGxlbmd0aCBpcyBkZXRlcm1pbmVkIGJ5IHBsb3RSZWN0Lndcbi8vICAgYXhlc1NwYWNlID0ge1xuLy8gICAgIHg6IGNoYXJ0U3BhY2UuaCAqIHBlcmNlbnRhZ2VzLmF4ZXMueCxcbi8vICAgICB5OiBjaGFydFNwYWNlLncgKiBwZXJjZW50YWdlcy5heGVzLnlcbi8vICAgfSxcbi8vXG4vLyAgIC8vIHNwYWNlIGxlZnQgZm9yIGRyYXdpbmcgdGhlIGNoYXJ0IHByb3Blcmx5IChlLmcuIGJhcnMsIHZpb2xpbnMsIGV0Yylcbi8vICAgZHJhd2luZ1NwYWNlID0ge1xuLy8gICAgIHg6IGNoYXJ0U3BhY2UudyAtIGF4ZXNTcGFjZS55LFxuLy8gICAgIHk6IGNoYXJ0U3BhY2UuaCAtIGF4ZXNTcGFjZS54XG4vLyAgIH0sXG4vL1xuLy9cbi8vICAgeUF4aXNSZWN0ID0ge1xuLy8gICAgIHg6IGF4ZXNTcGFjZS55ICsgKG1hcmdpbnMubGVmdCAqIHNwYWNlLncpLFxuLy8gICAgIHk6IChtYXJnaW5zLnRvcCAqIHNwYWNlLmgpLFxuLy8gICAgIHc6IGF4ZXNTcGFjZS55LFxuLy8gICAgIGg6IGRyYXdpbmdTcGFjZS55XG4vLyAgIH0sXG4vL1xuLy8gICBwbG90UmVjdCA9IHtcbi8vICAgICB4OiBheGVzU3BhY2UueSArIChtYXJnaW5zLmxlZnQgKiBzcGFjZS53KSxcbi8vICAgICB5OiAobWFyZ2lucy50b3AgKiBzcGFjZS5oKSxcbi8vICAgICB3OiBkcmF3aW5nU3BhY2UueCxcbi8vICAgICBoOiBkcmF3aW5nU3BhY2UueVxuLy8gICB9LFxuLy9cbi8vICAgeEF4aXNSZWN0ID0ge1xuLy8gICAgIHg6IGF4ZXNTcGFjZS55ICsgKG1hcmdpbnMubGVmdCAqIHNwYWNlLncpLFxuLy8gICAgIHk6IChtYXJnaW5zLnRvcCAqIHNwYWNlLmggKyBwbG90UmVjdC5oKSxcbi8vICAgICB3OiBkcmF3aW5nU3BhY2UueCxcbi8vICAgICBoOiBheGVzU3BhY2UueFxuLy8gICB9XG4vL1xuLy9cbi8vICAgdmFyIGNvbnRhaW5lciA9IHNhZmVTZWxlY3Qoc2VsZWN0aW9uLCAnc3ZnJywgbmFtZXNwYWNlKVxuLy8gICAgIC5zdHlsZSgnd2lkdGgnLCBzdmdTcGFjZS53KydweCcpXG4vLyAgICAgLnN0eWxlKCdoZWlnaHQnLCBzdmdTcGFjZS5oKydweCcpXG4vL1xuLy8gICB2YXIgYXhlcyA9IHNhZmVTZWxlY3QoY29udGFpbmVyLCAnZycsIGh5cGVuYXRlKG5hbWVzcGFjZSwgJ2F4ZXMnKSlcbi8vXG4vLyAgIC8vIC5hdHRyKCd0cmFuc2Zvcm0nLCBcInRyYW5zbGF0ZShcIitwbG90UmVjdC54K1wiLFwiK3Bsb3RSZWN0LnkrXCIpXCIpLFxuLy9cbi8vICAgdmFyIHBsb3QgPSBzYWZlU2VsZWN0KGNvbnRhaW5lciwgJ2cnLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdwbG90JykpXG4vLyAgICAgLmF0dHIoJ3RyYW5zZm9ybScsIFwidHJhbnNsYXRlKFwiK3Bsb3RSZWN0LngrXCIsXCIrcGxvdFJlY3QueStcIilcIilcbi8vXG4vLyAgIHZhciB4QXhpcyA9IHNhZmVTZWxlY3QoYXhlcywgJ2cnLCBoeXBlbmF0ZShuYW1lc3BhY2UsICd4LWF4aXMnKSlcbi8vICAgICAuYXR0cigndHJhbnNmb3JtJywgXCJ0cmFuc2xhdGUoXCIreEF4aXNSZWN0LngrXCIsXCIreEF4aXNSZWN0LnkrXCIpXCIpXG4vL1xuLy8gICB2YXIgeUF4aXMgPSBzYWZlU2VsZWN0KGF4ZXMsICdnJywgaHlwZW5hdGUobmFtZXNwYWNlLCAneS1heGlzJykpXG4vLyAgICAgLmF0dHIoJ3RyYW5zZm9ybScsIFwidHJhbnNsYXRlKFwiK3lBeGlzUmVjdC54K1wiLFwiK3lBeGlzUmVjdC55K1wiKVwiKVxuLy9cbi8vICAgcmV0dXJuIHtcbi8vICAgICBzdmc6IHtcbi8vICAgICAgIHNlbGVjdGlvbjogY29udGFpbmVyLFxuLy8gICAgICAgcmVjdDogc3ZnU3BhY2Vcbi8vICAgICB9LFxuLy8gICAgIHBsb3Q6IHtcbi8vICAgICAgIHNlbGVjdGlvbjogcGxvdCxcbi8vICAgICAgIHJlY3Q6IHBsb3RSZWN0XG4vLyAgICAgfSxcbi8vICAgICB4QXhpczoge1xuLy8gICAgICAgc2VsZWN0aW9uOiB4QXhpcyxcbi8vICAgICAgIHJlY3Q6IHhBeGlzUmVjdFxuLy8gICAgIH0sXG4vLyAgICAgeUF4aXM6IHtcbi8vICAgICAgIHNlbGVjdGlvbjogeUF4aXMsXG4vLyAgICAgICByZWN0OiB5QXhpc1JlY3Rcbi8vICAgICB9XG4vLyAgIH1cbi8vXG4vLyAgIC8vIHJldHVybiBbcGxvdCwgeEF4aXMsIHlBeGlzXVxuLy8gfVxuLy9cbi8vXG5cblxuXG5cblxuZXhwb3J0IGZ1bmN0aW9uIHNldHVwU3RhbmRhcmRDaGFydENvbnRhaW5lcnMoXG4gIHNlbGVjdGlvbixcbiAgbmFtZXNwYWNlLFxuICBjb250YWluZXIsXG4gIG1hcmdpbnM9e3RvcDowLjAxLCBib3R0b206MC4wMSwgbGVmdDowLjAxLCByaWdodDowLjAxfSxcbiAgc3ZnPXt3OjAuOCwgaDowLjZ9LCAvLyBwZXJjZW50IG9mIGNvbnRhaW5lciBzcGFjZSBmb3Igc3ZnXG4gIGF4ZXM9e3k6MC4xLCB4OjAuMX0sIC8vIHBlcmNlbnQgb2YgY29udGFpbmVyIHNwYWNlIGZvciBheGVzLFxuICBsZWc9e3g6MCwgbWFyZ2luOjAsIHBvczonbGVmdCd9IC8vIGFic29sdXRlIHdpZHRoIG9mIGxlZ2VuZCBhbmQgc3BhY2Ugb24gZWl0aGVyIHNpemVcbilcbntcbiAgaWYgKGNvbnRhaW5lciA9PSB1bmRlZmluZWQpIHtjb250YWluZXIgPSB7dzp3aW5kb3cuaW5uZXJXaWR0aCwgaDp3aW5kb3cuSGVpZ2h0fX1cbiAgLy8gU1ZHIHdpZHRoIGFuZCBoZWlnaHRcblxuICB2YXIgc3ZnU3BhY2UgPSAge1xuICAgIHc6IGNvbnRhaW5lci53ICogc3ZnLncsXG4gICAgaDogY29udGFpbmVyLmggKiBzdmcuaFxuICB9XG5cbiAgdmFyIG1hcmdQeCA9IHtcbiAgICB0b3A6IG1hcmdpbnMudG9wICogc3ZnU3BhY2UuaCxcbiAgICBib3R0b206IG1hcmdpbnMuYm90dG9tICogc3ZnU3BhY2UuaCxcbiAgICBsZWZ0OiBtYXJnaW5zLmxlZnQgKiBzdmdTcGFjZS53LFxuICAgIHJpZ2h0OiBtYXJnaW5zLnJpZ2h0ICogc3ZnU3BhY2Uud1xuICB9LFxuXG5cblxuICAvLyBTcGFjZSBhZnRlciByZW1vdmluZyBtYXJnaW5zXG4gIGNoYXJ0U3BhY2UgPSB7XG4gICAgdzogc3ZnU3BhY2UudyAtIG1hcmdQeC5sZWZ0IC0gbWFyZ1B4LnJpZ2h0LFxuICAgIGg6IHN2Z1NwYWNlLmggLSBtYXJnUHgudG9wIC0gbWFyZ1B4LmJvdHRvbVxuICB9LFxuXG4gIC8vIG1haW4gZGltZW5zaW9uIG9mIHggYW5kIHkgYXhpZXNcbiAgLy8gZS5nLiBkZWZpbmVzIGhvdyB0YWxsIHggYXhpcyBpcyBhcyBsZW5ndGggaXMgZGV0ZXJtaW5lZCBieSBwbG90UmVjdC53XG4gIGF4ZXNTcGFjZSA9IHtcbiAgICB4OiBjaGFydFNwYWNlLmggKiBheGVzLngsXG4gICAgeTogY2hhcnRTcGFjZS53ICogYXhlcy55XG4gIH0sXG5cbiAgLy8gc3BhY2UgbGVmdCBmb3IgZHJhd2luZyB0aGUgY2hhcnQgcHJvcGVybHkgKGUuZy4gYmFycywgdmlvbGlucywgZXRjKVxuICBkcmF3aW5nU3BhY2UgPSB7XG4gICAgeDogY2hhcnRTcGFjZS53IC0gYXhlc1NwYWNlLnkgLSBsZWcueCAtIDIqbGVnLm1hcmdpbixcbiAgICB5OiBjaGFydFNwYWNlLmggLSBheGVzU3BhY2UueFxuICB9LFxuXG5cbiAgbGVnUmVjdCA9IHtcbiAgICB4OiBsZWcubWFyZ2luICsgbWFyZ1B4LmxlZnQgKyAobGVnLnBvcyA9PSAnbGVmdCcgPyAwIDogZHJhd2luZ1NwYWNlLnggKyBheGVzU3BhY2UueSksXG4gICAgeTogbWFyZ1B4LnRvcCwgLy8gdGhpcyBpcyBzb29tZWhvdyBnZXR0aW5nIGNhbGN1bGF0ZWQgaW5jb3JlY3RseVxuICAgIHc6IGxlZy54LFxuICAgIGg6IGRyYXdpbmdTcGFjZS55XG4gIH0sXG5cbiAgeUF4aXNSZWN0ID0ge1xuICAgIHg6IGF4ZXNTcGFjZS55ICsgbWFyZ1B4LmxlZnQgKyAobGVnLnBvcyA9PSAnbGVmdCcgPyBsZWcueCArIDIqbGVnLm1hcmdpbiA6IDApLFxuICAgIHk6IG1hcmdQeC50b3AsXG4gICAgdzogYXhlc1NwYWNlLnksXG4gICAgaDogZHJhd2luZ1NwYWNlLnlcbiAgfSxcblxuICBwbG90UmVjdCA9IHtcbiAgICB4OiBheGVzU3BhY2UueSArIG1hcmdQeC5sZWZ0ICsgKGxlZy5wb3MgPT0gJ2xlZnQnID8gbGVnLnggKyAyKmxlZy5tYXJnaW4gOiAwKSxcbiAgICB5OiBtYXJnUHgudG9wLFxuICAgIHc6IGRyYXdpbmdTcGFjZS54LFxuICAgIGg6IGRyYXdpbmdTcGFjZS55XG4gIH0sXG5cbiAgeEF4aXNSZWN0ID0ge1xuICAgIHg6IGF4ZXNTcGFjZS55ICsgbWFyZ1B4LmxlZnQgKyAobGVnLnBvcyA9PSAnbGVmdCcgPyBsZWcueCArIDIqbGVnLm1hcmdpbiA6IDApLFxuICAgIHk6IG1hcmdQeC50b3AgKyBkcmF3aW5nU3BhY2UueSxcbiAgICB3OiBkcmF3aW5nU3BhY2UueCxcbiAgICBoOiBheGVzU3BhY2UueFxuICB9XG5cblxuXG4gIGNvbnRhaW5lciA9IGQzc20uc2FmZVNlbGVjdChzZWxlY3Rpb24sICdzdmcnLCBuYW1lc3BhY2UpXG4gICAgLnN0eWxlKCd3aWR0aCcsIHN2Z1NwYWNlLncrJ3B4JylcbiAgICAuc3R5bGUoJ2hlaWdodCcsIHN2Z1NwYWNlLmgrJ3B4JylcblxuICB2YXIgYXhlcyA9IGQzc20uc2FmZVNlbGVjdChjb250YWluZXIsICdnJywgZDNzbS5oeXBlbmF0ZShuYW1lc3BhY2UsICdheGVzJykpXG5cbiAgdmFyIGxlZyA9IGQzc20uc2FmZVNlbGVjdChjb250YWluZXIsICdnJywgZDNzbS5oeXBlbmF0ZShuYW1lc3BhY2UsICdsZWdlbmQnKSlcbiAgLmF0dHIoJ3RyYW5zZm9ybScsIFwidHJhbnNsYXRlKFwiK2xlZ1JlY3QueCtcIixcIitsZWdSZWN0LnkrXCIpXCIpXG5cbiAgdmFyIHBsb3QgPSBkM3NtLnNhZmVTZWxlY3QoY29udGFpbmVyLCAnZycsIGQzc20uaHlwZW5hdGUobmFtZXNwYWNlLCAncGxvdCcpKVxuICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBcInRyYW5zbGF0ZShcIitwbG90UmVjdC54K1wiLFwiK3Bsb3RSZWN0LnkrXCIpXCIpXG5cbiAgdmFyIHhBeGlzID0gZDNzbS5zYWZlU2VsZWN0KGF4ZXMsICdnJywgZDNzbS5oeXBlbmF0ZShuYW1lc3BhY2UsICd4LWF4aXMnKSlcbiAgICAuYXR0cigndHJhbnNmb3JtJywgXCJ0cmFuc2xhdGUoXCIreEF4aXNSZWN0LngrXCIsXCIreEF4aXNSZWN0LnkrXCIpXCIpXG5cbiAgdmFyIHlBeGlzID0gZDNzbS5zYWZlU2VsZWN0KGF4ZXMsICdnJywgZDNzbS5oeXBlbmF0ZShuYW1lc3BhY2UsICd5LWF4aXMnKSlcbiAgICAuYXR0cigndHJhbnNmb3JtJywgXCJ0cmFuc2xhdGUoXCIreUF4aXNSZWN0LngrXCIsXCIreUF4aXNSZWN0LnkrXCIpXCIpXG5cbiAgcmV0dXJuIHtcbiAgICBzdmc6IHtcbiAgICAgIHNlbGVjdGlvbjogY29udGFpbmVyLFxuICAgICAgcmVjdDogc3ZnU3BhY2VcbiAgICB9LFxuICAgIHBsb3Q6IHtcbiAgICAgIHNlbGVjdGlvbjogcGxvdCxcbiAgICAgIHJlY3Q6IHBsb3RSZWN0XG4gICAgfSxcbiAgICB4QXhpczoge1xuICAgICAgc2VsZWN0aW9uOiB4QXhpcyxcbiAgICAgIHJlY3Q6IHhBeGlzUmVjdFxuICAgIH0sXG4gICAgeUF4aXM6IHtcbiAgICAgIHNlbGVjdGlvbjogeUF4aXMsXG4gICAgICByZWN0OiB5QXhpc1JlY3RcbiAgICB9LFxuICAgIGxlZ2VuZDoge1xuICAgICAgc2VsZWN0aW9uOiBsZWcsXG4gICAgICByZWN0OiBsZWdSZWN0XG4gICAgfVxuICB9XG59XG5cblxuXG4vKipcbiogQWRkcyBhIGNsaXAtcGF0aCByZWN0IGFuZCBiaW5kcyBpdCB0byBjb250YWluZXJcbiogQHBhcmFtIHtkMy5zZWxlY3Rpb259IGNvbnRhaW5lciBpbiB3aGljaCB0byBhZGQgdGhlIGNsaXAtcGF0aCBhbmQgdG8gd2hpY2ggdG8gYmluZCB0aGUgY2xpcGluZyBwYXRoIHRvXG4qIEBwYXJhbSB7T2JqZWN0fSByZWN0IHRoZSBjb29yZGluYXRlcyAoeCwgeSwgd2lkdGgsIGhlaWdodCkgb2YgdGhlIGNsaXAtcGF0aFxuKiBAcGFyYW0ge3N0cmluZ30gbmFtZXNwYWNlXG4qIEByZXR1cm5zIHtkMy5zZWxlY3Rpb259IG9mIHRoZSBjbGlwLXBhdGggcmVjdFxuKi9cbmV4cG9ydCBmdW5jdGlvbiBjcFJlY3QoY29udGFpbmVyLCByZWN0LCBuYW1lc3BhY2UpIHtcbiAgdmFyIGRlZnMgPSBzYWZlU2VsZWN0KGNvbnRhaW5lciwgJ2RlZnMnLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdkZWZpbml0aW9ucycpKVxuICB2YXIgY3AgPSBzYWZlU2VsZWN0KGRlZnMsICdjbGlwUGF0aCcsIGh5cGVuYXRlKG5hbWVzcGFjZSwgJ2NsaXAtcGF0aCcpKVxuICAuYXR0cignaWQnLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdjbGlwLXBhdGgnKSlcblxuICB2YXIgY3BSZWN0ID0gc2FmZVNlbGVjdChjcCwgJ3JlY3QnKVxuICAuYXR0cigneCcsIHJlY3QueClcbiAgLmF0dHIoJ3knLCByZWN0LnkpXG4gIC5hdHRyKCd3aWR0aCcsIHJlY3Qud2lkdGgpXG4gIC5hdHRyKCdoZWlnaHQnLCByZWN0LmhlaWdodClcblxuICBkZWZzLnJhaXNlKClcbiAgLy8gc2V0IGNsaXBwaW5nIHBhdGggdG8gY29udGFpbmVyXG4gIGNvbnRhaW5lci5hdHRyKCdjbGlwLXBhdGgnLCAndXJsKCMnKyBoeXBlbmF0ZShuYW1lc3BhY2UsICdjbGlwLXBhdGgnKSsnKScpXG5cbiAgcmV0dXJuIGNwUmVjdFxufVxuXG5cbi8qKlxuKiBBZGRzIGEgYmFja2dyb3VuZCByZWN0IHQgdG8gY29udGFpbmVyXG4qIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBjb250YWluZXIgaW4gd2hpY2ggdG8gYWRkIHRoZSBiYWNrZ3JvdW5kIHJlY3RhbmdsZVxuKiBAcGFyYW0ge09iamVjdH0gcmVjdCB0aGUgY29vcmRpbmF0ZXMgKHgsIHksIHdpZHRoLCBoZWlnaHQpIG9mIHRoZSBiYWNrZ3JvdW5kXG4qIEBwYXJhbSB7c3RyaW5nfSBmaWxsIHRoZSBjb2xvciBvZiB0aGUgYmFja2dyb3VuZFxuKiBAcmV0dXJucyB7ZDMuc2VsZWN0aW9ufSBvZiB0aGUgYmFja2dyb3VuZCBmaWxsXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIGJnUmVjdChjb250YWluZXIsIHJlY3QsIGZpbGwpIHtcbiAgcmV0dXJuIHNhZmVTZWxlY3QoY29udGFpbmVyLCAncmVjdCcsICdiZycpXG4gIC5hdHRyKCd4JywgcmVjdC54KVxuICAuYXR0cigneScsIHJlY3QueSlcbiAgLmF0dHIoJ3dpZHRoJywgcmVjdC53aWR0aClcbiAgLmF0dHIoJ2hlaWdodCcsIHJlY3QuaGVpZ2h0KVxuICAuYXR0cignZmlsbCcsIGZpbGwpXG59XG5cblxuLyoqXG4qIFNldHMgdXAgdGhlIGNvbnRhaW5lciBmb3IgbWFraW5nIGNoYXJ0IGVsZW1lbnRzLiBUaGlzIGluY2x1ZGVzIG1ha2luZ1xuKiBhIGNsaXAtcGF0aCByZWN0IGJvdW5kIHRvIHRoZSBwYXNzZWQgY29udGFpbmVyLCBhIGJhY2tncm91bmQgcmVjdCwgYW5kXG4qIGEgZyBlbGVtZW50IHdpdGggY2xhc3MgPG5hbWVzcGFjZT4tb2JqZWN0LWNvbnRhaW5lci5cbiogQHBhcmFtIHtkMy5zZWxlY3Rpb259IGNvbnRhaW5lciBpbiB3aGljaCB0byBhZGQgdGhlIGNsaXAtcGF0aCBhbmQgYmFja2dyb3VuZFxuKiBAcGFyYW0ge3N0cmluZ30gbmFtZXNwYWNlXG4qIEBwYXJhbSB7T2JqZWN0fSByZWN0IHRoZSBjb29yZGluYXRlcyAoeCwgeSwgd2lkdGgsIGhlaWdodCkgb2YgdGhlIGJhY2tncm91bmQgYW5kIGNsaXAtcGF0aFxuKiBAcGFyYW0ge3N0cmluZ30gZmlsbCB0aGUgY29sb3Igb2YgdGhlIGJhY2tncm91bmRcbiogQHJldHVybnMge2QzLnNlbGVjdGlvbn0gb2YgZy48bmFtZXNwYWNlPi1vYmplY3QtY29udGFpbmVyXG4qXG4qIEBzZWV7QGxpbmsgYmdSZWN0fVxuKiBAc2Vle0BsaW5rIGNwUmVjdH1cbiovXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBDb250YWluZXIoc2VsZWN0aW9uLCBuYW1lc3BhY2UsIHJlY3QsIGZpbGwpIHtcbiAgLy8gdGhlIGNvbnRhaW5lciBmb3IgdGhyZWUgbWFpbiBpdGVtcywgYmcsIGRlZnMsIGFuZCBvYmplY3QtY29udGFpbmVyXG4gIHZhclxuICBjb250YWluZXIgPSBzYWZlU2VsZWN0KHNlbGVjdGlvbiwgJ2cnLCBuYW1lc3BhY2UpLFxuICBiZyA9IGJnUmVjdChjb250YWluZXIsIHJlY3QsIGZpbGwpLFxuICBjcCA9IGNwUmVjdChjb250YWluZXIsIHJlY3QsIG5hbWVzcGFjZSksXG4gIG9iamVjdENvbnRhaW5lciA9IHNhZmVTZWxlY3QoY29udGFpbmVyLCAnZycsIGh5cGVuYXRlKG5hbWVzcGFjZSwgJ29iamVjdC1jb250YWluZXInKSlcbiAgcmV0dXJuIG9iamVjdENvbnRhaW5lclxufVxuXG5cbi8qKlxuKiBkZXRlcm1pbmVzIHRoZSB3aWR0aCBvZiBhbiBvYmplY3QgZm9yIHRoZSBjYWxsaW5nIHBsb3R0aW5nIGZ1bmN0aW9uXG4qIEBwYXJhbSB7bnVtYmVyfSBmcmVlU3BhY2UgaG93IG11Y2ggc3BhY2UgaXMgYXZhbGlibGVcbiogQHBhcmFtIHtudW1iZXJ9IG51bWJlck9mT2JqZWN0cyBob3cgbWFueSBvYmplY3QgZG8gd2UgbmVlZFxuKiBAcGFyYW0ge251bWJlcn0gbWluT2JqZWN0V2lkdGggaG93IHNtYWxsIGFyZSB0aGVzZSBvYmplY3RzIGFsbG93ZWQgdG8gYmVcbiogQHBhcmFtIHtudW1iZXJ9IG1heE9iamVjdFdpZHRoIGhvdyBsYXJnZSBhcmUgdGhlc2Ugb2JqZWN0IGFsbG93ZWQgdG8gYmVcbiogQHBhcmFtIHtudW1iZXJ9IHNpemVPZlNwYWNlciBwZXJjZW50IG9mIGZyZWVTcGFjZSB0aGF0IGEgc2luZ2xlIHNwYWNlciBzaG91bGQgdGFrZSB1cCAobmVlZCBudW1iZXJPZk9iamVjdHMgLSAxIHNwYWNlcnMpXG4qIEBwYXJhbSB7Ym9vbGVhbn0gb3ZlcmZsb3dRIGNhbiB3ZSBnbyBiZXlvbmQgYWxsb3RlZCBzcGFjZVxuKiBAcmV0dXJucyB7bnVtYmVyfSBob3cgbGFyZ2Ugb2JqZWN0IHNob3VsZCBiZVxuKiBmdW5jdGlvbiB0cmllcyB0byBrZWVwIG9iamVjdCB3aXRoaW4gbWluIC8gbWF4IHdpZHRoLCBidXQgd2lsIGRlZmF1bHQgdG9cbiogNWUtMTAgKHNtYWxsZXN0IGNvbnNpc3Rlbmx5IHZpc2libGUgYnkgc3ZnIHNpemUgb2YgZWxlbWVudCkgaWYgb3ZlcmZsb3dRIGlzIGZhbHNlXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3QoZnJlZVNwYWNlLCBudW1iZXJPZk9iamVjdHMsIG1pbk9iamVjdFdpZHRoLCBtYXhPYmplY3RXaWR0aCwgc2l6ZU9mU3BhY2VyLCBvdmVyZmxvd1EpIHtcbiAgdmFyIHNpemVPZlNwYWNlciA9XG4gIHNpemVPZlNwYWNlciA9PSAwIHx8IHNpemVPZlNwYWNlciA+IDFcbiAgPyBzaXplT2ZTcGFjZXJcbiAgOiBmcmVlU3BhY2UgKiBzaXplT2ZTcGFjZXJcblxuICB2YXIgbnVtYmVyT2ZTcGFjZXJzID0gbnVtYmVyT2ZPYmplY3RzIC0gMVxuICB2YXIgc3BhY2VUYWtlbkJ5U3BhY2VycyA9IG51bWJlck9mU3BhY2VycyAqIHNpemVPZlNwYWNlclxuICB2YXIgcmVtYWluaW5nU3BhY2UgPSBmcmVlU3BhY2UgLSBzcGFjZVRha2VuQnlTcGFjZXJzXG4gIHJlbWFpbmluZ1NwYWNlID0gcmVtYWluaW5nU3BhY2UgPCAwID8gMCA6IHJlbWFpbmluZ1NwYWNlXG4gIHZhciBvYmplY3RXaWR0aCA9IHJlbWFpbmluZ1NwYWNlIC8gbnVtYmVyT2ZPYmplY3RzXG5cbiAgaWYgKCBvdmVyZmxvd1EgJiYgbWluT2JqZWN0V2lkdGggIT0gdW5kZWZpbmVkICYmIG9iamVjdFdpZHRoIDwgbWluT2JqZWN0V2lkdGggKSB7IG9iamVjdFdpZHRoID0gbWluT2JqZWN0V2lkdGggfVxuICAvLyBpZiAoIG1heE9iamVjdFdpZHRoICE9IHVuZGVmaW5lZCAmJiBvYmplY3RXaWR0aCA+IG1heE9iamVjdFdpZHRoICkgeyBvYmplY3RXaWR0aCA9IG1heE9iamVjdFdpZHRoIH1cbiAgaWYgKCBvdmVyZmxvd1EgJiYgbWF4T2JqZWN0V2lkdGggIT0gdW5kZWZpbmVkICYmIG9iamVjdFdpZHRoIDwgbWF4T2JqZWN0V2lkdGggKSB7IG9iamVjdFdpZHRoID0gbWF4T2JqZWN0V2lkdGggfVxuICByZXR1cm4gTWF0aC5tYXgob2JqZWN0V2lkdGgsIDVlLTEwKVxufVxuXG4vKipcbiogQHBhcmFtIHtBcnJheVtdfSBkYXRhIGxpc3QgZGF0YSAoY2FuIGJlIG5lc3RlZCkuIElmIG5lc3RlZCB3aWxsIGNyZWF0ZSBtb3JlIGNvbXBsZXggc3BhY2VyIHNpemVcbiogQHBhcmFtIHtudW1iZXJ9IGZyZWVTcGFjZSBob3cgbXVjaCBzcGFjZSBpcyBhdmFsaWJsZVxuKiBAcGFyYW0ge251bWJlcn0gb2JqZWN0V2lkdGggQHNlZXtAbGluayBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0fVxuKiBAcGFyYW0ge251bWJlcn0gbnVtYmVyT2ZPYmplY3RzIGhvdyBtYW55IG9iamVjdCBkbyB3ZSBuZWVkXG4qIEBwYXJhbSB7bnVtYmVyfSBiYXNlU3BhY2VyU2l6ZSBwZXJjZW50IG9mIGZyZWVTcGFjZSB0aGF0IGEgc2luZ2xlIHNwYWNlciBzaG91bGQgdGFrZSB1cCAobmVlZCBudW1iZXJPZk9iamVjdHMgLSAxIHNwYWNlcnMpXG4qIEBwYXJhbSB7Ym9vbGVhbn0gb3ZlcmZsb3dRIGNhbiB3ZSBnbyBiZXlvbmQgYWxsb3RlZCBzcGFjZVxuKiBAcmV0dXJucyB7bnVtYmVyfSByZXR1cm5zIHNpemUgdGhhdCBzcGFjZXIgc2hvdWxkIGJlIGF0IGxldmVsPTBcbiovXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcihkYXRhLCBmcmVlU3BhY2UsIG9iamVjdFdpZHRoLCBudW1iZXJPZk9iamVjdHMsIGJhc2VTcGFjZXJTaXplLCBvdmVyZmxvd1EpIHtcbiAgaWYgKG92ZXJmbG93USkge1xuICAgIC8vIHZhciBsaW1pdGVkTnVtYmVyT2ZPYmplY3RzID0gbnVtYmVyT2ZPYmplY3RzID4gNiA/IDYgOiBudW1iZXJPZk9iamVjdHNcbiAgICAvLyB2YXIgc3BhY2VMZWZ0ID0gZnJlZVNwYWNlIC0gbGltaXRlZE51bWJlck9mT2JqZWN0cyAqIG9iamVjdFdpZHRoXG4gICAgLy8gcmV0dXJuIHNwYWNlTGVmdCAvIChsaW1pdGVkTnVtYmVyT2ZPYmplY3RzIC0gMSlcbiAgICByZXR1cm4gZnJlZVNwYWNlICogYmFzZVNwYWNlclNpemVcbiAgfVxuICB2YXIgc3BhY2Vyc0F0RWFjaExldmVsID0gc3BhY2Vyc05lZWRlZEF0RWFjaExldmVsKGRhdGEpXG4gIHZhciB0b3RhbFNwYWNlclBlcmNlbnQgPSB0b3RhbChzcGFjZXJzQXRFYWNoTGV2ZWwubWFwKGZ1bmN0aW9uKGUsIGkpIHtyZXR1cm4gZSAqIDEgLyAoaSsxKX0pKVxuICB2YXIgYmFzZVNwYWNlclNpemUgPSAoZnJlZVNwYWNlIC0gKG9iamVjdFdpZHRoICogbnVtYmVyT2ZPYmplY3RzKSkgLyB0b3RhbFNwYWNlclBlcmNlbnRcbiAgLy8gY29uc29sZS5sb2coZnJlZVNwYWNlLCBvYmplY3RXaWR0aCwgbnVtYmVyT2ZPYmplY3RzLCB0b3RhbFNwYWNlclBlcmNlbnQpXG4gIC8vIGNvbnNvbGUubG9nKHRvdGFsU3BhY2VyUGVyY2VudCwgYmFzZVNwYWNlclNpemUsIHRvdGFsU3BhY2VyUGVyY2VudCAqIGJhc2VTcGFjZXJTaXplKVxuICByZXR1cm4gaXNOYU4oYmFzZVNwYWNlclNpemUpID8gMCA6IGJhc2VTcGFjZXJTaXplXG59XG5cblxuLyoqXG4qIENhbGN1bGF0ZXMgbnVtYmVyIG9mIHNwYWNlcnMgbmVlZGVkIHRvIHNlcGVyYXRlIGVsZW1lbnRzIGF0IGVhY2ggbGV2ZWwuXG4qIEBwYXJhbSB7QXJyYXlbXX0gYXJyYXkgbGlzdCBkYXRhIChjYW4gYmUgbmVzdGVkKS4gSWYgbmVzdGVkIHdpbGwgY3JlYXRlIG1vcmUgY29tcGxleCBzcGFjZXIgc2l6ZVxuKiBAcGFyYW0ge251bWJlcn0gW2xldmVsPTBdIGN1cnJlbnQgbGV2ZWwsIHVzZWQgaW4gcmVjdXNyaW9uXG4qIEBwYXJhbSB7QXJyYXl9IFtsZXZlbERhdGE9W11dIGhvdyBtYW55IHNwYWNlcnMgbmVlZGVkIGF0IGEgZ2l2ZW4gbGV2ZWxcbiogQHJldHVybnMge0FycmF5fSBsZXZlbERhdGFcbipcbiogQGV4YW1wbGVcbiogYXJyYXkgPSBbWzEsMl0sIFszLDRdXVxuKiAvLyByZXR1cm5zIFsxLCAyXVxuKiBhcyBhdCBsZXZlbD0wIHRoZSBvbmx5IHNwYWNlciBuZWVkZWQgaXMgYmV0d2VlbiBbMSwyXSBhbmQgWzMsNF1cbiogYW5kIGF0IGxldmVsPTEgdGhlIG9ubHkgdHdvIHNwYWNlcnMgbmVlZGVkIGlzIGJldHdlZW4gMSBhbmQgMiBhcyB3ZWxsIGFzXG4qIDMgYW5kIDQgc2luY2UgdGhlIHNwYWNlciBiZXR3ZWVuIDIgYW5kIDMgaXMgaGFuZGxlZCBhdCBsZXZlbD0wXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIHNwYWNlcnNOZWVkZWRBdEVhY2hMZXZlbCAoYXJyYXksIGxldmVsLCBsZXZlbERhdGEgKSB7XG4gIGlmICggbGV2ZWwgPT0gdW5kZWZpbmVkICkgeyBsZXZlbCA9IDA7ICB9IGVsc2UgeyBsZXZlbCArPSAxIH1cbiAgaWYgKCBsZXZlbERhdGEgPT0gdW5kZWZpbmVkICkgeyBsZXZlbERhdGEgPSBbXTsgfVxuICBpZiAoIGxldmVsID49IGxldmVsRGF0YS5sZW5ndGggKSB7IGxldmVsRGF0YS5wdXNoKGFycmF5Lmxlbmd0aCAtIDEpIH1cbiAgZWxzZSB7IGxldmVsRGF0YVtsZXZlbF0gKz0gYXJyYXkubGVuZ3RoIC0gMSB9XG4gIGFycmF5Lm1hcChmdW5jdGlvbihlLCBpKSB7IGlmIChBcnJheS5pc0FycmF5KGUpKSB7IHNwYWNlcnNOZWVkZWRBdEVhY2hMZXZlbChlLCBsZXZlbCwgbGV2ZWxEYXRhKSB9IH0gKVxuICByZXR1cm4gbGV2ZWxEYXRhXG59XG5cblxuXG5cbi8qKlxuKiBEcmF3cyBhIHdoaXNrZXIgZm9yIEBzZWV7QGxpbmsgYm94d2hpc2tlcn1cbiogQHBhcmFtIHtib29sZWFufSBkaXIgZGlyZWN0aW9uIHRvIGRyYXcgd2hpc2tlciwgc2hvdWxkIGJlIGVpdGhlciB0cnVlICh1cCwgdG9wKSBvciBmYWxzZSAoZG93biBvciBib3R0b20pXG4qIEBwYXJhbSB7bnVtYmVyfSB4IHN0YXJ0aW5nIHggY29vcmRpbmF0ZSBpbiB3aGljaCB0byBkcmF3IHdoaXNrZXJcbiogQHBhcmFtIHtudW1iZXJ9IHkgc3RhcnRpbmcgeSBjb29yZGluYXRlIGluIHdoaWNoIHRvIGRyYXcgd2hpc2tlclxuKiBAcGFyYW0ge251bWJlcn0gdyB3aWR0aCBvZiBzcGFjZSBpbiB3aGljaCB0byBkcmF3IHdoaXNrZXJcbiogQHBhcmFtIHtudW1iZXJ9IGggaGVpZ2h0IG9mIHNwYWNlIGluIHdoaWNoIHRvIGRyYXcgd2hpc2tlclxuKiBAcGFyYW0ge251bWJlcn0gcGVyIHBlcmNlbnRhZ2Ugb2YgdyBvciBoIChkZXBlbmRzIG9uIG8pIHRvIG1ha2Ugd2hpc2tlclxuKiBAcGFyYW0ge2Jvb2xlYW59IG8gb3JpZW50YXRpb24sIHRydWUgaXMgaG9yaXpvbnRhbCBhbmQgZmFsc2UgaXMgdmVydGljYWxcbiogQHJldHVybnMge3N0cmluZ30gcmVwcmVzZW50aW5nIHRoZSBzdmcgcGF0aCAoaS5lLiB0aGUgZCBhdHRyaWJ1dGUgZm9yIGEgcGF0aCB0YWcpXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIHdoaXNrZXJQYXRoKGRpciwgeCwgeSwgdywgaCwgcGVyLCBvKSB7XG4gIC8vIGQgPSBkaXJlY3Rpb24gKHRydWUgaXMgdXApLCBwID0gcGVyY2VudCB3aWR0aFxuICBpZiAoZGlyID09ICd1cCcgfHwgZGlyID09ICd0b3AnIHx8IGRpciA9PSB0cnVlKSB7ZGlyID0gdHJ1ZX1cbiAgaWYgKGRpciA9PSAnZG93bicgfHwgZGlyID09ICdib3R0b20nIHx8IGRpciA9PSBmYWxzZSkge2RpciA9IGZhbHNlfVxuICBvID0gbyA9PSB1bmRlZmluZWQgPyAnaG9yaXpvbnRhbCcgOiBvXG4gIHBlciA9IHBlciA9PSB1bmRlZmluZWQgPyAxIDogcGVyXG4gIGlmIChvICE9IFwiaG9yaXpvbnRhbFwiKSB7XG4gICAgdmFyIGhoID0gaCAqIHBlciAsXG4gICAgdyA9IGRpciA/IHcgOiAtdyAsXG4gICAgYSA9IGRpciA/IHggKyB3IDogeCAsXG4gICAgYiA9IGRpciA/IHggOiB4ICsgdyAsXG4gICAgYyA9IGRpciA/IGEgOiBiXG4gICAgcCA9IFwiTSBcIiArIGEgKyAnICcgKyAoICAgICBoIC8gMiAgICAgICkgKyAnICdcbiAgICAgICsgJ0wgJyArIGIgKyAnICcgKyAoICAgICBoIC8gMiAgICAgICkgKyAnICdcbiAgICAgICsgJ00gJyArIGMgKyAnICcgKyAoIGggLyAyIC0gaGggLyAyICkgKyAnICdcbiAgICAgICsgJ0wgJyArIGMgKyAnICcgKyAoIGggLyAyICsgaGggLyAyICkgKyAnICdcblxuICAgIHJldHVybiBwXG4gIH1cbiAgdmFyIHd3ID0gdyAqIHBlcixcbiAgYSA9IGRpciA/IHkgKyBoIDogeSAgLFxuICBiID0gZGlyID8geSA6IHkgKyBoICAsXG4gIHAgPSBcIk0gXCIgKyAoICB3IC8gMiAgKSArICcgJyArIGEgKyAnICcgLy8gc3RyYWlnaHQgbGluZSBwYXJ0XG4gICAgKyAnTCAnICsgKCAgdyAvIDIgICkgKyAnICcgKyBiICsgJyAnIC8vIHN0cmFpZ2h0IGxpbmUgcGFydFxuICAgICsgJ2ggJyArICggLXd3IC8gMiApICsgJyAnICsgMCArICcgJyAvLyBob3Jpem9udGFsIGxpbmUgcGFydFxuICAgICsgJ2ggJyArICggICAgd3cgICApICsgJyAnICsgMCArICcgJ1xuICByZXR1cm4gcFxufVxuXG5cblxuXG5cblxuXG5cblxuXG5cblxuXG5leHBvcnQgZnVuY3Rpb24gcmVzaXplRGVib3VuY2UoZiwgd2FpdCkge1xuICB2YXIgcmVzaXplID0gZGVib3VuY2UoZnVuY3Rpb24oKXtmKCl9LHdhaXQpXG4gIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCByZXNpemUpXG59XG5cblxuXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBpbW1lZGlhdGUpIHtcbiAgdmFyIHRpbWVvdXQ7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgY29udGV4dCA9IHRoaXMsIGFyZ3MgPSBhcmd1bWVudHM7XG4gICAgICAgIHZhciBsYXRlciA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgdGltZW91dCA9IG51bGw7XG4gICAgICAgICAgICBpZiAoIWltbWVkaWF0ZSkgZnVuYy5hcHBseShjb250ZXh0LCBhcmdzKTtcbiAgICAgICAgfTtcbiAgICAgICAgdmFyIGNhbGxOb3cgPSBpbW1lZGlhdGUgJiYgIXRpbWVvdXQ7XG4gICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0KTtcbiAgICAgICAgdGltZW91dCA9IHNldFRpbWVvdXQobGF0ZXIsIHdhaXQpO1xuICAgICAgICBpZiAoY2FsbE5vdykgZnVuYy5hcHBseShjb250ZXh0LCBhcmdzKTtcbiAgICB9O1xufVxuIiwiaW1wb3J0IHtsb2csIHdhcm4sIGVycm9yLCBpbmZvfSBmcm9tICcuL3V0aWxzJztcbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU1BBQ0VHUk9VUElORyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKipcbiAqIFByb2R1Y2VzIGEgZnVuY3Rpb24gZm9yIHNwYWNpbmcgb2JqZWN0cyBieSBhbiBhcmJpdHJhcmx5IGNvbXBsZXggZ3JvdXBpbmdcbiAqIEByZXR1cm5zIHtyZWN1cnNpdmVseVBvc2l0aW9ufSB0aGUgZnVuY3Rpb24gZm9yIG1vdmluZyB0aGUgb2JqZWN0c1xuICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIjcmVjdXJzaXZlbHlQb3NpdGlvbn0pXG4gKiBAbmFtZXNwYWNlIGdyb3VwaW5nU3BhY2VyXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBncm91cGluZ1NwYWNlcigpIHtcbiAgdmFyXG4gIC8qQHZhciB7Ym9vbGVhbn0gaG9yaXpvbnRhbFEgQGRlZmF1bHQqL1xuXG4gIC8qKlxuICAqIFdoZXRoZXIgb3Igbm90IHRvIHNwYWNlIG9iamVjdHMgaG9yaXpvbnRhbGx5IG9yIHZlcnRpY2FsbHkuXG4gICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIuaG9yaXpvbnRhbFF9KVxuICAqIEBwYXJhbSB7Ym9vbGVhbn0gW2hvcml6b250YWxRPXRydWVdXG4gICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyI1xuICAqIEBpbnN0YW5jZVxuICAqL1xuICBob3Jpem9udGFsUSA9IHRydWUsXG4gIC8qKlxuICAqIFRoZSBzY2FsZSB0byB1c2UgdG8gcG9zaXRpb24gZWxlbWVudHMgaWYge0BsaW5rIGdyb3VwaW5nU3BhY2VyI21vdmVieX09XCJzdHJpbmdcIlxuICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyLnNjYWxlfSlcbiAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbc2NhbGU9ZDMuc2NhbGVMaW5lYXIoKV1cbiAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICogQGluc3RhbmNlXG4gICovXG4gIHNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKSxcbiAgLyoqXG4gICogSG93IGVsZW1lbnRzIGluIHRoZSBjb21wbGV4IGdyb3VwaW5nIHNob3VsZCBiZSBtb3ZlZCBvdmVyIGJ5LlxuICAqIEJ5IGRlZmF1bHQsIG1vdmVieT1cImNhdGVnb3J5XCIsIHdoaWNoIG1vdmVzIG9iamVjdHMgYnkgdGhlIGNvbXBsZXggZ3JvdXBpbmdcbiAgKiBCdXQgb2JqZWN0cyBjYW4gYWxzbyBiZSBtb3ZlZCBvdmVyIGJ5IHNjYWxlLlxuICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyLm1vdmVieX0pXG4gICogQHBhcmFtIHtzdHJpbmd9IFttb3ZlYnk9XCJjYXRlZ29yeVwiXVxuICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlciNcbiAgKiBAaW5zdGFuY2VcbiAgKi9cbiAgbW92ZWJ5ID0gJ2NhdGVnb3J5JyxcbiAgLyoqXG4gICogSG93IG1hbnkgb2JqZWN0cyBhcmUgdGhlcmUgaW4gdG90YWxcbiAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlci5udW1iZXJPZk9iamVjdHN9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbnVtYmVyT2ZPYmplY3RzPW5vbmVdXG4gICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyI1xuICAqIEBpbnN0YW5jZVxuICAqL1xuICBudW1iZXJPZk9iamVjdHMsXG4gIC8qKlxuICAqIFRoZSBjbGFzcyBnaXZlbiB0byBhbiBuZXN0ZWQgPGc+IHRhZyB3aG9zZSBwYXJlbnQocykgaGF2ZSB0aGUgY29ycmVjdCB0cmFuc2l0aW9uXG4gICogcHJvcGVydGllc1xuICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyLm51bWJlck9mT2JqZWN0c30pXG4gICogQHBhcmFtIHtzdHJpbmd9IFtudW1iZXJPZk9iamVjdHM9J2Qzc20tZ3JvdXBwZWQtaXRlbSddXG4gICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyI1xuICAqIEBpbnN0YW5jZVxuICAqL1xuICBvYmplY3RDbGFzcyA9ICdkM3NtLWdyb3VwcGVkLWl0ZW0nLFxuICAvKipcbiAgKiBUaGUgc2l6ZSBvZiB0aGUgb2JqZWN0cyBiZWluZyBwb3NpdGlvbmVkXG4gICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIub2JqZWN0U2l6ZX0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvYmplY3RTaXplPW5vbmVdXG4gICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyI1xuICAqIEBpbnN0YW5jZVxuICAqL1xuICBvYmplY3RTaXplLFxuICAvKipcbiAgKiBUaGUgc2l6ZSBvZiB0aGUgdW4tbmVzdGVkIHNwYWNlciBiZXR3ZWVuIG9iamVjdHNcbiAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlci5zcGFjZXJTaXplfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlclNpemU9bm9uZV1cbiAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICogQGluc3RhbmNlXG4gICovXG4gIHNwYWNlclNpemUsXG4gIC8qKlxuICAqIFRoZSBkdXJhdGlvbiBvZiB0cmFuc2l0aW9ucyBpbiBtc1xuICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyLnRyYW5zaXRpb25EdXJhdGlvbn0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFt0cmFuc2l0aW9uRHVyYXRpb249MTAwMF1cbiAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICogQGluc3RhbmNlXG4gICovXG4gIHRyYW5zaXRpb25EdXJhdGlvbiA9IDEwMDAsXG4gIC8qKlxuICAqIFRoZSBlYXNlIGZ1bmN0aW9uIGZvciB0aGUgdHJhbnNpdGlvbnNcbiAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlci5lYXNlRnVuY30pXG4gICogQHBhcmFtIHtkMy5lYXNlfSBbZWFzZUZ1bmM9ZDMuZWFzZVNpbl1cbiAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICogQGluc3RhbmNlXG4gICovXG4gIGVhc2VGdW5jID0gZDMuZWFzZVNpbixcbiAgLyoqXG4gICogVGhlIG5hbWVzcGFjZSBmb3IgdGhlIG9iamVjdHMgYmVpbmcgbW92ZWRcbiAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlci5uYW1lc3BhY2V9KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbbmFtZXNwYWNlPSdzcGFjZXInXVxuICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlciNcbiAgKiBAaW5zdGFuY2VcbiAgKi9cbiAgbmFtZXNwYWNlID0gJ3NwYWNlcicsXG4gIC8qKlxuICAqIFRoZSBhbmltYXRpb24gZm9yIG5ldyBvYmplY3RzIGJlaW5nIGFkZGVkXG4gICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIuZW50ZXJGdW5jdGlvbn0pXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gZW50ZXJGdW5jdGlvblxuICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlciNcbiAgKiBAaW5zdGFuY2VcbiAgKiBAZXhhbXBsZVxuICAqIC8vIGJ5IGRlZmF1bHRcbiAgKiBmdW5jdGlvbihuZXdPYmplY3RTZWxlY3Rpb24pIHtcbiAgKiAgbmV3T2JqZWN0U2VsZWN0aW9uLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpe1xuICAqICAgIHZhclxuICAqICAgIHggPSBob3Jpem9udGFsUSA/IG9iamVjdFNpemUgKiBudW1iZXJPZk9iamVjdHMgKyBzcGFjZXJTaXplICogKG51bWJlck9mT2JqZWN0cyAtIDEpIDogMCxcbiAgKiAgICB5ID0gIWhvcml6b250YWxRID8gb2JqZWN0U2l6ZSAqIG51bWJlck9mT2JqZWN0cyArIHNwYWNlclNpemUgKiAobnVtYmVyT2ZPYmplY3RzIC0gMSkgOiAwLFxuICAqICAgIHQgPSAndHJhbnNsYXRlKCcreCsnLCcreSsnKSdcbiAgKiAgICByZXR1cm4gdFxuICAqICB9KVxuICAqIH1cbiAgKi9cbiAgZW50ZXJGdW5jdGlvbiA9IGZ1bmN0aW9uKGN1cikge1xuICAgIGN1ci5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhclxuICAgICAgLy8geCA9IGhvcml6b250YWxRID8gb2JqZWN0U2l6ZSAqIG51bWJlck9mT2JqZWN0cyArIHNwYWNlclNpemUgKiAobnVtYmVyT2ZPYmplY3RzIC0gMSkgOiAwLFxuICAgICAgLy8geSA9ICFob3Jpem9udGFsUSA/IG9iamVjdFNpemUgKiBudW1iZXJPZk9iamVjdHMgKyBzcGFjZXJTaXplICogKG51bWJlck9mT2JqZWN0cyAtIDEpIDogMCxcbiAgICAgIHggPSBob3Jpem9udGFsUSA/IHdpbmRvdy5vdXRlcldpZHRoIDogMCxcbiAgICAgIHkgPSAhaG9yaXpvbnRhbFEgPyB3aW5kb3cub3V0ZXJXaWR0aCA6IDAsXG4gICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAvLyBpZih5ID09IHVuZGVmaW5lZCkge2NvbnNvbGUubG9nKGN1ci5ub2RlKCksIHksIGQpfVxuICAgICAgcmV0dXJuIHRcbiAgICB9KVxuICB9LFxuICAvKipcbiAgKiBUaGUgYW5pbWF0aW9uIGZvciBvbGQgb2JqZWN0cyBiZWluZyByZW1vdmVkXG4gICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIuZXhpdEZ1bmN0aW9ufSlcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBleGl0RnVuY3Rpb25cbiAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICogQGluc3RhbmNlXG4gICogQGV4YW1wbGVcbiAgKiAvLyBieSBkZWZhdWx0XG4gICogb2xkT2JqZWN0U2VsZWN0aW9uLnRyYW5zaXRpb24oKS5kdXJhdGlvbih0cmFuc2l0aW9uRHVyYXRpb24pLmVhc2UoZWFzZUZ1bmMpXG4gICogLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpe1xuICAqICAgICB2YXJcbiAgKiAgIHggPSBob3Jpem9udGFsUSA/IG9iamVjdFNpemUgKiBudW1iZXJPZk9iamVjdHMgKyBzcGFjZXJTaXplICogKG51bWJlck9mT2JqZWN0cyAtIDEpIDogMCxcbiAgKiAgIHkgPSAhaG9yaXpvbnRhbFEgPyBvYmplY3RTaXplICogbnVtYmVyT2ZPYmplY3RzICsgc3BhY2VyU2l6ZSAqIChudW1iZXJPZk9iamVjdHMgLSAxKSA6IDAsXG4gICogICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICogICByZXR1cm4gdFxuICAqIH0pLnJlbW92ZSgpXG4gICovXG4gIGV4aXRGdW5jdGlvbiA9IGZ1bmN0aW9uKGN1cil7XG4gICAgbG9nKFwiZ3JvdXBpbmdTcGFjZXJcIiwgXCJleGl0aW5nIHdpdGhcIiwge2N1cnJlbnQ6IGN1ciwgY3VycmVudE5vZGU6IGN1ci5ub2RlKCl9KVxuICAgIGN1ci5zZWxlY3RBbGwoJ2cnKS5jbGFzc2VkKCd0by1yZW1vdmUnLCB0cnVlKVxuXG4gICAgY3VyLnRyYW5zaXRpb24oKS5kdXJhdGlvbih0cmFuc2l0aW9uRHVyYXRpb24qMC45KS5lYXNlKGVhc2VGdW5jKVxuICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhclxuICAgICAgLy8geCA9IGhvcml6b250YWxRID8gb2JqZWN0U2l6ZSAqIG51bWJlck9mT2JqZWN0cyArIHNwYWNlclNpemUgKiAobnVtYmVyT2ZPYmplY3RzIC0gMSkgOiAwLFxuICAgICAgLy8geSA9ICFob3Jpem9udGFsUSA/IG9iamVjdFNpemUgKiBudW1iZXJPZk9iamVjdHMgKyBzcGFjZXJTaXplICogKG51bWJlck9mT2JqZWN0cyAtIDEpIDogMCxcbiAgICAgIHggPSBob3Jpem9udGFsUSA/IHdpbmRvdy5vdXRlcldpZHRoIDogMCxcbiAgICAgIHkgPSAhaG9yaXpvbnRhbFEgPyB3aW5kb3cub3V0ZXJXaWR0aCA6IDAsXG4gICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAvLyBpZih5ID09IHVuZGVmaW5lZCkge2NvbnNvbGUubG9nKGN1ci5ub2RlKCksIHksIGQpfVxuICAgICAgcmV0dXJuIHRcbiAgICB9KS5yZW1vdmUoKVxuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIGhvcml6b250YWxRICh3aGV0aGVyIG9yIG5vdCB0byBzcGFjZSBvYmplY3RzIGhvcml6b250YWxseSBvciB2ZXJ0aWNhbGx5KS5cbiAgICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIjaG9yaXpvbnRhbFF9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXJcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcmVjdXJzaXZlbHlQb3NpdGlvbi5ob3Jpem9udGFsUSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoaG9yaXpvbnRhbFEgPSBfLCByZWN1cnNpdmVseVBvc2l0aW9uKSA6IGhvcml6b250YWxRIH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzY2FsZSB0byB1c2UgdG8gcG9zaXRpb24gZWxlbWVudHMgaWYge0BsaW5rIGdyb3VwaW5nU3BhY2VyI21vdmVieX09XCJzdHJpbmdcIlxuICAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlciNzY2FsZX0pXG4gICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtncm91cGluZ1NwYWNlciB8IGQzLnNjYWxlfVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXJcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcmVjdXJzaXZlbHlQb3NpdGlvbi5zY2FsZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGUgPSBfLCByZWN1cnNpdmVseVBvc2l0aW9uKSA6IHNjYWxlIH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIG1vdmVieSAod2hldGhlciBvciBub3QgdG8gbW92ZSBieSBzY2FsZSBvciBieSBncm91cGluZykuXG4gICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyI21vdmVieX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Z3JvdXBpbmdTcGFjZXIgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlclxuICAgKiBAc3RhdGljXG4gICAqL1xuICByZWN1cnNpdmVseVBvc2l0aW9uLm1vdmVieSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobW92ZWJ5ID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiBtb3ZlYnkgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgbnVtYmVyT2ZPYmplY3RzLlxuICAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlciNudW1iZXJPZk9iamVjdHN9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXJcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcmVjdXJzaXZlbHlQb3NpdGlvbi5udW1iZXJPZk9iamVjdHMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG51bWJlck9mT2JqZWN0cyA9IF8sIHJlY3Vyc2l2ZWx5UG9zaXRpb24pIDogbnVtYmVyT2ZPYmplY3RzIH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RDbGFzcyAod2lsbCBiZSBhcHBsaWVkIHRvIDxnPiBlbGVtZW50cykuXG4gICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyI29iamVjdENsYXNzfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtncm91cGluZ1NwYWNlciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyXG4gICAqIEBzdGF0aWNcbiAgICovXG4gIHJlY3Vyc2l2ZWx5UG9zaXRpb24ub2JqZWN0Q2xhc3MgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdENsYXNzID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiBvYmplY3RDbGFzcyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0U2l6ZS5cbiAgICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIjb2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Z3JvdXBpbmdTcGFjZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlclxuICAgKiBAc3RhdGljXG4gICAqL1xuICByZWN1cnNpdmVseVBvc2l0aW9uLm9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNpemUgPSBfLCByZWN1cnNpdmVseVBvc2l0aW9uKSA6IG9iamVjdFNpemUgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHNwYWNlclNpemUuXG4gICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyI3NwYWNlclNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXJcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcmVjdXJzaXZlbHlQb3NpdGlvbi5zcGFjZXJTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZXJTaXplID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiBzcGFjZXJTaXplIH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0cmFuc2l0aW9uRHVyYXRpb24uXG4gICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyI3RyYW5zaXRpb25EdXJhdGlvbn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Z3JvdXBpbmdTcGFjZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlclxuICAgKiBAc3RhdGljXG4gICAqL1xuICByZWN1cnNpdmVseVBvc2l0aW9uLnRyYW5zaXRpb25EdXJhdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodHJhbnNpdGlvbkR1cmF0aW9uID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiB0cmFuc2l0aW9uRHVyYXRpb24gfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGVhc2VGdW5jLlxuICAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlciNlYXNlRnVuY30pXG4gICAqIEBwYXJhbSB7ZDMuZWFzZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgZDMuZWFzZX1cbiAgICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyXG4gICAqIEBzdGF0aWNcbiAgICovXG4gIHJlY3Vyc2l2ZWx5UG9zaXRpb24uZWFzZUZ1bmMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGVhc2VGdW5jID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiBlYXNlRnVuYyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbmFtZXNwYWNlLlxuICAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlciNuYW1lc3BhY2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXJcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcmVjdXJzaXZlbHlQb3NpdGlvbi5uYW1lc3BhY2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG5hbWVzcGFjZSA9IF8sIHJlY3Vyc2l2ZWx5UG9zaXRpb24pIDogbmFtZXNwYWNlIH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBlbnRlckZ1bmN0aW9uLlxuICAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlciNlbnRlckZ1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlclxuICAgKiBAc3RhdGljXG4gICAqL1xuICByZWN1cnNpdmVseVBvc2l0aW9uLmVudGVyRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGVudGVyRnVuY3Rpb24gPSBfLCByZWN1cnNpdmVseVBvc2l0aW9uKSA6IGVudGVyRnVuY3Rpb24gfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGV4aXRGdW5jdGlvbi5cbiAgICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIjZXhpdEZ1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlclxuICAgKiBAc3RhdGljXG4gICAqL1xuICByZWN1cnNpdmVseVBvc2l0aW9uLmV4aXRGdW5jdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZXhpdEZ1bmN0aW9uID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiBleGl0RnVuY3Rpb24gfVxuXG5cbiAgLyoqXG4gICAqIHJlY3Vyc2l2ZWx5IHBvc2l0aW9uIHRoZSBvYmplY3RzIGluc2lkZSBvZiB0aGUgc2VsZWN0aW9uLlxuICAgKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gc2VsZWN0aW9uXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhXG4gICAqIEBwYXJhbSB7bGV2ZWx9IFtsZXZlbD0wXSByZWN1cnNpb24gZGVwdGhcbiAgICogQHJldHVybnMge251bWJlcn0gKGhvdyBtdWNoIHRvIG1vdmUgbmV4dCBlbGVtZW50KVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICAqL1xuICBmdW5jdGlvbiByZWN1cnNpdmVseVBvc2l0aW9uKHNlbGVjdGlvbiwgZGF0YSwgbGV2ZWwpIHtcbiAgICBpZiAoIGxldmVsID09IHVuZGVmaW5lZCApIHsgbGV2ZWwgPSAwOyAgfVxuXG4gICAgdmFyIGN1cnJlbnRTZWxlY3Rpb24gPSBzZWxlY3Rpb24uc2VsZWN0QWxsKCdnLicrbmFtZXNwYWNlKydbbGV2ZWw9XCInK2xldmVsKydcIl0nKS5kYXRhKGRhdGEpXG4gICAgdmFyIGVudGVyID0gY3VycmVudFNlbGVjdGlvbi5lbnRlcigpLmFwcGVuZCgnZycpLmF0dHIoJ2xldmVsJywgbGV2ZWwpLmF0dHIoJ2NsYXNzJywgbmFtZXNwYWNlKVxuICAgIHZhciBleGl0ID0gY3VycmVudFNlbGVjdGlvbi5leGl0KClcbiAgICBjdXJyZW50U2VsZWN0aW9uID0gY3VycmVudFNlbGVjdGlvbi5tZXJnZShlbnRlcilcblxuXG4gICAgaWYgKHR5cGVvZiBleGl0RnVuY3Rpb24gPT0gJ2Z1bmN0aW9uJyApeyBleGl0LmVhY2goZnVuY3Rpb24oZCwgaSl7IGV4aXRGdW5jdGlvbihkMy5zZWxlY3QodGhpcykpfSkgfVxuICAgIGVsc2V7ZXhpdC5yZW1vdmUoKX1cbiAgICAvLyBzcGFjZXIgZm9yIGN1cnJlbnQgbGV2ZWxcbiAgICB2YXIgbGV2ZWxTcGFjZXIgPSBzcGFjZXJTaXplIC8gKGxldmVsKzEpXG4gICAgLy8gbW92ZW1lbnQgZm9yIGN1cnJlbnQgbGV2ZWxcbiAgICB2YXIgbW92ZSA9IDBcbiAgICBjdXJyZW50U2VsZWN0aW9uLmVhY2goZnVuY3Rpb24oY3VycmVudEVsZW1lbnQsIGluZGV4KSB7XG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgICAgaWYgKHQuYXR0cigndHJhbnNmb3JtJykgPT0gdW5kZWZpbmVkICYmIHR5cGVvZiBlbnRlckZ1bmN0aW9uID09ICdmdW5jdGlvbicpIHsgZW50ZXJGdW5jdGlvbih0KSB9XG5cbiAgICAgIHQudHJhbnNpdGlvbigpLmR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbikuZWFzZShlYXNlRnVuYylcbiAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKSB7XG4gICAgICAgIHZhclxuICAgICAgICB4ID0gaG9yaXpvbnRhbFEgPyAobW92ZWJ5ID09XCJzY2FsZVwiID8gc2NhbGUoZCkgOiBtb3ZlKSA6IDAsXG4gICAgICAgIHkgPSAhaG9yaXpvbnRhbFEgPyAobW92ZWJ5ID09XCJzY2FsZVwiID8gc2NhbGUoZCkgOiBtb3ZlKTogMCxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcblxuICAgICAgaWYgKEFycmF5LmlzQXJyYXkoY3VycmVudEVsZW1lbnQpKSB7XG4gICAgICAgIG1vdmUgKz0gcmVjdXJzaXZlbHlQb3NpdGlvbih0LCBjdXJyZW50RWxlbWVudCwgbGV2ZWwrMSlcbiAgICAgICAgdmFyIHRvUmVtb3ZlID0gdC5zZWxlY3RBbGwoJ2cuJytuYW1lc3BhY2UrJ1tsZXZlbD1cIicrKGxldmVsKSsnXCJdID4gZy4nK29iamVjdENsYXNzKycuJytuYW1lc3BhY2UpXG4gICAgICAgIGlmICh0eXBlb2YgZXhpdEZ1bmN0aW9uID09ICdmdW5jdGlvbicgKXsgdG9SZW1vdmUuZWFjaChmdW5jdGlvbihkLCBpKXsgZXhpdEZ1bmN0aW9uKGQzLnNlbGVjdCh0aGlzKSl9KSB9XG4gICAgICAgIGVsc2V7dG9SZW1vdmUucmVtb3ZlKCl9XG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgbW92ZSArPSBvYmplY3RTaXplXG4gICAgICAgIHZhciBvYmogPSB0LnNlbGVjdCgnZy4nK25hbWVzcGFjZSsnW2xldmVsPVwiJytsZXZlbCsnXCJdID4gZy4nK29iamVjdENsYXNzKycuJytuYW1lc3BhY2UpXG4gICAgICAgIGlmIChvYmouZW1wdHkoKSkgeyBvYmogPSB0LmFwcGVuZCgnZycpLmF0dHIoJ2NsYXNzJywgb2JqZWN0Q2xhc3MpLmNsYXNzZWQobmFtZXNwYWNlLCB0cnVlKSB9XG4gICAgICAgIG9iai5hdHRyKCdwYXJlbnQtaW5kZXgnLCBpbmRleClcbiAgICAgICAgdmFyIHRvUmVtb3ZlID0gdC5zZWxlY3RBbGwoJ2cuJytuYW1lc3BhY2UrJ1tsZXZlbD1cIicrKGxldmVsKzEpKydcIl0nKVxuXG4gICAgICAgIGlmICh0eXBlb2YgZXhpdEZ1bmN0aW9uID09ICdmdW5jdGlvbicgKXsgdG9SZW1vdmUuZWFjaChmdW5jdGlvbihkLCBpKXsgZXhpdEZ1bmN0aW9uKGQzLnNlbGVjdCh0aGlzKSl9KSB9XG4gICAgICAgIGVsc2V7dG9SZW1vdmUucmVtb3ZlKCl9XG4gICAgICB9XG4gICAgICBtb3ZlICs9IChpbmRleCA9PSBjdXJyZW50U2VsZWN0aW9uLnNpemUoKS0xKSA/IDAgOiBsZXZlbFNwYWNlclxuICAgIH0pXG4gICAgcmV0dXJuIG1vdmVcbiAgfVxuICByZXR1cm4gcmVjdXJzaXZlbHlQb3NpdGlvblxufVxuIiwiaW1wb3J0IHttb2RpZnlIZXhpZGVjaW1hbENvbG9yTHVtaW5hbmNlfSBmcm9tICcuL2hlbHBlcnMnO1xuXG4vKipcbiAqIENyZWF0ZXMgYSBjb2xvckZ1bmN0aW9uXG4gKiBAY29uc3RydWN0b3IgY29sb3JGdW5jdGlvblxuICogQG5hbWVzcGFjZSBjb2xvckZ1bmN0aW9uXG4gKiBAcmV0dXJucyB7ZnVuY3Rpb259IGNvbG9yRnVuY3Rpb25cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbG9yRnVuY3Rpb24oKSB7XG4gIHZhclxuICBkYXRhLFxuXG4gIC8qKlxuICAqIERlZmF1bHQgY29sb3JzIHRvIHVzZVxuICAqIEBwYXJhbSB7bnVtYmVyW119IFtjb2xvcnM9W1wiIzJjN2JiNlwiLCBcIiMwMGE2Y2FcIiwgXCIjMDBjY2JjXCIsIFwiIzkwZWI5ZFwiLCBcIiNmZmZmOGNcIiwgXCIjZjlkMDU3XCIsIFwiI2YyOWUyZVwiLCBcIiNlNzY4MThcIiwgXCIjZDcxOTFjXCJdXVxuICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjb2xvcnMgPSBbXCIjMmM3YmI2XCIsIFwiIzAwYTZjYVwiLCBcIiMwMGNjYmNcIiwgXCIjOTBlYjlkXCIsIFwiI2ZmZmY4Y1wiLCBcIiNmOWQwNTdcIiwgXCIjZjI5ZTJlXCIsIFwiI2U3NjgxOFwiLCBcIiNkNzE5MWNcIl0sXG4gIC8qKlxuICAqIEludGVycG9sYXRvciBmb3IgY29sb3JzXG4gICogQHBhcmFtIHtkMy5pbnRlcnBvbGF0aW9ufSBbaW50ZXJwb2xhdGlvbj1kMy5pbnRlcnBvbGF0ZVJnYl1cbiAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgaW50ZXJwb2xhdGlvbiA9IGQzLmludGVycG9sYXRlUmdiLFxuICAvKipcbiAgKiBGdW5jdGlvbiBmb3IgbW9kaWZ5aW5nIGNvbG9yIGx1bWluYW5jZVxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFttb2RpZnlPcGFjaXR5PW1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2VdXG4gICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb24jXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1vZGlmeU9wYWNpdHkgPSBtb2RpZnlIZXhpZGVjaW1hbENvbG9yTHVtaW5hbmNlLFxuICAvKipcbiAgKiBIb3cgdG8gbW9kaWZ5IGNvbG9yIGZvciBzdHJva2VcbiAgKiBAcGFyYW0ge251bWJlcn0gW3N0cm9rZU9wYWNpdHk9MF1cbiAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3Ryb2tlT3BhY2l0eSA9IDAsXG4gIC8qKlxuICAqIEhvdyB0byBtb2RpZnkgY29sb3IgZm9yIGZpbGxcbiAgKiBAcGFyYW0ge251bWJlcn0gW2ZpbGxPcGFjaXR5PTAuNF1cbiAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZmlsbE9wYWNpdHkgPSAwLjQsXG4gIC8qKlxuICAqIEhvdyB0byBkZXRlcm1pbmUgdGhlIGNvbG9yIHRvIHVzZVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbY29sb3JCeT0naW5kZXgnXVxuICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjb2xvckJ5ID0gJ2luZGV4JyxcbiAgLyoqXG4gICogU2V0cyB0aGUgc2NhbGUgZm9yIGludGVycG9sYXRpbmcgdGhlIGNvbG9yc1xuICAqIEBwYXJhbSB7bnVtYmVyW119IFtkYXRhRXh0ZW50PVswLCBjb2xvcnMubGVuZ3RoIC0gMV1dXG4gICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb24jXG4gICogQHByb3BlcnR5XG4gICovXG4gIGRhdGFFeHRlbnQgPSBbMCwgY29sb3JzLmxlbmd0aCAtIDFdLFxuICAvKipcbiAgKiBFeHRyYWN0cyB0aGUgdmFsdWUgdG8gY29sb3IgYnlcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdmFsdWVFeHRyYWN0b3I9ZnVuY3Rpb24oaywgdiwgaSkge3JldHVybiB2fV1cbiAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihrLCB2LCBpKSB7cmV0dXJuIHZ9LFxuXG4gIC8qKlxuICAqIEV4dHJhY3RzIHRoZSBjYXRlZ29yeSB0byBjb2xvciBieVxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtjYXRlZ29yeUV4dHJhY3Rvcj1mdW5jdGlvbihrLCB2LCBpKSB7cmV0dXJuIHYuY2F0ZWdvcnl9XVxuICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjYXRlZ29yeUV4dHJhY3RvciA9IGZ1bmN0aW9uKGssIHYsIGkpIHtyZXR1cm4gdi5jYXRlZ29yeX0sXG5cbiAgLyoqXG4gICogVGhlIGRpZmZlcmVudCB0eXBlIG9mIGNhdGVnb3JpZXMgb2Ygd2hpY2ggdG8gY29sb3IgYnlcbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbY2F0ZWdvcmllcz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb24jXG4gICogQHByb3BlcnR5XG4gICovXG4gIGNhdGVnb3JpZXMsXG5cbiAgLyoqXG4gICogU2NhbGUgZm9yIGludGVycG9sYXRpbmcgdGhlIGNvbG9yc1xuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZT1kMy5zY2FsZUxpbmVhcigpXVxuICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcbiAgLmludGVycG9sYXRlKGludGVycG9sYXRpb24pLmRvbWFpbihkYXRhRXh0ZW50KS5yYW5nZShjb2xvcnMpLFxuICBoZWxwZXJTY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcblxuXG5cbiAgLy8gdmFyIGggPSB4ID0+ICcjJyArIHgubWF0Y2goL1xcZCsvZykubWFwKHkgPSB6ID0+ICgoK3ogPCAxNik/JzAnOicnKSArICgreikudG9TdHJpbmcoMTYpKS5qb2luKCcnKTtcbiAgdmFyIGggPSBmdW5jdGlvbih4KSB7XG4gICAgcmV0dXJuIFwiI1wiICsgeC5tYXRjaCgvXFxkKy9nKS5tYXAoXG4gICAgICBmdW5jdGlvbih5LCBpKSB7XG4gICAgICAgIHJldHVybiAgKCgreSA8IDE2KT8nMCc6JycpICsgKCt5KS50b1N0cmluZygxNilcbiAgICAgIH0pLmpvaW4oJycpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZGVmYXVsdCBjb2xvcnNcbiAgICogKHNlZSB7QGxpbmsgY29sb3JGdW5jdGlvbiNjb2xvcnN9KVxuICAgKiBAcGFyYW0ge251bWJlcltdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y29sb3JGdW5jdGlvbiB8IG51bWJlcltdfVxuICAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGNvbG9yRnVuY3Rpb24uY29sb3JzID0gZnVuY3Rpb24oXykge1xuICAgIHJldHVybiBhcmd1bWVudHMubGVuZ3RoXG4gICAgP1xuICAgICAgKFxuICAgICAgICBjb2xvcnMgPSBfLFxuICAgICAgICBzY2FsZS5yYW5nZShjb2xvcnMpLFxuICAgICAgICBjb2xvckZ1bmN0aW9uXG4gICAgICApXG4gICAgOiBjb2xvcnM7XG4gIH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGZ1bmN0aW9uIGZvciBpbnRlcnBvbGF0aW5nIHRoZSBjb2xvcnNcbiAgICogKHNlZSB7QGxpbmsgY29sb3JGdW5jdGlvbiNpbnRlcnBvbGF0aW9ufSlcbiAgICogQHBhcmFtIHtkMy5pbnRlcnBvbGF0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y29sb3JGdW5jdGlvbiB8IGQzLmludGVycG9sYXRpb259XG4gICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgY29sb3JGdW5jdGlvbi5pbnRlcnBvbGF0aW9uID0gZnVuY3Rpb24oXykge1xuICAgIHJldHVybiBhcmd1bWVudHMubGVuZ3RoXG4gICAgP1xuICAgIChcbiAgICAgIGludGVycG9sYXRpb24gPSBfLFxuICAgICAgc2NhbGUuaW50ZXJwb2xhdGUoaW50ZXJwb2xhdGlvbikucmFuZ2UoY29sb3JzKSxcbiAgICAgIGNvbG9yRnVuY3Rpb25cbiAgICApXG4gICAgOiBpbnRlcnBvbGF0aW9uO1xuICB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB2YWx1ZXMgZm9yIHRoZSBzY2FsZSB3aGljaCB0cmFuc2Zvcm1zIHRoZSB2YWx1ZSB0byBhIGNvbG9yXG4gICAqIChzZWUge0BsaW5rIGNvbG9yRnVuY3Rpb24jZGF0YUV4dGVudH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtjb2xvckZ1bmN0aW9uIHwgbnVtYmVyW119XG4gICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50ID0gZnVuY3Rpb24oXykge1xuICAgIHJldHVybiBhcmd1bWVudHMubGVuZ3RoXG4gICAgPyAoXG4gICAgICAgIGRhdGFFeHRlbnQgPSBfLFxuICAgICAgICBzY2FsZS5kb21haW4oZGF0YUV4dGVudCkuaW50ZXJwb2xhdGUoc2NhbGUuaW50ZXJwb2xhdGUoKSksXG4gICAgICAgIGNvbG9yRnVuY3Rpb25cbiAgICAgIClcbiAgICA6IGRhdGFFeHRlbnQ7XG4gIH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZ0aGUgc2NhbGUgd2hpY2ggdHJhbnNmb3JtcyB0aGUgdmFsdWUgdG8gYSBjb2xvclxuICAgKiAoc2VlIHtAbGluayBjb2xvckZ1bmN0aW9uI3NjYWxlfSlcbiAgICogQHBhcmFtIHtkMy5zY2FsZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2NvbG9yRnVuY3Rpb24gfCBkMy5zY2FsZX1cbiAgICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb25cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICBjb2xvckZ1bmN0aW9uLnNjYWxlID0gZnVuY3Rpb24oXykge1xuICAgIHJldHVybiBhcmd1bWVudHMubGVuZ3RoXG4gICAgPyAoXG4gICAgICAgIF8gPSBfLmRvbWFpbihzY2FsZS5kb21haW4oKSkuaW50ZXJwb2xhdGUoc2NhbGUuaW50ZXJwb2xhdGUoKSkucmFuZ2Uoc2NhbGUucmFuZ2UoKSksXG4gICAgICAgIHNjYWxlID0gXyxcbiAgICAgICAgY29sb3JGdW5jdGlvblxuICAgICAgKVxuICAgIDogc2NhbGU7XG4gIH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGZ1bmN0aW9uIGZvciBtb2RpZnkgb3BhY2l0eVxuICAgKiAoc2VlIHtAbGluayBjb2xvckZ1bmN0aW9uI21vZGlmeU9wYWNpdHl9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y29sb3JGdW5jdGlvbiB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGNvbG9yRnVuY3Rpb24ubW9kaWZ5T3BhY2l0eSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobW9kaWZ5T3BhY2l0eSA9IF8sIGNvbG9yRnVuY3Rpb24pIDogbW9kaWZ5T3BhY2l0eTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgdmFsdWUgdG8gbW9kaWZ5IHRoZSBjb2xvciBmb3IgdGhlIHN0cm9rZSB2aWEge0BsaW5rIGNvbG9yRnVuY3Rpb24jbW9kaWZ5T3BhY2l0eX1cbiAgICogKHNlZSB7QGxpbmsgY29sb3JGdW5jdGlvbiNzdHJva2VPcGFjaXR5fSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtjb2xvckZ1bmN0aW9uIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGNvbG9yRnVuY3Rpb24uc3Ryb2tlT3BhY2l0eSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3Ryb2tlT3BhY2l0eSA9IF8sIGNvbG9yRnVuY3Rpb24pIDogc3Ryb2tlT3BhY2l0eTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgdmFsdWUgdG8gbW9kaWZ5IHRoZSBjb2xvciBmb3IgdGhlIHN0cm9rZSB2aWEge0BsaW5rIGNvbG9yRnVuY3Rpb24jZmlsbE9wYWNpdHl9XG4gICAqIChzZWUge0BsaW5rIGNvbG9yRnVuY3Rpb24jZmlsbE9wYWNpdHl9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2NvbG9yRnVuY3Rpb24gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgY29sb3JGdW5jdGlvbi5maWxsT3BhY2l0eSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZmlsbE9wYWNpdHkgPSBfLCBjb2xvckZ1bmN0aW9uKSA6IGZpbGxPcGFjaXR5OyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB2YWx1ZSB0byBjb2xvckJ5XG4gICAqIChzZWUge0BsaW5rIGNvbG9yRnVuY3Rpb24jY29sb3JCeX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y29sb3JGdW5jdGlvbiB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb25cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICBjb2xvckZ1bmN0aW9uLmNvbG9yQnkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNvbG9yQnkgPSBfLCBjb2xvckZ1bmN0aW9uKSA6IGNvbG9yQnk7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZhbHVlIG9mIHZhbHVlRXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGNvbG9yRnVuY3Rpb24jdmFsdWVFeHRyYWN0b3J9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y29sb3JGdW5jdGlvbiB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGNvbG9yRnVuY3Rpb24udmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZhbHVlRXh0cmFjdG9yID0gXywgY29sb3JGdW5jdGlvbikgOiB2YWx1ZUV4dHJhY3RvcjsgfTtcblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB2YWx1ZSBvZiBjYXRlZ29yeUV4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayBjb2xvckZ1bmN0aW9uI2NhdGVnb3J5RXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2NvbG9yRnVuY3Rpb24gfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb25cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICBjb2xvckZ1bmN0aW9uLmNhdGVnb3J5RXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjYXRlZ29yeUV4dHJhY3RvciA9IF8sIGNvbG9yRnVuY3Rpb24pIDogY2F0ZWdvcnlFeHRyYWN0b3I7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZhbHVlIG9mIGNhdGVnb3J5RXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGNvbG9yRnVuY3Rpb24jY2F0ZWdvcmllc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtjb2xvckZ1bmN0aW9uIHwgc3RyaW5nW119XG4gICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgY29sb3JGdW5jdGlvbi5jYXRlZ29yaWVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjYXRlZ29yaWVzID0gXywgY29sb3JGdW5jdGlvbikgOiBjYXRlZ29yaWVzOyB9O1xuXG5cbiAgZnVuY3Rpb24gY29sb3JGdW5jdGlvbihrZXksIHZhbHVlLCBpbmRleCwgdHlwZSwgaG92ZXJRKSB7XG4gICAgdmFyIGMsXG4gICAgb3BhYyA9IHR5cGUgPT0gXCJmaWxsXCIgPyBmaWxsT3BhY2l0eSA6IHN0cm9rZU9wYWNpdHk7XG5cblxuICAgIHVwZGF0ZVNjYWxlKClcblxuICAgIGlmIChjb2xvckJ5ID09IFwiaW5kZXhcIikge1xuICAgICAgYyA9ICh0eXBlICE9IHVuZGVmaW5lZCkgPyBtb2RpZnlPcGFjaXR5KGgoc2NhbGUoaW5kZXgpKSwgb3BhYykgOiBoKHNjYWxlKGluZGV4KSlcbiAgICB9XG5cbiAgICBlbHNlIGlmIChjb2xvckJ5ID09ICd2YWx1ZScpIHtcbiAgICAgIHZhciB2ID0gdmFsdWVFeHRyYWN0b3Ioa2V5LCB2YWx1ZSwgaW5kZXgpO1xuICAgICAgLy8gaWYgKHYgPCBkYXRhRXh0ZW50WzBdKSB7ZGF0YUV4dGVudFswXSA9IHY7IHVwZGF0ZVNjYWxlKCl9XG4gICAgICAvLyBpZiAodiA+IGRhdGFFeHRlbnRbMV0pIHtkYXRhRXh0ZW50WzFdID0gdjsgdXBkYXRlU2NhbGUoKX1cblxuICAgICAgYyA9ICh0eXBlICE9IHVuZGVmaW5lZCkgPyBtb2RpZnlPcGFjaXR5KGgoc2NhbGUodikpLCBvcGFjKSA6IGgoc2NhbGUodikpXG4gICAgfVxuXG4gICAgZWxzZSBpZiAoY29sb3JCeSA9PSAnY2F0ZWdvcnknICl7XG4gICAgICB2YXIgY2F0ID0gY2F0ZWdvcnlFeHRyYWN0b3Ioa2V5LCB2YWx1ZSwgaW5kZXgpO1xuICAgICAgdmFyIHYgPSBjYXRlZ29yaWVzLmluZGV4T2YoY2F0KVxuICAgICAgYyA9ICh0eXBlICE9IHVuZGVmaW5lZCkgPyBtb2RpZnlPcGFjaXR5KGgoc2NhbGUodikpLCBvcGFjKSA6IGgoc2NhbGUodikpXG5cbiAgICB9XG5cbiAgICBlbHNlIHtcbiAgICAgIGMgPSAodHlwZSAhPSB1bmRlZmluZWQpID8gbW9kaWZ5T3BhY2l0eShoKHNjYWxlKGluZGV4KSksIG9wYWMpIDogaChzY2FsZShpbmRleCkpXG4gICAgfVxuXG4gICAgcmV0dXJuIGNcbiAgfVxuXG4gIGZ1bmN0aW9uIHVwZGF0ZVNjYWxlKCl7XG5cblxuICAgIGhlbHBlclNjYWxlLmRvbWFpbihbMCwgY29sb3JzLmxlbmd0aF0pXG4gICAgaWYgKGNvbG9yQnkgPT0gJ2NhdGVnb3J5JyAmJiBjYXRlZ29yaWVzICE9IHVuZGVmaW5lZCkgeyBoZWxwZXJTY2FsZS5yYW5nZShbMCwgY2F0ZWdvcmllcy5sZW5ndGhdKSB9XG4gICAgZWxzZSB7IGhlbHBlclNjYWxlLnJhbmdlKGRhdGFFeHRlbnQpIH1cblxuXG4gICAgdmFyIGEgPSBBcnJheShjb2xvcnMubGVuZ3RoKS5maWxsKDApLm1hcChmdW5jdGlvbihkLCBpKXsgcmV0dXJuIGhlbHBlclNjYWxlKGkpIH0pXG4gICAgc2NhbGUuZG9tYWluKGEpXG4gIH1cblxuICByZXR1cm4gY29sb3JGdW5jdGlvblxufVxuIiwiaW1wb3J0IHtzYWZlU2VsZWN0LCByb3VuZH0gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7bG9nLCB3YXJuLCBpbmZvLCBlcnJvciwgY29uc29sZUdyb3VwLCBjb25zb2xlR3JvdXBFbmR9IGZyb20gJy4vdXRpbHMnO1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUT09MVElQICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qKlxuICogUHJvZHVjZXMgYSBmdW5jdGlvbiBmb3IgaGFuZGxpbmcgdGhlIHRvb2x0aXBcbiAqXG4gKiB7QGxpbmsgaHR0cHM6Ly9zdW1uZXVyb24uZ2l0bGFiLmlvL2Qzc20vZGVtb3MvdG9vbHRpcC1kZXNpZ24vaW5kZXguaHRtbCBEZW1vfVxuICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IHNlbGVjdGlvblxuICogQHJldHVybnMge3Rvb2x0aXB9XG4gKiBAbmFtZXNwYWNlIHRvb2x0aXBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRvb2x0aXAoIHNlbGVjdGlvbiApIHtcblxuICB2YXJcbiAga2V5cyxcbiAgdmFsdWVzLFxuICBoZWFkZXIsXG4gIGRhdGEsXG4gIHNlbGVjdGlvblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUga2V5cyB0byBiZSBkaXNwbGF5ZWQgaW4gdGhlIHRvb2x0aXAuXG4gICAqIElmIG5vdCBzZXQsIHVzZXMgZDMua2V5cyhkYXRhW2tleV0pXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt0b29sdGlwIHwgc3RyaW5nW119XG4gICAqIEBtZW1iZXJvZiB0b29sdGlwXG4gICAqL1xuICB0b29sdGlwLmtleXMgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChrZXlzID0gXywgdG9vbHRpcCkgOiBrZXlzfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2YWx1ZXMgdG8gYmUgZGlzcGxheWVkIG5leHQgdG8gdGhlIGtleXMuXG4gICAqIElmIG5vdCBzZXQsIHVzZXMgZGF0YVtrZXldW2tleXNbaV1dLlxuICAgKiBJZiBhIGZ1bmN0aW9uLCBnZXRzIHBhc3NlZCBjdXJyZW50RGF0YSAoZGF0YVtrZXldKSBhbmQga2V5c1tpXS5cbiAgICogQHBhcmFtIHsqW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt0b29sdGlwIHwgKltdfVxuICAgKiBAbWVtYmVyb2YgdG9vbHRpcFxuICAgKi9cbiAgdG9vbHRpcC52YWx1ZXMgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZXMgPSBfLCB0b29sdGlwKSA6IHZhbHVlc307XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgaGVhZGVyIHRvIGJlIGRpc3BsYXllZCBpbiB0aGUgdG9vbHRpcC5cbiAgICogSWYgbm90IHNldCwgdXNlcyBrZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt0b29sdGlwIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgdG9vbHRpcFxuICAgKi9cbiAgdG9vbHRpcC5oZWFkZXIgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChoZWFkZXIgPSBfLCB0b29sdGlwKSA6IGhlYWRlcn07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZGF0YSAob3ZlciB0aGUgc2VsZWN0aW9uKSB0byBiZSB1c2VkIGZvciB0aGUgdG9vbHRpcFxuICAgKiBAcGFyYW0ge09iamVjdH0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Rvb2x0aXAgfCBPYmplY3R9XG4gICAqIEBtZW1iZXJvZiB0b29sdGlwXG4gICAqL1xuICB0b29sdGlwLmRhdGEgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkYXRhID0gXywgdG9vbHRpcCkgOiBkYXRhfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzZWxlY3Rpb24gZm9yIHRoZSB0b29sdGlwIHRvIGJlIGFwcGxpZWQgb25cbiAgICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt0b29sdGlwIHwgZDMuc2VsZWN0aW9ufVxuICAgKiBAbWVtYmVyb2YgdG9vbHRpcFxuICAgKi9cbiAgdG9vbHRpcC5zZWxlY3Rpb24gPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCB0b29sdGlwKSA6IHNlbGVjdGlvbn07XG5cbiAgLyoqXG4gICAqIEJpbmQsIHZpYSBzZWxlY3Rpb24ub24oKSwgdGhlIG1vdXNlbW92ZSBhbmQgbW91c2VvdXQgZXZlbnRzXG4gICAqIEByZXR1cm5zIHVuZGVmaW5lZFxuICAgKi9cbiAgZnVuY3Rpb24gdG9vbHRpcCggKSB7XG4gICAgc2VsZWN0aW9uLm9uKCdtb3VzZW92ZXInLCBtb3VzZW1vdmUpXG4gICAgc2VsZWN0aW9uLm9uKCdtb3VzZW1vdmUnLCBtb3VzZW1vdmUpXG4gICAgc2VsZWN0aW9uLm9uKCdtb3VzZW91dCcsIGZ1bmN0aW9uKCl7IGQzLnNlbGVjdEFsbChcIi5kM3NtLXRvb2x0aXBcIikucmVtb3ZlKCl9KVxuICB9XG5cblxuICAvKipcbiAgICogUHJvZHVjZXMgdGhlIHRvb2x0aXAgb24gbW91c2Vtb3ZlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBrZXkgb2YgdGhlIG9iamVjdCB0YXJnZXRlZCBieSB0aGUgbW91c2Vtb3ZlXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBpIChpbmRleCkgb2YgdGhlIG9iamVjdCB0YXJnZXRlZCBieSBtb3VzZW1vdmVcbiAgICogQG1lbWJlcm9mIHRvb2x0aXBcbiAgICogQHByaXZhdGVcbiAgICovXG4gIGZ1bmN0aW9uIG1vdXNlbW92ZShrZXksIGkpIHtcbiAgICBjb25zb2xlR3JvdXAoJ2Qzc20tdG9vbHRpcCcpXG4gICAgdmFyIGN1cnJlbnREYXRhID0gZGF0YVtrZXldXG5cbiAgICB2YXIgW3gsIHldID0gZDMubW91c2UoZDMuc2VsZWN0KFwiaHRtbFwiKS5ub2RlKCkpXG4gICAgbG9nKCd0b29sdGlwJywgJ21vdXNlbW92ZSBkZXRlY3RlZCcse2tleToga2V5LCBpbmRleDogaSwgeDp4LCB5Onl9KVxuICAgIGxvZygndG9vbHRpcCcsICdjdXJyZW50IGRhdGEnLCBjdXJyZW50RGF0YSlcblxuXG5cbiAgICB2YXIgZGl2ID0gc2FmZVNlbGVjdChkMy5zZWxlY3QoJ2h0bWwnKSwgJ3Rvb2x0aXAnLCAnZDNzbS10b29sdGlwJylcbiAgICAuY2xhc3NlZCgnY2FyZCcsIHRydWUpXG4gICAgLnN0eWxlKCdtYXgtd2lkdGgnLCAnMzAwcHgnKVxuICAgIC5zdHlsZSgnYmFja2dyb3VuZC1jb2xvcicsIFwiIzIxMjUyOVwiKVxuICAgIC5zdHlsZSgnY29sb3InLCAnd2hpdGUnKVxuXG5cblxuICAgIHZhciBjYXJkQm9keSA9IHNhZmVTZWxlY3QoZGl2LCAnZGl2JywgJ2NhcmQtYm9keScpXG4gICAgdmFyIGNhcmRUaXRsZSA9IHNhZmVTZWxlY3QoY2FyZEJvZHksICdoNScsICdjYXJkLXRpdGxlJylcbiAgICAudGV4dChoZWFkZXIgPT0gdW5kZWZpbmVkID8ga2V5IDogdHlwZW9mIGhlYWRlciA9PSAnZnVuY3Rpb24nID8gaGVhZGVyKGtleSwgY3VycmVudERhdGEsIGkpIDogaGVhZGVyKVxuICAgIC5zdHlsZSgnY29sb3InLCAnY3lhbicpXG5cblxuICAgIHZhciB0YWJsZSA9IHNhZmVTZWxlY3QoY2FyZEJvZHksICd0YWJsZScsICd0YWJsZScpLmNsYXNzZWQoJ3RhYmxlLWRhcmsnLCB0cnVlKVxuICAgIHZhciB0Qm9keSA9IHNhZmVTZWxlY3QodGFibGUsICd0Ym9keScpXG5cbiAgICB0Qm9keSA9IHRCb2R5LnNlbGVjdEFsbCgndHInKVxuICAgIHRCb2R5PSB0Qm9keS5kYXRhKGtleXMgPT0gdW5kZWZpbmVkID8gZDMua2V5cyhjdXJyZW50RGF0YSk6IGtleXMpXG4gICAgdEJvZHkuZXhpdCgpLnJlbW92ZSgpXG5cblxuICAgIHZhciB0ciA9IHRCb2R5LmVudGVyKCkuYXBwZW5kKCd0cicpLnN0eWxlKCdtYXgtd2lkdGgnLCAnMzAwcHgnKVxuICAgIHRyLmFwcGVuZCgndGQnKS5hdHRyKCdjbGFzcycsIGZ1bmN0aW9uKGQsIGkpe3JldHVybiAndG9vbHRpcC1rZXknfSlcbiAgICB0ci5hcHBlbmQoJ3RkJykuYXR0cignY2xhc3MnLCAgZnVuY3Rpb24oZCwgaSwgail7cmV0dXJuICd0b29sdGlwLXZhbHVlJ30pXG4gICAgLmF0dHIoJ3Rvb2x0aXAtcm93LWluZGV4JywgZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGl9KVxuXG4gICAgLy8gdEJvZHkgPSB0Qm9keS5tZXJnZSh0cilcbiAgICBjb25zb2xlR3JvdXAoJ3Rvb2x0aXAtcm93cycpXG4gICAgdEJvZHkuc2VsZWN0QWxsKCcudG9vbHRpcC1rZXknKS50ZXh0KGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkfSlcbiAgICB0Qm9keS5zZWxlY3RBbGwoJ3RyIC50b29sdGlwLXZhbHVlJylcbiAgICAudGV4dChmdW5jdGlvbihkLCBpKXtcbiAgICAgIGxvZygndG9vbHRpcCcsICd0cnlpbmcgdG8gc2V0IHZhbHVlJywge3Jvd0tleTogZCwgcm93SW5kZXg6IGl9KVxuICAgICAgdmFyIGkgPSBkMy5zZWxlY3QodGhpcykuYXR0cigndG9vbHRpcC1yb3ctaW5kZXgnKVxuICAgICAgdmFyIHYgPSBjdXJyZW50RGF0YVtkXTtcblxuXG4gICAgICBpZiAodmFsdWVzICE9IHVuZGVmaW5lZCkge3YgPSB2YWx1ZXNbaV07IGlmKHR5cGVvZiB2ID09IFwiZnVuY3Rpb25cIikge3YgPSB2KGN1cnJlbnREYXRhLCBkKX19XG4gICAgICByZXR1cm4gIHR5cGVvZiB2ID09ICdudW1iZXInID8gcm91bmQodiwgNSkgOiB2XG4gICAgfSlcbiAgICBjb25zb2xlR3JvdXBFbmQoKVxuICAgIGNvbnNvbGVHcm91cEVuZCgpXG5cbiAgICB4ICs9IDE1XG4gICAgLy8geCArPSAxNVxuICAgIHZhciBiYm94ID0gZGl2Lm5vZGUoKS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuICAgIGlmICh4ICsgYmJveC53aWR0aCA+IHdpbmRvdy5pbm5lcldpZHRoIC0gd2luZG93LnNjcm9sbFgpIHsgeCA9IGQzLmV2ZW50LnBhZ2VYIC0gYmJveC53aWR0aCAtIDE1IH1cbiAgICBpZiAoeSArIGJib3guaGVpZ2h0ID4gd2luZG93LmlubmVySGVpZ2h0ICAtIHdpbmRvdy5zY3JvbGxZKSB7IHkgPSBkMy5ldmVudC5wYWdlWSAtIGJib3guaGVpZ2h0IC0gMTUgfVxuICAgIGRpdi5zdHlsZSgncG9zaXRpb24nKSA9PSBcInJlbGF0aXZlXCJcbiAgICA/IGRpdi5zdHlsZSgncG9zaXRpb24nLCAnYWJzb2x1dGUnKS5zdHlsZSgnbGVmdCcsIHgrJ3B4Jykuc3R5bGUoJ3RvcCcsIHkrJ3B4JylcbiAgICA6IGRpdi5zdHlsZSgnbGVmdCcsIHgrJ3B4Jykuc3R5bGUoJ3RvcCcsIHkrJ3B4JylcbiAgICAvLyAudHJhbnNpdGlvbigpLmR1cmF0aW9uKDIwMCkuZWFzZShkMy5lYXNlU2luKVxuXG4gICAgLy8gaWYgKGJib3gueCArIGJib3gud2lkdGggPiB3aW5kb3cuaW5uZXJXaWR0aCkge1xuICAgIC8vICAgZGl2LnN0eWxlKCdsZWZ0JywgKGQzLmV2ZW50LnBhZ2VYLTE1LWJib3gud2lkdGgpKydweCcpXG4gICAgLy8gfVxuICAgIC8vIGlmIChiYm94LnkgKyBiYm94LmhlaWdodCA+IHdpbmRvdy5pbm5lckhlaWdodCkge1xuICAgIC8vICAgZGl2LnN0eWxlKCd0b3AnLCAoZDMuZXZlbnQucGFnZVktMTUtYmJveC5oZWlnaHQpKydweCcpXG4gICAgLy8gfVxuXG4gICAgZGl2LmF0dHIoJ3otaW5kZXgnLCAxMDAwMClcbiAgfVxuXG4gIHJldHVybiB0b29sdGlwXG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuXG5leHBvcnQgZnVuY3Rpb24gc2VsZWN0RmlsdGVyKHNlbGVjdGlvbikge1xuXG4gIHZhclxuICBkYXRhLFxuICBuYW1lc3BhY2UgPSAnZDNzbS1zZWxlY3QtZmlsdGVyJyxcbiAgc2VsZWN0aW9uTmFtZSA9ICdTZWxlY3Qgb3B0aW9uczonLFxuICBkZWZhdWx0VmFsdWUgPSB1bmRlZmluZWRcblxuXG5cblxuICB2YXIgbGFzdFZhbHVlID0gdW5kZWZpbmVkXG5cbiAgc2VsZWN0RmlsdGVyLmRhdGEgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRhdGEgPSBfLCBzZWxlY3RGaWx0ZXIpIDogZGF0YX1cbiAgc2VsZWN0RmlsdGVyLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgc2VsZWN0RmlsdGVyKSA6IG5hbWVzcGFjZX1cbiAgc2VsZWN0RmlsdGVyLnNlbGVjdGlvbk5hbWUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNlbGVjdGlvbk5hbWUgPSBfLCBzZWxlY3RGaWx0ZXIpIDogc2VsZWN0aW9uTmFtZX1cbiAgc2VsZWN0RmlsdGVyLmRlZmF1bHRWYWx1ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGVmYXVsdFZhbHVlID0gXywgc2VsZWN0RmlsdGVyKSA6IGRlZmF1bHRWYWx1ZX1cbiAgc2VsZWN0RmlsdGVyLmN1cnJlbnRPcHRpb24gPSBjdXJyZW50T3B0aW9uXG5cbiAgZnVuY3Rpb24gc2VsZWN0RmlsdGVyKCkge1xuICAgIHZhclxuICAgIGNvbnRhaW5lciA9IHNhZmVTZWxlY3Qoc2VsZWN0aW9uLCAnZGl2JywgJ2lucHV0LWdyb3VwJykuY2xhc3NlZChoeXBlbmF0ZShuYW1lc3BhY2UsJ2NvbnRhaW5lcicpLHRydWUpLFxuXG4gICAgICBzZWxlY3RQcmVwZW5kID0gc2FmZVNlbGVjdChjb250YWluZXIsICdkaXYnLCAnc2VsZWN0LXByZXBlbmQnKS5jbGFzc2VkKCdpbnB1dC1ncm91cC1wcmVwZW5kJywgdHJ1ZSksXG4gICAgICAgIHNlbGVjdFByZXBlbmRTcGFuID0gc2FmZVNlbGVjdChzZWxlY3RQcmVwZW5kLCAnc3BhbicsICdpbnB1dC1ncm91cC10ZXh0JykudGV4dChzZWxlY3Rpb25OYW1lKSxcblxuICAgICAgc2VsZWN0ID0gc2FmZVNlbGVjdChjb250YWluZXIsICdzZWxlY3QnLCAnY3VzdG9tLXNlbGVjdCcpLmNsYXNzZWQoaHlwZW5hdGUobmFtZXNwYWNlLCdzZWxlY3QnKSx0cnVlKSxcblxuICAgICAgc2VsZWN0QXBwZW5kID0gc2FmZVNlbGVjdChjb250YWluZXIsICdkaXYnLCAnc2VsZWN0LWFwcGVuZCcpLmNsYXNzZWQoJ2lucHV0LWdyb3VwLXByZXBlbmQnLCB0cnVlKSxcbiAgICAgICAgc2VsZWN0QXBwZW5kQnV0dG9uID0gc2FmZVNlbGVjdChzZWxlY3RBcHBlbmQsICdhJywgJ2ZpbHRlci1idXR0b24nKS5jbGFzc2VkKCdidG4gYnRuLW91dGxpbmUtc2Vjb25kYXJ5JywgdHJ1ZSksXG4gICAgICAgICAgZmlsdGVyQnV0dG9uSWNvbiA9IHNhZmVTZWxlY3Qoc2VsZWN0QXBwZW5kQnV0dG9uLCAnaScsICdmYSBmYS1maWx0ZXInKSxcblxuICAgICAgaW5wdXRHcm91cCA9IHNhZmVTZWxlY3QoY29udGFpbmVyLCAnZGl2JywgJ2ZpbHRlci1pbnB1dC1ncm91cCcpLmNsYXNzZWQoJ2lucHV0LWdyb3VwJyx0cnVlKS5jbGFzc2VkKCdkLW5vbmUnLCB0cnVlKSxcbiAgICAgICAgaW5wdXRQcmVwZW5kID0gc2FmZVNlbGVjdChpbnB1dEdyb3VwLCAnZGl2JywgJ2lucHV0LWdyb3VwLXByZXBlbmQnKSxcbiAgICAgICAgICBpbnB1dFByZXBlbmRTcGFuID0gc2FmZVNlbGVjdChpbnB1dFByZXBlbmQsICdzcGFuJywgJ2lucHV0LWdyb3VwLXRleHQnKS5jbGFzc2VkKCdzZWFyY2gtYnV0dG9uJywgdHJ1ZSksXG4gICAgICAgICAgICBpbnB1dFByZXBlbmRTcGFuSWNvbiA9IHNhZmVTZWxlY3QoaW5wdXRQcmVwZW5kU3BhbiwnaScsJ2ZhIGZhLXNlYXJjaCcpLFxuXG4gICAgICAgIGlucHV0ID0gc2FmZVNlbGVjdChpbnB1dEdyb3VwLCAnaW5wdXQnLCAnZm9ybS1jb250cm9sJykuYXR0cigncGxhY2Vob2xkZXInLCAnYWxsJykuYXR0cigndHlwZScsICd0ZXh0JyksXG4gICAgICAgIGlucHV0QXBwZW5kID0gc2FmZVNlbGVjdChpbnB1dEdyb3VwLCAnZGl2JywgJ2lucHV0LWdyb3VwLWFwcGVuZCcpLFxuICAgICAgICAgIGlucHV0QXBwZW5kQnV0dG9uID0gc2FmZVNlbGVjdChpbnB1dEFwcGVuZCwgJ2EnLCAnY2xvc2UtYnV0dG9uJykuY2xhc3NlZCgnYnRuIGJ0bi1vdXRsaW5lLXNlY29uZGFyeScsIHRydWUpLFxuICAgICAgICAgICAgaW5wdXRBcHBlbmRCdXR0b25JY29uID0gc2FmZVNlbGVjdChpbnB1dEFwcGVuZEJ1dHRvbiwgJ2knLCAnZmEgZmEtY2xvc2UnKVxuXG5cbiAgICB2YXIga2V5cyA9IGQzLmtleXMoZGF0YSksXG4gICAgb3B0aW9ucyA9IHNlbGVjdC5zZWxlY3RBbGwoJ29wdGlvbicpXG5cbiAgICBvcHRpb25zID0gb3B0aW9ucy5kYXRhKGQzLmtleXMoZGF0YSkpXG4gICAgb3B0aW9ucyA9IG9wdGlvbnMubWVyZ2Uob3B0aW9ucy5lbnRlcigpLmFwcGVuZCgnb3B0aW9uJykpXG4gICAgLmF0dHIoJ3ZhbHVlJywgZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGR9KVxuICAgIC50ZXh0KGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkfSlcblxuICAgIHZhclxuICAgIGZpbHRlckJ1dHRvbiA9IHNlbGVjdEFwcGVuZEJ1dHRvbixcbiAgICBjbG9zZUJ1dHRvbiA9IGlucHV0QXBwZW5kQnV0dG9uXG5cbiAgICBmaWx0ZXJCdXR0b24ub24oJ2NsaWNrJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICB2YXIgY3VycmVudFN0eWxlID0gaW5wdXRHcm91cC5jbGFzc2VkKCdkLW5vbmUnKVxuICAgICAgaW5wdXRHcm91cC5jbGFzc2VkKCdkLW5vbmUnLCAhY3VycmVudFN0eWxlKVxuICAgIH0pXG5cbiAgICBjbG9zZUJ1dHRvbi5vbignY2xpY2snLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIGlucHV0LnByb3BlcnR5KCd2YWx1ZScsICcnKS5kaXNwYXRjaCgnaW5wdXQnKVxuICAgIH0pXG5cbiAgICBpbnB1dC5vbignaW5wdXQnLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhclxuICAgICAgdmFsID0gaW5wdXQucHJvcGVydHkoJ3ZhbHVlJyksXG4gICAgICByZWcgPSBuZXcgUmVnRXhwKHZhbCwgJ2dpJyksXG4gICAgICB1c2VcblxuICAgICAgaWYgKHZhbCA9PSAnJykge3VzZSA9IGtleXN9XG4gICAgICBlbHNlIHtcbiAgICAgICAgdXNlID0gW11cbiAgICAgICAgZDMua2V5cyhkYXRhKS5tYXAoZnVuY3Rpb24ob3B0aW9uLCBqKXtcbiAgICAgICAgICB2YXIgbWF0Y2ggPSBvcHRpb24ubWF0Y2gocmVnKVxuICAgICAgICAgIGlmIChtYXRjaCA9PSBudWxsIHx8IG1hdGNoLmpvaW4oJycpID09ICcnKSB7fVxuICAgICAgICAgIGVsc2UgeyB1c2UucHVzaChvcHRpb24pIH1cbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgb3B0aW9ucyA9IHNlbGVjdC5zZWxlY3RBbGwoJ29wdGlvbicpXG4gICAgICBvcHRpb25zID0gb3B0aW9ucy5kYXRhKHVzZSlcbiAgICAgIG9wdGlvbnMuZXhpdCgpLnJlbW92ZSgpXG4gICAgICBvcHRpb25zID0gb3B0aW9ucy5tZXJnZShvcHRpb25zLmVudGVyKCkuYXBwZW5kKCdvcHRpb24nKSlcbiAgICAgIC5hdHRyKCd2YWx1ZScsIGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkfSlcbiAgICAgIC50ZXh0KGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkfSlcblxuICAgICAgdmFyIGN1cnJlbnQgPSBjdXJyZW50T3B0aW9uKClcbiAgICAgIGlmIChsYXN0VmFsdWUgIT0gY3VycmVudCkge1xuICAgICAgICBsYXN0VmFsdWUgPSBjdXJyZW50XG4gICAgICAgIHNlbGVjdC5kaXNwYXRjaCgnY2hhbmdlJylcbiAgICAgIH1cbiAgICB9KVxuXG5cbiAgfVxuXG4gIGZ1bmN0aW9uIGN1cnJlbnRPcHRpb24oKSB7XG4gICAgdmFyIHZhbCA9IHNlbGVjdGlvbi5zZWxlY3QoXCJzZWxlY3RcIikucHJvcGVydHkoJ3ZhbHVlJylcbiAgICByZXR1cm4gdmFsID09IHVuZGVmaW5lZCB8fCB2YWwgPT0gJydcbiAgICA/IGRlZmF1bHRWYWx1ZSA9PSB1bmRlZmluZWRcbiAgICAgID8gZDMua2V5cyhkYXRhKVswXVxuICAgICAgOiBkZWZhdWx0VmFsdWVcbiAgICA6IHZhbFxuICB9XG5cbiAgcmV0dXJuIHNlbGVjdEZpbHRlclxufVxuIiwiaW1wb3J0IHtoeXBlbmF0ZSwgc2FmZVNlbGVjdCwgZXVjbGlkZWFuRGlzdGFuY2V9IGZyb20gJy4vaGVscGVycyc7XG5pbXBvcnQge3NldHVwQ29udGFpbmVyLCBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0LCBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7dW5pcXVlLCBoYXNRLCBmbGF0dGVuLCB3aGljaEJpbn0gZnJvbSAnLi9hcnJheS1mdW5jdGlvbnMnO1xuaW1wb3J0IHtncm91cGluZ1NwYWNlcn0gZnJvbSAnLi9ncm91cGluZy1zcGFjZXInO1xuaW1wb3J0IHtjb2xvckZ1bmN0aW9uIGFzIENGfSBmcm9tICcuL2NvbG9yLWZ1bmN0aW9uJztcbmltcG9ydCB7dG9vbHRpcCBhcyBUVGlwfSBmcm9tICcuL3Rvb2x0aXAnO1xuaW1wb3J0ICcuL2QzLXByb3RvdHlwZXMnO1xuXG5mdW5jdGlvbiBnZXRUcmFuc2xhdGlvbihzZWxlY3Rpb24pe1xuICB2YXIgdHJhbnNmb3JtID0gc2VsZWN0aW9uLmF0dHIoJ3RyYW5zZm9ybScpXG4gIHZhciBbanVuaywgeHldID10cmFuc2Zvcm0uc3BsaXQoJ3RyYW5zbGF0ZSgnKVxuICB2YXIgW3gsIHldID0geHkuc3BsaXQoJywnKVxuICB5LCBqdW5rID0geS5zcGxpdCgnKScpXG4gIHJldHVybiBbcGFyc2VGbG9hdCh4KSwgcGFyc2VGbG9hdCh5KV1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxhc3NvKCBzZWxlY3Rpb24gKSB7XG4gIHZhclxuICBzdmcsIC8vIHN2ZyB0aGF0IGlzIHRhcmdldCBvZiBldmVudHNcbiAgb2JqZWN0Q29udGFpbmVyLCAvLyBjb250YWluZXIgd2hpY2ggaG91c2VzIG9iamVjdHMgd2UgYXJlIHNlbGVjdGluZyAoYWxsb3dzIGZvciB0cmFuc2Zvcm0gdG8gYmUgYXBwbGllZCB0byBsYXNzbylcbiAgb2JqZWN0Q2xhc3MsIC8vIGNsYXNzIG9mIG9iamVjdCB3ZSBhcmUgc2VsZWN0aW5nXG4gIG5hbWVzcGFjZT1cImQzc20tbGFzc29cIixcbiAgY2hhcnRDb250YWluZXIsXG4gIGNoYXJ0T2Zmc2V0LFxuICBvYmplY3RzT2Zmc2V0LFxuICBldmVudENhdGNoZXIsXG5cbiAgeFNjYWxlLCAvLyBvcHRpb25hbCBzY2FsZSBmb3IgdGhlIGxhc3NvIGN1cnJlbnRQb2ludHNcbiAgeVNjYWxlLCAvLyBvcHRpb25hbCBzY2FsZSBmb3IgdGhlIGxhc3NvIGN1cnJlbnRQb2ludHNcblxuICBhY3RpdmVRID0gZmFsc2UsIC8vIHdoZXRoZXIgb3Igbm90IGxhc3NvIGlzIGFjdGl2ZVxuXG4gIGN1cnJlbnRQb2ludHM9W10sIC8vIG1vdXNlIHBvaW50cyBmb3IgY3VycmVudCBsYXNzb1xuICBhbGxQb2ludHM9W10sIC8vIGxpc3Qgb2YgbGlzdHMgZm9yIGFsbCBwb2ludHMgb2YgbGFzc29zXG5cbiAgbGluZSA9IGQzLmxpbmUoKVxuICAueChmdW5jdGlvbihkLCBpKXtcbiAgICB2YXIgeFxuICAgIGlmICh4U2NhbGUgIT0gdW5kZWZpbmVkKSB7IHggPSB4U2NhbGUoZFswXSkgfVxuICAgIGVsc2Uge3ggPSBkWzBdfVxuICAgIHJldHVybiB4IC8vLSBjaGFydE9mZnNldFswXS8vIC0gb2JqZWN0c09mZnNldFswXVxuICB9KVxuICAueShmdW5jdGlvbihkLCBpKXtcbiAgICB2YXIgeVxuICAgIGlmICh5U2NhbGUgIT0gdW5kZWZpbmVkKSB7IHk9IHlTY2FsZShkWzFdKSB9XG4gICAgZWxzZSB7eSA9ICBkWzFdfVxuICAgIHJldHVybiB5Ly8gLSBjaGFydE9mZnNldFsxXS8vIC0gb2JqZWN0c09mZnNldFsxXVxuICB9KVxuICAuY3VydmUoZDMuY3VydmVMaW5lYXJDbG9zZWQpLFxuXG4gIGluc3RhbmNlPTAsICAgIC8vIGFuIGluZGVudGlmaWVyIGZvciB3aGljaCBpbnN0YW5jZSB0aGlzIGxhc3NvIGlzIHVuZGVyIHRoZSBjdXJyZW50IHN2Z1xuXG4gIHRpY2tEaXN0YW5jZSA9IDEwLFxuXG4gIC8vIHN0eWxlcyBmb3IgbGFzc28gcGF0aFxuICBjb2xvciA9ICcjMTdhMmI4JyxcbiAgYW5pbWF0aW9uUmF0ZSA9ICcxMHMnLFxuICBvcGFjaXR5PTAuMyxcbiAgZGFzaEFycmF5ID0gJzUsIDEwJyxcbiAgc3Ryb2tlID0gJ2JsYWNrJyxcbiAgc3Ryb2tlV2lkdGg9MixcblxuICAvLyBzdHlsZXMgZm9yIGxhc3NvZWQgb2JqZWN0c1xuICBsYXNzb2VkRmlsbCA9IFwid2hpdGVcIixcbiAgbGFzc29lZFN0cm9rZSA9ICdibGFjaycsXG4gIGxhc3NvZWRTdHJva2VXaWR0aCA9IDMsXG5cbiAgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMCxcbiAgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwXG5cbiAgdmFyIHBhdGhcblxuICBsYXNzby5zdmcgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHN2ZyA9IF8sIGxhc3NvKSA6IHN2ZzsgfVxuICBsYXNzby5jaGFydENvbnRhaW5lciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY2hhcnRDb250YWluZXIgPSBfLCBsYXNzbykgOiBjaGFydENvbnRhaW5lcjsgfVxuICBsYXNzby5vYmplY3RDb250YWluZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdENvbnRhaW5lciA9IF8sIGxhc3NvKSA6IG9iamVjdENvbnRhaW5lcjsgfVxuICBsYXNzby5vYmplY3RDbGFzcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3MgPSBfLCBsYXNzbykgOiBvYmplY3RDbGFzczsgfVxuICBsYXNzby5uYW1lc3BhY2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG5hbWVzcGFjZSA9IF8sIGxhc3NvKSA6IG5hbWVzcGFjZTsgfVxuICBsYXNzby54U2NhbGUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhTY2FsZSA9IF8sIGxhc3NvKSA6IHhTY2FsZTsgfVxuICBsYXNzby55U2NhbGUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlTY2FsZSA9IF8sIGxhc3NvKSA6IHlTY2FsZTsgfVxuICBsYXNzby5hY3RpdmVRID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChhY3RpdmVRID0gXywgbGFzc28pIDogYWN0aXZlUTsgfVxuICBsYXNzby5jdXJyZW50UG9pbnRzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjdXJyZW50UG9pbnRzID0gXywgbGFzc28pIDogY3VycmVudFBvaW50czsgfVxuICBsYXNzby5hbGxQb2ludHMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGFsbFBvaW50cyA9IF8sIGxhc3NvKSA6IGFsbFBvaW50czsgfVxuICBsYXNzby5pbnN0YW5jZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoaW5zdGFuY2UgPSBfLCBsYXNzbykgOiBpbnN0YW5jZTsgfVxuICBsYXNzby50aWNrRGlzdGFuY2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tEaXN0YW5jZSA9IF8sIGxhc3NvKSA6IHRpY2tEaXN0YW5jZTsgfVxuICBsYXNzby5jb2xvciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY29sb3IgPSBfLCBsYXNzbykgOiBjb2xvcjsgfVxuICBsYXNzby5hbmltYXRpb25SYXRlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChhbmltYXRpb25SYXRlID0gXywgbGFzc28pIDogYW5pbWF0aW9uUmF0ZTsgfVxuICBsYXNzby5vcGFjaXR5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvcGFjaXR5ID0gXywgbGFzc28pIDogb3BhY2l0eTsgfVxuICBsYXNzby5kYXNoQXJyYXkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRhc2hBcnJheSA9IF8sIGxhc3NvKSA6IGRhc2hBcnJheTsgfVxuICBsYXNzby5zdHJva2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHN0cm9rZSA9IF8sIGxhc3NvKSA6IHN0cm9rZTsgfVxuICBsYXNzby5sYXNzb2VkRmlsbCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobGFzc29lZEZpbGwgPSBfLCBsYXNzbykgOiBsYXNzb2VkRmlsbDsgfVxuICBsYXNzby5sYXNzb2VkU3Ryb2tlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChsYXNzb2VkU3Ryb2tlID0gXywgbGFzc28pIDogbGFzc29lZFN0cm9rZTsgfVxuICBsYXNzby5sYXNzb2VkU3Ryb2tlV2lkdGggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGxhc3NvZWRTdHJva2VXaWR0aCA9IF8sIGxhc3NvKSA6IGxhc3NvZWRTdHJva2VXaWR0aDsgfVxuICBsYXNzby5ldmVudENhdGNoZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGV2ZW50Q2F0Y2hlciA9IF8sIGxhc3NvKSA6IGV2ZW50Q2F0Y2hlcjsgfVxuXG4gIGxhc3NvLmRyYWcgPSBkcmFnXG4gIGxhc3NvLmRyYXcgPSBkcmF3XG4gIGxhc3NvLnRpY2sgPSB0aWNrXG4gIGxhc3NvLmRldGVjdCA9IGRldGVjdFxuICBsYXNzby50b2dnbGUgPSB0b2dnbGVcbiAgbGFzc28ucmVtb3ZlID0gcmVtb3ZlXG4gIGxhc3NvLnJlbmRlciA9IHJlbmRlclxuICBsYXNzby5rZXlGcmFtZXMgPSBrZXlGcmFtZXNcbiAgbGFzc28udXBkYXRlT2JqZWN0cyA9IHVwZGF0ZU9iamVjdHNcbiAgbGFzc28uYXBwbHlQYXRoQXR0cmlidXRlcyA9IGFwcGx5UGF0aEF0dHJpYnV0ZXNcbiAgbGFzc28uYXBwbHlPYmplY3RBdHRyaWJ1dGVzID0gYXBwbHlPYmplY3RBdHRyaWJ1dGVzXG5cbiAga2V5RnJhbWVzKClcblxuICBmdW5jdGlvbiBsYXNzbygpIHtcbiAgICAvLyBhZGQgYSBkYXNoIGFuaW1hdGlvbiBpZiBuZWVkZWRcbiAgICBpZiAoYWN0aXZlUSkgeyB0cmFuc2l0aW9uRHJhdygpIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHRvZ2dsZShzdGF0ZSkge1xuICAgIC8vIHVzZSBvcHRpb25hbCBwYXJhbSB0byBzZXQgc3RhdGUsIG90aGVyd2lzZSB0b2dnbGUgc3RhdGVcbiAgICBhY3RpdmVRID0gKHN0YXRlIT11bmRlZmluZWQpID8gc3RhdGUgOiAhYWN0aXZlUVxuICAgIGNoYXJ0T2Zmc2V0ID0gZ2V0VHJhbnNsYXRpb24oY2hhcnRDb250YWluZXIpXG4gICAgb2JqZWN0c09mZnNldCA9IGdldFRyYW5zbGF0aW9uKG9iamVjdENvbnRhaW5lcilcblxuICAgIGlmIChhY3RpdmVRKSB7XG4gICAgICBzdmcubm9kZSgpLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIHJlbmRlciwgdHJ1ZSlcbiAgICB9IGVsc2Uge1xuICAgICAgc3ZnLm5vZGUoKS5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZWRvd24nLCByZW5kZXIsIHRydWUpXG4gICAgICByZW1vdmUoKVxuICAgIH1cblxuICB9XG5cbiAgZnVuY3Rpb24gZHJhdygpIHtcbiAgICBjaGFydE9mZnNldCA9IGdldFRyYW5zbGF0aW9uKGNoYXJ0Q29udGFpbmVyKVxuICAgIG9iamVjdHNPZmZzZXQgPSBnZXRUcmFuc2xhdGlvbihvYmplY3RDb250YWluZXIpXG5cbiAgICB2YXIgY29udGFpbmVyID0gc2FmZVNlbGVjdChvYmplY3RDb250YWluZXIsICdnJywgJ2xhc3NvLWNvbnRhaW5lcicpXG4gICAgdmFyIHBhdGhzID0gY29udGFpbmVyLnNlbGVjdEFsbCgncGF0aFtpbnN0YW5jZT1cIicraW5zdGFuY2UrJ1wiXScpXG5cbiAgICAvLyB1cGRhdGVcbiAgICBwYXRocyA9IHBhdGhzLmRhdGEoYWxsUG9pbnRzKVxuXG4gICAgLy8gcmVtb3ZlIGV4Y2Vzc1xuICAgIHZhciBwRXhpdCA9IHBhdGhzLmV4aXQoKS5yZW1vdmUoKVxuICAgIC8vIGFkZCBuZWVkZWQgcGF0aHNcbiAgICB2YXIgcEVudGVyID0gcGF0aHMuZW50ZXIoKS5hcHBlbmQoJ3BhdGgnKVxuXG4gICAgLy8gbWVyZ2VcbiAgICBwYXRocyA9IHBhdGhzLm1lcmdlKHBFbnRlcilcblxuICAgIC8vIGFwcGx5XG4gICAgYXBwbHlQYXRoQXR0cmlidXRlcyhwYXRocylcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbW92ZSgpIHtcbiAgICB2YXIgY29udGFpbmVyID0gc2FmZVNlbGVjdChvYmplY3RDb250YWluZXIsICdnJywgJ2xhc3NvLWNvbnRhaW5lcicpXG4gICAgdmFyIHBhdGhzID0gY29udGFpbmVyLnNlbGVjdEFsbCgncGF0aFtpbnN0YW5jZT1cIicraW5zdGFuY2UrJ1wiXScpLnJlbW92ZSgpXG4gICAgY29udGFpbmVyLnJlbW92ZSgpXG4gICAgb2JqZWN0Q29udGFpbmVyLnNlbGVjdEFsbChvYmplY3RDbGFzcykuY2xhc3NlZChcImluLWxhc3NvXCIsIGZhbHNlKVxuICAgIHVwZGF0ZU9iamVjdHMoKVxuICB9XG5cbiAgZnVuY3Rpb24gcmVuZGVyKCBldmVudCApIHtcbiAgICAvLyBub3RoaW5nIGNhbiBpbnRlcmVmZXIgd2l0aCBkcmF3aW5nIHRoZSBsYXNzb1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7IGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuXG4gICAgdmFyIGNvbnRhaW5lciA9IHNhZmVTZWxlY3Qob2JqZWN0Q29udGFpbmVyLCAnZycsICdsYXNzby1jb250YWluZXInKVxuXG4gICAgLypcbiAgICBlYWNoIHRpbWUgdGhlIHVzZXIgcHJlc3NlcyBkb3duLCB3aGlsZSB0aGUgc3RhdGUgaXMgYWN0aXZlLCB0aGUgbGFzc29cbiAgICB0aGUgbGFzc28gc2hvdWxkIG1ha2UgYSBzZXBlcmF0ZSBzZWdtZW50LlxuICAgICovXG4gICAgY3VycmVudFBvaW50cyA9IFtdO1xuXG4gICAgc3ZnLm5vZGUoKS5hZGRFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCBkcmFnKVxuICAgIHN2Zy5ub2RlKCkuYWRkRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsIGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgICBzdmcubm9kZSgpLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIGRyYWcpXG4gICAgICBhbGxQb2ludHMucHVzaChjdXJyZW50UG9pbnRzKVxuICAgICAgLy8gQlVHOiAgc29tZWhvdyB0aGlzIGlzIHB1c2hpbmcgY3VycmVudFBvaW50cyBuIHRpbWVzIHdoZXJlIG4gaXMgdGhlIG50aCBsYXNzbyBwYXRoIGZvciB0aGUgY3VycmVudCBpbnN0YW5jZVxuICAgICAgLy8gTk9URTogYWxsUG9pbnRzID0gdW5pcXVlKGFsbFBvaW50cykgaXMgYSB0ZW1wb3JhcnkgYW5kIGluZWZmaWNpZW50IGZpeFxuICAgICAgYWxsUG9pbnRzID0gdW5pcXVlKGFsbFBvaW50cylcbiAgICB9KVxuXG4gICAgcGF0aCA9IGNvbnRhaW5lci5hcHBlbmQoJ3BhdGgnKS5kYXRhKFtjdXJyZW50UG9pbnRzXSlcbiAgICBhcHBseVBhdGhBdHRyaWJ1dGVzKHBhdGgpXG4gIH1cblxuICBmdW5jdGlvbiB0cmFuc2l0aW9uRHJhdygpIHtcbiAgICB2YXIgY29udGFpbmVyID0gc2FmZVNlbGVjdChvYmplY3RDb250YWluZXIsICdnJywgJ2xhc3NvLWNvbnRhaW5lcicpXG4gICAgdmFyIHBhdGhzID0gY29udGFpbmVyLnNlbGVjdEFsbCgncGF0aFtpbnN0YW5jZT1cIicraW5zdGFuY2UrJ1wiXScpXG5cbiAgICAvLyB1cGRhdGVcbiAgICBwYXRocyA9IHBhdGhzLmRhdGEoYWxsUG9pbnRzKVxuXG4gICAgLy8gcmVtb3ZlIGV4Y2Vzc1xuICAgIHZhciBwRXhpdCA9IHBhdGhzLmV4aXQoKS5yZW1vdmUoKVxuICAgIC8vIGFkZCBuZWVkZWQgcGF0aHNcbiAgICB2YXIgcEVudGVyID0gcGF0aHMuZW50ZXIoKS5hcHBlbmQoJ3BhdGgnKVxuXG4gICAgLy8gbWVyZ2VcbiAgICBwYXRocyA9IHBhdGhzLm1lcmdlKHBFbnRlcilcbiAgICAudHJhbnNpdGlvbigpLmR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbilcbiAgICAuZWFzZShlYXNlRnVuYylcbiAgICBhcHBseVBhdGhBdHRyaWJ1dGVzKHBhdGhzKVxuXG4gIH1cblxuICBmdW5jdGlvbiBhcHBseVBhdGhBdHRyaWJ1dGVzKHBhdGgpIHtcbiAgICBwYXRoXG4gICAgLmF0dHIoXCJjbGFzc1wiLCBoeXBlbmF0ZShuYW1lc3BhY2UsIFwibGFzc28tcGF0aFwiKSlcbiAgICAuc3R5bGUoJ29wYWNpdHknLCBvcGFjaXR5KVxuICAgIC5hdHRyKCdmaWxsJywgY29sb3IpXG4gICAgLmF0dHIoXCJkXCIsIGxpbmUpXG4gICAgLmF0dHIoJ2luc3RhbmNlJywgaW5zdGFuY2UpXG4gICAgLnN0eWxlKFwic3Ryb2tlLWRhc2hhcnJheVwiLCBkYXNoQXJyYXkpXG4gICAgLmF0dHIoXCJzdHJva2VcIiwgc3Ryb2tlKVxuICAgIC5hdHRyKFwic3Ryb2tlLXdpZHRoXCIsIHN0cm9rZVdpZHRoKVxuICAgIC5zdHlsZSgnYW5pbWF0aW9uJywgJ2xhc3NvRGFzaCAnK2FuaW1hdGlvblJhdGUrJyBsaW5lYXInKVxuICAgIC5zdHlsZShcImFuaW1hdGlvbi1pdGVyYXRpb24tY291bnRcIiwgXCJpbmZpbml0ZVwiKVxuICB9XG5cbiAgZnVuY3Rpb24gZHJhZyhldmVudCkge1xuICAgIC8qXG4gICAgZWZmZWN0aXZlbHkgY3JlYXRlIGEgbW91c2UgZG93biBhbmQgbW92ZSBldmVudCAod2hpY2ggbm9ybWFsbHkgaXMgaW50ZXBlcmF0ZWRcbiAgICBhcyAnZHJhZycgYnkgdGhlIGJyb3dzZXIpIGJ5IGR5bmFtaWNhbGx5IGFkZGluZyAvIHJlbW92aW5nIHRoaXMgZXZlbnQgb25cbiAgICBtb3VzZSBkb3duIC8gbW91c2UgdXAuXG4gICAgKi9cblxuICAgIGlmIChldmVudENhdGNoZXIgIT0gdW5kZWZpbmVkKSB7ZXZlbnRDYXRjaGVyLmRpc3BhdGNoKGh5cGVuYXRlKG5hbWVzcGFjZSxcImRyYWdcIikpfVxuICAgIC8vIGQzLmRpc3BhdGNoKGh5cGVuYXRlKG5hbWVzcGFjZSxcImRyYWdcIikpXG5cbiAgICBpZiAoZXZlbnQud2hpY2ggIT0gMSkge3JldHVybn0gLy8gZW5zdXJlcyBsZWZ0IG1vdXNlIGJ1dHRvbiBzZXRcbiAgICBkMy5ldmVudCA9IGV2ZW50XG4gICAgdmFyIHB0ID0gZDMubW91c2Uob2JqZWN0Q29udGFpbmVyLm5vZGUoKSk7XG4gICAgdmFyIHB0ID0gZDMubW91c2Uoc3ZnLm5vZGUoKSk7XG5cbiAgICBpZiAoeFNjYWxlICE9IHVuZGVmaW5lZCkge3B0WzBdID0geFNjYWxlLmludmVydChwdFswXSl9XG4gICAgaWYgKHlTY2FsZSAhPSB1bmRlZmluZWQpIHtwdFsxXSA9IHlTY2FsZS5pbnZlcnQocHRbMV0pfVxuICAgIHB0WzBdID0gcHRbMF0gLSBjaGFydE9mZnNldFswXSAtIG9iamVjdHNPZmZzZXRbMF1cbiAgICBwdFsxXSA9IHB0WzFdIC0gY2hhcnRPZmZzZXRbMV0gLSBvYmplY3RzT2Zmc2V0WzFdXG5cbiAgICAvKiBpZiB3ZSBoYXZlIGEgcG9pbnQgYWxyZWFkeSwgdGVzdCBpZiBpdCBwYXNzZXMgYSBtaW5pbXVtIGRpc3RhbmNlIHRvIHByZXZlbnQgb3ZlcndoZWxtaW5nIHdpdGggdG9vIG1hbnkgdGljayBmdW5jdGlvbnMgKi9cbiAgICBpZiAoY3VycmVudFBvaW50cy5sZW5ndGgpIHtcbiAgICAgIHZhciBsYXN0UHQgPSBjdXJyZW50UG9pbnRzW2N1cnJlbnRQb2ludHMubGVuZ3RoIC0gMV1cbiAgICAgIHZhciBhID0gW3B0WzBdLCBwdFsxXV0sIGIgPSBbbGFzdFB0WzBdLCBsYXN0UHRbMV1dXG5cbiAgICAgIGlmICh4U2NhbGUpIHtiWzBdID0geFNjYWxlKGJbMF0pOyBhWzBdID0geFNjYWxlKGFbMF0pfVxuICAgICAgaWYgKHlTY2FsZSkge2JbMV0gPSB5U2NhbGUoYlsxXSk7IGFbMV0gPSB5U2NhbGUoYVsxXSl9XG5cbiAgICAgIHZhciBkaXN0ID0gZXVjbGlkZWFuRGlzdGFuY2UoYiwgYSlcbiAgICAgIGlmIChkaXN0ID4gdGlja0Rpc3RhbmNlKSB7IHRpY2socHQpIH1cbiAgICB9XG4gICAgZWxzZSB7IHRpY2socHQpIH1cbiAgfVxuXG5cbiAgZnVuY3Rpb24gdGljayAocHQpIHtcbiAgICAvKlxuICAgIElmIGEgcG9pbnQgaXMgcHJvdmlkZWQgdXBkYXRlIGRhdGEgYW5kIG9iamVjdHMuXG4gICAgT3RoZXJ3aXNlIGp1c3QgY2FsbCBvbiBkYXRhIHdlIGFscmVhZHkgaGF2ZS5cblxuICAgIFdoeSBsaWtlIHRoaXM/OlxuICAgIDEuIGN1cnJlbnRQb2ludHMgaXMgY3VycmVudCBwb2ludHMgdG8gYWxsb3cgZGlzanVuY3QgbGFzc29zLCBjdXJyZW50UG9pbnRzIGlzIG9ubHkgcHVzaGVkIHRvXG4gICAgYWxsUG9pbnRzIGFmdGVyIG1vdXNldXAuXG4gICAgMi4gdG8gYWxsb3cgcmVuZGVyIG9mIG9iamVjdHMgaW4gdGhlIGxhc3NvIGNsYXNzIC8gdXBkYXRpbmcgdGhlIGRhdGEgbGlzdFxuICAgIGp1c3QgYnkgdG9nZ2xpbmcgdGhlIGJ1dHRvblxuICAgICovXG5cbiAgICBpZiAocHQgIT0gdW5kZWZpbmVkKSB7XG4gICAgICBjdXJyZW50UG9pbnRzLnB1c2gocHQpO1xuICAgICAgcGF0aC5hdHRyKFwiZFwiLCBsaW5lKTtcbiAgICAgIGlmIChjdXJyZW50UG9pbnRzLmxlbmd0aCA8IDMpIHtyZXR1cm59IC8vIG5lZWQgYXQgbGVhc3QgMyBwb2ludHMgdG8gZGV0ZWN0IGFueXRoaW5nLlxuICAgICAgZGV0ZWN0KGFsbFBvaW50cy5jb25jYXQoW2N1cnJlbnRQb2ludHNdKSlcbiAgICB9IGVsc2Uge1xuICAgICAgZGV0ZWN0KGFsbFBvaW50cylcbiAgICB9XG4gIH1cblxuXG4gIGZ1bmN0aW9uIGRldGVjdChsYXNzb3MpIHtcbiAgICBpZiAobGFzc29zID09IHVuZGVmaW5lZCkge2xhc3NvcyA9IGFsbFBvaW50c31cbiAgICBvYmplY3RDb250YWluZXIuc2VsZWN0QWxsKG9iamVjdENsYXNzKS5lYWNoKGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgdmFyIGN1cnJlbnQgPSBkMy5zZWxlY3QodGhpcyksXG5cbiAgICAgIGJveCA9IGN1cnJlbnQuYWJzb2x1dGVQb3NpdGlvbigpLFxuICAgICAgLy8gYm94ID0gY3VycmVudC5yZWxhdGl2ZVBvc2l0aW9uVG8ob2JqZWN0Q29udGFpbmVyLm5vZGUoKSksXG5cbiAgICAgIGJveFB0cyA9IFtcbiAgICAgICAgW1xuICAgICAgICAgIGJveC5sZWZ0IC0gY2hhcnRPZmZzZXRbMF0gLSBvYmplY3RzT2Zmc2V0WzBdLFxuICAgICAgICAgIGJveC50b3AgLSBjaGFydE9mZnNldFsxXSAtIG9iamVjdHNPZmZzZXRbMV1cbiAgICAgICAgXSxcbiAgICAgICAgW1xuICAgICAgICAgIGJveC5yaWdodCAtIGNoYXJ0T2Zmc2V0WzBdIC0gb2JqZWN0c09mZnNldFswXSxcbiAgICAgICAgICBib3gudG9wIC0gY2hhcnRPZmZzZXRbMV0gLSBvYmplY3RzT2Zmc2V0WzFdXG4gICAgICAgIF0sXG4gICAgICAgIFtcbiAgICAgICAgICBib3gubGVmdCAtIGNoYXJ0T2Zmc2V0WzBdIC0gb2JqZWN0c09mZnNldFswXSxcbiAgICAgICAgICBib3guYm90dG9tIC0gY2hhcnRPZmZzZXRbMV0gLSBvYmplY3RzT2Zmc2V0WzFdXG4gICAgICAgIF0sXG4gICAgICAgIFtcbiAgICAgICAgICBib3gucmlnaHQgLSBjaGFydE9mZnNldFswXSAtIG9iamVjdHNPZmZzZXRbMF0sXG4gICAgICAgICAgYm94LmJvdHRvbSAtIGNoYXJ0T2Zmc2V0WzFdIC0gb2JqZWN0c09mZnNldFsxXVxuICAgICAgICBdXG4gICAgICBdXG5cbiAgICAgIGlmICh4U2NhbGUgIT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGJveFB0c1swXVswXSA9IHhTY2FsZS5pbnZlcnQoYm94UHRzWzBdWzBdKVxuICAgICAgICBib3hQdHNbMV1bMF0gPSB4U2NhbGUuaW52ZXJ0KGJveFB0c1sxXVswXSlcbiAgICAgICAgYm94UHRzWzJdWzBdID0geFNjYWxlLmludmVydChib3hQdHNbMl1bMF0pXG4gICAgICAgIGJveFB0c1szXVswXSA9IHhTY2FsZS5pbnZlcnQoYm94UHRzWzNdWzBdKVxuICAgICAgfVxuICAgICAgaWYgKHlTY2FsZSAhPSB1bmRlZmluZWQpIHtcbiAgICAgICAgYm94UHRzWzBdWzFdID0geVNjYWxlLmludmVydChib3hQdHNbMF1bMV0pXG4gICAgICAgIGJveFB0c1sxXVsxXSA9IHlTY2FsZS5pbnZlcnQoYm94UHRzWzFdWzFdKVxuICAgICAgICBib3hQdHNbMl1bMV0gPSB5U2NhbGUuaW52ZXJ0KGJveFB0c1syXVsxXSlcbiAgICAgICAgYm94UHRzWzNdWzFdID0geVNjYWxlLmludmVydChib3hQdHNbM11bMV0pXG4gICAgICB9XG5cblxuICAgICAgLypcbiAgICAgIGZsYWcgbmVlZGVkIGFzIHdlIGhhdmUgdG8gdGVzdCBtdWx0aXBsZSBsYXNzbyBzZWdtZW50cywgYW5kIGlmIHRoZSBwb2ludFxuICAgICAgaXMgbm90IGluIG9uZSBzZWdtZW50LCBpdCBkb2VzIG5vdCBtZWFuIGl0IGlzIG5vdCBpbiBhbnlcbiAgICAgICovXG4gICAgICB2YXIgaW5BbnlMYXNzb1EgPSBmYWxzZTtcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGFzc29zLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciBsYXNzb1BvaW50cyA9IGxhc3Nvc1tpXVxuICAgICAgICAvLyAubWFwKGZ1bmN0aW9uKHB0KXtcbiAgICAgICAgLy8gICB2YXIgeCwgeSA9IHB0XG4gICAgICAgIC8vICAgaWYgKHhTY2FsZSE9dW5kZWZpbmVkKSB7eCA9IHhTY2FsZSh4KX1cbiAgICAgICAgLy8gICBpZiAoeVNjYWxlIT11bmRlZmluZWQpIHt5ID0geVNjYWxlKHkpfVxuICAgICAgICAvLyAgIHJldHVybiBbeCwgeV1cbiAgICAgICAgLy8gfSlcbiAgICAgICAgdmFyIGJveEluTGFzc29RID0gYm94UHRzLmV2ZXJ5KGNvb3JkID0+IGQzLnBvbHlnb25Db250YWlucyhsYXNzb1BvaW50cywgY29vcmQpKVxuXG4gICAgICAgIGlmIChib3hJbkxhc3NvUSkgeyBpbkFueUxhc3NvUSA9IHRydWU7IH0gLy8gb25seSB1cGRhdGUgZmxhZyBpbiB0aGUgcG9zaXRpdmUgY2FzZS5cbiAgICAgIH1cblxuICAgICAgY3VycmVudC5jbGFzc2VkKCdpbi1sYXNzbycsIGluQW55TGFzc29RKVxuICAgICAgY3VycmVudC5jbGFzc2VkKCdpbi1sYXNzby0nK2luc3RhbmNlLCBpbkFueUxhc3NvUSlcbiAgICB9KVxuXG4gICAgdXBkYXRlT2JqZWN0cygpXG4gICAgcmV0dXJuIG9iamVjdENvbnRhaW5lci5zZWxlY3RBbGwoJy5pbi1sYXNzby0nK2luc3RhbmNlKVxuICB9XG5cblxuXG4gIGZ1bmN0aW9uIHVwZGF0ZU9iamVjdHMoKSB7XG4gICAgb2JqZWN0Q29udGFpbmVyLnNlbGVjdEFsbChvYmplY3RDbGFzcykuZWFjaChmdW5jdGlvbihkLCBpKSB7XG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgICAgYXBwbHlPYmplY3RBdHRyaWJ1dGVzKHQsIHQuY2xhc3NlZCgnaW4tbGFzc28nKSlcbiAgICB9KVxuICB9XG5cbiAgZnVuY3Rpb24gYXBwbHlPYmplY3RBdHRyaWJ1dGVzKG9iaiwgc2V0USkge1xuICAgIHZhclxuICAgIHByZUxhc3NvRmlsbCA9IG9iai5hdHRyKCdfcHJlX2xhc3NvX2ZpbGwnKSxcbiAgICBwcmVMYXNzb1N0cm9rZSA9IG9iai5hdHRyKCdfcHJlX2xhc3NvX3N0cm9rZScpLFxuICAgIHByZUxhc3NvU3Ryb2tlV2lkdGggPSBvYmouYXR0cignX3ByZV9sYXNzb19zdHJva2Utd2lkdGgnKVxuXG4gICAgaWYgKHNldFEpIHtcbiAgICAgIG9iai5jbGFzc2VkKFwiaW4tbGFzc29cIiwgdHJ1ZSlcbiAgICAgIG9iai5jbGFzc2VkKCdpbi1sYXNzby0nK2luc3RhbmNlLCB0cnVlKVxuICAgICAgaWYgKHByZUxhc3NvRmlsbCA9PSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ19wcmVfbGFzc29fZmlsbCcsIG9iai5hdHRyKCdmaWxsJykpIH1cbiAgICAgIGlmIChwcmVMYXNzb1N0cm9rZSA9PSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ19wcmVfbGFzc29fc3Ryb2tlJywgb2JqLmF0dHIoJ3N0cm9rZScpKSB9XG4gICAgICBpZiAocHJlTGFzc29TdHJva2VXaWR0aCA9PSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ19wcmVfbGFzc29fc3Ryb2tlLXdpZHRoJywgb2JqLmF0dHIoJ3N0cm9rZS13aWR0aCcpKSB9XG5cbiAgICAgIG9ialxuICAgICAgLy9CVUc6IHdoZW4gLnJhaXNlKClcbiAgICAgIC5hdHRyKCdmaWxsJywgbGFzc29lZEZpbGwpXG4gICAgICAuYXR0cignc3Ryb2tlJywgbGFzc29lZFN0cm9rZSlcbiAgICAgIC5hdHRyKCdzdG9rZS13aWR0aCcsIGxhc3NvZWRTdHJva2VXaWR0aClcblxuICAgIH0gZWxzZSB7XG4gICAgICBvYmouY2xhc3NlZChcImluLWxhc3NvXCIsIGZhbHNlKVxuICAgICAgb2JqLmNsYXNzZWQoJ2luLWxhc3NvLScraW5zdGFuY2UsIGZhbHNlKVxuICAgICAgaWYgKHByZUxhc3NvRmlsbCAhPSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ2ZpbGwnLCBwcmVMYXNzb0ZpbGwpIH1cbiAgICAgIGlmIChwcmVMYXNzb1N0cm9rZSAhPSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ3N0cm9rZScsIHByZUxhc3NvU3Ryb2tlKSB9XG4gICAgICBpZiAocHJlTGFzc29TdHJva2VXaWR0aCAhPSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ3N0cm9rZS13aWR0aCcsIHByZUxhc3NvU3Ryb2tlV2lkdGgpIH1cbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBrZXlGcmFtZXMoKSB7XG4gICAgdmFyIHN0eWxlID1cbiAgICBkMy5zZWxlY3QoXCJodG1sXCIpLnNlbGVjdCgnc3R5bGUuJytoeXBlbmF0ZShuYW1lc3BhY2UsXCJsYXNzby1kYXNoXCIpKVxuICAgIGlmIChzdHlsZS5lbXB0eSgpKSB7XG4gICAgICBkMy5zZWxlY3QoXCJodG1sXCIpLmFwcGVuZCgnc3R5bGUnKVxuICAgICAgLmNsYXNzZWQoaHlwZW5hdGUobmFtZXNwYWNlLFwibGFzc28tZGFzaFwiKSwgdHJ1ZSlcbiAgICAgIC5odG1sKFwiQGtleWZyYW1lcyBsYXNzb0Rhc2gge3RvIHsgc3Ryb2tlLWRhc2hvZmZzZXQ6IDEwMDA7fX1cIilcbiAgICB9XG5cbiAgfVxuICByZXR1cm4gbGFzc29cbn1cbiIsImltcG9ydCB7Z2V0Q29udGFpbmluZ1NWR30gZnJvbSBcIi4vaGVscGVyc1wiO1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEQzIEVYVEVOU0lPTlMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qKlxuKiBSZWN1cnNpdmVseSBhc2NlbmRzIHBhcmVudHMgb2Ygc2VsZWN0aW9uIHVudGlsIGl0IGZpbmRzIGFuIHN2ZyB0YWdcbiogQGZ1bmN0aW9uIGQzLnNlbGVjdGlvbi50aGlzU1ZHXG4qIEBhdWdtZW50cyBkMy5zZWxlY3Rpb25cbiogQHJldHVybnMge0VsZW1lbnR9IHdoaWNoIGlzIHRoZSBzdmcgdGFnLCBub3QgdGhlIGQzIHNlbGVjdGlvbiBvZiB0aGF0IHRhZ1xuKi9cbmQzLnNlbGVjdGlvbi5wcm90b3R5cGUudGhpc1NWRyA9IGZ1bmN0aW9uKCkgeyByZXR1cm4gZ2V0Q29udGFpbmluZ1NWRyh0aGlzLm5vZGUoKSk7IH1cblxuXG4vKipcbiogSGVscGVyIGZvciBnZXR0aW5nIGFic29sdXRlIHBvc2l0aW9uIG9mIHRoZSBtb3VzZVxuKiBAZnVuY3Rpb24gZDMubW91c2UuYWJzb2x1dGVcbiogQGF1Z21lbnRzIGQzLm1vdXNlXG4qIEByZXR1cm5zIHtudW1iZXJbXX0gW3gsIHldIGFzIHRoZXkgcmVsYXRlIHRvIGBodG1sYCBub3QgdG8gbG9jYWwgc2NvcGUuXG4qL1xuZDMubW91c2UuYWJzb2x1dGUgPSBmdW5jdGlvbigpIHtcbiAgdmFyIGh0bWwgPSBkMy5zZWxlY3QoJ2h0bWwnKS5ub2RlKClcbiAgdmFyIFt4LCB5XSA9IHRoaXMoaHRtbClcbiAgcmV0dXJuIFt4LCB5XVxufVxuXG5cbi8qKlxuKiBHZXRzIHBvc2l0aW9uIG9mIHRoZSBzZWxlY3Rpb24gaW4gcmVsYXRpb24gdG8gdGhlIGNvbnRhaW5pbmcgc3ZnXG4qIEBzZWV7QGxpbmsgZ2V0Q29udGFpbmluZ1NWR31cbiogQGZ1bmN0aW9uIGQzLnNlbGVjdGlvbi5hYnNvbHV0ZVBvc2l0aW9uXG4qIEBhdWdtZW50cyBkMy5zZWxlY3Rpb25cbiogQHJldHVybnMge09iamVjdH0gd2l0aCBzdHJ1Y3R1cmUgc2ltaWxhciB0byBnZXRCb3VuZGluZ0NsaWVudFJlY3QsIGUuZy5cbiogdG9wLCBsZWZ0LCBib3R0b20sIHJpZ2h0LCBoZWlnaHQsIHdpZHRoXG4qL1xuZDMuc2VsZWN0aW9uLnByb3RvdHlwZS5hYnNvbHV0ZVBvc2l0aW9uID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGVsZW1lbnQgPSB0aGlzLm5vZGUoKTtcbiAgICB2YXIgZWxlbWVudFBvc2l0aW9uID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICB2YXIgY29udGFpbmVyU1ZHID0gZ2V0Q29udGFpbmluZ1NWRyhlbGVtZW50KVxuICAgIHZhciBzdmdQb3NpdGlvbiA9IGNvbnRhaW5lclNWRy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblxuICAgIHJldHVybiB7XG4gICAgICAgIHRvcDogICAgZWxlbWVudFBvc2l0aW9uLnRvcCAgICAtIHN2Z1Bvc2l0aW9uLnRvcCxcbiAgICAgICAgbGVmdDogICBlbGVtZW50UG9zaXRpb24ubGVmdCAgIC0gc3ZnUG9zaXRpb24ubGVmdCxcbiAgICAgICAgYm90dG9tOiBlbGVtZW50UG9zaXRpb24uYm90dG9tIC0gc3ZnUG9zaXRpb24udG9wLFxuICAgICAgICByaWdodDogIGVsZW1lbnRQb3NpdGlvbi5yaWdodCAgLSBzdmdQb3NpdGlvbi5sZWZ0LFxuICAgICAgICBoZWlnaHQ6IGVsZW1lbnRQb3NpdGlvbi5oZWlnaHQsXG4gICAgICAgIHdpZHRoOiAgZWxlbWVudFBvc2l0aW9uLndpZHRoXG4gICAgfTtcblxufVxuXG5cbmQzLnNlbGVjdGlvbi5wcm90b3R5cGUucmVsYXRpdmVQb3NpdGlvblRvID0gZnVuY3Rpb24oY29udGFpbmVyKSB7XG4gICAgdmFyIGVsZW1lbnQgPSB0aGlzLm5vZGUoKTtcbiAgICB2YXIgZWxlbWVudFBvc2l0aW9uID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICB2YXIgY29udGFpbmVyU1ZHID0gY29udGFpbmVyXG4gICAgdmFyIHN2Z1Bvc2l0aW9uID0gY29udGFpbmVyU1ZHLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgICAgdG9wOiAgICBlbGVtZW50UG9zaXRpb24udG9wICAgIC0gc3ZnUG9zaXRpb24udG9wLFxuICAgICAgICBsZWZ0OiAgIGVsZW1lbnRQb3NpdGlvbi5sZWZ0ICAgLSBzdmdQb3NpdGlvbi5sZWZ0LFxuICAgICAgICBib3R0b206IGVsZW1lbnRQb3NpdGlvbi5ib3R0b20gLSBzdmdQb3NpdGlvbi50b3AsXG4gICAgICAgIHJpZ2h0OiAgZWxlbWVudFBvc2l0aW9uLnJpZ2h0ICAtIHN2Z1Bvc2l0aW9uLmxlZnQsXG4gICAgICAgIGhlaWdodDogZWxlbWVudFBvc2l0aW9uLmhlaWdodCxcbiAgICAgICAgd2lkdGg6ICBlbGVtZW50UG9zaXRpb24ud2lkdGhcbiAgICB9O1xuXG59XG4iLCIvLyBJbXBvcnQgc3R5bGVzIChhdXRvbWF0aWNhbGx5IGluamVjdCBpbnRvIDxoZWFkPikuXG4vLyBpbXBvcnQgJy4uL3N0eWxlcy9tYWluLmNzcyc7XG5pbXBvcnQge2F4aXN9IGZyb20gJy4vbW9kdWxlcy9heGlzJztcbmltcG9ydCB7YmFyfSBmcm9tICcuL21vZHVsZXMvYmFyJztcbmltcG9ydCB7YnViYmxlSGVhdG1hcH0gZnJvbSAnLi9tb2R1bGVzL2J1YmJsZS1oZWF0bWFwJztcbmltcG9ydCB7aGVhdG1hcH0gZnJvbSAnLi9tb2R1bGVzL2hlYXRtYXAnO1xuaW1wb3J0IHtib3h3aGlza2VyfSBmcm9tICcuL21vZHVsZXMvYm94LXdoaXNrZXInO1xuaW1wb3J0IHtjb2xvckZ1bmN0aW9ufSBmcm9tICcuL21vZHVsZXMvY29sb3ItZnVuY3Rpb24nO1xuaW1wb3J0IHtkYXRhdG9nZ2xlfSBmcm9tICcuL21vZHVsZXMvZGF0YS10b2dnbGUnO1xuaW1wb3J0IHtncm91cGluZ1NwYWNlcn0gZnJvbSAnLi9tb2R1bGVzL2dyb3VwaW5nLXNwYWNlcic7XG5pbXBvcnQge3Rvb2x0aXB9IGZyb20gJy4vbW9kdWxlcy90b29sdGlwJztcbmltcG9ydCB7c2NhdHRlcn0gZnJvbSAnLi9tb2R1bGVzL3NjYXR0ZXInO1xuaW1wb3J0IHtwbG90Wm9vbX0gZnJvbSAnLi9tb2R1bGVzL3Bsb3Qtem9vbSc7XG5pbXBvcnQge211bHRpUGxvdFpvb219IGZyb20gJy4vbW9kdWxlcy9tdWx0aS1wbG90LXpvb20nO1xuaW1wb3J0IHt2aW9saW59IGZyb20gJy4vbW9kdWxlcy92aW9saW4nO1xuaW1wb3J0IHtudW1lcmljTGVnZW5kfSBmcm9tICcuL21vZHVsZXMvbnVtZXJpYy1sZWdlbmQnO1xuaW1wb3J0IHtjYXRlZ29yaWNMZWdlbmR9IGZyb20gJy4vbW9kdWxlcy9jYXRlZ29yaWNhbC1sZWdlbmQnO1xuaW1wb3J0IHtsYXNzb30gZnJvbSAnLi9tb2R1bGVzL2xhc3NvJztcbmltcG9ydCB7bGFzc29XaWRnZXR9IGZyb20gJy4vbW9kdWxlcy9sYXNzby13aWRnZXQnO1xuaW1wb3J0IHtzZWxlY3RGaWx0ZXJ9IGZyb20gJy4vbW9kdWxlcy9zZWxlY3QtZmlsdGVyJztcbmltcG9ydCB7dXBzZXR9IGZyb20gJy4vbW9kdWxlcy91cHNldCc7XG5pbXBvcnQge2ZpbHRlclRhYmxlfSBmcm9tICcuL21vZHVsZXMvZmlsdGVyLXRhYmxlJztcblxuaW1wb3J0IHt1bmlxdWVFbGVtZW50cywgZ2V0VHJhbnNsYXRpb24sIG1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2UsIHRpY2tSYW5nZSxcbnF1YXJ0aWxlcywgZXh0cmFjdFZpb2xpblZhbHVlcywgaHlwZW5hdGUsIHJvdW5kLCBnZXRDb250YWluaW5nU1ZHLFxuaW50ZXJwb2xhdGVDb2xvcnMsIHRydW5jYXRlVGV4dCwgc2FmZVNlbGVjdH0gZnJvbSAnLi9tb2R1bGVzL2hlbHBlcnMnO1xuXG5pbXBvcnQge1xuICBhbGwsIHRhbGx5LCBoYXNRLCBmaXJzdCwgbGFzdCwgdG90YWwsIHVuaXF1ZSwgZ2V0LCBsaXN0T2ZMaXN0c1EsXG4gIGN1dCwgZ3JvdXBCeSwgYXJyYXlFcXVhbHMsIGVsZW1lbnRzQXRMZXZlbHMsIG51bWJlck9mRWxlbWVudHMsXG4gIGZsYXR0ZW4sIHdoaWNoQmluXG59IGZyb20gJy4vbW9kdWxlcy9hcnJheS1mdW5jdGlvbnMnO1xuXG5cbmltcG9ydCB7XG4gIHNldHVwU3RhbmRhcmRDaGFydENvbnRhaW5lcnMsIGxvZyBhcyBteUxvZywgd2FybiwgaW5mbywgZXJyb3IsXG4gIGNvbnNvbGVHcm91cCwgY29uc29sZUdyb3VwRW5kLCByZXNpemVEZWJvdW5jZVxufSBmcm9tICcuL21vZHVsZXMvdXRpbHMnO1xuXG4vLyAvKiogQG1vZHVsZSBkM3NtICovXG52YXIgZDNzbSA9IHt9O1xuZDNzbS5heGlzID0gYXhpcztcbmQzc20uYmFyID0gYmFyO1xuZDNzbS5idWJibGVIZWF0bWFwID0gYnViYmxlSGVhdG1hcDtcbmQzc20uaGVhdG1hcCA9IGhlYXRtYXA7XG5kM3NtLmJveHdoaXNrZXIgPSBib3h3aGlza2VyO1xuZDNzbS5jb2xvckZ1bmN0aW9uID0gY29sb3JGdW5jdGlvbjtcbmQzc20uZGF0YXRvZ2dsZSA9IGRhdGF0b2dnbGU7XG5kM3NtLmdyb3VwaW5nU3BhY2VyID0gZ3JvdXBpbmdTcGFjZXI7XG5kM3NtLnRvb2x0aXAgPSB0b29sdGlwO1xuZDNzbS5zY2F0dGVyID0gc2NhdHRlcjtcbmQzc20ucGxvdFpvb20gPSBwbG90Wm9vbTtcbmQzc20ubXVsdGlQbG90Wm9vbSA9IG11bHRpUGxvdFpvb207XG5kM3NtLnZpb2xpbiA9IHZpb2xpbjtcbmQzc20ubnVtZXJpY0xlZ2VuZCA9IG51bWVyaWNMZWdlbmQ7XG5kM3NtLmNhdGVnb3JpY0xlZ2VuZCA9IGNhdGVnb3JpY0xlZ2VuZDtcbmQzc20ubGFzc28gPSBsYXNzbztcbmQzc20ubGFzc29XaWRnZXQgPSBsYXNzb1dpZGdldDtcbmQzc20uc2VsZWN0RmlsdGVyID0gc2VsZWN0RmlsdGVyO1xuZDNzbS51cHNldCA9IHVwc2V0O1xuZDNzbS5maWx0ZXJUYWJsZSA9IGZpbHRlclRhYmxlO1xuXG5kM3NtLnVuaXF1ZUVsZW1lbnRzID0gdW5pcXVlRWxlbWVudHM7XG5kM3NtLmdldFRyYW5zbGF0aW9uID0gZ2V0VHJhbnNsYXRpb247XG5kM3NtLm1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2UgPSBtb2RpZnlIZXhpZGVjaW1hbENvbG9yTHVtaW5hbmNlO1xuZDNzbS50aWNrUmFuZ2UgPSB0aWNrUmFuZ2U7XG5kM3NtLnF1YXJ0aWxlcyA9IHF1YXJ0aWxlcztcbmQzc20uZXh0cmFjdFZpb2xpblZhbHVlcyA9IGV4dHJhY3RWaW9saW5WYWx1ZXM7XG5kM3NtLmh5cGVuYXRlID0gaHlwZW5hdGU7XG5kM3NtLnJvdW5kID0gcm91bmQ7XG5kM3NtLmdldENvbnRhaW5pbmdTVkcgPSBnZXRDb250YWluaW5nU1ZHO1xuZDNzbS5pbnRlcnBvbGF0ZUNvbG9ycyA9IGludGVycG9sYXRlQ29sb3JzO1xuZDNzbS50cnVuY2F0ZVRleHQgPSB0cnVuY2F0ZVRleHQ7XG5kM3NtLnNhZmVTZWxlY3QgPSBzYWZlU2VsZWN0O1xuXG5kM3NtLndoaWNoQmluID0gd2hpY2hCaW47XG5kM3NtLnVuaXF1ZSA9IHVuaXF1ZTtcbmQzc20uZmxhdHRlbiA9IGZsYXR0ZW47XG5cbmQzc20uc2V0dXBTdGFuZGFyZENoYXJ0Q29udGFpbmVycyA9IHNldHVwU3RhbmRhcmRDaGFydENvbnRhaW5lcnM7XG5kM3NtLmxvZyA9IG15TG9nO1xuZDNzbS53YXJuID0gd2FybjtcbmQzc20uaW5mbyA9IGluZm87XG5kM3NtLmVycm9yID0gZXJyb3I7XG5kM3NtLmNvbnNvbGVHcm91cCA9IGNvbnNvbGVHcm91cDtcbmQzc20uY29uc29sZUdyb3VwRW5kID0gY29uc29sZUdyb3VwRW5kO1xuZDNzbS5yZXNpemVEZWJvdW5jZSA9IHJlc2l6ZURlYm91bmNlO1xuXG5kM3NtLmRlYnVnUSA9IGZhbHNlXG5cblxuXG4vLyBJbXBvcnQgYSBsb2dnZXIgZm9yIGVhc2llciBkZWJ1Z2dpbmdcbi8vIGltcG9ydCBkZWJ1ZyBmcm9tICdkZWJ1Zyc7XG4vLyBjb25zdCBsb2cgPSBkZWJ1ZygnYXBwOmxvZycpO1xuXG4vLyBUaGUgbG9nZ2VyIHNob3VsZCBvbmx5IGJlIGRpc2FibGVkIGlmIHdlJ3JlIG5vdCBpbiBwcm9kdWN0aW9uLlxuLy8gaWYgKEVOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4vLyAgIC8vIEVuYWJsZSB0aGUgbG9nZ2VyLlxuLy8gICBkZWJ1Zy5lbmFibGUoJyonKTtcbi8vICAgbG9nKCdMb2dnaW5nIGlzIGVuYWJsZWQhJyk7XG4vL1xuLy8gICAvLyBFbmFibGUgTGl2ZVJlbG9hZFxuLy8gICBkb2N1bWVudC53cml0ZShcbi8vICAgICAnPHNjcmlwdCBzcmM9XCJodHRwOi8vJ1xuLy8gICAgICsgKGxvY2F0aW9uLmhvc3QgfHwgJ2xvY2FsaG9zdCcpLnNwbGl0KCc6JylbMF1cbi8vICAgICArICc6MzU3MjkvbGl2ZXJlbG9hZC5qcz9zbmlwdmVyPTFcIj48Lydcbi8vICAgICArICdzY3JpcHQ+J1xuLy8gICApO1xuLy8gfSBlbHNlIHtcbi8vICAgZGVidWcuZGlzYWJsZSgpO1xuLy8gfVxuXG53aW5kb3cuZDNzbSA9IGQzc207XG5cbmV4cG9ydCBkZWZhdWx0IGQzc21cbiIsImltcG9ydCB7XG4gIGh5cGVuYXRlLCBzYWZlU2VsZWN0LCBleHRyYWN0VmlvbGluVmFsdWVzLFxuICB0aWNrUmFuZ2UsIG1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2UsIHRydW5jYXRlVGV4dCxcbiAgdHJ1bmNhdGVTdHJpbmcsXG4gIHJvdW5kXG59IGZyb20gJy4vaGVscGVycyc7XG5pbXBvcnQge3NldHVwQ29udGFpbmVyLCBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0LCBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7dW5pcXVlLCBoYXNRLCBmbGF0dGVufSBmcm9tICcuL2FycmF5LWZ1bmN0aW9ucyc7XG5pbXBvcnQge2dyb3VwaW5nU3BhY2VyfSBmcm9tICcuL2dyb3VwaW5nLXNwYWNlcic7XG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBWElTICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG4vKipcbiAqIENyZWF0ZXMgYW4gYXhpc1xuICpcbiAqIHtAbGluayBodHRwczovL3N1bW5ldXJvbi5naXRsYWIuaW8vZDNzbS9kZW1vcy9heGVzL2luZGV4Lmh0bWwgRGVtb31cbiAqIEBjb25zdHJ1Y3RvciBheGlzXG4gKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gc2VsZWN0aW9uXG4gKiBAbmFtZXNwYWNlIGF4aXNcbiAqIEByZXR1cm5zIHtmdW5jdGlvbn0gYXhpc1xuICovXG5leHBvcnQgZnVuY3Rpb24gYXhpcyAoIHNlbGVjdGlvbiApIHtcbiAgdmFyXG4gIC8qKlxuICAqIFRoZSBvcmllbnRhdGlvbiBvZiB0aGUgYXhpc1xuICAqIChzZWUge0BsaW5rIGF4aXMjb3JpZW50fSlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW29yaWVudD0nYm90dG9tJ11cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb3JpZW50ID0gJ2JvdHRvbScsICAgICAgIC8vIGRpcmVjdGlvbiBvZiB0aGUgYXhpc1xuXG4gIC8qKlxuICAqIEFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBheGlzIGluXG4gICogKHNlZSB7QGxpbmsgYXhpcyNzcGFjZVh9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VYPTBdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlWD0wLFxuICAvKipcbiAgKiBBbW91bnQgb2YgdmVydGljYWwgc3BhY2UgKGluIHBpeGVscykgYXZhaWJsZSB0byByZW5kZXIgdGhlIGF4aXMgaW5cbiAgKiAoc2VlIHtAbGluayBheGlzLnNwYWNlWX0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVk9MF1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VZPTAsXG5cblxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0byBhbGxvdyBheGlzIHRvIHJlbmRlciBlbGVtZW50cyBwYXNzIHRoZSBtYWluIHNwYXRpYWwgZGltZW5zaW9uXG4gICogZ2l2ZW4gdGhlIG9yaWVudGF0aW9uIChzZWUge0BsaW5rIGF4aXMjb3JpZW50fSksIHdoZXJlIHtAbGluayBheGlzI29yaWVudH09XCJib3R0b21cIiBvciB7QGxpbmsgYXhpcyNvcmllbnR9PVwidG9wXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGF4aXMjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGF4aXMjb3JpZW50fT1cImxlZnRcIiBvciB7QGxpbmsgYXhpcyNvcmllbnR9PVwicmlnaHRcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgYXhpcyNzcGFjZVl9XG4gICogQHBhcmFtIHtib29sZWFufSBbb3ZlcmZsb3dRPWZhbHNlXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvdmVyZmxvd1EgPSBmYWxzZSwgICAgLy8gd2hldGhlciBvciBub3QgdG8gYWxsb3cgb3ZlcmZsb3dcbiAgLyoqXG4gICogV2hldGhlciBvciBub3QgdGhlIGF4aXMgbGFiZWxzIGFyZSBmb3IgY2F0ZWdvcmljYWwgZGF0YS4gSWYgZmFsc2UsXG4gICogd2lsbCB1c2Uge0BsaW5rIGF4aXMjc2NhbGV9IHRvIHBvc2l0aW9uIHRpY2tzLlxuICAqIEBwYXJhbSB7Ym9vbGVhbn0gW2NhdGVnb3JpY2FsUT1mYWxzZV1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgY2F0ZWdvcmljYWxRID0gZmFsc2UsIC8vIHdoZXRoZXIgb3Igbm90IHRoZSBheGlzIGlzIHNob3dpbmcgdmFsdWVzIG9yIGdyb3Vwc1xuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0aGUgYXhpcyB0aWNrcyBzaG91bGQgaGF2ZSBndWlkZWxpbmVzXG4gICogQHBhcmFtIHtib29sZWFufSBbY2F0ZWdvcmljYWxRPWZhbHNlXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBndWlkZUxpbmVzUSA9IGZhbHNlLCAgICAvLyB3aGV0aGVyIG9yIG5vdCB0byBhbGxvdyBvdmVyZmxvd1xuXG5cbiAgLyoqXG4gICogSG93IHRvIGdyb3VwIHRoZSB0aWNrIGxhYmVsc1xuICAqIEBwYXJhbSB7QXJyYXlbXX0gW2dyb3VwaW5nPXVuZGVmaW5lZF0gbGlzdCBvZiBwdXRhdGl2ZWx5IG90aGVyIGxpc3RzLCB3aGljaCBzaG91bGQgY29ycmVzcG9uZCB0byB0aWNrTGFiZWxzXG4gICogd2lsbCBzcGFjZSB0aWNrIGxhYmVscyBpbiBuZXN0ZWQgbGlzdHMgY2xvc2VyIHRvZ2V0aGVyIHRoYW4gb3V0ZXIgbGlzdHNcbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZ3JvdXBpbmcsXG5cbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCBub24tY2F0ZWdvcmlhbCAoc2VlIHtAbGluayBheGlzI2NhdGVnb3JpY2FsUX0pIHRpY2tzIHNob3VsZCBiZSBzcGFjZWRcbiAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbc2NhbGU9ZDMuc2NhbGVMaW5lYXJdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG5cbiAgc2NhbGUgPSBkMy5zY2FsZUxpbmVhcigpLFxuICAvKipcbiAgKiBUaGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgc2NhbGUgKHNlZSB7QGxpbmsgYXhpcyNzY2FsZX0pXG4gICogQHBhcmFtIHtkMy5zY2FsZX0gW3NjYWxlPWQzLnNjYWxlTGluZWFyXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkb21haW5QYWRkaW5nID0gMC41LFxuXG5cbiAgLyoqXG4gICogRGVmYXVsdCBzcGFjZSBmb3IgdGhlIHNwYWNlciAocGVyY2VudGFnZSkgb2YgbWFpbiBkaW1lbnNpb24gZ2l2ZW4gdGhlIG9yaWVudGF0aW9uXG4gICogKHNlZSB7QGxpbmsgYXhpcyNvcmllbnR9KSwgd2hlcmUge0BsaW5rIGF4aXMjb3JpZW50fT1cImJvdHRvbVwiIG9yIHtAbGluayBheGlzI29yaWVudH09XCJ0b3BcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgYXhpcyNzcGFjZVh9IGFuZCB3aGVyZSB7QGxpbmsgYXhpcyNvcmllbnR9PVwibGVmdFwiIG9yIHtAbGluayBheGlzI29yaWVudH09XCJyaWdodFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayBheGlzI3NwYWNlWX1iZXR3ZWVuIHRpY2tzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvYmplY3RTcGFjZXI9MC4wNV1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0U3BhY2VyID0gMC4wNSxcbiAgLyoqXG4gICogVGhlIG1pbmltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmUgaWYge0BsaW5rIGF4aXMjY2F0ZWdvcmljYWxRfSBpcyBzZXQgdG8gdHJ1ZVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbWluT2JqZWN0U2l6ZT0xNV1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWluT2JqZWN0U2l6ZSA9IDE1LFxuICAvKipcbiAgKiBUaGUgbWF4aW11bSBzaXplIHRoYXQgYW4gb2JqZWN0IGNhbiBiZSBpZiB7QGxpbmsgYXhpcyNjYXRlZ29yaWNhbFF9IGlzIHNldCB0byB0cnVlXG4gICogQHBhcmFtIHtudW1iZXJ9IFttYXhPYmplY3RTaXplPTE1XVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBtYXhPYmplY3RTaXplID0gNTAsXG5cbiAgLyoqXG4gICogQ29sb3Igb2YgdGhlIGJhY2tncm91bmRcbiAgKiBAcGFyYW0ge3N0cmluZ30gW2JhY2tncm91bmRGaWxsPVwidHJhbnNwYXJlbnRcIl1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnLFxuICAvKipcbiAgKiBOYW1lc3BhY2UgZm9yIGFsbCBpdGVtcyBtYWRlIGJ5IHRoaXMgaW5zdGFuY2Ugb2YgYXhpc1xuICAqIEBwYXJhbSB7c3RyaW5nfSBbbmFtZXNwYWNlPVwiZDNzbS1heGlzXCJdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG5hbWVzcGFjZSA9ICdkM3NtLWF4aXMnLFxuICAvKipcbiAgKiBDbGFzcyBuYW1lIGZvciB0aWNrIGNvbnRhaW5lciAoPGc+IGVsZW1lbnQpXG4gICogQHBhcmFtIHtzdHJpbmd9IFtvYmplY3RDbGFzcz1cInRpY2stZ3JvdXBcIl1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0Q2xhc3MgPSAndGljay1ncm91cCcsXG5cbiAgLyoqXG4gICogVmFsdWVzIHRvIHNob3cgYXQgZWFjaCB0aWNrLiBPbmx5IHVzZWQgaWYgY2F0ZWdvcmljYWxRIGlzIHNldCB0cnVlLiBTZWUge0BsaW5rIGF4aXMjY2F0ZWdvcmljYWxRfVxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt0aWNrTGFiZWxzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdGlja0xhYmVscywgICAvLyB3aGF0IHRvIHBsYWNlIGF0IHRpY2tzXG4gIC8qKlxuICAqIFZhbHVlcyB0byBzaG93IGF0IGVhY2ggdGljay4gT25seSB1c2VkIGlmIGNhdGVnb3JpY2FsUSBpcyBzZXQgZmFsc2UuIFNlZSB7QGxpbmsgYXhpcyNjYXRlZ29yaWNhbFF9XG4gICogQHBhcmFtIHtzdHJpbmdbXSB8IG51bWJlcltdfSBbb2JqZWN0Q2xhc3M9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0aWNrVmFsdWVzLCAgIC8vIHdoZXJlIHRvIHBsYWNlIHRpY2tzIGlmIG5vdFxuICAvKipcbiAgKiBOdW1iZXIgb2YgdGlja3MgdG8gZGlzcGxheSBpZiBjYXRlZ29yaWNhbFEgaXMgZmFsc2UuIFNlZSB7QGxpbmsgYXhpcyNjYXRlZ29yaWNhbFF9XG4gICogQHBhcmFtIHtudW1iZXJ9IFtudW1iZXJPZlRpY2tzPTVdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG51bWJlck9mVGlja3MgPSA1LFxuXG5cbiAgLyoqXG4gICogU3Ryb2tlIGNvbG9yIG9mIHRoZSBtYWluIGF4aXMgbGluZVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbbGluZVN0cm9rZT0nYmxhY2snXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBsaW5lU3Ryb2tlID0gJ2JsYWNrJyxcbiAgLyoqXG4gICogU3Ryb2tlIHdpZHRoIG9mIHRoZSBtYWluIGF4aXMgbGluZVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbGluZVN0cm9rZVdpZHRoPTNdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGxpbmVTdHJva2VXaWR0aCA9IDMsXG5cblxuICAvKipcbiAgKiBTdHJva2UgY29sb3Igb2YgdGlja3NcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3RpY2tTdHJva2U9J2JsYWNrJ11cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdGlja1N0cm9rZSA9ICdibGFjaycsXG4gIC8qKlxuICAqIFN0cm9rZSBudW1iZXIgb2YgdGlja3NcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3RpY2tTdHJva2VXaWR0aD0yXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0aWNrU3Ryb2tlV2lkdGggPSAyLFxuICAvKipcbiAgKiBMZW5ndGggLSBpbiBwaXhlbHMgLSBvZiB0aWNrc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbdGlja0xlbmd0aD0xMF1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdGlja0xlbmd0aCA9IDEwLFxuXG4gIHRpY2tUaWNrTGFiZWxTcGFjZXIgPSAxMCxcbiAgdGlja0xhYmVsTWFyZ2luID0gMTAsXG5cblxuICAvKipcbiAgKiBGb250IHNpemUgb2YgdGljayBsYWJlbHNcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RpY2tMYWJlbEZvbnRTaXplPTE0XVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0aWNrTGFiZWxGb250U2l6ZSA9IDE0LFxuICAvKipcbiAgKiBNaW4gZm9udCBzaXplIG9mIHRpY2sgbGFiZWxzXG4gICogQHBhcmFtIHtudW1iZXJ9IFt0aWNrTGFiZWxNaW5Gb250U2l6ZT04XVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0aWNrTGFiZWxNaW5Gb250U2l6ZSA9IDgsXG4gIC8qKlxuICAqIE1heCBmb250IHNpemUgb2YgdGljayBsYWJlbHNcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RpY2tMYWJlbE1heEZvbnRTaXplPTIwXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0aWNrTGFiZWxNYXhGb250U2l6ZSA9IDIwLFxuXG5cbiAgLyoqXG4gICogVGV4dCBhbmNob3Igb2YgdGljayBsYWJlbHNcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3RpY2tMYWJlbFRleHRBbmNob3I9XCJtaWRkbGVcIl1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdGlja0xhYmVsVGV4dEFuY2hvcixcbiAgLyoqXG4gICogUm90YXRpb24gb2YgdGljayBsYWJlbHNcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RpY2tMYWJlbFJvdGF0aW9uPTBdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRpY2tMYWJlbFJvdGF0aW9uLFxuICAvKipcbiAgKiBPcHRpb25hbCBmdW5jdGlvbiBmb3IgZXh0cmFjdGluZyB0aGUgdGljayBsYWJlbCBmcm9tIGRhdGFcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdGlja0xhYmVsRnVuYz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRpY2tMYWJlbEZ1bmMgPSB1bmRlZmluZWQsXG5cbiAgLyoqXG4gICogT3B0aW9uYWwgZnVuY3Rpb24gZm9yIHdoYXQgdG8gZG8gd2hlbiBsYWJlbCBpcyBjbGlja2VkXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3RpY2tMYWJlbE9uQ2xpY2s9ZnVuY3Rpb24oZCwgaSl7fV1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdGlja0xhYmVsT25DbGljayA9IGZ1bmN0aW9uKGQsIGkpe30sXG5cbiAgLyoqXG4gICogT3B0aW9uYWwgZnVuY3Rpb24gZm9yIHdoYXQgdG8gZG8gd2hlbiBsYWJlbCBpcyBob3ZlcmVkXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3RpY2tMYWJlbE9uSG92ZXJGdW5jPWZ1bmN0aW9uKGQsIGkpe31dXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRpY2tMYWJlbE9uSG92ZXJGdW5jID0gZnVuY3Rpb24oZCwgaSl7XG4gICAgcmV0dXJuIFN0cmluZyhkKS5yZXBsYWNlKCctJywgJyAnKS5yZXBsYWNlKCdfJywgJyAnKVxuICB9LFxuXG5cbiAgLyoqXG4gICogTGVuZ3RoIG9mIGd1aWRlbGluZXNcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbZ3VpZGVsaW5lU3BhY2U9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBndWlkZWxpbmVTcGFjZSxcbiAgLyoqXG4gICogU3Ryb2tlIGNvbG9yIG9mIGd1aWRsaW5lc1xuICAqIEBwYXJhbSB7c3RyaW5nfSBbZ3VpZGVsaW5lU3BhY2U9XCIjMzMzMzMzXCJdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGd1aWRlTGluZVN0cm9rZSA9ICcjMzMzMzMzJyxcbiAgLyoqXG4gICogU3Ryb2tlIHdpZHRoIG9mIGd1aWRsaW5lc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbZ3VpZGVsaW5lU3BhY2U9Ml1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZ3VpZGVMaW5lU3Ryb2tlV2lkdGggPSAyLFxuXG4gIC8qKlxuICAqIER1cmF0aW9uIG9mIGFsbCB0cmFuc2l0aW9ucyBvZiB0aGlzIGVsZW1lbnRcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RyYW5zaXRpb25EdXJhdGlvbj0xMDAwXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwLFxuICAvKipcbiAgKiBFYXNpbmcgZnVuY3Rpb24gZm9yIHRyYW5zaXRpb25zXG4gICogQHBhcmFtIHtkMy5lYXNlfSBbZWFzZUZ1bmM9ZDMuZWFzZUV4cF1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwLFxuXG5cbiAgLyoqXG4gICogQ2xvc3VyZSB2YXJpYWJsZSBmb3IgZ2V0dGluZyBvYmplY3Qgc2l6ZSBhZnRlciBjYWxjdWxhdGlvblxuICAqIEBwYXJhbSB7bnVtYmVyfSBbb2JqZWN0U2l6ZT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdFNpemUsXG4gIC8qKlxuICAqIENsb3N1cmUgdmFyaWFibGUgZm9yIGdldHRpbmcgc3BhY2VyIHNpemUgYWZ0ZXIgY2FsY3VsYXRpb25cbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlclNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzcGFjZXJTaXplLFxuXG4gIC8qKlxuICAqIERlY2ltYWwgcGVyY2lzaW9uIHRvIHJvdW5kIG51bWVyaWNhbCB0aWNrIGxhYmVscyB0b1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbcm91bmRUbz0yXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICByb3VuZFRvID0gMixcblxuICBsYWJlbCxcblxuXG4gIHJldmVyc2VTY2FsZVEgPSBmYWxzZVxuXG4gIGF4aXMubGFiZWwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGxhYmVsID0gXywgYXhpcykgOiBsYWJlbDsgfTtcbiAgYXhpcy50aWNrVGlja0xhYmVsU3BhY2VyID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0aWNrVGlja0xhYmVsU3BhY2VyID0gXywgYXhpcykgOiB0aWNrVGlja0xhYmVsU3BhY2VyOyB9O1xuICBheGlzLnRpY2tMYWJlbE1hcmdpbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xhYmVsTWFyZ2luID0gXywgYXhpcykgOiB0aWNrTGFiZWxNYXJnaW47IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHNlbGVjdGlvbiBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgZDMuc2VsZWN0aW9ufVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzZWxlY3Rpb24gPSBzZWxlY3Rpb25cbiAgICovXG5cbiAgYXhpcy5zZWxlY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNlbGVjdGlvbiA9IF8sIGF4aXMpIDogc2VsZWN0aW9uOyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIG9yaWVudGF0aW9uIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBheGlzI29yaWVudH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXSBzaG91bGQgYmUgaG9yaXpvbnRhbCBvciB2ZXJ0aWNhbFxuICAgKiBAcmV0dXJucyB7YXhpcyB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3JpZW50PVwiYm90dG9tXCJcbiAgICovXG4gIGF4aXMub3JpZW50ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvcmllbnQgPSBfLCBheGlzKSA6IG9yaWVudDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIGhvcml6b250YWwgc3BhY2UgaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIGF4aXMjc3BhY2VYfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge2F4aXMgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlWCA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYXhpcy5zcGFjZVggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWCA9IF8sIGF4aXMpIDogc3BhY2VYOyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBhbW91bnQgb2YgdmVydGljYWwgc3BhY2UgaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIGF4aXMjc3BhY2VZfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge2F4aXMgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlWSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYXhpcy5zcGFjZVkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWSA9IF8sIGF4aXMpIDogc3BhY2VZOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IGF4aXMgaXMgYWxsb3dlZCB0byBnbyBiZXlvbmQgc3BlY2lmaWVkIGRpbWVuc2lvbnNcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNzcGFjZVh9KVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgYm9vbGVhbn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3ZlcmZsb3dRID0gZmFsc2VcbiAgICovXG4gIGF4aXMub3ZlcmZsb3dRID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvdmVyZmxvd1EgPSBfLCBheGlzKSA6IG92ZXJmbG93UTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IGF4aXMgd2lsbCBkaXNwbGF5IGNhdGVnb3JpYWwgdGlja3Mgb3IgYnkgbnVtZXJpY2FsIHZhbHVlXG4gICAqIChzZWUge0BsaW5rIGF4aXMjY2F0ZWdvcmljYWxRfSlcbiAgICogQHBhcmFtIHtib29sZWFufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IGJvb2xlYW59XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGNhdGVnb3JpY2FsUSA9IGZhbHNlXG4gICAqL1xuICBheGlzLmNhdGVnb3JpY2FsUSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY2F0ZWdvcmljYWxRID0gXywgYXhpcykgOiBjYXRlZ29yaWNhbFE7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB3aGV0aGVyIG9yIG5vdCBheGlzIHRpY2tzIHNob3VsZCBoYXZlIGd1aWRlbGluZXNcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNndWlkZUxpbmVzUX0pXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBib29sZWFufVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBndWlkZUxpbmVzUSA9IGZhbHNlXG4gICAqL1xuICBheGlzLmd1aWRlTGluZXNRID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChndWlkZUxpbmVzUSA9IF8sIGF4aXMpIDogZ3VpZGVMaW5lc1E7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgaG93IHRpY2tzIHNob3VsZCBiZSBncm91cHBlZFxuICAgKiAoc2VlIHtAbGluayBheGlzI2dyb3VwaW5nfSlcbiAgICogQHBhcmFtIHtBcnJheVtdfSBbXz1ub25lXSBsaXN0IG9mIHB1dGF0aXZlbHkgb3RoZXIgbGlzdHMsIHdoaWNoIHNob3VsZCBjb3JyZXNwb25kIHRvIHRpY2tMYWJlbHNcbiAgICogd2lsbCBzcGFjZSB0aWNrIGxhYmVscyBpbiBuZXN0ZWQgbGlzdHMgY2xvc2VyIHRvZ2V0aGVyIHRoYW4gb3V0ZXIgbGlzdHNcbiAgICogQHJldHVybnMge2F4aXMgfCBBcnJheVtdfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBncm91cGluZyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYXhpcy5ncm91cGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZ3JvdXBpbmcgPSBfLCBheGlzKSA6IGdyb3VwaW5nOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzY2FsZSBmb3Igd2hpY2ggbm9uLWNhdGVnb3JpYWwgIHRpY2tzIHNob3VsZFxuICAgKiBiZSBzcGFjZWRcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNzY2FsZX0pXG4gICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgZDMuc2NhbGV9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKVxuICAgKi9cbiAgYXhpcy5zY2FsZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGUgPSBfLCBheGlzKSA6IHNjYWxlOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHBhZGRpbmcgZm9yIHRoZSBkb21haW4gb2YgdGhlIHNjYWxlXG4gICAqIChzZWUge0BsaW5rIGF4aXMjZG9tYWluUGFkZGluZ30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZG9tYWluUGFkZGluZyA9IDAuNVxuICAgKi9cbiAgYXhpcy5kb21haW5QYWRkaW5nID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkb21haW5QYWRkaW5nID0gXywgYXhpcykgOiBkb21haW5QYWRkaW5nOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIG9iamVjdFNwYWNlclxuICAgKiAoc2VlIHtAbGluayBheGlzI29iamVjdFNwYWNlcn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0U3BhY2VyID0gMC4wNVxuICAgKi9cbiAgYXhpcy5vYmplY3RTcGFjZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNwYWNlciA9IF8sIGF4aXMpIDogb2JqZWN0U3BhY2VyOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1pbk9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNtaW5PYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtaW5PYmplY3RTaXplID0gMTVcbiAgICovXG4gIGF4aXMubWluT2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWluT2JqZWN0U2l6ZSA9IF8sIGF4aXMpIDogbWluT2JqZWN0U2l6ZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtYXhPYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIGF4aXMjbWF4T2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWF4T2JqZWN0U2l6ZSA9IDUwXG4gICAqL1xuICBheGlzLm1heE9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1heE9iamVjdFNpemUgPSBfLCBheGlzKSA6IG1heE9iamVjdFNpemU7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG5hbWVzcGFjZVxuICAgKiAoc2VlIHtAbGluayBheGlzI25hbWVzcGFjZX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbmFtZXNwYWNlID0gJ2Qzc20tYXhpcydcbiAgICovXG4gIGF4aXMubmFtZXNwYWNlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChuYW1lc3BhY2UgPSBfLCBheGlzKSA6IG5hbWVzcGFjZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBiYWNrZ3JvdW5kRmlsbFxuICAgKiAoc2VlIHtAbGluayBheGlzI2JhY2tncm91bmRGaWxsfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCdcbiAgICovXG4gIGF4aXMuYmFja2dyb3VuZEZpbGwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhY2tncm91bmRGaWxsID0gXywgYXhpcykgOiBiYWNrZ3JvdW5kRmlsbDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RDbGFzc1xuICAgKiAoc2VlIHtAbGluayBheGlzI29iamVjdENsYXNzfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RDbGFzcyA9ICd0aWNrLWdyb3VwJ1xuICAgKi9cbiAgYXhpcy5vYmplY3RDbGFzcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3MgPSBfLCBheGlzKSA6IG9iamVjdENsYXNzOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0aWNrTGFiZWxzXG4gICAqIChzZWUge0BsaW5rIGF4aXMjdGlja0xhYmVsc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgc3RyaW5nW119XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRpY2tMYWJlbHMgPSB1bmRlZmluZWRcbiAgICovXG4gIGF4aXMudGlja0xhYmVscyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xhYmVscyA9IF8sIGF4aXMpIDogdGlja0xhYmVsczsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0aWNrVmFsdWVzXG4gICAqIChzZWUge0BsaW5rIGF4aXMjdGlja1ZhbHVlc30pXG4gICAqIEBwYXJhbSB7bnVtYmVyW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyW119XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRpY2tWYWx1ZXMgPSB1bmRlZmluZWRcbiAgICovXG4gIGF4aXMudGlja1ZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja1ZhbHVlcyA9IF8sIGF4aXMpIDogdGlja1ZhbHVlczsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0aWNrVmFsdWVzXG4gICAqIChzZWUge0BsaW5rIGF4aXMjbnVtYmVyT2ZUaWNrc30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbnVtYmVyT2ZUaWNrcyA9IDVcbiAgICovXG4gIGF4aXMubnVtYmVyT2ZUaWNrcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobnVtYmVyT2ZUaWNrcyA9IF8sIGF4aXMpIDogbnVtYmVyT2ZUaWNrczsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbGluZVN0cm9rZVxuICAgKiAoc2VlIHtAbGluayBheGlzI2xpbmVTdHJva2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGxpbmVTdHJva2UgPSAnYmxhY2snXG4gICAqL1xuICBheGlzLmxpbmVTdHJva2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGxpbmVTdHJva2UgPSBfLCBheGlzKSA6IGxpbmVTdHJva2U7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbGluZVN0cm9rZVdpZHRoXG4gICAqIChzZWUge0BsaW5rIGF4aXMjbGluZVN0cm9rZVdpZHRofSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBsaW5lU3Ryb2tlV2lkdGggPSAzXG4gICAqL1xuICBheGlzLmxpbmVTdHJva2VXaWR0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobGluZVN0cm9rZVdpZHRoID0gXywgYXhpcykgOiBsaW5lU3Ryb2tlV2lkdGg7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRpY2tTdHJva2VcbiAgICogKHNlZSB7QGxpbmsgYXhpcyN0aWNrU3Ryb2tlfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0aWNrU3Ryb2tlID0gJ2JsYWNrJ1xuICAgKi9cbiAgYXhpcy50aWNrU3Ryb2tlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0aWNrU3Ryb2tlID0gXywgYXhpcykgOiB0aWNrU3Ryb2tlOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRpY2tTdHJva2VXaWR0aFxuICAgKiAoc2VlIHtAbGluayBheGlzI3RpY2tTdHJva2VXaWR0aH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdGlja1N0cm9rZVdpZHRoID0gMlxuICAgKi9cbiAgYXhpcy50aWNrU3Ryb2tlV2lkdGggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tTdHJva2VXaWR0aCA9IF8sIGF4aXMpIDogdGlja1N0cm9rZVdpZHRoOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRpY2tMZW5ndGhcbiAgICogKHNlZSB7QGxpbmsgYXhpcyN0aWNrTGVuZ3RofSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0aWNrTGVuZ3RoID0gMTBcbiAgICovXG4gIGF4aXMudGlja0xlbmd0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xlbmd0aCA9IF8sIGF4aXMpIDogdGlja0xlbmd0aDsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdGlja0xhYmVsRm9udFNpemVcbiAgICogKHNlZSB7QGxpbmsgYXhpcyN0aWNrTGFiZWxGb250U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdGlja0xhYmVsRm9udFNpemUgPSAxNFxuICAgKi9cbiAgYXhpcy50aWNrTGFiZWxGb250U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xhYmVsRm9udFNpemUgPSBfLCBheGlzKSA6IHRpY2tMYWJlbEZvbnRTaXplOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRpY2tMYWJlbE1pbkZvbnRTaXplXG4gICAqIChzZWUge0BsaW5rIGF4aXMjdGlja0xhYmVsTWluRm9udFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRpY2tMYWJlbE1pbkZvbnRTaXplID0gOFxuICAgKi9cbiAgYXhpcy50aWNrTGFiZWxNaW5Gb250U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xhYmVsTWluRm9udFNpemUgPSBfLCBheGlzKSA6IHRpY2tMYWJlbE1pbkZvbnRTaXplOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRpY2tMYWJlbE1heEZvbnRTaXplXG4gICAqIChzZWUge0BsaW5rIGF4aXMjdGlja0xhYmVsTWF4Rm9udFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRpY2tMYWJlbE1heEZvbnRTaXplID0gMjBcbiAgICovXG4gIGF4aXMudGlja0xhYmVsTWF4Rm9udFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tMYWJlbE1heEZvbnRTaXplID0gXywgYXhpcykgOiB0aWNrTGFiZWxNYXhGb250U2l6ZTt9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0aWNrTGFiZWxUZXh0QW5jaG9yXG4gICAqIChzZWUge0BsaW5rIGF4aXMjdGlja0xhYmVsVGV4dEFuY2hvcn0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdGlja0xhYmVsVGV4dEFuY2hvciA9ICdjZW50ZXInXG4gICAqL1xuICBheGlzLnRpY2tMYWJlbFRleHRBbmNob3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tMYWJlbFRleHRBbmNob3IgPSBfLCBheGlzKSA6IHRpY2tMYWJlbFRleHRBbmNob3I7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdGlja0xhYmVsUm90YXRpb25cbiAgICogKHNlZSB7QGxpbmsgYXhpcyN0aWNrTGFiZWxSb3RhdGlvbn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdGlja0xhYmVsUm90YXRpb24gPSAwXG4gICAqL1xuICBheGlzLnRpY2tMYWJlbFJvdGF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0aWNrTGFiZWxSb3RhdGlvbiA9IF8sIGF4aXMpIDogdGlja0xhYmVsUm90YXRpb247IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdGlja0xhYmVsRnVuY1xuICAgKiAoc2VlIHtAbGluayBheGlzI3RpY2tMYWJlbEZ1bmN9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0aWNrTGFiZWxGdW5jID0gdW5kZWZpbmVkXG4gICAqL1xuICBheGlzLnRpY2tMYWJlbEZ1bmMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tMYWJlbEZ1bmMgPSBfLCBheGlzKSA6IHRpY2tMYWJlbEZ1bmM7IH07XG5cblxuICAvKipcbiAgKiBHZXRzIC8gc2V0cyB0aGUgdGlja0xhYmVsT25DbGlja1xuICAqIChzZWUge0BsaW5rIGF4aXMjdGlja0xhYmVsT25DbGlja30pXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgKiBAcmV0dXJucyB7YXhpcyB8IGZ1bmN0aW9ufVxuICAqIEBtZW1iZXJvZiBheGlzXG4gICogQHByb3BlcnR5XG4gICovXG4gIGF4aXMudGlja0xhYmVsT25DbGljayA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xhYmVsT25DbGljayA9IF8sIGF4aXMpIDogdGlja0xhYmVsT25DbGljazsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZ3VpZGVsaW5lU3BhY2VcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNndWlkZWxpbmVTcGFjZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZ3VpZGVsaW5lU3BhY2UgPSB1bmRlZmluZWRcbiAgICovXG4gIGF4aXMuZ3VpZGVsaW5lU3BhY2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGd1aWRlbGluZVNwYWNlID0gXywgYXhpcykgOiBndWlkZWxpbmVTcGFjZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBndWlkZUxpbmVTdHJva2VcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNndWlkZUxpbmVTdHJva2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGd1aWRlTGluZVN0cm9rZSA9IFwiIzMzMzMzM1wiXG4gICAqL1xuICBheGlzLmd1aWRlTGluZVN0cm9rZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZ3VpZGVMaW5lU3Ryb2tlID0gXywgYXhpcykgOiBndWlkZUxpbmVTdHJva2U7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZ3VpZGVMaW5lU3Ryb2tlV2lkdGhcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNndWlkZUxpbmVTdHJva2VXaWR0aH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZ3VpZGVMaW5lU3Ryb2tlV2lkdGggPSAyXG4gICAqL1xuICBheGlzLmd1aWRlTGluZVN0cm9rZVdpZHRoID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChndWlkZUxpbmVTdHJva2VXaWR0aCA9IF8sIGF4aXMpIDogZ3VpZGVMaW5lU3Ryb2tlV2lkdGg7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRyYW5zaXRpb25EdXJhdGlvblxuICAgKiAoc2VlIHtAbGluayBheGlzI3RyYW5zaXRpb25EdXJhdGlvbn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMFxuICAgKi9cbiAgYXhpcy50cmFuc2l0aW9uRHVyYXRpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRyYW5zaXRpb25EdXJhdGlvbiA9IF8sIGF4aXMpIDogdHJhbnNpdGlvbkR1cmF0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGVhc2VGdW5jXG4gICAqIChzZWUge0BsaW5rIGF4aXMjZWFzZUZ1bmN9KVxuICAgKiBAcGFyYW0ge2QzLmVhc2V9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgZDMuZWFzZX1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwXG4gICAqL1xuICBheGlzLmVhc2VGdW5jID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChlYXNlRnVuYyA9IF8sIGF4aXMpIDogZWFzZUZ1bmM7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNvYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RTaXplID0gdW5kZWZpbmVkXG4gICAqL1xuICBheGlzLm9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNpemUgPSBfLCBheGlzKSA6IG9iamVjdFNpemU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgc3BhY2VyU2l6ZVxuICAgKiAoc2VlIHtAbGluayBheGlzI3NwYWNlclNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlclNpemUgPSB1bmRlZmluZWRcbiAgICovXG4gIGF4aXMuc3BhY2VyU2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3BhY2VyU2l6ZSA9IF8sIGF4aXMpIDogc3BhY2VyU2l6ZTsgfTtcblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHJvdW5kVG9cbiAgICogKHNlZSB7QGxpbmsgYXhpcyNyb3VuZFRvfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCByb3VuZFRvID0gMlxuICAgKi9cbiAgIGF4aXMucm91bmRUbyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocm91bmRUbyA9IF8sIGF4aXMpIDogcm91bmRUbzsgfTtcbiAgIGF4aXMucmV2ZXJzZVNjYWxlUSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocmV2ZXJzZVNjYWxlUSA9IF8sIGF4aXMpIDogcmV2ZXJzZVNjYWxlUTsgfTtcblxuXG4gICBheGlzLnRpY2tMYWJlbE9uSG92ZXJGdW5jID0gZnVuY3Rpb24oXykge3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tMYWJlbE9uSG92ZXJGdW5jID0gXywgYXhpcykgOiB0aWNrTGFiZWxPbkhvdmVyRnVuYzsgfTtcblxuXG4gIGZ1bmN0aW9uIGF4aXMgKCkge1xuICAgIC8vIGZvciBjb252ZW5pZW5jZSBpbiBoYW5kbGluZyBvcmllbnRhdGlvbiBzcGVjaWZpYyB2YWx1ZXNcbiAgICB2YXIgaG9yaXpvbnRhbFEgPSBoYXNRKFsndG9wJywgJ2JvdHRvbScsICdob3Jpem9udGFsJ10sIG9yaWVudCkgPyB0cnVlIDogZmFsc2VcbiAgICB2YXIgdmVydGljYWxRID0gIWhvcml6b250YWxRXG5cbiAgICAvLyBiYWNrZ3JvdW5kIGNsaXBpbmcgcmVjdGFuZ2xlXG4gICAgdmFyIGJnY3BSZWN0ID0ge3g6MCwgeTowLCB3aWR0aDogc3BhY2VYLCBoZWlnaHQ6c3BhY2VZfVxuICAgIC8vIG1vZGlmeSB0aGUgcmVjdCBiYXNlZCBvbiBheGlzIG9yaWVudGF0aW9uXG4gICAgaWYgKG9yaWVudCA9PSBcImxlZnRcIikge1xuICAgICAgYmdjcFJlY3QueCAtPSBzcGFjZVg7XG4gICAgICBpZihndWlkZUxpbmVzUSkgeyBiZ2NwUmVjdC53aWR0aCArPSBndWlkZWxpbmVTcGFjZSB9O1xuICAgICAgLyogdGhlc2UgdHdvIGxpbmVzIGluY3JlYXNlIHRoZSBjbGlwcGluZyByZWN0IHRvIGFsbG93IGZvciB0ZXh0IGF0IHRoZSBlZGdlIG9mIHRoZSBheGlzICovXG4gICAgICBiZ2NwUmVjdC55IC09IHRpY2tMYWJlbE1heEZvbnRTaXplO1xuICAgICAgYmdjcFJlY3QuaGVpZ2h0ICs9IDIqdGlja0xhYmVsTWF4Rm9udFNpemVcbiAgICB9XG4gICAgaWYgKG9yaWVudCA9PSBcImJvdHRvbVwiKXtcbiAgICAgIGJnY3BSZWN0LnkgPSBiZ2NwUmVjdC55O1xuICAgICAgaWYoZ3VpZGVMaW5lc1EpIHsgYmdjcFJlY3QueSAtPSBndWlkZWxpbmVTcGFjZTsgYmdjcFJlY3QuaGVpZ2h0ICs9IGd1aWRlbGluZVNwYWNlOyB9O1xuICAgICAgLyogdGhlc2UgdHdvIGxpbmVzIGluY3JlYXNlIHRoZSBjbGlwcGluZyByZWN0IHRvIGFsbG93IGZvciB0ZXh0IGF0IHRoZSBlZGdlIG9mIHRoZSBheGlzICovXG4gICAgICBiZ2NwUmVjdC54IC09IHRpY2tMYWJlbE1heEZvbnRTaXplO1xuICAgICAgYmdjcFJlY3Qud2lkdGggKz0gMip0aWNrTGFiZWxNYXhGb250U2l6ZVxuICAgIH1cbiAgICBpZiAob3JpZW50ID09IFwidG9wXCIpIHtcbiAgICAgIGJnY3BSZWN0LnkgLT0gc3BhY2VZO1xuICAgICAgaWYoZ3VpZGVMaW5lc1EpIHsgYmdjcFJlY3QuaGVpZ2h0ICs9IGd1aWRlbGluZVNwYWNlIH07XG4gICAgICAvKiB0aGVzZSB0d28gbGluZXMgaW5jcmVhc2UgdGhlIGNsaXBwaW5nIHJlY3QgdG8gYWxsb3cgZm9yIHRleHQgYXQgdGhlIGVkZ2Ugb2YgdGhlIGF4aXMgKi9cbiAgICAgIGJnY3BSZWN0LnkgLT0gdGlja0xhYmVsTWF4Rm9udFNpemU7XG4gICAgICBiZ2NwUmVjdC5oZWlnaHQgKz0gMip0aWNrTGFiZWxNYXhGb250U2l6ZVxuICAgIH1cbiAgICBpZiAob3JpZW50ID09IFwicmlnaHRcIikgeyBiZ2NwUmVjdC54ID0gMDtcbiAgICAgIGlmKGd1aWRlTGluZXNRKSB7IGJnY3BSZWN0LndpZHRoICs9IGd1aWRlbGluZVNwYWNlOyBiZ2NwUmVjdC54IC09IGd1aWRlbGluZVNwYWNlIH07XG4gICAgICAvKiB0aGVzZSB0d28gbGluZXMgaW5jcmVhc2UgdGhlIGNsaXBwaW5nIHJlY3QgdG8gYWxsb3cgZm9yIHRleHQgYXQgdGhlIGVkZ2Ugb2YgdGhlIGF4aXMgKi9cbiAgICAgIGJnY3BSZWN0LnkgLT0gdGlja0xhYmVsTWF4Rm9udFNpemU7XG4gICAgICBiZ2NwUmVjdC5oZWlnaHQgKz0gMip0aWNrTGFiZWxNYXhGb250U2l6ZVxuICAgIH1cblxuXG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cbiAgICAvLyBkZWZhdWx0cyBmb3IgdGV4dC1hbmNob3IgYW5kIHRleHQgcm90YXRpb25cbiAgICBpZiAob3JpZW50ID09ICd0b3AnKSB7XG4gICAgICB0aWNrTGFiZWxUZXh0QW5jaG9yID0gdGlja0xhYmVsVGV4dEFuY2hvciA9PSB1bmRlZmluZWQgPyAnc3RhcnQnIDogdGlja0xhYmVsVGV4dEFuY2hvclxuICAgICAgdGlja0xhYmVsUm90YXRpb24gPSB0aWNrTGFiZWxSb3RhdGlvbiA9PSB1bmRlZmluZWQgPyAtOTAgOiB0aWNrTGFiZWxSb3RhdGlvblxuICAgIH1cbiAgICBpZiAob3JpZW50ID09ICdib3R0b20nKSB7XG4gICAgICB0aWNrTGFiZWxUZXh0QW5jaG9yID0gdGlja0xhYmVsVGV4dEFuY2hvciA9PSB1bmRlZmluZWQgPyAnZW5kJyA6IHRpY2tMYWJlbFRleHRBbmNob3JcbiAgICAgIHRpY2tMYWJlbFJvdGF0aW9uID0gdGlja0xhYmVsUm90YXRpb24gPT0gdW5kZWZpbmVkID8gLTkwIDogdGlja0xhYmVsUm90YXRpb25cbiAgICB9XG4gICAgaWYgKG9yaWVudCA9PSAnbGVmdCcpIHtcbiAgICAgIHRpY2tMYWJlbFRleHRBbmNob3IgPSB0aWNrTGFiZWxUZXh0QW5jaG9yID09IHVuZGVmaW5lZCA/ICdlbmQnIDogdGlja0xhYmVsVGV4dEFuY2hvclxuICAgICAgdGlja0xhYmVsUm90YXRpb24gPSB0aWNrTGFiZWxSb3RhdGlvbiA9PSB1bmRlZmluZWQgPyAwIDogdGlja0xhYmVsUm90YXRpb25cbiAgICB9XG4gICAgaWYgKG9yaWVudCA9PSAncmlnaHQnKSB7XG4gICAgICB0aWNrTGFiZWxUZXh0QW5jaG9yID0gdGlja0xhYmVsVGV4dEFuY2hvciA9PSB1bmRlZmluZWQgPyAnc3RhcnQnIDogdGlja0xhYmVsVGV4dEFuY2hvclxuICAgICAgdGlja0xhYmVsUm90YXRpb24gPSB0aWNrTGFiZWxSb3RhdGlvbiA9PSB1bmRlZmluZWQgPyAwIDogdGlja0xhYmVsUm90YXRpb25cbiAgICB9XG5cbiAgICAvKlxuICAgIElmIGNhdGVnb3JpY2FsOlxuICAgICAgLT4gdXNlIGdyb3VwaW5nIGlmIGRlZmluZWQsXG4gICAgICAtPiBlbHNlIHVzZSB0aGUgbGFiZWxzIHByb3ZpZGVkXG4gICAgZWxzZTpcbiAgICAgIGlmIGdyb3VwaW5nIHVuZGVmaW5lZFxuICAgICAgICBhbmQgbm8gc3BlY2lmaWVkIG51bWJlciBvZiB0aWNrZXNcbiAgICAgICAgICAtPiBtYWtlIG51bWJlck9mVGljayB0aWNrc1xuICAgICAgICAgIC0+IGVsc2UgdXNlIHByb3ZpZGVkIHRpY2sgdmFsdWVzXG4gICAgICAgIC0+IHVzZSBncm91cGluZ1xuICAgICovXG4gICAgdmFyIHRpY2tEYXRhID0gY2F0ZWdvcmljYWxRXG4gICAgPyAoZ3JvdXBpbmcgPT0gdW5kZWZpbmVkKVxuICAgICAgPyB0aWNrTGFiZWxzXG4gICAgICA6IGdyb3VwaW5nXG4gICAgOiAoZ3JvdXBpbmcgPT0gdW5kZWZpbmVkKVxuICAgICAgPyAobnVtYmVyT2ZUaWNrcyAhPSB1bmRlZmluZWQpXG4gICAgICAvLyA/ICh0aWNrVmFsdWVzLmxlbmd0aCA8IG51bWJlck9mVGlja3MpXG4gICAgICAgID8gKHRpY2tSYW5nZSguLi5kMy5leHRlbnQodGlja1ZhbHVlcyksIG51bWJlck9mVGlja3MpKVxuICAgICAgICA6IHRpY2tWYWx1ZXNcbiAgICAgIDogZ3JvdXBpbmdcblxuXG4gICAgdmFyIGZsYXRUaWNrRGF0YSA9IGZsYXR0ZW4odGlja0RhdGEpXG4gICAgdmFyIG51bWJlck9mT2JqZWN0cyA9IGZsYXRUaWNrRGF0YS5sZW5ndGhcbiAgICB2YXIgc3BhY2UgPSBob3Jpem9udGFsUSA/IHNwYWNlWCA6IHNwYWNlWVxuICAgIHZhciBleHRlbnQgPSBkMy5leHRlbnQoZmxhdFRpY2tEYXRhKVxuXG5cbiAgICBpZiAocmV2ZXJzZVNjYWxlUSkge2V4dGVudC5yZXZlcnNlKCl9XG4gICAgdmFyIGRvbWFpbiA9IHJldmVyc2VTY2FsZVFcbiAgICA/IFtleHRlbnRbMF0gKyBkb21haW5QYWRkaW5nLCBleHRlbnRbMV0gLSBkb21haW5QYWRkaW5nXVxuICAgIDogW2V4dGVudFswXSAtIGRvbWFpblBhZGRpbmcsIGV4dGVudFsxXSArIGRvbWFpblBhZGRpbmddXG5cbiAgICBzY2FsZVxuICAgIC5kb21haW4oZG9tYWluKVxuICAgIC5yYW5nZShbaG9yaXpvbnRhbFEgPyAwIDogc3BhY2VZLCBob3Jpem9udGFsUSA/IHNwYWNlWCA6IDBdKVxuXG5cbiAgICAvKlxuICAgIFNjYWxlcyBhcmUgYmFzZWQgb24gdGhlIHZhbHVlcyBvZiB0aGUgY2hhcnQgYW5kIGNvcnJlc3BvbmQgdG8gdGhlIHNwYWNpbmdzIG9mIHRoZVxuICAgIGNoYXJ0LiBJZiB0aGUgY2hhcnQgaGFzIGFscmVhZHkgYmVlbiByZW5kZXJlZCwgdGhlc2UgdmFsdWVzIChleHBlbnNpdmUgdG8gY2FsdWNsYXRlKSBjYW5cbiAgICBiZSBwYXNzZWQgdG8gYXhpcyB0byBwcmV2ZW50IHJlY2FsY3VsYXRpb24uXG4gICAgKi9cblxuICAgIC8vIGNhbGN1bGF0ZSBvYmplY3Qgc2l6ZSBpZiBuZWVkZWRcbiAgICBvYmplY3RTaXplID0gKG9iamVjdFNpemUgPT0gdW5kZWZpbmVkKVxuICAgID8gY2FsY3VsYXRlV2lkdGhPZk9iamVjdChzcGFjZSwgbnVtYmVyT2ZPYmplY3RzLCBtaW5PYmplY3RTaXplLCBtYXhPYmplY3RTaXplLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICA6IG9iamVjdFNpemVcblxuICAgIC8vIGNhbGN1bGF0ZSBzcGFjZXIgc2l6ZSBpZiBuZWVkZWRcbiAgICBzcGFjZXJTaXplID0gKHNwYWNlclNpemUgPT0gdW5kZWZpbmVkKVxuICAgID8gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcihmbGF0VGlja0RhdGEsIHNwYWNlLCBvYmplY3RTaXplLCBudW1iZXJPZk9iamVjdHMsIG9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIDogc3BhY2VyU2l6ZVxuXG5cbiAgICB2YXIgb2JqQ2xhc3MgPSBoeXBlbmF0ZShuYW1lc3BhY2UsIGNhdGVnb3JpY2FsUSA/IG9iamVjdENsYXNzKyctY2F0ZWdvcmljYWwnIDogb2JqZWN0Q2xhc3MpXG5cbiAgICB2YXIgc3BhY2VyRnVuY3Rpb24gPSBncm91cGluZ1NwYWNlcigpXG4gICAgLmhvcml6b250YWxRKGhvcml6b250YWxRKS5zY2FsZShzY2FsZSkubW92ZWJ5KChjYXRlZ29yaWNhbFE/J2NhdGVnb3J5Jzonc2NhbGUnKSkubnVtYmVyT2ZPYmplY3RzKG51bWJlck9mT2JqZWN0cylcbiAgICAub2JqZWN0Q2xhc3Mob2JqQ2xhc3MpLm9iamVjdFNpemUob2JqZWN0U2l6ZSkuc3BhY2VyU2l6ZShzcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcbiAgICAubmFtZXNwYWNlKG5hbWVzcGFjZSlcblxuICAgIHZhciB0aWNrRW50ZXJBbmltYXRpb24gPSBmdW5jdGlvbihzZWwpe1xuICAgICAgdmFyIG10ID0gc2NhbGUoc2VsLmRhdHVtKCkpLFxuICAgICAgZGlzdCA9IHNjYWxlKGV4dGVudFsxXSkgKiAyLFxuICAgICAgayA9IChtdCA8IGV4dGVudFsxXSAvIDIpID8gMSA6IC0xXG4gICAgICBrID0gaG9yaXpvbnRhbFEgPyBrICogLTEgOiBrXG4gICAgICBzZWwuYXR0cigndHJhbnNmb3JtJywgZnVuY3Rpb24gKGQsIGkpIHtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUSA/ICBkaXN0ICogayA6IDAsXG4gICAgICAgIHkgPSAhaG9yaXpvbnRhbFEgPyBkaXN0ICogayA6IDAsXG4gICAgICAgIHQgPSAndHJhbnNsYXRlKCcreCsnLCcreSsnKSdcbiAgICAgICAgcmV0dXJuIHRcbiAgICAgIH0pXG4gICAgfVxuICAgIHZhciB0aWNrRXhpdEFuaW1hdGlvbiA9IGZ1bmN0aW9uKHNlbCkge1xuICAgICAgdmFyIG10ID0gc2NhbGUoc2VsLmRhdHVtKCkpLFxuICAgICAgZGlzdCA9IHNjYWxlKGV4dGVudFsxXSkgKiAyLFxuICAgICAgayA9IChtdCA8IGV4dGVudFsxXSAvIDIpID8gMSA6IC0xXG4gICAgICBrID0gaG9yaXpvbnRhbFEgPyBrICogLTEgOiBrXG4gICAgICBzZWwudHJhbnNpdGlvbigpLmR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbikuZWFzZShlYXNlRnVuYylcbiAgICAgIC5zdHlsZSgnb3BhY2l0eScsIDApXG4gICAgICAuYXR0cigndHJhbnNmb3JtJywgZnVuY3Rpb24gKGQsIGkpIHtcbiAgICAgICAgdmFyXG5cbiAgICAgICAgeCA9IGhvcml6b250YWxRID8gIGRpc3QgKiBrICA6IDAsXG4gICAgICAgIHkgPSAhaG9yaXpvbnRhbFEgPyBkaXN0ICogayA6IDAsXG4gICAgICAgIHQgPSAndHJhbnNsYXRlKCcreCsnLCcreSsnKSdcbiAgICAgICAgcmV0dXJuIHRcbiAgICAgIH0pLnJlbW92ZSgpXG4gICAgfVxuXG4gICAgaWYgKCFjYXRlZ29yaWNhbFEpe1xuICAgICAgc3BhY2VyRnVuY3Rpb24uZW50ZXJGdW5jdGlvbih0aWNrRW50ZXJBbmltYXRpb24pXG4gICAgICBzcGFjZXJGdW5jdGlvbi5leGl0RnVuY3Rpb24odGlja0V4aXRBbmltYXRpb24pXG4gICAgfVxuXG4gICAgLy8gbW92ZSB0aWNrIGNvbnRhaW5lcnNcbiAgICBzcGFjZXJGdW5jdGlvbihjb250YWluZXIsIHRpY2tEYXRhLCAwKVxuXG4gICAgLy8gbW92ZSBieSBmb3IgeCBhbmQgeSBuZWVkZWQgdG8gY2VudGVyIGNhdGVnb3JpY2FsIHRpY2tzLCBsYWJlbHMsIGFuZCBndWlkZWxpbmVzXG4gICAgZnVuY3Rpb24gbW92ZVhCeShkLCBpLCBob3Jpem9udGFsUSwgY2F0ZWdvcmljYWxRLCBvYmplY3RTaXplKXtcbiAgICAgIHJldHVybiAoaG9yaXpvbnRhbFEpXG4gICAgICA/IChjYXRlZ29yaWNhbFEpXG4gICAgICAgID8gb2JqZWN0U2l6ZSAvIDJcbiAgICAgICAgOiAwXG4gICAgICA6IDBcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBtb3ZlWUJ5KGQsIGksIHZlcnRpY2FsUSwgY2F0ZWdvcmljYWxRLCBvYmplY3RTaXplKXtcbiAgICAgIHJldHVybiAodmVydGljYWxRKVxuICAgICAgPyAoY2F0ZWdvcmljYWxRKVxuICAgICAgICA/IG9iamVjdFNpemUgLyAyXG4gICAgICAgIDogMFxuICAgICAgOiAwXG4gICAgfVxuXG5cblxuICAgIHZhciBsYWJlbE5hbWVHcm91cCA9IHNhZmVTZWxlY3Qoc2VsZWN0aW9uLCAnZycsIGh5cGVuYXRlKG5hbWVzcGFjZSwnYXhpcy1uYW1lJykpXG5cblxuXG4gICAgdmFyIGxhYmVsRWxlbWVudCA9IHNhZmVTZWxlY3QobGFiZWxOYW1lR3JvdXAsICd0ZXh0JywgaHlwZW5hdGUobmFtZXNwYWNlLCAnbmFtZScpKVxuICAgIGlmIChsYWJlbEVsZW1lbnQgIT0gdW5kZWZpbmVkKSB7XG4gICAgICBsYWJlbEVsZW1lbnQudGV4dChsYWJlbClcblxuICAgICAgaWYgKG9yaWVudCA9PSAnbGVmdCcgfHwgb3JpZW50ID09ICdyaWdodCcpIHtcbiAgICAgICAgbGFiZWxFbGVtZW50LmF0dHIoJ3RyYW5zZm9ybScsICdyb3RhdGUoLTkwKScpXG4gICAgICB9XG5cbiAgICAgIHZhciBiYm94ID0gbGFiZWxFbGVtZW50Lm5vZGUoKS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuICAgICAgbGFiZWxOYW1lR3JvdXAuYXR0cigndHJhbnNmb3JtJyxmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSAwLFxuICAgICAgICB5ID0gMCxcbiAgICAgICAgdFxuXG4gICAgICAgIGlmIChvcmllbnQgPT0gJ2JvdHRvbScpIHtcbiAgICAgICAgICB4ID0gc3BhY2VYIC0gYmJveC53aWR0aCAtIHRpY2tMYWJlbE1hcmdpblxuICAgICAgICAgIHkgPSAtIHRpY2tUaWNrTGFiZWxTcGFjZXJcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChvcmllbnQgPT0gJ3RvcCcpIHtcbiAgICAgICAgICB4ID0gc3BhY2VYIC0gYmJveC53aWR0aCAtIHRpY2tMYWJlbE1hcmdpblxuICAgICAgICAgIHggPSB0aWNrTGFiZWxNYXJnaW5cbiAgICAgICAgICB5ID0gYmJveC5oZWlnaHQgKyB0aWNrVGlja0xhYmVsU3BhY2VyXG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAob3JpZW50ID09ICdsZWZ0Jykge1xuICAgICAgICAgIHggPSBiYm94LndpZHRoICsgdGlja1RpY2tMYWJlbFNwYWNlclxuICAgICAgICAgIHkgPSBiYm94LmhlaWdodCArIHRpY2tMYWJlbE1hcmdpblxuICAgICAgICB9IGVsc2UgaWYgKG9yaWVudCA9PSAncmlnaHQnKSB7XG4gICAgICAgICAgeCA9IC0oYmJveC53aWR0aCArIHRpY2tUaWNrTGFiZWxTcGFjZXIpXG4gICAgICAgICAgeSA9IGJib3guaGVpZ2h0ICsgdGlja0xhYmVsTWFyZ2luXG4gICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgfVxuICAgICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAgIHJldHVybiB0XG4gICAgICB9KVxuXG5cblxuICAgIH0gZWxzZSB7XG4gICAgICBsYWJlbEVsZW1lbnQucmVtb3ZlKClcbiAgICB9XG4gICAgLypcbiAgICBJZGVhIGZyb20gU3RhY2sgT3ZlcmZsb3dcbiAgICBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy81MDU3OTUzNS9kMy1qcy12NC10cnVuY2F0ZS10ZXh0LXRvLWZpdC1pbi1maXhlZC1zcGFjZS81MDU4NTAyMj9ub3JlZGlyZWN0PTEjY29tbWVudDg4MjM1NTYyXzUwNTg1MDIyXG4gICAgdG8gdXNlIGNsaXAgcGF0aCB0byBtYWtlIHRoaW5ncyBmaXQgaW4gZml4ZWQgc2l6ZS4gSGF2ZSB5ZXQgZ290IHRoaXMgdG8gd29yayBuaWNlbHkuXG4gICAgKi9cbiAgICAvLyB2YXIgZGVmcyA9IGQzLnNlbGVjdChjb250YWluZXIubm9kZSgpLnBhcmVudE5vZGUpLnNlbGVjdCgnZGVmcycpXG4gICAgLy8gdmFyIHRpY2tMYWJlbENsaXBQYXRoID0gc2FmZVNlbGVjdChkZWZzLCAnY2xpcFBhdGgnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ3RpY2stbGFiZWwtY2xpcC1wYXRoJykpLmF0dHIoJ2lkJywgIGh5cGVuYXRlKG5hbWVzcGFjZSwndGljay1sYWJlbC1jbGlwLXBhdGgnKSlcbiAgICAvLyB2YXIgdGlja0xhYmVsQ2xpcFBhdGhSZWN0ID0gc2FmZVNlbGVjdCh0aWNrTGFiZWxDbGlwUGF0aCwgJ3JlY3QnLCAgaHlwZW5hdGUobmFtZXNwYWNlLCd0aWNrLWxhYmVsLWNsaXAtcGF0aC1yZWN0JykpXG4gICAgLy8gLmF0dHIoJ3gnLCAwKVxuICAgIC8vIC5hdHRyKCd5JywgMClcbiAgICAvLyAuYXR0cignd2lkdGgnLCBmdW5jdGlvbihkLCBpKXtcbiAgICAvLyAgIGlmIChob3Jpem9udGFsUSkgeyByZXR1cm4gdGlja0xhYmVsRm9udFNpemUgfVxuICAgIC8vICAgaWYgKHZlcnRpY2FsUSkgeyByZXR1cm4gc3BhY2VYIC0gdGlja0xlbmd0aCB9XG4gICAgLy8gfSlcbiAgICAvLyAuYXR0cignaGVpZ2h0JywgZnVuY3Rpb24oZCwgaSl7XG4gICAgLy8gICBpZiAodmVydGljYWxRKSB7IHJldHVybiB0aWNrTGFiZWxGb250U2l6ZSB9XG4gICAgLy8gICBpZiAoaG9yaXpvbnRhbFEpIHsgcmV0dXJuIHNwYWNlWSAtIHRpY2tMZW5ndGggfVxuICAgIC8vIH0pXG5cblxuICAgIC8vIGZvciBlYWNoIHRpY2sgY29udGFpbmVyXG4gICAgdmFyIHRpY2tzID0gY29udGFpbmVyLnNlbGVjdEFsbCgnZzpub3QoLnRvLXJlbW92ZSkuJytvYmpDbGFzcykuZWFjaChmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhciB0aGF0ID0gZDMuc2VsZWN0KHRoaXMpLnN0eWxlKCdvcGFjaXR5JywgMSlcblxuICAgICAgLy8gbWFrZSBhbmQgbW92ZSB0aWNrXG4gICAgICB2YXIgdGljayA9IHNhZmVTZWxlY3QodGhhdCwgJ2xpbmUnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ3RpY2snKSlcbiAgICAgIC5hdHRyKFwieDFcIiwgMClcbiAgICAgIC5hdHRyKFwieDJcIiwgaG9yaXpvbnRhbFEgPyAwIDogb3JpZW50ID09IFwibGVmdFwiID8gLXRpY2tMZW5ndGggOiB0aWNrTGVuZ3RoKVxuICAgICAgLmF0dHIoXCJ5MVwiLCAwKVxuICAgICAgLmF0dHIoJ3kyJywgIHZlcnRpY2FsUSA/IDAgOiBvcmllbnQgPT0gXCJ0b3BcIiA/IC10aWNrTGVuZ3RoIDogdGlja0xlbmd0aClcbiAgICAgIC5hdHRyKCdzdHJva2UnLCB0aWNrU3Ryb2tlKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIHRpY2tTdHJva2VXaWR0aClcbiAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKSB7XG4gICAgICAgIHZhclxuICAgICAgICB4ID0gbW92ZVhCeShkLCBpLCBob3Jpem9udGFsUSwgY2F0ZWdvcmljYWxRLCBvYmplY3RTaXplKSxcbiAgICAgICAgeSA9IG1vdmVZQnkoZCwgaSwgdmVydGljYWxRLCBjYXRlZ29yaWNhbFEsIG9iamVjdFNpemUpLFxuICAgICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAgIHJldHVybiB0XG4gICAgICB9KVxuXG4gICAgICAvLyBtYWtlIGFuZCBtb3ZlIGxhYmVsXG4gICAgICB2YXIgbGFiZWwgPSBzYWZlU2VsZWN0KHRoYXQsICd0ZXh0JywgaHlwZW5hdGUobmFtZXNwYWNlLCdsYWJlbCcpKVxuICAgICAgLnRleHQoZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIHZhciBzID0gdHlwZW9mIGQgPT0gJ251bWJlcicgPyByb3VuZChkLCByb3VuZFRvKSA6IGRcbiAgICAgICAgcyA9IHRydW5jYXRlU3RyaW5nKFN0cmluZyhzKSwgKGhvcml6b250YWxRID8gc3BhY2VZIDogc3BhY2VYKSAtIHRpY2tMZW5ndGgtdGlja0xhYmVsTWFyZ2luLXRpY2tUaWNrTGFiZWxTcGFjZXIsIHRpY2tMYWJlbEZvbnRTaXplICogMC40NSlcbiAgICAgICAgcmV0dXJuIHNcbiAgICAgIH0pXG4gICAgICAuYXR0cignZm9udC1zaXplJywgdGlja0xhYmVsRm9udFNpemUpXG4gICAgICAuYXR0cigndGV4dC1hbmNob3InLCB0aWNrTGFiZWxUZXh0QW5jaG9yKVxuICAgICAgLy8gdHJ1bmNhdGVUZXh0KGxhYmVsLCBsYWJlbC50ZXh0KCksIG9yaWVudCwgdGlja0xlbmd0aCwgaG9yaXpvbnRhbFEgPyBzcGFjZVkgOiBzcGFjZVgsIG92ZXJmbG93USlcblxuICAgICAgbGFiZWwuYXR0cigndHJhbnNmb3JtJywgZnVuY3Rpb24oZCwgaSkge1xuICAgICAgICB2YXJcbiAgICAgICAgcmVjdCA9IGQzLnNlbGVjdCh0aGlzKS5ub2RlKCkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCksXG4gICAgICAgIGxlbmcgPSBkMy5zZWxlY3QodGhpcykubm9kZSgpLmdldENvbXB1dGVkVGV4dExlbmd0aCgpLFxuICAgICAgICB4ID0gbW92ZVhCeShkLCBpLCBob3Jpem9udGFsUSwgY2F0ZWdvcmljYWxRLCBvYmplY3RTaXplKSxcbiAgICAgICAgeSA9IG1vdmVZQnkoZCwgaSwgdmVydGljYWxRLCBjYXRlZ29yaWNhbFEsIG9iamVjdFNpemUpXG4gICAgICAgIC8vIG9uIHJlY2FsbCwgcmVjdCBjaGFuZ2VzIGJlY2F1c2Ugb2Ygcm90YXRpb24gc28gbmVlZCBNYXRoLm1pbihyZWN0LmhlaWdodCwgcmVjdC53aWR0aClcblxuICAgICAgICB2YXIgcyA9IE1hdGguc2luKHRpY2tMYWJlbFJvdGF0aW9uKSAqIGxlbmcgKiAwXG5cbiAgICAgICAgaWYgKG9yaWVudCA9PSAndG9wJykge1xuICAgICAgICAgIHkgPSAtKHRpY2tMZW5ndGgrdGlja1RpY2tMYWJlbFNwYWNlcik7XG4gICAgICAgICAgLy8geSA9IHRpY2tMZW5ndGgrdGlja1RpY2tMYWJlbFNwYWNlcjtcblxuICAgICAgICAgIC8vIHkgLT0gTWF0aC5tYXgocmVjdC5oZWlnaHQsIHJlY3Qud2lkdGgpO1xuICAgICAgICAgIHggKz0gTWF0aC5taW4ocmVjdC5oZWlnaHQsIHJlY3Qud2lkdGgpICogMC4yNVxuICAgICAgICAgIC8vIHggLT0gbGVuZyAqIDAuMjUgKyBzXG4gICAgICAgIH1cbiAgICAgICAgaWYgKG9yaWVudCA9PSAnYm90dG9tJykge1xuICAgICAgICAgIHkgPSB0aWNrTGVuZ3RoK3RpY2tUaWNrTGFiZWxTcGFjZXI7XG4gICAgICAgICAgeCArPSBNYXRoLm1pbihyZWN0LmhlaWdodCwgcmVjdC53aWR0aCkgKiAwLjI1XG4gICAgICAgICAgLy8geCArPSBsZW5nICogMC4yNSAtIHNcbiAgICAgICAgfVxuICAgICAgICBpZiAob3JpZW50ID09ICdsZWZ0Jykge1xuICAgICAgICAgIHggLT0gKHRpY2tMZW5ndGgrdGlja1RpY2tMYWJlbFNwYWNlcik7XG4gICAgICAgICAgLy8geSArPSByZWN0LmhlaWdodCAqIDAuNTsgeS09IHJlY3QuaGVpZ2h0LzRcbiAgICAgICAgICB5ICs9IE1hdGgubWluKHJlY3QuaGVpZ2h0LCByZWN0LndpZHRoKSAqIDAuMjVcbiAgICAgICAgICAvLyB5ICs9IGxlbmcgKiAwLjI1XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG9yaWVudCA9PSAncmlnaHQnKSB7XG4gICAgICAgICAgeCArPSAodGlja0xlbmd0aCt0aWNrVGlja0xhYmVsU3BhY2VyKTtcbiAgICAgICAgICAvLyB5ICs9IE1hdGgubWluKHJlY3QuaGVpZ2h0LCByZWN0LndpZHRoKSAqIDAuMjVcbiAgICAgICAgICAvLyB5ICs9IGxlbmcgKiAwLjI1XG4gICAgICAgICAgeSArPSByZWN0LmhlaWdodCAqIDAuNTsgeS09IHJlY3QuaGVpZ2h0LzRcbiAgICAgICAgfVxuXG4gICAgICAgIHZhclxuICAgICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknLFxuICAgICAgICByID0gJ3JvdGF0ZSgnK3RpY2tMYWJlbFJvdGF0aW9uKycpJ1xuICAgICAgICByZXR1cm4gdCArIHJcbiAgICAgIH0pXG4gICAgICAub24oJ21vdXNlbW92ZScsIGxhYmVsSG92ZXIpXG4gICAgICAub24oJ21vdXNlb3V0JywgbGFiZWxIb3Zlck9mZilcbiAgICAgIC5vbignY2xpY2snLCB0aWNrTGFiZWxPbkNsaWNrKVxuICAgICAgLy8gLmF0dHIoJ2NsaXAtcGF0aCcsICd1cmwoIycraHlwZW5hdGUobmFtZXNwYWNlLCd0aWNrLWxhYmVsLWNsaXAtcGF0aCcpKycpJylcblxuICAgICAgLy8gYWRkIGd1aWRsaW5lcyBhcyBuZWVkZWRcbiAgICAgICBpZiAoZ3VpZGVMaW5lc1EpIHtcbiAgICAgICAgIHZhciBnbGluZSA9IHNhZmVTZWxlY3QodGhhdCwgJ2xpbmUnLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdndWlkZWxpbmUnKSlcbiAgICAgICAgIC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgICAgLmF0dHIoXCJ4MVwiLCAwKVxuICAgICAgICAgLmF0dHIoXCJ4MlwiLCBob3Jpem9udGFsUSA/IDAgOiBvcmllbnQgPT0gXCJsZWZ0XCIgPyBndWlkZWxpbmVTcGFjZSA6IC1ndWlkZWxpbmVTcGFjZSlcbiAgICAgICAgIC5hdHRyKFwieTFcIiwgMClcbiAgICAgICAgIC5hdHRyKCd5MicsICB2ZXJ0aWNhbFEgPyAwIDogb3JpZW50ID09IFwidG9wXCIgPyBndWlkZWxpbmVTcGFjZSA6IC1ndWlkZWxpbmVTcGFjZSlcbiAgICAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKSB7XG4gICAgICAgICAgIHZhclxuICAgICAgICAgICB4ID0gbW92ZVhCeShkLCBpLCBob3Jpem9udGFsUSwgY2F0ZWdvcmljYWxRLCBvYmplY3RTaXplKSxcbiAgICAgICAgICAgeSA9IG1vdmVZQnkoZCwgaSwgdmVydGljYWxRLCBjYXRlZ29yaWNhbFEsIG9iamVjdFNpemUpLFxuICAgICAgICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAgICAgIHJldHVybiB0XG4gICAgICAgICB9KVxuICAgICAgIH0gZWxzZSB7IHRoYXQuc2VsZWN0KCdsaW5lLicraHlwZW5hdGUobmFtZXNwYWNlLCAnZ3VpZGVsaW5lJykpLnJlbW92ZSgpIH1cblxuICAgIH0pXG5cbiAgICAvLyBhcHBseSBhbHRlcm5hdGluZyBndWlkbGluZSB0aGlja25lc3NcbiAgICBpZiAoZ3VpZGVMaW5lc1EpIHtcbiAgICAgIGNvbnRhaW5lci5zZWxlY3RBbGwoJy4nK2h5cGVuYXRlKG5hbWVzcGFjZSwnZ3VpZGVsaW5lJykpXG4gICAgICAuYXR0cignc3Ryb2tlJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIGlmIChpICUgMiA9PSAwKSB7IHJldHVybiBtb2RpZnlIZXhpZGVjaW1hbENvbG9yTHVtaW5hbmNlKGd1aWRlTGluZVN0cm9rZSwgMC44KSB9XG4gICAgICAgIHJldHVybiBndWlkZUxpbmVTdHJva2VcbiAgICAgIH0pXG4gICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIGlmIChpICUgMiA9PSAwKSB7IHJldHVybiBndWlkZUxpbmVTdHJva2VXaWR0aCAqMC44fVxuICAgICAgICByZXR1cm4gZ3VpZGVMaW5lU3Ryb2tlV2lkdGhcbiAgICAgIH0pXG4gICAgICAuYXR0cignbWlub3InLCBmdW5jdGlvbihkLCBpKXtyZXR1cm4gaSUyID09IDB9KVxuICAgIH1cblxuXG4gICAgLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuICAgICoqIE1ha2UgdGhlIGxpbmUgb2YgdGhlIGF4aXNcbiAgICAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4gICAgdmFyIGxpbmUgPSBzYWZlU2VsZWN0KHNlbGVjdGlvbiwgJ3BhdGgnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ2xpbmUnKSlcbiAgICAvLyAuYXR0cigneDEnLCAwKVxuICAgIC8vIC5hdHRyKCd4MicsIGhvcml6b250YWxRID8gc3BhY2VYIDogMClcbiAgICAvLyAuYXR0cigneTEnLCAwKVxuICAgIC8vIC5hdHRyKCd5MicsIGhvcml6b250YWxRID8gMCA6IHNwYWNlWSlcbiAgICAuYXR0cignZCcsXG4gICAgICBob3Jpem9udGFsUVxuICAgICAgPyAnTSAwLDAgSCcgKyBzcGFjZVggKyAnLDAnXG4gICAgICA6ICdNIDAsMCBWIDAsJyArIHNwYWNlWVxuICAgIClcbiAgICAuYXR0cignc3Ryb2tlJywgbGluZVN0cm9rZSlcbiAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgbGluZVN0cm9rZVdpZHRoKVxuICAgIC5jbGFzc2VkKCdheGlzLWxpbmUnLCB0cnVlKVxuXG5cbiAgfVxuXG4gIC8vIGhvdmVyIG9mIGxhYmVsIHNob3cgZnVsbCB0ZXh0IGxhYmVsIGluIGNhc2UgaXQgaXMgdHJ1bmNhdGVkXG4gIGZ1bmN0aW9uIGxhYmVsSG92ZXIoZCwgaSl7XG4gICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcykuc3R5bGUoJ2ZpbGwnLCAncmVkJylcbiAgICBkMy5zZWxlY3QodC5ub2RlKCkucGFyZW50Tm9kZSkuc2VsZWN0KFwibGluZS5cIitoeXBlbmF0ZShuYW1lc3BhY2UsJ3RpY2snKSlcbiAgICAuYXR0cihcInN0cm9rZVwiLCAncmVkJylcbiAgICAuYXR0cihcInN0cm9rZS13aWR0aFwiLCB0aWNrU3Ryb2tlV2lkdGgqMilcblxuICAgIGlmIChndWlkZUxpbmVzUSkge1xuICAgICAgZDMuc2VsZWN0KHQubm9kZSgpLnBhcmVudE5vZGUpLnNlbGVjdCgnbGluZS4nK2h5cGVuYXRlKG5hbWVzcGFjZSwgJ2d1aWRlbGluZScpKVxuICAgICAgLmF0dHIoJ3N0cm9rZScsICdyZWQnKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIGd1aWRlTGluZVN0cm9rZVdpZHRoKjIpXG4gICAgfVxuXG4gICAgdmFyIHMgPSB0eXBlb2YgZCA9PSAnbnVtYmVyJyA/IHJvdW5kKGQsIHJvdW5kVG8pIDogZFxuXG4gICAgdmFyIG0gPSBkMy5tb3VzZShkMy5zZWxlY3QoJ2h0bWwnKS5ub2RlKCkpXG4gICAgdmFyIGRpdiA9IHNhZmVTZWxlY3QoZDMuc2VsZWN0KCdib2R5JyksICdkaXYnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ2d1aWRlbGluZS10b29sdGlwJykpXG4gICAgLmF0dHIoJ2lkJywgaHlwZW5hdGUobmFtZXNwYWNlLCdndWlkZWxpbmUtdG9vbHRpcCcpKVxuICAgIC5zdHlsZSgncG9zaXRpb24nLCAnYWJzb2x1dGUnKVxuICAgIC5zdHlsZSgnbGVmdCcsIChkMy5ldmVudC5wYWdlWCsxNSkrJ3B4JylcbiAgICAuc3R5bGUoJ3RvcCcsIChkMy5ldmVudC5wYWdlWSsxNSkrJ3B4JylcbiAgICAuc3R5bGUoJ2JhY2tncm91bmQtY29sb3InLCAnd2hpdGUnKVxuICAgIC5zdHlsZSgnYm9yZGVyLWNvbG9yJywgJ2JsYWNrJylcbiAgICAvLyAuc3R5bGUoJ21pbi13aWR0aCcsICh0aWNrTGFiZWxGb250U2l6ZSAqIChTdHJpbmcocykuc3BsaXQoJy4nKVswXS5sZW5ndGgrMykpKydweCcpXG4gICAgLy8gLnN0eWxlKCdtaW4taGVpZ2h0JywgKHRpY2tMYWJlbEZvbnRTaXplICogKFN0cmluZyhzKS5zcGxpdCgnLicpWzBdLmxlbmd0aCszKSkrJ3B4JylcbiAgICAuc3R5bGUoJ2JvcmRlci1yYWRpdXMnLCAnMTBweCcpXG4gICAgLnN0eWxlKCdkaXNwbGF5JywgJ2ZsZXgnKVxuICAgIC5zdHlsZSgnanVzdGlmeS1jb250ZW50JywgJ2NlbnRlcicpXG4gICAgLnN0eWxlKCd0ZXh0LWFsaWduJywgJ21pZGRsZScpXG4gICAgLnN0eWxlKCdwYWRkaW5nJywgNCtcInB4XCIpXG5cbiAgICAuc3R5bGUoJ2JvcmRlci1zdHlsZScsICdzb2xpZCcpXG4gICAgLnN0eWxlKCdib3JkZXItd2lkdGgnLCAyKVxuXG4gICAgdmFyIHRleHQgPSBzYWZlU2VsZWN0KGRpdiwgJ2RpdicpXG4gICAgLnRleHQodGlja0xhYmVsT25Ib3ZlckZ1bmMocywgaSkpXG4gICAgLnN0eWxlKCdjb2xvcicsICdibGFjaycpXG4gICAgLnN0eWxlKCdhbGlnbi1zZWxmJywgJ2NlbnRlcicpXG5cbiAgICB2YXIgYmJveCA9IGRpdi5ub2RlKCkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICBpZiAoYmJveC54ICsgYmJveC53aWR0aCA+IHdpbmRvdy5pbm5lcldpZHRoKSB7XG4gICAgICBkaXYuc3R5bGUoJ2xlZnQnLCAoZDMuZXZlbnQucGFnZVgtMTUtMzAwKSsncHgnKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGxhYmVsSG92ZXJPZmYoZCwgaSl7XG4gICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcykuc3R5bGUoJ2ZpbGwnLCAnYmxhY2snKVxuICAgIGQzLnNlbGVjdCh0Lm5vZGUoKS5wYXJlbnROb2RlKS5zZWxlY3QoXCJsaW5lLlwiK2h5cGVuYXRlKG5hbWVzcGFjZSwndGljaycpKVxuICAgIC5hdHRyKFwic3Ryb2tlXCIsIHRpY2tTdHJva2UpXG4gICAgLmF0dHIoXCJzdHJva2Utd2lkdGhcIiwgdGlja1N0cm9rZVdpZHRoKVxuXG4gICAgaWYgKGd1aWRlTGluZXNRKSB7XG4gICAgICB2YXIgZ2xpbmUgPSBkMy5zZWxlY3QodC5ub2RlKCkucGFyZW50Tm9kZSkuc2VsZWN0KCdsaW5lLicraHlwZW5hdGUobmFtZXNwYWNlLCAnZ3VpZGVsaW5lJykpXG4gICAgICB2YXIgbWlub3JRID0gZ2xpbmUuYXR0cignbWlub3InKVxuICAgICAgZ2xpbmUuYXR0cignc3Ryb2tlJywgZnVuY3Rpb24oZCwgaWkpe1xuICAgICAgICBpZiAobWlub3JRID09ICd0cnVlJykgeyByZXR1cm4gbW9kaWZ5SGV4aWRlY2ltYWxDb2xvckx1bWluYW5jZShndWlkZUxpbmVTdHJva2UsIDAuOCkgfVxuICAgICAgICByZXR1cm4gZ3VpZGVMaW5lU3Ryb2tlXG4gICAgICB9KVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIGZ1bmN0aW9uKGQsIGlpKXtcbiAgICAgICAgaWYgKG1pbm9yUSA9PSAndHJ1ZScpIHsgcmV0dXJuIGd1aWRlTGluZVN0cm9rZVdpZHRoICowLjh9XG4gICAgICAgIHJldHVybiBndWlkZUxpbmVTdHJva2VXaWR0aFxuICAgICAgfSlcbiAgICB9XG4gICAgZDMuc2VsZWN0KFwiI1wiK2h5cGVuYXRlKG5hbWVzcGFjZSwnZ3VpZGVsaW5lLXRvb2x0aXAnKSkucmVtb3ZlKClcbiAgfVxuXG5cblxuICByZXR1cm4gYXhpc1xufVxuIiwiaW1wb3J0IHtoeXBlbmF0ZSwgc2FmZVNlbGVjdH0gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7c2V0dXBDb250YWluZXIsIGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3QsIGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXJ9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHt1bmlxdWUsIGZsYXR0ZW59IGZyb20gJy4vYXJyYXktZnVuY3Rpb25zJztcbmltcG9ydCB7Z3JvdXBpbmdTcGFjZXJ9IGZyb20gJy4vZ3JvdXBpbmctc3BhY2VyJztcbmltcG9ydCB7Y29sb3JGdW5jdGlvbiBhcyBDRn0gZnJvbSAnLi9jb2xvci1mdW5jdGlvbic7XG5pbXBvcnQge3Rvb2x0aXAgYXMgVFRpcH0gZnJvbSAnLi90b29sdGlwJztcbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCQVIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbi8qKlxuICogQ3JlYXRlcyBhIGJhclxuICpcbiAqIHtAbGluayBodHRwczovL3N1bW5ldXJvbi5naXRsYWIuaW8vZDNzbS9kZW1vcy9iYXItY2hhcnQtc2FtZS1kYXRhLWNvbXBsZXgtZ3JvdXBpbmcvaW5kZXguaHRtbCBEZW1vfVxuICogQGNvbnN0cnVjdG9yIGJhclxuICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IHNlbGVjdGlvblxuICogQG5hbWVzcGFjZSBiYXJcbiAqIEByZXR1cm5zIHtmdW5jdGlvbn0gYmFyXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBiYXIgKCBzZWxlY3Rpb24gKSB7XG4gIC8qXG4gIEFzc3VtZXMgdGhhdCBkYXRhIGlzIGxpc3QgYW4gb2JqZWN0LlxuXG4gIFRoZSBrZXlzIG9mIGRhdGEgd2lsbCBiZSBib3VuZCB0byB0aGUgYmFycy5cbiAgVGhlIHZhbHVlRXh0cmFjdG9yIGZ1bmN0aW9uIHdpbGwgZXh0cmFjdCB0aGUgdmFsdWUgZnJvbSBkYXRhW2tleV0uXG5cbiAgR3JvdXBpbmcgY2FuIGJlIHVzZWQgaWYgZGVzaXJlZC4gSXQgc2hvdWxkIGJlIGFuIGFyYml0cmFyeSBjb21wbGV4IGxpc3Qgd2hlcmVcbiAgdGhlIHZhbHVlcyBhcmUgc3RyaW5ncyBtYXRjaGluZyBrZXlzIGluIGRhdGEuXG4gICovXG4gIHZhclxuICAvKipcbiAgKiBEYXRhIHRvIHBsb3QuIEFzc3VtZWQgdG8gYmUgYSBvYmplY3QsIHdoZXJlIGVhY2gga2V5IGNvcnJlc3BvbmRzIHRvIGEgYmFyXG4gICogKHNlZSB7QGxpbmsgYmFyI2RhdGF9KVxuICAqIEBwYXJhbSB7T2JqZWN0fSBbZGF0YT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZGF0YSxcbiAgLyoqXG4gICogV2hpY2ggZGlyZWN0aW9uIHRvIHJlbmRlciB0aGUgYmFycyBpblxuICAqIChzZWUge0BsaW5rIGJhciNvcmllbnR9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbb3JpZW50PSdob3Jpem9udGFsJ11cbiAgKiBAbWVtYmVyb2YgYmFyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvcmllbnQ9J2hvcml6b250YWwnLFxuICAvKipcbiAgKiBBbW91bnQgb2YgaG9yaXpvbnRhbCBzcGFjZSAoaW4gcGl4ZWxzKSBhdmFpYmxlIHRvIHJlbmRlciB0aGUgYmFyIGluXG4gICogKHNlZSB7QGxpbmsgYmFyI3NwYWNlWH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVg9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlWCxcbiAgLyoqXG4gICogQW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBiYXIgaW5cbiAgKiAoc2VlIHtAbGluayBiYXIuc3BhY2VZfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlWT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VZLFxuXG4gIC8qKlxuICAqIFdoZXRoZXIgb3Igbm90IHRvIGFsbG93IGJhciB0byByZW5kZXIgZWxlbWVudHMgcGFzcyB0aGUgbWFpbiBzcGF0aWFsIGRpbWVuc2lvblxuICAqIGdpdmVuIHRoZSBvcmllbnRhdGlvbiAoc2VlIHtAbGluayBiYXIjb3JpZW50fSksIHdoZXJlIHtAbGluayBiYXIjb3JpZW50fT1cImhvcml6b250YWxcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgYmFyI3NwYWNlWH0gYW5kIHdoZXJlIHtAbGluayBiYXIjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJhciNzcGFjZVl9XG4gICogQHBhcmFtIHtib29sZWFufSBbb3ZlcmZsb3dRPWZhbHNlXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG92ZXJmbG93USA9IGZhbHNlLFxuXG4gIC8qKlxuICAqIEFuIGFycmF5IC0gcHV0YXRpdmVseSBvZiBvdGhlciBhcnJheXMgLSBkZXBpY3RpbmcgaG93IGJhcnMgc2hvdWxkIGJlIGFycmFuZ2VkXG4gICogQHBhcmFtIHtBcnJheVtdfSBbZ3JvdXBpbmc9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGdyb3VwaW5nLFxuXG4gIC8qKlxuICAqIEhvdyB0byBnZXQgdGhlIHZhbHVlIG9mIHRoZSBiYXJcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdmFsdWVFeHRyYWN0b3I9ZnVuY3Rpb24oa2V5LCBpbmRleCkgeyByZXR1cm4gZGF0YVtrZXldIH1dXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV0gfSxcbiAgLyoqXG4gICogSG93IHRvIHNvcnQgdGhlIGJhcnMgLSBpZiB7QGxpbmsgYmFyI2dyb3VwaW5nfSBpcyBub3QgcHJvdmlkZWQuXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3NvcnRpbmdGdW5jdGlvbj1mdW5jdGlvbihrZXlBLCBrZXlCKSB7cmV0dXJuIGQzLmRlc2NlbmRpbmcoZGF0YVtrZXlBXSwgZGF0YVtrZXlCXSl9XVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKGtleUEsIGtleUIpIHtyZXR1cm4gZDMuZGVzY2VuZGluZyhkYXRhW2tleUFdLCBkYXRhW2tleUJdKX0sXG5cbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCBiYXIgdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZT1kMy5zY2FsZUxpbmVhcl1cbiAgKiBAbWVtYmVyb2YgYmFyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKCksXG4gIC8qKlxuICAqIFRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSBzY2FsZSAoc2VlIHtAbGluayBiYXIjc2NhbGV9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbZG9tYWluUGFkZGluZz0wLjVdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZG9tYWluUGFkZGluZyA9IDAuNSxcblxuICAvKipcbiAgKiBEZWZhdWx0IHNwYWNlIGZvciB0aGUgc3BhY2VyIChwZXJjZW50YWdlKSBvZiBtYWluIGRpbWVuc2lvbiBnaXZlbiB0aGUgb3JpZW50YXRpb25cbiAgKiAoc2VlIHtAbGluayBiYXIjb3JpZW50fSksIHdoZXJlIHtAbGluayBiYXIjb3JpZW50fT1cImhvcml6b250YWxcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgYmFyI3NwYWNlWH0gYW5kIHdoZXJlIHtAbGluayBiYXIjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJhciNzcGFjZVl9IGJldHdlZW4gYmFyc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbb2JqZWN0U3BhY2VyPTAuMDVdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0U3BhY2VyID0gMC4wNSxcbiAgLyoqXG4gICogVGhlIG1pbmltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmVcbiAgKiBAcGFyYW0ge251bWJlcn0gW21pbk9iamVjdFNpemU9NTBdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWluT2JqZWN0U2l6ZSA9IDUwLFxuICAvKipcbiAgKiBUaGUgbWF4aW11bSBzaXplIHRoYXQgYW4gb2JqZWN0IGNhbiBiZVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbWF4T2JqZWN0U2l6ZT0xMDBdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWF4T2JqZWN0U2l6ZSA9IDEwMCxcblxuICAvKipcbiAgKiBUaGUgc3Ryb2tlIHdpZHRoIG9mIHRoZSBiYXJzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtiYXJTdHJva2VXaWR0aD0yXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJhclN0cm9rZVdpZHRoID0gMixcbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgQ29sb3JGdW5jdGlvblxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtjb2xvckZ1bmN0aW9uID0gY29sb3JGdW5jdGlvbigpXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGNvbG9yRnVuY3Rpb24gPSBDRigpLFxuXG5cbiAgLyoqXG4gICogQ29sb3Igb2YgdGhlIGJhY2tncm91bmRcbiAgKiBAcGFyYW0ge3N0cmluZ30gW2JhY2tncm91bmRGaWxsPVwidHJhbnNwYXJlbnRcIl1cbiAgKiBAbWVtYmVyb2YgYmFyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCcsXG4gIC8qKlxuICAqIE5hbWVzcGFjZSBmb3IgYWxsIGl0ZW1zIG1hZGUgYnkgdGhpcyBpbnN0YW5jZSBvZiBiYXJcbiAgKiBAcGFyYW0ge3N0cmluZ30gW25hbWVzcGFjZT1cImQzc20tYmFyXCJdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbmFtZXNwYWNlID0gJ2Qzc20tYmFyJyxcbiAgLyoqXG4gICogQ2xhc3MgbmFtZSBmb3IgYmFyIGNvbnRhaW5lciAoPGc+IGVsZW1lbnQpXG4gICogQHBhcmFtIHtzdHJpbmd9IFtvYmplY3RDbGFzcz1cImJhclwiXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdENsYXNzID0gJ2JhcicsXG5cbiAgLyoqXG4gICogRHVyYXRpb24gb2YgYWxsIHRyYW5zaXRpb25zIG9mIHRoaXMgZWxlbWVudFxuICAqIEBwYXJhbSB7bnVtYmVyfSBbdHJhbnNpdGlvbkR1cmF0aW9uPTEwMDBdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMCxcbiAgLyoqXG4gICogRWFzaW5nIGZ1bmN0aW9uIGZvciB0cmFuc2l0aW9uc1xuICAqIEBwYXJhbSB7ZDMuZWFzZX0gW2Vhc2VGdW5jPWQzLmVhc2VFeHBdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwLFxuXG4gIC8vIHVzZWZ1bCB2YWx1ZXMgdG8gZXh0cmFjdCB0byBwcmV2ZW50IHJlLWNhbGN1bGF0aW9uXG4gIC8qKlxuICAqIFRoZSBrZXlzIG9mIHRoZSBiYXJzXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW2JhcktleXM9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJhcktleXMsXG4gIC8qKlxuICAqIFRoZSB2YWx1ZXMgb2YgdGhlIGJhcnNcbiAgKiBAcGFyYW0ge251bWJlcltdfSBbYmFyVmFsdWVzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYmFyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBiYXJWYWx1ZXMsXG4gIC8qKlxuICAqIFRoZSBvYmplY3RTaXplIChhY3R1YWwgd2lkdGgpIHVzZWQgYnkgdGhlIGJhcnNcbiAgKiBAcGFyYW0ge251bWJlcn0gW29iamVjdFNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdFNpemUsXG4gIC8qKlxuICAqIFRoZSBzcGFjZXJTaXplIChhY3R1YWwgd2lkdGgpIHVzZWQgYnkgdGhlIHNwYWNlcnMgYmV0d2VlbiB0aGUgYmFyc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VyU2l6ZT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VyU2l6ZSxcbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgVG9vbHRpcFxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt0b29sdGlwPXRvb2x0aXAoKV1cbiAgKiBAbWVtYmVyb2YgYmFyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0b29sdGlwID0gVFRpcCgpLFxuICBiYXJQZXJjZW50ID0gMVxuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHNlbGVjdGlvbiBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBkMy5zZWxlY3Rpb259XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2VsZWN0aW9uID0gc2VsZWN0aW9uXG4gICAqL1xuICBiYXIuc2VsZWN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCBiYXIpIDogc2VsZWN0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBkYXRhXG4gICAqIChzZWUge0BsaW5rIGJhciNkYXRhfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBvYmplY3R9XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqL1xuICBiYXIuZGF0YSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGF0YSA9IF8sIGJhcikgOiBkYXRhOyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBvcmllbnQgb2YgdGhlIGJhcnNcbiAgICogKHNlZSB7QGxpbmsgYmFyI29yaWVudH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgb2JqZWN0fVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgYmFyLm9yaWVudCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3JpZW50ID0gXywgYmFyKSA6IG9yaWVudDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIGhvcml6b250YWwgc3BhY2UgaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIGJhciNzcGFjZVh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV0gc2hvdWxkIGJlIGEgbnVtYmVyID4gMFxuICAgKiBAcmV0dXJucyB7YmFyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlWCA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmFyLnNwYWNlWCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3BhY2VYID0gXywgYmFyKSA6IHNwYWNlWDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBiYXIjc3BhY2VZfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVkgPSB1bmRlZmluZWRcbiAgICovXG4gIGJhci5zcGFjZVkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWSA9IF8sIGJhcikgOiBzcGFjZVk7IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IGJhciBpcyBhbGxvd2VkIHRvIGdvIGJleW9uZCBzcGVjaWZpZWQgZGltZW5zaW9uc1xuICAgKiAoc2VlIHtAbGluayBiYXIjc3BhY2VYfSlcbiAgICogQHBhcmFtIHtib29sZWFufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgYm9vbGVhbn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvdmVyZmxvd1EgPSBmYWxzZVxuICAgKi9cbiAgYmFyLm92ZXJmbG93USA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3ZlcmZsb3dRID0gXywgYmFyKSA6IG92ZXJmbG93UTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBncm91cGluZyBvZiB0aGUgYmFyc1xuICAgKiAoc2VlIHtAbGluayBiYXIjZ3JvdXBpbmd9KVxuICAgKiBAcGFyYW0ge0FycmF5W119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBBcnJheVtdfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGdyb3VwaW5nID0gdW5kZWZpbmVkXG4gICAqL1xuICBiYXIuZ3JvdXBpbmcgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGdyb3VwaW5nID0gXywgYmFyKSA6IGdyb3VwaW5nOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHZhbHVlRXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGJhciN2YWx1ZUV4dHJhY3Rvcn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2YWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKGtleSwgaW5kZXgpIHsgcmV0dXJuIGRhdGFba2V5XSB9LFxuICAgKi9cbiAgYmFyLnZhbHVlRXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZUV4dHJhY3RvciA9IF8sIGJhcikgOiB2YWx1ZUV4dHJhY3RvcjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzb3J0aW5nRnVuY3Rpb25cbiAgICogKHNlZSB7QGxpbmsgYmFyI3NvcnRpbmdGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihrZXlBLCBrZXlCKSB7cmV0dXJuIGQzLmRlc2NlbmRpbmcoZGF0YVtrZXlBXSwgZGF0YVtrZXlCXSl9LFxuICAgKi9cbiAgYmFyLnNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc29ydGluZ0Z1bmN0aW9uID0gXywgYmFyKSA6IHNvcnRpbmdGdW5jdGlvbjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzY2FsZSBmb3Igd2hpY2ggdGhlIGJhciB2YWx1ZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICAqIChzZWUge0BsaW5rIGJhciNzY2FsZX0pXG4gICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBkMy5zY2FsZX1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcbiAgICovXG4gIGJhci5zY2FsZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGUgPSBfLCBiYXIpIDogc2NhbGU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgc2NhbGVcbiAgICogKHNlZSB7QGxpbmsgYmFyI2RvbWFpblBhZGRpbmd9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBkb21haW5QYWRkaW5nID0gMC41XG4gICAqL1xuICBiYXIuZG9tYWluUGFkZGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZG9tYWluUGFkZGluZyA9IF8sIGJhcikgOiBkb21haW5QYWRkaW5nOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgb2JqZWN0U3BhY2VyXG4gICAqIChzZWUge0BsaW5rIGJhciNvYmplY3RTcGFjZXJ9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RTcGFjZXIgPSAwLjA1XG4gICAqL1xuICBiYXIub2JqZWN0U3BhY2VyID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RTcGFjZXIgPSBfLCBiYXIpIDogb2JqZWN0U3BhY2VyOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1pbk9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgYmFyI21pbk9iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtaW5PYmplY3RTaXplID0gNTBcbiAgICovXG4gIGJhci5taW5PYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtaW5PYmplY3RTaXplID0gXywgYmFyKSA6IG1pbk9iamVjdFNpemU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbWF4T2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBiYXIjbWF4T2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1heE9iamVjdFNpemUgPSAxMDBcbiAgICovXG4gIGJhci5tYXhPYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtYXhPYmplY3RTaXplID0gXywgYmFyKSA6IG1heE9iamVjdFNpemU7IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBiYXJTdHJva2VXaWR0aFxuICAgKiAoc2VlIHtAbGluayBiYXIjYmFyU3Ryb2tlV2lkdGh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBiYXJTdHJva2VXaWR0aCA9IDJcbiAgICovXG4gIGJhci5iYXJTdHJva2VXaWR0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFyU3Ryb2tlV2lkdGggPSBfLCBiYXIpIDogYmFyU3Ryb2tlV2lkdGg7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgY29sb3JGdW5jdGlvblxuICAgKiAoc2VlIHtAbGluayBiYXIjY29sb3JGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGNvbG9yRnVuY3Rpb24gPSBjb2xvckZ1bmN0aW9uKClcbiAgICovXG4gIGJhci5jb2xvckZ1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjb2xvckZ1bmN0aW9uID0gXywgYmFyKSA6IGNvbG9yRnVuY3Rpb247IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBiYWNrZ3JvdW5kRmlsbFxuICAgKiAoc2VlIHtAbGluayBiYXIjYmFja2dyb3VuZEZpbGx9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCdcbiAgICovXG4gIGJhci5iYWNrZ3JvdW5kRmlsbCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFja2dyb3VuZEZpbGwgPSBfLCBiYXIpIDogYmFja2dyb3VuZEZpbGw7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbmFtZXNwYWNlXG4gICAqIChzZWUge0BsaW5rIGJhciNuYW1lc3BhY2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBuYW1lc3BhY2UgPSAnZDNzbS1iYXInXG4gICAqL1xuICBiYXIubmFtZXNwYWNlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChuYW1lc3BhY2UgPSBfLCBiYXIpIDogbmFtZXNwYWNlOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG9iamVjdENsYXNzXG4gICAqIChzZWUge0BsaW5rIGJhciNvYmplY3RDbGFzc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdENsYXNzID0gJ3RpY2stZ3JvdXAnXG4gICAqL1xuICBiYXIub2JqZWN0Q2xhc3MgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdENsYXNzID0gXywgYmFyKSA6IG9iamVjdENsYXNzOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRyYW5zaXRpb25EdXJhdGlvblxuICAgKiAoc2VlIHtAbGluayBiYXIjdHJhbnNpdGlvbkR1cmF0aW9ufSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMFxuICAgKi9cbiAgYmFyLnRyYW5zaXRpb25EdXJhdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodHJhbnNpdGlvbkR1cmF0aW9uID0gXywgYmFyKSA6IHRyYW5zaXRpb25EdXJhdGlvbjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBlYXNlRnVuY1xuICAgKiAoc2VlIHtAbGluayBiYXIjZWFzZUZ1bmN9KVxuICAgKiBAcGFyYW0ge2QzLmVhc2V9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBkMy5lYXNlfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGVhc2VGdW5jID0gZDMuZWFzZUV4cFxuICAgKi9cbiAgYmFyLmVhc2VGdW5jID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChlYXNlRnVuYyA9IF8sIGJhcikgOiBlYXNlRnVuYzsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFyS2V5c1xuICAgKiAoc2VlIHtAbGluayBiYXIjYmFyS2V5c30pXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBzdHJpbmdbXX1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBiYXJLZXlzID0gdW5kZWZpbmVkXG4gICAqL1xuICBiYXIuYmFyS2V5cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFyS2V5cyA9IF8sIGJhcikgOiBiYXJLZXlzOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGJhclZhbHVlc1xuICAgKiAoc2VlIHtAbGluayBiYXIjYmFyVmFsdWVzfSlcbiAgICogQHBhcmFtIHtudW1iZXJbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcltdfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGJhclZhbHVlcyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmFyLmJhclZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFyVmFsdWVzID0gXywgYmFyKSA6IGJhclZhbHVlczsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIGJhciNvYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0U2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmFyLm9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNpemUgPSBfLCBiYXIpIDogb2JqZWN0U2l6ZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzcGFjZXJTaXplXG4gICAqIChzZWUge0BsaW5rIGJhciNzcGFjZXJTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VyU2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmFyLnNwYWNlclNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlclNpemUgPSBfLCBiYXIpIDogc3BhY2VyU2l6ZTsgfTtcblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRvb2x0aXBcbiAgICogKHNlZSB7QGxpbmsgYmFyI3Rvb2x0aXB9KVxuICAgKiBAcGFyYW0ge3Rvb2x0aXB9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCB0b29sdGlwfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRvb2x0aXAgPSB0b29sdGlwKClcbiAgICovXG4gIGJhci50b29sdGlwID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0b29sdGlwID0gXywgYmFyKSA6IHRvb2x0aXA7IH07XG5cbiAgYmFyLmJhclBlcmNlbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhclBlcmNlbnQgPSBfLCBiYXIpIDogYmFyUGVyY2VudDsgfTtcblxuICBmdW5jdGlvbiBiYXIoKSB7XG4gICAgLy8gZm9yIGNvbnZlbmllbmNlIGluIGhhbmRsaW5nIG9yaWVudGF0aW9uIHNwZWNpZmljIHZhbHVlc1xuICAgIHZhciBob3Jpem9udGFsUSA9IChvcmllbnQgPT0gJ2hvcml6b250YWwnIHx8IG9yaWVudCA9PSAnYm90dG9tJyB8fCBvcmllbnQgPT0gJ3RvcCcpID8gdHJ1ZSA6IGZhbHNlXG4gICAgdmFyIHZlcnRpY2FsUSA9ICFob3Jpem9udGFsUVxuXG4gICAgLy8gYmFja2dyb3VuZCBjbGlwaW5nIHJlY3RhbmdsZVxuICAgIHZhciBiZ2NwUmVjdCA9IHt4OjAsIHk6MCwgd2lkdGg6IHNwYWNlWCwgaGVpZ2h0OnNwYWNlWX1cbiAgICB2YXIgY29udGFpbmVyID0gc2V0dXBDb250YWluZXIoIHNlbGVjdGlvbiwgbmFtZXNwYWNlLCBiZ2NwUmVjdCwgYmFja2dyb3VuZEZpbGwgKTtcblxuICAgIC8vIHRvIHByZXZlbnQgcmUtY2FsY3VsYXRpb24gYW5kIGdldHRlcnMgdG8gYmUgcGFzc2VkIHRvIGF4ZXNcbiAgICBiYXJLZXlzID0gZDMua2V5cyhkYXRhKVxuICAgIGJhclZhbHVlcyA9IGJhcktleXMubWFwKHZhbHVlRXh0cmFjdG9yKVxuXG4gICAgLy8gaWYgZ3JvdXBpbmcgaXMgdW5kZWZpbmVkIHNvcnQgYmFyS2V5cyBieSBzb3J0aW5nRnVuY3Rpb25cbiAgICB2YXIgb3JkZXJlZCA9IChncm91cGluZyA9PSB1bmRlZmluZWQpID8gYmFyS2V5cy5zb3J0KHNvcnRpbmdGdW5jdGlvbikgOiBncm91cGluZ1xuICAgIC8vIG9yZGVyZWQgbWlnaHQgYmUgbmVzdGVkIGRlcGVuZGluZyBvbiBncm91cGluZ1xuICAgIGJhcktleXMgPSBmbGF0dGVuKG9yZGVyZWQpXG5cbiAgICB2YXIgbnVtYmVyT2ZPYmplY3RzID0gYmFyS2V5cy5sZW5ndGhcbiAgICB2YXIgZXh0ZW50ID0gW01hdGgubWluKC4uLmJhclZhbHVlcykgLSBkb21haW5QYWRkaW5nLE1hdGgubWF4KC4uLmJhclZhbHVlcykgKyBkb21haW5QYWRkaW5nXTtcblxuXG5cbiAgICAvLyBzZXQgdGhlIHNjYWxlXG5cbiAgICBzY2FsZS5kb21haW4oZXh0ZW50KS5yYW5nZShob3Jpem9udGFsUVxuICAgICAgPyBbMCxzcGFjZVldXG4gICAgICA6IG9yaWVudCA9PSAncmlnaHQnXG4gICAgICAgID8gWzAsIHNwYWNlWF1cbiAgICAgICAgOiBbc3BhY2VYLCAwXVxuICAgIClcbiAgICB2YXIgc3BhY2UgPSBob3Jpem9udGFsUSA/IHNwYWNlWCA6IHNwYWNlWVxuICAgIC8vIGNhbGN1bGF0ZSBvYmplY3Qgc2l6ZVxuICAgIG9iamVjdFNpemUgPSAgKG9iamVjdFNpemUgPT0gdW5kZWZpbmVkKVxuICAgID8gY2FsY3VsYXRlV2lkdGhPZk9iamVjdChzcGFjZSwgbnVtYmVyT2ZPYmplY3RzLCBtaW5PYmplY3RTaXplLCBtYXhPYmplY3RTaXplLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICA6IG9iamVjdFNpemVcblxuICAgIC8vIGNhbGN1bGF0ZSBzcGFjZXIgc2l6ZSBpZiBuZWVkZWRcbiAgICBzcGFjZXJTaXplID0gKHNwYWNlclNpemUgPT0gdW5kZWZpbmVkKVxuICAgID8gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcihiYXJLZXlzLCBzcGFjZSwgb2JqZWN0U2l6ZSwgbnVtYmVyT2ZPYmplY3RzLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICA6IHNwYWNlclNpemVcbiAgICAvLyBtYWtlIHRoZSBuZXN0ZWQgZ3JvdXBzXG4gICAgdmFyIHNwYWNlckZ1bmN0aW9uID0gZ3JvdXBpbmdTcGFjZXIoKVxuICAgIC5ob3Jpem9udGFsUShob3Jpem9udGFsUSkuc2NhbGUoc2NhbGUpLm1vdmVieSgnY2F0ZWdvcnknKS5udW1iZXJPZk9iamVjdHMobnVtYmVyT2ZPYmplY3RzKVxuICAgIC5vYmplY3RDbGFzcyhvYmplY3RDbGFzcykub2JqZWN0U2l6ZShvYmplY3RTaXplKS5zcGFjZXJTaXplKHNwYWNlclNpemUpXG4gICAgLnRyYW5zaXRpb25EdXJhdGlvbih0cmFuc2l0aW9uRHVyYXRpb24pLmVhc2VGdW5jKGVhc2VGdW5jKVxuICAgIC5uYW1lc3BhY2UobmFtZXNwYWNlKVxuICAgIC8vIHNhZmUgZGVmYXVsdCBmdW5jdGlvblxuICAgIHZhciBkZWZhdWx0RXhpdCA9IHNwYWNlckZ1bmN0aW9uLmV4aXRGdW5jdGlvbigpXG5cbiAgICBzcGFjZXJGdW5jdGlvbi5leGl0RnVuY3Rpb24oZnVuY3Rpb24oc2VsKXtcbiAgICAgIC8vIHVzZSBkZWZhdWx0IHRvIG1vdmUgb2JqZWN0cyBvZmYgc2NyZWVuXG4gICAgICAvLyBjb25zb2xlLmxvZyhcIkVYSVRcIiwgc2VsLm5vZGVzKCksIG9iamVjdFNpemUsIHNjYWxlKGV4dGVudFsxXSkpXG4gICAgICBpZiAob2JqZWN0U2l6ZSA9PSB1bmRlZmluZWQpIHtjb25zb2xlLmxvZyhzZWwubm9kZXMoKSwgb2JqZWN0U2l6ZSl9XG4gICAgICBkZWZhdWx0RXhpdChzZWwpXG4gICAgICBzZWwuc2VsZWN0QWxsKCdnJykuY2xhc3NlZChcInRvLXJlbW92ZVwiLCB0cnVlKVxuICAgICAgLy8gc2hyaW5rIHJlY3RhbmdsZXMgaW4gYWRkaXRpb25cbiAgICAgIHNlbC5zZWxlY3RBbGwoJyogPiByZWN0JylcbiAgICAgIC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKVxuICAgICAgLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpIHtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUVxuICAgICAgICAgID8gMFxuICAgICAgICAgIDogMFxuICAgICAgICAsXG4gICAgICAgIHkgPSB2ZXJ0aWNhbFFcbiAgICAgICAgICA/IDBcbiAgICAgICAgICA6IHNjYWxlKGV4dGVudFsxXSlcbiAgICAgICAgLFxuICAgICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAgIHJldHVybiB0XG4gICAgICB9KVxuICAgICAgLmF0dHIoJ3dpZHRoJywgaG9yaXpvbnRhbFEgPyBvYmplY3RTaXplIDogMClcbiAgICAgIC5hdHRyKCdoZWlnaHQnLCB2ZXJ0aWNhbFEgPyBvYmplY3RTaXplIDogMClcbiAgICAgIC5yZW1vdmUoKVxuICAgIH0pXG5cblxuICAgIC8vIG1vdmUgc3R1ZmZcbiAgICBzcGFjZXJGdW5jdGlvbihjb250YWluZXIsIG9yZGVyZWQsIDApXG5cblxuXG5cblxuICAgIHZhciBwYXJlbnRJbmRleEFycmF5ID0gW11cbiAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnOm5vdCgudG8tcmVtb3ZlKS4nK29iamVjdENsYXNzKVxuICAgIC5lYWNoKGZ1bmN0aW9uKGQsIGkpe3BhcmVudEluZGV4QXJyYXkucHVzaChOdW1iZXIoZDMuc2VsZWN0KHRoaXMpLmF0dHIoJ3BhcmVudC1pbmRleCcpKSl9KVxuXG5cbiAgICBjb2xvckZ1bmN0aW9uID0gY29sb3JGdW5jdGlvbi5jb2xvckJ5KCkgPT0gJ2luZGV4J1xuICAgID8gY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KFswLCBNYXRoLm1heCguLi5wYXJlbnRJbmRleEFycmF5KV0pXG4gICAgOiBjb2xvckZ1bmN0aW9uLmRhdGFFeHRlbnQoZXh0ZW50KVxuXG5cblxuICAgIGNvbnRhaW5lci5zZWxlY3RBbGwoJ2cuJytvYmplY3RDbGFzcysnOm5vdCgudG8tcmVtb3ZlKScpLmVhY2goZnVuY3Rpb24oa2V5LCBpKSB7XG4gICAgICAvLyBjb25zb2xlLmxvZyhrZXksIHNjYWxlKGV4dGVudFsxXSkgLSBzY2FsZSh2YWx1ZUV4dHJhY3RvcihrZXksIGkpKSlcbiAgICAgIHZhciB0ID0gZDMuc2VsZWN0KHRoaXMpLFxuICAgICAgY3VycmVudERhdGEgPSBkYXRhW2tleV0sXG4gICAgICB2YWx1ZSA9IHZhbHVlRXh0cmFjdG9yKGtleSwgaSksXG4gICAgICBpID0gdC5hdHRyKCdwYXJlbnQtaW5kZXgnKSA9PSB1bmRlZmluZWQgPyBpIDogdC5hdHRyKCdwYXJlbnQtaW5kZXgnKSxcbiAgICAgIGZpbGxDb2xvciA9IGNvbG9yRnVuY3Rpb24oa2V5LCB2YWx1ZSwgaSwgJ2ZpbGwnKSwgLy8gcHJldmVudCBkdXBsaWNhdGUgY29tcHV0YXRpb25cbiAgICAgIHN0cm9rZUNvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIHZhbHVlLCBpLCAgJ3N0cm9rZScpXG5cblxuICAgICAgdmFyIGJhciA9IHNhZmVTZWxlY3QodCwgJ3JlY3QnLCAnYmFyLXJlY3QnKVxuXG4gICAgICBpZiAoYmFyLmF0dHIoJ3RyYW5zZm9ybScpID09IHVuZGVmaW5lZCkge1xuICAgICAgICBiYXIuYXR0cigndHJhbnNmb3JtJywgZnVuY3Rpb24oZCwgaSkge1xuICAgICAgICAgIHZhclxuICAgICAgICAgIHggPSBob3Jpem9udGFsUVxuICAgICAgICAgICAgPyAwXG4gICAgICAgICAgICA6IDBcbiAgICAgICAgICAsXG4gICAgICAgICAgeSA9IHZlcnRpY2FsUVxuICAgICAgICAgICAgPyAwXG4gICAgICAgICAgICA6IHNjYWxlKGV4dGVudFsxXSlcbiAgICAgICAgICAsXG4gICAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICAgIHJldHVybiB0XG4gICAgICAgIH0pXG4gICAgICAgIC5hdHRyKCd3aWR0aCcsIGhvcml6b250YWxRID8gb2JqZWN0U2l6ZSA6IDApXG4gICAgICAgIC5hdHRyKCdoZWlnaHQnLCB2ZXJ0aWNhbFEgPyBvYmplY3RTaXplIDogMClcblxuICAgICAgfVxuXG5cbiAgICAgIGJhci50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpIHtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUVxuICAgICAgICAgID8gb2JqZWN0U2l6ZSAtIG9iamVjdFNpemUgKiBiYXJQZXJjZW50XG4gICAgICAgICAgOiBvcmllbnQgPT0gJ3JpZ2h0J1xuICAgICAgICAgICAgPyBzY2FsZShleHRlbnRbMV0pIC0gc2NhbGUodmFsdWUpXG4gICAgICAgICAgICA6IG9iamVjdFNpemUgLSBvYmplY3RTaXplICogYmFyUGVyY2VudFxuICAgICAgICAgICxcbiAgICAgICAgeSA9IHZlcnRpY2FsUVxuICAgICAgICAgID8gb2JqZWN0U2l6ZSAtIG9iamVjdFNpemUgKiBiYXJQZXJjZW50XG4gICAgICAgICAgOiBzY2FsZShleHRlbnRbMV0pIC0gc2NhbGUodmFsdWUpXG4gICAgICAgICxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcbiAgICAgIC5hdHRyKCd3aWR0aCcsIGhvcml6b250YWxRID8gb2JqZWN0U2l6ZSAqIGJhclBlcmNlbnQgOiBzY2FsZSh2YWx1ZSkpXG4gICAgICAuYXR0cignaGVpZ2h0JywgdmVydGljYWxRID8gb2JqZWN0U2l6ZSAqIGJhclBlcmNlbnQ6IHNjYWxlKHZhbHVlKSlcbiAgICAgIC5hdHRyKCdmaWxsJywgZmlsbENvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZScsIHN0cm9rZUNvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIGJhclN0cm9rZVdpZHRoKVxuXG5cblxuICAgICAgdC5vbignbW91c2VvdmVyJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIGNvbnRhaW5lci5zZWxlY3RBbGwoJ2cuJytvYmplY3RDbGFzcykuc3R5bGUoJ29wYWNpdHknLCAwLjIpXG4gICAgICAgIHQuc3R5bGUoJ29wYWNpdHknLCAxKVxuICAgICAgICBiYXIuYXR0cignc3Ryb2tlLXdpZHRoJyxiYXJTdHJva2VXaWR0aCoyKVxuXG4gICAgICB9KVxuICAgICAgdC5vbignbW91c2VvdXQnLCBmdW5jdGlvbigpe1xuICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnLicrb2JqZWN0Q2xhc3MpLnN0eWxlKCdvcGFjaXR5JywgMSlcbiAgICAgICAgYmFyLmF0dHIoJ3N0cm9rZS13aWR0aCcsIGJhclN0cm9rZVdpZHRoKVxuICAgICAgfSlcbiAgICB9KVxuXG4gICAgdG9vbHRpcC5zZWxlY3Rpb24oY29udGFpbmVyLnNlbGVjdEFsbCgnLmJhci1yZWN0JykpXG4gICAgLmRhdGEoZGF0YSlcblxuICAgIHRvb2x0aXAoKVxuXG4gIH1cbiAgcmV0dXJuIGJhclxufVxuIiwiaW1wb3J0IHtoeXBlbmF0ZSwgc2FmZVNlbGVjdH0gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7c2V0dXBDb250YWluZXIsIGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3QsIGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXIsIGxvZ30gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQge3VuaXF1ZX0gZnJvbSAnLi9hcnJheS1mdW5jdGlvbnMnO1xuaW1wb3J0IHtncm91cGluZ1NwYWNlcn0gZnJvbSAnLi9ncm91cGluZy1zcGFjZXInO1xuaW1wb3J0IHtjb2xvckZ1bmN0aW9uIGFzIENGfSBmcm9tICcuL2NvbG9yLWZ1bmN0aW9uJztcbmltcG9ydCB7dG9vbHRpcCBhcyBUVGlwfSBmcm9tICcuL3Rvb2x0aXAnO1xuXG5cbi8qKlxuICogQ3JlYXRlcyBhIGJ1YmJsZUhlYXRtYXBcbiAqXG4gKiB7QGxpbmsgaHR0cHM6Ly9zdW1uZXVyb24uZ2l0bGFiLmlvL2Qzc20vZGVtb3MvYnViYmxlLWhlYXRtYXAvaW5kZXguaHRtbCBEZW1vfVxuICogQGNvbnN0cnVjdG9yIGJ1YmJsZUhlYXRtYXBcbiAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBzZWxlY3Rpb25cbiAqIEBuYW1lc3BhY2UgYnViYmxlSGVhdG1hcFxuICogQHJldHVybnMge2Z1bmN0aW9ufSBidWJibGVIZWF0bWFwXG4gKi9cbmZ1bmN0aW9uIGJ1YmJsZUhlYXRtYXAoIHNlbGVjdGlvbiApIHtcbiAgdmFyXG4gIC8qKlxuICAqIERhdGEgdG8gcGxvdC4gQXNzdW1lZCB0byBiZSBhIG9iamVjdCwgd2hlcmUgZWFjaCBrZXkgY29ycmVzcG9uZHMgdG8gYSBjZWxsXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNkYXRhfSlcbiAgKiBAcGFyYW0ge09iamVjdH0gW2RhdGE9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkYXRhLFxuXG4gIC8qKlxuICAqIEFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBidWJibGVIZWF0bWFwIGluXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNzcGFjZVh9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VYPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VYLFxuICAvKipcbiAgKiBBbW91bnQgb2YgdmVydGljYWwgc3BhY2UgKGluIHBpeGVscykgYXZhaWJsZSB0byByZW5kZXIgdGhlIGJ1YmJsZUhlYXRtYXAgaW5cbiAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwLnNwYWNlWX0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVk9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzcGFjZVksXG5cbiAgLyoqXG4gICogVGhlIGludGVybmFsIGtleSBvZiB0aGUgY2VsbCBzcGVjaWZpeWluZyB0byB3aGljaCB4IGF4aXMga2V5IGl0IGJlbG9uZ3NcbiAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwLnhLZXl9KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbeEtleT0neCddXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhLZXkgPSAneCcsXG4gIC8qKlxuICAqIFRoZSBpbnRlcm5hbCBrZXkgb2YgdGhlIGNlbGwgc3BlY2lmaXlpbmcgdG8gd2hpY2ggeSBheGlzIGtleSBpdCBiZWxvbmdzXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcC55S2V5fSlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3lLZXk9J3knXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5S2V5ID0gJ3knLFxuICAvKipcbiAgKiBUaGUgaW50ZXJuYWwga2V5IG9mIHRoZSBjZWxsIHNwZWNpZml5aW5nIHdoYXQgdmFsdWUgdG8gdXNlIHRvIGRldGVybWluZSB0aGUgcmFkaXVzXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcC5yS2V5fSlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3JLZXk9J3InXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICByS2V5ID0gJ3InLFxuICAvKipcbiAgKiBUaGUgaW50ZXJuYWwga2V5IG9mIHRoZSBjZWxsIHNwZWNpZml5aW5nIHdoYXQgdmFsdWUgdG8gdXNlIHRvIGRldGVybWluZSB0aGUgY29sb3JcbiAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwLnZLZXl9KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbdktleT0ndiddXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHZLZXkgPSAndicsXG5cbiAgLyoqXG4gICogRnVuY3Rpb24gZm9yIGV4dHJhY3RpbmcgdGhlIHRoZSB2YWx1ZSBmcm9tIHhLZXkuXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcC54RXh0cmFjdG9yfSlcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbeEV4dHJhY3Rvcj1mdW5jdGlvbihrZXksIGkpIHsgcmV0dXJuIGRhdGFba2V5XVt4S2V5XSB9XVxuICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGkpIHtyZXR1cm4gZGF0YVtrZXldW3hLZXldIH0sXG4gIC8qKlxuICAqIEZ1bmN0aW9uIGZvciBleHRyYWN0aW5nIHRoZSB0aGUgdmFsdWUgZnJvbSB5S2V5LlxuICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAueUV4dHJhY3Rvcn0pXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3lFeHRyYWN0b3I9ZnVuY3Rpb24oa2V5LCBpKSB7IHJldHVybiBkYXRhW2tleV1beUtleV0gfV1cbiAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5RXh0cmFjdG9yID0gZnVuY3Rpb24oa2V5LCBpKSB7IHJldHVybiBkYXRhW2tleV1beUtleV0gfSxcbiAgLyoqXG4gICogRnVuY3Rpb24gZm9yIGV4dHJhY3RpbmcgdGhlIHRoZSB2YWx1ZSBmcm9tIHJLZXkuXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcC5yRXh0cmFjdG9yfSlcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbckV4dHJhY3Rvcj1mdW5jdGlvbihrZXksIGkpIHsgcmV0dXJuIGRhdGFba2V5XVtyS2V5XSB9XVxuICAqIEByZXR1cm5zIHtudW1iZXJ9XG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHJFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGkpIHsgcmV0dXJuIGRhdGFba2V5XVtyS2V5XSB9LFxuICAvKipcbiAgKiBGdW5jdGlvbiBmb3IgZXh0cmFjdGluZyB0aGUgdGhlIHZhbHVlIGZyb20gdktleS5cbiAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwLnZFeHRyYWN0b3J9KVxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt2RXh0cmFjdG9yPWZ1bmN0aW9uKGtleSwgaSkgeyByZXR1cm4gZGF0YVtrZXldW3ZLZXldIH1dXG4gICogQHJldHVybnMge251bWJlcn1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdkV4dHJhY3RvciA9IGZ1bmN0aW9uKGtleSwgaSkgeyByZXR1cm4gZGF0YVtrZXldW3ZLZXldIH0sXG5cblxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0byBhbGxvdyBidWJibGVIZWF0bWFwIHRvIHJlbmRlciBlbGVtZW50cyBwYXNzIHRoZSBtYWluIHNwYXRpYWwgZGltZW5zaW9uXG4gICogZ2l2ZW4gdGhlIG9yaWVudGF0aW9uIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjb3JpZW50fSksIHdoZXJlIHtAbGluayBidWJibGVIZWF0bWFwI29yaWVudH09XCJib3R0b21cIiBvciB7QGxpbmsgYnViYmxlSGVhdG1hcCNvcmllbnR9PVwidG9wXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJ1YmJsZUhlYXRtYXAjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjb3JpZW50fT1cImxlZnRcIiBvciB7QGxpbmsgYnViYmxlSGVhdG1hcCNvcmllbnR9PVwicmlnaHRcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgYnViYmxlSGVhdG1hcCNzcGFjZVl9XG4gICogQHBhcmFtIHtib29sZWFufSBbb3ZlcmZsb3dRPWZhbHNlXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvdmVyZmxvd1EgPSBmYWxzZSxcblxuICAvKipcbiAgKiBUaGUgc2NhbGUgZm9yIHdoaWNoIHRoZSByYWRpdXMgdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZT1kMy5zY2FsZUxpbmVhcl1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc2NhbGUgPSBkMy5zY2FsZUxpbmVhcigpLFxuICAvKipcbiAgKiBUaGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgc2NhbGUgKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNzY2FsZX0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtkb21haW5QYWRkaW5nPTAuNV1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZG9tYWluUGFkZGluZyA9IDAuNSxcblxuICAvKipcbiAgKiBEZWZhdWx0IHNwYWNlIGZvciB0aGUgc3BhY2VyIChwZXJjZW50YWdlKSBvZiBtYWluIGRpbWVuc2lvbiBnaXZlbiB0aGUgb3JpZW50YXRpb25cbiAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI29yaWVudH0pLCB3aGVyZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNvcmllbnR9PVwiaG9yaXpvbnRhbFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayBidWJibGVIZWF0bWFwI3NwYWNlWH0gYW5kIHdoZXJlIHtAbGluayBidWJibGVIZWF0bWFwI29yaWVudH09XCJ2ZXJ0aWNhbFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayBidWJibGVIZWF0bWFwI3NwYWNlWX0gYmV0d2VlbiBidWJibGVzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvYmplY3RTcGFjZXI9MC4wXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvYmplY3RTcGFjZXIgPSAwLjAsXG4gIC8qKlxuICAqIFRoZSBtaW5pbXVtIHNpemUgdGhhdCBhbiBvYmplY3QgY2FuIGJlXG4gICogQHBhcmFtIHtudW1iZXJ9IFttaW5PYmplY3RTaXplPTUwXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBtaW5PYmplY3RTaXplID0gNTAsXG4gIC8qKlxuICAqIFRoZSBtYXhpbXVtIHNpemUgdGhhdCBhbiBvYmplY3QgY2FuIGJlXG4gICogQHBhcmFtIHtudW1iZXJ9IFttYXhPYmplY3RTaXplPTEwMF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWF4T2JqZWN0U2l6ZSA9IDEwMCxcblxuXG4gIC8qKlxuICAqIFRoZSBzdHJva2Ugd2lkdGggb2YgdGhlIGJ1YmJsZXNcbiAgKiBAcGFyYW0ge251bWJlcn0gW2J1YmJsZVN0cm9rZVdpZHRoPTJdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJ1YmJsZVN0cm9rZVdpZHRoID0gMixcbiAgLy8gY29sb3JGdW5jID0gY29sb3JGdW5jdGlvbigpLFxuXG4gIC8qKlxuICAqIENvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kXG4gICogQHBhcmFtIHtzdHJpbmd9IFtiYWNrZ3JvdW5kRmlsbD1cInRyYW5zcGFyZW50XCJdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJhY2tncm91bmRGaWxsID0gJ3RyYW5zcGFyZW50JyxcbiAgLyoqXG4gICogTmFtZXNwYWNlIGZvciBhbGwgaXRlbXMgbWFkZSBieSB0aGlzIGluc3RhbmNlIG9mIGJ1YmJsZUhlYXRtYXBcbiAgKiBAcGFyYW0ge3N0cmluZ30gW25hbWVzcGFjZT1cImQzc20tYnViYmxlXCJdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG5hbWVzcGFjZSA9ICdkM3NtLWJ1YmJsZScsXG4gIC8qKlxuICAqIENsYXNzIG5hbWUgZm9yIGJ1YmJsZSBjb250YWluZXIgKDxnPiBlbGVtZW50KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbb2JqZWN0Q2xhc3M9XCJidWJibGVcIl1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0Q2xhc3MgPSAnYnViYmxlJyxcbiAgLyoqXG4gICogRHVyYXRpb24gb2YgYWxsIHRyYW5zaXRpb25zIG9mIHRoaXMgZWxlbWVudFxuICAqIEBwYXJhbSB7bnVtYmVyfSBbdHJhbnNpdGlvbkR1cmF0aW9uPTEwMDBdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRyYW5zaXRpb25EdXJhdGlvbiA9IDEwMDAsXG4gIC8qKlxuICAqIEVhc2luZyBmdW5jdGlvbiBmb3IgdHJhbnNpdGlvbnNcbiAgKiBAcGFyYW0ge2QzLmVhc2V9IFtlYXNlRnVuYz1kMy5lYXNlRXhwXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBlYXNlRnVuYyA9IGQzLmVhc2VFeHAsXG5cbiAgLyoqXG4gICogU3RvcmVzIHRoZSBrZXlzIG9mIGFsbCB0aGUgY2VsbHNcbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGJ1YmJsZUhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFtjZWxsS2V5cz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGNlbGxLZXlzLFxuICAvKipcbiAgKiBTdG9yZXMgdGhlIGxpc3Qgb2YgdW5pcXVlIHhWYWx1ZXNcbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGJ1YmJsZUhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt4VmFsdWVzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeFZhbHVlcyxcbiAgLyoqXG4gICogU3RvcmVzIHRoZSBsaXN0IG9mIHVuaXF1ZSB5VmFsdWVzXG4gICogQ2FsY3VsYXRlZCBhZnRlciBidWJibGVIZWF0bWFwIGNhbGxlZC5cbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbeVZhbHVlcz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHlWYWx1ZXMsXG4gIC8qKlxuICAqIFN0b3JlcyB0aGUgbGlzdCBvZiB1bmlxdWUgclZhbHVlc1xuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgYnViYmxlSGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW3JWYWx1ZXM9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICByVmFsdWVzLFxuICAvKipcbiAgKiBTdG9yZXMgdGhlIGxpc3Qgb2YgdW5pcXVlIHZWYWx1ZXNcbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGJ1YmJsZUhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt2VmFsdWVzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdlZhbHVlcyxcblxuICB4S2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4geEV4dHJhY3RvcihhKSAtIHhFeHRyYWN0b3IoYikgfSxcbiAgeUtleVNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKGEsIGIpIHsgcmV0dXJuIHlFeHRyYWN0b3IoYSkgLSB5RXh0cmFjdG9yKGIpIH0sXG4gIHJLZXlTb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihhLCBiKSB7IHJldHVybiByRXh0cmFjdG9yKGEpIC0gckV4dHJhY3RvcihiKSB9LFxuICB2S2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4gdkV4dHJhY3RvcihhKSAtIHZFeHRyYWN0b3IoYikgfSxcblxuICAvKipcbiAgKiBJbnN0YW5jZSBvZiBDb2xvckZ1bmN0aW9uIHdpdGggLmNvbG9yQnkgc2V0IHRvICdjYXRlZ29yeSdcbiAgKiBAZnVuY3Rpb24gY29sb3JGdW5jdGlvblxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjb2xvckZ1bmN0aW9uID0gQ0YoKS5jb2xvckJ5KCd2YWx1ZScpLFxuICAvKipcbiAgKiBJbnN0YW5jZSBvZiBUb29sdGlwXG4gICogQGZ1bmN0aW9uIHRvb2x0aXBcbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdG9vbHRpcCA9IFRUaXAoKSxcblxuICAvKipcbiAgKiBzdG9yZSB0aGUgc2l6ZSB0aGUgYnViYmxlIGNvdWxkIGJlIGluIHRoZSB4IGRpbWVuc2lvblxuICAqIHRoZSBhY3R1YWxsIHNpemUgb2YgdGhlIGJ1YmJsZSB3aWxsIGJlIHRoZSBtaW4gb2YgeFNpemUgYW5kIHtAbGluayBidWJibGVIZWF0bWFwI3lTaXplfVxuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgYnViYmxlSGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW3hTaXplPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeFNpemUsXG4gIC8qKlxuICAqIHN0b3JlIHRoZSBzaXplIG9mIHRoZSBzcGFjZXIgaW4gdGhlIHggZGltZW5zaW9uXG4gICogQ2FsY3VsYXRlZCBhZnRlciBidWJibGVIZWF0bWFwIGNhbGxlZC5cbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbeFNwYWNlclNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB4U3BhY2VyU2l6ZSxcblxuICAvKipcbiAgKiBzdG9yZSB0aGUgc2l6ZSB0aGUgYnViYmxlIGNvdWxkIGJlIGluIHRoZSB5IGRpbWVuc2lvblxuICAqIHRoZSBhY3R1YWxsIHNpemUgb2YgdGhlIGJ1YmJsZSB3aWxsIGJlIHRoZSBtaW4gb2YgeFNpemUgYW5kIHtAbGluayBidWJibGVIZWF0bWFwI3hTaXplfVxuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgYnViYmxlSGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW3lTaXplPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeVNpemUsXG4gIC8qKlxuICAqIHN0b3JlIHRoZSBzaXplIG9mIHRoZSBzcGFjZXIgaW4gdGhlIHkgZGltZW5zaW9uLlxuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgYnViYmxlSGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW3hTcGFjZXJTaXplPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeVNwYWNlclNpemVcblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBzZWxlY3Rpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IGQzLnNlbGVjdGlvbn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2VsZWN0aW9uID0gc2VsZWN0aW9uXG4gICAqL1xuICBiaG0uc2VsZWN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCBiaG0pIDogc2VsZWN0aW9uOyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGRhdGFcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNkYXRhfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgb2JqZWN0fVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGJobS5kYXRhID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkYXRhID0gXywgYmhtKSA6IGRhdGE7IH1cbiAgLy8gYmhtLm9yaWVudCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3JpZW50ID0gXywgYmhtKSA6IG9yaWVudDsgfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBhbW91bnQgb2YgaG9yaXpvbnRhbCBzcGFjZSBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNzcGFjZVh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV0gc2hvdWxkIGJlIGEgbnVtYmVyID4gMFxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VYID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0uc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVggPSBfLCBiaG0pIDogc3BhY2VYOyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiB2ZXJ0aWNhbCBzcGFjZSBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNzcGFjZVl9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV0gc2hvdWxkIGJlIGEgbnVtYmVyID4gMFxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VZID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0uc3BhY2VZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVkgPSBfLCBiaG0pIDogc3BhY2VZOyB9XG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeEtleVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3hLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhLZXkgPSAneCdcbiAgICovXG4gIGJobS54S2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4S2V5ID0gXywgYmhtKSA6IHhLZXk7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeUtleVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3lLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHlLZXkgPSAneSdcbiAgICovXG4gIGJobS55S2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5S2V5ID0gXywgYmhtKSA6IHlLZXk7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgcktleVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3JLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHJLZXkgPSAncidcbiAgICovXG4gIGJobS5yS2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChyS2V5ID0gXywgYmhtKSA6IHJLZXk7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgdktleVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3ZLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZLZXkgPSAneSdcbiAgICovXG4gIGJobS52S2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2S2V5ID0gXywgYmhtKSA6IHZLZXk7IH1cblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBjZWxsS2V5c1xuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI2NlbGxLZXlzfSlcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmdbXX1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgY2VsbEtleXMgPSB1bmRlZmluZWRcbiAgICovXG4gIGJobS5jZWxsS2V5cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY2VsbEtleXMgPSBfLCBiaG0pIDogY2VsbEtleXM7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeFZhbHVlc1xuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3hWYWx1ZXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB4VmFsdWVzID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0ueFZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeFZhbHVlcyA9IF8sIGJobSkgOiB4VmFsdWVzOyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHlWYWx1ZXNcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCN5VmFsdWVzfSlcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmdbXX1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgeVZhbHVlcyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmhtLnlWYWx1ZXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlWYWx1ZXMgPSBfLCBiaG0pIDogeVZhbHVlczsgfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSByVmFsdWVzXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjclZhbHVlc30pXG4gICAqIEBwYXJhbSB7bnVtYmVyW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyW119XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHJWYWx1ZXMgPSB1bmRlZmluZWRcbiAgICovXG4gIGJobS5yVmFsdWVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChyVmFsdWVzID0gXywgYmhtKSA6IHJWYWx1ZXM7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgdlZhbHVlc1xuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3ZWYWx1ZXN9KVxuICAgKiBAcGFyYW0ge251bWJlcltdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IG51bWJlcltdfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2VmFsdWVzID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0udlZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodlZhbHVlcyA9IF8sIGJobSkgOiB2VmFsdWVzOyB9XG5cblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB4RXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjeEV4dHJhY3Rvcn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhFeHRyYWN0b3IgPSB1bmRlZmluZWRcbiAgICovXG4gIGJobS54RXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4RXh0cmFjdG9yID0gXywgYmhtKSA6IHhFeHRyYWN0b3I7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeUV4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3lFeHRyYWN0b3J9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5RXh0cmFjdG9yID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0ueUV4dHJhY3RvciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeUV4dHJhY3RvciA9IF8sIGJobSkgOiB5RXh0cmFjdG9yOyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHJFeHRyYWN0b3JcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNyRXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgckV4dHJhY3RvciA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmhtLnJFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHJFeHRyYWN0b3IgPSBfLCBiaG0pIDogckV4dHJhY3RvcjsgfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB2RXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjdkV4dHJhY3Rvcn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZFeHRyYWN0b3IgPSB1bmRlZmluZWRcbiAgICovXG4gIGJobS52RXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2RXh0cmFjdG9yID0gXywgYmhtKSA6IHZFeHRyYWN0b3I7IH1cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgd2hldGhlciBvciBub3QgYnViYmxlSGVhdG1hcCBpcyBhbGxvd2VkIHRvIGdvIGJleW9uZCBzcGVjaWZpZWQgZGltZW5zaW9uc1xuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3NwYWNlWH0pXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBib29sZWFufVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvdmVyZmxvd1EgPSBmYWxzZVxuICAgKi9cbiAgYmhtLm92ZXJmbG93USA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3ZlcmZsb3dRID0gXywgYmhtKSA6IG92ZXJmbG93UTsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHNjYWxlIGZvciB3aGljaCB0aGUgcmFkaXVzIG9mIGJ1YmJsZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjc2NhbGV9KVxuICAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IGQzLnNjYWxlfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcbiAgICovXG4gIGJobS5zY2FsZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGUgPSBfLCBiaG0pIDogc2NhbGU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgc2NhbGVcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNkb21haW5QYWRkaW5nfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBkb21haW5QYWRkaW5nID0gMC41XG4gICAqL1xuICBiaG0uZG9tYWluUGFkZGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZG9tYWluUGFkZGluZyA9IF8sIGJobSkgOiBkb21haW5QYWRkaW5nOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgb2JqZWN0U3BhY2VyXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjb2JqZWN0U3BhY2VyfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RTcGFjZXIgPSAwLjBcbiAgICovXG4gIGJobS5vYmplY3RTcGFjZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNwYWNlciA9IF8sIG9iamVjdFNwYWNlcikgOiBkYXRhOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbWluT2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI21pbk9iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1pbk9iamVjdFNpemUgPSA1MFxuICAgKi9cbiAgYmhtLm1pbk9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1pbk9iamVjdFNpemUgPSBfLCBiaG0pIDogbWluT2JqZWN0U2l6ZTsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1heE9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNtYXhPYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtYXhPYmplY3RTaXplID0gMTAwXG4gICAqL1xuICBiaG0ubWF4T2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWF4T2JqZWN0U2l6ZSA9IF8sIGJobSkgOiBtYXhPYmplY3RTaXplOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYnViYmxlU3Ryb2tlV2lkdGhcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNidWJibGVTdHJva2VXaWR0aH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYnViYmxlU3Ryb2tlV2lkdGggPSAyXG4gICAqL1xuICBiaG0uYnViYmxlU3Ryb2tlV2lkdGggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJ1YmJsZVN0cm9rZVdpZHRoID0gXywgYmhtKSA6IGJ1YmJsZVN0cm9rZVdpZHRoOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFja2dyb3VuZEZpbGxcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNiYWNrZ3JvdW5kRmlsbH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnXG4gICAqL1xuICBiaG0uYmFja2dyb3VuZEZpbGwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhY2tncm91bmRGaWxsID0gXywgYmhtKSA6IGJhY2tncm91bmRGaWxsOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbmFtZXNwYWNlXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjbmFtZXNwYWNlfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBuYW1lc3BhY2UgPSAnZDNzbS1idWJibGVIZWF0bWFwJ1xuICAgKi9cbiAgYmhtLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgYmhtKSA6IG5hbWVzcGFjZTsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG9iamVjdENsYXNzXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjb2JqZWN0Q2xhc3N9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdENsYXNzID0gJ3RpY2stZ3JvdXAnXG4gICAqL1xuICBiaG0ub2JqZWN0Q2xhc3MgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdENsYXNzID0gXywgYmhtKSA6IG9iamVjdENsYXNzOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdHJhbnNpdGlvbkR1cmF0aW9uXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjdHJhbnNpdGlvbkR1cmF0aW9ufSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwXG4gICAqL1xuICBiaG0udHJhbnNpdGlvbkR1cmF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0cmFuc2l0aW9uRHVyYXRpb24gPSBfLCBiaG0pIDogdHJhbnNpdGlvbkR1cmF0aW9uOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZWFzZUZ1bmNcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNlYXNlRnVuY30pXG4gICAqIEBwYXJhbSB7ZDMuZWFzZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBkMy5lYXNlfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBlYXNlRnVuYyA9IGQzLmVhc2VFeHBcbiAgICovXG4gIGJobS5lYXNlRnVuYyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZWFzZUZ1bmMgPSBfLCBiaG0pIDogZWFzZUZ1bmM7IH1cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRvb2x0aXBcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCN0b29sdGlwfSlcbiAgICogQHBhcmFtIHt0b29sdGlwfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IHRvb2x0aXB9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRvb2x0aXAgPSB0b29sdGlwKClcbiAgICovXG4gIGJobS50b29sdGlwID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0b29sdGlwID0gXywgYmhtKSA6IHRvb2x0aXA7IH1cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGNvbG9yRnVuY3Rpb25cbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNjb2xvckZ1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtjb2xvckZ1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IGNvbG9yRnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGNvbG9yRnVuY3Rpb24gPSBjb2xvckZ1bmN0aW9uKClcbiAgICovXG4gIGJobS5jb2xvckZ1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjb2xvckZ1bmN0aW9uID0gXywgYmhtKSA6IGNvbG9yRnVuY3Rpb247IH1cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHhTaXplXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjeFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhTaXplID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0ueFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhTaXplID0gXywgYmhtKSA6IHhTaXplOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgeFNwYWNlclNpemVcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCN4U3BhY2VyU2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgeFNwYWNlclNpemUgPSB1bmRlZmluZWRcbiAgICovXG4gIGJobS54U3BhY2VyU2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeFNwYWNlclNpemUgPSBfLCBiaG0pIDogeFNwYWNlclNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB5U2l6ZVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3lTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5U2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmhtLnlTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5U2l6ZSA9IF8sIGJobSkgOiB5U2l6ZTsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHlTcGFjZXJTaXplXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjeVNwYWNlclNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHlTcGFjZXJTaXplID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0ueVNwYWNlclNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlTcGFjZXJTaXplID0gXywgYmhtKSA6IHlTcGFjZXJTaXplOyB9XG4gIC8vIGJobS55S2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5S2V5U29ydGluZ0Z1bmN0aW9uID0gXywgYmhtKSA6IHlLZXlTb3J0aW5nRnVuY3Rpb247IH1cbiAgLy8gYmhtLnhLZXlTb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhLZXlTb3J0aW5nRnVuY3Rpb24gPSBfLCBiaG0pIDogeEtleVNvcnRpbmdGdW5jdGlvbjsgfVxuXG5cblxuICBmdW5jdGlvbiBiaG0oKSB7XG4gICAgdmFyIGhvcml6b250YWxRID0gdHJ1ZTsgLy8gbm8gb3JpZW50YXRpb24gaW4gaGVhdG1hcHMuIEFpZHMgYXMgcGxhY2Vob2xkZXIgaW4gZnVuY3Rpb25zIHRoYXQgdGFrZSBvcmllbnQgYXMgYSBwYXJhbWV0ZXJcbiAgICB2YXIgYmdjcFJlY3QgPSB7eDowLCB5OjAsIHdpZHRoOiBzcGFjZVgsIGhlaWdodDpzcGFjZVl9XG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cbiAgICBjZWxsS2V5cyA9IGQzLmtleXMoZGF0YSk7XG4gICAgY2VsbEtleXMuc29ydChmdW5jdGlvbihhLCBiKXsgcmV0dXJuIHhLZXlTb3J0aW5nRnVuY3Rpb24oYSwgYikgfHwgeUtleVNvcnRpbmdGdW5jdGlvbihhLCBiKSB9KVxuICAgIGxvZygnYnViYmxlSGVhdG1hcCcsICdjZWxscyBhcmUgc29ydGVkIGJ5JywgY2VsbEtleXMpXG5cblxuXG4gICAgeFZhbHVlcyA9IHVuaXF1ZShjZWxsS2V5cy5tYXAoeEV4dHJhY3RvcikpO1xuICAgIHlWYWx1ZXMgPSB1bmlxdWUoY2VsbEtleXMubWFwKHlFeHRyYWN0b3IpKTtcbiAgICByVmFsdWVzID0gdW5pcXVlKGNlbGxLZXlzLm1hcChyRXh0cmFjdG9yKSk7XG4gICAgdlZhbHVlcyA9IHVuaXF1ZShjZWxsS2V5cy5tYXAodkV4dHJhY3RvcikpO1xuICAgIGxvZygnYnViYmxlSGVhdG1hcCcsICd4IGFuZCB5IGtleXMgYXJlJywge3g6IHhWYWx1ZXMsIHk6eVZhbHVlc30pXG5cblxuICAgIHZhciB4RGltID0geFZhbHVlcy5sZW5ndGgsIHlEaW0gPSB5VmFsdWVzLmxlbmd0aDtcblxuXG4gICAgdmFyIGV4dGVudCA9IFtNYXRoLm1pbiguLi5yVmFsdWVzKSAtIGRvbWFpblBhZGRpbmcsTWF0aC5tYXgoLi4uclZhbHVlcykgKyBkb21haW5QYWRkaW5nXTtcblxuXG4gICAgeVNpemUgPSBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0KHNwYWNlWSwgeURpbSwgbWluT2JqZWN0U2l6ZSwgbWF4T2JqZWN0U2l6ZSwgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgeFNpemUgPSBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0KHNwYWNlWCwgeERpbSwgbWluT2JqZWN0U2l6ZSwgbWF4T2JqZWN0U2l6ZSwgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgeVNwYWNlclNpemUgPSBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyKHlWYWx1ZXMsIHNwYWNlWSwgeVNpemUsIHlEaW0sIG9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIHhTcGFjZXJTaXplID0gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcih4VmFsdWVzLCBzcGFjZVgsIHhTaXplLCB4RGltLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICBsb2coJ2J1YmJsZUhlYXRtYXAnLCAnc2l6ZSBvZicsIHt4OiB4U2l6ZSwgeTogeVNpemV9KVxuXG5cbiAgICBzY2FsZS5kb21haW4oZXh0ZW50KS5yYW5nZShbTWF0aC5taW4obWluT2JqZWN0U2l6ZS8yLCAgIE1hdGgubWluKHlTaXplLCB4U2l6ZSkvMiksIE1hdGgubWluKHlTaXplLCB4U2l6ZSkvMl0pXG5cbiAgICB2YXIgeVNwYWNlciA9IGdyb3VwaW5nU3BhY2VyKClcbiAgICAuaG9yaXpvbnRhbFEoZmFsc2UpXG4gICAgLm1vdmVieSgnY2F0ZWdvcnknKS5udW1iZXJPZk9iamVjdHMoeURpbSlcbiAgICAub2JqZWN0Q2xhc3MoaHlwZW5hdGUob2JqZWN0Q2xhc3MsICdyb3cnKSlcbiAgICAub2JqZWN0U2l6ZSh5U2l6ZSkuc3BhY2VyU2l6ZSh5U3BhY2VyU2l6ZSlcbiAgICAudHJhbnNpdGlvbkR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbikuZWFzZUZ1bmMoZWFzZUZ1bmMpXG4gICAgLm5hbWVzcGFjZSgncm93JylcblxuICAgIHZhciB4U3BhY2VyID0gZ3JvdXBpbmdTcGFjZXIoKVxuICAgIC5ob3Jpem9udGFsUSh0cnVlKVxuICAgIC5tb3ZlYnkoJ2NhdGVnb3J5JykubnVtYmVyT2ZPYmplY3RzKHhEaW0pXG4gICAgLm9iamVjdENsYXNzKG9iamVjdENsYXNzKVxuICAgIC5vYmplY3RTaXplKHhTaXplKS5zcGFjZXJTaXplKHhTcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcblxuXG4gICAgeVNwYWNlcihjb250YWluZXIsIHlWYWx1ZXMsIDApXG4gICAgY29udGFpbmVyLnNlbGVjdEFsbCgnZy4nK2h5cGVuYXRlKG9iamVjdENsYXNzLCAncm93JykpXG4gICAgLmVhY2goZnVuY3Rpb24oZCwgaSl7XG4gICAgICB4U3BhY2VyKGQzLnNlbGVjdCh0aGlzKSwgeFZhbHVlcywgMClcbiAgICB9KVxuICAgIHZhciBjZWxscyA9IGNvbnRhaW5lci5zZWxlY3RBbGwoJ2c6bm90KC50by1yZW1vdmUpLicrb2JqZWN0Q2xhc3MpLmRhdGEoY2VsbEtleXMpO1xuXG4gICAgdmFyIHBhcmVudEluZGV4QXJyYXkgPSBbXVxuICAgIGNlbGxzLmVhY2goZnVuY3Rpb24oZCwgaSl7IHBhcmVudEluZGV4QXJyYXkucHVzaChOdW1iZXIoZDMuc2VsZWN0KHRoaXMpLmF0dHIoJ3BhcmVudC1pbmRleCcpKSkgfSlcblxuXG4gICAgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24uY29sb3JCeSgpID09ICdpbmRleCdcbiAgICA/IGNvbG9yRnVuY3Rpb24uZGF0YUV4dGVudChbMCwgTWF0aC5tYXgoLi4ucGFyZW50SW5kZXhBcnJheSldKVxuICAgIDogY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KFswLCBNYXRoLm1heCguLi52VmFsdWVzKV0pXG5cbiAgICBjZWxscy5lYWNoKGZ1bmN0aW9uKGtleSwgaSkge1xuICAgICAgbG9nKCdidWJibGVIZWF0bWFwJywgJ2VhY2ggY2VsbCcsIHtrZXk6IGtleSwgaW5kZXg6IGksIG5vZGU6IGQzLnNlbGVjdCh0aGlzKS5ub2RlKCl9KVxuXG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKSxcbiAgICAgIGN1cnJlbnREYXRhID0gZGF0YVtrZXldLFxuICAgICAgdmFsdWUgPSB2RXh0cmFjdG9yKGtleSwgaSksXG4gICAgICByYWRpdXM9IHJFeHRyYWN0b3Ioa2V5LCBpKSxcbiAgICAgIGkgPSB0LmF0dHIoJ3BhcmVudC1pbmRleCcpID09IHVuZGVmaW5lZCA/IGkgOiB0LmF0dHIoJ3BhcmVudC1pbmRleCcpLFxuICAgICAgZmlsbENvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIHZhbHVlLCBpLCAnZmlsbCcpLCAvLyBwcmV2ZW50IGR1cGxpY2F0ZSBjb21wdXRhdGlvblxuICAgICAgc3Ryb2tlQ29sb3IgPSBjb2xvckZ1bmN0aW9uKGtleSwgdmFsdWUsIGksICAnc3Ryb2tlJylcblxuICAgICAgbG9nKCdidWJibGVIZWF0bWFwJywgJ3JhZGl1cycse3JhZGl1czogcmFkaXVzLCBzY2FsZWQ6IHNjYWxlKHJhZGl1cyksIGV4dGVudDogZXh0ZW50LCByYW5nZTpzY2FsZS5yYW5nZSgpfSlcblxuICAgICAgdmFyIGMgPSBzYWZlU2VsZWN0KHQsICdjaXJjbGUnLCBoeXBlbmF0ZShvYmplY3RDbGFzcywnY2lyY2xlJykpXG4gICAgICBjLmF0dHIoJ2N4JywgeFNpemUgLyAyKVxuICAgICAgLmF0dHIoJ2N5JywgeVNpemUgLyAyIClcbiAgICAgIC5hdHRyKCdyJywgc2NhbGUocmFkaXVzKSlcbiAgICAgIC5hdHRyKCdmaWxsJywgZmlsbENvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZScsIHN0cm9rZUNvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIGJ1YmJsZVN0cm9rZVdpZHRoKVxuXG4gICAgfSlcblxuICAgIHRvb2x0aXAuc2VsZWN0aW9uKGNlbGxzLnNlbGVjdEFsbCgnY2lyY2xlLicraHlwZW5hdGUob2JqZWN0Q2xhc3MsICdjaXJjbGUnKSkpXG4gICAgLmRhdGEoZGF0YSlcbiAgICAvLyAua2V5cyhbJ3InLCAndiddKVxuICAgIC8vIC5oZWFkZXIoZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGh5cGVuYXRlKGRhdGFbZF1beEtleV0sIGRhdGFbZF1beUtleV0pIH0pXG5cbiAgICB0b29sdGlwKClcblxuXG4gIH1cblxuICByZXR1cm4gYmhtO1xufVxuXG5leHBvcnQge2J1YmJsZUhlYXRtYXB9XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHtzZXR1cENvbnRhaW5lciwgY2FsY3VsYXRlV2lkdGhPZk9iamVjdCwgY2FsY3VsYXRlV2lkdGhPZlNwYWNlciwgbG9nfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7dW5pcXVlfSBmcm9tICcuL2FycmF5LWZ1bmN0aW9ucyc7XG5pbXBvcnQge2dyb3VwaW5nU3BhY2VyfSBmcm9tICcuL2dyb3VwaW5nLXNwYWNlcic7XG5pbXBvcnQge2NvbG9yRnVuY3Rpb24gYXMgQ0Z9IGZyb20gJy4vY29sb3ItZnVuY3Rpb24nO1xuaW1wb3J0IHt0b29sdGlwIGFzIFRUaXB9IGZyb20gJy4vdG9vbHRpcCc7XG5cblxuLyoqXG4gKiBDcmVhdGVzIGEgaGVhdG1hcFxuICpcbiAqIHtAbGluayBodHRwczovL3N1bW5ldXJvbi5naXRsYWIuaW8vZDNzbS9kZW1vcy9oZWF0bWFwLWhlYXRtYXAvaW5kZXguaHRtbCBEZW1vfVxuICogQGNvbnN0cnVjdG9yIGhlYXRtYXBcbiAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBzZWxlY3Rpb25cbiAqIEBuYW1lc3BhY2UgaGVhdG1hcFxuICogQHJldHVybnMge2Z1bmN0aW9ufSBoZWF0bWFwXG4gKi9cbmZ1bmN0aW9uIGhlYXRtYXAoIHNlbGVjdGlvbiApIHtcbiAgdmFyXG4gIC8qKlxuICAqIERhdGEgdG8gcGxvdC4gQXNzdW1lZCB0byBiZSBhIG9iamVjdCwgd2hlcmUgZWFjaCBrZXkgY29ycmVzcG9uZHMgdG8gYSBjZWxsXG4gICogKHNlZSB7QGxpbmsgaGVhdG1hcCNkYXRhfSlcbiAgKiBAcGFyYW0ge09iamVjdH0gW2RhdGE9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkYXRhLFxuXG4gIC8qKlxuICAqIEFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBoZWF0bWFwIGluXG4gICogKHNlZSB7QGxpbmsgaGVhdG1hcCNzcGFjZVh9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VYPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VYLFxuICAvKipcbiAgKiBBbW91bnQgb2YgdmVydGljYWwgc3BhY2UgKGluIHBpeGVscykgYXZhaWJsZSB0byByZW5kZXIgdGhlIGhlYXRtYXAgaW5cbiAgKiAoc2VlIHtAbGluayBoZWF0bWFwLnNwYWNlWX0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVk9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzcGFjZVksXG5cbiAgLyoqXG4gICogVGhlIGludGVybmFsIGtleSBvZiB0aGUgY2VsbCBzcGVjaWZpeWluZyB0byB3aGljaCB4IGF4aXMga2V5IGl0IGJlbG9uZ3NcbiAgKiAoc2VlIHtAbGluayBoZWF0bWFwLnhLZXl9KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbeEtleT0neCddXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhLZXkgPSAneCcsXG4gIC8qKlxuICAqIFRoZSBpbnRlcm5hbCBrZXkgb2YgdGhlIGNlbGwgc3BlY2lmaXlpbmcgdG8gd2hpY2ggeSBheGlzIGtleSBpdCBiZWxvbmdzXG4gICogKHNlZSB7QGxpbmsgaGVhdG1hcC55S2V5fSlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3lLZXk9J3knXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5S2V5ID0gJ3knLFxuXG4gIC8qKlxuICAqIFRoZSBpbnRlcm5hbCBrZXkgb2YgdGhlIGNlbGwgc3BlY2lmaXlpbmcgd2hhdCB2YWx1ZSB0byB1c2UgdG8gZGV0ZXJtaW5lIHRoZSBjb2xvclxuICAqIChzZWUge0BsaW5rIGhlYXRtYXAudktleX0pXG4gICogQHBhcmFtIHtzdHJpbmd9IFt2S2V5PSd2J11cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdktleSA9ICd2JyxcblxuICAvKipcbiAgKiBGdW5jdGlvbiBmb3IgZXh0cmFjdGluZyB0aGUgdGhlIHZhbHVlIGZyb20geEtleS5cbiAgKiAoc2VlIHtAbGluayBoZWF0bWFwLnhFeHRyYWN0b3J9KVxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt4RXh0cmFjdG9yPWZ1bmN0aW9uKGtleSwgaSkgeyByZXR1cm4gZGF0YVtrZXldW3hLZXldIH1dXG4gICogQHJldHVybnMge3N0cmluZ31cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeEV4dHJhY3RvciA9IGZ1bmN0aW9uKGtleSwgaSkge3JldHVybiBkYXRhW2tleV1beEtleV0gfSxcbiAgLyoqXG4gICogRnVuY3Rpb24gZm9yIGV4dHJhY3RpbmcgdGhlIHRoZSB2YWx1ZSBmcm9tIHlLZXkuXG4gICogKHNlZSB7QGxpbmsgaGVhdG1hcC55RXh0cmFjdG9yfSlcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbeUV4dHJhY3Rvcj1mdW5jdGlvbihrZXksIGkpIHsgcmV0dXJuIGRhdGFba2V5XVt5S2V5XSB9XVxuICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHlFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGkpIHsgcmV0dXJuIGRhdGFba2V5XVt5S2V5XSB9LFxuXG4gIC8qKlxuICAqIEZ1bmN0aW9uIGZvciBleHRyYWN0aW5nIHRoZSB0aGUgdmFsdWUgZnJvbSB2S2V5LlxuICAqIChzZWUge0BsaW5rIGhlYXRtYXAudkV4dHJhY3Rvcn0pXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3ZFeHRyYWN0b3I9ZnVuY3Rpb24oa2V5LCBpKSB7IHJldHVybiBkYXRhW2tleV1bdktleV0gfV1cbiAgKiBAcmV0dXJucyB7bnVtYmVyfVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2RXh0cmFjdG9yID0gZnVuY3Rpb24oa2V5LCBpKSB7IHJldHVybiBkYXRhW2tleV1bdktleV0gfSxcblxuXG4gIC8qKlxuICAqIFdoZXRoZXIgb3Igbm90IHRvIGFsbG93IGhlYXRtYXAgdG8gcmVuZGVyIGVsZW1lbnRzIHBhc3MgdGhlIG1haW4gc3BhdGlhbCBkaW1lbnNpb25cbiAgKiBnaXZlbiB0aGUgb3JpZW50YXRpb24gKHNlZSB7QGxpbmsgaGVhdG1hcCNvcmllbnR9KSwgd2hlcmUge0BsaW5rIGhlYXRtYXAjb3JpZW50fT1cImJvdHRvbVwiIG9yIHtAbGluayBoZWF0bWFwI29yaWVudH09XCJ0b3BcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgaGVhdG1hcCNzcGFjZVh9IGFuZCB3aGVyZSB7QGxpbmsgaGVhdG1hcCNvcmllbnR9PVwibGVmdFwiIG9yIHtAbGluayBoZWF0bWFwI29yaWVudH09XCJyaWdodFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayBoZWF0bWFwI3NwYWNlWX1cbiAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvdmVyZmxvd1E9ZmFsc2VdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG92ZXJmbG93USA9IGZhbHNlLFxuXG4gIC8qKlxuICAqIERlZmF1bHQgc3BhY2UgZm9yIHRoZSBzcGFjZXIgKHBlcmNlbnRhZ2UpIG9mIG1haW4gZGltZW5zaW9uIGdpdmVuIHRoZSBvcmllbnRhdGlvblxuICAqIChzZWUge0BsaW5rIGhlYXRtYXAjb3JpZW50fSksIHdoZXJlIHtAbGluayBoZWF0bWFwI29yaWVudH09XCJob3Jpem9udGFsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGhlYXRtYXAjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGhlYXRtYXAjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGhlYXRtYXAjc3BhY2VZfSBiZXR3ZWVuIGJ1YmJsZXNcbiAgKiBAcGFyYW0ge251bWJlcn0gW29iamVjdFNwYWNlcj0wLjBdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdFNwYWNlciA9IDAuMCxcbiAgLyoqXG4gICogVGhlIG1pbmltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmUgaW4gdGhlIHkgZGltZW5zaW9uXG4gICogQHBhcmFtIHtudW1iZXJ9IFttaW5PYmplY3RTaXplPTUwXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5TWluT2JqZWN0U2l6ZSA9IDUwLFxuICAvKipcbiAgKiBUaGUgbWluaW11bSBzaXplIHRoYXQgYW4gb2JqZWN0IGNhbiBiZSBpbiB0aGUgeCBkaW1lbnNpb25cbiAgKiBAcGFyYW0ge251bWJlcn0gW21pbk9iamVjdFNpemU9NTBdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhNaW5PYmplY3RTaXplID0gNTAsXG4gIC8qKlxuICAqIFRoZSBtYXhpbXVtIHNpemUgdGhhdCBhbiBvYmplY3QgY2FuIGJlIGluIHRoZSB4IGRpbWVuc2lvblxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbWF4T2JqZWN0U2l6ZT0xMDBdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhNYXhPYmplY3RTaXplID0gMTAwLFxuICAvKipcbiAgKiBUaGUgbWF4aW11bSBzaXplIHRoYXQgYW4gb2JqZWN0IGNhbiBiZSBpbiB0aGUgeSBkaW1lbnNpb25cbiAgKiBAcGFyYW0ge251bWJlcn0gW21heE9iamVjdFNpemU9MTAwXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5TWF4T2JqZWN0U2l6ZSA9IDEwMCxcblxuXG4gIC8qKlxuICAqIFRoZSBzdHJva2Ugd2lkdGggb2YgdGhlIGJ1YmJsZXNcbiAgKiBAcGFyYW0ge251bWJlcn0gW29iamVjdFN0cm9rZVdpZHRoPTJdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdFN0cm9rZVdpZHRoID0gMixcbiAgLy8gY29sb3JGdW5jID0gY29sb3JGdW5jdGlvbigpLFxuXG4gIC8qKlxuICAqIENvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kXG4gICogQHBhcmFtIHtzdHJpbmd9IFtiYWNrZ3JvdW5kRmlsbD1cInRyYW5zcGFyZW50XCJdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJhY2tncm91bmRGaWxsID0gJ3RyYW5zcGFyZW50JyxcbiAgLyoqXG4gICogTmFtZXNwYWNlIGZvciBhbGwgaXRlbXMgbWFkZSBieSB0aGlzIGluc3RhbmNlIG9mIGhlYXRtYXBcbiAgKiBAcGFyYW0ge3N0cmluZ30gW25hbWVzcGFjZT1cImQzc20taGVhdG1hcFwiXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBuYW1lc3BhY2UgPSAnZDNzbS1oZWF0bWFwJyxcbiAgLyoqXG4gICogQ2xhc3MgbmFtZSBmb3IgaGVhdG1hcCBjb250YWluZXIgKDxnPiBlbGVtZW50KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbb2JqZWN0Q2xhc3M9XCJoZWF0bWFwXCJdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdENsYXNzID0gJ2hlYXRtYXAnLFxuICAvKipcbiAgKiBEdXJhdGlvbiBvZiBhbGwgdHJhbnNpdGlvbnMgb2YgdGhpcyBlbGVtZW50XG4gICogQHBhcmFtIHtudW1iZXJ9IFt0cmFuc2l0aW9uRHVyYXRpb249MTAwMF1cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMCxcbiAgLyoqXG4gICogRWFzaW5nIGZ1bmN0aW9uIGZvciB0cmFuc2l0aW9uc1xuICAqIEBwYXJhbSB7ZDMuZWFzZX0gW2Vhc2VGdW5jPWQzLmVhc2VFeHBdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGVhc2VGdW5jID0gZDMuZWFzZUV4cCxcblxuICAvKipcbiAgKiBTdG9yZXMgdGhlIGtleXMgb2YgYWxsIHRoZSBjZWxsc1xuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgaGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW2NlbGxLZXlzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgY2VsbEtleXMsXG4gIC8qKlxuICAqIFN0b3JlcyB0aGUgbGlzdCBvZiB1bmlxdWUgeFZhbHVlc1xuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgaGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW3hWYWx1ZXM9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB4VmFsdWVzLFxuICAvKipcbiAgKiBTdG9yZXMgdGhlIGxpc3Qgb2YgdW5pcXVlIHlWYWx1ZXNcbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt5VmFsdWVzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeVZhbHVlcyxcbiAgLyoqXG4gICogU3RvcmVzIHRoZSBsaXN0IG9mIHVuaXF1ZSB2VmFsdWVzXG4gICogQ2FsY3VsYXRlZCBhZnRlciBoZWF0bWFwIGNhbGxlZC5cbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbdlZhbHVlcz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHZWYWx1ZXMsXG5cbiAgeEtleVNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKGEsIGIpIHsgcmV0dXJuIHhWYWx1ZXMuaW5kZXhPZih4RXh0cmFjdG9yKGEpKSAtIHhWYWx1ZXMuaW5kZXhPZih4RXh0cmFjdG9yKGIpKSB9LFxuICB5S2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4geVZhbHVlcy5pbmRleE9mKHlFeHRyYWN0b3IoYSkpIC0geVZhbHVlcy5pbmRleE9mKHlFeHRyYWN0b3IoYikpIH0sXG4gIHZLZXlTb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihhLCBiKSB7IHJldHVybiB2VmFsdWVzLmluZGV4T2YodkV4dHJhY3RvcihhKSkgLSB5VmFsdWVzLmluZGV4T2YodkV4dHJhY3RvcihiKSkgfSxcblxuICAvKipcbiAgKiBJbnN0YW5jZSBvZiBDb2xvckZ1bmN0aW9uIHdpdGggLmNvbG9yQnkgc2V0IHRvICdjYXRlZ29yeSdcbiAgKiBAZnVuY3Rpb24gY29sb3JGdW5jdGlvblxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjb2xvckZ1bmN0aW9uID0gQ0YoKS5jb2xvckJ5KCdjYXRlZ29yeScpLFxuICAvKipcbiAgKiBJbnN0YW5jZSBvZiBUb29sdGlwXG4gICogQGZ1bmN0aW9uIHRvb2x0aXBcbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdG9vbHRpcCA9IFRUaXAoKSxcblxuICAvKipcbiAgKiBzdG9yZSB0aGUgc2l6ZSB0aGUgaGVhdG1hcCBjb3VsZCBiZSBpbiB0aGUgeCBkaW1lbnNpb25cbiAgKiB0aGUgYWN0dWFsbCBzaXplIG9mIHRoZSBoZWF0bWFwIHdpbGwgYmUgdGhlIG1pbiBvZiB4U2l6ZSBhbmQge0BsaW5rIGhlYXRtYXAjeVNpemV9XG4gICogQ2FsY3VsYXRlZCBhZnRlciBoZWF0bWFwIGNhbGxlZC5cbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbeFNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB4U2l6ZSxcbiAgLyoqXG4gICogc3RvcmUgdGhlIHNpemUgb2YgdGhlIHNwYWNlciBpbiB0aGUgeCBkaW1lbnNpb25cbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt4U3BhY2VyU2l6ZT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhTcGFjZXJTaXplLFxuXG4gIC8qKlxuICAqIHN0b3JlIHRoZSBzaXplIHRoZSBoZWF0bWFwIGNvdWxkIGJlIGluIHRoZSB5IGRpbWVuc2lvblxuICAqIHRoZSBhY3R1YWxsIHNpemUgb2YgdGhlIGhlYXRtYXAgd2lsbCBiZSB0aGUgbWluIG9mIHhTaXplIGFuZCB7QGxpbmsgaGVhdG1hcCN4U2l6ZX1cbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt5U2l6ZT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHlTaXplLFxuICAvKipcbiAgKiBzdG9yZSB0aGUgc2l6ZSBvZiB0aGUgc3BhY2VyIGluIHRoZSB5IGRpbWVuc2lvbi5cbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt4U3BhY2VyU2l6ZT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHlTcGFjZXJTaXplXG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgc2VsZWN0aW9uIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBkMy5zZWxlY3Rpb259XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNlbGVjdGlvbiA9IHNlbGVjdGlvblxuICAgKi9cbiAgaG0uc2VsZWN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCBobSkgOiBzZWxlY3Rpb247IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZGF0YVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI2RhdGF9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBvYmplY3R9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgaG0uZGF0YSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGF0YSA9IF8sIGhtKSA6IGRhdGE7IH1cbiAgLy8gaG0ub3JpZW50ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvcmllbnQgPSBfLCBobSkgOiBvcmllbnQ7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIGhvcml6b250YWwgc3BhY2UgaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjc3BhY2VYfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlWCA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0uc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVggPSBfLCBobSkgOiBzcGFjZVg7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3NwYWNlWX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVkgPSB1bmRlZmluZWRcbiAgICovXG4gIGhtLnNwYWNlWSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3BhY2VZID0gXywgaG0pIDogc3BhY2VZOyB9XG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeEtleVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3hLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhLZXkgPSAneCdcbiAgICovXG4gIGhtLnhLZXkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhLZXkgPSBfLCBobSkgOiB4S2V5OyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHlLZXlcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCN5S2V5fSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5S2V5ID0gJ3knXG4gICAqL1xuICBobS55S2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5S2V5ID0gXywgaG0pIDogeUtleTsgfVxuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZLZXlcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCN2S2V5fSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2S2V5ID0gJ3knXG4gICAqL1xuICBobS52S2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2S2V5ID0gXywgaG0pIDogdktleTsgfVxuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGNlbGxLZXlzXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjY2VsbEtleXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBjZWxsS2V5cyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0uY2VsbEtleXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNlbGxLZXlzID0gXywgaG0pIDogY2VsbEtleXM7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeFZhbHVlc1xuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3hWYWx1ZXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB4VmFsdWVzID0gdW5kZWZpbmVkXG4gICAqL1xuICBobS54VmFsdWVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4VmFsdWVzID0gXywgaG0pIDogeFZhbHVlczsgfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB5VmFsdWVzXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjeVZhbHVlc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgc3RyaW5nW119XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHlWYWx1ZXMgPSB1bmRlZmluZWRcbiAgICovXG4gIGhtLnlWYWx1ZXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlWYWx1ZXMgPSBfLCBobSkgOiB5VmFsdWVzOyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZWYWx1ZXNcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCN2VmFsdWVzfSlcbiAgICogQHBhcmFtIHtudW1iZXJbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBudW1iZXJbXX1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdlZhbHVlcyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0udlZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodlZhbHVlcyA9IF8sIGhtKSA6IHZWYWx1ZXM7IH1cblxuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHhFeHRyYWN0b3JcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCN4RXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgeEV4dHJhY3RvciA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0ueEV4dHJhY3RvciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeEV4dHJhY3RvciA9IF8sIGhtKSA6IHhFeHRyYWN0b3I7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeUV4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3lFeHRyYWN0b3J9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5RXh0cmFjdG9yID0gdW5kZWZpbmVkXG4gICAqL1xuICBobS55RXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5RXh0cmFjdG9yID0gXywgaG0pIDogeUV4dHJhY3RvcjsgfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB2RXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjdkV4dHJhY3Rvcn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZFeHRyYWN0b3IgPSB1bmRlZmluZWRcbiAgICovXG4gIGhtLnZFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZFeHRyYWN0b3IgPSBfLCBobSkgOiB2RXh0cmFjdG9yOyB9XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IGhlYXRtYXAgaXMgYWxsb3dlZCB0byBnbyBiZXlvbmQgc3BlY2lmaWVkIGRpbWVuc2lvbnNcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNzcGFjZVh9KVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgYm9vbGVhbn1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3ZlcmZsb3dRID0gZmFsc2VcbiAgICovXG4gIGhtLm92ZXJmbG93USA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3ZlcmZsb3dRID0gXywgaG0pIDogb3ZlcmZsb3dROyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyBvYmplY3RTcGFjZXJcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNvYmplY3RTcGFjZXJ9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdFNwYWNlciA9IDAuMFxuICAgKi9cbiAgaG0ub2JqZWN0U3BhY2VyID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RTcGFjZXIgPSBfLCBvYmplY3RTcGFjZXIpIDogZGF0YTsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1pbk9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNtaW5PYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtaW5PYmplY3RTaXplID0gNTBcbiAgICovXG4gIGhtLnlNaW5PYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5TWluT2JqZWN0U2l6ZSA9IF8sIGhtKSA6IHlNaW5PYmplY3RTaXplOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbWF4T2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI21heE9iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1heE9iamVjdFNpemUgPSAxMDBcbiAgICovXG4gIGhtLnlNYXhPYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5TWF4T2JqZWN0U2l6ZSA9IF8sIGhtKSA6IHlNYXhPYmplY3RTaXplOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbWluT2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI21pbk9iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1pbk9iamVjdFNpemUgPSA1MFxuICAgKi9cbiAgaG0ueE1pbk9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhNaW5PYmplY3RTaXplID0gXywgaG0pIDogeE1pbk9iamVjdFNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtYXhPYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjbWF4T2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWF4T2JqZWN0U2l6ZSA9IDEwMFxuICAgKi9cbiAgaG0ueE1heE9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhNYXhPYmplY3RTaXplID0gXywgaG0pIDogeE1heE9iamVjdFNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RTdHJva2VXaWR0aFxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI29iamVjdFN0cm9rZVdpZHRofSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RTdHJva2VXaWR0aCA9IDJcbiAgICovXG4gIGhtLm9iamVjdFN0cm9rZVdpZHRoID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RTdHJva2VXaWR0aCA9IF8sIGhtKSA6IG9iamVjdFN0cm9rZVdpZHRoOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFja2dyb3VuZEZpbGxcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNiYWNrZ3JvdW5kRmlsbH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnXG4gICAqL1xuICBobS5iYWNrZ3JvdW5kRmlsbCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFja2dyb3VuZEZpbGwgPSBfLCBobSkgOiBiYWNrZ3JvdW5kRmlsbDsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG5hbWVzcGFjZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI25hbWVzcGFjZX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbmFtZXNwYWNlID0gJ2Qzc20taGVhdG1hcCdcbiAgICovXG4gIGhtLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgaG0pIDogbmFtZXNwYWNlOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0Q2xhc3NcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNvYmplY3RDbGFzc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0Q2xhc3MgPSAndGljay1ncm91cCdcbiAgICovXG4gIGhtLm9iamVjdENsYXNzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RDbGFzcyA9IF8sIGhtKSA6IG9iamVjdENsYXNzOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdHJhbnNpdGlvbkR1cmF0aW9uXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjdHJhbnNpdGlvbkR1cmF0aW9ufSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwXG4gICAqL1xuICBobS50cmFuc2l0aW9uRHVyYXRpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRyYW5zaXRpb25EdXJhdGlvbiA9IF8sIGhtKSA6IHRyYW5zaXRpb25EdXJhdGlvbjsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGVhc2VGdW5jXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjZWFzZUZ1bmN9KVxuICAgKiBAcGFyYW0ge2QzLmVhc2V9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgZDMuZWFzZX1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwXG4gICAqL1xuICBobS5lYXNlRnVuYyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZWFzZUZ1bmMgPSBfLCBobSkgOiBlYXNlRnVuYzsgfVxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdG9vbHRpcFxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3Rvb2x0aXB9KVxuICAgKiBAcGFyYW0ge3Rvb2x0aXB9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgdG9vbHRpcH1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdG9vbHRpcCA9IHRvb2x0aXAoKVxuICAgKi9cbiAgaG0udG9vbHRpcCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodG9vbHRpcCA9IF8sIGhtKSA6IHRvb2x0aXA7IH1cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGNvbG9yRnVuY3Rpb25cbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNjb2xvckZ1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtjb2xvckZ1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IGNvbG9yRnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGNvbG9yRnVuY3Rpb24gPSBjb2xvckZ1bmN0aW9uKClcbiAgICovXG4gIGhtLmNvbG9yRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNvbG9yRnVuY3Rpb24gPSBfLCBobSkgOiBjb2xvckZ1bmN0aW9uOyB9XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB4U2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3hTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB4U2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0ueFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhTaXplID0gXywgaG0pIDogeFNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB4U3BhY2VyU2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3hTcGFjZXJTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB4U3BhY2VyU2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0ueFNwYWNlclNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhTcGFjZXJTaXplID0gXywgaG0pIDogeFNwYWNlclNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB5U2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3lTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5U2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0ueVNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlTaXplID0gXywgaG0pIDogeVNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB5U3BhY2VyU2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3lTcGFjZXJTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5U3BhY2VyU2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0ueVNwYWNlclNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlTcGFjZXJTaXplID0gXywgaG0pIDogeVNwYWNlclNpemU7IH1cbiAgLy8gaG0ueUtleVNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeUtleVNvcnRpbmdGdW5jdGlvbiA9IF8sIGhtKSA6IHlLZXlTb3J0aW5nRnVuY3Rpb247IH1cbiAgLy8gaG0ueEtleVNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeEtleVNvcnRpbmdGdW5jdGlvbiA9IF8sIGhtKSA6IHhLZXlTb3J0aW5nRnVuY3Rpb247IH1cblxuXG5cbiAgZnVuY3Rpb24gaG0oKSB7XG4gICAgdmFyIGhvcml6b250YWxRID0gdHJ1ZTsgLy8gbm8gb3JpZW50YXRpb24gaW4gaGVhdG1hcHMuIEFpZHMgYXMgcGxhY2Vob2xkZXIgaW4gZnVuY3Rpb25zIHRoYXQgdGFrZSBvcmllbnQgYXMgYSBwYXJhbWV0ZXJcbiAgICB2YXIgYmdjcFJlY3QgPSB7eDowLCB5OjAsIHdpZHRoOiBzcGFjZVgsIGhlaWdodDpzcGFjZVl9XG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cbiAgICBjZWxsS2V5cyA9IGQzLmtleXMoZGF0YSk7XG5cbiAgICB4VmFsdWVzID0gdW5pcXVlKGNlbGxLZXlzLm1hcCh4RXh0cmFjdG9yKSk7XG4gICAgeVZhbHVlcyA9IHVuaXF1ZShjZWxsS2V5cy5tYXAoeUV4dHJhY3RvcikpO1xuICAgIHZWYWx1ZXMgPSB1bmlxdWUoY2VsbEtleXMubWFwKHZFeHRyYWN0b3IpKTtcblxuICAgIGNlbGxLZXlzLnNvcnQoZnVuY3Rpb24oYSwgYil7IHJldHVybiB4S2V5U29ydGluZ0Z1bmN0aW9uKGEsIGIpIHx8IHlLZXlTb3J0aW5nRnVuY3Rpb24oYSwgYikgfSlcbiAgICBsb2coJ2hlYXRtYXAnLCAnY2VsbHMgYXJlIHNvcnRlZCBieScsIGNlbGxLZXlzKVxuXG5cblxuICAgIGxvZygnaGVhdG1hcCcsICd4IGFuZCB5IGtleXMgYXJlJywge3g6IHhWYWx1ZXMsIHk6eVZhbHVlc30pXG5cblxuICAgIHZhciB4RGltID0geFZhbHVlcy5sZW5ndGgsIHlEaW0gPSB5VmFsdWVzLmxlbmd0aDtcblxuXG4gICAgeVNpemUgPSBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0KHNwYWNlWSwgeURpbSwgeU1pbk9iamVjdFNpemUsIHlNYXhPYmplY3RTaXplLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICB4U2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3Qoc3BhY2VYLCB4RGltLCB4TWluT2JqZWN0U2l6ZSwgeE1heE9iamVjdFNpemUsIG9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIHlTcGFjZXJTaXplID0gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcih5VmFsdWVzLCBzcGFjZVksIHlTaXplLCB5RGltLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICB4U3BhY2VyU2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXIoeFZhbHVlcywgc3BhY2VYLCB4U2l6ZSwgeERpbSwgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgLy8gY29uc29sZS50YWJsZSh7XG4gICAgLy8gICAgIHg6e1xuICAgIC8vICAgICAgIG9iamVjdDogeFNpemUsXG4gICAgLy8gICAgICAgc3BhY2VyOiB4U3BhY2VyU2l6ZSxcbiAgICAvLyAgICAgICBkaW06IHhEaW1cbiAgICAvLyAgICAgfSxcbiAgICAvLyAgICAgeTp7XG4gICAgLy8gICAgICAgb2JqZWN0OiB5U2l6ZSxcbiAgICAvLyAgICAgICBzcGFjZXI6IHlTcGFjZXJTaXplLFxuICAgIC8vICAgICAgIGRpbTogeURpbVxuICAgIC8vICAgICB9XG4gICAgLy9cbiAgICAvLyB9KVxuICAgIGxvZygnaGVhdG1hcCcsICdzaXplIG9mJywge3g6IHhTaXplLCB5OiB5U2l6ZX0pXG5cblxuXG4gICAgdmFyIHlTcGFjZXIgPSBncm91cGluZ1NwYWNlcigpXG4gICAgLmhvcml6b250YWxRKGZhbHNlKVxuICAgIC5tb3ZlYnkoJ2NhdGVnb3J5JylcbiAgICAubnVtYmVyT2ZPYmplY3RzKHlEaW0pXG4gICAgLm9iamVjdENsYXNzKGh5cGVuYXRlKG9iamVjdENsYXNzLCAncm93JykpXG4gICAgLm9iamVjdFNpemUoeVNpemUgKyB5U3BhY2VyU2l6ZSlcbiAgICAuc3BhY2VyU2l6ZSgwKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKVxuICAgIC5lYXNlRnVuYyhlYXNlRnVuYylcbiAgICAubmFtZXNwYWNlKCdyb3cnKVxuXG4gICAgdmFyIHhTcGFjZXIgPSBncm91cGluZ1NwYWNlcigpXG4gICAgLmhvcml6b250YWxRKHRydWUpXG4gICAgLm1vdmVieSgnY2F0ZWdvcnknKVxuICAgIC5udW1iZXJPZk9iamVjdHMoeERpbSlcbiAgICAub2JqZWN0Q2xhc3Mob2JqZWN0Q2xhc3MpXG4gICAgLm9iamVjdFNpemUoeFNpemUgKyB4U3BhY2VyU2l6ZSlcbiAgICAuc3BhY2VyU2l6ZSgwKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKVxuICAgIC5lYXNlRnVuYyhlYXNlRnVuYylcblxuXG4gICAgeVNwYWNlcihjb250YWluZXIsIHlWYWx1ZXMsIDApXG4gICAgY29udGFpbmVyLnNlbGVjdEFsbCgnZy4nK2h5cGVuYXRlKG9iamVjdENsYXNzLCAncm93JykpXG4gICAgLmVhY2goZnVuY3Rpb24oZCwgaSl7IHhTcGFjZXIoZDMuc2VsZWN0KHRoaXMpLCB4VmFsdWVzLCAwKSB9KVxuXG4gICAgdmFyIGNlbGxzID0gY29udGFpbmVyLnNlbGVjdEFsbCgnZzpub3QoLnRvLXJlbW92ZSkuJytvYmplY3RDbGFzcylcblxuXG4gICAgaWYgKGNlbGxLZXlzLmxlbmd0aCAhPSB5VmFsdWVzLmxlbmd0aCAqIHhWYWx1ZXMubGVuZ3RoKSB7XG4gICAgICB2YXIgbG9va3VwID0ge31cbiAgICAgIGNlbGxLZXlzLm1hcChmdW5jdGlvbihrLCBpKXtcbiAgICAgICAgbG9va3VwW3hFeHRyYWN0b3IoaykrJzo6Jyt5RXh0cmFjdG9yKGspXSA9IGtcbiAgICAgIH0pXG5cbiAgICAgIHZhciBwb3NpdGlvbmVkQ2VsbEtleXMgPSBbXVxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB5VmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgeFZhbHVlcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgIHZhciBsb29rdXBWYWx1ZSA9IGxvb2t1cFt4VmFsdWVzW2pdK1wiOjpcIit5VmFsdWVzW2ldXVxuICAgICAgICAgIGlmIChsb29rdXBWYWx1ZSA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHBvc2l0aW9uZWRDZWxsS2V5cy5wdXNoKHVuZGVmaW5lZClcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcG9zaXRpb25lZENlbGxLZXlzLnB1c2gobG9va3VwVmFsdWUpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNlbGxzLmRhdGEocG9zaXRpb25lZENlbGxLZXlzKTtcblxuICAgICAgLy8gbWF5YmUgYnJlYWtzIHRoaXNcbiAgICAgIC8vICEhISEhISBJTVBPUlRBTlQgTk9URSBUT0RPIExPT0sgSEVSRSBCVUdcbiAgICAgIGNlbGxLZXlzID0gcG9zaXRpb25lZENlbGxLZXlzXG4gICAgfSBlbHNlIHtcbiAgICAgIGNlbGxzLmRhdGEoY2VsbEtleXMpO1xuICAgIH1cblxuXG5cbiAgICB2YXIgcGFyZW50SW5kZXhBcnJheSA9IFtdXG4gICAgY2VsbHMuZWFjaChmdW5jdGlvbihkLCBpKXsgcGFyZW50SW5kZXhBcnJheS5wdXNoKE51bWJlcihkMy5zZWxlY3QodGhpcykuYXR0cigncGFyZW50LWluZGV4JykpKSB9KVxuXG4gICAgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24uY29sb3JCeSgpID09ICdpbmRleCdcbiAgICA/IGNvbG9yRnVuY3Rpb24uZGF0YUV4dGVudChbMCwgTWF0aC5tYXgoLi4ucGFyZW50SW5kZXhBcnJheSldKVxuICAgIDogY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KFswLCBNYXRoLm1heCguLi52VmFsdWVzKV0pXG5cbiAgICBjZWxscy5lYWNoKGZ1bmN0aW9uKGtleSwgaSkge1xuICAgICAgbG9nKCdoZWF0bWFwJywgJ2VhY2ggY2VsbCcsIHtrZXk6IGtleSwgaW5kZXg6IGksIG5vZGU6IGQzLnNlbGVjdCh0aGlzKS5ub2RlKCl9KVxuXG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgICAgaWYgKGtleSA9PSB1bmRlZmluZWQpIHtyZXR1cm59XG4gICAgICB2YXIgY3VycmVudERhdGEgPSBkYXRhW2tleV0sXG4gICAgICB2YWx1ZSA9IHZFeHRyYWN0b3Ioa2V5LCBpKSxcbiAgICAgIGkgPSB0LmF0dHIoJ3BhcmVudC1pbmRleCcpID09IHVuZGVmaW5lZCA/IGkgOiB0LmF0dHIoJ3BhcmVudC1pbmRleCcpLFxuICAgICAgZmlsbENvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIHZhbHVlLCBpLCAnZmlsbCcpLCAvLyBwcmV2ZW50IGR1cGxpY2F0ZSBjb21wdXRhdGlvblxuICAgICAgc3Ryb2tlQ29sb3IgPSBjb2xvckZ1bmN0aW9uKGtleSwgdmFsdWUsIGksICAnc3Ryb2tlJylcblxuICAgICAgdmFyIGMgPSBzYWZlU2VsZWN0KHQsICdyZWN0JywgaHlwZW5hdGUob2JqZWN0Q2xhc3MsJ3JlY3QnKSlcbiAgICAgIGMuYXR0cignd2lkdGgnLCB4U2l6ZSArIHhTcGFjZXJTaXplIC0gb2JqZWN0U3Ryb2tlV2lkdGgpXG4gICAgICAuYXR0cignaGVpZ2h0JywgeVNpemUgKyB5U3BhY2VyU2l6ZSAtIG9iamVjdFN0cm9rZVdpZHRoKVxuICAgICAgLmF0dHIoJ2ZpbGwnLCBmaWxsQ29sb3IpXG4gICAgICAuYXR0cigneCcsIG9iamVjdFN0cm9rZVdpZHRoLzIpXG4gICAgICAuYXR0cigneScsIG9iamVjdFN0cm9rZVdpZHRoLzIpXG4gICAgICAuYXR0cignc3Ryb2tlJywgXCIjMDAwXCIpXG4gICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgb2JqZWN0U3Ryb2tlV2lkdGgpXG5cbiAgICB9KVxuXG4gICAgdG9vbHRpcC5zZWxlY3Rpb24oY2VsbHMuc2VsZWN0QWxsKCdyZWN0LicraHlwZW5hdGUob2JqZWN0Q2xhc3MsICdyZWN0JykpKVxuICAgIC5kYXRhKGRhdGEpXG4gICAgLy8gLmtleXMoWydyJywgJ3YnXSlcbiAgICAvLyAuaGVhZGVyKGZ1bmN0aW9uKGQsIGkpe3JldHVybiBoeXBlbmF0ZShkYXRhW2RdW3hLZXldLCBkYXRhW2RdW3lLZXldKSB9KVxuXG4gICAgdG9vbHRpcCgpXG5cblxuICB9XG5cbiAgcmV0dXJuIGhtO1xufVxuXG5leHBvcnQge2hlYXRtYXB9XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHtzZXR1cENvbnRhaW5lciwgY2FsY3VsYXRlV2lkdGhPZk9iamVjdCwgY2FsY3VsYXRlV2lkdGhPZlNwYWNlciwgd2hpc2tlclBhdGh9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHt1bmlxdWUsIGhhc1EsIGZsYXR0ZW59IGZyb20gJy4vYXJyYXktZnVuY3Rpb25zJztcbmltcG9ydCB7Z3JvdXBpbmdTcGFjZXJ9IGZyb20gJy4vZ3JvdXBpbmctc3BhY2VyJztcbmltcG9ydCB7Y29sb3JGdW5jdGlvbiBhcyBDRn0gZnJvbSAnLi9jb2xvci1mdW5jdGlvbic7XG5pbXBvcnQge3Rvb2x0aXAgYXMgVFRpcH0gZnJvbSAnLi90b29sdGlwJztcbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCT1ggQU5EIFdISVNLRVIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKipcbiAqIENyZWF0ZXMgYSBib3h3aGlza2VyXG4gKlxuICoge0BsaW5rIGh0dHBzOi8vc3VtbmV1cm9uLmdpdGxhYi5pby9kM3NtL2RlbW9zL2JveC13aGlza2Vycy9pbmRleC5odG1sIERlbW99XG4gKiBAY29uc3RydWN0b3IgYm94d2hpc2tlclxuICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IHNlbGVjdGlvblxuICogQG5hbWVzcGFjZSBib3h3aGlza2VyXG4gKiBAcmV0dXJucyB7ZnVuY3Rpb259IGJveHdoaXNrZXJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJveHdoaXNrZXIoIHNlbGVjdGlvbiApIHtcbiAgdmFyXG4gIC8qKlxuICAqIERhdGEgdG8gcGxvdC4gQXNzdW1lZCB0byBiZSBhIG9iamVjdCwgd2hlcmUgZWFjaCBrZXkgY29ycmVzcG9uZHMgdG8gYSBib3hcbiAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI2RhdGF9KVxuICAqIEBwYXJhbSB7T2JqZWN0fSBbZGF0YT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGRhdGEsXG4gIC8qKlxuICAqIFdoaWNoIGRpcmVjdGlvbiB0byByZW5kZXIgdGhlIGJveGVzIGluXG4gICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNvcmllbnR9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbb3JpZW50PSdob3Jpem9udGFsJ11cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb3JpZW50ID0gJ2hvcml6b250YWwnLFxuICAvKipcbiAgKiBBbW91bnQgb2YgaG9yaXpvbnRhbCBzcGFjZSAoaW4gcGl4ZWxzKSBhdmFpYmxlIHRvIHJlbmRlciB0aGUgYm94d2hpc2tlciBpblxuICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VYfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlWD11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlWCxcbiAgLyoqXG4gICogQW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBib3h3aGlza2VyIGluXG4gICogKHNlZSB7QGxpbmsgYm94d2hpc2tlci5zcGFjZVl9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VZPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VZLFxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0byBhbGxvdyBib3h3aGlza2VyIHRvIHJlbmRlciBlbGVtZW50cyBwYXNzIHRoZSBtYWluIHNwYXRpYWwgZGltZW5zaW9uXG4gICogZ2l2ZW4gdGhlIG9yaWVudGF0aW9uIChzZWUge0BsaW5rIGJveHdoaXNrZXIjb3JpZW50fSksIHdoZXJlIHtAbGluayBib3h3aGlza2VyI29yaWVudH09XCJob3Jpem9udGFsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGJveHdoaXNrZXIjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VZfVxuICAqIEBwYXJhbSB7Ym9vbGVhbn0gW292ZXJmbG93UT1mYWxzZV1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb3ZlcmZsb3dRID0gdHJ1ZSxcblxuICAvKipcbiAgKiBBbiBhcnJheSAtIHB1dGF0aXZlbHkgb2Ygb3RoZXIgYXJyYXlzIC0gZGVwaWN0aW5nIGhvdyBib3hlcyBzaG91bGQgYmUgYXJyYW5nZWRcbiAgKiBAcGFyYW0ge0FycmF5W119IFtncm91cGluZz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGdyb3VwaW5nLFxuICBxdWFydGlsZXNLZXkgPSAncXVhcnRpbGVzJywgLy8ga2V5IGluIG9iamVjdCB3aGVyZSBxdWFydGlsZXMgYXJlIHN0b3JlZFxuICBxdWFydGlsZXNLZXlzID0gW1wiMC4wMFwiLCBcIjAuMjVcIiwgXCIwLjUwXCIsIFwiMC43NVwiLCBcIjEuMDBcIl0sIC8vIGtleXMgaW4gcXVhcnRpbGVzIG9iamVjdCBtYXBwaW5nIHZhbHVlcyB0byBtaW4sIHExLCBxMiwgcTMgYW5kIG1heFxuXG5cbiAgLyoqXG4gICogSG93IHRvIGdldCB0aGUgdmFsdWUgb2YgdGhlIGJveHdoaXNrZXJcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdmFsdWVFeHRyYWN0b3I9ZnVuY3Rpb24oa2V5LCBpbmRleCkgeyByZXR1cm4gZGF0YVtrZXldW3F1YXJ0aWxlc0tleV0gfV1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV1bcXVhcnRpbGVzS2V5XSB9LFxuICAvKipcbiAgKiBIb3cgdG8gc29ydCB0aGUgYm94ZXMgLSBpZiB7QGxpbmsgYm94d2hpc2tlciNncm91cGluZ30gaXMgbm90IHByb3ZpZGVkLlxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtzb3J0aW5nRnVuY3Rpb249ZGVzY2VuZGluZ11cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKiBkZWZhdWx0XG4gICogZnVuY3Rpb24oa2V5QSwga2V5Qikge3JldHVybiBkMy5kZXNjZW5kaW5nKFxuICAqICAgdmFsdWVFeHRyYWN0b3Ioa2V5QSlbcXVhcnRpbGVzS2V5c1s0XV0sXG4gICogICB2YWx1ZUV4dHJhY3RvcihrZXlCKVtxdWFydGlsZXNLZXlzWzRdXVxuICAqICl9XG4gICovXG4gIHNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKGtleUEsIGtleUIpIHtyZXR1cm4gZDMuZGVzY2VuZGluZyhcbiAgICB2YWx1ZUV4dHJhY3RvcihrZXlBKVtxdWFydGlsZXNLZXlzWzRdXSxcbiAgICB2YWx1ZUV4dHJhY3RvcihrZXlCKVtxdWFydGlsZXNLZXlzWzRdXVxuICApfSxcbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCBib3h3aGlza2VyIHZhbHVlcyBzaG91bGQgYmUgdHJhbnNmb3JtZWQgYnlcbiAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbc2NhbGU9ZDMuc2NhbGVMaW5lYXJdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKSxcbiAgLyoqXG4gICogVGhlIHBhZGRpbmcgZm9yIHRoZSBkb21haW4gb2YgdGhlIHNjYWxlIChzZWUge0BsaW5rIGJveHdoaXNrZXIjc2NhbGV9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbZG9tYWluUGFkZGluZz0wLjVdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGRvbWFpblBhZGRpbmcgPSAwLjUsXG4gIC8qKlxuICAqIERlZmF1bHQgc3BhY2UgZm9yIHRoZSBzcGFjZXIgKHBlcmNlbnRhZ2UpIG9mIG1haW4gZGltZW5zaW9uIGdpdmVuIHRoZSBvcmllbnRhdGlvblxuICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjb3JpZW50fSksIHdoZXJlIHtAbGluayBib3h3aGlza2VyI29yaWVudH09XCJob3Jpem9udGFsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGJveHdoaXNrZXIjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VZfSBiZXR3ZWVuIGJveGVzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvYmplY3RTcGFjZXI9MC4wNV1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0U3BhY2VyID0gMC4wNSxcbiAgLyoqXG4gICogVGhlIG1pbmltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmVcbiAgKiBAcGFyYW0ge251bWJlcn0gW21pbk9iamVjdFNpemU9MTVdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1pbk9iamVjdFNpemUgPSAxNSxcbiAgLyoqXG4gICogVGhlIG1heGltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmVcbiAgKiBAcGFyYW0ge251bWJlcn0gW21heE9iamVjdFNpemU9NTBdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1heE9iamVjdFNpemUgPSA1MCxcbiAgLyoqXG4gICogUGVyY2VudCBvZiBib3ggYW5kIHdoaXNrZXIgc2l6ZSB0aGF0IHdoaXNrZXJzIHdpbGwgYmUgcmVuZGVyZWRcbiAgKiBzZWUge0BsaW5rIGJveHdoaXNrZXIjb2JqZWN0U2l6ZX1cbiAgKiBAcGFyYW0ge251bWJlcn0gW3doaXNrZXJXaWR0aFBlcmNlbnQ9MC42XVxuICAqIEBtZW1iZXJvZiBib3h3aGlza2VyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB3aGlza2VyV2lkdGhQZXJjZW50ID0gLjYsXG4gIC8qKlxuICAqIEluc3RhbmNlIG9mIENvbG9yRnVuY3Rpb25cbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKV1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgY29sb3JGdW5jdGlvbiA9IENGKCksXG4gIC8qKlxuICAqIFRoZSBzdHJva2Ugd2lkdGggb2YgdGhlIGJveGVzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtib3hTdHJva2VXaWR0aD0yXVxuICAqIEBtZW1iZXJvZiBib3h3aGlza2VyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBib3hTdHJva2VXaWR0aCA9IDIsXG4gIC8qKlxuICAqIFRoZSBzdHJva2Ugd2lkdGggb2YgdGhlIHdoaXNrZXJzXG4gICogQHBhcmFtIHtudW1iZXJ9IFt3aGlza2VyU3Ryb2tlV2lkdGg9Ml1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgd2hpc2tlclN0cm9rZVdpZHRoID0gMixcblxuICAvKipcbiAgKiBDb2xvciBvZiB0aGUgYmFja2dyb3VuZFxuICAqIEBwYXJhbSB7c3RyaW5nfSBbYmFja2dyb3VuZEZpbGw9XCJ0cmFuc3BhcmVudFwiXVxuICAqIEBtZW1iZXJvZiBib3h3aGlza2VyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCcsXG4gIC8qKlxuICAqIE5hbWVzcGFjZSBmb3IgYWxsIGl0ZW1zIG1hZGUgYnkgdGhpcyBpbnN0YW5jZSBvZiBib3h3aGlza2VyXG4gICogQHBhcmFtIHtzdHJpbmd9IFtuYW1lc3BhY2U9XCJkM3NtLWJveC13aGlza2VyXCJdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG5hbWVzcGFjZSA9ICdkM3NtLWJveC13aGlza2VyJyxcbiAgLyoqXG4gICogQ2xhc3MgbmFtZSBmb3IgYm94d2hpc2tlciBjb250YWluZXIgKDxnPiBlbGVtZW50KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbb2JqZWN0Q2xhc3M9XCJib3gtd2hpc2tcIl1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0Q2xhc3MgPSAnYm94LXdoaXNrJyxcblxuICAvKipcbiAgKiBEdXJhdGlvbiBvZiBhbGwgdHJhbnNpdGlvbnMgb2YgdGhpcyBlbGVtZW50XG4gICogQHBhcmFtIHtudW1iZXJ9IFt0cmFuc2l0aW9uRHVyYXRpb249MTAwMF1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMCxcbiAgLyoqXG4gICogRWFzaW5nIGZ1bmN0aW9uIGZvciB0cmFuc2l0aW9uc1xuICAqIEBwYXJhbSB7ZDMuZWFzZX0gW2Vhc2VGdW5jPWQzLmVhc2VFeHBdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGVhc2VGdW5jID0gZDMuZWFzZUV4cCxcblxuICAvKipcbiAgKiBUaGUga2V5cyBvZiB0aGUgYm94ZXNcbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbYm94S2V5cz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJveEtleXMsXG4gIC8qKlxuICAqIFRoZSB2YWx1ZXMgb2YgdGhlIGJveGVzXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW2JveFZhbHVlcz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJveFZhbHVlcyxcbiAgLyoqXG4gICogVGhlIG9iamVjdFNpemUgKGFjdHVhbCB3aWR0aCkgdXNlZCBieSB0aGUgYm94ZXNcbiAgKiBAcGFyYW0ge251bWJlcn0gW29iamVjdFNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBib3h3aGlza2VyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvYmplY3RTaXplLFxuICAvKipcbiAgKiBUaGUgc3BhY2VyU2l6ZSAoYWN0dWFsIHdpZHRoKSB1c2VkIGJ5IHRoZSBzcGFjZXJzIGJldHdlZW4gdGhlIGJveGVzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZXJTaXplPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VyU2l6ZSxcbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgVG9vbHRpcFxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt0b29sdGlwPXRvb2x0aXAoKV1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdG9vbHRpcCA9IFRUaXAoKVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBzZWxlY3Rpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IGQzLnNlbGVjdGlvbn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2VsZWN0aW9uID0gc2VsZWN0aW9uXG4gICAqL1xuICBib3h3aGlza2VyLnNlbGVjdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2VsZWN0aW9uID0gXywgYm94d2hpc2tlcikgOiBzZWxlY3Rpb247IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGRhdGFcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNkYXRhfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgb2JqZWN0fVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGJveHdoaXNrZXIuZGF0YSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGF0YSA9IF8sIGJveHdoaXNrZXIpIDogZGF0YTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgb3JpZW50IG9mIHRoZSBib3hlc1xuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI29yaWVudH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IG9iamVjdH1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqL1xuICBib3h3aGlza2VyLm9yaWVudCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3JpZW50ID0gXywgYm94d2hpc2tlcikgOiBvcmllbnQ7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3NwYWNlWH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVggPSB1bmRlZmluZWRcbiAgICovXG4gIGJveHdoaXNrZXIuc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVggPSBfLCBib3h3aGlza2VyKSA6IHNwYWNlWDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3NwYWNlWX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVkgPSB1bmRlZmluZWRcbiAgICovXG4gIGJveHdoaXNrZXIuc3BhY2VZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVkgPSBfLCBib3h3aGlza2VyKSA6IHNwYWNlWTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IGJveHdoaXNrZXIgaXMgYWxsb3dlZCB0byBnbyBiZXlvbmQgc3BlY2lmaWVkIGRpbWVuc2lvbnNcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNvdmVyZmxvd1F9KVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgYm9vbGVhbn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3ZlcmZsb3dRID0gZmFsc2VcbiAgICovXG4gIGJveHdoaXNrZXIub3ZlcmZsb3dRID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvdmVyZmxvd1EgPSBfLCBib3h3aGlza2VyKSA6IG92ZXJmbG93UTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBncm91cGluZyBvZiB0aGUgYm94ZXNcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNncm91cGluZ30pXG4gICAqIEBwYXJhbSB7QXJyYXlbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBBcnJheVtdfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBncm91cGluZyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYm94d2hpc2tlci5ncm91cGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZ3JvdXBpbmcgPSBfLCBib3h3aGlza2VyKSA6IGdyb3VwaW5nOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHF1YXJ0aWxlc0tleVxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3F1YXJ0aWxlc0tleX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgcXVhcnRpbGVzS2V5ID0gcXVhcnRpbGVzXG4gICAqL1xuICBib3h3aGlza2VyLnF1YXJ0aWxlc0tleSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocXVhcnRpbGVzS2V5ID0gXywgYm94d2hpc2tlcikgOiBxdWFydGlsZXNLZXk7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcXVhcnRpbGVzS2V5c1xuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3F1YXJ0aWxlc0tleXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBxdWFydGlsZXNLZXlzID0gW1EwLCBRMSwgUTIsIFEzLCBRNF1cbiAgICovXG4gIGJveHdoaXNrZXIucXVhcnRpbGVzS2V5cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocXVhcnRpbGVzS2V5cyA9IF8sIGJveHdoaXNrZXIpIDogcXVhcnRpbGVzS2V5czsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2YWx1ZUV4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3ZhbHVlRXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV1bcXVhcnRpbGVzS2V5XSB9LFxuICAgKi9cblxuICBib3h3aGlza2VyLnZhbHVlRXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZUV4dHJhY3RvciA9IF8sIGJveHdoaXNrZXIpIDogdmFsdWVFeHRyYWN0b3I7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgc29ydGluZ0Z1bmN0aW9uXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjc29ydGluZ0Z1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oa2V5QSwga2V5Qikge3JldHVybiBkMy5kZXNjZW5kaW5nKFxuICAgKiAgIHZhbHVlRXh0cmFjdG9yKGtleUEpW3F1YXJ0aWxlc0tleXNbNF1dLFxuICAgKiAgIHZhbHVlRXh0cmFjdG9yKGtleUIpW3F1YXJ0aWxlc0tleXNbNF1dXG4gICAqICl9LFxuICAgKi9cbiAgYm94d2hpc2tlci5zb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNvcnRpbmdGdW5jdGlvbiA9IF8sIGJveHdoaXNrZXIpIDogc29ydGluZ0Z1bmN0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHNjYWxlIGZvciB3aGljaCB0aGUgYm94d2hpc2tlciB2YWx1ZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjc2NhbGV9KVxuICAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IGQzLnNjYWxlfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcbiAgICovXG4gIGJveHdoaXNrZXIuc2NhbGUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNjYWxlID0gXywgYm94d2hpc2tlcikgOiBzY2FsZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSBzY2FsZVxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI2RvbWFpblBhZGRpbmd9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGRvbWFpblBhZGRpbmcgPSAwLjVcbiAgICovXG4gIGJveHdoaXNrZXIuZG9tYWluUGFkZGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZG9tYWluUGFkZGluZyA9IF8sIGJveHdoaXNrZXIpIDogZG9tYWluUGFkZGluZzsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIG9iamVjdFNwYWNlclxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI29iamVjdFNwYWNlcn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0U3BhY2VyID0gMC4wNVxuICAgKi9cbiAgYm94d2hpc2tlci5vYmplY3RTcGFjZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNwYWNlciA9IF8sIGJveHdoaXNrZXIpIDogb2JqZWN0U3BhY2VyOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1pbk9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNtaW5PYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtaW5PYmplY3RTaXplID0gMTVcbiAgICovXG4gIGJveHdoaXNrZXIubWluT2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWluT2JqZWN0U2l6ZSA9IF8sIGJveHdoaXNrZXIpIDogbWluT2JqZWN0U2l6ZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtYXhPYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjbWF4T2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWF4T2JqZWN0U2l6ZSA9IDUwXG4gICAqL1xuICBib3h3aGlza2VyLm1heE9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1heE9iamVjdFNpemUgPSBfLCBib3h3aGlza2VyKSA6IG1heE9iamVjdFNpemU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgd2hpc2tlcldpZHRoUGVyY2VudFxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3doaXNrZXJXaWR0aFBlcmNlbnR9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1heE9iamVjdFNpemUgPSAwLjZcbiAgICovXG4gIGJveHdoaXNrZXIud2hpc2tlcldpZHRoUGVyY2VudCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAod2hpc2tlcldpZHRoUGVyY2VudCA9IF8sIGJveHdoaXNrZXIpIDogd2hpc2tlcldpZHRoUGVyY2VudDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBjb2xvckZ1bmN0aW9uXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjY29sb3JGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7Y29sb3JGdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBjb2xvckZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBjb2xvckZ1bmN0aW9uID0gY29sb3JGdW5jdGlvbigpXG4gICAqL1xuICBib3h3aGlza2VyLmNvbG9yRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNvbG9yRnVuY3Rpb24gPSBfLCBib3h3aGlza2VyKSA6IGNvbG9yRnVuY3Rpb247IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYm94U3Ryb2tlV2lkdGhcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNib3hTdHJva2VXaWR0aH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYm94U3Ryb2tlV2lkdGggPSAyXG4gICAqL1xuICBib3h3aGlza2VyLmJveFN0cm9rZVdpZHRoID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChib3hTdHJva2VXaWR0aCA9IF8sIGJveHdoaXNrZXIpIDogYm94U3Ryb2tlV2lkdGg7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgd2hpc2tlclN0cm9rZVdpZHRoXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjd2hpc2tlclN0cm9rZVdpZHRofSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB3aGlza2VyU3Ryb2tlV2lkdGggPSAyXG4gICAqL1xuICBib3h3aGlza2VyLndoaXNrZXJTdHJva2VXaWR0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAod2hpc2tlclN0cm9rZVdpZHRoID0gXywgYm94d2hpc2tlcikgOiB3aGlza2VyU3Ryb2tlV2lkdGg7IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBiYWNrZ3JvdW5kRmlsbFxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI2JhY2tncm91bmRGaWxsfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCdcbiAgICovXG4gIGJveHdoaXNrZXIuYmFja2dyb3VuZEZpbGwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhY2tncm91bmRGaWxsID0gXywgYm94d2hpc2tlcikgOiBiYWNrZ3JvdW5kRmlsbDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBuYW1lc3BhY2VcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNuYW1lc3BhY2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG5hbWVzcGFjZSA9ICdkM3NtLWJveHdoaXNrZXInXG4gICAqL1xuICBib3h3aGlza2VyLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgYm94d2hpc2tlcikgOiBuYW1lc3BhY2U7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0Q2xhc3NcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNvYmplY3RDbGFzc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0Q2xhc3MgPSAndGljay1ncm91cCdcbiAgICovXG4gIGJveHdoaXNrZXIub2JqZWN0Q2xhc3MgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdENsYXNzID0gXywgYm94d2hpc2tlcikgOiBvYmplY3RDbGFzczsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0cmFuc2l0aW9uRHVyYXRpb25cbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciN0cmFuc2l0aW9uRHVyYXRpb259KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRyYW5zaXRpb25EdXJhdGlvbiA9IDEwMDBcbiAgICovXG4gIGJveHdoaXNrZXIudHJhbnNpdGlvbkR1cmF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0cmFuc2l0aW9uRHVyYXRpb24gPSBfLCBib3h3aGlza2VyKSA6IHRyYW5zaXRpb25EdXJhdGlvbjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBlYXNlRnVuY1xuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI2Vhc2VGdW5jfSlcbiAgICogQHBhcmFtIHtkMy5lYXNlfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IGQzLmVhc2V9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGVhc2VGdW5jID0gZDMuZWFzZUV4cFxuICAgKi9cbiAgYm94d2hpc2tlci5lYXNlRnVuYyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZWFzZUZ1bmMgPSBfLCBib3h3aGlza2VyKSA6IGVhc2VGdW5jOyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFyS2V5c1xuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI2JveEtleXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBib3hLZXlzID0gdW5kZWZpbmVkXG4gICAqL1xuICBib3h3aGlza2VyLmJveEtleXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJveEtleXMgPSBfLCBib3h3aGlza2VyKSA6IGJveEtleXM7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYm94VmFsdWVzXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjYm94VmFsdWVzfSlcbiAgICogQHBhcmFtIHtudW1iZXJbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBudW1iZXJbXX1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYm94VmFsdWVzID0gdW5kZWZpbmVkXG4gICAqL1xuICBib3h3aGlza2VyLmJveFZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYm94VmFsdWVzID0gXywgYm94d2hpc2tlcikgOiBib3hWYWx1ZXM7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI29iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdFNpemUgPSB1bmRlZmluZWRcbiAgICovXG4gIGJveHdoaXNrZXIub2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0U2l6ZSA9IF8sIGJveHdoaXNrZXIpIDogb2JqZWN0U2l6ZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzcGFjZXJTaXplXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VyU2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VyU2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYm94d2hpc2tlci5zcGFjZXJTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZXJTaXplID0gXywgYm94d2hpc2tlcikgOiBzcGFjZXJTaXplOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRvb2x0aXBcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciN0b29sdGlwfSlcbiAgICogQHBhcmFtIHt0b29sdGlwfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IHRvb2x0aXB9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRvb2x0aXAgPSB0b29sdGlwKClcbiAgICovXG4gIGJveHdoaXNrZXIudG9vbHRpcCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodG9vbHRpcCA9IF8sIGJveHdoaXNrZXIpIDogdG9vbHRpcDsgfTtcblxuXG4gIGZ1bmN0aW9uIGJveHdoaXNrZXIoKSB7XG4gICAgLy8gZm9yIGNvbnZlbmllbmNlIGluIGhhbmRsaW5nIG9yaWVudGF0aW9uIHNwZWNpZmljIHZhbHVlc1xuICAgIHZhciBob3Jpem9udGFsUSA9IChvcmllbnQgPT0gJ2hvcml6b250YWwnKSA/IHRydWUgOiBmYWxzZVxuICAgIHZhciB2ZXJ0aWNhbFEgPSAhaG9yaXpvbnRhbFFcblxuICAgIC8vIGJhY2tncm91bmQgY2xpcGluZyByZWN0YW5nbGVcbiAgICB2YXIgYmdjcFJlY3QgPSB7eDowLCB5OjAsIHdpZHRoOiBzcGFjZVgsIGhlaWdodDpzcGFjZVl9XG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cbiAgICAvLyBpZiBncm91cGluZyBpcyB1bmRlZmluZWQgc29ydCBrZXlzIGJ5IHNvcnRpbmcgZnVuY3RcbiAgICB2YXIgb3JkZXJlZCA9IChncm91cGluZyA9PSB1bmRlZmluZWQpID8gZDMua2V5cyhkYXRhKS5zb3J0KHNvcnRpbmdGdW5jdGlvbikgOiBncm91cGluZ1xuICAgIC8vIHRvIHByZXZlbnQgcmUtY2FsY3VsYXRpb24gYW5kIGdldHRlcnMgdG8gYmUgcGFzc2VkIHRvIGF4ZXNcbiAgICBib3hLZXlzID0gZmxhdHRlbihvcmRlcmVkKVxuICAgIGJveFZhbHVlcyA9IGJveEtleXMubWFwKHZhbHVlRXh0cmFjdG9yKVxuXG5cbiAgICB2YXIgbnVtYmVyT2ZPYmplY3RzID0gYm94S2V5cy5sZW5ndGhcbiAgICB2YXIgZXh0ZW50ID0gW1xuICAgICAgTWF0aC5taW4oLi4uYm94VmFsdWVzLm1hcChmdW5jdGlvbihkLGkpe3JldHVybiBkW3F1YXJ0aWxlc0tleXNbMF1dfSkpIC0gZG9tYWluUGFkZGluZyxcbiAgICAgIE1hdGgubWF4KC4uLmJveFZhbHVlcy5tYXAoZnVuY3Rpb24oZCxpKXtyZXR1cm4gZFtxdWFydGlsZXNLZXlzWzRdXX0pKSArIGRvbWFpblBhZGRpbmdcbiAgICBdO1xuXG4gICAgLy8gc2V0IHRoZSBzY2FsZVxuICAgIHNjYWxlLmRvbWFpbihleHRlbnQpLnJhbmdlKGhvcml6b250YWxRID8gWzAsc3BhY2VZXSA6IFtzcGFjZVgsIDBdKVxuICAgIHZhciBzcGFjZSA9IGhvcml6b250YWxRID8gc3BhY2VYIDogc3BhY2VZXG4gICAgLy8gY2FsY3VsYXRlIG9iamVjdCBzaXplXG4gICAgb2JqZWN0U2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3Qoc3BhY2UsIG51bWJlck9mT2JqZWN0cywgbWluT2JqZWN0U2l6ZSwgbWF4T2JqZWN0U2l6ZSwgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgLy8gY2FsY3VsYXRlIHNwYWNlciBzaXplIGlmIG5lZWRlZFxuICAgIHNwYWNlclNpemUgPSBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyKGJveEtleXMsIHNwYWNlLCBvYmplY3RTaXplLCBudW1iZXJPZk9iamVjdHMsIG9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIC8vIG1ha2UgdGhlIG5lc3RlZCBncm91cHNcbiAgICB2YXIgc3BhY2VyRnVuY3Rpb24gPSBncm91cGluZ1NwYWNlcigpXG4gICAgLmhvcml6b250YWxRKGhvcml6b250YWxRKS5zY2FsZShzY2FsZSkubW92ZWJ5KCdjYXRlZ29yeScpLm51bWJlck9mT2JqZWN0cyhudW1iZXJPZk9iamVjdHMpXG4gICAgLm9iamVjdENsYXNzKG9iamVjdENsYXNzKS5vYmplY3RTaXplKG9iamVjdFNpemUpLnNwYWNlclNpemUoc3BhY2VyU2l6ZSlcbiAgICAudHJhbnNpdGlvbkR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbikuZWFzZUZ1bmMoZWFzZUZ1bmMpXG4gICAgLm5hbWVzcGFjZShuYW1lc3BhY2UpXG5cbiAgICAvLyBtb3ZlIHN0dWZmXG4gICAgc3BhY2VyRnVuY3Rpb24oY29udGFpbmVyLCBvcmRlcmVkLCAwKVxuXG4gICAgdmFyIHBhcmVudEluZGV4QXJyYXkgPSBbXVxuICAgIGNvbnRhaW5lci5zZWxlY3RBbGwoJ2c6bm90KC50by1yZW1vdmUpLicrb2JqZWN0Q2xhc3MpXG4gICAgLmVhY2goZnVuY3Rpb24oZCwgaSl7aWYgKGhhc1EoYm94S2V5cywgZCkpeyBwYXJlbnRJbmRleEFycmF5LnB1c2goTnVtYmVyKGQzLnNlbGVjdCh0aGlzKS5hdHRyKCdwYXJlbnQtaW5kZXgnKSkpfX0pXG5cblxuXG4gICAgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24uY29sb3JCeSgpID09ICdpbmRleCdcbiAgICA/IGNvbG9yRnVuY3Rpb24uZGF0YUV4dGVudChbMCwgTWF0aC5tYXgoLi4ucGFyZW50SW5kZXhBcnJheSldKVxuICAgIDogY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KGV4dGVudClcblxuXG4gICAgLy8gc2V0IGF0dHJpYnV0ZXMgZm9yIGJveCBhbmQgd2hpc2tlcnNcbiAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnOm5vdCgudG8tcmVtb3ZlKS4nK29iamVjdENsYXNzKS5lYWNoKGZ1bmN0aW9uKGtleSwgaSkge1xuICAgICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcyksXG4gICAgICBjdXJyZW50RGF0YSA9IGRhdGFba2V5XSxcblxuICAgICAgcXVhcnRpbGVzID0gdmFsdWVFeHRyYWN0b3Ioa2V5LCBpKSxcbiAgICAgIHEwID0gcXVhcnRpbGVzW3F1YXJ0aWxlc0tleXNbMF1dLFxuICAgICAgcTEgPSBxdWFydGlsZXNbcXVhcnRpbGVzS2V5c1sxXV0sXG4gICAgICBxMiA9IHF1YXJ0aWxlc1txdWFydGlsZXNLZXlzWzJdXSxcbiAgICAgIHEzID0gcXVhcnRpbGVzW3F1YXJ0aWxlc0tleXNbM11dLFxuICAgICAgcTQgPSBxdWFydGlsZXNbcXVhcnRpbGVzS2V5c1s0XV1cblxuICAgICAgdmFyIGkgPSB0LmF0dHIoJ3BhcmVudC1pbmRleCcpID09IHVuZGVmaW5lZCA/IGkgOiB0LmF0dHIoJ3BhcmVudC1pbmRleCcpLFxuICAgICAgZmlsbENvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIHEyLCBpLCAnZmlsbCcpLCAvLyBwcmV2ZW50IGR1cGxpY2F0ZSBjb21wdXRhdGlvblxuICAgICAgc3Ryb2tlQ29sb3IgPSBjb2xvckZ1bmN0aW9uKGtleSwgcTIsIGksICAnc3Ryb2tlJylcblxuXG4gICAgICB2YXJcbiAgICAgIHdoaXNrID0gc2FmZVNlbGVjdCh0LCAnZycsICd3aGlza2VyJyksXG4gICAgICB1V2hpc2sgPSBzYWZlU2VsZWN0KHdoaXNrLCAncGF0aCcsICd1cHBlcicpLFxuICAgICAgbFdoaXNrID0gc2FmZVNlbGVjdCh3aGlzaywgJ3BhdGgnLCAnbG93ZXInKSxcbiAgICAgIHF1YXJ0ID0gc2FmZVNlbGVjdCh0LCAnZycsICdxdWFydGlsZScpLFxuICAgICAgdVF1YXJ0ID0gc2FmZVNlbGVjdChxdWFydCwgJ3JlY3QnLCAndXBwZXInKSxcbiAgICAgIGxRdWFydCA9IHNhZmVTZWxlY3QocXVhcnQsICdyZWN0JywgJ2xvd2VyJyksXG4gICAgICBtUXVhcnQgPSBzYWZlU2VsZWN0KHF1YXJ0LCAnY2lyY2xlJywgJ21lZGlhbicpXG5cblxuICAgICAgLy8gc2V0IHVwcGVyIHF1YXJ0aWxlIChxMylcbiAgICAgIHVRdWFydC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ3dpZHRoJywgaG9yaXpvbnRhbFEgPyBvYmplY3RTaXplIDogc2NhbGUocTMpIC0gc2NhbGUocTIpKVxuICAgICAgLmF0dHIoJ2hlaWdodCcsIHZlcnRpY2FsUSA/IG9iamVjdFNpemUgOiBzY2FsZShxMykgLSBzY2FsZShxMikpXG4gICAgICAuYXR0cignZmlsbCcsIGZpbGxDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2UnLCBzdHJva2VDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2Utd2lkdGgnLCBib3hTdHJva2VXaWR0aClcbiAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUSA/IDAgOiBzY2FsZShxMiksXG4gICAgICAgIHkgPSB2ZXJ0aWNhbFEgPyAwIDogc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHEzKSxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcblxuICAgICAgLy8gc2V0IGxvd2VyIHF1YXJ0aWxlIChxMSlcbiAgICAgIGxRdWFydC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ3dpZHRoJywgaG9yaXpvbnRhbFEgPyBvYmplY3RTaXplIDogc2NhbGUocTIpIC0gc2NhbGUocTEpKVxuICAgICAgLmF0dHIoJ2hlaWdodCcsIHZlcnRpY2FsUSA/IG9iamVjdFNpemUgOiBzY2FsZShxMikgLSBzY2FsZShxMSkpXG4gICAgICAuYXR0cignZmlsbCcsIGZpbGxDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2UnLCBzdHJva2VDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2Utd2lkdGgnLCBib3hTdHJva2VXaWR0aClcbiAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUSA/IDAgOiBzY2FsZShxMSksXG4gICAgICAgIHkgPSB2ZXJ0aWNhbFEgPyAwIDogc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHEyKSxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcblxuXG4gICAgICAvLyBzZXQgbWVkaWFuIChxMilcbiAgICAgIG1RdWFydC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ3InLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyIHIgPSBvYmplY3RTaXplIC8gMlxuICAgICAgICB2YXIgZGlmID0gKHNjYWxlKHEzKSAtIHNjYWxlKHExKSkgLyAyXG4gICAgICAgIHJldHVybiAociA+IGRpZikgPyBkaWYgOiByXG4gICAgICB9KVxuICAgICAgLmF0dHIoJ2ZpbGwnLCBmaWxsQ29sb3IpXG4gICAgICAuYXR0cignc3Ryb2tlJywgc3Ryb2tlQ29sb3IpXG4gICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgYm94U3Ryb2tlV2lkdGgpXG4gICAgICAuYXR0cigndHJhbnNmb3JtJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIHZhclxuICAgICAgICB4ID0gaG9yaXpvbnRhbFEgPyBvYmplY3RTaXplIC8gMiA6IHNjYWxlKHEyKSxcbiAgICAgICAgeSA9IHZlcnRpY2FsUSA/IG9iamVjdFNpemUgLyAyIDogc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHEyKSxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcblxuICAgICAgLy8gc2V0IGxvd2VyIHdoaXNrZXIgKG1pbilcbiAgICAgIGxXaGlzay50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ2QnLCBmdW5jdGlvbihkZCwgaWkpe1xuICAgICAgICB2YXJcbiAgICAgICAgZGlyID0gZmFsc2UsXG4gICAgICAgIHggPSAwLFxuICAgICAgICB5ID0gMCxcbiAgICAgICAgaCA9IGhvcml6b250YWxRID8gc2NhbGUocTEpIC0gc2NhbGUocTApIDogb2JqZWN0U2l6ZSxcbiAgICAgICAgdyA9IHZlcnRpY2FsUSA/IHNjYWxlKHExKSAtIHNjYWxlKHEwKSA6IG9iamVjdFNpemVcbiAgICAgICAgcmV0dXJuIHdoaXNrZXJQYXRoKGRpciwgeCwgeSwgdywgaCwgd2hpc2tlcldpZHRoUGVyY2VudCwgb3JpZW50KVxuICAgICAgfSlcbiAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUSA/IDAgOiBzY2FsZShxMSksXG4gICAgICAgIHkgPSB2ZXJ0aWNhbFEgPyAwIDogc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHExKSxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcbiAgICAgIC5hdHRyKCdzdHJva2UnLCAnYmxhY2snKS5hdHRyKCdzdHJva2Utd2lkdGgnLCB3aGlza2VyU3Ryb2tlV2lkdGgpXG4gICAgICAuYXR0cignZmlsbCcsICdub25lJylcblxuICAgICAgLy8gc2V0IHVwcGVyIHdoaXNrZXIgKG1heClcbiAgICAgIHVXaGlzay50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ2QnLCBmdW5jdGlvbihkZCwgaWkpe1xuICAgICAgICB2YXJcbiAgICAgICAgZGlyID0gdHJ1ZSxcbiAgICAgICAgeCA9IDAsXG4gICAgICAgIHkgPSAwLFxuICAgICAgICBoID0gaG9yaXpvbnRhbFEgPyBzY2FsZShxNCkgLSBzY2FsZShxMykgOiBvYmplY3RTaXplLFxuICAgICAgICB3ID0gdmVydGljYWxRID8gc2NhbGUocTQpIC0gc2NhbGUocTMpIDogb2JqZWN0U2l6ZVxuICAgICAgICByZXR1cm4gd2hpc2tlclBhdGgoZGlyLCB4LCB5LCB3LCBoLCB3aGlza2VyV2lkdGhQZXJjZW50LCBvcmllbnQpXG4gICAgICB9KVxuICAgICAgLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgICB2YXJcbiAgICAgICAgeCA9IGhvcml6b250YWxRID8gMCA6IHNjYWxlKHEzKSxcbiAgICAgICAgeSA9IHZlcnRpY2FsUSA/IDAgOiAgc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHE0KSxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcbiAgICAgIC5hdHRyKCdzdHJva2UnLCAnYmxhY2snKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIHdoaXNrZXJTdHJva2VXaWR0aClcbiAgICAgIC5hdHRyKCdmaWxsJywgJ25vbmUnKVxuXG4gICAgfSlcblxuICAgIHRvb2x0aXAuc2VsZWN0aW9uKGNvbnRhaW5lci5zZWxlY3RBbGwoJ2c6bm90KC50by1yZW1vdmUpLicrb2JqZWN0Q2xhc3MpKVxuICAgIC5kYXRhKGRhdGEpXG4gICAgdG9vbHRpcCgpXG5cblxuICB9XG5cbiAgcmV0dXJuIGJveHdoaXNrZXJcbn1cbiIsImltcG9ydCB7aHlwZW5hdGUsIHNhZmVTZWxlY3R9IGZyb20gJy4vaGVscGVycyc7XG5pbXBvcnQge3NlbGVjdEZpbHRlcn0gZnJvbSAnLi9zZWxlY3QtZmlsdGVyJztcblxuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERBVEFUT0dHTEUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qKlxuICogQ3JlYXRlcyBhIGRhdGF0b2dnbGVcbiAqIEBjb25zdHJ1Y3RvciBkYXRhdG9nZ2xlXG4gKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gc2VsZWN0aW9uXG4gKiBAbmFtZXNwYWNlIGRhdGF0b2dnbGVcbiAqIEByZXR1cm5zIHtmdW5jdGlvbn0gZGF0YXRvZ2dsZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZGF0YXRvZ2dsZSggc2VsZWN0aW9uICkge1xuICB2YXJcbiAgLyoqXG4gICogS2V5cyB0byBtYWtlIHRvZ2dsZS1hYmxlIG9wdGlvbnNcbiAgKiAoc2VlIHtAbGluayBkYXRhdG9nZ2xlI2tleXN9KVxuICAqIEBwYXJhbSB7c3RyaW5nW119IFtrZXlzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgZGF0YXRvZ2dsZSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAga2V5cyxcbiAgLyoqXG4gICogV2hhdCB0byBkbyB3aGVuIGEgZGlmZmVyZW50IGtleSBpcyBjbGlja2VkXG4gICogKHNlZSB7QGxpbmsgZGF0YXRvZ2dsZSN1cGRhdGVGdW5jdGlvbn0pXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3VwZGF0ZUZ1bmN0aW9uPWZ1bmN0aW9uKCl7fV1cbiAgKiBAbWVtYmVyb2YgZGF0YXRvZ2dsZSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdXBkYXRlRnVuY3Rpb24gPSBmdW5jdGlvbigpe30sXG4gIC8qKlxuICAqIE5hbWVzcGFjZSBmb3IgYWxsIGl0ZW1zIG1hZGUgYnkgdGhpcyBpbnN0YW5jZSBvZiBkYXRhdG9nZ2xlXG4gICogQHBhcmFtIHtzdHJpbmd9IFtuYW1lc3BhY2U9XCJkM3NtLWRhdGFiYXJcIl1cbiAgKiBAbWVtYmVyb2YgZGF0YXRvZ2dsZSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbmFtZXNwYWNlPSdkM3NtLWRhdGFiYXInLFxuICAvKipcbiAgKiBDdXJyZW50bHkgdG9nZ2xlZCBrZXlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW2N1cnJlbnRLZXk9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBkYXRhdG9nZ2xlI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjdXJyZW50S2V5LFxuXG4gIHhBeGlzU2VsZWN0USA9IGZhbHNlLFxuICB4QXhpc09wdGlvbnMsXG4gIHlBeGlzU2VsZWN0USA9IGZhbHNlLFxuICB5QXhpc09wdGlvbnMsXG4gIGRhdGFcbiAgdG9nZ2xlLnhBeGlzU2VsZWN0USA9IGZ1bmN0aW9uKF8pe3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhBeGlzU2VsZWN0USA9IF8sIHRvZ2dsZSkgOiB4QXhpc1NlbGVjdFF9XG4gIHRvZ2dsZS55QXhpc1NlbGVjdFEgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5QXhpc1NlbGVjdFEgPSBfLCB0b2dnbGUpIDogeUF4aXNTZWxlY3RRfVxuICB0b2dnbGUueEF4aXNPcHRpb25zID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeEF4aXNPcHRpb25zID0gXywgdG9nZ2xlKSA6IHhBeGlzT3B0aW9uc31cbiAgdG9nZ2xlLnlBeGlzT3B0aW9ucyA9IGZ1bmN0aW9uKF8pe3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlBeGlzT3B0aW9ucyA9IF8sIHRvZ2dsZSkgOiB5QXhpc09wdGlvbnN9XG4gIHRvZ2dsZS5kYXRhID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGF0YSA9IF8sIHRvZ2dsZSkgOiBkYXRhfVxuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB1cGRhdGVGdW5jdGlvblxuICAgKiAoc2VlIHtAbGluayBkYXRhdG9nZ2xlI3VwZGF0ZUZ1bmN0aW9ufSlcbiAgICogQGZ1bmN0aW9uIGRhdGF0b2dnbGUudXBkYXRlRnVuY3Rpb25cbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2RhdGF0b2dnbGUgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGRhdGF0b2dnbGVcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdXBkYXRlRnVuY3Rpb24gPSBmdW5jdGlvbigpe31cbiAgICovXG4gIHRvZ2dsZS51cGRhdGVGdW5jdGlvbiA9IGZ1bmN0aW9uKF8pe3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHVwZGF0ZUZ1bmN0aW9uID0gXywgdG9nZ2xlKSA6IHVwZGF0ZUZ1bmN0aW9ufVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG5hbWVzcGFjZVxuICAgKiAoc2VlIHtAbGluayBkYXRhdG9nZ2xlI25hbWVzcGFjZX0pXG4gICAqIEBmdW5jdGlvbiBkYXRhdG9nZ2xlLm5hbWVzcGFjZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2RhdGF0b2dnbGUgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBkYXRhdG9nZ2xlXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG5hbWVzcGFjZSA9ICdkM3NtLWRhdGFiYXInXG4gICAqL1xuICB0b2dnbGUubmFtZXNwYWNlID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgdG9nZ2xlKSA6IG5hbWVzcGFjZX1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBjdXJyZW50S2V5XG4gICAqIChzZWUge0BsaW5rIGRhdGF0b2dnbGUjY3VycmVudEtleX0pXG4gICAqIEBmdW5jdGlvbiBkYXRhdG9nZ2xlLmN1cnJlbnRLZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtkYXRhdG9nZ2xlIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgZGF0YXRvZ2dsZVxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBjdXJyZW50S2V5ID0gdW5kZWZpbmVkXG4gICAqL1xuICB0b2dnbGUuY3VycmVudEtleXMgPVxuICBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHZhbHMgPSB7fVxuICAgIGQzLmtleXMoZmlsdGVyU2VsZWN0cykubWFwKGZ1bmN0aW9uKGssIGkpe1xuICAgICAgdmFsc1trXT0gZmlsdGVyU2VsZWN0c1trXS5jdXJyZW50T3B0aW9uKClcbiAgICB9KVxuICAgIHJldHVybiB2YWxzXG4gIH1cblxuICB2YXIgZmlsdGVyU2VsZWN0cyA9IHt9XG4gIGZ1bmN0aW9uIHRvZ2dsZSgpIHtcbiAgICAvLyBzZWxlY3Rpb24gb3B0aW9uc1xuXG4gICAgLy8gc2VsZWN0aW9uLmNsYXNzZWQoJ2QtZmxleCBmbGV4LXJvdycsIHRydWUpXG4gICAgLy8gdmFyIGZpbHRlckJ1dHRvbiA9IHNhZmVTZWxlY3Qoc2VsZWN0aW9uLCAnYScsICdzbGlkZXItYnV0dG9ucycpXG4gICAgLy8gdmFyIGZpbHRlckkgPSBzYWZlU2VsZWN0KGZpbHRlckJ1dHRvbiwgJ2knLCAnZmEgZmEtc2xpZGVycycpXG5cbiAgICAvKkJVRzogdW5leHBlY3RlZCBiZWhhdmlvci5cbiAgICAgIC0gd2hlbiB1c2luZyBib290c3RyYXAtZXF1ZSB3YXkgZm9yIGNvbGxhcHNlLCBjbGlja2luZyBidXR0b24gc3VibWl0cyB0b1xuICAgICAgdGhlIHNhbWUgcGFnZSBpbiBhcHBsaWNhdGlvbnMgKGJ1dCBub3QgaW4gZGVtbyksIHNvIHVzaW5nIGFuY2hvciAoPGE+KVxuICAgICAgLSB3aGVuIHVzaW5nIGFuY2hvciwgY2F1c2UgcGFnZSB0byBqdW1wIHRvIHRoYXQgbG9jYXRpb25cbiAgICAgIC0gd2hlbiB1c2luZyBzaG93LCB0aGUgZmlyc3Qgb3BlbiB3b3JrcywgYnV0IHRoZSBjbG9zZSBkb2VzIG5vdC5cbiAgICAqL1xuICAgIC8vIGZpbHRlckJ1dHRvbi5hdHRyKCdjbGFzcycsICdidG4gYnRuLXNlY29uZGFyeScpXG4gICAgLy8gLmF0dHIoJ2RhdGEtdG9nZ2xlJywgJ2NvbGxhcHNlJylcbiAgICAvLyAuYXR0cignaHJlZicsICcjJytoeXBlbmF0ZShuYW1lc3BhY2UsICdkYXRhLXRvZ2dsZScpKVxuICAgIC8vIC5hdHRyKCd0YXJnZXQnLCAnX2JsYW5rJylcbiAgICAvLyAuaHRtbChmaWx0ZXJCdXR0b24uaHRtbCgpKycgRmlsdGVycycpXG4gICAgLy8gLm9uKCdjbGljaycsIGZ1bmN0aW9uKGQsIGkpe1xuICAgIC8vICAgZDMuZXZlbnQucHJldmVudERlZmF1bHQoKVxuICAgIC8vICAgZDMuZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcbiAgICAvLyAgIHZhciBkdCA9IGQzLnNlbGVjdChcIiNcIitoeXBlbmF0ZShuYW1lc3BhY2UsICdkYXRhLXRvZ2dsZScpKVxuICAgIC8vICAgZHQuY2xhc3NlZCgnc2hvdycsICFkdC5jbGFzc2VkKCdzaG93JykpXG4gICAgLy8gICBkdC5jbGFzc2VkKCdkLWlubGluZS1mbGV4JywgZHQuY2xhc3NlZCgnc2hvdycpKVxuICAgIC8vXG4gICAgLy8gICBmaWx0ZXJCdXR0b24uY2xhc3NlZCgnYnRuLXByaW1hcnknLCBkdC5jbGFzc2VkKCdzaG93JykpXG4gICAgLy8gICBmaWx0ZXJCdXR0b24uY2xhc3NlZCgnYnRuLXNlY29uZGFyeScsICFkdC5jbGFzc2VkKCdzaG93JykpXG4gICAgLy8gfSlcbiAgICAvLyAuY2xhc3NlZCgnZC1pbmxpbmUtZmxleCcsIHRydWUpXG4gICAgLy8gLnN0eWxlKFwibWFyZ2luLXJpZ2h0XCIsICcxMHB4JylcblxuXG5cbiAgICAvLyB2YXIgZGF0YXRvZ2dsZUNvbGxhcHNlID0gc2FmZVNlbGVjdChzZWxlY3Rpb24sICdkaXYnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ2NvbGxhcHNlJykpXG4gICAgLy8gLmF0dHIoJ2lkJywgaHlwZW5hdGUobmFtZXNwYWNlLCAnZGF0YS10b2dnbGUnKSlcbiAgICAvLyAuY2xhc3NlZCgnY29sbGFwc2UnLCB0cnVlKVxuXG4gICAgLy8gdmFyIGZsZXhSb3cgPSBzYWZlU2VsZWN0KGRhdGF0b2dnbGVDb2xsYXBzZSwgJ2RpdicsICdkLWlubGluZS1mbGV4IGZsZXgtcm93IGZsZXgtd3JhcCcpXG4gICAgdmFyIGZsZXhSb3cgPSBzYWZlU2VsZWN0KHNlbGVjdGlvbiwgJ2RpdicsICdkLWlubGluZS1mbGV4IGZsZXgtcm93IGZsZXgtd3JhcCcpXG5cbiAgICB2YXIgZGF0YW9wdHMgPSBmbGV4Um93LnNlbGVjdEFsbCgnZGl2LicraHlwZW5hdGUobmFtZXNwYWNlLCdzZWxlY3QtZmlsdGVyJykpXG4gICAgLy8gcmVtb3ZlIGV4Y2Vzc1xuICAgIGRhdGFvcHRzLmV4aXQoKS5yZW1vdmUoKVxuICAgIC8vIGJpbmQgZGF0YVxuICAgIGRhdGFvcHRzID0gZGF0YW9wdHMuZGF0YShkMy5rZXlzKGRhdGEpKVxuICAgIC8vZW50ZXJcbiAgICB2YXIgZG9FbnRlciA9IGRhdGFvcHRzLmVudGVyKCkuYXBwZW5kKCdkaXYnKVxuICAgIC5hdHRyKCdjbGFzcycsICdzZWxlY3QtZmlsdGVyJylcblxuICAgIGRhdGFvcHRzID0gZGF0YW9wdHMubWVyZ2UoZG9FbnRlcikuc3R5bGUoJ21hcmdpbi1yaWdodCcsIFwiMTBweFwiKVxuXG4gICAgZGF0YW9wdHMuZWFjaChmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhciB0ID0gZDMuc2VsZWN0KHRoaXMpXG4gICAgICB2YXIgc2YgPSBzZWxlY3RGaWx0ZXIodClcbiAgICAgIC5kYXRhKGRhdGFbZF0pXG4gICAgICAubmFtZXNwYWNlKGh5cGVuYXRlKG5hbWVzcGFjZSwgZCkpXG4gICAgICAuc2VsZWN0aW9uTmFtZShkKVxuICAgICAgc2YoKVxuICAgICAgZmlsdGVyU2VsZWN0c1tkXSA9IHNmXG4gICAgfSlcblxuXG4gICAgc2VsZWN0aW9uLnNlbGVjdEFsbCgnc2VsZWN0JylcbiAgICAub24oJ2NoYW5nZScsIGZ1bmN0aW9uKCl7dXBkYXRlRnVuY3Rpb24oKX0pXG4gICAgLy8gYmluZCB1cGRhdGUgZnVuY3Rpb25cbiAgICAvLyBkMy5zZWxlY3RBbGxcbiAgICByZXR1cm4gdG9nZ2xlXG4gIH1cblxuXG4gIGZ1bmN0aW9uIG9ubHlPbmUoKSB7XG4gICAgLy8gZDMuZXZlbnQucHJldmVudERlZmF1bHQoKVxuICAgIC8vIGQzLmV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXG4gICAgdmFyIGRhdGFvcHRzID0gc2VsZWN0aW9uLnNlbGVjdEFsbCgnZGl2LmRhdGEtb3B0aW9uJylcbiAgICBjdXJyZW50S2V5ID0gZGF0YW9wdHMuc2VsZWN0KCc6Y2hlY2tlZCcpLmRhdHVtKClcbiAgICB1cGRhdGVGdW5jdGlvbigpXG5cbiAgfVxuXG4gIGZ1bmN0aW9uIGF4aXNTZWxlY3RGaWx0ZXIoc2VsZWN0aW9uLCBheGlzPVwieC1heGlzXCIsIGF4aXNEYXRhKSB7XG4gICAgaWYgKGF4aXNEYXRhID09IHVuZGVmaW5lZCkge1xuICAgICAgYXhpc0RhdGEgPSB7XG4gICAgICAgICdsaW5lYXInOiBkMy5zY2FsZUxpbmVhcigpLFxuICAgICAgICAnbG9nJzogZDMuc2NhbGVMb2coKSxcbiAgICAgICAgJ3Bvdyc6IGQzLnNjYWxlUG93KCksXG4gICAgICAgICdzcXJ0JzogZDMuc2NhbGVTcXJ0KClcbiAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgc2YgPSBzZWxlY3RGaWx0ZXIoc2VsZWN0aW9uKVxuICAgIC5kYXRhKGF4aXNEYXRhKVxuICAgIC5uYW1lc3BhY2UoYXhpcylcbiAgICAuc2VsZWN0aW9uTmFtZShheGlzKycgc2NhbGUnKVxuICAgIC5kZWZhdWx0VmFsdWUoZDMuc2NhbGVMaW5lYXIoKSlcblxuXG4gICAgc2YoKVxuXG4gIH1cblxuICByZXR1cm4gdG9nZ2xlXG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHtzZXR1cENvbnRhaW5lciwgY2FsY3VsYXRlV2lkdGhPZk9iamVjdCwgY2FsY3VsYXRlV2lkdGhPZlNwYWNlcn0gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQge3VuaXF1ZSwgZmxhdHRlbn0gZnJvbSAnLi9hcnJheS1mdW5jdGlvbnMnO1xuaW1wb3J0IHtncm91cGluZ1NwYWNlcn0gZnJvbSAnLi9ncm91cGluZy1zcGFjZXInO1xuaW1wb3J0IHtjb2xvckZ1bmN0aW9uIGFzIENGfSBmcm9tICcuL2NvbG9yLWZ1bmN0aW9uJztcbmltcG9ydCB7dG9vbHRpcCBhcyBUVGlwfSBmcm9tICcuL3Rvb2x0aXAnO1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTQ0FUVEVSICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qKlxuICogQ3JlYXRlcyBhIHNjYXR0ZXJcbiAqXG4gKiB7QGxpbmsgaHR0cHM6Ly9zdW1uZXVyb24uZ2l0bGFiLmlvL2Qzc20vZGVtb3Mvc2NhdHRlci9pbmRleC5odG1sIERlbW99XG4gKiBAY29uc3RydWN0b3Igc2NhdHRlclxuICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IHNlbGVjdGlvblxuICogQG5hbWVzcGFjZSBzY2F0dGVyXG4gKiBAcmV0dXJucyB7ZnVuY3Rpb259IHNjYXR0ZXJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNjYXR0ZXIgKCBzZWxlY3Rpb24gKSB7XG5cbiAgdmFyXG4gIC8qKlxuICAqIERhdGEgdG8gcGxvdC4gQXNzdW1lZCB0byBiZSBhIG9iamVjdCwgd2hlcmUgZWFjaCBrZXkgY29ycmVzcG9uZHMgdG8gYSBwb2ludFxuICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjZGF0YX0pXG4gICogQHBhcmFtIHtPYmplY3R9IFtkYXRhPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZGF0YSxcbiAgLyoqXG4gICogQW1vdW50IG9mIGhvcml6b250YWwgc3BhY2UgKGluIHBpeGVscykgYXZhaWJsZSB0byByZW5kZXIgdGhlIHNjYXR0ZXIgaW5cbiAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3NwYWNlWH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVg9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzcGFjZVgsXG4gIC8qKlxuICAqIEFtb3VudCBvZiB2ZXJ0aWNhbCBzcGFjZSAoaW4gcGl4ZWxzKSBhdmFpYmxlIHRvIHJlbmRlciB0aGUgc2NhdHRlciBpblxuICAqIChzZWUge0BsaW5rIHNjYXR0ZXIuc3BhY2VZfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlWT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlWSxcblxuICAvKipcbiAgKiBUaGUgc2NhbGUgZm9yIHdoaWNoIHNjYXR0ZXIgeCB2YWx1ZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICogQHBhcmFtIHtkMy5zY2FsZX0gW3NjYWxlWD1kMy5zY2FsZUxpbmVhcl1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc2NhbGVYID0gZDMuc2NhbGVMaW5lYXIoKSxcbiAgLyoqXG4gICogVGhlIHBhZGRpbmcgZm9yIHRoZSBkb21haW4gb2YgdGhlIHNjYWxlWCAoc2VlIHtAbGluayBzY2F0dGVyI3NjYWxlWH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtkb21haW5QYWRkaW5nWD0wLjVdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGRvbWFpblBhZGRpbmdYID0gMC41LFxuICAvKipcbiAgKiBUaGUgZnVuY3Rpb24gZm9yIGdldHRpbmcgdGhlIHggdmFsdWUgb2YgdGhlIGN1cnJlbnQgcG9pbnRcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdmFsdWVFeHRyYWN0b3JYPWZ1bmN0aW9uKGQsIGkpe3JldHVybiBkYXRhW2RdWyd4J119XVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2YWx1ZUV4dHJhY3RvclggPSBmdW5jdGlvbihkLCBpKSB7cmV0dXJuIGRhdGFbZF1bJ3gnXX0sXG5cbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCBzY2F0dGVyIHkgdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZVk9ZDMuc2NhbGVMaW5lYXJdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNjYWxlWSA9IGQzLnNjYWxlTGluZWFyKCksXG4gIC8qKlxuICAqIFRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSBzY2FsZVkgKHNlZSB7QGxpbmsgc2NhdHRlciNzY2FsZVl9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbZG9tYWluUGFkZGluZ1k9MC41XVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkb21haW5QYWRkaW5nWSA9IDAuNSxcbiAgLyoqXG4gICogVGhlIGZ1bmN0aW9uIGZvciBnZXR0aW5nIHRoZSB5IHZhbHVlIG9mIHRoZSBjdXJyZW50IHBvaW50XG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3ZhbHVlRXh0cmFjdG9yWT1mdW5jdGlvbihkLCBpKXtyZXR1cm4gZGF0YVtkXVsneSddfV1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmFsdWVFeHRyYWN0b3JZID0gZnVuY3Rpb24oZCwgaSkge3JldHVybiBkYXRhW2RdWyd5J119LFxuXG5cbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCBzY2F0dGVyIHIgdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZVI9ZDMuc2NhbGVMaW5lYXJdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNjYWxlUiA9IGQzLnNjYWxlTGluZWFyKCksXG4gIC8qKlxuICAqIFRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSBzY2FsZVIgKHNlZSB7QGxpbmsgc2NhdHRlciNzY2FsZVJ9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbZG9tYWluUGFkZGluZ1I9MC41XVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkb21haW5QYWRkaW5nUiA9IDAuNSxcbiAgLyoqXG4gICogVGhlIGZ1bmN0aW9uIGZvciBnZXR0aW5nIHRoZSByIHZhbHVlIG9mIHRoZSBjdXJyZW50IHBvaW50XG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3ZhbHVlRXh0cmFjdG9yUj1mdW5jdGlvbihkLCBpKXtyZXR1cm4gMn1dXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHZhbHVlRXh0cmFjdG9yUiA9IGZ1bmN0aW9uKGQsIGkpIHtyZXR1cm4gMn0sXG4gIC8qKlxuICAqIFRoZSBtaW4gcmFkaXVzIGEgcG9pbnQgY2FuIGhhdmVcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbbWluUmFkaXVzPTJdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1pblJhZGl1cyA9IDIsXG4gIC8qKlxuICAqIFRoZSBtaW4gcmFkaXVzIGEgcG9pbnQgY2FuIGhhdmVcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbbWF4UmFkaXVzPTEwXVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBtYXhSYWRpdXMgPSAxMCxcblxuICAvKipcbiAgKiBUaGUgc3Ryb2tlIHdpZHRoIG9mIHRoZSBwb2ludHNcbiAgKiBAcGFyYW0ge251bWJlcn0gW3BvaW50U3Ryb2tlV2lkdGg9Ml1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgcG9pbnRTdHJva2VXaWR0aCA9IDIsXG4gIC8qKlxuICAqIEluc3RhbmNlIG9mIENvbG9yRnVuY3Rpb25cbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKV1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgY29sb3JGdW5jdGlvbiA9IENGKCksXG4gIC8qKlxuICAqIENvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kXG4gICogQHBhcmFtIHtzdHJpbmd9IFtiYWNrZ3JvdW5kRmlsbD1cInRyYW5zcGFyZW50XCJdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJhY2tncm91bmRGaWxsID0gJ3RyYW5zcGFyZW50JyxcbiAgLyoqXG4gICogTmFtZXNwYWNlIGZvciBhbGwgaXRlbXMgbWFkZSBieSB0aGlzIGluc3RhbmNlIG9mIHNjYXR0ZXJcbiAgKiBAcGFyYW0ge3N0cmluZ30gW25hbWVzcGFjZT1cImQzc20tc2NhdHRlclwiXVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBuYW1lc3BhY2UgPSAnZDNzbS1zY2F0dGVyJyxcbiAgLyoqXG4gICogQ2xhc3MgbmFtZSBmb3Igc2NhdHRlciBjb250YWluZXIgKDxjaXJjbGU+IGVsZW1lbnQpXG4gICogQHBhcmFtIHtzdHJpbmd9IFtvYmplY3RDbGFzcz1cInNjYXR0ZXItcG9pbnRcIl1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0Q2xhc3MgPSAnc2NhdHRlci1wb2ludCcsXG4gIC8qKlxuICAqIER1cmF0aW9uIG9mIGFsbCB0cmFuc2l0aW9ucyBvZiB0aGlzIGVsZW1lbnRcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RyYW5zaXRpb25EdXJhdGlvbj0xMDAwXVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwLFxuICAvKipcbiAgKiBFYXNpbmcgZnVuY3Rpb24gZm9yIHRyYW5zaXRpb25zXG4gICogQHBhcmFtIHtkMy5lYXNlfSBbZWFzZUZ1bmM9ZDMuZWFzZUV4cF1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwLFxuXG4gIC8vIHVzZWZ1bCB2YWx1ZXMgdG8gZXh0cmFjdCB0byBwcmV2ZW50IHJlLWNhbGN1bGF0aW9uXG4gIC8qKlxuICAqIFRoZSBrZXlzIG9mIHRoZSBwb2ludHNcbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbcG9pbnRLZXlzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgcG9pbnRLZXlzLFxuICAvKipcbiAgKiBUaGUgeCB2YWx1ZXMgb2YgdGhlIHBvaW50c1xuICAqIEBwYXJhbSB7bnVtYmVyW119IFt2YWx1ZXNYPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmFsdWVzWCxcbiAgLyoqXG4gICogVGhlIHkgdmFsdWVzIG9mIHRoZSBwb2ludHNcbiAgKiBAcGFyYW0ge251bWJlcltdfSBbdmFsdWVzWT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHZhbHVlc1ksXG4gIC8qKlxuICAqIFRoZSByIHZhbHVlcyBvZiB0aGUgcG9pbnRzXG4gICogQHBhcmFtIHtudW1iZXJbXX0gW3ZhbHVlc1I9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2YWx1ZXNSLFxuXG4gIC8qKlxuICAqIEluc3RhbmNlIG9mIFRvb2x0aXBcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdG9vbHRpcD10b29sdGlwKCldXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRvb2x0aXAgPSBUVGlwKClcblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBzZWxlY3Rpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IGQzLnNlbGVjdGlvbn1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2VsZWN0aW9uID0gc2VsZWN0aW9uXG4gICAqL1xuICBzY2F0dGVyLnNlbGVjdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2VsZWN0aW9uID1fLCBzY2F0dGVyKSA6IHNlbGVjdGlvbn1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZGF0YVxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI2RhdGF9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBvYmplY3R9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgc2NhdHRlci5kYXRhID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkYXRhID1fLCBzY2F0dGVyKSA6IGRhdGF9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3NwYWNlWH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVggPSB1bmRlZmluZWRcbiAgICovXG4gIHNjYXR0ZXIuc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVggPV8sIHNjYXR0ZXIpIDogc3BhY2VYfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBhbW91bnQgb2YgdmVydGljYWwgc3BhY2UgaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjc3BhY2VZfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlWSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgc2NhdHRlci5zcGFjZVkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWSA9Xywgc2NhdHRlcikgOiBzcGFjZVl9XG5cblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgeCBzY2FsZSBmb3Igd2hpY2ggdGhlIHNjYXR0ZXIgeCB2YWx1ZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjc2NhbGVYfSlcbiAgICogQHBhcmFtIHtkMy5zY2FsZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBkMy5zY2FsZX1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2NhbGVYID0gZDMuc2NhbGVMaW5lYXIoKVxuICAgKi9cbiAgc2NhdHRlci5zY2FsZVggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNjYWxlWCA9Xywgc2NhdHRlcikgOiBzY2FsZVh9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgeCBzY2FsZVxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI2RvbWFpblBhZGRpbmdYfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBkb21haW5QYWRkaW5nWCA9IDAuNVxuICAgKi9cbiAgc2NhdHRlci5kb21haW5QYWRkaW5nWCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZG9tYWluUGFkZGluZ1ggPV8sIHNjYXR0ZXIpIDogZG9tYWluUGFkZGluZ1h9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdmFsdWVFeHRyYWN0b3JYXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjdmFsdWVFeHRyYWN0b3JYfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdmFsdWVFeHRyYWN0b3JYID0gZnVuY3Rpb24oa2V5LCBpbmRleCkgeyByZXR1cm4gZGF0YVtrZXldWyd4J10gfVxuICAgKi9cbiAgc2NhdHRlci52YWx1ZUV4dHJhY3RvclggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZhbHVlRXh0cmFjdG9yWCA9Xywgc2NhdHRlcikgOiB2YWx1ZUV4dHJhY3Rvclh9XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHkgc2NhbGUgZm9yIHdoaWNoIHRoZSBzY2F0dGVyIHkgdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3NjYWxlWX0pXG4gICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgZDMuc2NhbGV9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNjYWxlWSA9IGQzLnNjYWxlTGluZWFyKClcbiAgICovXG4gIHNjYXR0ZXIuc2NhbGVZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzY2FsZVkgPV8sIHNjYXR0ZXIpIDogc2NhbGVZfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHBhZGRpbmcgZm9yIHRoZSBkb21haW4gb2YgdGhlIHkgc2NhbGVcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNkb21haW5QYWRkaW5nWX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZG9tYWluUGFkZGluZ1kgPSAwLjVcbiAgICovXG4gIHNjYXR0ZXIuZG9tYWluUGFkZGluZ1kgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRvbWFpblBhZGRpbmdZID1fLCBzY2F0dGVyKSA6IGRvbWFpblBhZGRpbmdZfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHZhbHVlRXh0cmFjdG9yWVxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3ZhbHVlRXh0cmFjdG9yWX0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZhbHVlRXh0cmFjdG9yWSA9IGZ1bmN0aW9uKGtleSwgaW5kZXgpIHsgcmV0dXJuIGRhdGFba2V5XVsneSddIH1cbiAgICovXG4gIHNjYXR0ZXIudmFsdWVFeHRyYWN0b3JZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZUV4dHJhY3RvclkgPV8sIHNjYXR0ZXIpIDogdmFsdWVFeHRyYWN0b3JZfVxuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSByIHNjYWxlIGZvciB3aGljaCB0aGUgc2NhdHRlciByIHZhbHVlcyBzaG91bGQgYmUgdHJhbnNmb3JtZWQgYnlcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNzY2FsZVJ9KVxuICAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IGQzLnNjYWxlfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzY2FsZVIgPSBkMy5zY2FsZUxpbmVhcigpXG4gICAqL1xuICBzY2F0dGVyLnNjYWxlUiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGVSID1fLCBzY2F0dGVyKSA6IHNjYWxlUn1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSByIHNjYWxlXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjZG9tYWluUGFkZGluZ1J9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGRvbWFpblBhZGRpbmdSID0gMC41XG4gICAqL1xuICBzY2F0dGVyLmRvbWFpblBhZGRpbmdSID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkb21haW5QYWRkaW5nUiA9Xywgc2NhdHRlcikgOiBkb21haW5QYWRkaW5nUn1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2YWx1ZUV4dHJhY3RvclJcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciN2YWx1ZUV4dHJhY3RvclJ9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2YWx1ZUV4dHJhY3RvclIgPSBmdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV1bJ3InXSB9XG4gICAqL1xuICBzY2F0dGVyLnZhbHVlRXh0cmFjdG9yUiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodmFsdWVFeHRyYWN0b3JSID1fLCBzY2F0dGVyKSA6IHZhbHVlRXh0cmFjdG9yUn1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtaW5SYWRpdXNcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNtaW5SYWRpdXN9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1pblJhZGl1cyA9IDJcbiAgICovXG4gIHNjYXR0ZXIubWluUmFkaXVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtaW5SYWRpdXMgPV8sIHNjYXR0ZXIpIDogbWluUmFkaXVzfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1heFJhZGl1c1xuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI21heFJhZGl1c30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWF4UmFkaXVzID0gMTBcbiAgICovXG4gIHNjYXR0ZXIubWF4UmFkaXVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtYXhSYWRpdXMgPV8sIHNjYXR0ZXIpIDogbWF4UmFkaXVzfVxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcG9pbnRTdHJva2VXaWR0aFxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3BvaW50U3Ryb2tlV2lkdGh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHBvaW50U3Ryb2tlV2lkdGggPSAyXG4gICAqL1xuICBzY2F0dGVyLnBvaW50U3Ryb2tlV2lkdGggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHBvaW50U3Ryb2tlV2lkdGggPV8sIHNjYXR0ZXIpIDogcG9pbnRTdHJva2VXaWR0aH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBjb2xvckZ1bmN0aW9uXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjY29sb3JGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKVxuICAgKi9cbiAgc2NhdHRlci5jb2xvckZ1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjb2xvckZ1bmN0aW9uID1fLCBzY2F0dGVyKSA6IGNvbG9yRnVuY3Rpb259XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFja2dyb3VuZEZpbGxcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNiYWNrZ3JvdW5kRmlsbH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnXG4gICAqL1xuICBzY2F0dGVyLmJhY2tncm91bmRGaWxsID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChiYWNrZ3JvdW5kRmlsbCA9Xywgc2NhdHRlcikgOiBiYWNrZ3JvdW5kRmlsbH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBuYW1lc3BhY2VcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNuYW1lc3BhY2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG5hbWVzcGFjZSA9ICdkM3NtLXNjYXR0ZXInXG4gICAqL1xuICBzY2F0dGVyLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID1fLCBzY2F0dGVyKSA6IG5hbWVzcGFjZX1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RDbGFzc1xuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI29iamVjdENsYXNzfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RDbGFzcyA9ICd0aWNrLWdyb3VwJ1xuICAgKi9cbiAgc2NhdHRlci5vYmplY3RDbGFzcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3MgPV8sIHNjYXR0ZXIpIDogb2JqZWN0Q2xhc3N9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdHJhbnNpdGlvbkR1cmF0aW9uXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjdHJhbnNpdGlvbkR1cmF0aW9ufSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwXG4gICAqL1xuICBzY2F0dGVyLnRyYW5zaXRpb25EdXJhdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodHJhbnNpdGlvbkR1cmF0aW9uID1fLCBzY2F0dGVyKSA6IHRyYW5zaXRpb25EdXJhdGlvbn1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBlYXNlRnVuY1xuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI2Vhc2VGdW5jfSlcbiAgICogQHBhcmFtIHtkMy5lYXNlfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IGQzLmVhc2V9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGVhc2VGdW5jID0gZDMuZWFzZUV4cFxuICAgKi9cbiAgc2NhdHRlci5lYXNlRnVuYyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZWFzZUZ1bmMgPV8sIHNjYXR0ZXIpIDogZWFzZUZ1bmN9XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBwb2ludEtleXNcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNwb2ludEtleXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBwb2ludEtleXMgPSB1bmRlZmluZWRcbiAgICovXG4gIHNjYXR0ZXIucG9pbnRLZXlzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChwb2ludEtleXMgPV8sIHNjYXR0ZXIpIDogcG9pbnRLZXlzfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHZhbHVlc1hcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciN2YWx1ZXNYfSlcbiAgICogQHBhcmFtIHtudW1iZXJbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBudW1iZXJbXX1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdmFsdWVzWCA9IHVuZGVmaW5lZFxuICAgKi9cbiAgc2NhdHRlci52YWx1ZXNYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZXNYID1fLCBzY2F0dGVyKSA6IHZhbHVlc1h9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdmFsdWVzWVxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3ZhbHVlc1l9KVxuICAgKiBAcGFyYW0ge251bWJlcltdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IG51bWJlcltdfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2YWx1ZXNZID0gdW5kZWZpbmVkXG4gICAqL1xuICBzY2F0dGVyLnZhbHVlc1kgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZhbHVlc1kgPV8sIHNjYXR0ZXIpIDogdmFsdWVzWX1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2YWx1ZXNSXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjdmFsdWVzUn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgbnVtYmVyW119XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZhbHVlc1IgPSB1bmRlZmluZWRcbiAgICovXG4gIHNjYXR0ZXIudmFsdWVzUiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodmFsdWVzUiA9Xywgc2NhdHRlcikgOiB2YWx1ZXNSfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRvb2x0aXBcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciN0b29sdGlwfSlcbiAgICogQHBhcmFtIHt0b29sdGlwfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IHRvb2x0aXB9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRvb2x0aXAgPSB0b29sdGlwKClcbiAgICovXG5cbiAgc2NhdHRlci50b29sdGlwID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0b29sdGlwID1fLCBzY2F0dGVyKSA6IHRvb2x0aXB9XG5cblxuICBmdW5jdGlvbiBzY2F0dGVyKCkge1xuICAgIC8vIGJhY2tncm91bmQgY2xpcGluZyByZWN0YW5nbGVcbiAgICB2YXIgYmdjcFJlY3QgPSB7eDowLCB5OjAsIHdpZHRoOiBzcGFjZVgsIGhlaWdodDpzcGFjZVl9XG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cblxuICAgIHBvaW50S2V5cyA9IGQzLmtleXMoZGF0YSlcbiAgICB2YWx1ZXNYID0gcG9pbnRLZXlzLm1hcCh2YWx1ZUV4dHJhY3RvclgpXG4gICAgdmFsdWVzWSA9IHBvaW50S2V5cy5tYXAodmFsdWVFeHRyYWN0b3JZKVxuICAgIHZhbHVlc1IgPSBwb2ludEtleXMubWFwKHZhbHVlRXh0cmFjdG9yUilcblxuICAgIHZhciBudW1iZXJPZk9iamVjdHMgPSBwb2ludEtleXMubGVuZ3RoXG4gICAgdmFyIGV4dGVudFggPSBbTWF0aC5taW4oLi4udmFsdWVzWCkgLSBkb21haW5QYWRkaW5nWCwgTWF0aC5tYXgoLi4udmFsdWVzWCkgKyBkb21haW5QYWRkaW5nWF1cbiAgICB2YXIgZXh0ZW50WSA9IFtNYXRoLm1pbiguLi52YWx1ZXNZKSAtIGRvbWFpblBhZGRpbmdZLCBNYXRoLm1heCguLi52YWx1ZXNZKSArIGRvbWFpblBhZGRpbmdZXVxuICAgIHZhciBleHRlbnRSID0gW01hdGgubWluKC4uLnZhbHVlc1IpIC0gZG9tYWluUGFkZGluZ1IsIE1hdGgubWF4KC4uLnZhbHVlc1IpICsgZG9tYWluUGFkZGluZ1JdXG5cbiAgICBzY2FsZVguZG9tYWluKGV4dGVudFgpLnJhbmdlKFswLCBzcGFjZVhdKVxuICAgIHNjYWxlWS5kb21haW4oZXh0ZW50WSkucmFuZ2UoW3NwYWNlWSwgMF0pXG4gICAgc2NhbGVSLmRvbWFpbihleHRlbnRSKS5yYW5nZShbbWluUmFkaXVzLCBtYXhSYWRpdXNdKVxuXG4gICAgdmFyIHBvaW50cyA9IGNvbnRhaW5lci5zZWxlY3RBbGwoJy4nK29iamVjdENsYXNzKVxuICAgIHBvaW50cyA9IHBvaW50cy5kYXRhKHBvaW50S2V5cylcbiAgICB2YXIgcEVudGVyID0gcG9pbnRzLmVudGVyKCkuYXBwZW5kKCdjaXJjbGUnKVxuICAgIC5hdHRyKCdjbGFzcycsIG9iamVjdENsYXNzKVxuICAgIC5hdHRyKCdjeCcsIDApLmF0dHIoJ2N5Jywgc3BhY2VZKS5hdHRyKCdyJywgMClcblxuICAgIHZhciBwRXhpdCA9IHBvaW50cy5leGl0KClcblxuICAgIHBvaW50cyA9IHBvaW50cy5tZXJnZShwRW50ZXIpXG5cbiAgICBwb2ludHMuZWFjaChmdW5jdGlvbihrZXksIGkpe1xuICAgICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcyksXG4gICAgICBjdXJyZW50RGF0YSA9IGRhdGFba2V5XSxcbiAgICAgIHggPSB2YWx1ZXNYW2ldLFxuICAgICAgeSA9IHZhbHVlc1lbaV0sXG4gICAgICByID0gdmFsdWVzUltpXSxcbiAgICAgIGZpbGxDb2xvciA9IGNvbG9yRnVuY3Rpb24oa2V5LCBjdXJyZW50RGF0YSwgaSwgJ2ZpbGwnKSxcbiAgICAgIHN0cm9rZUNvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIGN1cnJlbnREYXRhLCBpLCAnc3Ryb2tlJylcblxuICAgICAgdC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ2N4Jywgc2NhbGVYKHgpKVxuICAgICAgLmF0dHIoJ2N5Jywgc2NhbGVZKHkpKVxuICAgICAgLmF0dHIoJ3InLCBzY2FsZVIocikpXG4gICAgICAuYXR0cignZmlsbCcsIGZpbGxDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2UnLCBzdHJva2VDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2Utd2lkdGgnLCBwb2ludFN0cm9rZVdpZHRoKVxuXG5cblxuICAgICAgdC5vbignbW91c2VvdmVyJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIHBvaW50cy5zdHlsZSgnb3BhY2l0eScsIDAuMilcbiAgICAgICAgdC5zdHlsZSgnb3BhY2l0eScsIDEpXG4gICAgICAgIHQudHJhbnNpdGlvbigpLmR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbi8yKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgcG9pbnRTdHJva2VXaWR0aCoyKVxuICAgICAgICAuYXR0cigncicsIHNjYWxlUihyKSAqIDEuNSlcblxuICAgICAgfSlcbiAgICAgIHQubm9kZSgpLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlb3V0JywgZnVuY3Rpb24oKXtcbiAgICAgICAgY29udGFpbmVyLnNlbGVjdEFsbCgnLicrb2JqZWN0Q2xhc3MpLnN0eWxlKCdvcGFjaXR5JywgMSlcbiAgICAgICAgdC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uLzIpLmVhc2UoZWFzZUZ1bmMpXG4gICAgICAgIC5hdHRyKCdzdHJva2Utd2lkdGgnLCBwb2ludFN0cm9rZVdpZHRoKVxuICAgICAgICAuYXR0cigncicsIHNjYWxlUihyKSlcblxuICAgICAgfSlcblxuICAgIH0pXG5cblxuXG4gICAgcEV4aXQudHJhbnNpdGlvbigpLmR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbikuZWFzZShlYXNlRnVuYylcbiAgICAuYXR0cignY3gnLCAwKS5hdHRyKCdjeScsIHNwYWNlWSkuYXR0cigncicsIDApXG4gICAgLnJlbW92ZSgpXG5cbiAgICB0b29sdGlwLnNlbGVjdGlvbihwb2ludHMpXG4gICAgLmRhdGEoZGF0YSlcblxuICAgIHRvb2x0aXAoKVxuICB9XG5cblxuICByZXR1cm4gc2NhdHRlclxufVxuIiwiaW1wb3J0IHtoeXBlbmF0ZSwgc2FmZVNlbGVjdCwgZ2V0VHJhbnNsYXRpb259IGZyb20gJy4vaGVscGVycyc7XG5pbXBvcnQge2xvZywgd2FybiwgZXJyb3IsIGluZm99IGZyb20gJy4vdXRpbHMnO1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUExPVFpPT00gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqXG4gKiBDcmVhdGVzIGFuIHBsb3Rab29tIGluc3RhbmNlLCB3aGljaCBjYW4gaGFuZGxlIGRyYWcgYW5kIHNjcm9sbCBldmVudHNcbiAqIEBjb25zdHJ1Y3RvciBwbG90Wm9vbVxuICogQHBhcmFtIHtmdW5jdGlvbn0gY2hhcnQgYSBmdW5jdGlvbiBpbnN0YW5jZSBvZiBvbmUgb2YgdGhlIGQzc20gcGxvdHMgKGUuZy4gYmFyLCBib3h3aGlza2VyLCBidWJibGVIZWF0bWFwLCB2aW9saW4sIGV0YylcbiAqIEBwYXJhbSB7YXhpc30gIHhBeGlzIHRoZSBheGlzIGluc3RhbmNlIHJlc3BvbnNpYmxlIGZvciB0aGUgeCBheGlzXG4gKiBAcGFyYW0ge2F4aXN9IHlBeGlzIHRoZSBheGlzIGluc3RhbmNlIHJlc3BvbnNpYmxlIGZvciB0aGUgeSBheGlzXG4gKiBAbmFtZXNwYWNlIHBsb3Rab29tXG4gKiBAcmV0dXJucyB7ZnVuY3Rpb259IHpvb21cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBsb3Rab29tKCBjaGFydCwgeEF4aXMsIHlBeGlzICkge1xuICB2YXJcbiAgLyoqXG4gICogVGhlIGV2ZW50IG9uIHdoaWNoIHRvIGZpcmVcbiAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSNldmVudFR5cGV9KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBldmVudFR5cGUgd2hpY2ggZXZlbnQgaXQgc2hvdWxkIGhhbmRsZS4gQ3VycmVudGx5IHN1cHBvcnRzIHNjcm9sbCBhbmQgd2hlZWxcbiAgKiBAbWVtYmVyb2YgcGxvdFpvb20jXG4gICogQHByb3BlcnR5XG4gICovXG4gIGV2ZW50VHlwZSxcbiAgLyoqXG4gICogQSBzY2FsaW5nIGZhY3RvciBmb3IgdGhlIHdoZWVsIFwic3BlZWRcIlxuICAqIChzZWUge0BsaW5rIHBsb3Rab29tI3doZWVsU3BlZWR9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbd2hlZWxTcGVlZD0yMF0gc2NhbGVzIHRoZSB3aGVlbCB0cmFuc2xhdGlvbiBieSB3aGVlbFNwZWVkXG4gICogQG1lbWJlcm9mIHBsb3Rab29tI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB3aGVlbFNwZWVkID0gMjAsXG4gIC8qKlxuICAqIFRoZSBvcmllbnRhdGlvbiBpbiB3aGljaCB0byBhbGxvdyBzY3JvbGxpbmc6ICdob3Jpem9udGFsJywgJ3ZlcnRpY2FsJywgb3IgJzJEJ1xuICAqIChzZWUge0BsaW5rIHBsb3Rab29tI29yaWVudH0pXG4gICogQHBhcmFtIHtzdHJpbmd9IFtvcmllbnQ9Y2hhcnQub3JpZW50KCkgfHwgJ2hvcml6b250YWwnXVxuICAqIEBtZW1iZXJvZiBwbG90Wm9vbSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb3JpZW50ID0gKGNoYXJ0Lm9yaWVudCA9PSB1bmRlZmluZWQpID8gJ2hvcml6b250YWwnIDogY2hhcnQub3JpZW50KCksXG4gIC8qKlxuICAqIFRoZSBtYXggZGlzdGFuY2UgYWxsb3dlZCB0byBzY3JvbGwgaW4gdGhlIHggZGlyZWN0aW9uXG4gICogKHNlZSB7QGxpbmsgcGxvdFpvb20jeExvY2t9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbeExvY2s9Y2hhcnQuc3BhY2VYKCldIGlkZWFsbHkgY2hhcnQub3ZlcmZsb3dRKCkgPT0gdHJ1ZSBhbmQgdGhpcyB2YWx1ZSBpcyB0aGVcbiAgKiBib3VuZGluZyByZWN0IGFjcm9zcyBhbGwgZWxlbWVudHMgaW4gdGhlIGNoYXJ0ICBtaW51cyB0aGUgc3BhY2UgaW4gd2hpY2ggdG8gc2hvdy5cbiAgKiBAbWVtYmVyb2YgcGxvdFpvb20jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhMb2NrPWNoYXJ0LnNwYWNlWCgpLFxuICAvKipcbiAgKiBUaGUgbWF4IGRpc3RhbmNlIGFsbG93ZWQgdG8gc2Nyb2xsIGluIHRoZSB5IGRpcmVjdGlvblxuICAqIChzZWUge0BsaW5rIHBsb3Rab29tI3lMb2NrfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3lMb2NrPWNoYXJ0LnNwYWNlWSgpXSBpZGVhbGx5IGNoYXJ0Lm92ZXJmbG93USgpID09IHRydWUgYW5kIHRoaXMgdmFsdWUgaXMgdGhlXG4gICogYm91bmRpbmcgcmVjdCBhY3Jvc3MgYWxsIGVsZW1lbnRzIGluIHRoZSBjaGFydCAgbWludXMgdGhlIHNwYWNlIGluIHdoaWNoIHRvIHNob3cuXG4gICogQG1lbWJlcm9mIHBsb3Rab29tI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5TG9jaz1jaGFydC5zcGFjZVkoKSxcblxuICBjaGFydFNlbCA9IGNoYXJ0LnNlbGVjdGlvbigpLFxuICB4QXhpc1NlbCA9IHhBeGlzLnNlbGVjdGlvbigpLFxuICB5QXhpc1NlbCA9IHlBeGlzLnNlbGVjdGlvbigpLFxuICBzdmcgPSBkMy5zZWxlY3QoY2hhcnRTZWwudGhpc1NWRygpKVxuXG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZXZlbnQgdHlwZSBpbiB3aGljaCB0byByZXNwb25kXG4gICAqIChzZWUge0BsaW5rIHBsb3Rab29tI2V2ZW50VHlwZX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXSBzaG91bGQgYmUgJ2RyYWcnIG9yICd3aGVlbCdcbiAgICogQHJldHVybnMge3pvb20gfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBwbG90Wm9vbVxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBwbG90Wm9vbT11bmRlZmluZWRcbiAgICovXG4gIHpvb20uZXZlbnRUeXBlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChldmVudFR5cGUgPSBfLCB6b29tKSA6IGV2ZW50VHlwZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgd2hlZWwgc3BlZWQgaW4gd2hpY2ggdG8gc2NhbGUgdGhlIHdoZWVsIHNjcm9sbCB0cmFuc2Zvcm1cbiAgICogKHNlZSB7QGxpbmsgcGxvdFpvb20jd2hlZWxTcGVlZH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7em9vbSB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHBsb3Rab29tXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHdoZWVsU3BlZWQ9MjBcbiAgICovXG4gIHpvb20ud2hlZWxTcGVlZCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAod2hlZWxTcGVlZCA9IF8sIHpvb20pIDogd2hlZWxTcGVlZDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgb3JpZW50YXRpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIHBsb3Rab29tI29yaWVudH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXSBzaG91bGQgYmUgaG9yaXpvbnRhbCwgdmVydGljYWwsIG9yIDJEXG4gICAqIEByZXR1cm5zIHt6b29tIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgcGxvdFpvb21cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3JpZW50PWNoYXJ0Lm9yaWVudCgpIHx8ICdob3Jpem9udGFsJ1xuICAgKi9cbiAgem9vbS5vcmllbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9yaWVudCA9IF8sIHpvb20pIDogb3JpZW50OyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIG1heCBkaXN0YW5jZSBpbiB3aGljaCB0byBzY3JvbGwgWFxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN4TG9ja30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBwb3NpdGl2ZSB2YWx1ZVxuICAgKiBAcmV0dXJucyB7em9vbSB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHBsb3Rab29tXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhMb2NrPWNoYXJ0LnNwYWNlWCgpXG4gICAqL1xuICB6b29tLnhMb2NrID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4TG9jayA9IF8sIHpvb20pIDogeExvY2s7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIG1heCBkaXN0YW5jZSBpbiB3aGljaCB0byBzY3JvbGwgWVxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN5TG9ja30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSAgc2hvdWxkIGJlIGEgcG9zaXRpdmUgdmFsdWVcbiAgICogQHJldHVybnMge3pvb20gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBwbG90Wm9vbVxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5TG9jaz1jaGFydC5zcGFjZVkoKVxuICAgKi9cbiAgem9vbS55TG9jayA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeUxvY2sgPSBfLCB6b29tKSA6IHlMb2NrOyB9O1xuXG5cbiAgZnVuY3Rpb24gc2V0TG9ja3MoKSB7XG4gICAgdmFyIGNoYXJ0T2JqU2VsID0gY2hhcnRTZWwuc2VsZWN0KCcuJytoeXBlbmF0ZShjaGFydC5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuICAgIHZhciBjaGFydE9ialRyYW5zID0gZ2V0VHJhbnNsYXRpb24oY2hhcnRPYmpTZWwuYXR0cigndHJhbnNmb3JtJykpXG4gICAgdmFyIGNvcyA9IGNoYXJ0T2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoMCwwKScpXG4gICAgeExvY2sgPSBjaGFydFNlbC5ub2RlKCkuZ2V0QkJveCgpLndpZHRoIC0gY2hhcnQuc3BhY2VYKCkgKiAuOVxuICAgIHlMb2NrID0gY2hhcnRTZWwubm9kZSgpLmdldEJCb3goKS5oZWlnaHQgLSBjaGFydC5zcGFjZVkoKSAqIC45XG4gICAgY29zLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoJytjaGFydE9ialRyYW5zWzBdKycsJytjaGFydE9ialRyYW5zWzFdKycpJylcbiAgICBsb2coJ3Bsb3Rab29tJywgJ3NldExvY2tzJywge3hMb2NrOnhMb2NrLCB5TG9jazp5TG9ja30pXG4gIH1cblxuXG4gIC8qKlxuICAgKiBTZXRzIHRoZSB4IGFuZCB5IGxvY2tzIChob3cgZmFyIG9uZSBjYW4gc2Nyb2xsKVxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN4TG9ja30gYW5kIHtAbGluayBwbG90Wm9vbSN5TG9ja30pXG4gICAqIEBmdW5jdGlvbiBwbG90Wm9vbS5zZXRMb2Nrc1xuICAgKiBAcmV0dXJucyB7dW5kZWZpbmVkfVxuICAgKiBAbWVtYmVyb2YgcGxvdFpvb21cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICB6b29tLnNldExvY2tzID0gc2V0TG9ja3NcblxuICBmdW5jdGlvbiB6b29tKCkge1xuICAgIHNldExvY2tzKClcblxuICAgIHZhciBob3Jpem9udGFsUSwgdmVydGljYWxRXG4gICAgaWYgKG9yaWVudCA9PSAnMkQnKSB7aG9yaXpvbnRhbFEgPSB0cnVlOyB2ZXJ0aWNhbFEgPSB0cnVlO31cbiAgICBpZiAob3JpZW50ID09ICdob3Jpem9udGFsJykge2hvcml6b250YWxRID0gdHJ1ZTsgdmVydGljYWxRID0gZmFsc2U7fVxuICAgIGlmIChvcmllbnQgPT0gJ3ZlcnRpY2FsJykge3ZlcnRpY2FsUSA9IHRydWU7IGhvcml6b250YWxRID0gZmFsc2U7fVxuXG4gICAgLy8gY2FwdHVyZSB0cmFuc2Zvcm0gZXZlbnRcbiAgICB2YXIgdHJhbnNmb3JtID0gZDMuZXZlbnQudHJhbnNmb3JtXG5cbiAgICB2YXIgY2hhcnRCb3ggPSBjaGFydFNlbC5ub2RlKCkuZ2V0QkJveCgpXG4gICAgdmFyIHhBeGlzQm94ID0geEF4aXNTZWwubm9kZSgpLmdldEJCb3goKVxuICAgIHZhciB5QXhpc0JveCA9IHhBeGlzU2VsLm5vZGUoKS5nZXRCQm94KClcblxuICAgIHZhciBjaGFydFdpZHRoID0gY2hhcnRCb3gud2lkdGggLSBjaGFydEJveC54XG4gICAgdmFyIGNoYXJ0SGVpZ2h0ID0gY2hhcnRCb3guaGVpZ2h0IC0gY2hhcnRCb3gueVxuICAgIHZhciB4QXhpc1dpZHRoID0geEF4aXNCb3gud2lkdGgvLyAtIHhBeGlzQm94LnhcbiAgICB2YXIgeEF4aXNIZWlnaHQgPSB4QXhpc0JveC5oZWlnaHQvLyAtIHhBeGlzQm94LnlcbiAgICB2YXIgeUF4aXNXaWR0aCA9IHlBeGlzQm94LndpZHRoLy8gLSB5QXhpc0JveC54XG4gICAgdmFyIHlBeGlzSGVpZ2h0ID0geUF4aXNCb3guaGVpZ2h0Ly8gLXlBeGlzQm94LnlcblxuICAgIC8vIGVuYWJsZSB3aGVlbCBldmVudFxuICAgIGlmIChldmVudFR5cGUgPT0gXCJ3aGVlbFwiKSB7XG4gICAgICB2YXIgZSA9IGQzLmV2ZW50XG4gICAgICAvLyBwcmV2ZW50IHBhZ2Ugc2Nyb2xsaW5nXG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIC8vIGV2ZW50IGRlbHRhIGlzIHZlcnkgdmVyeSBzbG93LCBzbyBzcGVlZCBpdCB1cCB3aXRoIHdoZWVsIHNwZWVkXG4gICAgICB2YXIgdyA9IGQzLmV2ZW50LmRlbHRhWSAqIHdoZWVsU3BlZWRcbiAgICAgIHZhciBzaGlmdFEgPSBkMy5ldmVudC5zaGlmdEtleVxuXG4gICAgICAvLyBkMyBoYXMgbm8gd2F5IHRvIG1ha2UgY3VzdG9tIHRyYW5zZm9ybSwgc28gbWFrZSBhbiBvYmplY3QgYW5kIGFkZFxuICAgICAgLy8gdGhlIG5lY2Vzc2FyeSBmdW5jdGlvbnMgdG8gbWFrZSB3aGVlbCBldmVudCBjb21wYXRpYmxlIHdpdGggZHJhZyBldmVudHNcbiAgICAgIGlmIChvcmllbnQgPT0gJzJEJykge1xuICAgICAgICB0cmFuc2Zvcm0gPSBzaGlmdFEgPyB7azogMSwgeDogdywgeTogMH0gOiB7azogMSwgeDogMCwgeTogd31cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRyYW5zZm9ybSA9IGhvcml6b250YWxRID8ge2s6IDEsIHg6IHcsIHk6IDB9IDoge2s6IDEsIHg6IDAsIHk6IHd9XG4gICAgICB9XG4gICAgICAvLyB0aGUgKiAtMSBpbnZlcnRzIHRoZSBkaXJlY3Rpb25cbiAgICAgIHRyYW5zZm9ybS5hcHBseVggPSBmdW5jdGlvbih4KSB7IHJldHVybiB4ICogdGhpcy5rICsgdGhpcy54ICogLTE7IH1cbiAgICAgIHRyYW5zZm9ybS5hcHBseVkgPSAgZnVuY3Rpb24oeSkgeyByZXR1cm4geSAqIHRoaXMuayArIHRoaXMueSAqIC0xOyB9XG4gICAgfVxuXG5cblxuICAgIHZhciBjaGFydE9ialNlbCA9IGNoYXJ0U2VsLnNlbGVjdCgnLicraHlwZW5hdGUoY2hhcnQubmFtZXNwYWNlKCksJ29iamVjdC1jb250YWluZXInKSlcbiAgICB2YXIgeEF4aXNPYmpTZWwgPSB4QXhpc1NlbC5zZWxlY3QoJy4nK2h5cGVuYXRlKHhBeGlzLm5hbWVzcGFjZSgpLCdvYmplY3QtY29udGFpbmVyJykpXG4gICAgdmFyIHlBeGlzT2JqU2VsID0geUF4aXNTZWwuc2VsZWN0KCcuJytoeXBlbmF0ZSh5QXhpcy5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuXG4gICAgLy8geExvY2sgPSBjaGFydFNlbC5ub2RlKCkuZ2V0QkJveCgpLndpZHRoIC0gY2hhcnQuc3BhY2VYKCkgLSBjaGFydFNlbC5ub2RlKCkuZ2V0QkJveCgpLnhcbiAgICAvLyB5TG9jayA9IGNoYXJ0U2VsLm5vZGUoKS5nZXRCQm94KCkuaGVpZ2h0IC0gY2hhcnQuc3BhY2VZKClcbiAgICAvLyBjb25zb2xlLnRhYmxlKHsneExvY2snOnhMb2NrLCBcInlMb2NrXCI6eUxvY2t9KVxuICAgIC8vIGJobS5zZWxlY3Rpb24oKS5ub2RlKCkuZ2V0QkJveCgpLndpZHRoIC0gYmhtLnNwYWNlWCgpXG5cblxuICAgIHZhciBjaGFydE9ialRyYW5zID0gZ2V0VHJhbnNsYXRpb24oY2hhcnRPYmpTZWwuYXR0cigndHJhbnNmb3JtJykpXG4gICAgdmFyIHhBeGlzT2JqVHJhbnMgPSBnZXRUcmFuc2xhdGlvbih4QXhpc09ialNlbC5hdHRyKCd0cmFuc2Zvcm0nKSlcbiAgICB2YXIgeUF4aXNPYmpUcmFucyA9IGdldFRyYW5zbGF0aW9uKHlBeGlzT2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScpKVxuXG5cbiAgICB2YXIgeCA9IGhvcml6b250YWxRID8gdHJhbnNmb3JtLmFwcGx5WChjaGFydE9ialRyYW5zWzBdKSA6IDBcbiAgICBpZiAoaG9yaXpvbnRhbFEpIHt4ID0geCA8IC14TG9jayA/ICh0cmFuc2Zvcm0ueCA9IDAsIC14TG9jaykgOiAodHJhbnNmb3JtLnggPSAwLCBNYXRoLm1pbih4LCAwKSkgfVxuXG4gICAgdmFyIHkgPSB2ZXJ0aWNhbFEgPyB0cmFuc2Zvcm0uYXBwbHlZKGNoYXJ0T2JqVHJhbnNbMV0pIDogMFxuICAgIGlmICh2ZXJ0aWNhbFEpIHt5ID0geSA8IC15TG9jayA/ICh0cmFuc2Zvcm0ueSA9IDAsIC15TG9jayk6ICh0cmFuc2Zvcm0ueSA9IDAsIE1hdGgubWluKHksIDApKX1cblxuICAgIGNoYXJ0T2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJylcbiAgICBpZiAoaG9yaXpvbnRhbFEpIHsgeEF4aXNPYmpTZWwuYXR0cigndHJhbnNmb3JtJywgJ3RyYW5zbGF0ZSgnK3grJywnKzArJyknKSB9XG4gICAgaWYgKHZlcnRpY2FsUSkgeyB5QXhpc09ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcrMCsnLCcreSsnKScpIH1cblxuICAgIC8vIHZhciBsYXNzbyA9IHN2Zy5zZWxlY3QoXCIubGFzc28tY29udGFpbmVyXCIpXG4gICAgLy8gaWYgKCFsYXNzby5lbXB0eSgpKSB7XG4gICAgLy8gICBsYXNzby5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcreCsnLCcreSsnKScpXG4gICAgLy8gfVxuXG4gIH1cblxuICB6b29tLnJlc2V0ID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGhvcml6b250YWxRLCB2ZXJ0aWNhbFFcbiAgICBpZiAob3JpZW50ID09ICcyRCcpIHtob3Jpem9udGFsUSA9IHRydWU7IHZlcnRpY2FsUSA9IHRydWU7fVxuICAgIGlmIChvcmllbnQgPT0gJ2hvcml6b250YWwnKSB7aG9yaXpvbnRhbFEgPSB0cnVlOyB2ZXJ0aWNhbFEgPSBmYWxzZTt9XG4gICAgaWYgKG9yaWVudCA9PSAndmVydGljYWwnKSB7dmVydGljYWxRID0gdHJ1ZTsgaG9yaXpvbnRhbFEgPSBmYWxzZTt9XG5cbiAgICB2YXIgY2hhcnRPYmpTZWwgPSBjaGFydFNlbC5zZWxlY3QoJy4nK2h5cGVuYXRlKGNoYXJ0Lm5hbWVzcGFjZSgpLCdvYmplY3QtY29udGFpbmVyJykpXG4gICAgdmFyIHhBeGlzT2JqU2VsID0geEF4aXNTZWwuc2VsZWN0KCcuJytoeXBlbmF0ZSh4QXhpcy5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuICAgIHZhciB5QXhpc09ialNlbCA9IHlBeGlzU2VsLnNlbGVjdCgnLicraHlwZW5hdGUoeUF4aXMubmFtZXNwYWNlKCksJ29iamVjdC1jb250YWluZXInKSlcbiAgICBjaGFydE9ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcrMCsnLCcrMCsnKScpXG4gICAgeEF4aXNPYmpTZWwuYXR0cigndHJhbnNmb3JtJywgJ3RyYW5zbGF0ZSgnKzArJywnKzArJyknKVxuICAgIHlBeGlzT2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoJyswKycsJyswKycpJylcbiAgfVxuXG4gIHJldHVybiB6b29tXG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0LCBnZXRUcmFuc2xhdGlvbn0gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7bG9nLCB3YXJuLCBlcnJvciwgaW5mb30gZnJvbSAnLi91dGlscyc7XG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQTE9UWk9PTSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKipcbiAqIENyZWF0ZXMgYW4gcGxvdFpvb20gaW5zdGFuY2UsIHdoaWNoIGNhbiBoYW5kbGUgZHJhZyBhbmQgc2Nyb2xsIGV2ZW50c1xuICogQGNvbnN0cnVjdG9yIHBsb3Rab29tXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBjaGFydCBhIGZ1bmN0aW9uIGluc3RhbmNlIG9mIG9uZSBvZiB0aGUgZDNzbSBwbG90cyAoZS5nLiBiYXIsIGJveHdoaXNrZXIsIGJ1YmJsZUhlYXRtYXAsIHZpb2xpbiwgZXRjKVxuICogQHBhcmFtIHtheGlzfSAgeEF4aXMgdGhlIGF4aXMgaW5zdGFuY2UgcmVzcG9uc2libGUgZm9yIHRoZSB4IGF4aXNcbiAqIEBwYXJhbSB7YXhpc30geUF4aXMgdGhlIGF4aXMgaW5zdGFuY2UgcmVzcG9uc2libGUgZm9yIHRoZSB5IGF4aXNcbiAqIEBuYW1lc3BhY2UgcGxvdFpvb21cbiAqIEByZXR1cm5zIHtmdW5jdGlvbn0gem9vbVxuICovXG5leHBvcnQgZnVuY3Rpb24gbXVsdGlQbG90Wm9vbSggY2hhcnQgKSB7XG4gIHZhclxuICAvKipcbiAgKiBUaGUgZXZlbnQgb24gd2hpY2ggdG8gZmlyZVxuICAqIChzZWUge0BsaW5rIHBsb3Rab29tI2V2ZW50VHlwZX0pXG4gICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50VHlwZSB3aGljaCBldmVudCBpdCBzaG91bGQgaGFuZGxlLiBDdXJyZW50bHkgc3VwcG9ydHMgc2Nyb2xsIGFuZCB3aGVlbFxuICAqIEBtZW1iZXJvZiBwbG90Wm9vbSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZXZlbnRUeXBlLFxuICAvKipcbiAgKiBBIHNjYWxpbmcgZmFjdG9yIGZvciB0aGUgd2hlZWwgXCJzcGVlZFwiXG4gICogKHNlZSB7QGxpbmsgcGxvdFpvb20jd2hlZWxTcGVlZH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFt3aGVlbFNwZWVkPTIwXSBzY2FsZXMgdGhlIHdoZWVsIHRyYW5zbGF0aW9uIGJ5IHdoZWVsU3BlZWRcbiAgKiBAbWVtYmVyb2YgcGxvdFpvb20jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHdoZWVsU3BlZWQgPSAyMCxcbiAgLyoqXG4gICogVGhlIG9yaWVudGF0aW9uIGluIHdoaWNoIHRvIGFsbG93IHNjcm9sbGluZzogJ2hvcml6b250YWwnLCAndmVydGljYWwnLCBvciAnMkQnXG4gICogKHNlZSB7QGxpbmsgcGxvdFpvb20jb3JpZW50fSlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW29yaWVudD1jaGFydC5vcmllbnQoKSB8fCAnaG9yaXpvbnRhbCddXG4gICogQG1lbWJlcm9mIHBsb3Rab29tI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvcmllbnQgPSAoY2hhcnQub3JpZW50ID09IHVuZGVmaW5lZCkgPyAnaG9yaXpvbnRhbCcgOiBjaGFydC5vcmllbnQoKSxcbiAgLyoqXG4gICogVGhlIG1heCBkaXN0YW5jZSBhbGxvd2VkIHRvIHNjcm9sbCBpbiB0aGUgeCBkaXJlY3Rpb25cbiAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN4TG9ja30pXG4gICogQHBhcmFtIHtudW1iZXJ9IFt4TG9jaz1jaGFydC5zcGFjZVgoKV0gaWRlYWxseSBjaGFydC5vdmVyZmxvd1EoKSA9PSB0cnVlIGFuZCB0aGlzIHZhbHVlIGlzIHRoZVxuICAqIGJvdW5kaW5nIHJlY3QgYWNyb3NzIGFsbCBlbGVtZW50cyBpbiB0aGUgY2hhcnQgIG1pbnVzIHRoZSBzcGFjZSBpbiB3aGljaCB0byBzaG93LlxuICAqIEBtZW1iZXJvZiBwbG90Wm9vbSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeExvY2s9Y2hhcnQuc3BhY2VYKCksXG4gIC8qKlxuICAqIFRoZSBtYXggZGlzdGFuY2UgYWxsb3dlZCB0byBzY3JvbGwgaW4gdGhlIHkgZGlyZWN0aW9uXG4gICogKHNlZSB7QGxpbmsgcGxvdFpvb20jeUxvY2t9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbeUxvY2s9Y2hhcnQuc3BhY2VZKCldIGlkZWFsbHkgY2hhcnQub3ZlcmZsb3dRKCkgPT0gdHJ1ZSBhbmQgdGhpcyB2YWx1ZSBpcyB0aGVcbiAgKiBib3VuZGluZyByZWN0IGFjcm9zcyBhbGwgZWxlbWVudHMgaW4gdGhlIGNoYXJ0ICBtaW51cyB0aGUgc3BhY2UgaW4gd2hpY2ggdG8gc2hvdy5cbiAgKiBAbWVtYmVyb2YgcGxvdFpvb20jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHlMb2NrPWNoYXJ0LnNwYWNlWSgpLFxuXG4gIGNoYXJ0U2VsID0gY2hhcnQuc2VsZWN0aW9uKCksXG5cbiAgc3ZnID0gZDMuc2VsZWN0KGNoYXJ0U2VsLnRoaXNTVkcoKSksXG5cbiAgeENvbXBvbmVudHMgPSBbXSxcbiAgeUNvbXBvbmVudHMgPSBbXVxuXG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZXZlbnQgdHlwZSBpbiB3aGljaCB0byByZXNwb25kXG4gICAqIChzZWUge0BsaW5rIHBsb3Rab29tI2V2ZW50VHlwZX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXSBzaG91bGQgYmUgJ2RyYWcnIG9yICd3aGVlbCdcbiAgICogQHJldHVybnMge3pvb20gfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBwbG90Wm9vbVxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBwbG90Wm9vbT11bmRlZmluZWRcbiAgICovXG4gIHpvb20uZXZlbnRUeXBlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChldmVudFR5cGUgPSBfLCB6b29tKSA6IGV2ZW50VHlwZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgd2hlZWwgc3BlZWQgaW4gd2hpY2ggdG8gc2NhbGUgdGhlIHdoZWVsIHNjcm9sbCB0cmFuc2Zvcm1cbiAgICogKHNlZSB7QGxpbmsgcGxvdFpvb20jd2hlZWxTcGVlZH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7em9vbSB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHBsb3Rab29tXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHdoZWVsU3BlZWQ9MjBcbiAgICovXG4gIHpvb20ud2hlZWxTcGVlZCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAod2hlZWxTcGVlZCA9IF8sIHpvb20pIDogd2hlZWxTcGVlZDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgb3JpZW50YXRpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIHBsb3Rab29tI29yaWVudH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXSBzaG91bGQgYmUgaG9yaXpvbnRhbCwgdmVydGljYWwsIG9yIDJEXG4gICAqIEByZXR1cm5zIHt6b29tIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgcGxvdFpvb21cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3JpZW50PWNoYXJ0Lm9yaWVudCgpIHx8ICdob3Jpem9udGFsJ1xuICAgKi9cbiAgem9vbS5vcmllbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9yaWVudCA9IF8sIHpvb20pIDogb3JpZW50OyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIG1heCBkaXN0YW5jZSBpbiB3aGljaCB0byBzY3JvbGwgWFxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN4TG9ja30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBwb3NpdGl2ZSB2YWx1ZVxuICAgKiBAcmV0dXJucyB7em9vbSB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHBsb3Rab29tXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhMb2NrPWNoYXJ0LnNwYWNlWCgpXG4gICAqL1xuICB6b29tLnhMb2NrID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4TG9jayA9IF8sIHpvb20pIDogeExvY2s7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIG1heCBkaXN0YW5jZSBpbiB3aGljaCB0byBzY3JvbGwgWVxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN5TG9ja30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSAgc2hvdWxkIGJlIGEgcG9zaXRpdmUgdmFsdWVcbiAgICogQHJldHVybnMge3pvb20gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBwbG90Wm9vbVxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5TG9jaz1jaGFydC5zcGFjZVkoKVxuICAgKi9cbiAgem9vbS55TG9jayA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeUxvY2sgPSBfLCB6b29tKSA6IHlMb2NrOyB9O1xuXG4gIHpvb20ueENvbXBvbmVudHMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhDb21wb25lbnRzID0gXywgem9vbSkgOiB4Q29tcG9uZW50czsgfTtcbiAgem9vbS55Q29tcG9uZW50cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeUNvbXBvbmVudHMgPSBfLCB6b29tKSA6IHlDb21wb25lbnRzOyB9O1xuXG5cbiAgZnVuY3Rpb24gc2V0TG9ja3MoKSB7XG4gICAgdmFyIGNoYXJ0T2JqU2VsID0gY2hhcnRTZWwuc2VsZWN0KCcuJytoeXBlbmF0ZShjaGFydC5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuICAgIHZhciBjaGFydE9ialRyYW5zID0gZ2V0VHJhbnNsYXRpb24oY2hhcnRPYmpTZWwuYXR0cigndHJhbnNmb3JtJykpXG4gICAgdmFyIGNvcyA9IGNoYXJ0T2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoMCwwKScpXG5cbiAgICB4TG9jayA9IGNoYXJ0U2VsLm5vZGUoKS5nZXRCQm94KCkud2lkdGggLSBjaGFydC5zcGFjZVgoKS8vICogLjlcbiAgICB5TG9jayA9IGNoYXJ0U2VsLm5vZGUoKS5nZXRCQm94KCkuaGVpZ2h0IC0gY2hhcnQuc3BhY2VZKCkvLyAqIC45XG4gICAgY29zLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoJytjaGFydE9ialRyYW5zWzBdKycsJytjaGFydE9ialRyYW5zWzFdKycpJylcbiAgICBsb2coJ3Bsb3Rab29tJywgJ3NldExvY2tzJywge3hMb2NrOnhMb2NrLCB5TG9jazp5TG9ja30pXG4gIH1cblxuXG4gIC8qKlxuICAgKiBTZXRzIHRoZSB4IGFuZCB5IGxvY2tzIChob3cgZmFyIG9uZSBjYW4gc2Nyb2xsKVxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN4TG9ja30gYW5kIHtAbGluayBwbG90Wm9vbSN5TG9ja30pXG4gICAqIEBmdW5jdGlvbiBwbG90Wm9vbS5zZXRMb2Nrc1xuICAgKiBAcmV0dXJucyB7dW5kZWZpbmVkfVxuICAgKiBAbWVtYmVyb2YgcGxvdFpvb21cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICB6b29tLnNldExvY2tzID0gc2V0TG9ja3NcblxuICBmdW5jdGlvbiB6b29tKCkge1xuICAgIHNldExvY2tzKClcblxuICAgIHZhclxuICAgIHhDb21wb25lbnRzU2VsID0geENvbXBvbmVudHMubWFwKGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkLnNlbGVjdGlvbigpfSksXG4gICAgeUNvbXBvbmVudHNTZWwgPSB5Q29tcG9uZW50cy5tYXAoZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGQuc2VsZWN0aW9uKCl9KVxuXG4gICAgdmFyIGhvcml6b250YWxRLCB2ZXJ0aWNhbFFcbiAgICBpZiAob3JpZW50ID09ICcyRCcpIHtob3Jpem9udGFsUSA9IHRydWU7IHZlcnRpY2FsUSA9IHRydWU7fVxuICAgIGlmIChvcmllbnQgPT0gJ2hvcml6b250YWwnKSB7aG9yaXpvbnRhbFEgPSB0cnVlOyB2ZXJ0aWNhbFEgPSBmYWxzZTt9XG4gICAgaWYgKG9yaWVudCA9PSAndmVydGljYWwnKSB7dmVydGljYWxRID0gdHJ1ZTsgaG9yaXpvbnRhbFEgPSBmYWxzZTt9XG5cbiAgICAvLyBjYXB0dXJlIHRyYW5zZm9ybSBldmVudFxuICAgIHZhciB0cmFuc2Zvcm0gPSBkMy5ldmVudC50cmFuc2Zvcm1cblxuICAgIHZhciBjaGFydEJveCA9IGNoYXJ0U2VsLm5vZGUoKS5nZXRCQm94KClcbiAgICB2YXIgeENvbXBvbmVudHNCb3ggPSB4Q29tcG9uZW50c1NlbC5tYXAoZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGQubm9kZSgpLmdldEJCb3goKX0pXG4gICAgdmFyIHlDb21wb25lbnRzQm94ID0geENvbXBvbmVudHNTZWwubWFwKGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkLm5vZGUoKS5nZXRCQm94KCl9KVxuXG5cbiAgICB2YXIgY2hhcnRXaWR0aCA9IGNoYXJ0Qm94LndpZHRoIC0gY2hhcnRCb3gueFxuICAgIHZhciBjaGFydEhlaWdodCA9IGNoYXJ0Qm94LmhlaWdodCAtIGNoYXJ0Qm94LnlcblxuICAgIC8vIGVuYWJsZSB3aGVlbCBldmVudFxuICAgIGlmIChldmVudFR5cGUgPT0gXCJ3aGVlbFwiKSB7XG4gICAgICB2YXIgZSA9IGQzLmV2ZW50XG4gICAgICAvLyBwcmV2ZW50IHBhZ2Ugc2Nyb2xsaW5nXG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIC8vIGV2ZW50IGRlbHRhIGlzIHZlcnkgdmVyeSBzbG93LCBzbyBzcGVlZCBpdCB1cCB3aXRoIHdoZWVsIHNwZWVkXG4gICAgICB2YXIgdyA9IGQzLmV2ZW50LmRlbHRhWSAqIHdoZWVsU3BlZWRcbiAgICAgIHZhciBzaGlmdFEgPSBkMy5ldmVudC5zaGlmdEtleVxuXG4gICAgICAvLyBkMyBoYXMgbm8gd2F5IHRvIG1ha2UgY3VzdG9tIHRyYW5zZm9ybSwgc28gbWFrZSBhbiBvYmplY3QgYW5kIGFkZFxuICAgICAgLy8gdGhlIG5lY2Vzc2FyeSBmdW5jdGlvbnMgdG8gbWFrZSB3aGVlbCBldmVudCBjb21wYXRpYmxlIHdpdGggZHJhZyBldmVudHNcbiAgICAgIGlmIChvcmllbnQgPT0gJzJEJykge1xuICAgICAgICB0cmFuc2Zvcm0gPSBzaGlmdFEgPyB7azogMSwgeDogdywgeTogMH0gOiB7azogMSwgeDogMCwgeTogd31cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRyYW5zZm9ybSA9IGhvcml6b250YWxRID8ge2s6IDEsIHg6IHcsIHk6IDB9IDoge2s6IDEsIHg6IDAsIHk6IHd9XG4gICAgICB9XG4gICAgICAvLyAqIC0xIGlzIGludmVydFxuICAgICAgdHJhbnNmb3JtLmFwcGx5WCA9IGZ1bmN0aW9uKHgpIHsgcmV0dXJuIHggKiB0aGlzLmsgKyB0aGlzLnggKi0xOyB9XG4gICAgICB0cmFuc2Zvcm0uYXBwbHlZID0gIGZ1bmN0aW9uKHkpIHsgcmV0dXJuIHkgKiB0aGlzLmsgKyB0aGlzLnkgKi0xOyB9XG4gICAgfVxuXG5cblxuICAgIHZhciBjaGFydE9ialNlbCA9IGNoYXJ0U2VsLnNlbGVjdCgnLicraHlwZW5hdGUoY2hhcnQubmFtZXNwYWNlKCksJ29iamVjdC1jb250YWluZXInKSlcbiAgICB2YXIgeENvbXBvbmVudE9ialNlbCA9IHhDb21wb25lbnRzU2VsLm1hcChmdW5jdGlvbihkLCBpKXtcbiAgICAgIHJldHVybiBkLnNlbGVjdCgnLicraHlwZW5hdGUoeENvbXBvbmVudHNbaV0ubmFtZXNwYWNlKCksJ29iamVjdC1jb250YWluZXInKSlcbiAgICB9KVxuICAgIHZhciB5Q29tcG9uZW50T2JqU2VsID0geUNvbXBvbmVudHNTZWwubWFwKGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgcmV0dXJuIGQuc2VsZWN0KCcuJytoeXBlbmF0ZSh5Q29tcG9uZW50c1tpXS5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuICAgIH0pXG5cbiAgICB2YXIgY2hhcnRPYmpUcmFucyA9IGdldFRyYW5zbGF0aW9uKGNoYXJ0T2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScpKVxuICAgIHZhciB4Q29tcG9uZW50c09ialRyYW5zID0geENvbXBvbmVudE9ialNlbC5tYXAoZnVuY3Rpb24oZCwgaSl7XG4gICAgICByZXR1cm4gZ2V0VHJhbnNsYXRpb24oZC5hdHRyKCd0cmFuc2Zvcm0nKSlcbiAgICB9KVxuICAgIHZhciB5Q29tcG9uZW50c09ialRyYW5zID0geUNvbXBvbmVudE9ialNlbC5tYXAoZnVuY3Rpb24oZCwgaSl7XG4gICAgICByZXR1cm4gZ2V0VHJhbnNsYXRpb24oZC5hdHRyKCd0cmFuc2Zvcm0nKSlcbiAgICB9KVxuXG4gICAgdmFyIHggPSBob3Jpem9udGFsUSA/IHRyYW5zZm9ybS5hcHBseVgoY2hhcnRPYmpUcmFuc1swXSkgOiAwXG4gICAgaWYgKGhvcml6b250YWxRKSB7eCA9IHggPCAteExvY2sgPyAodHJhbnNmb3JtLnggPSAwLCAteExvY2spIDogKHRyYW5zZm9ybS54ID0gMCwgTWF0aC5taW4oeCwgMCkpIH1cblxuICAgIHZhciB5ID0gdmVydGljYWxRID8gdHJhbnNmb3JtLmFwcGx5WShjaGFydE9ialRyYW5zWzFdKSA6IDBcbiAgICBpZiAodmVydGljYWxRKSB7eSA9IHkgPCAteUxvY2sgPyAodHJhbnNmb3JtLnkgPSAwLCAteUxvY2spOiAodHJhbnNmb3JtLnkgPSAwLCBNYXRoLm1pbih5LCAwKSl9XG5cbiAgICBjaGFydE9ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcreCsnLCcreSsnKScpXG4gICAgaWYgKGhvcml6b250YWxRKSB7XG4gICAgICAvLyB4QXhpc09ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcreCsnLCcrMCsnKScpXG4gICAgICB4Q29tcG9uZW50T2JqU2VsLm1hcChmdW5jdGlvbihkLCBpKXsgZC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcreCsnLCcrMCsnKScpICB9KVxuICAgIH1cbiAgICBpZiAodmVydGljYWxRKSB7XG4gICAgICAvLyB5QXhpc09ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcrMCsnLCcreSsnKScpXG4gICAgICB5Q29tcG9uZW50T2JqU2VsLm1hcChmdW5jdGlvbihkLCBpKXsgZC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcrMCsnLCcreSsnKScpICB9KVxuXG4gICAgfVxuXG5cbiAgfVxuXG4gIHpvb20ucmVzZXQgPSBmdW5jdGlvbigpIHtcbiAgICB2YXIgaG9yaXpvbnRhbFEsIHZlcnRpY2FsUVxuICAgIGlmIChvcmllbnQgPT0gJzJEJykge2hvcml6b250YWxRID0gdHJ1ZTsgdmVydGljYWxRID0gdHJ1ZTt9XG4gICAgaWYgKG9yaWVudCA9PSAnaG9yaXpvbnRhbCcpIHtob3Jpem9udGFsUSA9IHRydWU7IHZlcnRpY2FsUSA9IGZhbHNlO31cbiAgICBpZiAob3JpZW50ID09ICd2ZXJ0aWNhbCcpIHt2ZXJ0aWNhbFEgPSB0cnVlOyBob3Jpem9udGFsUSA9IGZhbHNlO31cblxuICAgIHZhciBjaGFydE9ialNlbCA9IGNoYXJ0U2VsLnNlbGVjdCgnLicraHlwZW5hdGUoY2hhcnQubmFtZXNwYWNlKCksJ29iamVjdC1jb250YWluZXInKSlcbiAgICB2YXIgeEF4aXNPYmpTZWwgPSB4QXhpc1NlbC5zZWxlY3QoJy4nK2h5cGVuYXRlKHhBeGlzLm5hbWVzcGFjZSgpLCdvYmplY3QtY29udGFpbmVyJykpXG4gICAgdmFyIHlBeGlzT2JqU2VsID0geUF4aXNTZWwuc2VsZWN0KCcuJytoeXBlbmF0ZSh5QXhpcy5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuICAgIGNoYXJ0T2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoJyswKycsJyswKycpJylcbiAgICB4QXhpc09ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcrMCsnLCcrMCsnKScpXG4gICAgeUF4aXNPYmpTZWwuYXR0cigndHJhbnNmb3JtJywgJ3RyYW5zbGF0ZSgnKzArJywnKzArJyknKVxuICB9XG5cbiAgcmV0dXJuIHpvb21cbn1cbiIsImltcG9ydCB7aHlwZW5hdGUsIHNhZmVTZWxlY3QsIG1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2UsIGV4dHJhY3RWaW9saW5WYWx1ZXMsIHF1YXJ0aWxlc30gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7c2V0dXBDb250YWluZXIsIGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3QsIGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXJ9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHt1bmlxdWUsIGhhc1EsIGZsYXR0ZW4sIHdoaWNoQmlufSBmcm9tICcuL2FycmF5LWZ1bmN0aW9ucyc7XG5pbXBvcnQge2dyb3VwaW5nU3BhY2VyfSBmcm9tICcuL2dyb3VwaW5nLXNwYWNlcic7XG5pbXBvcnQge2NvbG9yRnVuY3Rpb24gYXMgQ0Z9IGZyb20gJy4vY29sb3ItZnVuY3Rpb24nO1xuaW1wb3J0IHt0b29sdGlwIGFzIFRUaXB9IGZyb20gJy4vdG9vbHRpcCc7XG5cbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVklPTElOICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbi8qKlxuICogQ3JlYXRlcyBhIHZpb2xpblxuICpcbiAqIHtAbGluayBodHRwczovL3N1bW5ldXJvbi5naXRsYWIuaW8vZDNzbS9kZW1vcy9iYXNpYy12aW9saW5zL2luZGV4Lmh0bWwgRGVtb31cbiAqIEBjb25zdHJ1Y3RvciB2aW9saW5cbiAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBzZWxlY3Rpb25cbiAqIEBuYW1lc3BhY2UgdmlvbGluXG4gKiBAcmV0dXJucyB7ZnVuY3Rpb259IHZpb2xpblxuICovXG5leHBvcnQgZnVuY3Rpb24gdmlvbGluKCBzZWxlY3Rpb24gKSB7XG4gIHZhclxuICAvKipcbiAgKiBEYXRhIHRvIHBsb3QuIEFzc3VtZWQgdG8gYmUgYSBvYmplY3QsIHdoZXJlIGVhY2gga2V5IGNvcnJlc3BvbmRzIHRvIGEgdmlvbGluXG4gICogKHNlZSB7QGxpbmsgdmlvbGluI2RhdGF9KVxuICAqIEBwYXJhbSB7T2JqZWN0fSBbZGF0YT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZGF0YSxcbiAgLyoqXG4gICogV2hpY2ggZGlyZWN0aW9uIHRvIHJlbmRlciB0aGUgYmFycyBpblxuICAqIChzZWUge0BsaW5rIHZpb2xpbiNvcmllbnR9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbb3JpZW50PSdob3Jpem9udGFsJ11cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvcmllbnQ9J2hvcml6b250YWwnLFxuICAvKipcbiAgKiBBbW91bnQgb2YgaG9yaXpvbnRhbCBzcGFjZSAoaW4gcGl4ZWxzKSBhdmFpYmxlIHRvIHJlbmRlciB0aGUgdmlvbGluIGluXG4gICogKHNlZSB7QGxpbmsgdmlvbGluI3NwYWNlWH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVg9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlWCxcbiAgLyoqXG4gICogQW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSB2aW9saW4gaW5cbiAgKiAoc2VlIHtAbGluayB2aW9saW4uc3BhY2VZfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlWT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VZLFxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0byBhbGxvdyB2aW9saW4gdG8gcmVuZGVyIGVsZW1lbnRzIHBhc3MgdGhlIG1haW4gc3BhdGlhbCBkaW1lbnNpb25cbiAgKiBnaXZlbiB0aGUgb3JpZW50YXRpb24gKHNlZSB7QGxpbmsgdmlvbGluI29yaWVudH0pLCB3aGVyZSB7QGxpbmsgdmlvbGluI29yaWVudH09XCJob3Jpem9udGFsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIHZpb2xpbiNzcGFjZVh9IGFuZCB3aGVyZSB7QGxpbmsgdmlvbGluI29yaWVudH09XCJ2ZXJ0aWNhbFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayB2aW9saW4jc3BhY2VZfVxuICAqIEBwYXJhbSB7Ym9vbGVhbn0gW292ZXJmbG93UT1mYWxzZV1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvdmVyZmxvd1EgPSB0cnVlLFxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0byBkaXNwbGF5IHBvaW50cyBpbnNpZGUgdGhlIHBvaW50c1xuICAqIEBwYXJhbSB7Ym9vbGVhbn0gW3BvaW50c1E9ZmFsc2VdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgcG9pbnRzUSA9IHRydWUsXG4gIC8qKlxuICAqIEFuIGFycmF5IC0gcHV0YXRpdmVseSBvZiBvdGhlciBhcnJheXMgLSBkZXBpY3RpbmcgaG93IGJhcnMgc2hvdWxkIGJlIGFycmFuZ2VkXG4gICogQHBhcmFtIHtBcnJheVtdfSBbZ3JvdXBpbmc9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIGdyb3VwaW5nLFxuICAvKipcbiAgKiBIb3cgdG8gZ2V0IHRoZSB2YWx1ZSBvZiB0aGUgdmlvbGluXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3ZhbHVlRXh0cmFjdG9yPWZ1bmN0aW9uKGtleSwgaW5kZXgpIHsgcmV0dXJuIGRhdGFba2V5XSB9XVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHZhbHVlRXh0cmFjdG9yID0gZnVuY3Rpb24oa2V5LCBpbmRleCkge3JldHVybiBkYXRhW2tleV0gfSxcbiAgLyoqXG4gICogSG93IHRvIHNvcnQgdGhlIGJhcnMgLSBpZiB7QGxpbmsgdmlvbGluI2dyb3VwaW5nfSBpcyBub3QgcHJvdmlkZWQuXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3NvcnRpbmdGdW5jdGlvbj1mdW5jdGlvbihrZXlBLCBrZXlCKSB7cmV0dXJuIGQzLmRlc2NlbmRpbmcoZGF0YVtrZXlBXSwgZGF0YVtrZXlCXSl9XVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKGtleUEsIGtleUIpIHtyZXR1cm4gZDMuZGVzY2VuZGluZyhkYXRhW2tleUFdLCBkYXRhW2tleUJdKX0sXG5cbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCB2aW9saW4gdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZT1kMy5zY2FsZUxpbmVhcl1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKCksXG4gIC8qKlxuICAqIFRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSBzY2FsZSAoc2VlIHtAbGluayB2aW9saW4jc2NhbGV9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbZG9tYWluUGFkZGluZz0wLjVdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZG9tYWluUGFkZGluZyA9IDAuNSxcbiAgLyoqXG4gICogRGVmYXVsdCBzcGFjZSBmb3IgdGhlIHNwYWNlciAocGVyY2VudGFnZSkgb2YgbWFpbiBkaW1lbnNpb24gZ2l2ZW4gdGhlIG9yaWVudGF0aW9uXG4gICogKHNlZSB7QGxpbmsgdmlvbGluI29yaWVudH0pLCB3aGVyZSB7QGxpbmsgdmlvbGluI29yaWVudH09XCJob3Jpem9udGFsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIHZpb2xpbiNzcGFjZVh9IGFuZCB3aGVyZSB7QGxpbmsgdmlvbGluI29yaWVudH09XCJ2ZXJ0aWNhbFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayB2aW9saW4jc3BhY2VZfSBiZXR3ZWVuIGJhcnNcbiAgKiBAcGFyYW0ge251bWJlcn0gW29iamVjdFNwYWNlcj0wLjA1XVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdFNwYWNlciA9IDAuMDUsXG4gIC8qKlxuICAqIFRoZSBtaW5pbXVtIHNpemUgdGhhdCBhbiBvYmplY3QgY2FuIGJlXG4gICogQHBhcmFtIHtudW1iZXJ9IFttaW5PYmplY3RTaXplPTUwXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1pbk9iamVjdFNpemUgPSA1MCxcbiAgLyoqXG4gICogVGhlIG1heGltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmVcbiAgKiBAcGFyYW0ge251bWJlcn0gW21heE9iamVjdFNpemU9MTAwXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1heE9iamVjdFNpemUgPSAxMDAsXG5cbiAgLyoqXG4gICogVGhlIHN0cm9rZSB3aWR0aCBvZiB0aGUgYmFyc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbYmFyU3Ryb2tlV2lkdGg9Ml1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvYmplY3RTdHJva2VXaWR0aCA9IDIsXG4gIC8qKlxuICAqIEluc3RhbmNlIG9mIENvbG9yRnVuY3Rpb25cbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKV1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjb2xvckZ1bmN0aW9uID0gQ0YoKSxcbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgQ29sb3JGdW5jdGlvbiBtb2RpZmllZCBieSBhIHNjYWxlIGZvciB0aGUgcG9pbnRzXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3BvaW50Q29sb3JGdW5jID0gY29sb3JGdW5jdGlvbigpXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHBvaW50Q29sb3JGdW5jID0gZnVuY3Rpb24gKGQsIHR5cGUsIGJhc2UsIG1pbiwgbWF4KSB7XG4gICAgdmFyIG1pbk1heEhleFNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKS5kb21haW4oW21pbiwgbWF4XSkucmFuZ2UoWy0wLjI1LCAwLjA1XSlcbiAgICB2YXIgc2NhbGVkQ29sb3IgPSBtb2RpZnlIZXhpZGVjaW1hbENvbG9yTHVtaW5hbmNlKGJhc2UucmVwbGFjZSgnIycsICcnKSwgbWluTWF4SGV4U2NhbGUoZCkpXG4gICAgdmFyIG1vZCA9IHR5cGUgPT0gXCJzdHJva2VcIiA/IDAgOiAwLjI1XG4gICAgcmV0dXJuIG1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2Uoc2NhbGVkQ29sb3IucmVwbGFjZSgnIycsICcnKSwgbW9kKVxuICB9LFxuXG4gIC8qKlxuICAqIFRoZSByYWRpdXMgb2YgYSBwb2ludFxuICAqIEBwYXJhbSB7bnVtYmVyfSBbcG9pbnRSYWRpdXM9M11cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBwb2ludFJhZGl1cyA9IDMsXG4gIC8qKlxuICAqIFRoZSBzdHJva2Ugd2lkdGggb2YgdGhlIG9pbnRzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtwb2ludFN0cm9rZVdpZHRoPTJdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgcG9pbnRTdHJva2VXaWR0aCA9IDIsXG5cbiAgLyoqXG4gICogQ29sb3Igb2YgdGhlIGJhY2tncm91bmRcbiAgKiBAcGFyYW0ge3N0cmluZ30gW2JhY2tncm91bmRGaWxsPVwidHJhbnNwYXJlbnRcIl1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCcsXG4gIC8qKlxuICAqIE5hbWVzcGFjZSBmb3IgYWxsIGl0ZW1zIG1hZGUgYnkgdGhpcyBpbnN0YW5jZSBvZiB2aW9saW5cbiAgKiBAcGFyYW0ge3N0cmluZ30gW25hbWVzcGFjZT1cImQzc20tdmlvbGluXCJdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbmFtZXNwYWNlID0gJ2Qzc20tdmlvbGluJyxcbiAgLyoqXG4gICogQ2xhc3MgbmFtZSBmb3IgdmlvbGluIGNvbnRhaW5lciAoPGc+IGVsZW1lbnQpXG4gICogQHBhcmFtIHtzdHJpbmd9IFtvYmplY3RDbGFzcz1cInZpb2xpblwiXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdENsYXNzID0gJ3Zpb2xpbicsXG4gIC8qKlxuICAqIER1cmF0aW9uIG9mIGFsbCB0cmFuc2l0aW9ucyBvZiB0aGlzIGVsZW1lbnRcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RyYW5zaXRpb25EdXJhdGlvbj0xMDAwXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRyYW5zaXRpb25EdXJhdGlvbiA9IDEwMDAsXG4gIC8qKlxuICAqIEVhc2luZyBmdW5jdGlvbiBmb3IgdHJhbnNpdGlvbnNcbiAgKiBAcGFyYW0ge2QzLmVhc2V9IFtlYXNlRnVuYz1kMy5lYXNlRXhwXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIGVhc2VGdW5jID0gZDMuZWFzZUV4cCxcblxuICAvKipcbiAgKiBUaGUga2V5IGNvbnRhaW5pbmcgdGhlIHF1YXJ0aWxlc1xuICAqIEBwYXJhbSB7c3RyaW5nfSBbcXVhcnRpbGVzS2V5PXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBxdWFydGlsZXNLZXkgPSBcInF1YXJ0aWxlc1wiLFxuICAvKipcbiAgKiBUaGUga2V5cyBjb3JyZXNwb25kaW5nIHRvIGVhY2ggcXVhcnRpbGVcbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbcXVhcnRpbGVLZXlzPVtcIlEwXCIsIFwiUTFcIiwgXCJRMlwiLCBcIlEzXCIsIFwiUTRcIl1dXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgcXVhcnRpbGVLZXlzID0gW1wiUTBcIiwgXCJRMVwiLCBcIlEyXCIsIFwiUTNcIiwgXCJRNFwiXSxcblxuICAvKipcbiAgKiBUaGUga2V5cyBvZiB0aGUgYmFyc1xuICAqIEBwYXJhbSB7c3RyaW5nW119IFt2aW9saW5LZXlzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2aW9saW5LZXlzLFxuICAvKipcbiAgKiBUaGUgdmFsdWVzIG9mIHRoZSBiYXJzXG4gICogQHBhcmFtIHtudW1iZXJbXX0gW3Zpb2xpblZhbHVlcz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmlvbGluVmFsdWVzLFxuICAvKipcbiAgKiBUaGUgb2JqZWN0U2l6ZSAoYWN0dWFsIHdpZHRoKSB1c2VkIGJ5IHRoZSBiYXJzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvYmplY3RTaXplPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvYmplY3RTaXplLFxuICAvKipcbiAgKiBUaGUgc3BhY2VyU2l6ZSAoYWN0dWFsIHdpZHRoKSB1c2VkIGJ5IHRoZSBzcGFjZXJzIGJldHdlZW4gdGhlIGJhcnNcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlclNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlclNpemUsXG5cbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgVG9vbHRpcFxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt0b29sdGlwPXRvb2x0aXAoKV1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0b29sdGlwID0gVFRpcCgpLmtleXMoW3F1YXJ0aWxlS2V5c1s0XSwgcXVhcnRpbGVLZXlzWzNdLCBxdWFydGlsZUtleXNbMl0sIHF1YXJ0aWxlS2V5c1sxXSwgcXVhcnRpbGVLZXlzWzBdXSksXG4gIHBvaW50c1Rvb2x0aXAgPSBUVGlwKCksXG4gIC8vIHBvaW50S2V5RXh0cmFjdG9yID0gZnVuY3Rpb24odmlvbGluS2V5LCB2aW9saW5EYXRhLCB2aW9saW5WYWx1ZXMpIHtyZXR1cm4gZDMua2V5cyh2aW9saW5WYWx1ZXNbdmlvbGluS2V5XS52YWx1ZXMpfSxcbiAgLy8gcG9pbnRWYWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKHBvaW50S2V5LCB2aW9saW5LZXksIHZpb2xpbkRhdGEsIHZpb2xpblZhbHVlcykge3JldHVybiB2aW9saW5WYWx1ZXNbdmlvbGluS2V5XS52YWx1ZXNbcG9pbnRLZXldfSxcblxuXG4gIC8qKlxuICAqIEZ1bmN0aW9uIHdoaWNoIGdpdmVuIHRoZSBrZXkgb2YgdGhlIHZpb2xpbiBhbmQgdGhhdCBrZXkncyBhc3NvY2lhdGVkIHZhbHVlXG4gICogcmV0dXJucyB0aGUgb2JqZWN0IGNvbnNpdGluZyBvZiB0aGUgcG9pbnRzIG9mIHRoZSB2aW9saW5cbiAgKiAoc2VlIHtAbGluayB2aW9saW4jdmlvbGluUG9pbnRzRXh0cmFjdG9yfSlcbiAgKiBAcGFyYW0ge09iamVjdH0gW3Zpb2xpblBvaW50c0V4dHJhY3Rvcj1mdW5jdGlvbih2aW9saW5LZXksIHZpb2xpbkRhdGEpIHsgcmV0dXJuIHZpb2xpbkRhdGEucG9pbnRzIH1dXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmlvbGluUG9pbnRzRXh0cmFjdG9yID0gZnVuY3Rpb24gKHZpb2xpbktleSwgdmlvbGluRGF0YSkge3JldHVybiB2aW9saW5EYXRhLnBvaW50cyB9LFxuICAvKipcbiAgKiBGdW5jdGlvbiB3aGljaCBnaXZlbiB0aGUga2V5IG9mIHRoZSBjdXJyZW50IHBvaW50IGFuZCB0aGUgb2JqZWN0IG9mIHBvaW50cyBmb3IgdGhlXG4gICogdmlvbGluLCByZXR1cm5zIHRoZSBudW1lcmljYWwgdmFsdWUgb2YgdGhlIHBvaW50XG4gICogKHNlZSB7QGxpbmsgdmlvbGluI3Zpb2xpblBvaW50VmFsdWVFeHRyYWN0b3J9KVxuICAqIEBwYXJhbSB7T2JqZWN0fSBbdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3Rvcj1mdW5jdGlvbih2aW9saW5Qb2ludEtleSwgdmlvbGluUG9pbnREYXRhKSB7IHJldHVybiB2aW9saW5Qb2ludERhdGFbdmlvbGluUG9pbnRLZXldLnZhbHVlIH1dXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKHZpb2xpblBvaW50S2V5LCB2aW9saW5Qb2ludERhdGEpIHsgcmV0dXJuIHZpb2xpblBvaW50RGF0YVt2aW9saW5Qb2ludEtleV0udmFsdWUgfVxuXG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgdmlvbGluUG9pbnRzRXh0cmFjdG9yXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2aW9saW5Qb2ludHNFeHRyYWN0b3IgPSBmdW5jdGlvbih2aW9saW5LZXksIHZpb2xpbkRhdGEpIHsgcmV0dXJuIHZpb2xpbkRhdGEucG9pbnRzIH1cbiAgICovXG4gIHZpb2xpbi52aW9saW5Qb2ludHNFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZpb2xpblBvaW50c0V4dHJhY3RvciA9IF8sIHZpb2xpbikgOiB2aW9saW5Qb2ludHNFeHRyYWN0b3I7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3JcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZpb2xpblBvaW50c0V4dHJhY3RvciA9IGZ1bmN0aW9uKHBvaW50S2V5LCB2aW9saW5LZXksIHZpb2xpbkRhdGEsIHZpb2xpblZhbHVlcykge3JldHVybiB2aW9saW5WYWx1ZXNbdmlvbGluS2V5XS52YWx1ZXNbcG9pbnRLZXldfVxuICAgKi9cbiAgdmlvbGluLnZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3IgPSBfLCB2aW9saW4pIDogdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3RvcjsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHNlbGVjdGlvbiBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBkMy5zZWxlY3Rpb259XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2VsZWN0aW9uID0gc2VsZWN0aW9uXG4gICAqL1xuICB2aW9saW4uc2VsZWN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCB2aW9saW4pIDogc2VsZWN0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBkYXRhXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNkYXRhfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBvYmplY3R9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICB2aW9saW4uZGF0YSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGF0YSA9IF8sIHZpb2xpbikgOiBkYXRhOyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBvcmllbnQgb2YgdGhlIGJveGVzXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNvcmllbnR9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG9iamVjdH1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIHZpb2xpbi5vcmllbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9yaWVudCA9IF8sIHZpb2xpbikgOiBvcmllbnQ7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jc3BhY2VYfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVggPSB1bmRlZmluZWRcbiAgICovXG4gIHZpb2xpbi5zcGFjZVggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWCA9IF8sIHZpb2xpbikgOiBzcGFjZVg7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiB2ZXJ0aWNhbCBzcGFjZSBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3NwYWNlWX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VZID0gdW5kZWZpbmVkXG4gICAqL1xuICB2aW9saW4uc3BhY2VZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVkgPSBfLCB2aW9saW4pIDogc3BhY2VZOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IHZpb2xpbiBpcyBhbGxvd2VkIHRvIGdvIGJleW9uZCBzcGVjaWZpZWQgZGltZW5zaW9uc1xuICAgKiAoc2VlIHtAbGluayB2aW9saW4jb3ZlcmZsb3dRfSlcbiAgICogQHBhcmFtIHtib29sZWFufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgYm9vbGVhbn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvdmVyZmxvd1EgPSBmYWxzZVxuICAgKi9cbiAgdmlvbGluLm92ZXJmbG93USA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3ZlcmZsb3dRID0gXywgdmlvbGluKSA6IG92ZXJmbG93UTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IHRvIHBsb3QgcG9pbnRzIHdpdGggdGhlIHZpb2xpbnNcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3BvaW50c1F9KVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBib29sZWFufVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHBvaW50c1EgPSBmYWxzZVxuICAgKi9cbiAgdmlvbGluLnBvaW50c1EgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHBvaW50c1EgPSBfLCB2aW9saW4pIDogcG9pbnRzUTsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZ3JvdXBpbmcgb2YgdGhlIGJveGVzXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNncm91cGluZ30pXG4gICAqIEBwYXJhbSB7QXJyYXlbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IEFycmF5W119XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZ3JvdXBpbmcgPSB1bmRlZmluZWRcbiAgICovXG4gIHZpb2xpbi5ncm91cGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZ3JvdXBpbmcgPSBfLCB2aW9saW4pIDogZ3JvdXBpbmc7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdmFsdWVFeHRyYWN0b3JcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3ZhbHVlRXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgdmlvbGluLnZhbHVlRXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZUV4dHJhY3RvciA9IF8sIHZpb2xpbikgOiB2YWx1ZUV4dHJhY3RvcjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzb3J0aW5nRnVuY3Rpb25cbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3NvcnRpbmdGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIHZpb2xpbi5zb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNvcnRpbmdGdW5jdGlvbiA9IF8sIHZpb2xpbikgOiBzb3J0aW5nRnVuY3Rpb247IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzY2FsZSBmb3Igd2hpY2ggdGhlIHZpb2xpbiB2YWx1ZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNzY2FsZX0pXG4gICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBkMy5zY2FsZX1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcbiAgICovXG4gIHZpb2xpbi5zY2FsZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGUgPSBfLCB2aW9saW4pIDogc2NhbGU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgc2NhbGVcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI2RvbWFpblBhZGRpbmd9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBkb21haW5QYWRkaW5nID0gMC41XG4gICAqL1xuICB2aW9saW4uZG9tYWluUGFkZGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZG9tYWluUGFkZGluZyA9IF8sIHZpb2xpbikgOiBkb21haW5QYWRkaW5nOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIG9iamVjdFNwYWNlclxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jb2JqZWN0U3BhY2VyfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0U3BhY2VyID0gMC4wNVxuICAgKi9cbiAgdmlvbGluLm9iamVjdFNwYWNlciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0U3BhY2VyID0gXywgdmlvbGluKSA6IG9iamVjdFNwYWNlcjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtaW5PYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNtaW5PYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWluT2JqZWN0U2l6ZSA9IDE1XG4gICAqL1xuICB2aW9saW4ubWluT2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWluT2JqZWN0U2l6ZSA9IF8sIHZpb2xpbikgOiBtaW5PYmplY3RTaXplOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1heE9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI21heE9iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtYXhPYmplY3RTaXplID0gNTBcbiAgICovXG4gIHZpb2xpbi5tYXhPYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtYXhPYmplY3RTaXplID0gXywgdmlvbGluKSA6IG1heE9iamVjdFNpemU7IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RTdHJva2VXaWR0aFxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jb2JqZWN0U3Ryb2tlV2lkdGh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RTdHJva2VXaWR0aCA9IDJcbiAgICovXG4gIHZpb2xpbi5vYmplY3RTdHJva2VXaWR0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0U3Ryb2tlV2lkdGggPSBfLCB2aW9saW4pIDogb2JqZWN0U3Ryb2tlV2lkdGg7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGNvbG9yRnVuY3Rpb25cbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI2NvbG9yRnVuY3Rpb259KVxuICAgKiBAcGFyYW0ge2NvbG9yRnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBjb2xvckZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGNvbG9yRnVuY3Rpb24gPSBjb2xvckZ1bmN0aW9uKClcbiAgICovXG4gIHZpb2xpbi5jb2xvckZ1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjb2xvckZ1bmN0aW9uID0gXywgdmlvbGluKSA6IGNvbG9yRnVuY3Rpb247IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgY29sb3JGdW5jdGlvblxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jY29sb3JGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7Y29sb3JGdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IGNvbG9yRnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKVxuICAgKi9cbiAgdmlvbGluLnBvaW50Q29sb3JGdW5jID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChwb2ludENvbG9yRnVuYyA9IF8sIHZpb2xpbikgOiBwb2ludENvbG9yRnVuYzsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcG9pbnRSYWRpdXNcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3BvaW50UmFkaXVzfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgcG9pbnRSYWRpdXMgPSAyXG4gICAqL1xuICB2aW9saW4ucG9pbnRSYWRpdXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHBvaW50UmFkaXVzID0gXywgdmlvbGluKSA6IHBvaW50UmFkaXVzOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHBvaW50U3Ryb2tlV2lkdGhcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3BvaW50U3Ryb2tlV2lkdGh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBwb2ludFN0cm9rZVdpZHRoID0gMlxuICAgKi9cbiAgdmlvbGluLnBvaW50U3Ryb2tlV2lkdGggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHBvaW50U3Ryb2tlV2lkdGggPSBfLCB2aW9saW4pIDogcG9pbnRTdHJva2VXaWR0aDsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFja2dyb3VuZEZpbGxcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI2JhY2tncm91bmRGaWxsfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnXG4gICAqL1xuICB2aW9saW4uYmFja2dyb3VuZEZpbGwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhY2tncm91bmRGaWxsID0gXywgdmlvbGluKSA6IGJhY2tncm91bmRGaWxsOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG5hbWVzcGFjZVxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jbmFtZXNwYWNlfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbmFtZXNwYWNlID0gJ2Qzc20tdmlvbGluJ1xuICAgKi9cbiAgdmlvbGluLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgdmlvbGluKSA6IG5hbWVzcGFjZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RDbGFzc1xuICAgKiAoc2VlIHtAbGluayB2aW9saW4jb2JqZWN0Q2xhc3N9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RDbGFzcyA9ICd0aWNrLWdyb3VwJ1xuICAgKi9cbiAgdmlvbGluLm9iamVjdENsYXNzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RDbGFzcyA9IF8sIHZpb2xpbikgOiBvYmplY3RDbGFzczsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdHJhbnNpdGlvbkR1cmF0aW9uXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiN0cmFuc2l0aW9uRHVyYXRpb259KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwXG4gICAqL1xuICB2aW9saW4udHJhbnNpdGlvbkR1cmF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0cmFuc2l0aW9uRHVyYXRpb24gPSBfLCB2aW9saW4pIDogdHJhbnNpdGlvbkR1cmF0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGVhc2VGdW5jXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNlYXNlRnVuY30pXG4gICAqIEBwYXJhbSB7ZDMuZWFzZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IGQzLmVhc2V9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwXG4gICAqL1xuICB2aW9saW4uZWFzZUZ1bmMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGVhc2VGdW5jID0gXywgdmlvbGluKSA6IGVhc2VGdW5jOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBxdWFydGlsZUtleVxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jcXVhcnRpbGVLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBxdWFydGlsZUtleSA9IFwicXVhcnRpbGVzXCJcbiAgICovXG4gIHZpb2xpbi5xdWFydGlsZUtleSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocXVhcnRpbGVLZXkgPSBfLCB2aW9saW4pIDogcXVhcnRpbGVLZXk7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcXVhcnRpbGVLZXlzXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNxdWFydGlsZUtleXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgc3RyaW5nW119XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgcXVhcnRpbGVLZXlzID0gW1wiUTBcIixcIlExXCIsXCJRMlwiLFwiUTNcIixcIlE0XCJdXG4gICAqL1xuICB2aW9saW4ucXVhcnRpbGVLZXlzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChxdWFydGlsZUtleXMgPSBfLCB2aW9saW4pIDogcXVhcnRpbGVLZXlzOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2aW9saW5LZXlzXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiN2aW9saW5LZXlzfSlcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZpb2xpbktleXMgPSB1bmRlZmluZWRcbiAgICovXG4gIHZpb2xpbi52aW9saW5LZXlzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2aW9saW5LZXlzID0gXywgdmlvbGluKSA6IHZpb2xpbktleXM7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdmlvbGluVmFsdWVzXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiN2aW9saW5WYWx1ZXN9KVxuICAgKiBAcGFyYW0ge09iamVjdFtdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgT2JqZWN0W119XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdmlvbGluVmFsdWVzID0gdW5kZWZpbmVkXG4gICAqL1xuICB2aW9saW4udmlvbGluVmFsdWVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2aW9saW5WYWx1ZXMgPSBfLCB2aW9saW4pIDogdmlvbGluVmFsdWVzOyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jb2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdFNpemUgPSB1bmRlZmluZWRcbiAgICovXG4gIHZpb2xpbi5vYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RTaXplID0gXywgdmlvbGluKSA6IG9iamVjdFNpemU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgc3BhY2VyU2l6ZVxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jc3BhY2VyU2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlclNpemUgPSB1bmRlZmluZWRcbiAgICovXG4gIHZpb2xpbi5zcGFjZXJTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZXJTaXplID0gXywgdmlvbGluKSA6IHNwYWNlclNpemU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdG9vbHRpcFxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jdG9vbHRpcH0pXG4gICAqIEBwYXJhbSB7dG9vbHRpcH0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IHRvb2x0aXB9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdG9vbHRpcCA9IHRvb2x0aXAoKVxuICAgKi9cbiAgdmlvbGluLnRvb2x0aXAgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRvb2x0aXAgPSBfLCB2aW9saW4pIDogdG9vbHRpcDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBwb2ludHNUb29sdGlwXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNwb2ludHNUb29sdGlwfSlcbiAgICogQHBhcmFtIHt0b29sdGlwfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgdG9vbHRpcH1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBwb2ludHNUb29sdGlwID0gdG9vbHRpcCgpXG4gICAqL1xuICB2aW9saW4ucG9pbnRzVG9vbHRpcCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocG9pbnRzVG9vbHRpcCA9IF8sIHZpb2xpbikgOiBwb2ludHNUb29sdGlwOyB9O1xuXG5cblxuXG5cbiAgZnVuY3Rpb24gdmlvbGluICgpIHtcbiAgICAvLyBmb3IgY29udmVuaWVuY2UgaW4gaGFuZGxpbmcgb3JpZW50YXRpb24gc3BlY2lmaWMgdmFsdWVzXG4gICAgdmFyIGhvcml6b250YWxRID0gKG9yaWVudCA9PSAnaG9yaXpvbnRhbCcpID8gdHJ1ZSA6IGZhbHNlXG4gICAgdmFyIHZlcnRpY2FsUSA9ICFob3Jpem9udGFsUVxuXG4gICAgLy8gYmFja2dyb3VuZCBjbGlwaW5nIHJlY3RhbmdsZVxuICAgIHZhciBiZ2NwUmVjdCA9IHt4OjAsIHk6MCwgd2lkdGg6IHNwYWNlWCwgaGVpZ2h0OnNwYWNlWX1cbiAgICB2YXIgY29udGFpbmVyID0gc2V0dXBDb250YWluZXIoIHNlbGVjdGlvbiwgbmFtZXNwYWNlLCBiZ2NwUmVjdCwgYmFja2dyb3VuZEZpbGwgKTtcblxuICAgIC8vIGlmIGdyb3VwaW5nIGlzIHVuZGVmaW5lZCBzb3J0IHZpb2xpbktleXMgYnkgc29ydGluZ0Z1bmN0aW9uXG4gICAgdmFyIG9yZGVyZWQgPSAoZ3JvdXBpbmcgPT0gdW5kZWZpbmVkKSA/IGQzLmtleXMoZGF0YSkuc29ydChzb3J0aW5nRnVuY3Rpb24pIDogZ3JvdXBpbmdcblxuICAgIC8vIGNvbnNvbGUubG9nKG9yZGVyZWQpXG5cbiAgICB2aW9saW5LZXlzID0gZmxhdHRlbihvcmRlcmVkKVxuXG5cbiAgICB2YXIgY2FsY1ZhbHVlcyA9IG5lZWRlZFZpb2xpblZhbHVlcygpXG4gICAgLmhvcml6b250YWxRKGhvcml6b250YWxRKVxuICAgIC5xdWFydGlsZUtleXMocXVhcnRpbGVLZXlzKVxuICAgIC52aW9saW5Qb2ludHNFeHRyYWN0b3IodmlvbGluUG9pbnRzRXh0cmFjdG9yKVxuICAgIC52aW9saW5Qb2ludFZhbHVlRXh0cmFjdG9yKHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3IpXG5cblxuXG4gICAgLy8gYXVnbWVudCB2YWx1c1xuICAgIHZpb2xpbktleXMubWFwKGZ1bmN0aW9uKHZrLCBpKXsgY2FsY1ZhbHVlcyh2aywgZGF0YSkgfSlcblxuICAgIHZhciBudW1iZXJPZk9iamVjdHMgPSB2aW9saW5LZXlzLmxlbmd0aFxuXG4gICAgdmFyIG1pbiA9IFtdLmNvbmNhdCguLi52aW9saW5LZXlzLm1hcChmdW5jdGlvbihrLCBpKXtyZXR1cm4gZGF0YVtrXS5xdWFydGlsZXNbcXVhcnRpbGVLZXlzWzBdXX0pKVxuICAgIHZhciBtYXggPSBbXS5jb25jYXQoLi4udmlvbGluS2V5cy5tYXAoZnVuY3Rpb24oaywgaSl7cmV0dXJuIGRhdGFba10ucXVhcnRpbGVzW3F1YXJ0aWxlS2V5c1txdWFydGlsZUtleXMubGVuZ3RoIC0gMV1dfSkpXG4gICAgdmFyIGV4dGVudCA9IFtNYXRoLm1pbiguLi5taW4pIC0gZG9tYWluUGFkZGluZywgTWF0aC5tYXgoLi4ubWF4KSArIGRvbWFpblBhZGRpbmddXG4gICAgLy8gY29uc29sZS5sb2coZXh0ZW50LCB2aW9saW5WYWx1ZXMsIG9yZGVyZWQpXG5cbiAgICAvLyBzZXQgdGhlIHNjYWxlXG4gICAgc2NhbGUuZG9tYWluKGV4dGVudCkucmFuZ2UoaG9yaXpvbnRhbFEgPyBbMCxzcGFjZVldIDogWzAsIHNwYWNlWF0pXG4gICAgdmFyIHNwYWNlID0gaG9yaXpvbnRhbFEgPyBzcGFjZVggOiBzcGFjZVlcbiAgICAvLyBjYWxjdWxhdGUgb2JqZWN0IHNpemVcbiAgICBvYmplY3RTaXplID0gY2FsY3VsYXRlV2lkdGhPZk9iamVjdChzcGFjZSwgbnVtYmVyT2ZPYmplY3RzLCBtaW5PYmplY3RTaXplLCBtYXhPYmplY3RTaXplLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICAvLyBjYWxjdWxhdGUgc3BhY2VyIHNpemUgaWYgbmVlZGVkXG4gICAgc3BhY2VyU2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXIob3JkZXJlZCwgc3BhY2UsIG9iamVjdFNpemUsIG51bWJlck9mT2JqZWN0cywgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgLy8gbWFrZSB0aGUgbmVzdGVkIGdyb3Vwc1xuICAgIHZhciBzcGFjZXJGdW5jdGlvbiA9IGdyb3VwaW5nU3BhY2VyKClcbiAgICAuaG9yaXpvbnRhbFEoaG9yaXpvbnRhbFEpLnNjYWxlKHNjYWxlKS5tb3ZlYnkoJ2NhdGVnb3J5JykubnVtYmVyT2ZPYmplY3RzKG51bWJlck9mT2JqZWN0cylcbiAgICAub2JqZWN0Q2xhc3Mob2JqZWN0Q2xhc3MpLm9iamVjdFNpemUob2JqZWN0U2l6ZSkuc3BhY2VyU2l6ZShzcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcbiAgICAubmFtZXNwYWNlKG5hbWVzcGFjZSlcblxuICAgIC8vIG1vdmUgc3R1ZmZcbiAgICBzcGFjZXJGdW5jdGlvbihjb250YWluZXIsIG9yZGVyZWQsIDApXG4gICAgLy8gY29uc29sZS5sb2codmlvbGluS2V5cywgb3JkZXJlZCwgY29udGFpbmVyLnNlbGVjdEFsbCgnZzpub3QoLnRvLXJlbW92ZSkuJytvYmplY3RDbGFzcykubm9kZXMoKSlcblxuICAgIC8vIGZvciBjb2xvciBmdW5jdGlvblxuICAgIHZhciBwYXJlbnRJbmRleEFycmF5ID0gW11cbiAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnOm5vdCgudG8tcmVtb3ZlKS4nK29iamVjdENsYXNzKVxuICAgIC5lYWNoKGZ1bmN0aW9uKGQsIGkpe2lmIChoYXNRKHZpb2xpbktleXMsIGQpKXsgcGFyZW50SW5kZXhBcnJheS5wdXNoKE51bWJlcihkMy5zZWxlY3QodGhpcykuYXR0cigncGFyZW50LWluZGV4JykpKX19KVxuXG4gICAgLy8gdXBkYXRlIGNvbG9yIGZ1bmN0aW9uXG4gICAgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24uY29sb3JCeSgpID09ICdpbmRleCdcbiAgICA/IGNvbG9yRnVuY3Rpb24uZGF0YUV4dGVudChbMCwgTWF0aC5tYXgoLi4ucGFyZW50SW5kZXhBcnJheSldKVxuICAgIDogY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KGV4dGVudClcblxuICAgIC8qIHZpb2xpaW4gc3BlY2lmaWMgbmVlZHMgKi9cblxuXG4gICAgdmFyIGZyZXF1ZW5jeU1heCA9IE1hdGgubWF4KC4uLltdLmNvbmNhdCguLi52aW9saW5LZXlzLm1hcChmdW5jdGlvbihrLCBpKXtyZXR1cm4gZDMubWF4KGRhdGFba10uZnJlcXVlbmNpZXMpfSkpKVxuICAgIHZhciB2U2NhbGUgPSBkMy5zY2FsZUxpbmVhcigpLmRvbWFpbihbMCwgZnJlcXVlbmN5TWF4XSkucmFuZ2UoWzAsIG9iamVjdFNpemUgLyAyXSlcblxuICAgIHZhciBsQXJlYSA9IGQzLmxpbmUoKVxuICAgIC54KGZ1bmN0aW9uKGQsIGkpeyByZXR1cm4gaG9yaXpvbnRhbFEgPyAtdlNjYWxlKGQueCkgOiBzY2FsZShkLngpfSlcbiAgICAueShmdW5jdGlvbihkLCBpKXsgcmV0dXJuIGhvcml6b250YWxRID8gc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKGQueSkgOiAtdlNjYWxlKGQueSl9KVxuICAgIC5jdXJ2ZShkMy5jdXJ2ZUJhc2lzKVxuICAgIHZhciByQXJlYSA9IGQzLmxpbmUoKVxuICAgIC54KGZ1bmN0aW9uKGQsIGkpeyByZXR1cm4gaG9yaXpvbnRhbFEgPyB2U2NhbGUoZC54KSA6IHNjYWxlKGQueCl9KVxuICAgIC55KGZ1bmN0aW9uKGQsIGkpeyByZXR1cm4gaG9yaXpvbnRhbFEgPyBzY2FsZShleHRlbnRbMV0pIC0gc2NhbGUoZC55KSA6IHZTY2FsZShkLnkpfSlcbiAgICAuY3VydmUoZDMuY3VydmVCYXNpcylcblxuXG5cblxuXG5cbiAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnOm5vdCgudG8tcmVtb3ZlKS4nK29iamVjdENsYXNzKS5lYWNoKGZ1bmN0aW9uKGtleSwgaSl7XG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKSxcbiAgICAgIGN1cnJlbnREYXRhID0gZGF0YVtrZXldXG4gICAgICAvLyBuZWVkZWQgYmVjYXVzZSBidWcgaW4gc2VsZWN0aW5nIC50by1yZW1vdmVcbiAgICAgIGlmICghaGFzUSh2aW9saW5LZXlzLCBrZXkpKSB7cmV0dXJufVxuICAgICAgdmFyXG4gICAgICBpID0gdC5hdHRyKCdwYXJlbnQtaW5kZXgnKSA9PSB1bmRlZmluZWQgPyBpIDogdC5hdHRyKCdwYXJlbnQtaW5kZXgnKSxcbiAgICAgIGZpbGxDb2xvciA9IGNvbG9yRnVuY3Rpb24oa2V5LCBjdXJyZW50RGF0YSwgaSwgJ2ZpbGwnKSwgLy8gcHJldmVudCBkdXBsaWNhdGUgY29tcHV0YXRpb25cbiAgICAgIHN0cm9rZUNvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIGN1cnJlbnREYXRhLCBpLCAnc3Ryb2tlJyksXG4gICAgICBhcmVhID0gc2FmZVNlbGVjdCh0LCAnZycsICdhcmVhJyksXG4gICAgICBsYSA9IHNhZmVTZWxlY3QoYXJlYSwgJ3BhdGgnLCAnbGVmdCcpLFxuICAgICAgcmEgPSBzYWZlU2VsZWN0KGFyZWEsICdwYXRoJywgJ3JpZ2h0JyksXG4gICAgICBxdWFydHMgPSBzYWZlU2VsZWN0KHQsICdnJywgJ3F1YXJ0cycpLFxuICAgICAgbHEzID0gc2FmZVNlbGVjdChxdWFydHMsICdsaW5lJywgJ3EzJyksXG4gICAgICBscTEgPSBzYWZlU2VsZWN0KHF1YXJ0cywgJ2xpbmUnLCAncTEnKSxcbiAgICAgIHEzID0gY3VycmVudERhdGEucXVhcnRpbGVzW3F1YXJ0aWxlS2V5c1szXV0sXG4gICAgICBxMiA9IGN1cnJlbnREYXRhLnF1YXJ0aWxlc1txdWFydGlsZUtleXNbMl1dLFxuICAgICAgcTEgPSBjdXJyZW50RGF0YS5xdWFydGlsZXNbcXVhcnRpbGVLZXlzWzFdXVxuXG4gICAgICB0LmF0dHIoJ3RyYW5zZm9ybScsIGhvcml6b250YWxRID8gJ3RyYW5zbGF0ZSgnK29iamVjdFNpemUgLyAyKycsMCknIDogJ3RyYW5zbGF0ZSgwLCcrb2JqZWN0U2l6ZSAvIDIrJyknICApXG4gICAgICAvLyBkcmF3IGN1cnZlXG4gICAgICBsYS50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5hdHRyKCdkJywgZnVuY3Rpb24oZGQsIGlpKXsgcmV0dXJuIGxBcmVhKGN1cnJlbnREYXRhLmNvbnRvdXIpfSlcbiAgICAgIC5hdHRyKCdmaWxsJywgZmlsbENvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZScsIHN0cm9rZUNvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIG9iamVjdFN0cm9rZVdpZHRoKVxuXG4gICAgICByYS50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5hdHRyKCdkJywgZnVuY3Rpb24oZGQsIGlpKXsgcmV0dXJuIHJBcmVhKGN1cnJlbnREYXRhLmNvbnRvdXIpfSlcbiAgICAgIC5hdHRyKCdmaWxsJywgZmlsbENvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZScsIHN0cm9rZUNvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIG9iamVjdFN0cm9rZVdpZHRoKVxuXG4gICAgICBhcmVhLm5vZGUoKS5hZGRFdmVudExpc3RlbmVyKCdtb3VzZW92ZXInLCBmdW5jdGlvbihkZCwgaWkpe1xuICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnLicrb2JqZWN0Q2xhc3MpLnN0eWxlKCdvcGFjaXR5JywgMC4yKVxuICAgICAgICB0LnN0eWxlKCdvcGFjaXR5JywgMSlcbiAgICAgICAgbGEuYXR0cignc3Ryb2tlLXdpZHRoJyxvYmplY3RTdHJva2VXaWR0aCoyKVxuICAgICAgICByYS5hdHRyKCdzdHJva2Utd2lkdGgnLG9iamVjdFN0cm9rZVdpZHRoKjIpXG4gICAgICB9KVxuICAgICAgYXJlYS5ub2RlKCkuYWRkRXZlbnRMaXN0ZW5lcignbW91c2VvdXQnLCBmdW5jdGlvbihkZCwgaWkpe1xuICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnLicrb2JqZWN0Q2xhc3MpLnN0eWxlKCdvcGFjaXR5JywgMSlcbiAgICAgICAgbGEuYXR0cignc3Ryb2tlLXdpZHRoJyxvYmplY3RTdHJva2VXaWR0aClcbiAgICAgICAgcmEuYXR0cignc3Ryb2tlLXdpZHRoJyxvYmplY3RTdHJva2VXaWR0aClcbiAgICAgIH0pXG5cbiAgICAgIGlmIChwb2ludHNRKSB7XG4gICAgICAgIHZhciBwdHNDb250YWluZXIgPSBzYWZlU2VsZWN0KHQsICdnJywgJ3BvaW50cycpXG4gICAgICAgIHZhciBwdHMgPSBwdHNDb250YWluZXIuc2VsZWN0QWxsKCcucG9pbnQnKS5kYXRhKGN1cnJlbnREYXRhLnBvaW50S2V5cylcbiAgICAgICAgcHRzLm9uKCdtb3VzZW92ZXInLCBudWxsKVxuXG5cbiAgICAgICAgdmFyIHB0c0V4aXQgPSBwdHMuZXhpdCgpLnRyYW5zaXRpb24oKS5lYXNlKGVhc2VGdW5jKS5kdXJhdGlvbih0cmFuc2l0aW9uRHVyYXRpb24pXG4gICAgICAgIC5hdHRyKCdyJywgMClcbiAgICAgICAgLmF0dHIoJ2N5JywgaG9yaXpvbnRhbFEgPyBzY2FsZShleHRlbnRbMV0pIC0gc2NhbGUocTIpIDogdlNjYWxlKDApKVxuICAgICAgICAuYXR0cignY3gnLCBob3Jpem9udGFsUSA/IHZTY2FsZSgwKSA6IHNjYWxlKHEyKSkucmVtb3ZlKClcblxuICAgICAgICB2YXIgcHRzRW50ZXIgPSBwdHMuZW50ZXIoKS5hcHBlbmQoJ2NpcmNsZScpLmF0dHIoJ2NsYXNzJywgJ3BvaW50JykuYXR0cigncicsIDApXG4gICAgICAgIC5hdHRyKCdjeCcsIGhvcml6b250YWxRID8gMCA6IHNjYWxlKHEyKSlcbiAgICAgICAgLmF0dHIoJ2N5JywgaG9yaXpvbnRhbFEgPyBzY2FsZShxMikgOiAwKVxuXG4gICAgICAgIHB0cyA9IHB0cy5tZXJnZShwdHNFbnRlcilcblxuICAgICAgICAvLyBjb25zb2xlLmxvZyhwb2ludHNUb29sdGlwLmhlYWRlcigpKVxuXG4gICAgICAgIHZhciBwVFRpcHMgPSBUVGlwKCkuc2VsZWN0aW9uKHB0cykuZGF0YSh2aW9saW5Qb2ludHNFeHRyYWN0b3Ioa2V5LCBjdXJyZW50RGF0YSkpXG4gICAgICAgIC5oZWFkZXIocG9pbnRzVG9vbHRpcC5oZWFkZXIoKSlcbiAgICAgICAgLmtleXMocG9pbnRzVG9vbHRpcC5rZXlzKCkpXG4gICAgICAgIC52YWx1ZXMocG9pbnRzVG9vbHRpcC52YWx1ZXMoKSlcblxuICAgICAgICBwVFRpcHMoKVxuXG4gICAgICAgIHZhciBwTWluID0gZDMubWluKGN1cnJlbnREYXRhLnBvaW50VmFsdWVzKSwgcE1heCA9IGQzLm1heChjdXJyZW50RGF0YS5wb2ludFZhbHVlcylcblxuXG5cbiAgICAgICAgcHRzLnRyYW5zaXRpb24oKS5kdXJhdGlvbih0cmFuc2l0aW9uRHVyYXRpb24pLmVhc2UoZWFzZUZ1bmMpLmF0dHIoJ3InLCBwb2ludFJhZGl1cylcbiAgICAgICAgLmF0dHIoJ2N5JywgZnVuY3Rpb24ocG9pbnRLZXksIGlpKXtcbiAgICAgICAgICB2YXIgZGQgPSBjdXJyZW50RGF0YS5wb2ludFZhbHVlc1tpaV1cbiAgICAgICAgICBpZiAoaG9yaXpvbnRhbFEpIHsgcmV0dXJuIHNjYWxlKGV4dGVudFsxXSkgLSBzY2FsZShkZCkgfVxuICAgICAgICAgIHZhciBqID0gd2hpY2hCaW4oY3VycmVudERhdGEuYmlubmVkLCBkZClcbiAgICAgICAgICB2YXIgciA9IE1hdGgucmFuZG9tKClcbiAgICAgICAgICB2YXIgbiA9IHZTY2FsZShyICogY3VycmVudERhdGEuZnJlcXVlbmNpZXNbal0gKiAwLjUpXG4gICAgICAgICAgdmFyIGsgPSBNYXRoLnJhbmRvbSgpID4gMC41ID8gbiA6IC1uXG4gICAgICAgICAgcmV0dXJuIGtcbiAgICAgICAgfSlcbiAgICAgICAgLmF0dHIoJ2N4JywgZnVuY3Rpb24ocG9pbnRLZXksIGlpKXtcbiAgICAgICAgICB2YXIgZGQgPSBjdXJyZW50RGF0YS5wb2ludFZhbHVlc1tpaV1cbiAgICAgICAgICBpZiAoaG9yaXpvbnRhbFEpIHtcbiAgICAgICAgICAgIHZhciBqID0gd2hpY2hCaW4oY3VycmVudERhdGEuYmlubmVkLCBkZClcbiAgICAgICAgICAgIHZhciByID0gTWF0aC5yYW5kb20oKVxuICAgICAgICAgICAgdmFyIG4gPSB2U2NhbGUociAqIGN1cnJlbnREYXRhLmZyZXF1ZW5jaWVzW2pdICogMC41KVxuICAgICAgICAgICAgdmFyIGsgPSBNYXRoLnJhbmRvbSgpID4gMC41ID8gbiA6IC1uXG4gICAgICAgICAgICByZXR1cm4ga1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gc2NhbGUoZGQpXG4gICAgICAgIH0pXG4gICAgICAgIC5hdHRyKCdzdHJva2UnLCBmdW5jdGlvbihkZCwgaWkpIHsgdmFyIGRkID0gY3VycmVudERhdGEucG9pbnRWYWx1ZXNbaWldOyByZXR1cm4gcG9pbnRDb2xvckZ1bmMoZGQsICdzdHJva2UnLCBzdHJva2VDb2xvciwgcE1pbiwgcE1heCkgfSlcbiAgICAgICAgLmF0dHIoJ2ZpbGwnICAsIGZ1bmN0aW9uKGRkLCBpaSkgeyB2YXIgZGQgPSBjdXJyZW50RGF0YS5wb2ludFZhbHVlc1tpaV07IHJldHVybiBwb2ludENvbG9yRnVuYyhkZCwgJ2ZpbGwnICAsIHN0cm9rZUNvbG9yLCBwTWluLCBwTWF4KSB9KVxuICAgICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgcG9pbnRTdHJva2VXaWR0aClcblxuICAgICAgICBwdHNDb250YWluZXIuc2VsZWN0QWxsKCdjaXJjbGUucG9pbnQnKS5vbignbW91c2VvdmVyJywgZnVuY3Rpb24oZGQsIGlpKXtcbiAgICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnLicrb2JqZWN0Q2xhc3MpLnN0eWxlKCdvcGFjaXR5JywgMC4yKVxuICAgICAgICAgIHQuc3R5bGUoJ29wYWNpdHknLCAxKVxuICAgICAgICAgIGxhLmF0dHIoJ3N0cm9rZS13aWR0aCcsb2JqZWN0U3Ryb2tlV2lkdGgqMilcbiAgICAgICAgICByYS5hdHRyKCdzdHJva2Utd2lkdGgnLG9iamVjdFN0cm9rZVdpZHRoKjIpXG5cbiAgICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCcucG9pbnQnKS5zdHlsZSgnb3BhY2l0eScsIDAuMilcbiAgICAgICAgICBkMy5zZWxlY3QodGhpcykuc3R5bGUoJ29wYWNpdHknLCAxKS5hdHRyKCdyJywgcG9pbnRSYWRpdXMgKiAyKS5hdHRyKCdzdHJva2Utd2lkdGgnLHBvaW50U3Ryb2tlV2lkdGgqMilcbiAgICAgICAgfSlcbiAgICAgICAgcHRzQ29udGFpbmVyLnNlbGVjdEFsbCgnY2lyY2xlLnBvaW50Jykub24oJ21vdXNlb3V0JywgZnVuY3Rpb24oZGQsIGlpKXtcbiAgICAgICAgICB2YXIgZSA9IGRvY3VtZW50LmNyZWF0ZUV2ZW50KCdTVkdFdmVudHMnKVxuICAgICAgICAgIGUuaW5pdEV2ZW50KCdtb3VzZW91dCcsdHJ1ZSx0cnVlKTtcbiAgICAgICAgICBhcmVhLm5vZGUoKS5kaXNwYXRjaEV2ZW50KGUpXG5cbiAgICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCcucG9pbnQnKS5zdHlsZSgnb3BhY2l0eScsIDEpXG4gICAgICAgICAgZDMuc2VsZWN0KHRoaXMpLmF0dHIoJ3N0cm9rZS13aWR0aCcsIHBvaW50U3Ryb2tlV2lkdGgpLmF0dHIoJ3InLCBwb2ludFJhZGl1cylcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICAgIGVsc2Uge1xuICAgICAgICBjVi5zZWxlY3RBbGwoJy5wb2ludCcpXG4gICAgICAgIC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgICAuYXR0cigncicsIDApXG4gICAgICAgIC5hdHRyKCdjeScsIGhvcml6b250YWxRID8gc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHEyKSA6IHZTY2FsZSgwKSlcbiAgICAgICAgLmF0dHIoJ2N4JywgaG9yaXpvbnRhbFEgPyB2U2NhbGUoMCkgOiBzY2FsZShxMikpXG4gICAgICAgIC5yZW1vdmUoKVxuICAgICAgfVxuXG5cbiAgICB9KVxuXG5cbiAgICB0b29sdGlwLnNlbGVjdGlvbihjb250YWluZXIuc2VsZWN0QWxsKCdnOm5vdCgudG8tcmVtb3ZlKS4nK29iamVjdENsYXNzICsgJyAuYXJlYScpKVxuICAgIGlmICh0b29sdGlwLmRhdGEoKSA9PSB1bmRlZmluZWQpIHt0b29sdGlwLmRhdGEoZGF0YSl9XG4gICAgdG9vbHRpcCgpXG4gICAgaWYgKHRvb2x0aXAudmFsdWVzKCkgPT0gdW5kZWZpbmVkKSB7XG4gICAgICB0b29sdGlwLnZhbHVlcyhbXG4gICAgICAgIGZ1bmN0aW9uKGN1cnJlbnREYXRhLCB0b29sdGlwS2V5KXsgcmV0dXJuIGN1cnJlbnREYXRhWydxdWFydGlsZXMnXVt0b29sdGlwS2V5XSB9LFxuICAgICAgICBmdW5jdGlvbihjdXJyZW50RGF0YSwgdG9vbHRpcEtleSl7IHJldHVybiBjdXJyZW50RGF0YVsncXVhcnRpbGVzJ11bdG9vbHRpcEtleV0gfSxcbiAgICAgICAgZnVuY3Rpb24oY3VycmVudERhdGEsIHRvb2x0aXBLZXkpeyByZXR1cm4gY3VycmVudERhdGFbJ3F1YXJ0aWxlcyddW3Rvb2x0aXBLZXldIH0sXG4gICAgICAgIGZ1bmN0aW9uKGN1cnJlbnREYXRhLCB0b29sdGlwS2V5KXsgcmV0dXJuIGN1cnJlbnREYXRhWydxdWFydGlsZXMnXVt0b29sdGlwS2V5XSB9LFxuICAgICAgICBmdW5jdGlvbihjdXJyZW50RGF0YSwgdG9vbHRpcEtleSl7IHJldHVybiBjdXJyZW50RGF0YVsncXVhcnRpbGVzJ11bdG9vbHRpcEtleV0gfVxuICAgICAgXSlcblxuICAgIH1cblxuICB9XG5cbiAgcmV0dXJuIHZpb2xpblxufVxuXG5cblxuXG5cbi8qKlxuKiBQcm9kdWNlcyB0aGUgZnVuY3Rpb24gd2hpY2ggbWFuaXB1bGF0ZXMgdGhlIHZpb2xpbiBkYXRhIHRvIGhhdmUgdmFsdWVzIG5lZWRlZFxuKiBmb3IgcmVuZGVyaW5nIHRoZSB2aW9saW5zIGFzIHN2Zy5cbiogQHJldHVybnMge2Z1bmN0aW9ufSBjYWxjdWxhdGVWaW9saW5WYWx1ZXNcbiogQG5hbWVzcGFjZSBuZWVkZWRWaW9saW5WYWx1ZXNcbiovXG5mdW5jdGlvbiBuZWVkZWRWaW9saW5WYWx1ZXMoKSB7XG4gIHZhclxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0aGUgb3JpZW50YXRpb24gb2YgdGhlIHZpb2xpbnMgYXJlIGhvcml6b250YWxcbiAgKiAoc2VlIHtAbGluayB2aW9saW4jb3JpZW50fSlcbiAgKiBAcGFyYW0ge09iamVjdH0gW2hvcml6b250YWxRPXRydWVdXG4gICogQG1lbWJlcm9mIG5lZWRlZFZpb2xpblZhbHVlcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgaG9yaXpvbnRhbFEgPSB0cnVlLFxuICAvKipcbiAgKiBLZXlzIHRvIGJlIHB1dCBpbnRvIHRoZSBxdWFydGlsZXMgaWYgdGhleSBuZWVkIHRvIGJlIGNhbGN1bGF0ZWQuXG4gICogKHNlZSB7QGxpbmsgdmlvbGluI3F1YXJ0aWxlS2V5c30pXG4gICogQHBhcmFtIHtPYmplY3R9IFtxdWFydGlsZUtleXM9WydRMCcsICdRMScsICdRMicsICdRMycsICdRNCddXVxuICAqIEBtZW1iZXJvZiBuZWVkZWRWaW9saW5WYWx1ZXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHF1YXJ0aWxlS2V5cyA9IFsnUTAnLCAnUTEnLCAnUTInLCAnUTMnLCAnUTQnXSxcbiAgLyoqXG4gICogRnVuY3Rpb24gd2hpY2ggZ2l2ZW4gdGhlIGtleSBvZiB0aGUgdmlvbGluIGFuZCB0aGF0IGtleSdzIGFzc29jaWF0ZWQgdmFsdWVcbiAgKiByZXR1cm5zIHRoZSBvYmplY3QgY29uc2l0aW5nIG9mIHRoZSBwb2ludHMgb2YgdGhlIHZpb2xpblxuICAqIChzZWUge0BsaW5rIHZpb2xpbiN2aW9saW5Qb2ludHNFeHRyYWN0b3J9KVxuICAqIEBwYXJhbSB7T2JqZWN0fSBbdmlvbGluUG9pbnRzRXh0cmFjdG9yPWZ1bmN0aW9uKHZpb2xpbktleSwgdmlvbGluRGF0YSkgeyByZXR1cm4gdmlvbGluRGF0YS5wb2ludHMgfV1cbiAgKiBAbWVtYmVyb2YgbmVlZGVkVmlvbGluVmFsdWVzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2aW9saW5Qb2ludHNFeHRyYWN0b3IsXG4gIC8qKlxuICAqIEZ1bmN0aW9uIHdoaWNoIGdpdmVuIHRoZSBrZXkgb2YgdGhlIGN1cnJlbnQgcG9pbnQgYW5kIHRoZSBvYmplY3Qgb2YgcG9pbnRzIGZvciB0aGVcbiAgKiB2aW9saW4sIHJldHVybnMgdGhlIG51bWVyaWNhbCB2YWx1ZSBvZiB0aGUgcG9pbnRcbiAgKiAoc2VlIHtAbGluayB2aW9saW4jdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3Rvcn0pXG4gICogQHBhcmFtIHtPYmplY3R9IFt2aW9saW5Qb2ludFZhbHVlRXh0cmFjdG9yPWZ1bmN0aW9uKHZpb2xpblBvaW50S2V5LCB2aW9saW5Qb2ludERhdGEpIHsgcmV0dXJuIHZpb2xpblBvaW50RGF0YVt2aW9saW5Qb2ludEtleV0udmFsdWUgfV1cbiAgKiBAbWVtYmVyb2YgbmVlZGVkVmlvbGluVmFsdWVzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2aW9saW5Qb2ludFZhbHVlRXh0cmFjdG9yXG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGhvcml6b250YWxRXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNvcmllbnR9KVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtjYWxjdWxhdGVWaW9saW5WYWx1ZXMgfCBib29sZWFufVxuICAgKiBAbWVtYmVyb2YgY2FsY3VsYXRlVmlvbGluVmFsdWVzXG4gICAqIEBwcm9wZXJ0eVxuICAgKlxuICAgKiBieSBkZWZhdWx0IGhvcml6b250YWxRID0gdHJ1ZVxuICAgKi9cbiAgY2FsY3VsYXRlVmlvbGluVmFsdWVzLmhvcml6b250YWxRID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChob3Jpem9udGFsUT1fLCBjYWxjdWxhdGVWaW9saW5WYWx1ZXMpIDogaG9yaXpvbnRhbFEgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHF1YXJ0aWxlS2V5c1xuICAgKiAoc2VlIHtAbGluayB2aW9saW4jcXVhcnRpbGVLZXlzfSlcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2NhbGN1bGF0ZVZpb2xpblZhbHVlcyB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgY2FsY3VsYXRlVmlvbGluVmFsdWVzXG4gICAqIEBwcm9wZXJ0eVxuICAgKlxuICAgKiBieSBkZWZhdWx0IHF1YXJ0aWxlS2V5cyA9IFtcIlEwXCIsXCJRMVwiLFwiUTJcIixcIlEzXCIsXCJRNFwiXVxuICAgKi9cbiAgY2FsY3VsYXRlVmlvbGluVmFsdWVzLnF1YXJ0aWxlS2V5cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocXVhcnRpbGVLZXlzPV8sIGNhbGN1bGF0ZVZpb2xpblZhbHVlcykgOiBxdWFydGlsZUtleXMgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHZpb2xpblBvaW50c0V4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jdmlvbGluUG9pbnRzRXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2NhbGN1bGF0ZVZpb2xpblZhbHVlcyB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgY2FsY3VsYXRlVmlvbGluVmFsdWVzXG4gICAqIEBwcm9wZXJ0eVxuICAgKlxuICAgKiBieSBkZWZhdWx0IHZpb2xpblBvaW50c0V4dHJhY3RvciA9IGZ1bmN0aW9uKHZpb2xpbktleSwgdmlvbGluRGF0YSkgeyByZXR1cm4gdmlvbGluRGF0YS5wb2ludHMgfVxuICAgKi9cbiAgY2FsY3VsYXRlVmlvbGluVmFsdWVzLnZpb2xpblBvaW50c0V4dHJhY3RvciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodmlvbGluUG9pbnRzRXh0cmFjdG9yPV8sIGNhbGN1bGF0ZVZpb2xpblZhbHVlcykgOiB2aW9saW5Qb2ludHNFeHRyYWN0b3IgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3JcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3Zpb2xpblBvaW50VmFsdWVFeHRyYWN0b3J9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y2FsY3VsYXRlVmlvbGluVmFsdWVzIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBjYWxjdWxhdGVWaW9saW5WYWx1ZXNcbiAgICogQHByb3BlcnR5XG4gICAqXG4gICAqIGJ5IGRlZmF1bHQgdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKHZpb2xpblBvaW50S2V5LCB2aW9saW5Qb2ludERhdGEpIHsgcmV0dXJuIHZpb2xpblBvaW50RGF0YVt2aW9saW5Qb2ludEtleV0udmFsdWUgfVxuICAgKi9cbiAgY2FsY3VsYXRlVmlvbGluVmFsdWVzLnZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3I9XywgY2FsY3VsYXRlVmlvbGluVmFsdWVzKSA6IHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3IgfVxuXG5cbiAgLyoqXG4gICogVGhlIGZ1bmN0aW9uIHByb2R1Y2VkIGJ5IG5lZWRlZFZpb2xpblZhbHVlcy5cbiAgKlxuICAqIEFkZHMgdGhlIGRhdGEgbmVlZCB0byByZW5kZXIgdGhlIHZpb2xpbiBhcyBhbiBzdmdcbiAgKiBAcGFyYW0ge3N0cmluZ30gdmlvbGluS2V5IHRoZSBrZXkgb2YgdGhlIGN1cnJlbnQgdmlvbGluXG4gICogQHBhcmFtIHtvYmplY3R9IGRhdGEgdGhlIG9iamVjdCBjb25zaXN0aW5nIG9mIHZpb2xpbktleSAtIHZhbHVlcyAodmlvbGluIGRhdGEpIHBhaXJzXG4gICogQHJldHVybnMge25vbmV9IHRoaXMgZnVuY3Rpb24gbWFuaXB1bGF0ZXMgdGhlIHBhc3NlZCBkYXRhIG9iamVjdCBhZGRpbmcgdGhlIGZvbGxvd2luZyBrZXlzXG4gICpcbiAgKiBkYXRhW3Zpb2xpbktleV0uYmlubmVkIC8vIHRoZSBiaW5uZWQgdmFsdWVzIG9mIHRoZSBwb2ludHNcbiAgKlxuICAqIGRhdGFbdmlvbGluS2V5XS5mcmVxdWVuY2llcyAvLyB0aGUgbGlzdCBjb25zaXN0aW5nIG9mIHRoZSBsZW5ndGggb2YgZWFjaCBiaW5cbiAgKlxuICAqIGRhdGFbdmlvbGluS2V5XS5jb250b3VyIC8vIHRoZSBwb2ludHMgZGVwaWN0aW5nIHRoZSBjb250b3VyIG9mIDEvMiBvZiB0aGUgdmlvbGluXG4gICpcbiAgKiBkYXRhW3Zpb2xpbktleV0ucXVhcnRpbGVzIC8vIHRoZSBxdWFydGlsZXMgb2YgdGhlIHBvaW50cycgdmFsdWVzXG4gICpcbiAgKiBkYXRhW3Zpb2xpbktleV0ucG9pbnRLZXlzIC8vIHRoZSBrZXlzIGFzc29jaWF0ZWQgd2l0aCB0aGUgcG9pbnRzXG4gICpcbiAgKiBkYXRhW3Zpb2xpbktleV0ucG9pbnRWYWx1ZXMgLy8gdGhlIG51bWVyaWNhbCB2YWx1ZXMgb2YgdGhlIHBvaW50c1xuICAqXG4gICogQG1lbWJlcm9mIG5lZWRlZFZpb2xpblZhbHVlcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZnVuY3Rpb24gY2FsY3VsYXRlVmlvbGluVmFsdWVzKHZpb2xpbktleSwgZGF0YSkge1xuICAgIC8vIGRhdGEgZm9yIHRoZSBjdXJyZW50IHZpb2xpblxuICAgIHZhciB2aW9saW5EYXRhID0gZGF0YVt2aW9saW5LZXldO1xuICAgIC8vIHRoZSBvYmplY3Qgb2YgcG9pbnRzXG4gICAgdmFyIHZpb2xpblBvaW50cyA9IHZpb2xpblBvaW50c0V4dHJhY3Rvcih2aW9saW5LZXksIHZpb2xpbkRhdGEpO1xuICAgIC8vXG4gICAgdmFyIHZpb2xpblBvaW50c0tleXMgPSBkMy5rZXlzKHZpb2xpblBvaW50cyk7XG4gICAgLy8gdGhlIG51bWVyaWNhbCB2YWx1ZXMgb2YgdGhvc2UgcG9pbnRzXG4gICAgdmFyIHZpb2xpblBvaW50c1ZhbHVlcyA9IHZpb2xpblBvaW50c0tleXMubWFwKGZ1bmN0aW9uKHBrLCBpKXtyZXR1cm4gdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3RvcihwaywgdmlvbGluUG9pbnRzKX0pXG5cbiAgICAvLyBxdWFydGlsZXMgb2YgdGhvc2UgcG9pbnRzXG4gICAgdmFyIHBvaW50UXVhcnRpbGVzID0gcXVhcnRpbGVzKHZpb2xpblBvaW50c1ZhbHVlcywgcXVhcnRpbGVLZXlzKVxuXG4gICAgLy8gYmlubmVkIHBvaW50c1xuICAgIHZhciBiaW5uZWQgPSBkMy5oaXN0b2dyYW0oKSh2aW9saW5Qb2ludHNWYWx1ZXMpXG4gICAgLy8gbGVuZ3RoIG9mIGJpbnNcbiAgICB2YXIgZnJlcXVlbmNpZXMgPSBiaW5uZWQubWFwKGJpbj0+YmluLmxlbmd0aClcbiAgICAvLyBtaW4gYW5kIG1heCBjb3VudG91ciBwb2ludHMgZm9yIG5pY2UgZHJhd2luZ3NcbiAgICB2YXIgbWluQ29udG91clBvaW50ID0gaG9yaXpvbnRhbFEgPyB7eDogMCwgeTogZDMubWluKHZpb2xpblBvaW50c1ZhbHVlcyl9IDogIHt4OiBkMy5taW4odmlvbGluUG9pbnRzVmFsdWVzKSwgeTogMH1cbiAgICB2YXIgbWF4Q29udG91clBvaW50ID0gaG9yaXpvbnRhbFEgPyB7eDogMCwgeTogZDMubWF4KHZpb2xpblBvaW50c1ZhbHVlcyl9IDogIHt4OiBkMy5tYXgodmlvbGluUG9pbnRzVmFsdWVzKSwgeTogMH1cbiAgICB2YXIgdmlvbGluQ29udG91clBvaW50cyA9IGJpbm5lZC5tYXAoZnVuY3Rpb24oYmluLCBpKSB7XG4gICAgICAgIHJldHVybiBob3Jpem9udGFsUVxuICAgICAgICA/IHt5OiAoYmluLmxlbmd0aCkgPyBkMy5tZWRpYW4oYmluKTogZDMubWVkaWFuKFtiaW4ueDAsIGJpbi54MV0pLCB4OiBmcmVxdWVuY2llc1tpXX1cbiAgICAgICAgOiB7eDogKGJpbi5sZW5ndGgpID8gZDMubWVkaWFuKGJpbik6IGQzLm1lZGlhbihbYmluLngwLCBiaW4ueDFdKSwgeTogZnJlcXVlbmNpZXNbaV19XG4gICAgICB9KVxuICAgIC8vIHBvaW50cyBhbG9uZyB3aGljaCB0byBkcmF3IHRoZSB2aW9saW4gc2hwZVxuICAgIHZpb2xpbkNvbnRvdXJQb2ludHMgPSBbbWluQ29udG91clBvaW50XS5jb25jYXQodmlvbGluQ29udG91clBvaW50cykuY29uY2F0KFttYXhDb250b3VyUG9pbnRdKVxuXG4gICAgLy8gc2V0IGRhdGFcbiAgICB2aW9saW5EYXRhLmJpbm5lZCA9IGJpbm5lZDtcbiAgICB2aW9saW5EYXRhLmZyZXF1ZW5jaWVzID0gZnJlcXVlbmNpZXNcbiAgICB2aW9saW5EYXRhLmNvbnRvdXIgPSB2aW9saW5Db250b3VyUG9pbnRzXG4gICAgdmlvbGluRGF0YS5xdWFydGlsZXMgPSBwb2ludFF1YXJ0aWxlc1xuICAgIHZpb2xpbkRhdGEucG9pbnRLZXlzID0gdmlvbGluUG9pbnRzS2V5c1xuICAgIHZpb2xpbkRhdGEucG9pbnRWYWx1ZXMgPSB2aW9saW5Qb2ludHNWYWx1ZXNcbiAgfVxuXG4gIHJldHVybiBjYWxjdWxhdGVWaW9saW5WYWx1ZXNcbn1cbiIsImltcG9ydCB7aHlwZW5hdGUsIHNhZmVTZWxlY3QsIHJvdW5kLCBpbnRlcnBvbGF0ZUNvbG9yc30gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7c2V0dXBDb250YWluZXJ9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHtjb2xvckZ1bmN0aW9uIGFzIENGfSBmcm9tICcuL2NvbG9yLWZ1bmN0aW9uJztcblxuXG5leHBvcnQgZnVuY3Rpb24gbnVtZXJpY0xlZ2VuZCggc2VsZWN0aW9uICkge1xuXG4gIHZhclxuICBtaW49MCxcbiAgbWF4PTEsXG4gIHNwYWNlWCxcbiAgc3BhY2VZLFxuICBjb2xvckZ1bmN0aW9uID0gQ0YoKSxcbiAgbmFtZXNwYWNlPSdkM3NtLWxpbmVhci12ZXJ0aWNhbC1ncmFkaWVudCcsXG4gIGZvbnRTaXplID0gMTIsXG4gIGJhY2tncm91bmRGaWxsID0gJ3RyYW5zcGFyZW50JyxcbiAgdGV4dENvbG9yID0gJ2JsYWNrJyxcbiAgcm91bmRUbyA9IDJcblxuXG4gIGxlZ2VuZC5taW4gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1pbj1fLCBsZWdlbmQpIDogbWluIH1cbiAgbGVnZW5kLm1heCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWF4PV8sIGxlZ2VuZCkgOiBtYXggfVxuICBsZWdlbmQuc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVg9XywgbGVnZW5kKSA6IHNwYWNlWCB9XG4gIGxlZ2VuZC5zcGFjZVkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWT1fLCBsZWdlbmQpIDogc3BhY2VZIH1cbiAgbGVnZW5kLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlPV8sIGxlZ2VuZCkgOiBuYW1lc3BhY2UgfVxuICBsZWdlbmQuZm9udFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGZvbnRTaXplPV8sIGxlZ2VuZCkgOiBmb250U2l6ZSB9XG4gIGxlZ2VuZC5iYWNrZ3JvdW5kRmlsbCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFja2dyb3VuZEZpbGw9XywgbGVnZW5kKSA6IGJhY2tncm91bmRGaWxsIH1cbiAgbGVnZW5kLmNvbG9yRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNvbG9yRnVuY3Rpb249XywgbGVnZW5kKSA6IGNvbG9yRnVuY3Rpb24gfVxuICBsZWdlbmQudGV4dENvbG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0ZXh0Q29sb3I9XywgbGVnZW5kKSA6IHRleHRDb2xvciB9XG4gIGxlZ2VuZC5yb3VuZFRvID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChyb3VuZFRvPV8sIGxlZ2VuZCkgOiByb3VuZFRvIH1cblxuICBmdW5jdGlvbiBsZWdlbmQoKSB7XG4gICAgLy8gYmFja2dyb3VuZCBjbGlwaW5nIHJlY3RhbmdsZVxuICAgIHZhciBiZ2NwUmVjdCA9IHt4OjAsIHk6MCwgd2lkdGg6IHNwYWNlWCwgaGVpZ2h0OnNwYWNlWX1cbiAgICB2YXIgY29udGFpbmVyID0gc2V0dXBDb250YWluZXIoIHNlbGVjdGlvbiwgbmFtZXNwYWNlLCBiZ2NwUmVjdCwgYmFja2dyb3VuZEZpbGwgKTtcblxuICAgIHZhciBkZWZzID0gc2FmZVNlbGVjdChzZWxlY3Rpb24sICdkZWZzJylcbiAgICB2YXIgbGluZWFyR3JhZGllbnQgPSBzYWZlU2VsZWN0KGRlZnMsICdsaW5lYXJHcmFkaWVudCcpXG4gICAgLmF0dHIoXCJ4MVwiLCBcIjAlXCIpXG4gICAgLmF0dHIoXCJ5MVwiLCBcIjEwMCVcIilcbiAgICAuYXR0cihcIngyXCIsIFwiMCVcIilcbiAgICAuYXR0cihcInkyXCIsIFwiMCVcIilcbiAgICAuYXR0cignaWQnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ251bWVyaWNhbC1sZWdlbmQtZ3JhZGllbnQnKSlcblxuXG4gICAgY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KFttaW4sIG1heF0pXG4gICAgLmNvbG9yQnkoJ3ZhbHVlJylcbiAgICAudmFsdWVFeHRyYWN0b3IoZnVuY3Rpb24oaywgdiwgaSl7cmV0dXJuIHZ9KVxuXG5cbiAgICBsaW5lYXJHcmFkaWVudC5zZWxlY3RBbGwoJ3N0b3AnKVxuICAgIC5kYXRhKCBjb2xvckZ1bmN0aW9uLmNvbG9ycygpIClcbiAgICAuZW50ZXIoKVxuICAgIC5hcHBlbmQoJ3N0b3AnKVxuICAgIC5hdHRyKFwib2Zmc2V0XCIsIGZ1bmN0aW9uKGQsIGkpeyByZXR1cm4gaSAvIChjb2xvckZ1bmN0aW9uLmNvbG9ycygpLmxlbmd0aCAtIDEpIH0pXG4gICAgLmF0dHIoJ3N0b3AtY29sb3InLCBmdW5jdGlvbihkKSB7cmV0dXJuIGR9KVxuXG5cblxuXG4gICAgdmFyIHJlY3QgPSBzYWZlU2VsZWN0KGNvbnRhaW5lciwgJ3JlY3QnLCAnbGVnZW5kJylcbiAgICAuYXR0cigndHJhbnNmb3JtJywgJ3RyYW5zbGF0ZSgwLCcrZm9udFNpemUrJyknKVxuICAgIC5zdHlsZShcImZpbGxcIiwgXCJ1cmwoI1wiK2h5cGVuYXRlKG5hbWVzcGFjZSwnbnVtZXJpY2FsLWxlZ2VuZC1ncmFkaWVudCcpK1wiKVwiKVxuICAgIC5hdHRyKCd4JywgMClcbiAgICAuYXR0cigneScsIDApXG4gICAgLmF0dHIoJ3dpZHRoJywgc3BhY2VYKVxuICAgIC5hdHRyKCdoZWlnaHQnLCBzcGFjZVkgLSBmb250U2l6ZSoyKVxuICAgIC5vbignbW91c2Vtb3ZlJywgZnVuY3Rpb24oZCwgaSl7bGVnZW5kTW91c2Vtb3ZlKGQsIGksIHJlY3QsIGQzLnNlbGVjdCh0aGlzKSl9KVxuICAgIC5vbignbW91c2VvdXQnLCBmdW5jdGlvbihkLCBpKXsgZDMuc2VsZWN0KFwiI1wiK2h5cGVuYXRlKG5hbWVzcGFjZSwnbGVnZW5kLXRvb2x0aXAnKSkucmVtb3ZlKCkgfSlcblxuICAgIHZhciBtaW5UZXh0ID0gc2FmZVNlbGVjdChjb250YWluZXIsICd0ZXh0JywgJ21pbicpXG4gICAgLnRleHQocm91bmQobWluLCAyKSlcbiAgICAuYXR0cigndGV4dC1hbmNob3InLCAnbWlkZGxlJylcbiAgICAuYXR0cihcImZvbnQtc2l6ZVwiLCBmb250U2l6ZSsncHgnKVxuICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhclxuICAgICAgeCA9IHNwYWNlWCAvIDIsXG4gICAgICB5ID0gc3BhY2VZIC0gZm9udFNpemUgLyA0LFxuICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgcmV0dXJuIHRcbiAgICB9KVxuXG4gICAgdmFyIG1heFRleHQgPSBzYWZlU2VsZWN0KGNvbnRhaW5lciwgJ3RleHQnLCAnbWF4JylcbiAgICAudGV4dChyb3VuZChtYXgsIDIpKVxuICAgIC5hdHRyKCd0ZXh0LWFuY2hvcicsICdtaWRkbGUnKVxuICAgIC5hdHRyKFwiZm9udC1zaXplXCIsIGZvbnRTaXplKydweCcpXG4gICAgLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgdmFyXG4gICAgICB4ID0gc3BhY2VYIC8gMixcbiAgICAgIHkgPSBmb250U2l6ZSxcbiAgICAgIHQgPSAndHJhbnNsYXRlKCcreCsnLCcreSsnKSdcbiAgICAgIHJldHVybiB0XG4gICAgfSlcblxuXG5cblxuICB9XG5cbiAgZnVuY3Rpb24gbGVnZW5kTW91c2Vtb3ZlKGQsIGksIHJlY3QsIHQpIHtcbiAgICB2YXIgcyA9IGQzLnNjYWxlTGluZWFyKClcbiAgICAuZG9tYWluKFswLCByZWN0LmF0dHIoJ2hlaWdodCcpXSlcbiAgICAucmFuZ2UoW21heCwgbWluXSlcbiAgICB2YXIgbSA9IGQzLm1vdXNlKHJlY3Qubm9kZSgpKVxuICAgIHZhciB2ID0gcm91bmQocyhtWzFdKSxyb3VuZFRvKVxuXG4gICAgdmFyIHN0cm9rZUNvbG9yID0gY29sb3JGdW5jdGlvbih1bmRlZmluZWQsIHYsIHVuZGVmaW5lZCwgJ3N0cm9rZScpXG4gICAgdmFyIGZpbGxDb2xvciA9IGNvbG9yRnVuY3Rpb24odW5kZWZpbmVkLCB2LCB1bmRlZmluZWQsICdmaWxsJylcblxuICAgIHZhciBkaXYgPSBzYWZlU2VsZWN0KGQzLnNlbGVjdCgnYm9keScpLCAnZGl2JywgaHlwZW5hdGUobmFtZXNwYWNlLCdsZWdlbmQtdG9vbHRpcCcpKVxuICAgIC5hdHRyKCdpZCcsIGh5cGVuYXRlKG5hbWVzcGFjZSwnbGVnZW5kLXRvb2x0aXAnKSlcbiAgICAuc3R5bGUoJ3Bvc2l0aW9uJywgJ2Fic29sdXRlJylcbiAgICAuc3R5bGUoJ2xlZnQnLCAoZDMuZXZlbnQucGFnZVgrMTUpKydweCcpXG4gICAgLnN0eWxlKCd0b3AnLCAoZDMuZXZlbnQucGFnZVkrMTUpKydweCcpXG4gICAgLnN0eWxlKCdiYWNrZ3JvdW5kLWNvbG9yJywgZmlsbENvbG9yKVxuICAgIC5zdHlsZSgnYm9yZGVyLWNvbG9yJywgc3Ryb2tlQ29sb3IpXG5cbiAgICAuc3R5bGUoJ21pbi13aWR0aCcsIChmb250U2l6ZSAqIChTdHJpbmcobWF4KS5zcGxpdCgnLicpWzBdLmxlbmd0aCszKSkrJ3B4JylcbiAgICAuc3R5bGUoJ21pbi1oZWlnaHQnLCAoZm9udFNpemUgKiAoU3RyaW5nKG1heCkuc3BsaXQoJy4nKVswXS5sZW5ndGgrMykpKydweCcpXG4gICAgLnN0eWxlKCdib3JkZXItcmFkaXVzJywgJzUwJScpXG4gICAgLnN0eWxlKCdib3JkZXItcmFkaXVzJywgJzUwMDBweCcpXG5cbiAgICAuc3R5bGUoJ2Rpc3BsYXknLCAnZmxleCcpXG4gICAgLnN0eWxlKCdqdXN0aWZ5LWNvbnRlbnQnLCAnY2VudGVyJylcbiAgICAuc3R5bGUoJ3RleHQtYWxpZ24nLCAnbWlkZGxlJylcbiAgICAuc3R5bGUoJ3BhZGRpbmcnLCAyK1wicHhcIilcblxuICAgIC5zdHlsZSgnYm9yZGVyLXN0eWxlJywgJ3NvbGlkJylcbiAgICAuc3R5bGUoJ2JvcmRlci13aWR0aCcsIDIpXG5cbiAgICB2YXIgdGV4dCA9IHNhZmVTZWxlY3QoZGl2LCAnZGl2JylcbiAgICAudGV4dCh2KVxuICAgIC5zdHlsZSgnY29sb3InLCB0ZXh0Q29sb3IpXG4gICAgLnN0eWxlKCdhbGlnbi1zZWxmJywgJ2NlbnRlcicpXG4gIH1cblxuICByZXR1cm4gbGVnZW5kXG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0LCByb3VuZCwgaW50ZXJwb2xhdGVDb2xvcnN9IGZyb20gJy4vaGVscGVycyc7XG5pbXBvcnQge3NldHVwQ29udGFpbmVyLCBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0LCBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7Y29sb3JGdW5jdGlvbiBhcyBDRn0gZnJvbSAnLi9jb2xvci1mdW5jdGlvbic7XG5pbXBvcnQge2dyb3VwaW5nU3BhY2VyfSBmcm9tICcuL2dyb3VwaW5nLXNwYWNlcic7XG5pbXBvcnQge3VuaXF1ZSwgZmxhdHRlbn0gZnJvbSAnLi9hcnJheS1mdW5jdGlvbnMnO1xuXG5leHBvcnQgZnVuY3Rpb24gY2F0ZWdvcmljTGVnZW5kKCBzZWxlY3Rpb24gKSB7XG4gIHZhclxuICBjYXRlZ29yaWVzLFxuXG4gIC8qKlxuICAqIERhdGEgdG8gcGxvdC4gQXNzdW1lZCB0byBiZSBhIG9iamVjdCwgd2hlcmUgZWFjaCBrZXkgY29ycmVzcG9uZHMgdG8gYSBsZWdlbmRcbiAgKiAoc2VlIHtAbGluayBsZWdlbmQjZGF0YX0pXG4gICogQHBhcmFtIHtPYmplY3R9IFtkYXRhPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgbGVnZW5kI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkYXRhLFxuICAvKipcbiAgKiBXaGljaCBkaXJlY3Rpb24gdG8gcmVuZGVyIHRoZSBiYXJzIGluXG4gICogKHNlZSB7QGxpbmsgbGVnZW5kI29yaWVudH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvcmllbnQ9J2hvcml6b250YWwnXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9yaWVudD0naG9yaXpvbnRhbCcsXG4gIC8qKlxuICAqIEFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBsZWdlbmQgaW5cbiAgKiAoc2VlIHtAbGluayBsZWdlbmQjc3BhY2VYfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlWD11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VYLFxuICAvKipcbiAgKiBBbW91bnQgb2YgdmVydGljYWwgc3BhY2UgKGluIHBpeGVscykgYXZhaWJsZSB0byByZW5kZXIgdGhlIGxlZ2VuZCBpblxuICAqIChzZWUge0BsaW5rIGxlZ2VuZC5zcGFjZVl9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VZPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgbGVnZW5kI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzcGFjZVksXG5cbiAgLyoqXG4gICogV2hldGhlciBvciBub3QgdG8gYWxsb3cgbGVnZW5kIHRvIHJlbmRlciBlbGVtZW50cyBwYXNzIHRoZSBtYWluIHNwYXRpYWwgZGltZW5zaW9uXG4gICogZ2l2ZW4gdGhlIG9yaWVudGF0aW9uIChzZWUge0BsaW5rIGxlZ2VuZCNvcmllbnR9KSwgd2hlcmUge0BsaW5rIGxlZ2VuZCNvcmllbnR9PVwiaG9yaXpvbnRhbFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayBsZWdlbmQjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGxlZ2VuZCNvcmllbnR9PVwidmVydGljYWxcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgbGVnZW5kI3NwYWNlWX1cbiAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvdmVyZmxvd1E9ZmFsc2VdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb3ZlcmZsb3dRID0gZmFsc2UsXG5cbiAgLyoqXG4gICogQW4gYXJyYXkgLSBwdXRhdGl2ZWx5IG9mIG90aGVyIGFycmF5cyAtIGRlcGljdGluZyBob3cgYmFycyBzaG91bGQgYmUgYXJyYW5nZWRcbiAgKiBAcGFyYW0ge0FycmF5W119IFtncm91cGluZz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZ3JvdXBpbmcsXG5cbiAgLyoqXG4gICogSG93IHRvIGdldCB0aGUgdmFsdWUgb2YgdGhlIGxlZ2VuZFxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt2YWx1ZUV4dHJhY3Rvcj1mdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV0gfV1cbiAgKiBAbWVtYmVyb2YgbGVnZW5kI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2YWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKGtleSwgaW5kZXgpIHsgcmV0dXJuIGRhdGFba2V5XSB9LFxuICAvKipcbiAgKiBIb3cgdG8gc29ydCB0aGUgYmFycyAtIGlmIHtAbGluayBiYXIjZ3JvdXBpbmd9IGlzIG5vdCBwcm92aWRlZC5cbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbc29ydGluZ0Z1bmN0aW9uPWZ1bmN0aW9uKGtleUEsIGtleUIpIHtyZXR1cm4gZDMuZGVzY2VuZGluZyhkYXRhW2tleUFdLCBkYXRhW2tleUJdKX1dXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oa2V5QSwga2V5Qikge3JldHVybiBkMy5hc2NlbmRpbmcoa2V5QSwga2V5Qil9LFxuICAvKipcbiAgKiBEZWZhdWx0IHNwYWNlIGZvciB0aGUgc3BhY2VyIChwZXJjZW50YWdlKSBvZiBtYWluIGRpbWVuc2lvbiBnaXZlbiB0aGUgb3JpZW50YXRpb25cbiAgKiAoc2VlIHtAbGluayBsZWdlbmQjb3JpZW50fSksIHdoZXJlIHtAbGluayBsZWdlbmQjb3JpZW50fT1cImhvcml6b250YWxcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgbGVnZW5kI3NwYWNlWH0gYW5kIHdoZXJlIHtAbGluayBsZWdlbmQjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGxlZ2VuZCNzcGFjZVl9IGJldHdlZW4gYmFyc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbb2JqZWN0U3BhY2VyPTAuMDVdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0U3BhY2VyID0gMC4wNSxcbiAgLyoqXG4gICogVGhlIG1pbmltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmVcbiAgKiBAcGFyYW0ge251bWJlcn0gW21pbk9iamVjdFNpemU9NTBdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWluT2JqZWN0U2l6ZSA9IDEwLFxuICAvKipcbiAgKiBUaGUgbWF4aW11bSBzaXplIHRoYXQgYW4gb2JqZWN0IGNhbiBiZVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbWF4T2JqZWN0U2l6ZT0xMDBdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWF4T2JqZWN0U2l6ZSA9IDEwMCxcblxuICAvKipcbiAgKiBUaGUgc3Ryb2tlIHdpZHRoIG9mIHRoZSBiYXJzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtiYXJTdHJva2VXaWR0aD0yXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJ1YmJsZVN0cm9rZVdpZHRoID0gMixcbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgQ29sb3JGdW5jdGlvblxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtjb2xvckZ1bmN0aW9uID0gY29sb3JGdW5jdGlvbigpXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGNvbG9yRnVuY3Rpb24gPSBDRigpLFxuXG4gIC8qKlxuICAqIENvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kXG4gICogQHBhcmFtIHtzdHJpbmd9IFtiYWNrZ3JvdW5kRmlsbD1cInRyYW5zcGFyZW50XCJdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnLFxuICAvKipcbiAgKiBOYW1lc3BhY2UgZm9yIGFsbCBpdGVtcyBtYWRlIGJ5IHRoaXMgaW5zdGFuY2Ugb2YgbGVnZW5kXG4gICogQHBhcmFtIHtzdHJpbmd9IFtuYW1lc3BhY2U9XCJkM3NtLWxlZ2VuZFwiXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG5hbWVzcGFjZSA9ICdkM3NtLWxlZ2VuZCcsXG4gIC8qKlxuICAqIENsYXNzIG5hbWUgZm9yIGxlZ2VuZCBjb250YWluZXIgKDxnPiBlbGVtZW50KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbb2JqZWN0Q2xhc3M9XCJsZWdlbmRcIl1cbiAgKiBAbWVtYmVyb2YgbGVnZW5kI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvYmplY3RDbGFzcyA9ICdsZWdlbmQnLFxuXG4gIC8qKlxuICAqIER1cmF0aW9uIG9mIGFsbCB0cmFuc2l0aW9ucyBvZiB0aGlzIGVsZW1lbnRcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RyYW5zaXRpb25EdXJhdGlvbj0xMDAwXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRyYW5zaXRpb25EdXJhdGlvbiA9IDEwMDAsXG4gIC8qKlxuICAqIEVhc2luZyBmdW5jdGlvbiBmb3IgdHJhbnNpdGlvbnNcbiAgKiBAcGFyYW0ge2QzLmVhc2V9IFtlYXNlRnVuYz1kMy5lYXNlRXhwXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGVhc2VGdW5jID0gZDMuZWFzZUV4cFxuXG5cbiAgbGVnZW5kLmNhdGVnb3JpZXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNhdGVnb3JpZXM9XywgbGVnZW5kKSA6IGNhdGVnb3JpZXMgfTtcblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBzZWxlY3Rpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgZDMuc2VsZWN0aW9ufVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNlbGVjdGlvbiA9IHNlbGVjdGlvblxuICAgKi9cbiAgbGVnZW5kLnNlbGVjdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2VsZWN0aW9uID0gXywgbGVnZW5kKSA6IHNlbGVjdGlvbjsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZGF0YVxuICAgKiAoc2VlIHtAbGluayBsZWdlbmQjZGF0YX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgb2JqZWN0fVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgbGVnZW5kLmRhdGEgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRhdGEgPSBfLCBsZWdlbmQpIDogZGF0YTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgb3JpZW50IG9mIHRoZSBiYXJzXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNvcmllbnR9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2xlZ2VuZCB8IG9iamVjdH1cbiAgICogQG1lbWJlcm9mIGxlZ2VuZFxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGxlZ2VuZC5vcmllbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9yaWVudCA9IF8sIGxlZ2VuZCkgOiBvcmllbnQ7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBsZWdlbmQjc3BhY2VYfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge2xlZ2VuZCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGxlZ2VuZFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVggPSB1bmRlZmluZWRcbiAgICovXG4gIGxlZ2VuZC5zcGFjZVggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWCA9IF8sIGxlZ2VuZCkgOiBzcGFjZVg7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiB2ZXJ0aWNhbCBzcGFjZSBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI3NwYWNlWX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHtsZWdlbmQgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VZID0gdW5kZWZpbmVkXG4gICAqL1xuICBsZWdlbmQuc3BhY2VZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVkgPSBfLCBsZWdlbmQpIDogc3BhY2VZOyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB3aGV0aGVyIG9yIG5vdCBsZWdlbmQgaXMgYWxsb3dlZCB0byBnbyBiZXlvbmQgc3BlY2lmaWVkIGRpbWVuc2lvbnNcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI3NwYWNlWH0pXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2xlZ2VuZCB8IGJvb2xlYW59XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3ZlcmZsb3dRID0gZmFsc2VcbiAgICovXG4gIGxlZ2VuZC5vdmVyZmxvd1EgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG92ZXJmbG93USA9IF8sIGxlZ2VuZCkgOiBvdmVyZmxvd1E7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZ3JvdXBpbmcgb2YgdGhlIGJhcnNcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI2dyb3VwaW5nfSlcbiAgICogQHBhcmFtIHtBcnJheVtdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgQXJyYXlbXX1cbiAgICogQG1lbWJlcm9mIGxlZ2VuZFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBncm91cGluZyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgbGVnZW5kLmdyb3VwaW5nID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChncm91cGluZyA9IF8sIGxlZ2VuZCkgOiBncm91cGluZzsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2YWx1ZUV4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayBsZWdlbmQjdmFsdWVFeHRyYWN0b3J9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV0gfSxcbiAgICovXG4gIGxlZ2VuZC52YWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodmFsdWVFeHRyYWN0b3IgPSBfLCBsZWdlbmQpIDogdmFsdWVFeHRyYWN0b3I7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgc29ydGluZ0Z1bmN0aW9uXG4gICAqIChzZWUge0BsaW5rIGJhciNzb3J0aW5nRnVuY3Rpb259KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oa2V5QSwga2V5Qikge3JldHVybiBkMy5kZXNjZW5kaW5nKGRhdGFba2V5QV0sIGRhdGFba2V5Ql0pfSxcbiAgICovXG4gIGxlZ2VuZC5zb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNvcnRpbmdGdW5jdGlvbiA9IF8sIGxlZ2VuZCkgOiBzb3J0aW5nRnVuY3Rpb247IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyBvYmplY3RTcGFjZXJcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI29iamVjdFNwYWNlcn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdFNwYWNlciA9IDAuMDVcbiAgICovXG4gIGxlZ2VuZC5vYmplY3RTcGFjZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNwYWNlciA9IF8sIGxlZ2VuZCkgOiBvYmplY3RTcGFjZXI7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbWluT2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBsZWdlbmQjbWluT2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1pbk9iamVjdFNpemUgPSA1MFxuICAgKi9cbiAgbGVnZW5kLm1pbk9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1pbk9iamVjdFNpemUgPSBfLCBsZWdlbmQpIDogbWluT2JqZWN0U2l6ZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtYXhPYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNtYXhPYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtsZWdlbmQgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWF4T2JqZWN0U2l6ZSA9IDEwMFxuICAgKi9cbiAgbGVnZW5kLm1heE9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1heE9iamVjdFNpemUgPSBfLCBsZWdlbmQpIDogbWF4T2JqZWN0U2l6ZTsgfTtcblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGJhclN0cm9rZVdpZHRoXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNiYXJTdHJva2VXaWR0aH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGJhclN0cm9rZVdpZHRoID0gMlxuICAgKi9cbiAgbGVnZW5kLmJ1YmJsZVN0cm9rZVdpZHRoID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChidWJibGVTdHJva2VXaWR0aCA9IF8sIGxlZ2VuZCkgOiBidWJibGVTdHJva2VXaWR0aDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBjb2xvckZ1bmN0aW9uXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNjb2xvckZ1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtsZWdlbmQgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKVxuICAgKi9cbiAgbGVnZW5kLmNvbG9yRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNvbG9yRnVuY3Rpb24gPSBfLCBsZWdlbmQpIDogY29sb3JGdW5jdGlvbjsgfTtcblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGJhY2tncm91bmRGaWxsXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNiYWNrZ3JvdW5kRmlsbH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGJhY2tncm91bmRGaWxsID0gJ3RyYW5zcGFyZW50J1xuICAgKi9cbiAgbGVnZW5kLmJhY2tncm91bmRGaWxsID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChiYWNrZ3JvdW5kRmlsbCA9IF8sIGxlZ2VuZCkgOiBiYWNrZ3JvdW5kRmlsbDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBuYW1lc3BhY2VcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI25hbWVzcGFjZX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG5hbWVzcGFjZSA9ICdkM3NtLWxlZ2VuZCdcbiAgICovXG4gIGxlZ2VuZC5uYW1lc3BhY2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG5hbWVzcGFjZSA9IF8sIGxlZ2VuZCkgOiBuYW1lc3BhY2U7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0Q2xhc3NcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI29iamVjdENsYXNzfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtsZWdlbmQgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0Q2xhc3MgPSAndGljay1ncm91cCdcbiAgICovXG4gIGxlZ2VuZC5vYmplY3RDbGFzcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3MgPSBfLCBsZWdlbmQpIDogb2JqZWN0Q2xhc3M7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdHJhbnNpdGlvbkR1cmF0aW9uXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCN0cmFuc2l0aW9uRHVyYXRpb259KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2xlZ2VuZCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGxlZ2VuZFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwXG4gICAqL1xuICBsZWdlbmQudHJhbnNpdGlvbkR1cmF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0cmFuc2l0aW9uRHVyYXRpb24gPSBfLCBsZWdlbmQpIDogdHJhbnNpdGlvbkR1cmF0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGVhc2VGdW5jXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNlYXNlRnVuY30pXG4gICAqIEBwYXJhbSB7ZDMuZWFzZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2xlZ2VuZCB8IGQzLmVhc2V9XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwXG4gICAqL1xuICBsZWdlbmQuZWFzZUZ1bmMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGVhc2VGdW5jID0gXywgbGVnZW5kKSA6IGVhc2VGdW5jOyB9O1xuXG5cbiAgZnVuY3Rpb24gbGVnZW5kKCkge1xuICAgIHZhciBob3Jpem9udGFsUSA9IChvcmllbnQgPT0gJ2hvcml6b250YWwnKSA/IHRydWUgOiBmYWxzZVxuICAgIHZhciB2ZXJ0aWNhbFEgPSAhaG9yaXpvbnRhbFFcbiAgICAvLyBiYWNrZ3JvdW5kIGNsaXBpbmcgcmVjdGFuZ2xlXG4gICAgdmFyIGJnY3BSZWN0ID0ge3g6MCwgeTowLCB3aWR0aDogc3BhY2VYLCBoZWlnaHQ6c3BhY2VZfVxuICAgIHZhciBjb250YWluZXIgPSBzZXR1cENvbnRhaW5lciggc2VsZWN0aW9uLCBuYW1lc3BhY2UsIGJnY3BSZWN0LCBiYWNrZ3JvdW5kRmlsbCApO1xuXG5cbiAgICBjb2xvckZ1bmN0aW9uLmRhdGFFeHRlbnQoWzAsIGNhdGVnb3JpZXMubGVuZ3RoIC0gMV0pXG4gICAgLmNvbG9yQnkoJ2NhdGVnb3JpZXMnKVxuICAgIC5jYXRlZ29yeUV4dHJhY3RvcihmdW5jdGlvbihrLCB2LCBpKXtyZXR1cm4gdn0pXG5cbiAgICB2YXIgciA9IE1hdGgubWluKHNwYWNlWCwgc3BhY2VZKSAvIDJcbiAgICB2YXIgbnVtYmVyT2ZPYmplY3RzID0gY2F0ZWdvcmllcy5sZW5ndGhcblxuICAgIC8vIGlmIGdyb3VwaW5nIGlzIHVuZGVmaW5lZCBzb3J0IGJhcktleXMgYnkgc29ydGluZ0Z1bmN0aW9uXG4gICAgdmFyIG9yZGVyZWQgPSAoZ3JvdXBpbmcgPT0gdW5kZWZpbmVkKSA/IGNhdGVnb3JpZXMuc29ydChzb3J0aW5nRnVuY3Rpb24pIDogZ3JvdXBpbmdcbiAgICAvLyBvcmRlcmVkIG1pZ2h0IGJlIG5lc3RlZCBkZXBlbmRpbmcgb24gZ3JvdXBpbmdcbiAgICB2YXIgY2F0S2V5cyA9IGZsYXR0ZW4ob3JkZXJlZClcblxuICAgIHZhciBzcGFjZSA9IGhvcml6b250YWxRID8gc3BhY2VYIDogc3BhY2VZXG4gICAgLy8gY2FsY3VsYXRlIG9iamVjdCBzaXplXG4gICAgdmFyIG9iamVjdFNpemUgPSBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0KHNwYWNlLCBudW1iZXJPZk9iamVjdHMsIG1pbk9iamVjdFNpemUsIG1heE9iamVjdFNpemUsIG9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIC8vIGNhbGN1bGF0ZSBzcGFjZXIgc2l6ZSBpZiBuZWVkZWRcbiAgICB2YXIgc3BhY2VyU2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXIoY2F0S2V5cywgc3BhY2UsIG9iamVjdFNpemUsIG51bWJlck9mT2JqZWN0cywgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgLy8gbWFrZSB0aGUgbmVzdGVkIGdyb3Vwc1xuICAgIHZhciBzcGFjZXJGdW5jdGlvbiA9IGdyb3VwaW5nU3BhY2VyKClcbiAgICAuaG9yaXpvbnRhbFEoaG9yaXpvbnRhbFEpLnNjYWxlKHNjYWxlKS5tb3ZlYnkoJ2NhdGVnb3J5JykubnVtYmVyT2ZPYmplY3RzKG51bWJlck9mT2JqZWN0cylcbiAgICAub2JqZWN0Q2xhc3Mob2JqZWN0Q2xhc3MpLm9iamVjdFNpemUob2JqZWN0U2l6ZSkuc3BhY2VyU2l6ZShzcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcbiAgICAubmFtZXNwYWNlKG5hbWVzcGFjZSlcblxuICAgIHNwYWNlckZ1bmN0aW9uKGNvbnRhaW5lciwgb3JkZXJlZCwgMClcbiAgICB2YXIgciA9IE1hdGgubWluKG9iamVjdFNpemUsIHNwYWNlWCwgc3BhY2VZKSAvIDIgLSBidWJibGVTdHJva2VXaWR0aFxuXG4gICAgY29udGFpbmVyLnNlbGVjdEFsbCgnZzpub3QoLnRvLXJlbW92ZSkuJytvYmplY3RDbGFzcykuZWFjaChmdW5jdGlvbihjYXQsIGkpIHtcbiAgICAgIHZhciB0ID0gZDMuc2VsZWN0KHRoaXMpXG4gICAgICB2YXIgYyA9IHNhZmVTZWxlY3QodCwgJ2NpcmNsZScpXG4gICAgICB2YXIgZmlsbENvbG9yID0gY29sb3JGdW5jdGlvbih1bmRlZmluZWQsIGNhdCwgaSwgJ2ZpbGwnKSwgLy8gcHJldmVudCBkdXBsaWNhdGUgY29tcHV0YXRpb25cbiAgICAgIHN0cm9rZUNvbG9yID0gY29sb3JGdW5jdGlvbih1bmRlZmluZWQsIGNhdCwgaSwgICdzdHJva2UnKVxuXG4gICAgICB2YXIgY3ggPSBob3Jpem9udGFsUVxuICAgICAgICA/IHIrYnViYmxlU3Ryb2tlV2lkdGhcbiAgICAgICAgOiAoc3BhY2VYIC0gcioyKSAvIDIgKyByXG4gICAgICB2YXIgY3kgPSB2ZXJ0aWNhbFFcbiAgICAgICAgPyByK2J1YmJsZVN0cm9rZVdpZHRoXG4gICAgICAgIDogKHNwYWNlWCAtIHIqMikgLyAyICsgclxuXG4gICAgICBjLmF0dHIoXCJyXCIsIHIpXG4gICAgICAuYXR0cignY3gnLCBjeClcbiAgICAgIC5hdHRyKCdjeScsIGN5KVxuICAgICAgLmF0dHIoJ2ZpbGwnLCBmaWxsQ29sb3IpXG4gICAgICAuYXR0cignc3Ryb2tlJywgc3Ryb2tlQ29sb3IpXG4gICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgYnViYmxlU3Ryb2tlV2lkdGgpXG5cbiAgICAgIHZhciB0ZXh0ID0gc2FmZVNlbGVjdCh0LCAndGV4dCcpXG4gICAgICB0ZXh0LnRleHQoY2F0KVxuICAgICAgLmF0dHIoJ3RleHQtYW5jaG9yJywgJ21pZGRsZScpXG4gICAgICAuYXR0cihcInRyYW5zZm9ybVwiLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBjeCxcbiAgICAgICAgeSA9IGN5ICsgdGV4dC5ub2RlKCkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkuaGVpZ2h0IC8gNCxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcblxuICAgIH0pXG5cblxuICB9XG5cbiAgcmV0dXJuIGxlZ2VuZFxufVxuIiwiaW1wb3J0IHtoeXBlbmF0ZSwgc2FmZVNlbGVjdCwgbW9kaWZ5SGV4aWRlY2ltYWxDb2xvckx1bWluYW5jZSwgZXh0cmFjdFZpb2xpblZhbHVlcywgcXVhcnRpbGVzfSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHtzZXR1cENvbnRhaW5lciwgY2FsY3VsYXRlV2lkdGhPZk9iamVjdCwgY2FsY3VsYXRlV2lkdGhPZlNwYWNlcn0gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQge3VuaXF1ZSwgaGFzUSwgZmxhdHRlbiwgd2hpY2hCaW59IGZyb20gJy4vYXJyYXktZnVuY3Rpb25zJztcbmltcG9ydCB7Z3JvdXBpbmdTcGFjZXJ9IGZyb20gJy4vZ3JvdXBpbmctc3BhY2VyJztcbmltcG9ydCB7Y29sb3JGdW5jdGlvbiBhcyBDRn0gZnJvbSAnLi9jb2xvci1mdW5jdGlvbic7XG5pbXBvcnQge3Rvb2x0aXAgYXMgVFRpcH0gZnJvbSAnLi90b29sdGlwJztcbmltcG9ydCB7bGFzc299IGZyb20gJy4vbGFzc28nO1xuaW1wb3J0ICcuL2QzLXByb3RvdHlwZXMnO1xuXG5leHBvcnQgZnVuY3Rpb24gbGFzc29XaWRnZXQoIHNlbGVjdGlvbiApIHtcbiAgdmFyXG4gIG5hbWVzcGFjZSA9ICdkM3NtLWxhc3NvJyxcbiAgc2VsZWN0aW9uID0gc2VsZWN0aW9uLFxuICBzdmcsXG4gIGNoYXJ0Q29udGFpbmVyLFxuICBvYmplY3RDb250YWluZXIsXG4gIG9iamVjdENsYXNzLFxuICBsYXNzb0xpbmUgPSBkMy5saW5lKClcbiAgLngoZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGRbMF19KVxuICAueShmdW5jdGlvbihkLCBpKXtyZXR1cm4gZFsxXX0pXG4gIC5jdXJ2ZShkMy5jdXJ2ZUxpbmVhckNsb3NlZCksXG4gIHBhdGgsXG4gIGNvbG9yRnVuY3Rpb24gPSBDRigpLFxuICBtYXhOdW1iZXJPZkdyb3VwcyxcbiAgZGF0YUV4dHJhY3RvcixcbiAgeFNjYWxlLFxuICB5U2NhbGVcblxuICBmdW5jdGlvbiBvbkVycm9yKG1zZywgc3R5bGUpe1xuICAgIGNvbnNvbGUubG9nKG1zZywgc3R5bGUpXG4gIH1cbiAgLy8gc2V0dXAgdGhlIGNvbnRhaW5lclxuICBzZXR1cERhdGFzZWxlY3RDb250YWluZXIoKVxuXG4gIGxhc3NvV2lkZ2V0Lm9iamVjdENsYXNzID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3M9Jy4nK18ucmVwbGFjZSgnLicsJycpLCBsYXNzb1dpZGdldCkgOiBvYmplY3RDbGFzc31cbiAgbGFzc29XaWRnZXQuc3ZnID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3ZnPV8sIGxhc3NvV2lkZ2V0KSA6IHN2Z31cbiAgbGFzc29XaWRnZXQuc3VibWl0ID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3VibWl0PV8sIGxhc3NvV2lkZ2V0KSA6IHN1Ym1pdH1cbiAgbGFzc29XaWRnZXQubWF4TnVtYmVyT2ZHcm91cHMgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtYXhOdW1iZXJPZkdyb3Vwcz1fLCBsYXNzb1dpZGdldCkgOiBtYXhOdW1iZXJPZkdyb3Vwc31cbiAgbGFzc29XaWRnZXQub25FcnJvciA9IGZ1bmN0aW9uKF8pe3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9uRXJyb3I9XywgbGFzc29XaWRnZXQpIDogb25FcnJvcn1cbiAgbGFzc29XaWRnZXQub2JqZWN0Q29udGFpbmVyID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q29udGFpbmVyPV8sIGxhc3NvV2lkZ2V0KSA6IG9iamVjdENvbnRhaW5lcn1cbiAgbGFzc29XaWRnZXQuY2hhcnRDb250YWluZXIgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjaGFydENvbnRhaW5lcj1fLCBsYXNzb1dpZGdldCkgOiBjaGFydENvbnRhaW5lcn1cbiAgbGFzc29XaWRnZXQuZGF0YUV4dHJhY3RvciA9IGZ1bmN0aW9uKF8pe3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRhdGFFeHRyYWN0b3I9XywgbGFzc29XaWRnZXQpIDogZGF0YUV4dHJhY3Rvcn1cbiAgbGFzc29XaWRnZXQueFNjYWxlID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeFNjYWxlPV8sIGxhc3NvV2lkZ2V0KSA6IHhTY2FsZX1cbiAgbGFzc29XaWRnZXQueVNjYWxlID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeVNjYWxlPV8sIGxhc3NvV2lkZ2V0KSA6IHlTY2FsZX1cblxuICBmdW5jdGlvbiBzdHlsZXMoKSB7XG4gICAgdmFyIHMgPSBkMy5zZWxlY3QoXCJodG1sXCIpLnNlbGVjdChcInN0eWxlLlwiK25hbWVzcGFjZSsnbGFzc28td2lkZ2V0JylcbiAgICBpZiAocy5lbXB0eSgpKSB7XG4gICAgICBkMy5zZWxlY3QoXCJodG1sXCIpLmFwcGVuZChcInN0eWxlXCIpXG4gICAgICAuY2xhc3NlZChuYW1lc3BhY2UrJ2xhc3NvLXdpZGdldCcsIHRydWUpXG4gICAgICAuaHRtbChcbiAgICAgICAgXCIuXCIraHlwZW5hdGUobmFtZXNwYWNlLCBcImRhdGEtdGFibGVcIikgKyBcIntcXFxuICAgICAgICAgIGhlaWdodDoxMDBweDtcXFxuICAgICAgICAgIG92ZXJmbG93OmF1dG87XFxcbiAgICAgICAgfVwiXG4gICAgICApXG4gICAgfVxuICB9XG5cblxuICBmdW5jdGlvbiBsYXNzb1dpZGdldCgpIHtcbiAgICBzdHlsZXMoKVxuXG4gIH1cblxuICBmdW5jdGlvbiBzdWJtaXQoZGF0YSkge1xuICAgIGNvbnNvbGUubG9nKGRhdGEpXG4gIH1cblxuICBmdW5jdGlvbiBwbHVzVGFiKHRhYkxpc3QpIHtcbiAgICB2YXIgdGFiID0gc2FmZVNlbGVjdCh0YWJMaXN0LCAnbGknLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdwbHVzLXRhYicpKVxuICAgIC5jbGFzc2VkKCdtbC1hdXRvJywgdHJ1ZSlcbiAgICAuY2xhc3NlZCgnbmF2LWl0ZW0nLCB0cnVlKVxuICAgIC5vbignY2xpY2snLCBtYWtlTmV3R3JvdXApLFxuXG4gICAgYW5jaG9yID0gc2FmZVNlbGVjdCh0YWIsICdhJywgJ25hdi1saW5rJyksXG4gICAgaWNvbiA9IHNhZmVTZWxlY3QoYW5jaG9yLCAnaScsICdmYScpXG4gICAgLmNsYXNzZWQoJ2ZhLXBsdXMgZmEtMnggdGV4dC1zdWNjZXNzJywgdHJ1ZSlcbiAgfVxuXG4gIGZ1bmN0aW9uIHNlbmRUYWIodGFiTGlzdCkge1xuICAgIHZhciB0YWIgPSBzYWZlU2VsZWN0KHRhYkxpc3QsICdsaScsIGh5cGVuYXRlKG5hbWVzcGFjZSwgJ3NlbmQtdGFiJykpXG4gICAgLmNsYXNzZWQoJ21sLWF1dG8nLCB0cnVlKVxuICAgIC5jbGFzc2VkKCduYXYtaXRlbScsIHRydWUpXG4gICAgLm9uKCdjbGljaycsIGNsaWNrU2VuZClcbiAgICAsXG5cbiAgICBhbmNob3IgPSBzYWZlU2VsZWN0KHRhYiwgJ2EnLCAnbmF2LWxpbmsnKSxcbiAgICBpY29uID0gc2FmZVNlbGVjdChhbmNob3IsICdpJywgJ2ZhJylcbiAgICAuY2xhc3NlZCgnZmEtcGFwZXItcGxhbmUtbyBmYS0yeCB0ZXh0LXByaW1hcnknLCB0cnVlKVxuICB9XG5cbiAgZnVuY3Rpb24gY2xpY2tTZW5kKCkge1xuICAgIHZhciBkYXRhID0gZ2F0aGVyRGF0YUxpc3RzKClcbiAgICBzdWJtaXQoZGF0YSlcbiAgfVxuXG4gIGZ1bmN0aW9uIGNsb3NlVGFiKHRhYkxpc3QpIHtcbiAgICB2YXIgdGFiID0gc2FmZVNlbGVjdCh0YWJMaXN0LCAnbGknLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdjbG9zZS10YWInKSlcbiAgICAuY2xhc3NlZCgnbWwtYXV0bycsIHRydWUpXG4gICAgLmNsYXNzZWQoJ25hdi1pdGVtJywgdHJ1ZSlcbiAgICAub24oJ2NsaWNrJywgZnVuY3Rpb24oKXtcbiAgICAgIHZhciBtID0gZDMubW91c2UoZDMuc2VsZWN0KFwiaHRtbFwiKS5ub2RlKCkpXG4gICAgICBzZWxlY3Rpb24uc2VsZWN0KCdkaXYuY2FyZCcpLnN0eWxlKFwicG9zaXRpb25cIiwgJ3JlbGF0aXZlJylcbiAgICAgIC50cmFuc2l0aW9uKCkuZHVyYXRpb24oMTAwMClcbiAgICAgIC5lYXNlKGQzLmVhc2VCYWNrKVxuICAgICAgLnN0eWxlKCdsZWZ0Jywgd2luZG93Lm91dGVyV2lkdGggKydweCcpXG4gICAgICAucmVtb3ZlKClcbiAgICB9KVxuICAgICxcblxuICAgIGFuY2hvciA9IHNhZmVTZWxlY3QodGFiLCAnYScsICduYXYtbGluaycpLFxuICAgIGljb24gPSBzYWZlU2VsZWN0KGFuY2hvciwgJ2knLCAnZmEnKVxuICAgIC5jbGFzc2VkKCdmYS13aW5kb3ctY2xvc2UtbyBmYS0yeCB0ZXh0LWRhbmdlcicsIHRydWUpXG4gIH1cblxuXG5cbiAgZnVuY3Rpb24gc2V0dXBEYXRhc2VsZWN0Q29udGFpbmVyKCkge1xuICAgIHZhclxuICAgIGNhcmQgPSBzYWZlU2VsZWN0KHNlbGVjdGlvbiwgJ2RpdicsICdjYXJkJyksXG4gICAgY2FyZEhlYWRlciA9IHNhZmVTZWxlY3QoY2FyZCwgJ2RpdicsICdjYXJkLWhlYWRlcicpLFxuXG4gICAgdGFiTGlzdCA9IHNhZmVTZWxlY3QoY2FyZEhlYWRlciwgJ3VsJywgJ25hdicpXG4gICAgLmNsYXNzZWQoJ25hdi10YWJzIGNhcmQtaGVhZGVyLXRhYnMnLCB0cnVlKVxuICAgIC5jbGFzc2VkKGh5cGVuYXRlKG5hbWVzcGFjZSwgJ3RhYi1saXN0JyksIHRydWUpXG4gICAgLmF0dHIoJ3JvbGUnLCAndGFibGlzdCcpLFxuXG4gICAgY2FyZEJvZHkgPSBzYWZlU2VsZWN0KGNhcmQsICdkaXYnLCAnY2FyZC1ib2R5JyksXG4gICAgdGFiQ29udGVudCA9IHNhZmVTZWxlY3QoY2FyZEJvZHksICdkaXYnLCAndGFiLWNvbnRlbnQnKVxuICAgIC5jbGFzc2VkKGh5cGVuYXRlKG5hbWVzcGFjZSwgJ3RhYi1jb250ZW50JyksIHRydWUpLFxuXG4gICAgLy8gbGVmdFRhYnMgPSBzYWZlU2VsZWN0KHRhYkxpc3QsICdkaXYnLCAnbmF2IG1yLWF1dG8gbGVmdC1hbGlnbmVkLXRhYnMnKVxuICAgIHJpZ2h0VGFicyA9IHNhZmVTZWxlY3QodGFiTGlzdCwgJ2RpdicsICdyaWdodC1hbGlnbmVkLXRhYnMnKS5jbGFzc2VkKCduYXYgbWwtYXV0byAnLCB0cnVlKVxuXG4gICAgcGx1c1RhYihyaWdodFRhYnMpXG4gICAgc2VuZFRhYihyaWdodFRhYnMpXG4gICAgY2xvc2VUYWIocmlnaHRUYWJzKVxuICAgIHZhclxuICAgIGRlZmF1bHRUYWIgPSBzYWZlU2VsZWN0KHRhYkNvbnRlbnQsICdkaXYnLCAndGFiLXBhbmUnKVxuICAgIC5jbGFzc2VkKGh5cGVuYXRlKG5hbWVzcGFjZSwnZGVmYXVsdC10YWInKSwgdHJ1ZSlcbiAgICAuY2xhc3NlZChcImFjdGl2ZVwiLCB0cnVlKVxuICAgIC5jbGFzc2VkKCd0ZXh0LWxlZnQnLCB0cnVlKSxcblxuICAgIHAgPSBzYWZlU2VsZWN0KGRlZmF1bHRUYWIsICdkaXYnKVxuICAgIC5odG1sKFxuICAgICAgXCJDbGljayA8a2JkPjxpIGNsYXNzPSdmYSBmYS1wbHVzIHRleHQtc3VjY2Vzcyc+PC9pPjwva2JkPiB0byBhZGQgYSBuZXcgZ3JvdXAuPGJyPlwiK1xuICAgICAgXCJDbGljayA8a2JkPjxpIGNsYXNzPSdmYSBmYS1wYXBlci1wbGFuZS1vIHRleHQtcHJpbWFyeSc+PC9pPjwva2JkPiB0byBzdWJtaXQgZm9yIHJlLWFuYWx5c2lzLjxicj5cIitcbiAgICAgIFwiQ2xpY2sgPGtiZD48aSBjbGFzcz0nZmEgZmEtd2luZG93LWNsb3NlLW8gdGV4dC1kYW5nZXInPjwvaT48L2tiZD4gdG8gY2xvc2UgdGhlIGRhdGFzZWxlY3Qgd2lkZ2V0LlwiXG4gICAgKVxuICAgIC8vIC50ZXh0KCdDbGljayB0aGUgZ3JlZW4gcGx1cyB0byBhZGQgYSBuZXcgZ3JvdXAuJylcbiAgfVxuXG5cbiAgZnVuY3Rpb24gbWFrZVJlbW92ZUJ1dHRvbihwYW5lQnRuTGlzdCkge1xuICAgIHZhclxuICAgIGJ0biA9IHNhZmVTZWxlY3QocGFuZUJ0bkxpc3QsICdidXR0b24nLCAncmVtb3ZlLWJ0bicpXG4gICAgLmNsYXNzZWQoJ2J0biBidG4tZGFuZ2VyJywgdHJ1ZSlcbiAgICAub24oJ2NsaWNrJywgcmVtb3ZlQnRuQ2xpY2tUb1JlbW92ZSksXG4gICAgaSA9IHNhZmVTZWxlY3QoYnRuLCAnaScsICdmYScpLmNsYXNzZWQoJ2ZhLXRyYXNoLW8nLCB0cnVlKSxcbiAgICBzID0gc2FmZVNlbGVjdChidG4sICdzcGFuJykudGV4dCgnUmVtb3ZlIGdyb3VwJylcbiAgICByZXR1cm4gYnRuXG4gIH1cblxuICBmdW5jdGlvbiByZW1vdmVCdG5DbGlja1RvUmVtb3ZlKGQsIGkpIHtcbiAgICB2YXJcbiAgICB0YWIgPSBzZWxlY3Rpb24uc2VsZWN0KCcjJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicsZCkpLFxuICAgIHBhbmUgPSBzZWxlY3Rpb24uc2VsZWN0KCcjJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicsJ3BhbmUnLGQpKVxuXG4gICAgdGFiLnJlbW92ZSgpXG4gICAgcGFuZS5yZW1vdmUoKVxuXG4gICAgc2hvd1JlbWFpbmluZ1BhbmVzKClcbiAgICB1cGRhdGVUYWJBbmRQYW5lTnVtYmVycygpXG4gIH1cblxuXG4gIGZ1bmN0aW9uIHRvZ2dsZVBhbmVCeUlkKGlkKSB7XG4gICAgdmFyIHBhbmVzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWNvbnRlbnQnKSlcbiAgICBwYW5lcy5zZWxlY3RBbGwoJ2Rpdi50YWItcGFuZScpXG4gICAgLmVhY2goZnVuY3Rpb24oZCwgaSl7XG4gICAgICBkMy5zZWxlY3QodGhpcykuY2xhc3NlZCgnYWN0aXZlIHNob3cnLCBkMy5zZWxlY3QodGhpcykuYXR0cignaWQnKSA9PSBpZClcbiAgICB9KVxuICB9XG5cbiAgZnVuY3Rpb24gaW5zZXJ0VGFiKHRhYnMsIG4pIHtcblxuICAgIC8vIHZhciBsaSA9IHRhYnMuaW5zZXJ0KFwibGlcIiwgJzpudGgtY2hpbGQoJysobikrJyknKVxuICAgIHZhciBsaSA9IHRhYnMuaW5zZXJ0KFwibGlcIiwgJy5yaWdodC1hbGlnbmVkLXRhYnMnKVxuICAgIC5jbGFzc2VkKCduYXYtaXRlbScsIHRydWUpXG4gICAgLmNsYXNzZWQoJ2FsZXJ0JywgdHJ1ZSlcbiAgICAuY2xhc3NlZCgnYWxlcnQtc2Vjb25kYXJ5JywgdHJ1ZSlcbiAgICAuY2xhc3NlZCgnYWxlcnQtZGlzbWlzc2FibGUnLCB0cnVlKVxuICAgIC5jbGFzc2VkKCdmYWRlJywgdHJ1ZSlcbiAgICAuY2xhc3NlZCgnc2hvdycsIHRydWUpXG4gICAgLy8gLmNsYXNzZWQoJ21yLWF1dG8nLCB0cnVlKVxuICAgIC5hdHRyKFwicm9sZVwiLCAnYWxlcnQnKVxuICAgIC5hdHRyKFwiaWRcIiwgaHlwZW5hdGUobmFtZXNwYWNlLCd0YWInLG4pKVxuICAgIC5jbGFzc2VkKGh5cGVuYXRlKG5hbWVzcGFjZSwndGFiJyksIHRydWUpXG5cbiAgICB2YXIgYSA9IHNhZmVTZWxlY3QobGksICdhJylcbiAgICAuYXR0cignZGF0YS10b2dnbGUnLCAndGFiJylcbiAgICAudGV4dCgnR3JvdXAgJyArIG4pXG4gICAgLmF0dHIoJ2hyZWYnLCAnIycraHlwZW5hdGUobmFtZXNwYWNlLCd0YWInLCdwYW5lJyxuKSlcbiAgICAub24oJ2RibGNsaWNrJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgICAgdC5hdHRyKCdjb250ZW50ZWRpdGFibGUnLCB0cnVlKVxuICAgICAgZDMuc2VsZWN0KHQubm9kZSgpLnBhcmVudE5vZGUpXG4gICAgICAuY2xhc3NlZCgnYWxlcnQtc2Vjb25kYXJ5JywgZmFsc2UpXG4gICAgICAuY2xhc3NlZCgnYWxlcnQtd2FybmluZycsIHRydWUpXG5cbiAgICAgIC8vIGQzLnNlbGVjdChcImh0bWxcIikub24oXCJjbGlja1wiLCBkaXNwYXRjaEJsdXIpXG4gICAgICAvL1xuICAgICAgLy8gZnVuY3Rpb24gZGlzcGF0Y2hCbHVyKGQsIGkpIHtcbiAgICAgIC8vICAgaWYgKGQzLmV2ZW50LnRhcmdldCAhPSBkMy5zZWxlY3QodGhpcykpIHtcbiAgICAgIC8vICAgICBkMy5zZWxlY3QodGhpcykuZGlzcGF0Y2goXCJibHVyXCIpXG4gICAgICAvLyAgIH1cbiAgICAgIC8vIH1cblxuXG4gICAgfSlcbiAgICAub24oJ2JsdXInLCBmdW5jdGlvbihkLCBpKXtcblxuICAgICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcylcbiAgICAgIHQuYXR0cignY29udGVudGVkaXRhYmxlJywgZmFsc2UpXG4gICAgICBkMy5zZWxlY3QodC5ub2RlKCkucGFyZW50Tm9kZSlcbiAgICAgIC5jbGFzc2VkKCdhbGVydC1zZWNvbmRhcnknLCB0cnVlKVxuICAgICAgLmNsYXNzZWQoJ2FsZXJ0LXdhcm5pbmcnLCBmYWxzZSlcblxuICAgIH0pXG4gICAgLm9uKCdpbnB1dCcsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcylcbiAgICAgIHZhciB0eHQgPSB0LnRleHQoKVxuICAgICAgZDMuc2VsZWN0KHQuYXR0cignaHJlZicpKS5zZWxlY3QoXCJwLmxlYWRcIikudGV4dCh0eHQpXG4gICAgfSlcblxuXG4gICAgcmV0dXJuIGxpXG4gIH1cblxuICBmdW5jdGlvbiBtYWtlVGFiUGFuZShwYW5lcywgbikge1xuICAgIHZhciBwYW5lID0gcGFuZXMuYXBwZW5kKCdkaXYnKVxuICAgIC5kYXR1bShuKVxuICAgIC5hdHRyKCdyb2xlJywgJ3RhYnBhbmVsJylcbiAgICAuY2xhc3NlZCgndGFiLXBhbmUnLCB0cnVlKVxuICAgIC5jbGFzc2VkKCd0ZXh0LWxlZnQnLCB0cnVlKVxuICAgIC5jbGFzc2VkKGh5cGVuYXRlKG5hbWVzcGFjZSwndGFiJywncGFuZScpLCB0cnVlKVxuICAgIC5hdHRyKCdpZCcsIGh5cGVuYXRlKG5hbWVzcGFjZSwndGFiJywncGFuZScsbikpXG4gICAgcmV0dXJuIHBhbmVcbiAgfVxuXG4gIGZ1bmN0aW9uIHBvcHVsYXRlUGFuZShwYW5lLCBuKSB7XG4gICAgdmFyXG4gICAgbGVhZCA9IHNhZmVTZWxlY3QocGFuZSwgJ3AnLCAnbGVhZCcpLnRleHQoJ0dyb3VwICcrbiksXG5cbiAgICB0YWJsZUNvbnRhaW5lciA9IHNhZmVTZWxlY3QocGFuZSwgJ2RpdicsICd0YWJsZS1yZXNwb25zaXZlJylcbiAgICAuYXR0cihcImNsYXNzXCIsIGh5cGVuYXRlKG5hbWVzcGFjZSwgXCJkYXRhLXRhYmxlXCIpKSxcblxuICAgIHRhYmxlID0gc2FmZVNlbGVjdCh0YWJsZUNvbnRhaW5lciwgXCJ0YWJsZVwiLCBcInRhYmxlXCIpXG4gICAgLmNsYXNzZWQoXCJ0YWJsZS1zbVwiLCB0cnVlKS5jbGFzc2VkKFwidGFibGUtaG92ZXJcIiwgdHJ1ZSksXG5cbiAgICBjYXB0aW9uID0gc2FmZVNlbGVjdCh0YWJsZSwgXCJjYXB0aW9uXCIsIFwiY2FwdGlvblwiKS5odG1sKFwiTGlzdCBvZiBzZWxlY3RlZFwiKSxcblxuICAgIGJ0bnMgPSBzYWZlU2VsZWN0KHBhbmUsICdkaXYnLCAndGV4dC1yaWdodCcpLFxuXG4gICAgY04gPSBuICUgY29sb3JGdW5jdGlvbi5jb2xvcnMoKS5sZW5ndGhcbiAgICBpZiAobiAlIDIgIT0gMCkgeyBjTiA9IChjb2xvckZ1bmN0aW9uLmNvbG9ycygpLmxlbmd0aCAtIDEpIC0gY04gfVxuXG5cblxuXG5cbiAgICB2YXIgbGFzc29CdG4gPSBtYWtlTGFzc29CdXR0b24oYnRucylcbiAgICB2YXIgY3VycmVudExhc3NvID0gbWFrZUxhc3NvRnVuY3Rpb24obiwgY29sb3JGdW5jdGlvbi5jb2xvcnMoKVtjTl0pXG4gICAgdmFyIGNsZWFyQnRuID0gbWFrZUNsZWFyQnV0dG9uKGJ0bnMpXG5cbiAgICBiaW5kQnV0dG9ucyhsYXNzb0J0biwgY2xlYXJCdG4sIGN1cnJlbnRMYXNzbywgdGFibGUpXG5cbiAgICB2YXIgcmVtb3ZlQnRuID0gbWFrZVJlbW92ZUJ1dHRvbihidG5zKVxuXG4gICAgbGVhZC5vbihcIm1vdXNlb3ZlclwiLCBmdW5jdGlvbigpe1xuICAgICAgY3VycmVudExhc3NvLmRyYXcoKVxuICAgIH0pXG4gICAgbGVhZC5vbihcIm1vdXNlb3V0XCIsIGZ1bmN0aW9uKCl7XG4gICAgICBjdXJyZW50TGFzc28ucmVtb3ZlKClcbiAgICB9KVxuXG4gIH1cblxuICBmdW5jdGlvbiBtYWtlTGFzc29CdXR0b24oYnRucykge1xuICAgIHZhciBidG4gPSBzYWZlU2VsZWN0KGJ0bnMsICdidXR0b24nLCAnbGFzc28tYnRuJylcbiAgICAuY2xhc3NlZCgnYnRuIGJ0bi1pbmZvJywgdHJ1ZSlcbiAgICAuY2xhc3NlZChuYW1lc3BhY2UsIHRydWUpXG4gICAgLy8gLmRhdHVtKFtsYXNzb10pXG4gICAgdmFyIGkgPSBzYWZlU2VsZWN0KGJ0biwgJ2knLCAnZmEnKS5jbGFzc2VkKCdmYS1oYW5kLXBvaW50ZXItbycsIHRydWUpXG4gICAgdmFyIHMgPSBzYWZlU2VsZWN0KGJ0biwgJ3NwYW4nKS50ZXh0KCdMYXNzbyBzZWxlY3QnKVxuICAgIHJldHVybiBidG5cblxuICB9XG5cbiAgZnVuY3Rpb24gbWFrZUNsZWFyQnV0dG9uKGJ0bnMpIHtcbiAgICB2YXIgYnRuID0gc2FmZVNlbGVjdChidG5zLCAnYnV0dG9uJywgJ2NsZWFyLWJ0bicpXG4gICAgLmNsYXNzZWQoJ2J0biBidG4tbGlnaHQnLCB0cnVlKVxuICAgIC5jbGFzc2VkKG5hbWVzcGFjZSwgdHJ1ZSlcbiAgICB2YXIgaSA9IHNhZmVTZWxlY3QoYnRuLCAnaScsICdmYScpLmNsYXNzZWQoJ2ZhLWVyYXNlcicsIHRydWUpXG4gICAgdmFyIHMgPSBzYWZlU2VsZWN0KGJ0biwgJ3NwYW4nKS50ZXh0KCdDbGVhciBzZWxlY3Rpb24nKVxuICAgIHJldHVybiBidG5cbiAgfVxuXG4gIGZ1bmN0aW9uIG1ha2VMYXNzb0Z1bmN0aW9uKGluc3RhbmNlLCBjb2xvcikge1xuICAgIHZhciBjdXJyZW50TGFzc28gPSBsYXNzbygpXG4gICAgLm5hbWVzcGFjZShuYW1lc3BhY2UpXG4gICAgLnN2ZyhzdmcpXG4gICAgLm9iamVjdENsYXNzKG9iamVjdENsYXNzKVxuICAgIC5jaGFydENvbnRhaW5lcihjaGFydENvbnRhaW5lcilcbiAgICAub2JqZWN0Q29udGFpbmVyKG9iamVjdENvbnRhaW5lcilcbiAgICAuaW5zdGFuY2UoaW5zdGFuY2UpXG4gICAgLmNvbG9yKGNvbG9yKVxuICAgIC55U2NhbGUoeVNjYWxlKVxuICAgIC54U2NhbGUoeFNjYWxlKVxuXG4gICAgcmV0dXJuIGN1cnJlbnRMYXNzb1xuICB9XG5cbiAgZnVuY3Rpb24gYmluZEJ1dHRvbnMobGFzc29CdG4sIGNsZWFyQnRuLCBjdXJyZW50TGFzc28sIHRhYmxlKSB7XG4gICAgY3VycmVudExhc3NvLmV2ZW50Q2F0Y2hlcihsYXNzb0J0bilcbiAgICBsYXNzb0J0bi5ub2RlKCkuYWRkRXZlbnRMaXN0ZW5lcihcImNsaWNrXCIsIGxhc3NvQnRuVG9nZ2xlKVxuXG4gICAgbGFzc29CdG4uZGF0dW0oW2N1cnJlbnRMYXNzb10pXG4gICAgLm9uKGh5cGVuYXRlKG5hbWVzcGFjZSwncmVuZGVyJyksIGZ1bmN0aW9uKCl7XG4gICAgICAgIGQzLnNlbGVjdCh0aGlzKS5kYXR1bSgpWzBdLmRyYXcoKVxuICAgIH0pXG4gICAgLm9uKGh5cGVuYXRlKG5hbWVzcGFjZSxcImRyYWdcIiksIGZ1bmN0aW9uKGxhcyl7XG4gICAgICB2YXIgbm9kZXMgPSBjaGFydENvbnRhaW5lci5zZWxlY3RBbGwoXCIuaW4tbGFzc28tXCIrbGFzWzBdLmluc3RhbmNlKCkpLm5vZGVzKClcbiAgICAgIHZhciB0YWJsZURhdGEgPSBub2Rlcy5tYXAoZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIHZhciBleHRyYWN0ZWQgPSBkYXRhRXh0cmFjdG9yKGQzLnNlbGVjdChkKS5kYXR1bSgpKVxuICAgICAgICBleHRyYWN0ZWRbXCJfX25vZGVcIl0gPSBkXG4gICAgICAgIHJldHVybiBleHRyYWN0ZWRcbiAgICAgIH0pXG5cblxuICAgICAgbWFrZVRhYmxlKHRhYmxlLCB0YWJsZURhdGEsIGN1cnJlbnRMYXNzbylcbiAgICB9KVxuXG4gICAgY2xlYXJCdG4ub24oJ2NsaWNrJywgZnVuY3Rpb24oKXtcbiAgICAgIGN1cnJlbnRMYXNzby5hbGxQb2ludHMoW10pXG4gICAgICBjdXJyZW50TGFzc28uY3VycmVudFBvaW50cyhbXSlcbiAgICAgIGxhc3NvQnRuLmRpc3BhdGNoKGh5cGVuYXRlKG5hbWVzcGFjZSxcImRyYWdcIikpXG5cbiAgICB9KVxuXG4gIH1cblxuICBmdW5jdGlvbiBsYXNzb0J0blRvZ2dsZSgpIHtcbiAgICB2YXIgdGhhdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgIHZhciBsYXMgPSB0aGF0LmRhdHVtKClbMF1cblxuICAgIGxhcy50b2dnbGUoKVxuICAgIHZhciBhY3RpdmVRID0gbGFzLmFjdGl2ZVEoKVxuXG4gICAgaWYgKGxhcy5hY3RpdmVRKCkpIHtcbiAgICAgIHRoYXQuY2xhc3NlZCgnYnRuLWluZm8nLCAhYWN0aXZlUSlcbiAgICAgIHRoYXQuY2xhc3NlZCgnYnRuLXdhcm5pbmcnLCBhY3RpdmVRKVxuICAgICAgdGhhdC5zZWxlY3QoXCJzcGFuXCIpLnRleHQoXCJMYXNzbyBzZWxlY3QgKGFjdGl2ZSlcIilcbiAgICAgIHNlbGVjdGlvbi5zZWxlY3RBbGwoXCIuXCIrbmFtZXNwYWNlK1wiLmxhc3NvLWJ0blwiKS5kaXNwYXRjaChoeXBlbmF0ZShuYW1lc3BhY2UsJ3JlbmRlcicpKVxuICAgICAgZDMuc2VsZWN0KFwiaHRtbFwiKS5ub2RlKCkuYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vkb3duJywgbW9uaXRvckxhc3NvQnV0dG9uU3RhdGUpXG4gICAgfSBlbHNlIHtcbiAgICAgIHRoYXQuY2xhc3NlZCgnYnRuLWluZm8nLCAhYWN0aXZlUSlcbiAgICAgIHRoYXQuY2xhc3NlZCgnYnRuLXdhcm5pbmcnLCBhY3RpdmVRKVxuICAgICAgdGhhdC5zZWxlY3QoXCJzcGFuXCIpLnRleHQoXCJMYXNzbyBzZWxlY3RcIilcbiAgICAgIGQzLnNlbGVjdChcImh0bWxcIikubm9kZSgpLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIG1vbml0b3JMYXNzb0J1dHRvblN0YXRlKVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIG1vbml0b3JMYXNzb0J1dHRvblN0YXRlKGV2ZW50KSB7XG4gICAgICAvKlxuICAgICAgYWN0aXZlTGFzc28gc3RvcHMgZXZlbnQgc3RvcFByb3BhZ2F0aW9uLCBzbyB0aGlzIGV2ZW50IHdpbGwgbm90IHJlZ2lzdGVyIGluXG4gICAgICB0aGF0IGNhc2UuIFRodXMgd2Ugb25seSBuZWVkIHRvIGVuc3VyZSB0aGF0IHRoZSB1c3JlIGlzIGNsaWNraW5nIG9uIHRoZSBsYXNzbyBidXR0b25cbiAgICAgIG90aGVyd2lzZSwgZGUtc3Bhd24gbGFzc28uXG4gICAgICAqL1xuICAgICAgaWYgKFxuICAgICAgICBldmVudC50YXJnZXQgIT0gdGhhdC5ub2RlKCkgJiZcbiAgICAgICAgZXZlbnQudGFyZ2V0ICE9IHRoYXQuc2VsZWN0KFwic3BhblwiKS5ub2RlKCkgJiZcbiAgICAgICAgZXZlbnQudGFyZ2V0ICE9IHRoYXQuc2VsZWN0KFwiaVwiKS5ub2RlKClcbiAgICAgICkge1xuICAgICAgICAvLyBldmVudC5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgIC8vIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXG4gICAgICAgIGxhcy50b2dnbGUoZmFsc2UpXG4gICAgICAgIHRoYXQuY2xhc3NlZCgnYnRuLWluZm8nLCB0cnVlKVxuICAgICAgICB0aGF0LmNsYXNzZWQoJ2J0bi13YXJuaW5nJywgZmFsc2UpXG4gICAgICAgIHRoYXQuc2VsZWN0KFwic3BhblwiKS50ZXh0KFwiTGFzc28gc2VsZWN0XCIpXG4gICAgICAgIC8vIGNvbnNvbGUubG9nKHRoYXQsIHRoYXQubm9kZSgpKVxuICAgICAgICAvLyB0aGF0LmRpc3BhdGNoKFwiZm9jdXNvdXRcIilcbiAgICAgIH1cbiAgICB9XG5cbiAgfVxuXG5cbiAgZnVuY3Rpb24gdXBkYXRlVGFibGVIZWFkZXJDb2x1bW5zKGhlYWRSLCB0YWJsZURhdGEpIHtcbiAgICB2YXIgaGVhZGVyS2V5cyA9IGQzLmtleXModGFibGVEYXRhWzBdKS5maWx0ZXIoaz0+ayE9XCJfX25vZGVcIilcbiAgICBpZiAoaGVhZGVyS2V5cy5sZW5ndGggPiAwKSB7XG4gICAgICAvLyBoZWFkZXJLZXlzID0gW1wicmVtb3ZlXCJdLmNvbmNhdChoZWFkZXJLZXlzKVxuICAgICAgaGVhZGVyS2V5cy5wdXNoKFwicmVtb3ZlXCIpXG4gICAgfVxuXG4gICAgdmFyIGhlYWRlckNvbHMgPSBoZWFkUi5zZWxlY3RBbGwoXCJ0aFwiKVxuXG4gICAgaGVhZGVyQ29scyA9IGhlYWRlckNvbHMuZGF0YShoZWFkZXJLZXlzKVxuICAgIGhlYWRlckNvbHMuZXhpdCgpLnJlbW92ZSgpXG4gICAgaGVhZGVyQ29scyA9IGhlYWRlckNvbHMubWVyZ2UoaGVhZGVyQ29scy5lbnRlcigpLmFwcGVuZChcInRoXCIpLmF0dHIoXCJzY29wZVwiLFwiY29sXCIpKVxuICAgIC50ZXh0KGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkfSlcblxuICAgIHJldHVybiBoZWFkZXJLZXlzXG4gIH1cblxuICBmdW5jdGlvbiB1cGRhdGVUYWJsZVJvd3MoYm9keSwgdGFibGVEYXRhKSB7XG4gICAgdmFyIGJvZHlSb3dzID0gYm9keS5zZWxlY3RBbGwoXCJ0clwiKVxuXG4gICAgYm9keVJvd3MgPSBib2R5Um93cy5kYXRhKHRhYmxlRGF0YSlcbiAgICBib2R5Um93cy5leGl0KCkucmVtb3ZlKClcbiAgICBib2R5Um93cyA9IGJvZHlSb3dzLm1lcmdlKGJvZHlSb3dzLmVudGVyKCkuYXBwZW5kKFwidHJcIikpXG5cbiAgICByZXR1cm4gYm9keVJvd3NcbiAgfVxuXG4gIGZ1bmN0aW9uIHVwZGF0ZVRhYmxlUm93Q29sdW1ucyhjb2xzLCBoZWFkZXJLZXlzLCByb3dEYXRhLCB0YWJsZURhdGEsIGxhc3NvLCB0YWJsZSkge1xuICAgIGNvbHMgPSBjb2xzLmRhdGEoaGVhZGVyS2V5cylcbiAgICBjb2xzLmV4aXQoKS5yZW1vdmUoKVxuICAgIGNvbHMgPSBjb2xzLm1lcmdlKGNvbHMuZW50ZXIoKS5hcHBlbmQoXCJ0ZFwiKSlcblxuICAgIGNvbHMuaHRtbChmdW5jdGlvbihkLCBpKXtcbiAgICAgIGlmIChkICE9IFwicmVtb3ZlXCIpIHsgcmV0dXJuIHJvd0RhdGFbZF0gfVxuICAgICAgcmV0dXJuIFwiPGkgY2xhc3M9J2ZhIGZhLWNsb3NlJz48L2k+XCJcbiAgICB9KS5jbGFzc2VkKFwidGV4dC1sZWZ0XCIsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgaWYgKGQgIT0gXCJyZW1vdmVcIikgeyByZXR1cm4gZmFsc2UgfVxuICAgICAgcmV0dXJuIHRydWVcbiAgICB9KVxuICAgIC8vIC5hdHRyKFwic2NvcGVcIixmdW5jdGlvbihkLCBpKXtcbiAgICAvLyAgIGlmIChkICE9IFwicmVtb3ZlXCIpIHsgcmV0dXJuIGZhbHNlIH1cbiAgICAvLyAgIHJldHVybiB0cnVlXG4gICAgLy8gfSlcblxuICAgIGNvbHMuc2VsZWN0KFwiaS5mYS1jbG9zZVwiKS5vbihcImNsaWNrXCIsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgdmFyIG5vZGUgPSByb3dEYXRhW1wiX19ub2RlXCJdLFxuICAgICAgbiA9IGQzLnNlbGVjdChub2RlKVxuICAgICAgbi5jbGFzc2VkKFwiaW4tbGFzc29cIiwgZmFsc2UpXG4gICAgICBuLmNsYXNzZWQoXCJpbi1sYXNzby1cIitsYXNzby5pbnN0YW5jZSgpLCBmYWxzZSlcblxuICAgICAgdGFibGVEYXRhLm1hcChmdW5jdGlvbihkZCwgail7XG4gICAgICAgIGlmIChkZFtcIl9fbm9kZVwiXSA9PSBub2RlKSB7XG4gICAgICAgICAgdGFibGVEYXRhLnNwbGljZShqLCAxKVxuICAgICAgICAgIG1ha2VUYWJsZSh0YWJsZSwgdGFibGVEYXRhLCBsYXNzbylcbiAgICAgICAgfVxuICAgICAgfSlcblxuICAgIH0pXG4gIH1cblxuICBmdW5jdGlvbiBtYWtlVGFibGUodGFibGUsIHRhYmxlRGF0YSwgbGFzc28pIHtcbiAgICB2YXJcbiAgICBoZWFkID0gc2FmZVNlbGVjdCh0YWJsZSwgXCJ0aGVhZFwiKSxcbiAgICBoZWFkUiA9IHNhZmVTZWxlY3QoaGVhZCwgXCJ0clwiKSxcbiAgICBib2R5ID0gc2FmZVNlbGVjdCh0YWJsZSwgXCJ0Ym9keVwiKSxcblxuICAgIGhlYWRlcktleXMgPSB1cGRhdGVUYWJsZUhlYWRlckNvbHVtbnMoaGVhZFIsIHRhYmxlRGF0YSksXG5cbiAgICBib2R5Um93cyA9IHVwZGF0ZVRhYmxlUm93cyhib2R5LCB0YWJsZURhdGEpXG5cbiAgICBib2R5Um93cy5lYWNoKGZ1bmN0aW9uKHJvd0RhdGEsIGkpe1xuICAgICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcylcbiAgICAgIHZhciBjb2xzID0gdC5zZWxlY3RBbGwoXCJ0ZFwiKVxuXG4gICAgICB1cGRhdGVUYWJsZVJvd0NvbHVtbnMoY29scywgaGVhZGVyS2V5cywgcm93RGF0YSwgdGFibGVEYXRhLCBsYXNzbywgdGFibGUpXG5cbiAgICAgIHQub24oXCJtb3VzZW92ZXJcIiwgZnVuY3Rpb24oZCwgaSkge1xuICAgICAgICBsYXNzby5hcHBseU9iamVjdEF0dHJpYnV0ZXMoZDMuc2VsZWN0KGRbXCJfX25vZGVcIl0pLHRydWUpXG4gICAgICB9KVxuICAgICAgLm9uKFwibW91c2VvdXRcIiwgZnVuY3Rpb24oZCwgaSkge1xuICAgICAgICBsYXNzby5hcHBseU9iamVjdEF0dHJpYnV0ZXMoZDMuc2VsZWN0KGRbXCJfX25vZGVcIl0pLGZhbHNlKVxuICAgICAgfSlcbiAgICB9KVxuXG5cbiAgfVxuXG5cblxuXG5cblxuXG4gIGZ1bmN0aW9uIG1ha2VOZXdHcm91cChkLCBpKSB7XG5cbiAgICB2YXIgdGFicyA9IHNlbGVjdGlvbi5zZWxlY3QoJy4nK2h5cGVuYXRlKG5hbWVzcGFjZSwgJ3RhYi1saXN0JykpLFxuICAgIHBhbmVzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWNvbnRlbnQnKSksXG4gICAgbiA9IG5ld0dyb3VwTnVtYmVyKClcblxuICAgIGlmICh0YWJzLnNlbGVjdEFsbCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCd0YWInKSkuc2l6ZSgpID09IG1heE51bWJlck9mR3JvdXBzKSB7XG4gICAgICBvbkVycm9yKCdvbmx5ICcrbWF4TnVtYmVyT2ZHcm91cHMrJyBhbGxvd2VkLicsICd3YXJuaW5nJylcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHZhclxuICAgIHRhYiA9IGluc2VydFRhYih0YWJzLCBuKSxcbiAgICBwYW5lID0gbWFrZVRhYlBhbmUocGFuZXMsIG4pXG5cbiAgICBwb3B1bGF0ZVBhbmUocGFuZSwgbilcbiAgICB0b2dnbGVQYW5lQnlJZChwYW5lLmF0dHIoJ2lkJykpXG5cbiAgfVxuXG4gIGZ1bmN0aW9uIG5ld0dyb3VwTnVtYmVyKCkge1xuICAgIHZhciB0YWJzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWxpc3QnKSkvLyxcbiAgICB2YXJcbiAgICBuID0gdGFicy5zZWxlY3RBbGwoJ2xpJykuc2l6ZSgpIC0gMywgLy8gbWludXMgMSBmb3IgcGx1cyB0YWJcbiAgICBzID0gJ0dyb3VwICcgKyBuLFxuICAgIGdzID0gW11cbiAgICB0YWJzLmVhY2goZnVuY3Rpb24oZCwgaSl7IHJldHVybiBncy5wdXNoKGQzLnNlbGVjdCh0aGlzKS5zZWxlY3QoXCJhXCIpLnRleHQoKSkgfSlcbiAgICB3aGlsZSAoZ3MuaW5jbHVkZXMocykpIHsgbis9MTsgcyA9ICdHcm91cCAnICsgbjsgfVxuICAgIHJldHVybiBuXG4gIH1cblxuICBmdW5jdGlvbiB1cGRhdGVUYWJBbmRQYW5lTnVtYmVycygpIHtcbiAgICB2YXJcbiAgICB0YWJzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWxpc3QnKSksXG4gICAgcGFuZXMgPSBzZWxlY3Rpb24uc2VsZWN0KCcuJytoeXBlbmF0ZShuYW1lc3BhY2UsICd0YWItY29udGVudCcpKVxuXG4gICAgdGFicyA9IHRhYnMuc2VsZWN0QWxsKCcuJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicpKVxuICAgIHBhbmVzID0gcGFuZXMuc2VsZWN0QWxsKCcuJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicsICdwYW5lJykpXG5cbiAgICB0YWJzLmVhY2goZnVuY3Rpb24oZCwgaSl7XG4gICAgICBkMy5zZWxlY3QodGhpcykuZGF0dW0oaSlcbiAgICAgIC5hdHRyKFwiaWRcIiwgaHlwZW5hdGUobmFtZXNwYWNlLCd0YWInLGkpKVxuICAgICAgLnNlbGVjdCgnYScpXG4gICAgICAuYXR0cignaHJlZicsICcjJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicsJ3BhbmUnLGkpKVxuICAgICAgLnRleHQoZnVuY3Rpb24oZGQsIGlpKXtcbiAgICAgICAgdmFyIGN1clRleHQgPSBkMy5zZWxlY3QodGhpcykudGV4dCgpO1xuICAgICAgICBpZiAoY3VyVGV4dC5zcGxpdCgnICcpWzBdID09ICdHcm91cCcpIHtcbiAgICAgICAgICAgIHJldHVybiAnR3JvdXAgJyArIGlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gY3VyVGV4dFxuICAgICAgfSlcbiAgICB9KVxuXG4gICAgcGFuZXMuZWFjaChmdW5jdGlvbihkLCBpKXtcbiAgICAgIGQzLnNlbGVjdCh0aGlzKS5kYXR1bShpKVxuICAgICAgLmF0dHIoJ2lkJywgaHlwZW5hdGUobmFtZXNwYWNlLCd0YWInLCdwYW5lJyxpKSlcbiAgICAgIHNhZmVTZWxlY3QoZDMuc2VsZWN0KHRoaXMpLCAncCcsICdsZWFkJylcbiAgICAgIC50ZXh0KGZ1bmN0aW9uKGRkLCBpaSl7XG4gICAgICAgIHZhciBjdXJUZXh0ID0gZDMuc2VsZWN0KHRoaXMpLnRleHQoKTtcbiAgICAgICAgaWYgKGN1clRleHQuc3BsaXQoJyAnKVswXSA9PSAnR3JvdXAnKSB7XG4gICAgICAgICAgICByZXR1cm4gJ0dyb3VwICcgKyBpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGN1clRleHRcbiAgICAgIH0pXG4gICAgICBzYWZlU2VsZWN0KGQzLnNlbGVjdCh0aGlzKSwgJ2J1dHRvbicsICdyZW1vdmUtYnRuJylcbiAgICAgIC5vbignY2xpY2snLCByZW1vdmVCdG5DbGlja1RvUmVtb3ZlKVxuICAgIH0pXG5cbiAgfVxuXG4gIGZ1bmN0aW9uIGdhdGhlckRhdGFMaXN0cygpIHtcbiAgICB2YXIgdGFicyA9IHNlbGVjdGlvbi5zZWxlY3QoJy4nK2h5cGVuYXRlKG5hbWVzcGFjZSwgJ3RhYi1saXN0JykpXG4gICAgdmFyIHBhbmVzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWNvbnRlbnQnKSlcbiAgICB2YXIgdGFibGVzID0gcGFuZXMuc2VsZWN0QWxsKCcuJytoeXBlbmF0ZShuYW1lc3BhY2UsIFwiZGF0YS10YWJsZVwiKSlcbiAgICB2YXIgZGF0YSA9IHt9XG5cbiAgICB2YXIgdGV4dEdyb3VwcyA9IHRhYnMuc2VsZWN0QWxsKCdsaS4nK2h5cGVuYXRlKG5hbWVzcGFjZSwndGFiJykgKyAnID4gYScpXG4gICAgLm5vZGVzKCkubWFwKGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkMy5zZWxlY3QoZCkudGV4dCgpfSlcblxuICAgIHRleHRHcm91cHMubWFwKGZ1bmN0aW9uKGUsIGkpe1xuICAgICAgZGF0YVtlXSA9IFtdXG4gICAgfSlcblxuXG4gICAgdGFibGVzLmVhY2goZnVuY3Rpb24oZCwgaSl7XG4gICAgICB2YXIgdGFibGUgPSBkMy5zZWxlY3QodGhpcykuc2VsZWN0KFwidGJvZHlcIilcbiAgICAgIGRhdGFbdGV4dEdyb3Vwc1tpXV0gPSB0YWJsZS5zZWxlY3RBbGwoJ3RyJykuZGF0YSgpXG4gICAgfSlcblxuICAgIHJldHVybiBkYXRhXG4gIH1cblxuICBmdW5jdGlvbiBzaG93UmVtYWluaW5nUGFuZXMoKSB7XG4gICAgdmFyXG4gICAgdGFicyA9IHNlbGVjdGlvbi5zZWxlY3QoJy4nK2h5cGVuYXRlKG5hbWVzcGFjZSwgJ3RhYi1saXN0JykpLFxuICAgIHBhbmVzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWNvbnRlbnQnKSksXG4gICAgcmVtYWluaW5nVGFicyA9IHRhYnMuc2VsZWN0QWxsKCcuJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicpKVxuXG4gICAgaWYgKHJlbWFpbmluZ1RhYnMuc2l6ZSgpID09IDApIHtcbiAgICAgIHBhbmVzLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCdkZWZhdWx0LXRhYicpKVxuICAgICAgLmNsYXNzZWQoXCJhY3RpdmVcIiwgdHJ1ZSlcbiAgICAgIC5jbGFzc2VkKCd0ZXh0LWxlZnQnLCB0cnVlKVxuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIHZhciBsYXN0VGFiID0gcmVtYWluaW5nVGFicy5ub2RlcygpW3JlbWFpbmluZ1RhYnMuc2l6ZSgpLTFdLFxuICAgICAgbGFzdFBhbmVJZCA9IGQzLnNlbGVjdChsYXN0VGFiKS5zZWxlY3QoJ2EnKS5hdHRyKCdocmVmJylcbiAgICAgIHBhbmVzLnNlbGVjdChsYXN0UGFuZUlkKVxuICAgICAgLmNsYXNzZWQoXCJhY3RpdmVcIiwgdHJ1ZSlcbiAgICAgIC5jbGFzc2VkKCd0ZXh0LWxlZnQnLCB0cnVlKVxuICAgIH1cbiAgfVxuXG5cbiAgcmV0dXJuIGxhc3NvV2lkZ2V0XG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHtzZXR1cENvbnRhaW5lciwgY2FsY3VsYXRlV2lkdGhPZk9iamVjdCwgY2FsY3VsYXRlV2lkdGhPZlNwYWNlciwgbG9nfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7dW5pcXVlLCBmbGF0dGVufSBmcm9tICcuL2FycmF5LWZ1bmN0aW9ucyc7XG5pbXBvcnQge2dyb3VwaW5nU3BhY2VyfSBmcm9tICcuL2dyb3VwaW5nLXNwYWNlcic7XG5pbXBvcnQge2NvbG9yRnVuY3Rpb24gYXMgQ0Z9IGZyb20gJy4vY29sb3ItZnVuY3Rpb24nO1xuaW1wb3J0IHt0b29sdGlwIGFzIFRUaXB9IGZyb20gJy4vdG9vbHRpcCc7XG5leHBvcnQgZnVuY3Rpb24gdXBzZXQgKCBzZWxlY3Rpb24gKSB7XG4gIHZhclxuICBkYXRhLFxuICBvcmllbnQ9J2hvcml6b250YWwnLFxuICBzcGFjZVgsXG4gIHNwYWNlWSxcbiAgb3ZlcmZsb3dRPWZhbHNlLFxuICBtaW5PYmplY3RTaXplPTIwLFxuICBtYXhPYmplY3RTaXplPTUwLFxuICBjaXJjbGVTdHJva2VXaWR0aD0yLFxuICAvLyBjb2xvckZ1bmN0aW9uPVxuICBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCcsXG4gIG5hbWVzcGFjZT0nZDNzbS11cHNldCcsXG4gIG9iamVjdENsYXNzID0gJ3Vwc2V0JyxcblxuICB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwLFxuICBlYXNlRnVuYyA9IGQzLmVhc2VFeHAsXG5cbiAgc2V0S2V5ID0gXCJzZXRcIixcbiAgaW50ZXJzZWN0aW9uS2V5ID0gXCJpbnRlcnNlY3Rpb25cIixcbiAgZWxlbWVudHNLZXkgPSBcImVsZW1lbnRzXCIsXG5cbiAgc2V0RXh0cmFjdG9yID0gZnVuY3Rpb24oa2V5LCBpKSB7cmV0dXJuIGRhdGFba2V5XVtzZXRLZXldfSxcbiAgaW50ZXJzZWN0aW9uRXh0cmFjdG9yID0gZnVuY3Rpb24oa2V5LCBpKSB7cmV0dXJuIGRhdGFba2V5XVtpbnRlcnNlY3Rpb25LZXldfSxcbiAgZWxlbWVudEV4dHJhY3RvciA9IGZ1bmN0aW9uKGtleSwgaSkge3JldHVybiBkYXRhW2tleV1bZWxlbWVudHNLZXldfSxcblxuICBjZWxsS2V5cyxcbiAgc2V0VmFsdWVzLFxuICBpbnRlcnNlY3Rpb25WYWx1ZXMsXG4gIGVsZW1lbnRWYWx1ZXMsXG5cbiAgeE9iamVjdFNwYWNlciA9IDAuMDUsXG4gIHlPYmplY3RTcGFjZXIgPSAwLjA1LFxuICByYWRpdXMsXG5cbiAgLy8gbGlzdERlbGltID0gJzsnXG5cbiAgeU9iamVjdFNpemUsXG4gIHlTcGFjZXJTaXplLFxuICB4T2JqZWN0U2l6ZSxcbiAgeFNwYWNlclNpemUsXG5cbiAgc2V0S2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4gc2V0VmFsdWVzLmluZGV4T2Yoc2V0RXh0cmFjdG9yKGEpKSAtIHNldFZhbHVlcy5pbmRleE9mKHNldEV4dHJhY3RvcihiKSkgfSxcbiAgaW50ZXJzZWN0aW9uS2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4gaW50ZXJzZWN0aW9uVmFsdWVzLmluZGV4T2YoaW50ZXJzZWN0aW9uRXh0cmFjdG9yKGEpKSAtIGludGVyc2VjdGlvblZhbHVlcy5pbmRleE9mKGludGVyc2VjdGlvbkV4dHJhY3RvcihiKSkgfVxuXG5cbiAgdXBzZXQuc2VsZWN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCB1cHNldCkgOiBzZWxlY3Rpb247IH07XG4gIHVwc2V0LmRhdGEgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRhdGEgPSBfLCB1cHNldCkgOiBkYXRhOyB9O1xuICB1cHNldC5vcmllbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9yaWVudCA9IF8sIHVwc2V0KSA6IG9yaWVudDsgfTtcbiAgdXBzZXQuc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVggPSBfLCB1cHNldCkgOiBzcGFjZVg7IH07XG4gIHVwc2V0LnNwYWNlWSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3BhY2VZID0gXywgdXBzZXQpIDogc3BhY2VZOyB9O1xuICB1cHNldC5vdmVyZmxvd1EgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG92ZXJmbG93USA9IF8sIHVwc2V0KSA6IG92ZXJmbG93UTsgfTtcbiAgdXBzZXQubWluT2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWluT2JqZWN0U2l6ZSA9IF8sIHVwc2V0KSA6IG1pbk9iamVjdFNpemU7IH07XG4gIHVwc2V0Lm1heE9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1heE9iamVjdFNpemUgPSBfLCB1cHNldCkgOiBtYXhPYmplY3RTaXplOyB9O1xuICB1cHNldC5jaXJjbGVTdHJva2VXaWR0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY2lyY2xlU3Ryb2tlV2lkdGggPSBfLCB1cHNldCkgOiBjaXJjbGVTdHJva2VXaWR0aDsgfTtcbiAgdXBzZXQuYmFja2dyb3VuZEZpbGwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhY2tncm91bmRGaWxsID0gXywgdXBzZXQpIDogYmFja2dyb3VuZEZpbGw7IH07XG4gIHVwc2V0Lm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgdXBzZXQpIDogbmFtZXNwYWNlOyB9O1xuICB1cHNldC5vYmplY3RDbGFzcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3MgPSBfLCB1cHNldCkgOiBvYmplY3RDbGFzczsgfTtcbiAgdXBzZXQudHJhbnNpdGlvbkR1cmF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0cmFuc2l0aW9uRHVyYXRpb24gPSBfLCB1cHNldCkgOiB0cmFuc2l0aW9uRHVyYXRpb247IH07XG4gIHVwc2V0LmVhc2VGdW5jID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChlYXNlRnVuYyA9IF8sIHVwc2V0KSA6IGVhc2VGdW5jOyB9O1xuICB1cHNldC5jZWxsS2V5cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY2VsbEtleXMgPSBfLCB1cHNldCkgOiBjZWxsS2V5czsgfTtcbiAgdXBzZXQuc2V0VmFsdWVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZXRWYWx1ZXMgPSBfLCB1cHNldCkgOiBzZXRWYWx1ZXM7IH07XG4gIHVwc2V0LmludGVyc2VjdGlvblZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoaW50ZXJzZWN0aW9uVmFsdWVzID0gXywgdXBzZXQpIDogaW50ZXJzZWN0aW9uVmFsdWVzOyB9O1xuICB1cHNldC54T2JqZWN0U3BhY2VyID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4T2JqZWN0U3BhY2VyID0gXywgdXBzZXQpIDogeE9iamVjdFNwYWNlcjsgfTtcbiAgdXBzZXQueU9iamVjdFNwYWNlciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeU9iamVjdFNwYWNlciA9IF8sIHVwc2V0KSA6IHlPYmplY3RTcGFjZXI7IH07XG4gIHVwc2V0LnJhZGl1cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocmFkaXVzID0gXywgdXBzZXQpIDogcmFkaXVzOyB9O1xuICB1cHNldC5zZXRFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNldEV4dHJhY3RvciA9IF8sIHVwc2V0KSA6IHNldEV4dHJhY3RvcjsgfTtcbiAgdXBzZXQuaW50ZXJzZWN0aW9uRXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChpbnRlcnNlY3Rpb25FeHRyYWN0b3IgPSBfLCB1cHNldCkgOiBpbnRlcnNlY3Rpb25FeHRyYWN0b3I7IH07XG4gIHVwc2V0LmVsZW1lbnRFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGVsZW1lbnRFeHRyYWN0b3IgPSBfLCB1cHNldCkgOiBlbGVtZW50RXh0cmFjdG9yOyB9O1xuICB1cHNldC5zZXRLZXlTb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNldEtleVNvcnRpbmdGdW5jdGlvbiA9IF8sIHVwc2V0KSA6IHNldEtleVNvcnRpbmdGdW5jdGlvbjsgfTtcbiAgdXBzZXQuaW50ZXJzZWN0aW9uS2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChpbnRlcnNlY3Rpb25LZXlTb3J0aW5nRnVuY3Rpb24gPSBfLCB1cHNldCkgOiBpbnRlcnNlY3Rpb25LZXlTb3J0aW5nRnVuY3Rpb247IH07XG5cbiAgdXBzZXQueU9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlPYmplY3RTaXplID0gXywgdXBzZXQpIDogeU9iamVjdFNpemU7IH07XG4gIHVwc2V0LnlTcGFjZXJTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5U3BhY2VyU2l6ZSA9IF8sIHVwc2V0KSA6IHlTcGFjZXJTaXplOyB9O1xuICB1cHNldC54T2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeE9iamVjdFNpemUgPSBfLCB1cHNldCkgOiB4T2JqZWN0U2l6ZTsgfTtcbiAgdXBzZXQueFNwYWNlclNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhTcGFjZXJTaXplID0gXywgdXBzZXQpIDogeFNwYWNlclNpemU7IH07XG5cbiAgZnVuY3Rpb24gdXBzZXQoKSB7XG4gICAgLy8gZm9yIGNvbnZlbmllbmNlIGluIGhhbmRsaW5nIG9yaWVudGF0aW9uIHNwZWNpZmljIHZhbHVlc1xuICAgIHZhciBob3Jpem9udGFsUSA9IChvcmllbnQgPT0gJ2hvcml6b250YWwnKSA/IHRydWUgOiBmYWxzZVxuICAgIHZhciB2ZXJ0aWNhbFEgPSAhaG9yaXpvbnRhbFFcblxuICAgIC8vIGJhY2tncm91bmQgY2xpcGluZyByZWN0YW5nbGVcbiAgICB2YXIgYmdjcFJlY3QgPSB7eDowLCB5OjAsIHdpZHRoOiBzcGFjZVgsIGhlaWdodDpzcGFjZVl9XG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cblxuICAgIGNlbGxLZXlzID0gZDMua2V5cyhkYXRhKVxuICAgIHNldFZhbHVlcyA9IHVuaXF1ZShjZWxsS2V5cy5tYXAoc2V0RXh0cmFjdG9yKSkuc29ydCgpXG4gICAgaW50ZXJzZWN0aW9uVmFsdWVzID0gdW5pcXVlKGNlbGxLZXlzLm1hcChpbnRlcnNlY3Rpb25FeHRyYWN0b3IpKS5zb3J0KCkuc29ydChmdW5jdGlvbihhLCBiKXtcbiAgICAgIHJldHVybiBhLnNwbGl0KCc7JykubGVuZ3RoIC0gYi5zcGxpdCgnOycpLmxlbmd0aFxuICAgIH0pXG5cbiAgICBpZiAoIWhvcml6b250YWxRKSB7XG4gICAgICBjZWxsS2V5cy5zb3J0KGZ1bmN0aW9uKGEsIGIpeyByZXR1cm4gc2V0S2V5U29ydGluZ0Z1bmN0aW9uKGEsIGIpIHx8IGludGVyc2VjdGlvbktleVNvcnRpbmdGdW5jdGlvbihhLCBiKSB9KVxuICAgIH0gZWxzZSB7XG4gICAgICBjZWxsS2V5cy5zb3J0KGZ1bmN0aW9uKGEsIGIpeyByZXR1cm4gaW50ZXJzZWN0aW9uS2V5U29ydGluZ0Z1bmN0aW9uKGEsIGIpIHx8IHNldEtleVNvcnRpbmdGdW5jdGlvbihhLCBiKSB9KVxuICAgIH1cblxuXG5cblxuICAgIHZhclxuICAgIHhWYWx1ZXMgPSBob3Jpem9udGFsUSA/IGludGVyc2VjdGlvblZhbHVlcyA6IHNldFZhbHVlcyxcbiAgICB5VmFsdWVzID0gaG9yaXpvbnRhbFEgPyBzZXRWYWx1ZXMgOiBpbnRlcnNlY3Rpb25WYWx1ZXMsXG4gICAgeERpbSA9IGhvcml6b250YWxRID8geFZhbHVlcy5sZW5ndGggOiB5VmFsdWVzLmxlbmd0aCxcbiAgICB5RGltID0gaG9yaXpvbnRhbFEgPyB5VmFsdWVzLmxlbmd0aCA6IHhWYWx1ZXMubGVuZ3RoXG5cbiAgICAvLyBjb25zb2xlLmxvZyh4VmFsdWVzLCB5VmFsdWVzKVxuXG5cbiAgICB4T2JqZWN0U2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3Qoc3BhY2VYLCB4RGltLCBtaW5PYmplY3RTaXplLCBtYXhPYmplY3RTaXplLCB4T2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgeU9iamVjdFNpemUgPSBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0KHNwYWNlWSwgeURpbSwgbWluT2JqZWN0U2l6ZSwgbWF4T2JqZWN0U2l6ZSwgeU9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIHhTcGFjZXJTaXplID0gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcih4VmFsdWVzLCBzcGFjZVgsIHhPYmplY3RTaXplLCB4RGltLCB4T2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgeVNwYWNlclNpemUgPSBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyKHlWYWx1ZXMsIHNwYWNlWSwgeU9iamVjdFNpemUsIHlEaW0sIHlPYmplY3RTcGFjZXIsIG92ZXJmbG93USlcblxuICAgIHZhciB5U3BhY2VyID0gZ3JvdXBpbmdTcGFjZXIoKVxuICAgIC5ob3Jpem9udGFsUShmYWxzZSlcbiAgICAubW92ZWJ5KCdjYXRlZ29yeScpLm51bWJlck9mT2JqZWN0cyh5RGltKVxuICAgIC5vYmplY3RTaXplKHlPYmplY3RTaXplKS5zcGFjZXJTaXplKHlTcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcblxuICAgIHZhciB4U3BhY2VyID0gZ3JvdXBpbmdTcGFjZXIoKVxuICAgIC5ob3Jpem9udGFsUSh0cnVlKVxuICAgIC5tb3ZlYnkoJ2NhdGVnb3J5JykubnVtYmVyT2ZPYmplY3RzKHhEaW0pXG4gICAgLm9iamVjdENsYXNzKG9iamVjdENsYXNzKVxuICAgIC5vYmplY3RTaXplKHhPYmplY3RTaXplKS5zcGFjZXJTaXplKHhTcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcblxuXG5cbiAgICBpZiAodmVydGljYWxRKSB7XG4gICAgICB4U3BhY2VyLm9iamVjdENsYXNzKG9iamVjdENsYXNzKVxuICAgICAgeVNwYWNlci5uYW1lc3BhY2UoJ2Fjcm9zcycpLm9iamVjdENsYXNzKGh5cGVuYXRlKG9iamVjdENsYXNzLCAnYWNyb3NzJykpXG5cbiAgICAgIHlTcGFjZXIoY29udGFpbmVyLCB5VmFsdWVzLCAwKVxuICAgICAgY29udGFpbmVyLnNlbGVjdEFsbCgnZy4nK2h5cGVuYXRlKG9iamVjdENsYXNzLCAnYWNyb3NzJykpXG4gICAgICAuZWFjaChmdW5jdGlvbihkLCBpKXsgeFNwYWNlcihkMy5zZWxlY3QodGhpcyksIHhWYWx1ZXMsIDApIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHhTcGFjZXIubmFtZXNwYWNlKCdhY3Jvc3MnKS5vYmplY3RDbGFzcyhoeXBlbmF0ZShvYmplY3RDbGFzcywgJ2Fjcm9zcycpKVxuICAgICAgeVNwYWNlci5vYmplY3RDbGFzcyhvYmplY3RDbGFzcylcblxuICAgICAgeFNwYWNlcihjb250YWluZXIsIHhWYWx1ZXMsIDApXG4gICAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnLicraHlwZW5hdGUob2JqZWN0Q2xhc3MsICdhY3Jvc3MnKSlcbiAgICAgIC5lYWNoKGZ1bmN0aW9uKGQsIGkpeyB5U3BhY2VyKGQzLnNlbGVjdCh0aGlzKSwgeVZhbHVlcywgMCkgfSlcbiAgICB9XG5cblxuICAgIHZhciBjZWxscyA9IGNvbnRhaW5lci5zZWxlY3RBbGwoJ2c6bm90KC50by1yZW1vdmUpLicrb2JqZWN0Q2xhc3MpXG4gICAgdmFyIGxvb2t1cCA9IHt9XG4gICAgY2VsbEtleXMubWFwKGZ1bmN0aW9uKGssIGkpe1xuICAgICAgbG9va3VwW3NldEV4dHJhY3RvcihrKSsnOjonK2ludGVyc2VjdGlvbkV4dHJhY3RvcihrKV0gPSBrXG4gICAgfSlcblxuICAgIC8vIHZhciBwb3NpdGlvbmVkQ2VsbEtleXMgPSBbXVxuICAgIC8vIGZvciAodmFyIGkgPSAwOyBpIDwgc2V0VmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgLy8gICBmb3IgKHZhciBqID0gMDsgaiA8IGludGVyc2VjdGlvblZhbHVlcy5sZW5ndGg7IGorKykge1xuICAgIC8vICAgICB2YXIgbG9va3VwVmFsdWUgPSBsb29rdXBbc2V0VmFsdWVzW2pdK1wiOjpcIitpbnRlcnNlY3Rpb25WYWx1ZXNbaV1dXG4gICAgLy8gICAgIGlmIChsb29rdXBWYWx1ZSA9PSB1bmRlZmluZWQpIHtcbiAgICAvLyAgICAgICBwb3NpdGlvbmVkQ2VsbEtleXMucHVzaCh1bmRlZmluZWQpXG4gICAgLy8gICAgIH0gZWxzZSB7XG4gICAgLy8gICAgICAgcG9zaXRpb25lZENlbGxLZXlzLnB1c2gobG9va3VwVmFsdWUpXG4gICAgLy8gICAgIH1cbiAgICAvLyAgICAgY29uc29sZS5sb2coaSwgaiwgbG9va3VwVmFsdWUpXG4gICAgLy8gICB9XG4gICAgLy8gfVxuICAgIC8vXG4gICAgLy8gLy8gY29uc29sZS5sb2cocG9zaXRpb25lZENlbGxLZXlzKVxuICAgIC8vXG4gICAgLy8gY2VsbHMuZGF0YShwb3NpdGlvbmVkQ2VsbEtleXMpO1xuXG5cbiAgICBjZWxscy5kYXRhKGNlbGxLZXlzKTtcblxuICAgIGNlbGxzLmVhY2goZnVuY3Rpb24oa2V5LCBpKSB7XG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgICAgaWYgKGtleSA9PSB1bmRlZmluZWQpIHtyZXR1cm4gfVxuICAgICAgdmFyXG4gICAgICBjdXJyZW50RGF0YSA9IGRhdGFba2V5XVxuICAgICAgLy8gY29uc29sZS5sb2coa2V5LCBjdXJyZW50RGF0YSlcbiAgICAgIHZhclxuICAgICAgc2V0ID0gc2V0RXh0cmFjdG9yKGtleSwgaSksXG4gICAgICBpbnRlcnNlY3Rpb24gPSBpbnRlcnNlY3Rpb25FeHRyYWN0b3Ioa2V5LCBpKVxuXG4gICAgICAvLyBjb25zb2xlLmxvZyhzZXQsIGludGVyc2VjdGlvbilcblxuICAgICAgdC5jbGFzc2VkKGludGVyc2VjdGlvbiwgdHJ1ZSlcbiAgICAgIHQuY2xhc3NlZChzZXQsIHRydWUpXG5cbiAgICAgIHZhciBjID0gc2FmZVNlbGVjdCh0LCAnY2lyY2xlJywgaHlwZW5hdGUob2JqZWN0Q2xhc3MsJ2NpcmNsZScpKVxuICAgICAgYy5hdHRyKCdjeCcsIHhPYmplY3RTaXplIC8gMilcbiAgICAgIC5hdHRyKCdjeScsIHlPYmplY3RTaXplIC8gMiApXG4gICAgICAuYXR0cigncicsIHJhZGl1cyA9PSB1bmRlZmluZWQgPyBNYXRoLm1pbih4T2JqZWN0U2l6ZSwgeU9iamVjdFNpemUpIC8gMiA6IHJhZGl1cylcbiAgICAgIC5hdHRyKCdmaWxsJywgaW50ZXJzZWN0aW9uLmluY2x1ZGVzKHNldCkgPyBcImJsYWNrXCI6ICdyZ2IoMjMzLDIzMywyMzMpJylcbiAgICAgIC5hdHRyKCdzdHJva2UnLCBcImJsYWNrXCIpXG4gICAgICAuYXR0cihcImluLWludGVyc2VjdGlvblwiLCBpbnRlcnNlY3Rpb24uaW5jbHVkZXMoc2V0KSlcbiAgICB9KVxuXG5cblxuICB9XG5cbiAgZnVuY3Rpb24gaW50ZXJzZWN0aW9uVG90YWxzKCkge1xuICAgIHZhciB0b3RhbHMgPSB7fVxuICAgIC8vIGludGVyc2VjdGlvblZhbHVlcy5zb3J0KGZ1bmN0aW9uKGEsIGIpeyByZXR1cm4gaW50ZXJzZWN0aW9uS2V5U29ydGluZ0Z1bmN0aW9uKGEsIGIpIH0pXG5cbiAgICBpbnRlcnNlY3Rpb25WYWx1ZXMubWFwKGZ1bmN0aW9uKGssIGkpeyB0b3RhbHNba10gPSB7J3RvdGFsJzogMH0gfSlcbiAgICBjZWxsS2V5cy5tYXAoZnVuY3Rpb24oaywgaSl7XG4gICAgICB2YXIgZSA9IGVsZW1lbnRFeHRyYWN0b3IoaywgaSk7XG4gICAgICBpZiAodG90YWxzW2ludGVyc2VjdGlvbkV4dHJhY3RvcihrLCBpKV1bJ3RvdGFsJ10gPT0gMCkge1xuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShlKSkge1xuICAgICAgICAgIHRvdGFsc1tpbnRlcnNlY3Rpb25FeHRyYWN0b3IoaywgaSldWyd0b3RhbCddKz0gZS5sZW5ndGhcbiAgICAgICAgICB0b3RhbHNbaW50ZXJzZWN0aW9uRXh0cmFjdG9yKGssIGkpXVsndmFsdWVzJ10gPSBlXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdG90YWxzW2ludGVyc2VjdGlvbkV4dHJhY3RvcihrLCBpKV1bJ3RvdGFsJ10rPSBlXG4gICAgICAgIH1cblxuICAgICAgfVxuICAgIH0pXG4gICAgcmV0dXJuIHRvdGFsc1xuICB9XG5cbiAgZnVuY3Rpb24gc2V0VG90YWxzKCl7XG4gICAgdmFyIHRvdGFscyA9IHt9XG4gICAgLy8gaW50ZXJzZWN0aW9uVmFsdWVzLnNvcnQoZnVuY3Rpb24oYSwgYil7IHJldHVybiBpbnRlcnNlY3Rpb25LZXlTb3J0aW5nRnVuY3Rpb24oYSwgYikgfSlcblxuICAgIHNldFZhbHVlcy5tYXAoZnVuY3Rpb24oaywgaSl7IHRvdGFsc1trXSA9IHsndG90YWwnOiAwfSB9KVxuXG4gICAgY2VsbEtleXMubWFwKGZ1bmN0aW9uKGssIGkpe1xuICAgICAgdmFyIGUgPSBlbGVtZW50RXh0cmFjdG9yKGssIGkpO1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkoZSkpIHtcbiAgICAgICAgdG90YWxzW3NldEV4dHJhY3RvcihrLCBpKV1bJ3RvdGFsJ10rPSBlLmxlbmd0aFxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdG90YWxzW3NldEV4dHJhY3RvcihrLCBpKV1bJ3RvdGFsJ10rPSBlXG4gICAgICB9XG4gICAgfSlcbiAgICByZXR1cm4gdG90YWxzXG4gIH1cblxuICB1cHNldC5pbnRlcnNlY3Rpb25Ub3RhbHMgPSBpbnRlcnNlY3Rpb25Ub3RhbHNcbiAgdXBzZXQuc2V0VG90YWxzID0gc2V0VG90YWxzXG5cbiAgcmV0dXJuIHVwc2V0XG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuZXhwb3J0IGZ1bmN0aW9uIGZpbHRlclRhYmxlKHNlbGVjdGlvbikge1xuICB2YXJcbiAgZGF0YSxcbiAgbmFtZXNwYWNlID0gJ2Qzc20tZmlsdGVyLXRhYmxlJyxcbiAgc29ydFEgPSBmYWxzZSxcbiAgZmllbGRGdW5jdGlvbiA9IGZ1bmN0aW9uKHJlY29yZCwgY29sdW1uS2V5LCByZWNvcmRWYWx1ZSkge3JldHVybiByZWNvcmRWYWx1ZX1cblxuICBmaWx0ZXJUYWJsZS5kYXRhID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkYXRhID0gXywgZmlsdGVyVGFibGUpIDogZGF0YX1cbiAgZmlsdGVyVGFibGUubmFtZXNwYWNlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChuYW1lc3BhY2UgPSBfLCBmaWx0ZXJUYWJsZSkgOiBuYW1lc3BhY2V9XG4gIGZpbHRlclRhYmxlLmZpZWxkRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGZpZWxkRnVuY3Rpb24gPSBfLCBmaWx0ZXJUYWJsZSkgOiBmaWVsZEZ1bmN0aW9ufVxuXG4gIGZ1bmN0aW9uIGZpbHRlclRhYmxlICgpIHtcblxuICAgIHZhclxuICAgIGNvbnRhaW5lciA9IHNhZmVTZWxlY3Qoc2VsZWN0aW9uLCAnZGl2JywgJ2ZpbHRlci10YWJsZScpLmNsYXNzZWQoaHlwZW5hdGUobmFtZXNwYWNlLCdjb250YWluZXInKSx0cnVlKSxcblxuICAgIGlucHV0R3JvdXAgPSBzYWZlU2VsZWN0KGNvbnRhaW5lciwgJ2RpdicsICdmaWx0ZXItaW5wdXQtZ3JvdXAnKS5jbGFzc2VkKCdpbnB1dC1ncm91cCcsdHJ1ZSksXG4gICAgICBpbnB1dFByZXBlbmQgPSBzYWZlU2VsZWN0KGlucHV0R3JvdXAsICdkaXYnLCAnaW5wdXQtZ3JvdXAtcHJlcGVuZCcpLFxuICAgICAgICBpbnB1dFByZXBlbmRTcGFuID0gc2FmZVNlbGVjdChpbnB1dFByZXBlbmQsICdzcGFuJywgJ2lucHV0LWdyb3VwLXRleHQnKS5jbGFzc2VkKCdzZWFyY2gtYnV0dG9uJywgdHJ1ZSksXG4gICAgICAgICAgaW5wdXRQcmVwZW5kU3Bhbkljb24gPSBzYWZlU2VsZWN0KGlucHV0UHJlcGVuZFNwYW4sJ2knLCdmYSBmYS1zZWFyY2gnKSxcblxuICAgICAgaW5wdXQgPSBzYWZlU2VsZWN0KGlucHV0R3JvdXAsICdpbnB1dCcsICdmb3JtLWNvbnRyb2wnKS5hdHRyKCdwbGFjZWhvbGRlcicsICdmaWx0ZXInKS5hdHRyKCd0eXBlJywgJ3RleHQnKSxcbiAgICAgIGlucHV0QXBwZW5kID0gc2FmZVNlbGVjdChpbnB1dEdyb3VwLCAnZGl2JywgJ2lucHV0LWdyb3VwLWFwcGVuZCcpLFxuICAgICAgICBpbnB1dEFwcGVuZEJ1dHRvbiA9IHNhZmVTZWxlY3QoaW5wdXRBcHBlbmQsICdhJywgJ2Nsb3NlLWJ1dHRvbicpLmNsYXNzZWQoJ2J0biBidG4tb3V0bGluZS1zZWNvbmRhcnknLCB0cnVlKSxcbiAgICAgICAgICBpbnB1dEFwcGVuZEJ1dHRvbkljb24gPSBzYWZlU2VsZWN0KGlucHV0QXBwZW5kQnV0dG9uLCAnaScsICdmYSBmYS1jbG9zZScpLFxuXG5cbiAgICBjbG9zZUJ1dHRvbiA9IGlucHV0QXBwZW5kQnV0dG9uLFxuXG4gICAgclRhYmxlID0gc2FmZVNlbGVjdChjb250YWluZXIsICdkaXYnLCAndGFibGUtcmVzcG9uc2l2ZScpLFxuICAgICAgdGFibGUgPSBzYWZlU2VsZWN0KHJUYWJsZSwgJ3RhYmxlJywgJ3RhYmxlJylcbiAgICAgIC5jbGFzc2VkKCd0YWJsZS1ob3ZlcicsIHRydWUpXG4gICAgICAuY2xhc3NlZCgndGFibGUtYm9yZGVyZWQnLCB0cnVlKVxuICAgICAgLmNsYXNzZWQoXCJ0YWJsZS1zdHJpcGVkXCIsIHRydWUpLFxuICAgICAgICB0SGVhZCA9IHNhZmVTZWxlY3QodGFibGUsICd0aGVhZCcpXG4gICAgICAgIC5jbGFzc2VkKFwidGhlYWQtZGFya1wiLCB0cnVlKSxcbiAgICAgICAgICBoZWFkZXIgPSBzYWZlU2VsZWN0KHRIZWFkLCAndHInKSxcbiAgICAgICAgdEJvZHkgPSBzYWZlU2VsZWN0KHRhYmxlLCAndGJvZHknKVxuXG5cbiAgICB2YXJcbiAgICByb3dzID0gZDMua2V5cyhkYXRhKSxcbiAgICBjb2xzID0gZDMua2V5cyhkYXRhW3Jvd3NbMF1dKVxuXG4gICAgdmFyIHRoID0gbWFrZUNvbHVtbnMoaGVhZGVyLCBjb2xzKVxuICAgIHZhciB0ciA9IG1ha2VSb3dzKHRCb2R5LCByb3dzKVxuICAgIHZhciB0ciA9IHBvcHVsYXRlUmVjb3Jkcyh0ciwgY29scylcblxuXG5cbiAgICBpbnB1dC5vbignaW5wdXQnLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhclxuICAgICAgdmFsID0gaW5wdXQucHJvcGVydHkoJ3ZhbHVlJyksXG4gICAgICByZWcgPSBuZXcgUmVnRXhwKHZhbCwgJ2dpJyksXG4gICAgICB1c2VcbiAgICAgIGlmICh2YWwgPT0gJycpIHt1c2UgPSByb3dzfSBlbHNlIHtcbiAgICAgICAgdXNlID0gW11cbiAgICAgICAgcm93cy5tYXAoZnVuY3Rpb24ociwgaSl7XG4gICAgICAgICAgdmFyIHJvdyA9IGRhdGFbcl1cbiAgICAgICAgICB2YXIgZmllbGRKb2luID0gY29scy5tYXAoZnVuY3Rpb24oYywgail7XG4gICAgICAgICAgICByZXR1cm4gZmllbGRGdW5jdGlvbihyb3csIGMsIHJvd1tjXSlcbiAgICAgICAgICAgIC8vIHJldHVybiByb3dbY11cbiAgICAgICAgICB9KS5qb2luKCcnKVxuICAgICAgICAgIHZhciBtYXRjaCA9IGZpZWxkSm9pbi5tYXRjaChyZWcpXG4gICAgICAgICAgaWYgKG1hdGNoID09IG51bGwgfHwgbWF0Y2guam9pbignJykgPT0gJycpIHt9XG4gICAgICAgICAgZWxzZSB7IHVzZS5wdXNoKHIpIH1cbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgdHIgPSBtYWtlUm93cyh0Qm9keSwgdXNlKVxuICAgICAgdHIgPSBwb3B1bGF0ZVJlY29yZHModHIsIGNvbHMpXG4gICAgfSlcblxuICAgIGNsb3NlQnV0dG9uLm9uKCdjbGljaycsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgaW5wdXQucHJvcGVydHkoJ3ZhbHVlJywgJycpLmRpc3BhdGNoKCdpbnB1dCcpXG4gICAgfSlcblxuICB9XG5cbiAgZnVuY3Rpb24gbWFrZUNvbHVtbnMoaGVhZGVyLCBjb2xzKSB7XG4gICAgdmFyIHRoID0gaGVhZGVyLnNlbGVjdEFsbCgndGgnKVxuICAgIHRoID0gdGguZGF0YShjb2xzKVxuICAgIHRoLmV4aXQoKS5yZW1vdmUoKVxuICAgIHRoID0gdGgubWVyZ2UodGguZW50ZXIoKS5hcHBlbmQoJ3RoJykpXG4gICAgdGguYXR0cignc2NvcGUnLCAnY29sJykudGV4dChmdW5jdGlvbihkLCBpKXtyZXR1cm4gZH0pXG4gICAgcmV0dXJuIHRoXG4gIH1cblxuICBmdW5jdGlvbiBtYWtlUm93cyhib2R5LCByb3dzKSB7XG4gICAgdmFyIHRyID0gYm9keS5zZWxlY3RBbGwoJ3RyJylcbiAgICB0ciA9IHRyLmRhdGEocm93cylcbiAgICB0ci5leGl0KCkucmVtb3ZlKClcbiAgICB0ciA9IHRyLm1lcmdlKHRyLmVudGVyKCkuYXBwZW5kKCd0cicpKVxuICAgIHJldHVybiB0clxuICB9XG5cbiAgZnVuY3Rpb24gcG9wdWxhdGVSZWNvcmRzKHJvd3MsIGNvbHMpIHtcbiAgICByb3dzLmVhY2goZnVuY3Rpb24ociwgaSl7XG4gICAgICB2YXIgcmVjb3JkID0gZGF0YVtyXSxcbiAgICAgIHQgPSBkMy5zZWxlY3QodGhpcylcblxuICAgICAgdmFyIGZpZWxkcyA9IHQuc2VsZWN0QWxsKCd0ZCcpXG4gICAgICBmaWVsZHMgPSBmaWVsZHMuZGF0YShjb2xzKVxuICAgICAgZmllbGRzLmV4aXQoKS5yZW1vdmUoKVxuICAgICAgZmllbGRzID0gZmllbGRzLm1lcmdlKGZpZWxkcy5lbnRlcigpLmFwcGVuZCgndGQnKSlcblxuICAgICAgZmllbGRzLmF0dHIoJ3Njb3BlJywgZnVuY3Rpb24oZiwgail7IHJldHVybiBqID09IDAgfSlcbiAgICAgIC5odG1sKGZ1bmN0aW9uKGYsIGopeyByZXR1cm4gZmllbGRGdW5jdGlvbihyZWNvcmQsIGYsIHJlY29yZFtmXSkgfSlcblxuICAgIH0pXG4gICAgcmV0dXJuIHJvd3NcbiAgfVxuXG5cblxuICByZXR1cm4gZmlsdGVyVGFibGVcbn1cbiJdLCJuYW1lcyI6WyJ1bmlxdWVFbGVtZW50cyIsInZhbHVlIiwiaW5kZXgiLCJzZWxmIiwiaW5kZXhPZiIsImdldFRyYW5zbGF0aW9uIiwidHJhbnNmb3JtIiwiZyIsImRvY3VtZW50IiwiY3JlYXRlRWxlbWVudE5TIiwidW5kZWZpbmVkIiwic2V0QXR0cmlidXRlTlMiLCJtYXRyaXgiLCJiYXNlVmFsIiwiY29uc29saWRhdGUiLCJlIiwiZiIsIm1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2UiLCJoZXgiLCJsdW0iLCJTdHJpbmciLCJyZXBsYWNlIiwibGVuZ3RoIiwiYyIsImkiLCJyZ2IiLCJwYXJzZUludCIsInN1YnN0ciIsIk1hdGgiLCJyb3VuZCIsIm1pbiIsIm1heCIsInRvU3RyaW5nIiwicXVhcnRpbGVzIiwiZGF0YSIsInFLZXlzIiwicTIiLCJkMyIsIm1lZGlhbiIsImxvd2VyIiwiZmlsdGVyIiwieCIsInVwcGVyIiwicTEiLCJxMCIsInEzIiwicTQiLCJrMCIsImsxIiwiazIiLCJrMyIsIms0Iiwib2JqIiwiaHlwZW5hdGUiLCJBcnJheSIsInByb3RvdHlwZSIsInNsaWNlIiwiY2FsbCIsImFyZ3VtZW50cyIsImpvaW4iLCJudW1iZXIiLCJwcmVjaXNpb24iLCJzaGlmdCIsInJldmVyc2VTaGlmdCIsIm51bUFycmF5Iiwic3BsaXQiLCJnZXRDb250YWluaW5nU1ZHIiwiZWxlbWVudCIsInBhcmVudCIsInBhcmVudEVsZW1lbnQiLCJ0YWciLCJ0YWdOYW1lIiwidG9Mb3dlckNhc2UiLCJzYWZlU2VsZWN0Iiwic2VsIiwiY2xzIiwiY2xzU3RyIiwic1NlbCIsInNlbGVjdCIsImVtcHR5IiwiYXBwZW5kIiwiY2xhc3NlZCIsImF0dHIiLCJ0aWNrUmFuZ2UiLCJuIiwiYSIsInMiLCJwdXNoIiwiaGFzUSIsImFycmF5IiwiaXRlbSIsImluY2x1ZGVzIiwidW5pcXVlIiwiZmxhdHRlbiIsImZsYXQiLCJtYXAiLCJpc0FycmF5IiwiY29uY2F0Iiwid2hpY2hCaW4iLCJiaW5zIiwiaiIsImNvbnNvbGVHcm91cCIsIm5hbWUiLCJ3aW5kb3ciLCJkM3NtIiwiZGVidWdRIiwiZ3JvdXAiLCJjb25zb2xlR3JvdXBFbmQiLCJncm91cEVuZCIsImxvZyIsImZ1bmMiLCJtc2ciLCJ0YWJsZSIsInNldHVwQ29udGFpbmVyIiwic2VsZWN0aW9uIiwibmFtZXNwYWNlIiwicmVjdCIsImZpbGwiLCJjb250YWluZXIiLCJ5Iiwid2lkdGgiLCJoZWlnaHQiLCJiZ1JlY3QiLCJkZWZzIiwiY3BSZWN0IiwicmFpc2UiLCJjYWxjdWxhdGVXaWR0aE9mT2JqZWN0IiwiZnJlZVNwYWNlIiwibnVtYmVyT2ZPYmplY3RzIiwibWluT2JqZWN0V2lkdGgiLCJtYXhPYmplY3RXaWR0aCIsInNpemVPZlNwYWNlciIsIm92ZXJmbG93USIsInJlbWFpbmluZ1NwYWNlIiwib2JqZWN0V2lkdGgiLCJjYWxjdWxhdGVXaWR0aE9mU3BhY2VyIiwiYmFzZVNwYWNlclNpemUiLCJzcGFjZXJzQXRFYWNoTGV2ZWwiLCJzcGFjZXJzTmVlZGVkQXRFYWNoTGV2ZWwiLCJsZXZlbCIsImxldmVsRGF0YSIsInJlZHVjZSIsImIiLCJpc05hTiIsIndoaXNrZXJQYXRoIiwiZGlyIiwidyIsImgiLCJwZXIiLCJvIiwiaGgiLCJ3dyIsInAiLCJncm91cGluZ1NwYWNlciIsInNjYWxlTGluZWFyIiwiZWFzZVNpbiIsImN1ciIsImQiLCJob3Jpem9udGFsUSIsIm91dGVyV2lkdGgiLCJjdXJyZW50IiwiY3VycmVudE5vZGUiLCJub2RlIiwic2VsZWN0QWxsIiwidHJhbnNpdGlvbiIsImR1cmF0aW9uIiwidHJhbnNpdGlvbkR1cmF0aW9uIiwiZWFzZSIsImVhc2VGdW5jIiwicmVtb3ZlIiwicmVjdXJzaXZlbHlQb3NpdGlvbiIsImN1cnJlbnRTZWxlY3Rpb24iLCJlbnRlciIsImV4aXQiLCJtZXJnZSIsImV4aXRGdW5jdGlvbiIsImVhY2giLCJ0aGlzIiwibGV2ZWxTcGFjZXIiLCJzcGFjZXJTaXplIiwibW92ZSIsImN1cnJlbnRFbGVtZW50IiwidCIsImVudGVyRnVuY3Rpb24iLCJtb3ZlYnkiLCJzY2FsZSIsInRvUmVtb3ZlIiwib2JqZWN0Q2xhc3MiLCJvYmplY3RTaXplIiwic2l6ZSIsIl8iLCJjb2xvckZ1bmN0aW9uIiwiaW50ZXJwb2xhdGVSZ2IiLCJjb2xvcnMiLCJrIiwidiIsImNhdGVnb3J5IiwiaW50ZXJwb2xhdGUiLCJpbnRlcnBvbGF0aW9uIiwiZG9tYWluIiwiZGF0YUV4dGVudCIsInJhbmdlIiwiaGVscGVyU2NhbGUiLCJtYXRjaCIsImtleSIsInR5cGUiLCJob3ZlclEiLCJvcGFjIiwiZmlsbE9wYWNpdHkiLCJzdHJva2VPcGFjaXR5IiwiY29sb3JCeSIsImNhdGVnb3JpZXMiLCJtb2RpZnlPcGFjaXR5IiwidmFsdWVFeHRyYWN0b3IiLCJjYXQiLCJjYXRlZ29yeUV4dHJhY3RvciIsInRvb2x0aXAiLCJrZXlzIiwidmFsdWVzIiwiaGVhZGVyIiwib24iLCJtb3VzZW1vdmUiLCJjdXJyZW50RGF0YSIsIm1vdXNlIiwiZGl2Iiwic3R5bGUiLCJjYXJkQm9keSIsInRCb2R5IiwidGV4dCIsInRyIiwicm93S2V5Iiwicm93SW5kZXgiLCJiYm94IiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwiaW5uZXJXaWR0aCIsInNjcm9sbFgiLCJldmVudCIsInBhZ2VYIiwiaW5uZXJIZWlnaHQiLCJzY3JvbGxZIiwicGFnZVkiLCJzZWxlY3RGaWx0ZXIiLCJzZWxlY3Rpb25OYW1lIiwiZGVmYXVsdFZhbHVlIiwibGFzdFZhbHVlIiwic2VsZWN0QXBwZW5kQnV0dG9uIiwiaW5wdXRHcm91cCIsImlucHV0IiwiaW5wdXRBcHBlbmRCdXR0b24iLCJvcHRpb25zIiwiY2xvc2VCdXR0b24iLCJjdXJyZW50U3R5bGUiLCJwcm9wZXJ0eSIsImRpc3BhdGNoIiwidXNlIiwidmFsIiwicmVnIiwiUmVnRXhwIiwib3B0aW9uIiwiY3VycmVudE9wdGlvbiIsInBhcnNlRmxvYXQiLCJsYXNzbyIsInN2ZyIsImNoYXJ0Q29udGFpbmVyIiwiY2hhcnRPZmZzZXQiLCJvYmplY3RzT2Zmc2V0IiwiZXZlbnRDYXRjaGVyIiwieFNjYWxlIiwicGF0aCIsImxpbmUiLCJ5U2NhbGUiLCJjdXJ2ZSIsImN1cnZlTGluZWFyQ2xvc2VkIiwiaW5zdGFuY2UiLCJhbmltYXRpb25SYXRlIiwib3BhY2l0eSIsImRhc2hBcnJheSIsInN0cm9rZSIsInN0cm9rZVdpZHRoIiwibGFzc29lZFN0cm9rZSIsImxhc3NvZWRTdHJva2VXaWR0aCIsImVhc2VFeHAiLCJwYXRocyIsInBFbnRlciIsImFjdGl2ZVEiLCJvYmplY3RDb250YWluZXIiLCJhbGxQb2ludHMiLCJyZW5kZXIiLCJwcmV2ZW50RGVmYXVsdCIsInN0b3BQcm9wYWdhdGlvbiIsImFkZEV2ZW50TGlzdGVuZXIiLCJkcmFnIiwicmVtb3ZlRXZlbnRMaXN0ZW5lciIsImN1cnJlbnRQb2ludHMiLCJhcHBseVBhdGhBdHRyaWJ1dGVzIiwiY29sb3IiLCJ3aGljaCIsInB0IiwiaW52ZXJ0IiwibGFzdFB0IiwicDEiLCJwMiIsInNxcnQiLCJldWNsaWRlYW5EaXN0YW5jZSIsInRpY2tEaXN0YW5jZSIsInRpY2siLCJkZXRlY3QiLCJsYXNzb3MiLCJib3giLCJhYnNvbHV0ZVBvc2l0aW9uIiwibGVmdCIsInRvcCIsInJpZ2h0IiwiYm90dG9tIiwiYm94UHRzIiwiaW5BbnlMYXNzb1EiLCJsYXNzb1BvaW50cyIsImV2ZXJ5IiwicG9seWdvbkNvbnRhaW5zIiwiY29vcmQiLCJ1cGRhdGVPYmplY3RzIiwiYXBwbHlPYmplY3RBdHRyaWJ1dGVzIiwic2V0USIsInByZUxhc3NvRmlsbCIsInByZUxhc3NvU3Ryb2tlIiwicHJlTGFzc29TdHJva2VXaWR0aCIsImxhc3NvZWRGaWxsIiwia2V5RnJhbWVzIiwiaHRtbCIsImRyYXciLCJ0b2dnbGUiLCJzdGF0ZSIsInRoaXNTVkciLCJhYnNvbHV0ZSIsImVsZW1lbnRQb3NpdGlvbiIsInN2Z1Bvc2l0aW9uIiwicmVsYXRpdmVQb3NpdGlvblRvIiwiYXhpcyIsImxhYmVsIiwidGlja1RpY2tMYWJlbFNwYWNlciIsInRpY2tMYWJlbE1hcmdpbiIsInJldmVyc2VTY2FsZVEiLCJvcmllbnQiLCJ2ZXJ0aWNhbFEiLCJiZ2NwUmVjdCIsInNwYWNlWCIsInNwYWNlWSIsImd1aWRlTGluZXNRIiwiZ3VpZGVsaW5lU3BhY2UiLCJ0aWNrTGFiZWxNYXhGb250U2l6ZSIsImJhY2tncm91bmRGaWxsIiwidGlja0xhYmVsVGV4dEFuY2hvciIsInRpY2tMYWJlbFJvdGF0aW9uIiwidGlja0RhdGEiLCJjYXRlZ29yaWNhbFEiLCJncm91cGluZyIsInRpY2tMYWJlbHMiLCJudW1iZXJPZlRpY2tzIiwiZXh0ZW50IiwidGlja1ZhbHVlcyIsImZsYXRUaWNrRGF0YSIsInNwYWNlIiwicmV2ZXJzZSIsImRvbWFpblBhZGRpbmciLCJtaW5PYmplY3RTaXplIiwibWF4T2JqZWN0U2l6ZSIsIm9iamVjdFNwYWNlciIsIm9iakNsYXNzIiwic3BhY2VyRnVuY3Rpb24iLCJtb3ZlWEJ5IiwibW92ZVlCeSIsIm10IiwiZGF0dW0iLCJkaXN0IiwibGFiZWxOYW1lR3JvdXAiLCJsYWJlbEVsZW1lbnQiLCJ0aGF0IiwidGlja0xlbmd0aCIsInRpY2tTdHJva2UiLCJ0aWNrU3Ryb2tlV2lkdGgiLCJzdHJpbmciLCJjaGFycyIsInJvdW5kVG8iLCJ0aWNrTGFiZWxGb250U2l6ZSIsImdldENvbXB1dGVkVGV4dExlbmd0aCIsImxhYmVsSG92ZXIiLCJsYWJlbEhvdmVyT2ZmIiwidGlja0xhYmVsT25DbGljayIsImd1aWRlTGluZVN0cm9rZSIsImd1aWRlTGluZVN0cm9rZVdpZHRoIiwibGluZVN0cm9rZSIsImxpbmVTdHJva2VXaWR0aCIsInBhcmVudE5vZGUiLCJ0aWNrTGFiZWxPbkhvdmVyRnVuYyIsImdsaW5lIiwibWlub3JRIiwiaWkiLCJ0aWNrTGFiZWxNaW5Gb250U2l6ZSIsInRpY2tMYWJlbEZ1bmMiLCJiYXIiLCJrZXlBIiwia2V5QiIsImRlc2NlbmRpbmciLCJDRiIsIlRUaXAiLCJiYXJQZXJjZW50IiwiYmFyS2V5cyIsIm9yZGVyZWQiLCJzb3J0Iiwic29ydGluZ0Z1bmN0aW9uIiwiYmFyVmFsdWVzIiwiZGVmYXVsdEV4aXQiLCJub2RlcyIsInBhcmVudEluZGV4QXJyYXkiLCJOdW1iZXIiLCJmaWxsQ29sb3IiLCJzdHJva2VDb2xvciIsImJhclN0cm9rZVdpZHRoIiwiYnViYmxlSGVhdG1hcCIsInhLZXkiLCJ5S2V5IiwicktleSIsInZLZXkiLCJ4S2V5U29ydGluZ0Z1bmN0aW9uIiwieEV4dHJhY3RvciIsInlLZXlTb3J0aW5nRnVuY3Rpb24iLCJ5RXh0cmFjdG9yIiwiYmhtIiwiY2VsbEtleXMiLCJyRXh0cmFjdG9yIiwidkV4dHJhY3RvciIsInhWYWx1ZXMiLCJ5VmFsdWVzIiwieERpbSIsInlEaW0iLCJyVmFsdWVzIiwieVNpemUiLCJ4U2l6ZSIsInlTcGFjZXIiLCJ5U3BhY2VyU2l6ZSIsInhTcGFjZXIiLCJ4U3BhY2VyU2l6ZSIsImNlbGxzIiwidlZhbHVlcyIsInJhZGl1cyIsInNjYWxlZCIsImJ1YmJsZVN0cm9rZVdpZHRoIiwiaGVhdG1hcCIsImhtIiwieU1pbk9iamVjdFNpemUiLCJ5TWF4T2JqZWN0U2l6ZSIsInhNaW5PYmplY3RTaXplIiwieE1heE9iamVjdFNpemUiLCJsb29rdXAiLCJwb3NpdGlvbmVkQ2VsbEtleXMiLCJsb29rdXBWYWx1ZSIsIm9iamVjdFN0cm9rZVdpZHRoIiwiYm94d2hpc2tlciIsInF1YXJ0aWxlc0tleSIsInF1YXJ0aWxlc0tleXMiLCJib3hLZXlzIiwiYm94VmFsdWVzIiwid2hpc2siLCJ1V2hpc2siLCJsV2hpc2siLCJxdWFydCIsInVRdWFydCIsImxRdWFydCIsIm1RdWFydCIsImJveFN0cm9rZVdpZHRoIiwiciIsImRpZiIsImRkIiwid2hpc2tlcldpZHRoUGVyY2VudCIsIndoaXNrZXJTdHJva2VXaWR0aCIsImRhdGF0b2dnbGUiLCJ4QXhpc09wdGlvbnMiLCJ5QXhpc09wdGlvbnMiLCJ5QXhpc1NlbGVjdFEiLCJ4QXhpc1NlbGVjdFEiLCJ1cGRhdGVGdW5jdGlvbiIsImN1cnJlbnRLZXlzIiwidmFscyIsImZpbHRlclNlbGVjdHMiLCJkYXRhb3B0cyIsImRvRW50ZXIiLCJzZiIsInNjYXR0ZXIiLCJwb2ludEtleXMiLCJ2YWx1ZUV4dHJhY3RvclgiLCJ2YWx1ZUV4dHJhY3RvclkiLCJ2YWx1ZUV4dHJhY3RvclIiLCJleHRlbnRYIiwidmFsdWVzWCIsImRvbWFpblBhZGRpbmdYIiwiZXh0ZW50WSIsInZhbHVlc1kiLCJkb21haW5QYWRkaW5nWSIsImV4dGVudFIiLCJ2YWx1ZXNSIiwiZG9tYWluUGFkZGluZ1IiLCJtaW5SYWRpdXMiLCJtYXhSYWRpdXMiLCJwb2ludHMiLCJwRXhpdCIsInNjYWxlWCIsInNjYWxlWSIsInNjYWxlUiIsInBvaW50U3Ryb2tlV2lkdGgiLCJwbG90Wm9vbSIsImNoYXJ0IiwieEF4aXMiLCJ5QXhpcyIsImNoYXJ0U2VsIiwieEF4aXNTZWwiLCJ5QXhpc1NlbCIsInNldExvY2tzIiwiY2hhcnRPYmpTZWwiLCJjaGFydE9ialRyYW5zIiwiY29zIiwiZ2V0QkJveCIsInhMb2NrIiwieUxvY2siLCJ6b29tIiwiY2hhcnRCb3giLCJ4QXhpc0JveCIsInlBeGlzQm94IiwiZXZlbnRUeXBlIiwiZGVsdGFZIiwid2hlZWxTcGVlZCIsInNoaWZ0USIsInNoaWZ0S2V5IiwiYXBwbHlYIiwiYXBwbHlZIiwieEF4aXNPYmpTZWwiLCJ5QXhpc09ialNlbCIsInJlc2V0IiwibXVsdGlQbG90Wm9vbSIsInhDb21wb25lbnRzIiwieUNvbXBvbmVudHMiLCJ4Q29tcG9uZW50c1NlbCIsInlDb21wb25lbnRzU2VsIiwieENvbXBvbmVudE9ialNlbCIsInlDb21wb25lbnRPYmpTZWwiLCJ2aW9saW4iLCJiYXNlIiwibWluTWF4SGV4U2NhbGUiLCJzY2FsZWRDb2xvciIsIm1vZCIsInF1YXJ0aWxlS2V5cyIsInBvaW50c1Rvb2x0aXAiLCJ2aW9saW5LZXkiLCJ2aW9saW5EYXRhIiwidmlvbGluUG9pbnRLZXkiLCJ2aW9saW5Qb2ludERhdGEiLCJjYWxjVmFsdWVzIiwiY2FsY3VsYXRlVmlvbGluVmFsdWVzIiwidmlvbGluUG9pbnRzIiwidmlvbGluUG9pbnRzRXh0cmFjdG9yIiwidmlvbGluUG9pbnRzS2V5cyIsInZpb2xpblBvaW50c1ZhbHVlcyIsInBrIiwidmlvbGluUG9pbnRWYWx1ZUV4dHJhY3RvciIsInBvaW50UXVhcnRpbGVzIiwiYmlubmVkIiwiaGlzdG9ncmFtIiwiZnJlcXVlbmNpZXMiLCJiaW4iLCJtaW5Db250b3VyUG9pbnQiLCJtYXhDb250b3VyUG9pbnQiLCJ2aW9saW5Db250b3VyUG9pbnRzIiwieDAiLCJ4MSIsImNvbnRvdXIiLCJwb2ludFZhbHVlcyIsIm5lZWRlZFZpb2xpblZhbHVlcyIsInZrIiwidmlvbGluS2V5cyIsImZyZXF1ZW5jeU1heCIsInZTY2FsZSIsImxBcmVhIiwiY3VydmVCYXNpcyIsInJBcmVhIiwiYXJlYSIsImxhIiwicmEiLCJxdWFydHMiLCJwb2ludHNRIiwicHRzQ29udGFpbmVyIiwicHRzIiwicHRzRW50ZXIiLCJwTWluIiwicE1heCIsInBvaW50UmFkaXVzIiwicG9pbnRLZXkiLCJyYW5kb20iLCJwb2ludENvbG9yRnVuYyIsImNyZWF0ZUV2ZW50IiwiaW5pdEV2ZW50IiwiZGlzcGF0Y2hFdmVudCIsInRvb2x0aXBLZXkiLCJxdWFydGlsZUtleSIsInZpb2xpblZhbHVlcyIsIm51bWVyaWNMZWdlbmQiLCJmb250U2l6ZSIsInRleHRDb2xvciIsImxlZ2VuZCIsImxpbmVhckdyYWRpZW50IiwibSIsImNhdGVnb3JpY0xlZ2VuZCIsImFzY2VuZGluZyIsImNhdEtleXMiLCJjeCIsImN5IiwibGFzc29XaWRnZXQiLCJtYXhOdW1iZXJPZkdyb3VwcyIsImRhdGFFeHRyYWN0b3IiLCJvbkVycm9yIiwic3VibWl0IiwiY2xpY2tTZW5kIiwicmVtb3ZlQnRuQ2xpY2tUb1JlbW92ZSIsInRhYnMiLCJwYW5lcyIsInJlbWFpbmluZ1RhYnMiLCJsYXN0VGFiIiwiY3VyVGV4dCIsInBvcHVsYXRlUGFuZSIsInBhbmUiLCJsZWFkIiwiYnRucyIsImNOIiwiYnRuIiwibGFzc29CdG4iLCJtYWtlTGFzc29CdXR0b24iLCJjdXJyZW50TGFzc28iLCJjbGVhckJ0biIsImxhc3NvQnRuVG9nZ2xlIiwibGFzIiwidGFibGVEYXRhIiwiZXh0cmFjdGVkIiwibWFrZUNsZWFyQnV0dG9uIiwibW9uaXRvckxhc3NvQnV0dG9uU3RhdGUiLCJ0YXJnZXQiLCJtYWtlVGFibGUiLCJoZWFkUiIsImJvZHkiLCJoZWFkZXJLZXlzIiwiaGVhZGVyQ29scyIsInVwZGF0ZVRhYmxlSGVhZGVyQ29sdW1ucyIsImJvZHlSb3dzIiwidXBkYXRlVGFibGVSb3dzIiwicm93RGF0YSIsImNvbHMiLCJzcGxpY2UiLCJtYWtlTmV3R3JvdXAiLCJncyIsIm5ld0dyb3VwTnVtYmVyIiwibGkiLCJpbnNlcnQiLCJ0eHQiLCJpbnNlcnRUYWIiLCJtYWtlVGFiUGFuZSIsImNhcmQiLCJ0YWJMaXN0IiwidGFiQ29udGVudCIsInJpZ2h0VGFicyIsInVwc2V0Iiwic2V0VmFsdWVzIiwiaW50ZXJzZWN0aW9uVmFsdWVzIiwieE9iamVjdFNpemUiLCJjaXJjbGVTdHJva2VXaWR0aCIsInNldEV4dHJhY3RvciIsImludGVyc2VjdGlvbkV4dHJhY3RvciIsImVsZW1lbnRFeHRyYWN0b3IiLCJlbGVtZW50VmFsdWVzIiwieU9iamVjdFNwYWNlciIsInNldEtleVNvcnRpbmdGdW5jdGlvbiIsImludGVyc2VjdGlvbktleVNvcnRpbmdGdW5jdGlvbiIsInhPYmplY3RTcGFjZXIiLCJ5T2JqZWN0U2l6ZSIsInNldCIsImludGVyc2VjdGlvbiIsImludGVyc2VjdGlvblRvdGFscyIsInRvdGFscyIsInRvdGFsIiwic2V0VG90YWxzIiwiZmlsdGVyVGFibGUiLCJzb3J0USIsInJlY29yZCIsImNvbHVtbktleSIsInJlY29yZFZhbHVlIiwicm93cyIsInRoIiwibWFrZUNvbHVtbnMiLCJwb3B1bGF0ZVJlY29yZHMiLCJtYWtlUm93cyIsInJvdyIsImZpZWxkRnVuY3Rpb24iLCJmaWVsZHMiLCJleHRyYWN0VmlvbGluVmFsdWVzIiwidmFsdWVFeHRyYWN0b3JGdW5jdGlvbiIsInFLZXkiLCJtaW5Qb2ludCIsIm1heFBvaW50IiwiaW50ZXJwb2xhdGVDb2xvcnMiLCJpbnRlcnBvbGF0ZVJnYkJhc2lzIiwidHJ1bmNhdGVUZXh0Iiwic2V0dXBTdGFuZGFyZENoYXJ0Q29udGFpbmVycyIsIm1hcmdpbnMiLCJheGVzIiwibGVnIiwibWFyZ2luIiwicG9zIiwiSGVpZ2h0Iiwic3ZnU3BhY2UiLCJtYXJnUHgiLCJjaGFydFNwYWNlIiwiYXhlc1NwYWNlIiwibGVnUmVjdCIsImRyYXdpbmdTcGFjZSIsInlBeGlzUmVjdCIsInBsb3RSZWN0IiwieEF4aXNSZWN0IiwibXlMb2ciLCJ3YXJuIiwiY29uc29sZSIsImluZm8iLCJlcnJvciIsInJlc2l6ZURlYm91bmNlIiwid2FpdCIsInJlc2l6ZSIsImltbWVkaWF0ZSIsInRpbWVvdXQiLCJjb250ZXh0IiwiYXJncyIsImNhbGxOb3ciLCJzZXRUaW1lb3V0IiwiYXBwbHkiLCJkZWJvdW5jZSJdLCJtYXBwaW5ncyI6ImtDQWVPLFNBQVNBLEVB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 diff --git a/build/js/d3sm.min.v0.0.4.js b/build/js/d3sm.min.v0.0.4.js deleted file mode 100644 index b4ca54b..0000000 --- a/build/js/d3sm.min.v0.0.4.js +++ /dev/null @@ -1,2 +0,0 @@ -var d3sm=function(t){"use strict";function e(t,e,n){return n.indexOf(t)===e}function n(t){var e=document.createElementNS("http://www.w3.org/2000/svg","g");t=void 0==t?"translate(0,0)":t,e.setAttributeNS(null,"transform",t);var n=e.transform.baseVal.consolidate().matrix;return[n.e,n.f]}function r(t,e){(t=String(t).replace(/[^0-9a-f]/gi,"")).length<6&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]),e=e||0;var n,r,o="#";for(r=0;r<3;r++)n=parseInt(t.substr(2*r,2),16),o+=("00"+(n=Math.round(Math.min(Math.max(0,n+n*e),255)).toString(16))).substr(n.length);return o}function o(t,e){var n=d3.median(t),r=t.filter(function(t){return tn}),a=void 0==(a=d3.median(r))?n:a,i=void 0==(i=d3.min(r))?a:i,l=void 0==(l=d3.median(o))?n:l,c=void 0==(c=d3.max(o))?l:c,u="q0",s="q1",d="q2",f="q3",h="q4",g={};return void 0!=e&&5==e.length&&(u=e[0],s=e[1],d=e[2],f=e[3],h=e[4]),g[u]=i,g[s]=a,g[d]=n,g[f]=l,g[h]=c,g}function a(){return Array.prototype.slice.call(arguments).join("-")}function i(t,e){var n=function(t,e,n){n&&(e=-e);var r=(""+t).split("e");return+(r[0]+"e"+(r[1]?+r[1]+e:e))};return n(Math.round(n(t,e,!1)),e,!0)}function l(t){var e=t.parentElement,n=e.tagName.toLowerCase();return"svg"===n?e:"html"!==n?l(e):void 0}function c(t,e,n){var r=void 0==n?"":"."+n,o=t.select(e+r).empty()?t.append(e):t.select(e+r);return o.classed(r.replace(".",""),!0).attr("transform",void 0==o.attr("transform")?"translate(0,0)":o.attr("transform"))}function u(t,e,n){for(var r=[t],o=(e-t)/(n-1),a=0;a1?o:t*o),l=(i=i<0?0:i)/e;return a&&void 0!=n&&l=r.length?r.push(e.length-1):r[n]+=e.length-1;e.map(function(e,o){Array.isArray(e)&&t(e,n,r)});return r}(t);o=(e-n*r)/i.map(function(t,e){return 1*t/(e+1)}).reduce(function(t,e){return t+e},0);return isNaN(o)?0:o}function b(t,e,n,r,o,a,i){if("up"!=t&&"top"!=t&&1!=t||(t=!0),"down"!=t&&"bottom"!=t&&0!=t||(t=!1),i=void 0==i?"horizontal":i,a=void 0==a?1:a,"horizontal"!=i){var l=o*a,c=(r=t?r:-r,t?e+r:e),u=t?e:e+r,s=t?c:u;return f="M "+c+" "+o/2+" L "+u+" "+o/2+" M "+s+" "+(o/2-l/2)+" L "+s+" "+(o/2+l/2)+" "}var d=r*a,f="M "+r/2+" "+(c=t?n+o:n)+" L "+r/2+" "+(u=t?n:n+o)+" h "+-d/2+" 0 h "+d+" 0 ";return f}function k(){var t,e,n,r=!0,o=d3.scaleLinear(),a="category",i="d3sm-groupped-item",l=1e3,c=d3.easeSin,u="spacer",s=function(t){t.attr("transform",function(t,e){return"translate("+(r?window.outerWidth:0)+","+(r?0:window.outerWidth)+")"})},d=function(t){m("groupingSpacer","exiting with",{current:t,currentNode:t.node()}),t.selectAll("g").classed("to-remove",!0),t.transition().duration(.9*l).ease(c).attr("transform",function(t,e){return"translate("+(r?window.outerWidth:0)+","+(r?0:window.outerWidth)+")"}).remove()};function f(t,h,g){void 0==g&&(g=0);var p=t.selectAll("g."+u+'[level="'+g+'"]').data(h),m=p.enter().append("g").attr("level",g).attr("class",u),v=p.exit();p=p.merge(m),"function"==typeof d?v.each(function(t,e){d(d3.select(this))}):v.remove();var y=n/(g+1),x=0;return p.each(function(t,n){var h=d3.select(this);if(void 0==h.attr("transform")&&"function"==typeof s&&s(h),h.transition().duration(l).ease(c).attr("transform",function(t,e){return"translate("+(r?"scale"==a?o(t):x:0)+","+(r?0:"scale"==a?o(t):x)+")"}),Array.isArray(t)){x+=f(h,t,g+1);var m=h.selectAll("g."+u+'[level="'+g+'"] > g.'+i+"."+u);"function"==typeof d?m.each(function(t,e){d(d3.select(this))}):m.remove()}else{x+=e;var v=h.select("g."+u+'[level="'+g+'"] > g.'+i+"."+u);v.empty()&&(v=h.append("g").attr("class",i).classed(u,!0)),v.attr("parent-index",n);m=h.selectAll("g."+u+'[level="'+(g+1)+'"]');"function"==typeof d?m.each(function(t,e){d(d3.select(this))}):m.remove()}x+=n==p.size()-1?0:y}),x}return f.horizontalQ=function(t){return arguments.length?(r=t,f):r},f.scale=function(t){return arguments.length?(o=t,f):o},f.moveby=function(t){return arguments.length?(a=t,f):a},f.numberOfObjects=function(e){return arguments.length?(t=e,f):t},f.objectClass=function(t){return arguments.length?(i=t,f):i},f.objectSize=function(t){return arguments.length?(e=t,f):e},f.spacerSize=function(t){return arguments.length?(n=t,f):n},f.transitionDuration=function(t){return arguments.length?(l=t,f):l},f.easeFunc=function(t){return arguments.length?(c=t,f):c},f.namespace=function(t){return arguments.length?(u=t,f):u},f.enterFunction=function(t){return arguments.length?(s=t,f):s},f.exitFunction=function(t){return arguments.length?(d=t,f):d},f}var w=function(){return function(t,e){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return function(t,e){var n=[],r=!0,o=!1,a=void 0;try{for(var i,l=t[Symbol.iterator]();!(r=(i=l.next()).done)&&(n.push(i.value),!e||n.length!==e);r=!0);}catch(t){o=!0,a=t}finally{try{!r&&l.return&&l.return()}finally{if(o)throw a}}return n}(t,e);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),S=function(t){if(Array.isArray(t)){for(var e=0,n=Array(t.length);ewindow.innerWidth-window.scrollX&&(d=d3.event.pageX-b.width-15),f+b.height>window.innerHeight-window.scrollY&&(f=d3.event.pageY-b.height-15),"relative"==h.style("position")?h.style("position","absolute").style("left",d+"px").style("top",f+"px"):h.style("left",d+"px").style("top",f+"px"),h.attr("z-index",1e4)}return a.keys=function(t){return arguments.length?(e=t,a):e},a.values=function(t){return arguments.length?(n=t,a):n},a.header=function(t){return arguments.length?(r=t,a):r},a.data=function(t){return arguments.length?(o=t,a):o},a.selection=function(e){return arguments.length?(t=e,a):t},a}function A(t){var e,n="d3sm-select-filter",r="Select options:",o=void 0,i=void 0;function l(){var o=c(t,"div","input-group").classed(a(n,"container"),!0),l=(c(c(o,"div","select-prepend").classed("input-group-prepend",!0),"span","input-group-text").text(r),c(o,"select","custom-select").classed(a(n,"select"),!0)),s=c(c(o,"div","select-append").classed("input-group-prepend",!0),"a","filter-button").classed("btn btn-outline-secondary",!0),d=(c(s,"i","fa fa-filter"),c(o,"div","filter-input-group").classed("input-group",!0).classed("d-none",!0)),f=(c(c(c(d,"div","input-group-prepend"),"span","input-group-text").classed("search-button",!0),"i","fa fa-search"),c(d,"input","form-control").attr("placeholder","all").attr("type","text")),h=c(c(d,"div","input-group-append"),"a","close-button").classed("btn btn-outline-secondary",!0),g=(c(h,"i","fa fa-close"),d3.keys(e)),p=l.selectAll("option");p=(p=p.data(d3.keys(e))).merge(p.enter().append("option")).attr("value",function(t,e){return t}).text(function(t,e){return t});var m=h;s.on("click",function(t,e){var n=d.classed("d-none");d.classed("d-none",!n)}),m.on("click",function(t,e){f.property("value","").dispatch("input")}),f.on("input",function(t,n){var r,o=f.property("value"),a=new RegExp(o,"gi");""==o?r=g:(r=[],d3.keys(e).map(function(t,e){var n=t.match(a);null==n||""==n.join("")||r.push(t)})),(p=(p=l.selectAll("option")).data(r)).exit().remove(),p=p.merge(p.enter().append("option")).attr("value",function(t,e){return t}).text(function(t,e){return t});var c=u();i!=c&&(i=c,l.dispatch("change"))})}function u(){var n=t.select("select").property("value");return void 0==n||""==n?void 0==o?d3.keys(e)[0]:o:n}return l.data=function(t){return arguments.length?(e=t,l):e},l.namespace=function(t){return arguments.length?(n=t,l):n},l.selectionName=function(t){return arguments.length?(r=t,l):r},l.defaultValue=function(t){return arguments.length?(o=t,l):o},l.currentOption=u,l}function M(t){var e=t.attr("transform").split("translate("),n=w(e,2),r=(n[0],n[1].split(",")),o=w(r,2),a=o[0],i=o[1];return i.split(")"),[parseFloat(a),parseFloat(i)]}function E(t){var e,n,r,o,i,l,u,s,f,h,g="d3sm-lasso",p=!1,m=[],v=[],y=d3.line().x(function(t,e){return void 0!=s?s(t[0]):t[0]}).y(function(t,e){return void 0!=f?f(t[1]):t[1]}).curve(d3.curveLinearClosed),x=0,b=10,k="#17a2b8",w="10s",S=.3,j="5, 10",z="black",A=2,E="white",O="black",L=3,C=1e3,F=d3.easeExp;function Q(){var t,e;p&&((t=(t=c(n,"g","lasso-container").selectAll('path[instance="'+x+'"]')).data(v)).exit().remove(),e=t.enter().append("path"),V(t=t.merge(e).transition().duration(C).ease(F)))}function B(){var t=c(n,"g","lasso-container");t.selectAll('path[instance="'+x+'"]').remove();t.remove(),n.selectAll(r).classed("in-lasso",!1),Y()}function q(t){t.preventDefault(),t.stopPropagation();var r=c(n,"g","lasso-container");m=[],e.node().addEventListener("mousemove",D),e.node().addEventListener("mouseup",function(t){e.node().removeEventListener("mousemove",D),v.push(m),v=d(v)}),V(h=r.append("path").data([m]))}function V(t){t.attr("class",a(g,"lasso-path")).style("opacity",S).attr("fill",k).attr("d",y).attr("instance",x).style("stroke-dasharray",j).attr("stroke",z).attr("stroke-width",A).style("animation","lassoDash "+w+" linear").style("animation-iteration-count","infinite")}function D(t){if(void 0!=u&&u.dispatch(a(g,"drag")),1==t.which){d3.event=t;var r=d3.mouse(n.node());r=d3.mouse(e.node());if(void 0!=s&&(r[0]=s.invert(r[0])),void 0!=f&&(r[1]=f.invert(r[1])),r[0]=r[0]-i[0]-l[0],r[1]=r[1]-i[1]-l[1],m.length){var o=m[m.length-1],c=[r[0],r[1]],d=[o[0],o[1]];s&&(d[0]=s(d[0]),c[0]=s(c[0])),f&&(d[1]=f(d[1]),c[1]=f(c[1])),function(t,e){var n=t[0]-e[0],r=t[1]-e[1];return Math.sqrt(n*n+r*r)}(d,c)>b&&_(r)}else _(r)}}function _(t){if(void 0!=t){if(m.push(t),h.attr("d",y),m.length<3)return;K(v.concat([m]))}else K(v)}function K(t){return void 0==t&&(t=v),n.selectAll(r).each(function(e,n){var r=d3.select(this),o=r.absolutePosition(),a=[[o.left-i[0]-l[0],o.top-i[1]-l[1]],[o.right-i[0]-l[0],o.top-i[1]-l[1]],[o.left-i[0]-l[0],o.bottom-i[1]-l[1]],[o.right-i[0]-l[0],o.bottom-i[1]-l[1]]];void 0!=s&&(a[0][0]=s.invert(a[0][0]),a[1][0]=s.invert(a[1][0]),a[2][0]=s.invert(a[2][0]),a[3][0]=s.invert(a[3][0])),void 0!=f&&(a[0][1]=f.invert(a[0][1]),a[1][1]=f.invert(a[1][1]),a[2][1]=f.invert(a[2][1]),a[3][1]=f.invert(a[3][1]));var c=!1;for(n=0;nwindow.innerWidth&&o.style("left",d3.event.pageX-15-300+"px")}function ot(t,e){var n=d3.select(this).style("fill","black");if(d3.select(n.node().parentNode).select("line."+a(B,"tick")).attr("stroke",K).attr("stroke-width",Y),M){var o=d3.select(n.node().parentNode).select("line."+a(B,"guideline")),i=o.attr("minor");o.attr("stroke",function(t,e){return"true"==i?r(I,.8):I}).attr("stroke-width",function(t,e){return"true"==i?.8*J:J})}d3.select("#"+a(B,"guideline-tooltip")).remove()}return nt.label=function(t){return arguments.length?(m=t,nt):m},nt.tickTickLabelSpacer=function(t){return arguments.length?(X=t,nt):X},nt.tickLabelMargin=function(t){return arguments.length?(R=t,nt):R},nt.selection=function(e){return arguments.length?(t=e,nt):t},nt.orient=function(t){return arguments.length?(b=t,nt):b},nt.spaceX=function(t){return arguments.length?(w=t,nt):w},nt.spaceY=function(t){return arguments.length?(j=t,nt):j},nt.overflowQ=function(t){return arguments.length?(z=t,nt):z},nt.categoricalQ=function(t){return arguments.length?(A=t,nt):A},nt.guideLinesQ=function(t){return arguments.length?(M=t,nt):M},nt.grouping=function(t){return arguments.length?(e=t,nt):e},nt.scale=function(t){return arguments.length?(E=t,nt):E},nt.domainPadding=function(t){return arguments.length?(O=t,nt):O},nt.objectSpacer=function(t){return arguments.length?(L=t,nt):L},nt.minObjectSize=function(t){return arguments.length?(C=t,nt):C},nt.maxObjectSize=function(t){return arguments.length?(F=t,nt):F},nt.namespace=function(t){return arguments.length?(B=t,nt):B},nt.backgroundFill=function(t){return arguments.length?(Q=t,nt):Q},nt.objectClass=function(t){return arguments.length?(q=t,nt):q},nt.tickLabels=function(t){return arguments.length?(n=t,nt):n},nt.tickValues=function(t){return arguments.length?(o=t,nt):o},nt.numberOfTicks=function(t){return arguments.length?(V=t,nt):V},nt.lineStroke=function(t){return arguments.length?(D=t,nt):D},nt.lineStrokeWidth=function(t){return arguments.length?(_=t,nt):_},nt.tickStroke=function(t){return arguments.length?(K=t,nt):K},nt.tickStrokeWidth=function(t){return arguments.length?(Y=t,nt):Y},nt.tickLength=function(t){return arguments.length?(P=t,nt):P},nt.tickLabelFontSize=function(t){return arguments.length?(W=t,nt):W},nt.tickLabelMinFontSize=function(t){return arguments.length?(N=t,nt):N},nt.tickLabelMaxFontSize=function(t){return arguments.length?(T=t,nt):T},nt.tickLabelTextAnchor=function(t){return arguments.length?(l=t,nt):l},nt.tickLabelRotation=function(t){return arguments.length?(d=t,nt):d},nt.tickLabelFunc=function(t){return arguments.length?(G=t,nt):G},nt.tickLabelOnClick=function(t){return arguments.length?(H=t,nt):H},nt.guidelineSpace=function(t){return arguments.length?(h=t,nt):h},nt.guideLineStroke=function(t){return arguments.length?(I=t,nt):I},nt.guideLineStrokeWidth=function(t){return arguments.length?(J=t,nt):J},nt.transitionDuration=function(t){return arguments.length?(U=t,nt):U},nt.easeFunc=function(t){return arguments.length?($=t,nt):$},nt.objectSize=function(t){return arguments.length?(g=t,nt):g},nt.spacerSize=function(t){return arguments.length?(p=t,nt):p},nt.roundTo=function(t){return arguments.length?(tt=t,nt):tt},nt.reverseScaleQ=function(t){return arguments.length?(et=t,nt):et},nt.tickLabelOnHoverFunc=function(t){return arguments.length?(Z=t,nt):Z},nt},O.bar=function(t){var e,n,r,o,a,i,l,u,s="horizontal",d=!1,h=function(t,n){return e[t]},g=function(t,n){return d3.descending(e[t],e[n])},p=d3.scaleLinear(),m=.5,b=.05,w=50,A=100,M=2,E=j(),O="transparent",L="d3sm-bar",C="bar",F=1e3,Q=d3.easeExp,B=z(),q=1;function V(){var j="horizontal"==s||"bottom"==s||"top"==s,z=!j,V=v(t,L,{x:0,y:0,width:n,height:r},O);a=d3.keys(e),i=a.map(h);var D=void 0==o?a.sort(g):o,_=(a=f(D)).length,K=[Math.min.apply(Math,S(i))-m,Math.max.apply(Math,S(i))+m];p.domain(K).range(j?[0,r]:"right"==s?[0,n]:[n,0]);var Y=j?n:r;l=void 0==l?y(Y,_,w,A,b,d):l,u=void 0==u?x(a,Y,l,_,b,d):u;var P=k().horizontalQ(j).scale(p).moveby("category").numberOfObjects(_).objectClass(C).objectSize(l).spacerSize(u).transitionDuration(F).easeFunc(Q).namespace(L),X=P.exitFunction();P.exitFunction(function(t){void 0==l&&console.log(t.nodes(),l),X(t),t.selectAll("g").classed("to-remove",!0),t.selectAll("* > rect").transition().duration(F).attr("transform",function(t,e){return"translate(0,"+(z?0:p(K[1]))+")"}).attr("width",j?l:0).attr("height",z?l:0).remove()}),P(V,D,0);var R=[];V.selectAll("g:not(.to-remove)."+C).each(function(t,e){R.push(Number(d3.select(this).attr("parent-index")))}),E="index"==E.colorBy()?E.dataExtent([0,Math.max.apply(Math,R)]):E.dataExtent(K),V.selectAll("g."+C+":not(.to-remove)").each(function(t,n){var r=d3.select(this),o=(e[t],h(t,n)),a=(n=void 0==r.attr("parent-index")?n:r.attr("parent-index"),E(t,o,n,"fill")),i=E(t,o,n,"stroke"),u=c(r,"rect","bar-rect");void 0==u.attr("transform")&&u.attr("transform",function(t,e){return"translate(0,"+(z?0:p(K[1]))+")"}).attr("width",j?l:0).attr("height",z?l:0),u.transition().duration(F).ease(Q).attr("transform",function(t,e){return"translate("+(j?l-l*q:"right"==s?p(K[1])-p(o):l-l*q)+","+(z?l-l*q:p(K[1])-p(o))+")"}).attr("width",j?l*q:p(o)).attr("height",z?l*q:p(o)).attr("fill",a).attr("stroke",i).attr("stroke-width",M),r.on("mouseover",function(t,e){V.selectAll("g."+C).style("opacity",.2),r.style("opacity",1),u.attr("stroke-width",2*M)}),r.on("mouseout",function(){V.selectAll("g."+C).style("opacity",1),u.attr("stroke-width",M)})}),B.selection(V.selectAll(".bar-rect")).data(e),B()}return V.selection=function(e){return arguments.length?(t=e,V):t},V.data=function(t){return arguments.length?(e=t,V):e},V.orient=function(t){return arguments.length?(s=t,V):s},V.spaceX=function(t){return arguments.length?(n=t,V):n},V.spaceY=function(t){return arguments.length?(r=t,V):r},V.overflowQ=function(t){return arguments.length?(d=t,V):d},V.grouping=function(t){return arguments.length?(o=t,V):o},V.valueExtractor=function(t){return arguments.length?(h=t,V):h},V.sortingFunction=function(t){return arguments.length?(g=t,V):g},V.scale=function(t){return arguments.length?(p=t,V):p},V.domainPadding=function(t){return arguments.length?(m=t,V):m},V.objectSpacer=function(t){return arguments.length?(b=t,V):b},V.minObjectSize=function(t){return arguments.length?(w=t,V):w},V.maxObjectSize=function(t){return arguments.length?(A=t,V):A},V.barStrokeWidth=function(t){return arguments.length?(M=t,V):M},V.colorFunction=function(t){return arguments.length?(E=t,V):E},V.backgroundFill=function(t){return arguments.length?(O=t,V):O},V.namespace=function(t){return arguments.length?(L=t,V):L},V.objectClass=function(t){return arguments.length?(C=t,V):C},V.transitionDuration=function(t){return arguments.length?(F=t,V):F},V.easeFunc=function(t){return arguments.length?(Q=t,V):Q},V.barKeys=function(t){return arguments.length?(a=t,V):a},V.barValues=function(t){return arguments.length?(i=t,V):i},V.objectSize=function(t){return arguments.length?(l=t,V):l},V.spacerSize=function(t){return arguments.length?(u=t,V):u},V.tooltip=function(t){return arguments.length?(B=t,V):B},V.barPercent=function(t){return arguments.length?(q=t,V):q},V},O.bubbleHeatmap=function(t){var e,n,r,o,i,l,u,s,f,h,g,p,b="x",w="y",A="r",M="v",E=function(t,n){return e[t][b]},O=function(t,n){return e[t][w]},L=function(t,n){return e[t][A]},C=function(t,n){return e[t][M]},F=!1,Q=d3.scaleLinear(),B=.5,q=0,V=50,D=100,_=2,K="transparent",Y="d3sm-bubble",P="bubble",X=1e3,R=d3.easeExp,W=function(t,e){return E(t)-E(e)},N=function(t,e){return O(t)-O(e)},T=j().colorBy("value"),G=z();function H(){var b=v(t,Y,{x:0,y:0,width:n,height:r},K);(o=d3.keys(e)).sort(function(t,e){return W(t,e)||N(t,e)}),m("bubbleHeatmap","cells are sorted by",o),i=d(o.map(E)),l=d(o.map(O)),u=d(o.map(L)),s=d(o.map(C)),m("bubbleHeatmap","x and y keys are",{x:i,y:l});var w=i.length,j=l.length,z=[Math.min.apply(Math,S(u))-B,Math.max.apply(Math,S(u))+B];g=y(r,j,V,D,q,F),f=y(n,w,V,D,q,F),p=x(l,r,g,j,q,F),h=x(i,n,f,w,q,F),m("bubbleHeatmap","size of",{x:f,y:g}),Q.domain(z).range([Math.min(V/2,Math.min(g,f)/2),Math.min(g,f)/2]);var A=k().horizontalQ(!1).moveby("category").numberOfObjects(j).objectClass(a(P,"row")).objectSize(g).spacerSize(p).transitionDuration(X).easeFunc(R).namespace("row"),M=k().horizontalQ(!0).moveby("category").numberOfObjects(w).objectClass(P).objectSize(f).spacerSize(h).transitionDuration(X).easeFunc(R);A(b,l,0),b.selectAll("g."+a(P,"row")).each(function(t,e){M(d3.select(this),i,0)});var H=b.selectAll("g:not(.to-remove)."+P).data(o),Z=[];H.each(function(t,e){Z.push(Number(d3.select(this).attr("parent-index")))}),T="index"==T.colorBy()?T.dataExtent([0,Math.max.apply(Math,Z)]):T.dataExtent([0,Math.max.apply(Math,S(s))]),H.each(function(t,n){m("bubbleHeatmap","each cell",{key:t,index:n,node:d3.select(this).node()});var r=d3.select(this),o=(e[t],C(t,n)),i=L(t,n),l=(n=void 0==r.attr("parent-index")?n:r.attr("parent-index"),T(t,o,n,"fill")),u=T(t,o,n,"stroke");m("bubbleHeatmap","radius",{radius:i,scaled:Q(i),extent:z,range:Q.range()}),c(r,"circle",a(P,"circle")).attr("cx",f/2).attr("cy",g/2).attr("r",Q(i)).attr("fill",l).attr("stroke",u).attr("stroke-width",_)}),G.selection(H.selectAll("circle."+a(P,"circle"))).data(e),G()}return H.selection=function(e){return arguments.length?(t=e,H):t},H.data=function(t){return arguments.length?(e=t,H):e},H.spaceX=function(t){return arguments.length?(n=t,H):n},H.spaceY=function(t){return arguments.length?(r=t,H):r},H.xKey=function(t){return arguments.length?(b=t,H):b},H.yKey=function(t){return arguments.length?(w=t,H):w},H.rKey=function(t){return arguments.length?(A=t,H):A},H.vKey=function(t){return arguments.length?(M=t,H):M},H.cellKeys=function(t){return arguments.length?(o=t,H):o},H.xValues=function(t){return arguments.length?(i=t,H):i},H.yValues=function(t){return arguments.length?(l=t,H):l},H.rValues=function(t){return arguments.length?(u=t,H):u},H.vValues=function(t){return arguments.length?(s=t,H):s},H.xExtractor=function(t){return arguments.length?(E=t,H):E},H.yExtractor=function(t){return arguments.length?(O=t,H):O},H.rExtractor=function(t){return arguments.length?(L=t,H):L},H.vExtractor=function(t){return arguments.length?(C=t,H):C},H.overflowQ=function(t){return arguments.length?(F=t,H):F},H.scale=function(t){return arguments.length?(Q=t,H):Q},H.domainPadding=function(t){return arguments.length?(B=t,H):B},H.objectSpacer=function(t){return arguments.length?q=t:e},H.minObjectSize=function(t){return arguments.length?(V=t,H):V},H.maxObjectSize=function(t){return arguments.length?(D=t,H):D},H.bubbleStrokeWidth=function(t){return arguments.length?(_=t,H):_},H.backgroundFill=function(t){return arguments.length?(K=t,H):K},H.namespace=function(t){return arguments.length?(Y=t,H):Y},H.objectClass=function(t){return arguments.length?(P=t,H):P},H.transitionDuration=function(t){return arguments.length?(X=t,H):X},H.easeFunc=function(t){return arguments.length?(R=t,H):R},H.tooltip=function(t){return arguments.length?(G=t,H):G},H.colorFunction=function(t){return arguments.length?(T=t,H):T},H.xSize=function(t){return arguments.length?(f=t,H):f},H.xSpacerSize=function(t){return arguments.length?(h=t,H):h},H.ySize=function(t){return arguments.length?(g=t,H):g},H.ySpacerSize=function(t){return arguments.length?(p=t,H):p},H},O.heatmap=function(t){var e,n,r,o,i,l,u,s,f,h,g,p="x",b="y",w="v",A=function(t,n){return e[t][p]},M=function(t,n){return e[t][b]},E=function(t,n){return e[t][w]},O=!1,L=0,C=50,F=50,Q=100,B=100,q=2,V="transparent",D="d3sm-heatmap",_="heatmap",K=1e3,Y=d3.easeExp,P=function(t,e){return i.indexOf(A(t))-i.indexOf(A(e))},X=function(t,e){return l.indexOf(M(t))-l.indexOf(M(e))},R=j().colorBy("category"),W=z();function N(){var p=v(t,D,{x:0,y:0,width:n,height:r},V);o=d3.keys(e),i=d(o.map(A)),l=d(o.map(M)),u=d(o.map(E)),o.sort(function(t,e){return P(t,e)||X(t,e)}),m("heatmap","cells are sorted by",o),m("heatmap","x and y keys are",{x:i,y:l});var b=i.length,w=l.length;h=y(r,w,C,B,L,O),s=y(n,b,F,Q,L,O),g=x(l,r,h,w,L,O),f=x(i,n,s,b,L,O),m("heatmap","size of",{x:s,y:h});var j=k().horizontalQ(!1).moveby("category").numberOfObjects(w).objectClass(a(_,"row")).objectSize(h+g).spacerSize(0).transitionDuration(K).easeFunc(Y).namespace("row"),z=k().horizontalQ(!0).moveby("category").numberOfObjects(b).objectClass(_).objectSize(s+f).spacerSize(0).transitionDuration(K).easeFunc(Y);j(p,l,0),p.selectAll("g."+a(_,"row")).each(function(t,e){z(d3.select(this),i,0)});var N=p.selectAll("g:not(.to-remove)."+_);if(o.length!=l.length*i.length){var T={};o.map(function(t,e){T[A(t)+"::"+M(t)]=t});for(var G=[],H=0;Hr?r:n}).attr("fill",h).attr("stroke",v).attr("stroke-width",Q).attr("transform",function(t,e){return"translate("+(g?l/2:A(u))+","+(j?l/2:A(R[1])-A(u))+")"}),k.transition().duration(_).ease(K).attr("d",function(t,e){var n=g?A(i)-A(a):l;return b(!1,0,0,j?A(i)-A(a):l,n,C,d)}).attr("transform",function(t,e){return"translate("+(g?0:A(i))+","+(j?0:A(R[1])-A(i))+")"}).attr("stroke","black").attr("stroke-width",B).attr("fill","none"),x.transition().duration(_).ease(K).attr("d",function(t,e){var n=g?A(f)-A(s):l;return b(!0,0,0,j?A(f)-A(s):l,n,C,d)}).attr("transform",function(t,e){return"translate("+(g?0:A(s))+","+(j?0:A(R[1])-A(f))+")"}).attr("stroke","black").attr("stroke-width",B).attr("fill","none")}),Y.selection(z.selectAll("g:not(.to-remove)."+D)).data(e),Y()}return P.selection=function(e){return arguments.length?(t=e,P):t},P.data=function(t){return arguments.length?(e=t,P):e},P.orient=function(t){return arguments.length?(d=t,P):d},P.spaceX=function(t){return arguments.length?(n=t,P):n},P.spaceY=function(t){return arguments.length?(r=t,P):r},P.overflowQ=function(t){return arguments.length?(h=t,P):h},P.grouping=function(t){return arguments.length?(o=t,P):o},P.quartilesKey=function(t){return arguments.length?(g=t,P):g},P.quartilesKeys=function(t){return arguments.length?(p=t,P):p},P.valueExtractor=function(t){return arguments.length?(m=t,P):m},P.sortingFunction=function(t){return arguments.length?(w=t,P):w},P.scale=function(t){return arguments.length?(A=t,P):A},P.domainPadding=function(t){return arguments.length?(M=t,P):M},P.objectSpacer=function(t){return arguments.length?(E=t,P):E},P.minObjectSize=function(t){return arguments.length?(O=t,P):O},P.maxObjectSize=function(t){return arguments.length?(L=t,P):L},P.whiskerWidthPercent=function(t){return arguments.length?(C=t,P):C},P.colorFunction=function(t){return arguments.length?(F=t,P):F},P.boxStrokeWidth=function(t){return arguments.length?(Q=t,P):Q},P.whiskerStrokeWidth=function(t){return arguments.length?(B=t,P):B},P.backgroundFill=function(t){return arguments.length?(q=t,P):q},P.namespace=function(t){return arguments.length?(V=t,P):V},P.objectClass=function(t){return arguments.length?(D=t,P):D},P.transitionDuration=function(t){return arguments.length?(_=t,P):_},P.easeFunc=function(t){return arguments.length?(K=t,P):K},P.boxKeys=function(t){return arguments.length?(a=t,P):a},P.boxValues=function(t){return arguments.length?(i=t,P):i},P.objectSize=function(t){return arguments.length?(l=t,P):l},P.spacerSize=function(t){return arguments.length?(u=t,P):u},P.tooltip=function(t){return arguments.length?(Y=t,P):Y},P},O.colorFunction=j,O.datatoggle=function(t){var e,n,r,o=function(){},i="d3sm-databar",l=!1,u=!1;d.xAxisSelectQ=function(t){return arguments.length?(l=t,d):l},d.yAxisSelectQ=function(t){return arguments.length?(u=t,d):u},d.xAxisOptions=function(t){return arguments.length?(e=t,d):e},d.yAxisOptions=function(t){return arguments.length?(n=t,d):n},d.data=function(t){return arguments.length?(r=t,d):r},d.updateFunction=function(t){return arguments.length?(o=t,d):o},d.namespace=function(t){return arguments.length?(i=t,d):i},d.currentKeys=function(){var t={};return d3.keys(s).map(function(e,n){t[e]=s[e].currentOption()}),t};var s={};function d(){var e=c(t,"div","d-inline-flex flex-row flex-wrap").selectAll("div."+a(i,"select-filter"));e.exit().remove();var n=(e=e.data(d3.keys(r))).enter().append("div").attr("class","select-filter");return(e=e.merge(n).style("margin-right","10px")).each(function(t,e){var n=A(d3.select(this)).data(r[t]).namespace(a(i,t)).selectionName(t);n(),s[t]=n}),t.selectAll("select").on("change",function(){o()}),d}return d},O.groupingSpacer=k,O.tooltip=z,O.scatter=function(t){var e,n,r,o,a,i,l,c=d3.scaleLinear(),u=.5,s=function(t,n){return e[t].x},d=d3.scaleLinear(),f=.5,h=function(t,n){return e[t].y},g=d3.scaleLinear(),p=.5,m=function(t,e){return 2},y=2,x=10,b=2,k=j(),w="transparent",A="d3sm-scatter",M="scatter-point",E=1e3,O=d3.easeExp,L=z();function C(){var j=v(t,A,{x:0,y:0,width:n,height:r},w);o=d3.keys(e),a=o.map(s),i=o.map(h),l=o.map(m),o.length;var z=[Math.min.apply(Math,S(a))-u,Math.max.apply(Math,S(a))+u],C=[Math.min.apply(Math,S(i))-f,Math.max.apply(Math,S(i))+f],F=[Math.min.apply(Math,S(l))-p,Math.max.apply(Math,S(l))+p];c.domain(z).range([0,n]),d.domain(C).range([r,0]),g.domain(F).range([y,x]);var Q=j.selectAll("."+M),B=(Q=Q.data(o)).enter().append("circle").attr("class",M).attr("cx",0).attr("cy",r).attr("r",0),q=Q.exit();(Q=Q.merge(B)).each(function(t,n){var r=d3.select(this),o=e[t],u=a[n],s=i[n],f=l[n],h=k(t,o,n,"fill"),p=k(t,o,n,"stroke");r.transition().duration(E).ease(O).attr("cx",c(u)).attr("cy",d(s)).attr("r",g(f)).attr("fill",h).attr("stroke",p).attr("stroke-width",b),r.on("mouseover",function(t,e){Q.style("opacity",.2),r.style("opacity",1),r.transition().duration(E/2).ease(O).attr("stroke-width",2*b).attr("r",1.5*g(f))}),r.node().addEventListener("mouseout",function(){j.selectAll("."+M).style("opacity",1),r.transition().duration(E/2).ease(O).attr("stroke-width",b).attr("r",g(f))})}),q.transition().duration(E).ease(O).attr("cx",0).attr("cy",r).attr("r",0).remove(),L.selection(Q).data(e),L()}return C.selection=function(e){return arguments.length?(t=e,C):t},C.data=function(t){return arguments.length?(e=t,C):e},C.spaceX=function(t){return arguments.length?(n=t,C):n},C.spaceY=function(t){return arguments.length?(r=t,C):r},C.scaleX=function(t){return arguments.length?(c=t,C):c},C.domainPaddingX=function(t){return arguments.length?(u=t,C):u},C.valueExtractorX=function(t){return arguments.length?(s=t,C):s},C.scaleY=function(t){return arguments.length?(d=t,C):d},C.domainPaddingY=function(t){return arguments.length?(f=t,C):f},C.valueExtractorY=function(t){return arguments.length?(h=t,C):h},C.scaleR=function(t){return arguments.length?(g=t,C):g},C.domainPaddingR=function(t){return arguments.length?(p=t,C):p},C.valueExtractorR=function(t){return arguments.length?(m=t,C):m},C.minRadius=function(t){return arguments.length?(y=t,C):y},C.maxRadius=function(t){return arguments.length?(x=t,C):x},C.pointStrokeWidth=function(t){return arguments.length?(b=t,C):b},C.colorFunction=function(t){return arguments.length?(k=t,C):k},C.backgroundFill=function(t){return arguments.length?(w=t,C):w},C.namespace=function(t){return arguments.length?(A=t,C):A},C.objectClass=function(t){return arguments.length?(M=t,C):M},C.transitionDuration=function(t){return arguments.length?(E=t,C):E},C.easeFunc=function(t){return arguments.length?(O=t,C):O},C.pointKeys=function(t){return arguments.length?(o=t,C):o},C.valuesX=function(t){return arguments.length?(a=t,C):a},C.valuesY=function(t){return arguments.length?(i=t,C):i},C.valuesR=function(t){return arguments.length?(l=t,C):l},C.tooltip=function(t){return arguments.length?(L=t,C):L},C},O.plotZoom=function(t,e,r){var o,i=20,l=void 0==t.orient?"horizontal":t.orient(),c=t.spaceX(),u=t.spaceY(),s=t.selection(),d=e.selection(),f=r.selection();function h(){var e=s.select("."+a(t.namespace(),"object-container")),r=n(e.attr("transform")),o=e.attr("transform","translate(0,0)");c=s.node().getBBox().width-.9*t.spaceX(),u=s.node().getBBox().height-.9*t.spaceY(),o.attr("transform","translate("+r[0]+","+r[1]+")"),m("plotZoom","setLocks",{xLock:c,yLock:u})}function g(){var g,p;h(),"2D"==l&&(g=!0,p=!0),"horizontal"==l&&(g=!0,p=!1),"vertical"==l&&(p=!0,g=!1);var m=d3.event.transform,v=s.node().getBBox(),y=d.node().getBBox(),x=d.node().getBBox();if(v.width,v.x,v.height,v.y,y.width,y.height,x.width,x.height,"wheel"==o){d3.event.preventDefault();var b=d3.event.deltaY*i,k=d3.event.shiftKey;(m="2D"==l?k?{k:1,x:b,y:0}:{k:1,x:0,y:b}:g?{k:1,x:b,y:0}:{k:1,x:0,y:b}).applyX=function(t){return t*this.k+-1*this.x},m.applyY=function(t){return t*this.k+-1*this.y}}var w=s.select("."+a(t.namespace(),"object-container")),S=d.select("."+a(e.namespace(),"object-container")),j=f.select("."+a(r.namespace(),"object-container")),z=n(w.attr("transform")),A=(n(S.attr("transform")),n(j.attr("transform")),g?m.applyX(z[0]):0);g&&(A=A<-c?(m.x=0,-c):(m.x=0,Math.min(A,0)));var M=p?m.applyY(z[1]):0;p&&(M=M<-u?(m.y=0,-u):(m.y=0,Math.min(M,0))),w.attr("transform","translate("+A+","+M+")"),g&&S.attr("transform","translate("+A+",0)"),p&&j.attr("transform","translate(0,"+M+")")}return d3.select(s.thisSVG()),g.eventType=function(t){return arguments.length?(o=t,g):o},g.wheelSpeed=function(t){return arguments.length?(i=t,g):i},g.orient=function(t){return arguments.length?(l=t,g):l},g.xLock=function(t){return arguments.length?(c=t,g):c},g.yLock=function(t){return arguments.length?(u=t,g):u},g.setLocks=h,g.reset=function(){var n=s.select("."+a(t.namespace(),"object-container")),o=d.select("."+a(e.namespace(),"object-container")),i=f.select("."+a(r.namespace(),"object-container"));n.attr("transform","translate(0,0)"),o.attr("transform","translate(0,0)"),i.attr("transform","translate(0,0)")},g},O.multiPlotZoom=function(t){var e,r=20,o=void 0==t.orient?"horizontal":t.orient(),i=t.spaceX(),l=t.spaceY(),c=t.selection(),u=(d3.select(c.thisSVG()),[]),s=[];function d(){var e=c.select("."+a(t.namespace(),"object-container")),r=n(e.attr("transform")),o=e.attr("transform","translate(0,0)");i=c.node().getBBox().width-t.spaceX(),l=c.node().getBBox().height-t.spaceY(),o.attr("transform","translate("+r[0]+","+r[1]+")"),m("plotZoom","setLocks",{xLock:i,yLock:l})}function f(){d();var f,h,g=u.map(function(t,e){return t.selection()}),p=s.map(function(t,e){return t.selection()});"2D"==o&&(f=!0,h=!0),"horizontal"==o&&(f=!0,h=!1),"vertical"==o&&(h=!0,f=!1);var m=d3.event.transform,v=c.node().getBBox();if(g.map(function(t,e){return t.node().getBBox()}),g.map(function(t,e){return t.node().getBBox()}),v.width,v.x,v.height,v.y,"wheel"==e){d3.event.preventDefault();var y=d3.event.deltaY*r,x=d3.event.shiftKey;(m="2D"==o?x?{k:1,x:y,y:0}:{k:1,x:0,y:y}:f?{k:1,x:y,y:0}:{k:1,x:0,y:y}).applyX=function(t){return t*this.k+-1*this.x},m.applyY=function(t){return t*this.k+-1*this.y}}var b=c.select("."+a(t.namespace(),"object-container")),k=g.map(function(t,e){return t.select("."+a(u[e].namespace(),"object-container"))}),w=p.map(function(t,e){return t.select("."+a(s[e].namespace(),"object-container"))}),S=n(b.attr("transform")),j=(k.map(function(t,e){return n(t.attr("transform"))}),w.map(function(t,e){return n(t.attr("transform"))}),f?m.applyX(S[0]):0);f&&(j=j<-i?(m.x=0,-i):(m.x=0,Math.min(j,0)));var z=h?m.applyY(S[1]):0;h&&(z=z<-l?(m.y=0,-l):(m.y=0,Math.min(z,0))),b.attr("transform","translate("+j+","+z+")"),f&&k.map(function(t,e){t.attr("transform","translate("+j+",0)")}),h&&w.map(function(t,e){t.attr("transform","translate(0,"+z+")")})}return f.eventType=function(t){return arguments.length?(e=t,f):e},f.wheelSpeed=function(t){return arguments.length?(r=t,f):r},f.orient=function(t){return arguments.length?(o=t,f):o},f.xLock=function(t){return arguments.length?(i=t,f):i},f.yLock=function(t){return arguments.length?(l=t,f):l},f.xComponents=function(t){return arguments.length?(u=t,f):u},f.yComponents=function(t){return arguments.length?(s=t,f):s},f.setLocks=d,f.reset=function(){var e=c.select("."+a(t.namespace(),"object-container")),n=xAxisSel.select("."+a(xAxis.namespace(),"object-container")),r=yAxisSel.select("."+a(yAxis.namespace(),"object-container"));e.attr("transform","translate(0,0)"),n.attr("transform","translate(0,0)"),r.attr("transform","translate(0,0)")},f},O.violin=function(t){var e,n,a,i,l,u,d,g,p="horizontal",m=!0,b=!0,w=function(t,n){return e[t]},A=function(t,n){return d3.descending(e[t],e[n])},M=d3.scaleLinear(),E=.5,O=.05,L=50,C=100,F=2,Q=j(),B=function(t,e,n,o,a){var i=d3.scaleLinear().domain([o,a]).range([-.25,.05]),l=r(n.replace("#",""),i(t)),c="stroke"==e?0:.25;return r(l.replace("#",""),c)},q=3,V=2,D="transparent",_="d3sm-violin",K="violin",Y=1e3,P=d3.easeExp,X=["Q0","Q1","Q2","Q3","Q4"],R=z().keys([X[4],X[3],X[2],X[1],X[0]]),W=z(),N=function(t,e){return e.points},T=function(t,e){return e[t].value};function G(){var r,u,w,j="horizontal"==p,G=v(t,_,{x:0,y:0,width:n,height:a},D),H=void 0==i?d3.keys(e).sort(A):i;l=f(H);var Z=function(){var t,e,n=!0,r=["Q0","Q1","Q2","Q3","Q4"];function a(a,i){var l=i[a],c=t(a,l),u=d3.keys(c),s=u.map(function(t,n){return e(t,c)}),d=o(s,r),f=d3.histogram()(s),h=f.map(function(t){return t.length}),g=n?{x:0,y:d3.min(s)}:{x:d3.min(s),y:0},p=n?{x:0,y:d3.max(s)}:{x:d3.max(s),y:0},m=f.map(function(t,e){return n?{y:t.length?d3.median(t):d3.median([t.x0,t.x1]),x:h[e]}:{x:t.length?d3.median(t):d3.median([t.x0,t.x1]),y:h[e]}});m=[g].concat(m).concat([p]),l.binned=f,l.frequencies=h,l.contour=m,l.quartiles=d,l.pointKeys=u,l.pointValues=s}return a.horizontalQ=function(t){return arguments.length?(n=t,a):n},a.quartileKeys=function(t){return arguments.length?(r=t,a):r},a.violinPointsExtractor=function(e){return arguments.length?(t=e,a):t},a.violinPointValueExtractor=function(t){return arguments.length?(e=t,a):e},a}().horizontalQ(j).quartileKeys(X).violinPointsExtractor(N).violinPointValueExtractor(T);l.map(function(t,n){Z(t,e)});var I=l.length,J=(r=[]).concat.apply(r,S(l.map(function(t,n){return e[t].quartiles[X[0]]}))),U=(u=[]).concat.apply(u,S(l.map(function(t,n){return e[t].quartiles[X[X.length-1]]}))),$=[Math.min.apply(Math,S(J))-E,Math.max.apply(Math,S(U))+E];M.domain($).range(j?[0,a]:[0,n]);var tt=j?n:a;d=y(tt,I,L,C,O,m),g=x(H,tt,d,I,O,m),k().horizontalQ(j).scale(M).moveby("category").numberOfObjects(I).objectClass(K).objectSize(d).spacerSize(g).transitionDuration(Y).easeFunc(P).namespace(_)(G,H,0);var et=[];G.selectAll("g:not(.to-remove)."+K).each(function(t,e){s(l,t)&&et.push(Number(d3.select(this).attr("parent-index")))}),Q="index"==Q.colorBy()?Q.dataExtent([0,Math.max.apply(Math,et)]):Q.dataExtent($);var nt=Math.max.apply(Math,S((w=[]).concat.apply(w,S(l.map(function(t,n){return d3.max(e[t].frequencies)}))))),rt=d3.scaleLinear().domain([0,nt]).range([0,d/2]),ot=d3.line().x(function(t,e){return j?-rt(t.x):M(t.x)}).y(function(t,e){return j?M($[1])-M(t.y):-rt(t.y)}).curve(d3.curveBasis),at=d3.line().x(function(t,e){return j?rt(t.x):M(t.x)}).y(function(t,e){return j?M($[1])-M(t.y):rt(t.y)}).curve(d3.curveBasis);G.selectAll("g:not(.to-remove)."+K).each(function(t,n){var r=d3.select(this),o=e[t];if(s(l,t)){n=void 0==r.attr("parent-index")?n:r.attr("parent-index");var a=Q(t,o,n,"fill"),i=Q(t,o,n,"stroke"),u=c(r,"g","area"),f=c(u,"path","left"),g=c(u,"path","right"),p=c(r,"g","quarts"),m=(c(p,"line","q3"),c(p,"line","q1"),o.quartiles[X[3]],o.quartiles[X[2]]);if(o.quartiles[X[1]],r.attr("transform",j?"translate("+d/2+",0)":"translate(0,"+d/2+")"),f.transition().duration(Y).attr("d",function(t,e){return ot(o.contour)}).attr("fill",a).attr("stroke",i).attr("stroke-width",F),g.transition().duration(Y).attr("d",function(t,e){return at(o.contour)}).attr("fill",a).attr("stroke",i).attr("stroke-width",F),u.node().addEventListener("mouseover",function(t,e){G.selectAll("g."+K).style("opacity",.2),r.style("opacity",1),f.attr("stroke-width",2*F),g.attr("stroke-width",2*F)}),u.node().addEventListener("mouseout",function(t,e){G.selectAll("g."+K).style("opacity",1),f.attr("stroke-width",F),g.attr("stroke-width",F)}),b){var v=c(r,"g","points"),y=v.selectAll(".point").data(o.pointKeys);y.on("mouseover",null),y.exit().transition().ease(P).duration(Y).attr("r",0).attr("cy",j?M($[1])-M(m):rt(0)).attr("cx",j?rt(0):M(m)).remove();var x=y.enter().append("circle").attr("class","point").attr("r",0).attr("cx",j?0:M(m)).attr("cy",j?M(m):0);y=y.merge(x),z().selection(y).data(N(t,o)).header(W.header()).keys(W.keys()).values(W.values())();var k=d3.min(o.pointValues),w=d3.max(o.pointValues);y.transition().duration(Y).ease(P).attr("r",q).attr("cy",function(t,e){var n=o.pointValues[e];if(j)return M($[1])-M(n);var r=h(o.binned,n),a=Math.random(),i=rt(a*o.frequencies[r]*.5);return Math.random()>.5?i:-i}).attr("cx",function(t,e){var n=o.pointValues[e];if(j){var r=h(o.binned,n),a=Math.random(),i=rt(a*o.frequencies[r]*.5);return Math.random()>.5?i:-i}return M(n)}).attr("stroke",function(t,e){return t=o.pointValues[e],B(t,"stroke",i,k,w)}).attr("fill",function(t,e){return t=o.pointValues[e],B(t,"fill",i,k,w)}).attr("stroke-width",V),v.selectAll("circle.point").on("mouseover",function(t,e){G.selectAll("g."+K).style("opacity",.2),r.style("opacity",1),f.attr("stroke-width",2*F),g.attr("stroke-width",2*F),G.selectAll(".point").style("opacity",.2),d3.select(this).style("opacity",1).attr("r",2*q).attr("stroke-width",2*V)}),v.selectAll("circle.point").on("mouseout",function(t,e){var n=document.createEvent("SVGEvents");n.initEvent("mouseout",!0,!0),u.node().dispatchEvent(n),G.selectAll(".point").style("opacity",1),d3.select(this).attr("stroke-width",V).attr("r",q)})}else cV.selectAll(".point").transition().duration(Y).ease(P).attr("r",0).attr("cy",j?M($[1])-M(m):rt(0)).attr("cx",j?rt(0):M(m)).remove()}}),R.selection(G.selectAll("g:not(.to-remove)."+K+" .area")),void 0==R.data()&&R.data(e),R(),void 0==R.values()&&R.values([function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]}])}return G.violinPointsExtractor=function(t){return arguments.length?(N=t,G):N},G.violinPointValueExtractor=function(t){return arguments.length?(T=t,G):T},G.selection=function(e){return arguments.length?(t=e,G):t},G.data=function(t){return arguments.length?(e=t,G):e},G.orient=function(t){return arguments.length?(p=t,G):p},G.spaceX=function(t){return arguments.length?(n=t,G):n},G.spaceY=function(t){return arguments.length?(a=t,G):a},G.overflowQ=function(t){return arguments.length?(m=t,G):m},G.pointsQ=function(t){return arguments.length?(b=t,G):b},G.grouping=function(t){return arguments.length?(i=t,G):i},G.valueExtractor=function(t){return arguments.length?(w=t,G):w},G.sortingFunction=function(t){return arguments.length?(A=t,G):A},G.scale=function(t){return arguments.length?(M=t,G):M},G.domainPadding=function(t){return arguments.length?(E=t,G):E},G.objectSpacer=function(t){return arguments.length?(O=t,G):O},G.minObjectSize=function(t){return arguments.length?(L=t,G):L},G.maxObjectSize=function(t){return arguments.length?(C=t,G):C},G.objectStrokeWidth=function(t){return arguments.length?(F=t,G):F},G.colorFunction=function(t){return arguments.length?(Q=t,G):Q},G.pointColorFunc=function(t){return arguments.length?(B=t,G):B},G.pointRadius=function(t){return arguments.length?(q=t,G):q},G.pointStrokeWidth=function(t){return arguments.length?(V=t,G):V},G.backgroundFill=function(t){return arguments.length?(D=t,G):D},G.namespace=function(t){return arguments.length?(_=t,G):_},G.objectClass=function(t){return arguments.length?(K=t,G):K},G.transitionDuration=function(t){return arguments.length?(Y=t,G):Y},G.easeFunc=function(t){return arguments.length?(P=t,G):P},G.quartileKey=function(t){return arguments.length?(quartileKey=t,G):quartileKey},G.quartileKeys=function(t){return arguments.length?(X=t,G):X},G.violinKeys=function(t){return arguments.length?(l=t,G):l},G.violinValues=function(t){return arguments.length?(u=t,G):u},G.objectSize=function(t){return arguments.length?(d=t,G):d},G.spacerSize=function(t){return arguments.length?(g=t,G):g},G.tooltip=function(t){return arguments.length?(R=t,G):R},G.pointsTooltip=function(t){return arguments.length?(W=t,G):W},G},O.numericLegend=function(t){var e,n,r=0,o=1,l=j(),u="d3sm-linear-vertical-gradient",s=12,d="transparent",f="black",h=2;function g(){var g=v(t,u,{x:0,y:0,width:e,height:n},d),p=c(c(t,"defs"),"linearGradient").attr("x1","0%").attr("y1","100%").attr("x2","0%").attr("y2","0%").attr("id",a(u,"numerical-legend-gradient"));l.dataExtent([r,o]).colorBy("value").valueExtractor(function(t,e,n){return e}),p.selectAll("stop").data(l.colors()).enter().append("stop").attr("offset",function(t,e){return e/(l.colors().length-1)}).attr("stop-color",function(t){return t});var m=c(g,"rect","legend").attr("transform","translate(0,"+s+")").style("fill","url(#"+a(u,"numerical-legend-gradient")+")").attr("x",0).attr("y",0).attr("width",e).attr("height",n-2*s).on("mousemove",function(t,e){!function(t,e,n,d){var g=d3.scaleLinear().domain([0,n.attr("height")]).range([o,r]),p=d3.mouse(n.node()),m=i(g(p[1]),h),v=l(void 0,m,void 0,"stroke"),y=l(void 0,m,void 0,"fill");c(c(d3.select("body"),"div",a(u,"legend-tooltip")).attr("id",a(u,"legend-tooltip")).style("position","absolute").style("left",d3.event.pageX+15+"px").style("top",d3.event.pageY+15+"px").style("background-color",y).style("border-color",v).style("min-width",s*(String(o).split(".")[0].length+3)+"px").style("min-height",s*(String(o).split(".")[0].length+3)+"px").style("border-radius","50%").style("border-radius","5000px").style("display","flex").style("justify-content","center").style("text-align","middle").style("padding","2px").style("border-style","solid").style("border-width",2),"div").text(m).style("color",f).style("align-self","center")}(0,0,m,d3.select(this))}).on("mouseout",function(t,e){d3.select("#"+a(u,"legend-tooltip")).remove()});c(g,"text","min").text(i(r,2)).attr("text-anchor","middle").attr("font-size",s+"px").attr("transform",function(t,r){return"translate("+e/2+","+(n-s/4)+")"}),c(g,"text","max").text(i(o,2)).attr("text-anchor","middle").attr("font-size",s+"px").attr("transform",function(t,n){return"translate("+e/2+","+s+")"})}return g.min=function(t){return arguments.length?(r=t,g):r},g.max=function(t){return arguments.length?(o=t,g):o},g.spaceX=function(t){return arguments.length?(e=t,g):e},g.spaceY=function(t){return arguments.length?(n=t,g):n},g.namespace=function(t){return arguments.length?(u=t,g):u},g.fontSize=function(t){return arguments.length?(s=t,g):s},g.backgroundFill=function(t){return arguments.length?(d=t,g):d},g.colorFunction=function(t){return arguments.length?(l=t,g):l},g.textColor=function(t){return arguments.length?(f=t,g):f},g.roundTo=function(t){return arguments.length?(h=t,g):h},g},O.categoricLegend=function(t){var e,n,r,o,a,i="horizontal",l=!1,u=function(t,e){return n[t]},s=function(t,e){return d3.ascending(t,e)},d=.05,h=10,g=100,p=2,m=j(),b="transparent",w="d3sm-legend",S="legend",z=1e3,A=d3.easeExp;function M(){var n="horizontal"==i,u=!n,j=v(t,w,{x:0,y:0,width:r,height:o},b);m.dataExtent([0,e.length-1]).colorBy("categories").categoryExtractor(function(t,e,n){return e});var M=Math.min(r,o),E=e.length,O=void 0==a?e.sort(s):a,L=f(O),C=n?r:o,F=y(C,E,h,g,d,l),Q=x(L,C,F,E,d,l);k().horizontalQ(n).scale(scale).moveby("category").numberOfObjects(E).objectClass(S).objectSize(F).spacerSize(Q).transitionDuration(z).easeFunc(A).namespace(w)(j,O,0),M=Math.min(F,r,o)/2-p,j.selectAll("g:not(.to-remove)."+S).each(function(t,e){var o=d3.select(this),a=c(o,"circle"),i=m(void 0,t,e,"fill"),l=m(void 0,t,e,"stroke"),s=n?M+p:(r-2*M)/2+M,d=u?M+p:(r-2*M)/2+M;a.attr("r",M).attr("cx",s).attr("cy",d).attr("fill",i).attr("stroke",l).attr("stroke-width",p);var f=c(o,"text");f.text(t).attr("text-anchor","middle").attr("transform",function(t,e){return"translate("+s+","+(d+f.node().getBoundingClientRect().height/4)+")"})})}return M.categories=function(t){return arguments.length?(e=t,M):e},M.selection=function(e){return arguments.length?(t=e,M):t},M.data=function(t){return arguments.length?(n=t,M):n},M.orient=function(t){return arguments.length?(i=t,M):i},M.spaceX=function(t){return arguments.length?(r=t,M):r},M.spaceY=function(t){return arguments.length?(o=t,M):o},M.overflowQ=function(t){return arguments.length?(l=t,M):l},M.grouping=function(t){return arguments.length?(a=t,M):a},M.valueExtractor=function(t){return arguments.length?(u=t,M):u},M.sortingFunction=function(t){return arguments.length?(s=t,M):s},M.objectSpacer=function(t){return arguments.length?(d=t,M):d},M.minObjectSize=function(t){return arguments.length?(h=t,M):h},M.maxObjectSize=function(t){return arguments.length?(g=t,M):g},M.bubbleStrokeWidth=function(t){return arguments.length?(p=t,M):p},M.colorFunction=function(t){return arguments.length?(m=t,M):m},M.backgroundFill=function(t){return arguments.length?(b=t,M):b},M.namespace=function(t){return arguments.length?(w=t,M):w},M.objectClass=function(t){return arguments.length?(S=t,M):S},M.transitionDuration=function(t){return arguments.length?(z=t,M):z},M.easeFunc=function(t){return arguments.length?(A=t,M):A},M},O.lasso=E,O.lassoWidget=function(t){var e,n,r,o,i,l,u,s,d="d3sm-lasso",f=(t=t,d3.line().x(function(t,e){return t[0]}).y(function(t,e){return t[1]}).curve(d3.curveLinearClosed),j());function h(t,e){console.log(t,e)}function g(){d3.select("html").select("style."+d+"lasso-widget").empty()&&d3.select("html").append("style").classed(d+"lasso-widget",!0).html("."+a(d,"data-table")+"{ height:100px; overflow:auto; }")}function p(t){console.log(t)}function m(){p((t.select("."+a(d,"tab-list")),t.select("."+a(d,"tab-content")).selectAll("."+a(d,"data-table")),{}))}function v(e,n){var r,o;t.select("#"+a(d,"tab",e)),t.select("#"+a(d,"tab","pane",e)),function(){var e=t.select("."+a(d,"tab-list")),n=(t.select("."+a(d,"tab-content")),e.selectAll("."+a(d,"tab")));if(0==n.size());else{var r=n.nodes()[n.size()-1];d3.select(r).select("a").attr("href")}}(),r=t.select("."+a(d,"tab-list")),o=t.select("."+a(d,"tab-content")),r=r.selectAll("."+a(d,"tab")),o=o.selectAll("."+a(d,"tab","pane")),r.each(function(t,e){d3.select(this).datum(e).attr("id",a(d,"tab",e)).select("a").attr("href","#"+a(d,"tab","pane",e)).text(function(t,n){var r=d3.select(this).text();return"Group"==r.split(" ")[0]?"Group "+e:r})}),o.each(function(t,e){d3.select(this).datum(e).attr("id",a(d,"tab","pane",e)),c(d3.select(this),"p","lead").text(function(t,n){var r=d3.select(this).text();return"Group"==r.split(" ")[0]?"Group "+e:r}),c(d3.select(this),"button","remove-btn").on("click",v)})}function y(t,i){var h=c(t,"p","lead").text("Group "+i),g=c(c(t,"div","table-responsive").attr("class",a(d,"data-table")),"table","table").classed("table-sm",!0).classed("table-hover",!0),p=(c(g,"caption","caption").html("List of selected"),c(t,"div","text-right")),m=i%f.colors().length;i%2!=0&&(m=f.colors().length-1-m);var y,k,w,S=function(t){var e=c(t,"button","lasso-btn").classed("btn btn-info",!0).classed(d,!0);return c(e,"i","fa").classed("fa-hand-pointer-o",!0),c(e,"span").text("Lasso select"),e}(p),j=(y=i,k=f.colors()[m],E().namespace(d).svg(e).objectClass(o).chartContainer(n).objectContainer(r).instance(y).color(k).yScale(s).xScale(u));!function(t,e,r,o){r.eventCatcher(t),t.node().addEventListener("click",x),t.datum([r]).on(a(d,"render"),function(){d3.select(this).datum()[0].draw()}).on(a(d,"drag"),function(t){var e=n.selectAll(".in-lasso-"+t[0].instance()).nodes(),a=e.map(function(t,e){var n=l(d3.select(t).datum());return n.__node=t,n});b(o,a,r)}),e.on("click",function(){r.allPoints([]),r.currentPoints([]),t.dispatch(a(d,"drag"))})}(S,function(t){var e=c(t,"button","clear-btn").classed("btn btn-light",!0).classed(d,!0);return c(e,"i","fa").classed("fa-eraser",!0),c(e,"span").text("Clear selection"),e}(p),j,g),c(w=c(p,"button","remove-btn").classed("btn btn-danger",!0).on("click",v),"i","fa").classed("fa-trash-o",!0),c(w,"span").text("Remove group"),h.on("mouseover",function(){j.draw()}),h.on("mouseout",function(){j.remove()})}function x(){var e=d3.select(this),n=e.datum()[0];n.toggle();var r=n.activeQ();function o(t){t.target!=e.node()&&t.target!=e.select("span").node()&&t.target!=e.select("i").node()&&(n.toggle(!1),e.classed("btn-info",!0),e.classed("btn-warning",!1),e.select("span").text("Lasso select"))}n.activeQ()?(e.classed("btn-info",!r),e.classed("btn-warning",r),e.select("span").text("Lasso select (active)"),t.selectAll("."+d+".lasso-btn").dispatch(a(d,"render")),d3.select("html").node().addEventListener("mousedown",o)):(e.classed("btn-info",!r),e.classed("btn-warning",r),e.select("span").text("Lasso select"),d3.select("html").node().removeEventListener("mousedown",o))}function b(t,e,n){var r=c(c(t,"thead"),"tr"),o=c(t,"tbody"),a=function(t,e){var n=d3.keys(e[0]).filter(function(t){return"__node"!=t});n.length>0&&n.push("remove");var r=t.selectAll("th");return(r=r.data(n)).exit().remove(),r=r.merge(r.enter().append("th").attr("scope","col")).text(function(t,e){return t}),n}(r,e);(function(t,e){var n=t.selectAll("tr");return(n=n.data(e)).exit().remove(),n=n.merge(n.enter().append("tr"))})(o,e).each(function(r,o){var i=d3.select(this);!function(t,e,n,r,o,a){(t=t.data(e)).exit().remove(),(t=t.merge(t.enter().append("td"))).html(function(t,e){return"remove"!=t?n[t]:""}).classed("text-left",function(t,e){return"remove"==t}),t.select("i.fa-close").on("click",function(t,e){var i=n.__node,l=d3.select(i);l.classed("in-lasso",!1),l.classed("in-lasso-"+o.instance(),!1),r.map(function(t,e){t.__node==i&&(r.splice(e,1),b(a,r,o))})})}(i.selectAll("td"),a,r,e,n,t),i.on("mouseover",function(t,e){n.applyObjectAttributes(d3.select(t.__node),!0)}).on("mouseout",function(t,e){n.applyObjectAttributes(d3.select(t.__node),!1)})})}function k(e,n){var r=t.select("."+a(d,"tab-list")),o=t.select("."+a(d,"tab-content")),l=function(){for(var e=t.select("."+a(d,"tab-list")).selectAll("li").size()-3,n="Group "+e,r=[];r.includes(n);)n="Group "+(e+=1);return e}();if(r.selectAll("."+a(d,"tab")).size()!=i){!function(t,e){var n=t.insert("li",".right-aligned-tabs").classed("nav-item",!0).classed("alert",!0).classed("alert-secondary",!0).classed("alert-dismissable",!0).classed("fade",!0).classed("show",!0).attr("role","alert").attr("id",a(d,"tab",e)).classed(a(d,"tab"),!0);c(n,"a").attr("data-toggle","tab").text("Group "+e).attr("href","#"+a(d,"tab","pane",e)).on("dblclick",function(t,e){var n=d3.select(this);n.attr("contenteditable",!0),d3.select(n.node().parentNode).classed("alert-secondary",!1).classed("alert-warning",!0)}).on("blur",function(t,e){var n=d3.select(this);n.attr("contenteditable",!1),d3.select(n.node().parentNode).classed("alert-secondary",!0).classed("alert-warning",!1)}).on("input",function(t,e){var n=d3.select(this),r=n.text();d3.select(n.attr("href")).select("p.lead").text(r)})}(r,l);var u=function(t,e){return t.append("div").datum(e).attr("role","tabpanel").classed("tab-pane",!0).classed("text-left",!0).classed(a(d,"tab","pane"),!0).attr("id",a(d,"tab","pane",e))}(o,l);y(u,l),u.attr("id"),t.select("."+a(d,"tab-content"))}else h("only "+i+" allowed.","warning")}return w=c(t,"div","card"),S=c(c(w,"div","card-header"),"ul","nav").classed("nav-tabs card-header-tabs",!0).classed(a(d,"tab-list"),!0).attr("role","tablist"),z=c(c(w,"div","card-body"),"div","tab-content").classed(a(d,"tab-content"),!0),A=c(S,"div","right-aligned-tabs").classed("nav ml-auto ",!0),function(t){c(c(c(t,"li",a(d,"plus-tab")).classed("ml-auto",!0).classed("nav-item",!0).on("click",k),"a","nav-link"),"i","fa").classed("fa-plus fa-2x text-success",!0)}(A),function(t){c(c(c(t,"li",a(d,"send-tab")).classed("ml-auto",!0).classed("nav-item",!0).on("click",m),"a","nav-link"),"i","fa").classed("fa-paper-plane-o fa-2x text-primary",!0)}(A),function(t){c(c(c(t,"li",a(d,"close-tab")).classed("ml-auto",!0).classed("nav-item",!0).on("click",function(){d3.mouse(d3.select("html").node())}),"a","nav-link"),"i","fa").classed("fa-window-close-o fa-2x text-danger",!0)}(A),c(c(z,"div","tab-pane").classed(a(d,"default-tab"),!0).classed("active",!0).classed("text-left",!0),"div").html("Click to add a new group.
Click to submit for re-analysis.
Click to close the dataselect widget."),g.objectClass=function(t){return arguments.length?(o="."+t.replace(".",""),g):o},g.svg=function(t){return arguments.length?(e=t,g):e},g.submit=function(t){return arguments.length?(p=t,g):p},g.maxNumberOfGroups=function(t){return arguments.length?(i=t,g):i},g.onError=function(t){return arguments.length?(h=t,g):h},g.objectContainer=function(t){return arguments.length?(r=t,g):r},g.chartContainer=function(t){return arguments.length?(n=t,g):n},g.dataExtractor=function(t){return arguments.length?(l=t,g):l},g.xScale=function(t){return arguments.length?(u=t,g):u},g.yScale=function(t){return arguments.length?(s=t,g):s},g;var w,S,z,A},O.selectFilter=A,O.upset=function(t){var e,n,r,o,i,l,u,s,f,h,g,p="horizontal",m=!1,b=20,w=50,S=2,j="transparent",z="d3sm-upset",A="upset",M=1e3,E=d3.easeExp,O=function(t,n){return e[t].set},L=function(t,n){return e[t].intersection},C=function(t,n){return e[t].elements},F=.05,Q=.05,B=function(t,e){return i.indexOf(O(t))-i.indexOf(O(e))},q=function(t,e){return l.indexOf(L(t))-l.indexOf(L(e))};function V(){var S="horizontal"==p,C=!S,V=v(t,z,{x:0,y:0,width:n,height:r},j);o=d3.keys(e),i=d(o.map(O)).sort(),l=d(o.map(L)).sort().sort(function(t,e){return t.split(";").length-e.split(";").length}),S?o.sort(function(t,e){return q(t,e)||B(t,e)}):o.sort(function(t,e){return B(t,e)||q(t,e)});var D=S?l:i,_=S?i:l,K=S?D.length:_.length,Y=S?_.length:D.length;h=y(n,K,b,w,F,m),s=y(r,Y,b,w,Q,m),g=x(D,n,h,K,F,m),f=x(_,r,s,Y,Q,m);var P=k().horizontalQ(!1).moveby("category").numberOfObjects(Y).objectSize(s).spacerSize(f).transitionDuration(M).easeFunc(E),X=k().horizontalQ(!0).moveby("category").numberOfObjects(K).objectClass(A).objectSize(h).spacerSize(g).transitionDuration(M).easeFunc(E);C?(X.objectClass(A),P.namespace("across").objectClass(a(A,"across")),P(V,_,0),V.selectAll("g."+a(A,"across")).each(function(t,e){X(d3.select(this),D,0)})):(X.namespace("across").objectClass(a(A,"across")),P.objectClass(A),X(V,D,0),V.selectAll("g."+a(A,"across")).each(function(t,e){P(d3.select(this),_,0)}));var R=V.selectAll("g:not(.to-remove)."+A),W={};o.map(function(t,e){W[O(t)+"::"+L(t)]=t}),R.data(o),R.each(function(t,n){var r=d3.select(this);if(void 0!=t){e[t];var o=O(t,n),i=L(t,n);r.classed(i,!0),r.classed(o,!0),c(r,"circle",a(A,"circle")).attr("cx",h/2).attr("cy",s/2).attr("r",void 0==u?Math.min(h,s)/2:u).attr("fill",i.includes(o)?"black":"rgb(233,233,233)").attr("stroke","black").attr("in-intersection",i.includes(o))}})}return V.selection=function(e){return arguments.length?(t=e,V):t},V.data=function(t){return arguments.length?(e=t,V):e},V.orient=function(t){return arguments.length?(p=t,V):p},V.spaceX=function(t){return arguments.length?(n=t,V):n},V.spaceY=function(t){return arguments.length?(r=t,V):r},V.overflowQ=function(t){return arguments.length?(m=t,V):m},V.minObjectSize=function(t){return arguments.length?(b=t,V):b},V.maxObjectSize=function(t){return arguments.length?(w=t,V):w},V.circleStrokeWidth=function(t){return arguments.length?(S=t,V):S},V.backgroundFill=function(t){return arguments.length?(j=t,V):j},V.namespace=function(t){return arguments.length?(z=t,V):z},V.objectClass=function(t){return arguments.length?(A=t,V):A},V.transitionDuration=function(t){return arguments.length?(M=t,V):M},V.easeFunc=function(t){return arguments.length?(E=t,V):E},V.cellKeys=function(t){return arguments.length?(o=t,V):o},V.setValues=function(t){return arguments.length?(i=t,V):i},V.intersectionValues=function(t){return arguments.length?(l=t,V):l},V.xObjectSpacer=function(t){return arguments.length?(F=t,V):F},V.yObjectSpacer=function(t){return arguments.length?(Q=t,V):Q},V.radius=function(t){return arguments.length?(u=t,V):u},V.setExtractor=function(t){return arguments.length?(O=t,V):O},V.intersectionExtractor=function(t){return arguments.length?(L=t,V):L},V.elementExtractor=function(t){return arguments.length?(C=t,V):C},V.setKeySortingFunction=function(t){return arguments.length?(B=t,V):B},V.intersectionKeySortingFunction=function(t){return arguments.length?(q=t,V):q},V.yObjectSize=function(t){return arguments.length?(s=t,V):s},V.ySpacerSize=function(t){return arguments.length?(f=t,V):f},V.xObjectSize=function(t){return arguments.length?(h=t,V):h},V.xSpacerSize=function(t){return arguments.length?(g=t,V):g},V.intersectionTotals=function(){var t={};return l.map(function(e,n){t[e]={total:0}}),o.map(function(e,n){var r=C(e,n);0==t[L(e,n)].total&&(Array.isArray(r)?(t[L(e,n)].total+=r.length,t[L(e,n)].values=r):t[L(e,n)].total+=r)}),t},V.setTotals=function(){var t={};return i.map(function(e,n){t[e]={total:0}}),o.map(function(e,n){var r=C(e,n);Array.isArray(r)?t[O(e,n)].total+=r.length:t[O(e,n)].total+=r}),t},V},O.filterTable=function(t){var e,n="d3sm-filter-table",r=function(t,e,n){return n};function o(){var o=c(t,"div","filter-table").classed(a(n,"container"),!0),u=c(o,"div","filter-input-group").classed("input-group",!0),s=(c(c(c(u,"div","input-group-prepend"),"span","input-group-text").classed("search-button",!0),"i","fa fa-search"),c(u,"input","form-control").attr("placeholder","filter").attr("type","text")),d=c(c(u,"div","input-group-append"),"a","close-button").classed("btn btn-outline-secondary",!0),f=(c(d,"i","fa fa-close"),d),h=c(c(o,"div","table-responsive"),"table","table").classed("table-hover",!0).classed("table-bordered",!0).classed("table-striped",!0),g=c(c(h,"thead").classed("thead-dark",!0),"tr"),p=c(h,"tbody"),m=d3.keys(e),v=d3.keys(e[m[0]]);(function(t,e){var n=t.selectAll("th");(n=n.data(e)).exit().remove(),(n=n.merge(n.enter().append("th"))).attr("scope","col").text(function(t,e){return t})})(g,v),l(i(p,m),v),s.on("input",function(t,n){var o,a=s.property("value"),c=new RegExp(a,"gi");""==a?o=m:(o=[],m.map(function(t,n){var a=e[t],i=v.map(function(t,e){return r(a,t,a[t])}).join("").match(c);null==i||""==i.join("")||o.push(t)})),l(i(p,o),v)}),f.on("click",function(t,e){s.property("value","").dispatch("input")})}function i(t,e){var n=t.selectAll("tr");return(n=n.data(e)).exit().remove(),n=n.merge(n.enter().append("tr"))}function l(t,n){return t.each(function(t,o){var a=e[t],i=d3.select(this).selectAll("td");(i=i.data(n)).exit().remove(),(i=i.merge(i.enter().append("td"))).attr("scope",function(t,e){return 0==e}).html(function(t,e){return r(a,t,a[t])})}),t}return o.data=function(t){return arguments.length?(e=t,o):e},o.namespace=function(t){return arguments.length?(n=t,o):n},o.fieldFunction=function(t){return arguments.length?(r=t,o):r},o},O.uniqueElements=e,O.getTranslation=n,O.modifyHexidecimalColorLuminance=r,O.tickRange=u,O.quartiles=o,O.extractViolinValues=function(t,e,n,r,a,i){var l={};return t.map(function(t,c){var u=n(t,c,e),s=d3.histogram()(u),d=s.map(function(t){return t.length}),f=r?{y:d3.min(u),x:0}:{x:d3.min(u),y:0},h=r?{y:d3.max(u),x:0}:{x:d3.max(u),y:0},g=s.map(function(t,e){return r?{y:t.length?d3.median(t):d3.median([t.x0,t.x1]),x:d[e]}:{x:t.length?d3.median(t):d3.median([t.x0,t.x1]),y:d[e]}}),p=o(u,i),m={values:u,binned:s,frequencies:d,points:[f].concat(g).concat([h])};m[a]=p,l[t]=m}),l},O.hypenate=a,O.round=i,O.getContainingSVG=l,O.interpolateColors=function(){return d3.interpolateRgbBasis(arguments)},O.truncateText=function(t,e,n,r,o,a){var i=t.node().getBoundingClientRect();for(t.text(e);Math.max(i.width,i.height)>o-r&&(e=(e=String(e)).slice(0,e.length-1),t.text(e+"..."),i=t.node().getBoundingClientRect(),0!=e.length););},O.safeSelect=c,O.whichBin=h,O.unique=d,O.flatten=f,O.setupStandardChartContainers=function(t,e,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{top:.01,bottom:.01,left:.01,right:.01},o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{w:.8,h:.6},a=arguments.length>5&&void 0!==arguments[5]?arguments[5]:{y:.1,x:.1},i=arguments.length>6&&void 0!==arguments[6]?arguments[6]:{x:0,margin:0,pos:"left"};void 0==n&&(n={w:window.innerWidth,h:window.Height});var l={w:n.w*o.w,h:n.h*o.h},c={top:r.top*l.h,bottom:r.bottom*l.h,left:r.left*l.w,right:r.right*l.w},u=l.w-c.left-c.right,s=l.h-c.top-c.bottom,d={x:s*a.x,y:u*a.y},f={x:u-d.y-i.x-2*i.margin,y:s-d.x},h={x:i.margin+c.left+("left"==i.pos?0:f.x+d.y),y:c.top,w:i.x,h:f.y},g={x:d.y+c.left+("left"==i.pos?i.x+2*i.margin:0),y:c.top,w:d.y,h:f.y},p={x:d.y+c.left+("left"==i.pos?i.x+2*i.margin:0),y:c.top,w:f.x,h:f.y},m={x:d.y+c.left+("left"==i.pos?i.x+2*i.margin:0),y:c.top+f.y,w:f.x,h:d.x};return n=d3sm.safeSelect(t,"svg",e).style("width",l.w+"px").style("height",l.h+"px"),a=d3sm.safeSelect(n,"g",d3sm.hypenate(e,"axes")),i=d3sm.safeSelect(n,"g",d3sm.hypenate(e,"legend")).attr("transform","translate("+h.x+","+h.y+")"),{svg:{selection:n,rect:l},plot:{selection:d3sm.safeSelect(n,"g",d3sm.hypenate(e,"plot")).attr("transform","translate("+p.x+","+p.y+")"),rect:p},xAxis:{selection:d3sm.safeSelect(a,"g",d3sm.hypenate(e,"x-axis")).attr("transform","translate("+m.x+","+m.y+")"),rect:m},yAxis:{selection:d3sm.safeSelect(a,"g",d3sm.hypenate(e,"y-axis")).attr("transform","translate("+g.x+","+g.y+")"),rect:g},legend:{selection:i,rect:h}}},O.log=m,O.warn=function(t,e,n){!0===window.d3sm.debugQ&&console.warn("%c[d3sm::"+t+"]:\t"+e,["background: #ffd53e","border-radius: 5000px","padding: 0px 2px","font-size: 14px"].join(";")),console.table(n)},O.info=function(t,e,n){window.d3sm.debugQ&&console.info("%c[d3sm::"+t+"]:\t"+e,["background: #009ccd","border-radius: 5000px","padding: 0px 2px","font-size: 14px"].join(";")),console.table(n)},O.error=function(t,e,n){window.d3sm.debugQ&&console.error("[d3sm::"+t+"]:\t"+e+"\t%o",n)},O.consoleGroup=g,O.consoleGroupEnd=p,O.resizeDebounce=function(t,e){var n=function(t,e,n){var r;return function(){var o=this,a=arguments,i=n&&!r;clearTimeout(r),r=setTimeout(function(){r=null,n||t.apply(o,a)},e),i&&t.apply(o,a)}}(function(){t()},e);window.addEventListener("resize",n)},O.debugQ=!1,window.d3sm=O,t.default=O,t}({}); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZDNzbS5taW4udjAuMC40LmpzIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL2hlbHBlcnMuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL2FycmF5LWZ1bmN0aW9ucy5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvdXRpbHMuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL2dyb3VwaW5nLXNwYWNlci5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvY29sb3ItZnVuY3Rpb24uanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL3Rvb2x0aXAuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL3NlbGVjdC1maWx0ZXIuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL2xhc3NvLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9kMy1wcm90b3R5cGVzLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbWFpbi5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvYXhpcy5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvYmFyLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9idWJibGUtaGVhdG1hcC5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvaGVhdG1hcC5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvYm94LXdoaXNrZXIuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL2RhdGEtdG9nZ2xlLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9zY2F0dGVyLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9wbG90LXpvb20uanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL211bHRpLXBsb3Qtem9vbS5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvdmlvbGluLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9udW1lcmljLWxlZ2VuZC5qcyIsIi4uLy4uL3NyYy9zY3JpcHRzL21vZHVsZXMvY2F0ZWdvcmljYWwtbGVnZW5kLmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9sYXNzby13aWRnZXQuanMiLCIuLi8uLi9zcmMvc2NyaXB0cy9tb2R1bGVzL3Vwc2V0LmpzIiwiLi4vLi4vc3JjL3NjcmlwdHMvbW9kdWxlcy9maWx0ZXItdGFibGUuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gaW1wb3J0IHtoYXNRfSBmcm9tICcuL2FycmF5LWZ1bmN0aW9ucyc7XG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSEVMUEVSUyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqXG4qIEhlbHBlciBmdW5jdGlvbiBmb3IgQXJyYXkuZmlsdGVyIHRvIGdldCB1bmlxdWUgZWxlbWVudHMgb2YgdGhlIGFycmF5XG4qIEBwYXJhbSB7Kn0gdmFsdWUgY3VycmVudCB2YWx1ZSBhcyBtYXBwaW5nIG92ZXIgYXJyYXkgKHNlbGYpXG4qIEBwYXJhbSB7bnVtYmVyfSBpbmRleCBjdXJyZW50IGluZGV4IGluIHRoZSBhcnJheVxuKiBAcGFyYW0ge0FycmF5fSBzZWxmIHBhc3NlZCBhcnJheSBmcm9tIEFycmF5LmZpbHRlciBtZXRob2RcbiogQHJldHVybnMge2Jvb2xlYW59IHdoZXRoZXIgb3Igbm90IHZhbHVlIGlzIHRoZSBmaXJzdCBvZiBpdHMga2luZCAoaS5lLiBpbmRleE9mKHZhbHVlKSA9PSBpbmRleClcbiovXG5leHBvcnQgZnVuY3Rpb24gdW5pcXVlRWxlbWVudHModmFsdWUsIGluZGV4LCBzZWxmKSB7IHJldHVybiBzZWxmLmluZGV4T2YodmFsdWUpID09PSBpbmRleDsgfVxuXG4vKipcbiogRXh0cmFjdHMgeCBhbmQgeSBvZiB0cmFuc2xhdGUgZnJvbSB0cmFuc2Zvcm0gcHJvcGVydHlcbiogQHBhcmFtIHtzdHJpbmd9IHRyYW5zZm9ybSB0cmFuc2Zvcm0gcHJvcGVydHkgb2Ygc3ZnIGVsZW1lbnRcbiogQHJldHVybnMge251bWJlcltdfSB4LCB5IG9mIHRyYW5zbGF0ZSh4LCB5KVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRUcmFuc2xhdGlvbih0cmFuc2Zvcm0pIHtcbiAgLy8gQ3JlYXRlIGEgZHVtbXkgZyBmb3IgY2FsY3VsYXRpb24gcHVycG9zZXMgb25seS4gVGhpcyB3aWxsIG5ldmVyXG4gIC8vIGJlIGFwcGVuZGVkIHRvIHRoZSBET00gYW5kIHdpbGwgYmUgZGlzY2FyZGVkIG9uY2UgdGhpcyBmdW5jdGlvblxuICAvLyByZXR1cm5zLlxuICB2YXIgZyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnROUygnaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnLCAnZycpO1xuICAvLyBTZXQgdGhlIHRyYW5zZm9ybSBhdHRyaWJ1dGUgdG8gdGhlIHByb3ZpZGVkIHN0cmluZyB2YWx1ZS5cbiAgdHJhbnNmb3JtID0gdHJhbnNmb3JtID09IHVuZGVmaW5lZCA/ICd0cmFuc2xhdGUoMCwwKScgOiB0cmFuc2Zvcm07XG4gIGcuc2V0QXR0cmlidXRlTlMobnVsbCwgJ3RyYW5zZm9ybScsIHRyYW5zZm9ybSk7XG4gIC8vIGNvbnNvbGlkYXRlIHRoZSBTVkdUcmFuc2Zvcm1MaXN0IGNvbnRhaW5pbmcgYWxsIHRyYW5zZm9ybWF0aW9uc1xuICAvLyB0byBhIHNpbmdsZSBTVkdUcmFuc2Zvcm0gb2YgdHlwZSBTVkdfVFJBTlNGT1JNX01BVFJJWCBhbmQgZ2V0XG4gIC8vIGl0cyBTVkdNYXRyaXguXG4gIHZhciBtYXRyaXggPSBnLnRyYW5zZm9ybS5iYXNlVmFsLmNvbnNvbGlkYXRlKCkubWF0cml4O1xuICAvLyBBcyBwZXIgZGVmaW5pdGlvbiB2YWx1ZXMgZSBhbmQgZiBhcmUgdGhlIG9uZXMgZm9yIHRoZSB0cmFuc2xhdGlvbi5cbiAgcmV0dXJuIFttYXRyaXguZSwgbWF0cml4LmZdO1xufVxuXG5cbi8qKlxuKiBNb2RpZmllcyBsdW1pbmFuY2Ugb2YgaGV4aWRlY2ltYWwgbnVtYmVyXG4qIEBwYXJhbSB7c3RyaW5nfSBoZXggc2hvdWxkIGJlIGhleGlkZWNpbWFsIHZhbHVlIHdpdGggb3Igd2l0aG91dCB0aGUgcHJvY2VlZGluZyBvY3RvdHJvcGVcbiogQHBhcmFtIHtudW1iZXJ9IGx1bSB2YWx1ZSB0byBpbmNyZWFzZSBvciBkZWNyZWFzZSBsdW1pbm9zaXR5IGJ5XG4qIEByZXR1cm5zIHtzdHJpbmd9IHVwZGF0ZWQgaGV4aWRlY2ltYWwgdmFsdWUgd2l0aG91dCB0aGUgcHJvY2VlZGluZyBvY3RvdHJvcGVcbiovXG5leHBvcnQgZnVuY3Rpb24gbW9kaWZ5SGV4aWRlY2ltYWxDb2xvckx1bWluYW5jZShoZXgsIGx1bSkge1xuICAvLyB2YWxpZGF0ZSBoZXggc3RyaW5nXG4gIHZhciBoZXggPSBTdHJpbmcoaGV4KS5yZXBsYWNlKC9bXjAtOWEtZl0vZ2ksICcnKTtcblxuICBpZiAoaGV4Lmxlbmd0aCA8IDYpIHtcbiAgICBoZXggPSBoZXhbMF0raGV4WzBdK2hleFsxXStoZXhbMV0raGV4WzJdK2hleFsyXTtcblx0fVxuXHRsdW0gPSBsdW0gfHwgMDtcblxuXHQvLyBjb252ZXJ0IHRvIGRlY2ltYWwgYW5kIGNoYW5nZSBsdW1pbm9zaXR5XG5cdHZhciByZ2IgPSAnIycsIGMsIGk7XG5cdGZvciAoaSA9IDA7IGkgPCAzOyBpKyspIHtcblx0XHRjID0gcGFyc2VJbnQoaGV4LnN1YnN0cihpKjIsMiksIDE2KTtcblx0XHRjID0gTWF0aC5yb3VuZChNYXRoLm1pbihNYXRoLm1heCgwLCBjICsgKGMgKiBsdW0pKSwgMjU1KSkudG9TdHJpbmcoMTYpO1xuXHRcdHJnYiArPSAoJzAwJytjKS5zdWJzdHIoYy5sZW5ndGgpO1xuXHR9XG5cblx0cmV0dXJuIHJnYjtcbn1cblxuXG4vKipcbiogQGRlcHJlY2F0ZWQgQHNlZXtAbGluayB0aWNrUmFuZ2V9XG4qIEBwYXJhbSB7bnVtYmVyfSBtaW5cbiogQHBhcmFtIHtudW1iZXJ9IG1heFxuKiBAcGFyYW0ge251bWJlcn0gcGFydHNcbiogQHJldHVybnMge251bWJlcltdfSBhcnJheSBvZiBsZW5ndGggcGFydHMgZXZlbmx5IHBhcnRpdGlvbmVkIGJldHdlZW4gbWluIGFuZCBtYXhcbiovXG5leHBvcnQgZnVuY3Rpb24gcGFydGl0aW9uUmFuZ2VJbnRvKG1pbiwgbWF4LCBwYXJ0cykge1xuICB2YXIgZGlmZiA9IG1heCAtIG1pblxuICByZXR1cm4gQXJyYXkocGFydHMpLm1hcChmdW5jdGlvbiAoZSwgaSkgeyByZXR1cm4gbWluICsgZGlmZiAvIHBhcnRzICogaSB9KVxufVxuXG5cbi8qKlxuKiBDYWxjdWxhdGVkIHRoZSBxdWFydGlsZXMgb2YgdGhlIHBhc3NlZCBkYXRhIGFuZCBzdG9yZXMgdGhlbSB3aXRoIHFLZXlzXG4qIEBwYXJhbSB7bnVtYmVyW119IGRhdGEgbGlzdCBvZiBudW1lcmljYWwgdmFsdWVzXG4qIEBwYXJhbSB7c3RyaW5nW119IFtxS2V5cz1bJ3EwJywgJ3ExJywgJ3EyJywgJ3EzJywgJ3E0J11dIGhvdyByZXR1cm5lZCBvYmplY3Qgd2l0aCBxdWFydGlsZXMgc2hvdWxkIGJlIHN0b3JlZFxuKiBAcmV0dXJucyB7T2JqZWN0fSB3aXRoIGtleXMgcUtleXMgZ2l2aW5nIG9ubHkgdGhlIG51bWVyaWNhbCB2YWx1ZXMgZm9yIHRoZSBxdWFydGlsZXNcbiovXG5leHBvcnQgZnVuY3Rpb24gcXVhcnRpbGVzKGRhdGEsIHFLZXlzKSB7XG4gIHZhclxuICBxMiA9IGQzLm1lZGlhbihkYXRhKSxcbiAgbG93ZXIgPSBkYXRhLmZpbHRlcih4ID0+IHggPCBxMiksXG4gIHVwcGVyID0gZGF0YS5maWx0ZXIoeCA9PiB4ID4gcTIpLFxuXG4gIHExID0gZDMubWVkaWFuKGxvd2VyKSxcbiAgcTEgPSBxMSA9PSB1bmRlZmluZWQgPyBxMiA6IHExLFxuXG4gIHEwID0gZDMubWluKGxvd2VyKSxcbiAgcTAgPSBxMCA9PSB1bmRlZmluZWQgPyBxMSA6IHEwLFxuXG4gIHEzID0gZDMubWVkaWFuKHVwcGVyKSxcbiAgcTMgPSBxMyA9PSB1bmRlZmluZWQgPyBxMiA6IHEzLFxuXG4gIHE0ID0gZDMubWF4KHVwcGVyKSxcbiAgcTQgPSBxNCA9PSB1bmRlZmluZWQgPyBxMyA6IHE0LFxuXG4gIGswID0gJ3EwJywgazEgPSAncTEnLCBrMiA9ICdxMicsIGszID0gJ3EzJywgazQgPSAncTQnLFxuICBvYmogPSB7fVxuICBpZiAocUtleXMhPXVuZGVmaW5lZCAmJiBxS2V5cy5sZW5ndGggPT0gNSkgeyBrMCA9IHFLZXlzWzBdOyBrMSA9IHFLZXlzWzFdOyBrMiA9IHFLZXlzWzJdOyBrMyA9IHFLZXlzWzNdOyBrNCA9IHFLZXlzWzRdOyB9XG4gIG9ialtrMF0gPSBxMDsgb2JqW2sxXSA9IHExOyBvYmpbazJdID0gcTI7IG9ialtrM10gPSBxMzsgb2JqW2s0XSA9IHE0O1xuXG4gIHJldHVybiBvYmpcbn1cblxuXG4vKipcbiogSGVscGVyIGZ1bmN0aW9uIHRvIGdldCBhbGwgdmFsdWVzIG5lZWRlZCBpbiBtYWtpbmcgdmlvbGluIHBsb3RzXG4qIEBwYXJhbSB7c3RyaW5nW119IHZpb2xpbktleXNcbiogQHBhcmFtIHtudW1iZXJbXX0gZGF0YVxuKiBAcGFyYW0ge0Z1bmN0aW9ufSB2YWx1ZUV4dHJhY3RvckZ1bmN0aW9uIGhvdyB0byBnZXQgdmFsdWVzIGZyb20gZGF0YVt2aW9saW5LZXlzW2ldXVxuKiBAcGFyYW0ge2Jvb2xlYW59IGhvcml6b250YWxRIHdoZXRoZXIgb3Igbm90IHZpb2xpbnMgd2lsbCBiZSByZW5kZXJlZCBob3Jpem9udGFsbHkgb3IgdmVydGljYWxseVxuKiBAcGFyYW0ge3N0cmluZ30gcUtleSBob3cgdGhlIG9iamVjdCBjb250YWluaW5nIHRoZSBxdWFydGlsZXMgc2hvdWxkIGJlIGxhYmVsZWQgYXNcbiogQHBhcmFtIHtzdHJpbmdbXX0gcUtleXMgaG93IGVhY2ggcXVhcnRpbGUgc2hvdWxkIGJlIGxhYmVsZWQgYXNcbiogQHJldHVybnMge09iamVjdH0gcmVxdWlyZWQgZm9yIEBzZWV7QGxpbmsgdmlvbGlufSBjb250YWluaW5nIGtleXMgdmFsdWVzLCBiaW5ubmVkLCBmcmVxdWVuY2llcywgcG9pbnRzLCBhbmQgcXVhcnRpbGVzXG4qIEBzZWV7QGxpbmsgcXVhcnRpbGVzfVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0VmlvbGluVmFsdWVzKFxuICB2aW9saW5LZXlzLFxuICBkYXRhLFxuICB2YWx1ZUV4dHJhY3RvckZ1bmN0aW9uLFxuICBob3Jpem9udGFsUSxcbiAgcUtleSxcbiAgcUtleXNcbil7XG4gIHZhciBvYmogPSB7fVxuICB2aW9saW5LZXlzLm1hcChmdW5jdGlvbihrLCBpKXtcbiAgICAgdmFyIGQgPSB2YWx1ZUV4dHJhY3RvckZ1bmN0aW9uKGssIGksIGRhdGEpLFxuICAgICBiaW5uZWQgPSBkMy5oaXN0b2dyYW0oKShkKSxcbiAgICAgZnJlcXVlbmNpZXMgPSBiaW5uZWQubWFwKHg9PngubGVuZ3RoKSxcbiAgICAgbWluUG9pbnQgPSBob3Jpem9udGFsUSA/IHt5OiBkMy5taW4oZCksIHg6IDB9IDoge3g6IGQzLm1pbihkKSwgeTogMH0sXG4gICAgIG1heFBvaW50ID0gaG9yaXpvbnRhbFEgPyB7eTogZDMubWF4KGQpLCB4OiAwfSA6IHt4OiBkMy5tYXgoZCksIHk6IDB9LFxuICAgICBwb2ludHMgPSBiaW5uZWQubWFwKGZ1bmN0aW9uKGJpbiwgaSkge1xuICAgICAgIHJldHVybiBob3Jpem9udGFsUVxuICAgICAgID8ge3k6IChiaW4ubGVuZ3RoKSA/IGQzLm1lZGlhbihiaW4pOiBkMy5tZWRpYW4oW2Jpbi54MCwgYmluLngxXSksIHg6IGZyZXF1ZW5jaWVzW2ldfVxuICAgICAgIDoge3g6IChiaW4ubGVuZ3RoKSA/IGQzLm1lZGlhbihiaW4pOiBkMy5tZWRpYW4oW2Jpbi54MCwgYmluLngxXSksIHk6IGZyZXF1ZW5jaWVzW2ldfVxuICAgICB9KSxcbiAgICAgcXVhcnRzID0gcXVhcnRpbGVzKGQsIHFLZXlzKSxcbiAgICAgbyA9IHtcbiAgICAgICB2YWx1ZXM6IGQsXG4gICAgICAgYmlubmVkOiBiaW5uZWQsXG4gICAgICAgZnJlcXVlbmNpZXM6IGZyZXF1ZW5jaWVzLFxuICAgICAgIHBvaW50czogW21pblBvaW50XS5jb25jYXQocG9pbnRzKS5jb25jYXQoW21heFBvaW50XSlcbiAgICAgfVxuICAgICBvW3FLZXldID0gcXVhcnRzO1xuICAgICBvYmpba10gPSBvO1xuICAgfSk7XG4gICByZXR1cm4gb2JqO1xufVxuXG4vKipcbiogSHlwZW5hdGVzIGFsbCBzdHJpbmdzIHRvZ2V0aGVyXG4qIEBwYXJhbSB7c3RyaW5nW119IGFyZ3VtZW50c1xuKiBAcmV0dXJucyB7c3RyaW5nfSBcImFyZzEtYXJnMi0uLi4tYXJnblwiXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIGh5cGVuYXRlKCl7IHJldHVybiBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbChhcmd1bWVudHMpLmpvaW4oJy0nKSB9XG5cblxuLyoqXG4qIFJvdW5kcyBkZWNpbWFscyBvZiBudW1iZXIgdG8gcHJlY2lzaW9uXG4qIEBwYXJhbSB7bnVtYmVyfSBudW1iZXJcbiogQHBhcmFtIHtudW1iZXJ9IHByZWNpc2lvblxuKiBAcmV0dXJucyB7bnVtYmVyfSByb3VuZGVkIHRvIHByZWNpc2lvblxuKi9cbmV4cG9ydCBmdW5jdGlvbiByb3VuZChudW1iZXIsIHByZWNpc2lvbikge1xuICB2YXIgc2hpZnQgPSBmdW5jdGlvbiAobnVtYmVyLCBwcmVjaXNpb24sIHJldmVyc2VTaGlmdCkge1xuICAgIGlmIChyZXZlcnNlU2hpZnQpIHtcbiAgICAgIHByZWNpc2lvbiA9IC1wcmVjaXNpb247XG4gICAgfVxuICAgIHZhciBudW1BcnJheSA9ICgnJyArIG51bWJlcikuc3BsaXQoJ2UnKTtcbiAgICByZXR1cm4gKyhudW1BcnJheVswXSArICdlJyArIChudW1BcnJheVsxXSA/ICgrbnVtQXJyYXlbMV0gKyBwcmVjaXNpb24pIDogcHJlY2lzaW9uKSk7XG4gIH07XG4gIHJldHVybiBzaGlmdChNYXRoLnJvdW5kKHNoaWZ0KG51bWJlciwgcHJlY2lzaW9uLCBmYWxzZSkpLCBwcmVjaXNpb24sIHRydWUpO1xufVxuXG4vKipcbiogcmVjdXJzaXZlbHkgYXNjZW5kcyBlbGVtZW50LnBhcmVudEVsZW1lbnQgdG8gZmluZCBhIHN2ZyB0YWdcbiogQHBhcmFtIHtFbGVtZW50fSBlbGVtZW50XG4qIEByZXR1cm5zIHtFbGVtZW50IHwgdW5kZWZpbmVkfVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRDb250YWluaW5nU1ZHKGVsZW1lbnQpIHtcbiAgdmFyIHBhcmVudCA9IGVsZW1lbnQucGFyZW50RWxlbWVudFxuICB2YXIgdGFnID0gcGFyZW50LnRhZ05hbWUudG9Mb3dlckNhc2UoKVxuICBpZiAodGFnID09PSAnc3ZnJykgeyByZXR1cm4gcGFyZW50OyB9XG4gIGlmICh0YWcgPT09ICdodG1sJykgeyByZXR1cm4gdW5kZWZpbmVkOyB9XG4gIHJldHVybiBnZXRDb250YWluaW5nU1ZHKHBhcmVudCk7XG59XG5cbi8qKlxuKiBNYXBzIGFyZ3VtZW50cyBpbiB0byBkMy5pbnRlcnBvbGF0ZVJnYkJhc2lzXG4qIEBwYXJhbSBhcmd1bWVudHNcbiogQHJldHVybnMge0Z1bmN0aW9ufVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBpbnRlcnBvbGF0ZUNvbG9ycygpe3JldHVybiBkMy5pbnRlcnBvbGF0ZVJnYkJhc2lzKGFyZ3VtZW50cyl9XG5cblxuLyoqXG4qIFRyeXMgdG8gcmVkdWNlIHRleHQgdG8gZml0IGluIHNwZWNpZmllZCBhcmVhLCBtYWRlIGZvciB0aWNrIGxhYmVscyBhcyBjYWxsZWQgYnlcbiogQHNlZXtAbGluayBheGlzfVxuKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gdCBjb250YWluZXIgZm9yIHNwZWNpZmljIGF4aXMgdGlja1xuKiBAcGFyYW0ge3N0cmluZ30gdGV4dCB0byBiZSB0aGUgbGFiZWwgb2YgdGhlIHBhc3NlZCBheGlzIHRpY2tcbiogQHBhcmFtIHtib29sZWFufSBvcmllbnQgb2YgdGhlIGF4aXMsIHRydWUgaXMgaG9yaXpvbnRhbCwgZmFsc2UgaXMgdmVydGljYWxcbiogQHBhcmFtIHtudW1iZXJ9IHRpY2tMZW5ndGggaXMgdGhlIGxlbmd0aCBvZiB0aGUgdGV4dFxuKiBAcGFyYW0ge251bWJlcn0gc3BhY2UgaXMgdGhlIGFtb3VudCBvZiBhdmFpbGJsZSBzcGFjZSBmb3IgdGhlIHRleHQgYW5kIHRoZSB0aWNrIHRvIGZpdCBpblxuKiBAcGFyYW0ge2Jvb2xlYW59IG92ZXJmbG93USB3aGV0aGVyIG9yIG5vdCBhbGxvd2VkIHRvIGdvIG92ZXIgdGhlIGFsbG90ZWQgc3BhY2VcbiogQHJldHVybnMge25vbmV9XG4qL1xuZXhwb3J0IGZ1bmN0aW9uIHRydW5jYXRlVGV4dCh0LCB0ZXh0LCBvcmllbnQsIHRpY2tMZW5ndGgsIHNwYWNlLCBvdmVyZmxvd1EpIHtcbiAgdmFyIHJlY3QgPSB0Lm5vZGUoKS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuICB0LnRleHQodGV4dClcbiAgd2hpbGUgKE1hdGgubWF4KHJlY3Qud2lkdGgsIHJlY3QuaGVpZ2h0KSA+IHNwYWNlIC0gdGlja0xlbmd0aCkge1xuICAgIHRleHQgPSBTdHJpbmcodGV4dClcbiAgICB0ZXh0ID0gdGV4dC5zbGljZSgwLCB0ZXh0Lmxlbmd0aCAtIDEpXG4gICAgdC50ZXh0KHRleHQgKyAnLi4uJylcbiAgICByZWN0ID0gdC5ub2RlKCkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICBpZiAodGV4dC5sZW5ndGggPT0gMCkgYnJlYWtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gdHJ1bmNhdGVTdHJpbmcoc3RyaW5nLCBzcGFjZSwgZm9udCkge1xuICB2YXIgY2hhcnMgPSBzcGFjZSAvIGZvbnQ7XG4gIGlmIChjaGFycyA8IHN0cmluZy5sZW5ndGgpIHtcbiAgICByZXR1cm4gc3RyaW5nLnNsaWNlKDAsIE1hdGgucm91bmQoY2hhcnMtNSkpICsgJy4uLidcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gc3RyaW5nXG4gIH1cbn1cblxuXG4vKipcbiogVHJ5cyB0byB1c2UgZDMuc2VsZWN0aW9uIHRvIGdldCBlbGVtZW50LCBpZiBpdCBkb2VzbnQgZXhpc3QsIG1ha2VzIG9uZVxuKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gc2VsIHNlbGVjdGlvbiBpbiB3aGljaCB0byB0cnkgYW5kIGZpbmQgb2JqZWN0XG4qIEBwYXJhbSB7c3RyaW5nfSB0YWcgdGFnIG9mIHdoaWNoIHRvIHRyeSBhbmQgc2VsZWN0XG4qIEBwYXJhbSB7c3RyaW5nfSBbY2xzPScnXSBjbGFzcyBvZiB0YWcgdG8gdHJ5IGFuZCBncmFiXG4qIEByZXR1cm5zIHtkMy5zZWxlY3Rpb259IG9mIGVpdGhlciBhcHBlbmQgb3Igc2VsZWN0ZWQgdGFnLmNscyB3aXRoaW4gc2VsXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIHNhZmVTZWxlY3Qoc2VsLCB0YWcsIGNscykge1xuICB2YXIgY2xzU3RyID0gY2xzID09IHVuZGVmaW5lZCA/ICcnIDogJy4nK2NscztcbiAgdmFyIHNTZWwgPSBzZWwuc2VsZWN0KHRhZytjbHNTdHIpLmVtcHR5KClcbiAgPyBzZWwuYXBwZW5kKHRhZylcbiAgOiBzZWwuc2VsZWN0KHRhZytjbHNTdHIpXG4gIHJldHVybiBzU2VsXG4gIC5jbGFzc2VkKGNsc1N0ci5yZXBsYWNlKCcuJywgJycpLCB0cnVlKVxuICAuYXR0cigndHJhbnNmb3JtJywgc1NlbC5hdHRyKCd0cmFuc2Zvcm0nKSA9PSB1bmRlZmluZWQgPyAndHJhbnNsYXRlKDAsMCknIDogc1NlbC5hdHRyKCd0cmFuc2Zvcm0nKSlcbn1cblxuLyoqXG4qIGV2ZW5seSBwYXJ0aXRpb25zIHRoZSByYW5nZSBbbWluLCBtYXhdIGludG8gbiBwYXJ0c1xuKiBAcGFyYW0ge251bWJlcn0gbWluXG4qIEBwYXJhbSB7bnVtYmVyfSBtYXhcbiogQHBhcmFtIHtudW1iZXJ9IG5cbiogQHJldHVybnMge251bWJlcltdfSBhcnJheSBvZiBsZW5ndGggbiBldmVubHkgcGFydGl0aW9uZWQgYmV0d2VlbiBtaW4gYW5kIG1heFxuKi9cbmV4cG9ydCBmdW5jdGlvbiB0aWNrUmFuZ2UobWluLCBtYXgsIG4pIHtcbiAgdmFyIGEgPSBbbWluXVxuICB2YXIgZCA9IG1heC1taW5cbiAgdmFyIHMgPSBkIC8gKG4tMSlcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBuLTI7IGkrKykgeyBhLnB1c2gobWluICsgcyAqIChpKzEpKSB9XG4gIGEucHVzaChtYXgpXG4gIHJldHVybiBhXG59XG5cblxuZXhwb3J0IGZ1bmN0aW9uIGV1Y2xpZGVhbkRpc3RhbmNlKHAxLCBwMil7XG4gIHZhciBhID0gIHAxWzBdIC0gcDJbMF0sIGIgPSAgcDFbMV0gLSBwMlsxXVxuICByZXR1cm4gTWF0aC5zcXJ0KGEqYSArIGIqYilcbn1cbiIsImltcG9ydCB7dW5pcXVlRWxlbWVudHN9IGZyb20gJy4vaGVscGVycyc7XG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBST1RPVFlQRVMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqXG4qIFRoaXMgZnVuY3Rpb24gdGVzdHMgdG8gc2VlIGlmIGFsbCBlbGVtZW50cyBvZiB0aGUgcGFzc2VkIGFycmF5IGFyZSB0cnVlLlxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiB2YWx1ZXNcbiogQHBhcmFtIHtGdW5jdGlvbn0gW2Z1bmMgZnVuY3Rpb24odmFsdWUpe3JldHVybiB2YWx1ZSA9PSB0cnVlO31dIGlzIGFwcGxpZWQgdG8gZWFjaCB2YWx1ZSBvZiB0aGUgYXJyYXkgYW5kIHNob3VsZCByZXR1cm4gYSBib29sZWFuLlxuKiBAcmV0dXJucyB7Ym9vbGVhbn0gaWYgYWxsIHZhbHVlcyBhcmUgdHJ1ZSBieSBmdW5jdGlvblxuKi9cbmV4cG9ydCBmdW5jdGlvbiBhbGwoIGFycmF5LCBmdW5jICkge1xuICBpZiAoZnVuYyA9PSB1bmRlZmluZWQpIHsgcmV0dXJuIGFycmF5LmV2ZXJ5KCBmdW5jdGlvbih2YWx1ZSkgeyByZXR1cm4gdmFsdWUgPT09IHRydWU7IH0pOyB9XG4gIHJldHVybiBhcnJheS5ldmVyeSggZnVuY3Rpb24odmFsdWUpIHsgcmV0dXJuIGZ1bmModmFsdWUpOyB9ICk7XG59XG5cbi8qKlxuKiBDb3VudHMgdGhlIG51bWJlciBvZiBvY2N1cmFuY2VzIG9mIGVhY2ggZWxlbWVudCBpbiB0aGUgZ2l2ZW4gYXJyYXkuXG4qIEBwYXJhbSB7QXJyYXl9IGFycmF5IG9mIGVsZW1lbnRzXG4qIEByZXR1cm5zIHtPYmplY3R9IG9mIGtleTogdmFsdWUgcGFpcnMgd2hlcmUga2V5IGlzIGFuIGVsZW1lbnQgaW4gdGhlIGFycmF5IGFuZCB2YWx1ZSBpcyB0aGUgbnVtYmVyIG9mIHRpbWVzIGl0IG9jY3Vycy5cbiovXG5leHBvcnQgZnVuY3Rpb24gdGFsbHkoIGFycmF5ICkge1xuICB2YXIgdGFsbGllcyA9IHt9O1xuICBhcnJheS5tYXAoIGZ1bmN0aW9uICggZWxlbWVudCApIHtcbiAgICBpZiAoIGhhc1EoT2JqZWN0LmtleXModGFsbGllcyksIGVsZW1lbnQpICkgeyB0YWxsaWVzW2VsZW1lbnRdID0gMTsgfVxuICAgIGVsc2UgeyB0YWxsaWVzW2VsZW1lbnRdICs9IDE7IH1cbiAgfSk7XG4gIHJldHVybiB0YWxsaWVzO1xufVxuLyoqXG4qIFNob3J0LWhhbmQgZm9yIGFycmF5LmluY2x1ZGVzKGl0ZW0pO1xuKiBAcGFyYW0ge0FycmF5fSBhcnJheVxuKiBAcGFyYW0geyp9IGl0ZW0gdG8gdGVzdCBpZiBjb250YWluZWQgaW4gIHthcnJheX1cbiogQHJldHVybnMge2Jvb2xlYW59XG4qL1xuZXhwb3J0IGZ1bmN0aW9uIGhhc1EoIGFycmF5LCBpdGVtICkgeyByZXR1cm4gYXJyYXkuaW5jbHVkZXMoaXRlbSk7IH1cblxuLyoqXG4qIFJldHVybnMgZmlyc3QgaXRlbSBpbiBhcnJheVxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtc1xuKiBAcmV0dXJucyB7Kn0gYXJyYXlbMF1cbiovXG5leHBvcnQgZnVuY3Rpb24gZmlyc3QoIGFycmF5ICkgeyByZXR1cm4gYXJyYXlbMF07IH1cblxuLyoqXG4qIFJldHVybnMgbGFzdCBpdGVtIGluIGFycmF5XG4qIEBwYXJhbSB7QXJyYXl9IGFycmF5IG9mIGl0ZW1zXG4qIEByZXR1cm5zIHsqfSBhcnJheVthcnJheS5sZW5ndGgtMV1cbiovXG5leHBvcnQgZnVuY3Rpb24gbGFzdCggYXJyYXkgKSB7IHJldHVybiBhcnJheVthcnJheS5sZW5ndGgtMV07IH1cblxuLyoqXG4qIENhbGN1bGF0ZXMgdGhlIHRvdGFsIHZhbHVlIG9mIG51bWJlcnMgaW4gcGFzc2VkIGFycmF5XG4qIEBwYXJhbSB7bnVtYmVyW119IGFycmF5IG9mIG51bWVyaWNhbCB2YWx1ZXNcbiogQHJldHVybnMge251bWJlcn0gc3VtIG92ZXIgZWxlbWVudHMgaW4gYXJyYXlcbiovXG5leHBvcnQgZnVuY3Rpb24gdG90YWwoIGFycmF5ICkgeyByZXR1cm4gYXJyYXkucmVkdWNlKChhLCBiKSA9PiBhICsgYiwgMCkgfTtcblxuLyoqXG4qIFJlbW92ZXMgZHVwbGljYXRlcyBpbiBhcnJheVxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtc1xuKiBAcmV0dXJucyB7QXJyYXl9IG9mIGl0ZW1zIHN1Y2ggdGhhdCBpdGVtX2kgIT0gaXRlbV9qIGZvciBhbGwgaSA8IGpcbiogQHNlZXtAbGluayB1bmlxdWVFbGVtZW50c30gZm9yIHRoZSBmaWx0ZXJpbmcgZnVuY3Rpb25cbiovXG5leHBvcnQgZnVuY3Rpb24gdW5pcXVlKCBhcnJheSApIHsgcmV0dXJuIGFycmF5LmZpbHRlciggdW5pcXVlRWxlbWVudHMgKTsgfVxuXG4vKipcbiogRmlsdGVycyBwYXNzZWQgYXJyYXkgZm9yIHNwZWNpZmllZCBpbmRpY2llc1xuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtc1xuKiBAcGFyYW0ge251bWJlcltdfSBwb3NpdGlvbnMgb2YgaW50ZWdlcnMgc3VjaCB0aGF0IGkgPCBhcnJheS5sZW5ndGhcbiogQHJldHVybnMge0FycmF5fSBvZiBpdGVtcyBzdWNoIHRoYXQgZm9yIGFueSBpdGVtX2ksIHBvc2l0aW9ucy5pbmNsdWRlcyhpKSA9PT0gdHJ1ZVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXQoIGFycmF5LCBwb3NpdGlvbnMgKSB7XG4gIHJldHVybiBhcnJheS5maWx0ZXIoIGZ1bmN0aW9uKCB2YWx1ZSwgaW5kZXggKSB7IHJldHVybiBoYXNRKHBvc2l0aW9ucywgaW5kZXgpOyB9ICk7XG59XG5cbi8qKlxuKiBEZXRlcm1pbmVzIGlmIGFsbCBlbGVtZW50cyBpbiBwYXNzZWQgYXJyYXkgYXJlIGFycmF5cyB0aGVtc2VsdmVzLlxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtc1xuKiBAcmV0dXJucyB7Ym9vbGVhbn0gdHJ1ZSBpZiBBcnJheS5pc0FycmF5KGUpIGlzIHRydWUgZm9yIGFsbCBlIGluIGFycmF5XG4qIEBzZWV7QGxpbmsgYWxsfVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBsaXN0T2ZMaXN0c1EoIGFycmF5ICkge1xuICByZXR1cm4gYWxsKCBhcnJheS5tYXAoIGZ1bmN0aW9uKCBlbGVtZW50LCBpbmRleCApIHsgcmV0dXJuIEFycmF5LmlzQXJyYXkoZWxlbWVudCkgfSApIClcbn1cblxuLyoqXG4qIEJ1aWx0IG9uIHRvcCBvZiBAc2Vle0BsaW5rIGdldH0sIG1hcHBpbmcgaWYgcG9zaXRpb25zIGlzIGEgbGlzdCBvZiBsaXN0cyAoQHNlZXtAbGluayBsaXN0T2ZMaXN0c1F9KVxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtc1xuKiBAcGFyYW0ge251bWJlcltdIHwgW11udW1iZXJbXSB9IHBvc2l0aW9ucyBvZiBpbnRlZ2VycyBvciBsaXN0IG9mIHBvc2l0aW9ucyBvZiBpbnRlZ2Vyc1xuKiBAcmV0dXJucyB7Ym9vbGVhbn0gcmV0dXJucyBzcGVjaWZpZWQgcG9zaXRpb25zIGZyb20gYXJyYXkuIElmIG5lc3RlZCBwb3NpdGlvbnMgcGFzc2VkLCByZXR1cm5zIHJlcXVlc3RlZCBpdGVtcyBpbiBzYW1lIHN0cnVjdHVyZS5cbiovXG5leHBvcnQgZnVuY3Rpb24gY3V0KCBhcnJheSwgcG9zaXRpb25zICkge1xuICBpZiAoIGxpc3RPZkxpc3RzUShhcnJheSkgKSB7IHJldHVybiBwb3NpdGlvbnMubWFwKGZ1bmN0aW9uKHBvcywgaSkgeyByZXR1cm4gYXJyYXkuZ2V0KHBvcyk7IH0pOyB9XG4gIHJldHVybiBnZXQoIGFycmF5LCBwb3NpdGlvbnMgKTtcbn1cblxuLyoqXG4qIEdpdmVuIGFuIGFycmF5IG9mIG9iamVjdHMsIGNvbnN0cnVjdHMgbmV3IG9iamVjdHMgd2hlcmUgZWFjaCB2YWx1ZSBpcyBhIGxpc3RcbiogYmFzZWQgb24gdGhlIGNvcnJlc29uZGluZyBrZXksIHdoaWNoIGlzIGV4dHJhY3RlZCBieSB0aGUgcGFyYW1ldGVyIGJ5XG4qIEBwYXJhbSB7T2JqZWN0c1tdfSBhcnJheSBvZiBvYmplY3RzXG4qIEBwYXJhbSB7c3RyaW5nfSBieSBrZXkgd2l0aGluIGFsbCBvYmplY3RzIG9mIHBhc3NlZCBhcnJheVxuKiBAcGFyYW0ge3N0cmluZ1tdfSBbZ3JvdXBzXSBzYXZlcyBzb21lIGNvbXB1dGF0aW9uIGlmIGFsbCBrbm93biB2YWx1ZXMgZXh0cmFjdGVkIGJ5IG1hcHBpbmcgb3ZlciB0aGUgcGFyYW1ldGVyIGJ5IGFyZSBwYXNzZWRcbiogQHJldHVybnMge09iamVjdH0gb2Yga2V5IHZhbHVlIHBhaXJzLCB3aGVyZSBrZXlzIGFyZSBhbGwgdmFsdWVzIG9mIHRoZSBrZXkgYnkgZnJvbSBhbiBvYmplY3QgaW4gdGhlIHBhc3NlZCBhcnJheSBhbmQgdGhlIHZhbHVlIGFyZSB0aG9zZSBjb3JyZXNwb25kaW5nIG9iamVjdHMuXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIGdyb3VwQnkgKGFycmF5LCBieSwgZ3JvdXBzKSB7XG4gIGlmIChncm91cHMgPT0gdW5kZWZpbmVkKSB7XG4gICAgZ3JvdXBzID0gdW5pcXVlKGFycmF5Lm1hcChmdW5jdGlvbihlbGVtZW50cywgaW5kZXgpeyByZXR1cm4gZWxlbWVudFtieV07IH0pKTtcbiAgICBncm91cHMubWFwKGZ1bmN0aW9uKHZhbHVlLCBpbmRleCl7Z3JvdXBwZWRbdmFsdWVdID0gW119KVxuICB9XG5cbiAgdmFyIGdyb3VwcGVkID0ge307XG4gIGFycmF5Lm1hcChmdW5jdGlvbihlbGVtZW50LCBpbmRleCl7Z3JvdXBwZWRbZWxlbWVudFtieV1dLnB1c2goZWxlbWVudCl9KTtcbiAgcmV0dXJuIGdyb3VwcGVkXG59XG5cbi8qKlxuKiBUZXN0cyBpZiB0d28gYXJyYXlzIGFyZSBlcXVpdmFsZW50XG4qIEBwYXJhbSB7QXJyYXl9IGFycmF5XG4qIEBwYXJhbSB7QXJyYXl9IG90aGVyXG4qIEByZXR1cm5zIHtib29sZWFufSBpZiBldmVyeSBlbGVtZW50IG9mIGFycmF5IG1hdGNoZXMgdGhhdCBvZiBvdGhlclxuKi9cbmV4cG9ydCBmdW5jdGlvbiBhcnJheUVxdWFscyhhcnJheSwgb3RoZXIpIHtcbiAgaWYgKCFvdGhlcilcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgLy8gY29tcGFyZSBsZW5ndGhzIC0gY2FuIHNhdmUgYSBsb3Qgb2YgdGltZVxuICBpZiAoYXJyYXkubGVuZ3RoICE9IG90aGVyLmxlbmd0aClcbiAgICAgIHJldHVybiBmYWxzZTtcblxuICBmb3IgKHZhciBpID0gMCwgbD1hcnJheS5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgIC8vIENoZWNrIGlmIHdlIGhhdmUgbmVzdGVkIGFycmF5c1xuICAgICAgaWYgKGFycmF5W2ldIGluc3RhbmNlb2YgQXJyYXkgJiYgb3RoZXJbaV0gaW5zdGFuY2VvZiBBcnJheSkge1xuICAgICAgICAgIC8vIHJlY3Vyc2UgaW50byB0aGUgbmVzdGVkIGFycmF5c1xuICAgICAgICAgIGlmICghYXJyYXlFcXVhbHMoYXJyYXlbaV0sb3RoZXJbaV0pKVxuICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICB9XG4gICAgICBlbHNlIGlmIChhcnJheVtpXSAhPSBvdGhlcltpXSkge1xuICAgICAgICAgIC8vIFdhcm5pbmcgLSB0d28gZGlmZmVyZW50IG9iamVjdCBpbnN0YW5jZXMgd2lsbCBuZXZlciBiZSBlcXVhbDoge3g6MjB9ICE9IHt4OjIwfVxuICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgfVxuICByZXR1cm4gdHJ1ZTtcbn1cblxuXG5cbi8qKlxuKiBSZWN1cnNpdmVseSB0YWxsaWVzIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgYXQgZWFjaCBsZXZlbCBvZiB0aGUgcGFzc2VkLCBwdXRhdGl2ZWx5IG5lc3RlZCBhcnJheVxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtcyB3aGljaCBtYXkgaW5jbHVkZSBuZXN0ZWQgYXJyYXlzXG4qIEBwYXJhbSB7bnVtYmVyfSBbbGV2ZWw9MF0gY3VycmVudCBkZXB0aCBpbiB0aGUgcmVjdXJzaW9uXG4qIEBwYXJhbSB7QXJyYXl9IFtsZXZlbERhdGE9W11dIGtlZXBzIHRyYWNrIG9mIGl0ZW1zIHNlZW4gc28gZmFyIGF0IGVhY2ggZGVwdGhcbiogQHJldHVybnMge0FycmF5fSBzdGF0aW5nIHRoZSBudW1iZXIgb2YgZWxlbWVudHMgKGFycmF5IGluY2x1c2l2ZSkgZm91bmQgYXQgZWFjaCBsZXZlbCBvZiB0aGUgYXJyYXlcbiovXG5leHBvcnQgZnVuY3Rpb24gZWxlbWVudHNBdExldmVscyhhcnJheSwgbGV2ZWwsIGxldmVsRGF0YSkge1xuICBsZXZlbCA9IGxldmVsID09IHVuZGVmaW5lZCA/IDAgOiBsZXZlbCArIDE7XG4gIGxldmVsRGF0YSA9IGxldmVsRGF0YSA9PSB1bmRlZmluZWQgPyBbXSA6IGxldmVsRGF0YTtcbiAgaWYgKCBsZXZlbCA+PSBsZXZlbERhdGEubGVuZ3RoICkgeyBsZXZlbERhdGEucHVzaChhcnJheS5sZW5ndGgpfSBlbHNlIHtsZXZlbERhdGFbbGV2ZWxdICs9IGFycmF5Lmxlbmd0aCB9XG4gIGFycmF5Lm1hcChmdW5jdGlvbihlLCBpKSB7aWYgKEFycmF5LmlzQXJyYXkoZSkpeyBlbGVtZW50c0F0TGV2ZWxzKGUsIGxldmVsLCBsZXZlbERhdGEpIH19KVxuICByZXR1cm4gbGV2ZWxEYXRhXG59XG5cblxuLyoqXG4qIFJlY3Vyc2l2ZWx5IHRhbGxpZXMgdGhlIG51bWJlciBvZiBlbGVtZW50cyBvZiB0aGUgcGFzc2VkLCBwdXRhdGl2ZWx5IG5lc3RlZCBhcnJheVxuKiBAcGFyYW0ge0FycmF5fSBhcnJheSBvZiBpdGVtcyB3aGljaCBtYXkgaW5jbHVkZSBuZXN0ZWQgYXJyYXlzXG4qIEBwYXJhbSB7bnVtYmVyfSBbZWxlbWVudHM9MF0gY3VycmVudCBudW1iZXIgb2YgZWxlbWVudHMgc2VlbiBzbyBmYXJcbiogQHJldHVybnMge251bWJlcn0gbnVtYmVyIG9mIGVsZW1lbnRzIChhcnJheSBpbmNsdXNpdmUpIGZvdW5kIGluIHBhc3NlZCBhcnJheVxuKi9cbmV4cG9ydCBmdW5jdGlvbiBudW1iZXJPZkVsZW1lbnRzKCBhcnJheSwgZWxlbWVudHMgKSB7XG4gIGVsZW1lbnRzID0gZWxlbWVudHMgPT0gdW5kZWZpbmVkID8gMCA6IGVsZW1lbnRzO1xuICBhcnJheS5tYXAoZnVuY3Rpb24oZSwgaSkge1xuICAgIGlmICggQXJyYXkuaXNBcnJheShlKSApIHsgZWxlbWVudHMgPSBudW1iZXJPZkVsZW1lbnRzKGUsIGVsZW1lbnRzKSB9XG4gICAgZWxzZSB7IGVsZW1lbnRzICs9IDEgfVxuICB9KVxuICByZXR1cm4gZWxlbWVudHNcbn1cblxuLyoqXG4qIENvbmNhdHMgYWxsIG5lc3RlZCBhcnJheXMgaW4gcGFzc2VkIGFycmF5IHRvIGZvcm0gYSBzaW5nbGUgYXJyYXlcbiogQHBhcmFtIHtBcnJheX0gYXJyYXkgb2YgcHV0YXRpdmVseSBuZXN0ZWQgYXJyYXlzXG4qIEBwYXJhbSB7QXJyYXl9IFtmbGF0PVtdXSBjdXJyZW50IGZsYXR0ZW5lZCBhcnJheVxuKiBAcmV0dXJucyB7QXJyYXl9IHdpdGggZXZlcnkgZWxlbWVudCBpbiB0aGUgc2FtZSBsZXZlbFxuKi9cbmV4cG9ydCBmdW5jdGlvbiBmbGF0dGVuKCBhcnJheSwgZmxhdCApIHtcbiAgZmxhdCA9IGZsYXQgPT0gdW5kZWZpbmVkID8gW10gOiBmbGF0O1xuICBhcnJheS5tYXAoZnVuY3Rpb24oZSwgaSl7XG4gICAgaWYgKEFycmF5LmlzQXJyYXkoZSkpIHtmbGF0ID0gZmxhdC5jb25jYXQoZmxhdHRlbihlKSl9XG4gICAgZWxzZSB7ZmxhdC5wdXNoKGUpfVxuICB9KVxuICByZXR1cm4gZmxhdDtcbn1cblxuLyoqXG4qIFNlYXJjaCBvZiBsaXN0IG9mIGxpc3RzIHRvIGZpbmQgd2hpY2ggLSBpZiBhbnkgLSBwYXNzZWQgdmFsdWUgaXMgaW5cbiogQHBhcmFtIHtBcnJheVtdfSBiaW5zIGxpc3Qgb2YgbGlzdHMgb2YgdmFsdWVzXG4qIEBwYXJhbSB7Kn0gdmFsdWUgaXRlbSB0byB0ZXN0IGlmIGluIGFueSBvZiB0aGUgYmluc1xuKiBAcmV0dXJucyB7bnVtYmVyfSBpbmRpY2F0aW5nIHRoZSBpbmRleCBvZiB0aGUgYmluIGluIHdoaWNoIHZhbHVlIHdhcyBmb3VuZFxuKi9cbmV4cG9ydCBmdW5jdGlvbiB3aGljaEJpbihiaW5zLCB2YWx1ZSkge1xuICB2YXIgaSA9IC0xXG4gIGZvciAodmFyIGogPSAwOyBqIDwgYmlucy5sZW5ndGg7IGorKykgeyBpZiAoaGFzUShiaW5zW2pdLHZhbHVlKSkge3JldHVybiBqfSB9XG4gIHJldHVybiBpXG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHt0b3RhbH0gZnJvbSAnLi9hcnJheS1mdW5jdGlvbnMnO1xuXG5cbi8qKlxuICogY2FsbHMgY29uc29sZS5ncm91cCBpZiBkM3NtLmRlYnVnUSA9PSB0cnVlXG4gKiBAcGFyYW0ge3N0cmluZ30gbmFtZSBvZiB0aGUgZ3JvdXBcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb25zb2xlR3JvdXAobmFtZSkge1xuICBpZiAod2luZG93LmQzc20uZGVidWdRID09PSB0cnVlKXtcbiAgICBjb25zb2xlLmdyb3VwKG5hbWUpXG4gIH1cbn1cblxuLyoqXG4gKiBjYWxscyBjb25zb2xlLmdyb3VwRW5kIGlmIGQzc20uZGVidWdRID09IHRydWVcbiAqIEByZXR1cm5zIHt1bmRlZmluZWR9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb25zb2xlR3JvdXBFbmQoKSB7XG4gIGlmICh3aW5kb3cuZDNzbS5kZWJ1Z1EgPT09IHRydWUpe1xuICAgIGNvbnNvbGUuZ3JvdXBFbmQoKVxuICB9XG59XG5cbi8qKlxuICogQ2FsbHMgY29uc29sZS5sb2cgaWYgZDNzbS5kZWJ1Z1EgPT0gdHJ1ZVxuICogQHBhcmFtIHtzdHJpbmd9IGZ1bmMgbmFtZSBvZiB0aGUgZnVuY3Rpb24gbG9nZ2luZ1xuICogQHBhcmFtIHtzdHJpbmd9IG1zZyB0byBsb2dcbiAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhIHRvIGJlIGxvZ2dlZCBhbG9uZyBzaWRlIHRoZSBtZXNzYWdlXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfVxuICovXG5leHBvcnQgZnVuY3Rpb24gbG9nKGZ1bmMsIG1zZywgZGF0YSkge1xuICBpZiAod2luZG93LmQzc20uZGVidWdRID09PSB0cnVlKXtcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgIGAlY1tkM3NtOjoke2Z1bmN9XTpcXHQke21zZ31gLFxuICAgICAgW1xuICAgICAgICAnYmFja2dyb3VuZDogIzZjZDFlZicsXG4gICAgICAgICdib3JkZXItcmFkaXVzOiA1MDAwcHgnLFxuICAgICAgICAncGFkZGluZzogMHB4IDJweCcsXG4gICAgICAgICdmb250LXNpemU6IDE0cHgnXG4gICAgICBdLmpvaW4oJzsnKVxuICAgIClcbiAgICBjb25zb2xlLnRhYmxlKGRhdGEpXG4gICAgLy8gY29uc29sZS50cmFjZSgpXG4gIH1cbn1cblxuLyoqXG4gKiBDYWxscyBjb25zb2xlLndhcm4gaWYgZDNzbS5kZWJ1Z1EgPT0gdHJ1ZVxuICogQHBhcmFtIHtzdHJpbmd9IGZ1bmMgbmFtZSBvZiB0aGUgZnVuY3Rpb24gd2FybmluZ1xuICogQHBhcmFtIHtzdHJpbmd9IG1zZyB0byBkaXNwbGF5XG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YSB0byBiZSBkaXNwbGF5ZWQgYWxvbmcgc2lkZSB0aGUgbWVzc2FnZVxuICogQHJldHVybnMge3VuZGVmaW5lZH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdhcm4oZnVuYywgbXNnLCBkYXRhKSB7XG4gIGlmICh3aW5kb3cuZDNzbS5kZWJ1Z1EgPT09IHRydWUpXG4gICAgY29uc29sZS53YXJuKFxuICAgICAgYCVjW2Qzc206OiR7ZnVuY31dOlxcdCR7bXNnfWAsXG4gICAgICBbXG4gICAgICAgICdiYWNrZ3JvdW5kOiAjZmZkNTNlJyxcbiAgICAgICAgJ2JvcmRlci1yYWRpdXM6IDUwMDBweCcsXG4gICAgICAgICdwYWRkaW5nOiAwcHggMnB4JyxcbiAgICAgICAgJ2ZvbnQtc2l6ZTogMTRweCdcbiAgICAgIF0uam9pbignOycpXG4gICAgKVxuICAgIGNvbnNvbGUudGFibGUoZGF0YSlcbn1cbi8qKlxuICogQ2FsbHMgdGhlIGNvbnNvbGUuaW5mbyBpZiBkM3NtLmRlYnVnUSA9PSB0cnVlXG4gKiBAcGFyYW0ge3N0cmluZ30gZnVuYyBuYW1lIG9mIHRoZSBmdW5jdGlvbiBwcm92aWRpbmcgaW5mb1xuICogQHBhcmFtIHtzdHJpbmd9IG1zZyB0byBkaXNwbGF5XG4gKiBAcGFyYW0ge09iamVjdH0gZGF0YSB0byBiZSBkaXNwbGF5ZWQgYWxvbmcgc2lkZSB0aGUgbWVzc2FnZVxuICogQHJldHVybnMge3VuZGVmaW5lZH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGluZm8oZnVuYywgbXNnLCBkYXRhKSB7XG4gIGlmICh3aW5kb3cuZDNzbS5kZWJ1Z1EpXG4gICAgY29uc29sZS5pbmZvKFxuICAgICAgYCVjW2Qzc206OiR7ZnVuY31dOlxcdCR7bXNnfWAsXG4gICAgICBbXG4gICAgICAgICdiYWNrZ3JvdW5kOiAjMDA5Y2NkJyxcbiAgICAgICAgJ2JvcmRlci1yYWRpdXM6IDUwMDBweCcsXG4gICAgICAgICdwYWRkaW5nOiAwcHggMnB4JyxcbiAgICAgICAgJ2ZvbnQtc2l6ZTogMTRweCdcbiAgICAgIF0uam9pbignOycpXG4gICAgKVxuICAgIGNvbnNvbGUudGFibGUoZGF0YSlcbn1cblxuXG4vKipcbiAqIENhbGxzIGNvbnNvbGUuZXJyb3IgaWYgZDNzbS5kZWJ1Z1EgPT0gdHJ1ZVxuICogQHBhcmFtIHtzdHJpbmd9IGZ1bmMgbmFtZSBvZiB0aGUgZnVuY3Rpb24gd2hpY2ggc2VuZHMgdGhlIGVycm9yXG4gKiBAcGFyYW0ge3N0cmluZ30gbXNnIHRvIGRpc3BsYXlcbiAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhIHRvIGJlIGRpc3BsYXllZCBhbG9uZyBzaWRlIHRoZSBtZXNzYWdlXG4gKiBAcmV0dXJucyB7dW5kZWZpbmVkfVxuICovXG5leHBvcnQgZnVuY3Rpb24gZXJyb3IoZnVuYywgbXNnLCBkYXRhKSB7XG4gIGlmICh3aW5kb3cuZDNzbS5kZWJ1Z1EpXG4gICAgY29uc29sZS5lcnJvcihgW2Qzc206OiR7ZnVuY31dOlxcdCR7bXNnfVxcdCVvYCxkYXRhKVxufVxuXG5cblxuXG5cbi8qKlxuKiBGdW5jdGlvbiBmb3Igc2V0dGluZyB1cCBjb250YWluZXJzIGZvciBtb3N0IHBsb3RzIHdpdGggdGhlIHkgYXhpcyBjb250YWluZXJcbiogcG9zaXRpb25lZCBvbiB0aGUgbGVmdCBhbmQgdGhlIHggYXhpcyBjb250YWluZXIgcG9zaXRpb25lZCBvbiB0aGUgYm90dG9tXG4qIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBzZWxlY3Rpb24gc2VsZWN0aW9uIG9mIGNvbnRhaW5lciBpbiB3aGljaCB0aGUgc3ZnIGlzIG9yIHNob3VsZCBiZSBtYWRlXG4qIEBwYXJhbSB7c3RyaW5nfSBuYW1lc3BhY2UgbmFtZXNwYWNlIG9mIHRoZSBjaGFydFxuKiBAcGFyYW0ge09iamVjdH0gW3NwYWNlPXt3OndpbmRvdy5pbm5lcldpZHRoLCBoOndpbmRvdy5pbm5lckhlaWdodH1dIHRoZSB3aWR0aCAodykgYW5kIGhlaWdodCAoaCkgYXZhaWxibGVcbiogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZS53PXdpbmRvdy5pbm5lcldpZHRoXSB0aGUgYXZhaWxhYmxlIHdpZHRoIGluIHdoaWNoIHRvIHJlbmRlciB0aGUgY2hhcnRcbiogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZS5oPXdpbmRvdy5pbm5lckhlaWdodF0gdGhlIGF2YWlsYWJsZSBoZWlnaHQgaW4gd2hpY2ggdG8gcmVuZGVyIHRoZSBjaGFydFxuXG4qIEBwYXJhbSB7T2JqZWN0fSBbbWFyZ2lucz17dG9wOiAwLjAxLCBib3R0b206IDAuMDEsIGxlZnQ6IDAuMDEsIHJpZ2h0OiAwLjAxfV0gdGhlIG1hcmdpbnMgZm9yIHRoZSBjaGFydFxuKiBAcGFyYW0ge251bWJlcn0gW21hcmdpbnMudG9wPTAuMDFdIHRoZSB0b3AgbWFyZ2luIG9mIHRoZSBjaGFydFxuKiBAcGFyYW0ge251bWJlcn0gW21hcmdpbnMuYm90dG9tPTAuMDFdIHRoZSBib3R0b20gbWFyZ2luIG9mIHRoZSBjaGFydFxuKiBAcGFyYW0ge251bWJlcn0gW21hcmdpbnMubGVmdD0wLjAxXSB0aGUgbGVmdCBtYXJnaW4gb2YgdGhlIGNoYXJ0XG4qIEBwYXJhbSB7bnVtYmVyfSBbbWFyZ2lucy5yaWdodD0wLjAxXSB0aGUgcmlnaHQgbWFyZ2luIG9mIHRoZSBjaGFydFxuXG5cbiogQHBhcmFtIHtPYmplY3R9IFtwZXJjZW50YWdlcyA9IHtheGVzOnt4OjAuMSx5OjAuMX0sc3BhY2U6e3c6MC44LGg6MC42fX1dIHBlcmNlbnRhZ2VzIG9mIHRoZSBwYXJhbWF0ZXIgc3BhY2Ugb2Ygd2hpY2ggdG8gbWFrZSB0aGUgeCBhbmQgeSBheGVzIGFzIHdlbGwgYXMgdGhlIHBlcmNlbnQgb2YgdGhlIGF2YWlsYmxlIHNwYWNlIGluIHdoaWNoIHRvIHJlbmRlciB0aGUgcGxvdFxuKiBAcGFyYW0ge09iamVjdH0gW3BlcmNlbnRhZ2VzLmF4ZXM9e3g6MC4xLHk6MC4xfV0gdGhlIHBlcmNlbnRhZ2VzIG9mIHRoZSBwYXJhbWF0ZXIgc3BhY2UsIG9mIHdoaWNoIHRoZSB4IGFuZCB5IGF4ZXMgd2lsbCB0YWtlIHVwXG4qIEBwYXJhbSB7bnVtYmVyfSBbcGVyY2VudGFnZXMuYXhlcy54QXhpc1BlcmNlbnQ9MC4xXSB0aGUgcGVyY2VudGFnZXMgb2YgdGhlIHBhcmFtYXRlciBzcGFjZSwgb2Ygd2hpY2ggdGhlIHggYXhpcyB3aWxsIHRha2UgdXBcbiogQHBhcmFtIHtudW1iZXJ9IFtwZXJjZW50YWdlcy5heGVzLnlBeGlzUGVyY2VudD0wLjFdIHRoZSBwZXJjZW50YWdlcyBvZiB0aGUgcGFyYW1hdGVyIHNwYWNlLCBvZiB3aGljaCB0aGUgeSBheGlzIHdpbGwgdGFrZSB1cFxuXG4qIEBwYXJhbSB7T2JqZWN0fSBbcGVyY2VudGFnZXMuc3BhY2U9e3c6MC44LGg6MC42fV0gdGhlIHBlcmNlbnRhZ2VzIG9mIHRoZSBwYXJhbWF0ZXIgc3BhY2UsIG9mIHdoaWNoIHRoZSBTVkcncyB3aWR0aCBhbmQgaGVpZ2h0IHdpbGwgYmUgc2V0XG4qIEBwYXJhbSB7bnVtYmVyfSBbcGVyY2VudGFnZXMuc3BhY2UucGVyY2VudE9mU3BhY2VGb3JXaWR0aD0wLjFdIHRoZSBwZXJjZW50YWdlcyBvZiB0aGUgcGFyYW1hdGVyIHNwYWNlLCBvZiB3aGljaCB0aGUgU1ZHJ3Mgd2lkdGggd2lsbCBiZSBzZXRcbiogQHBhcmFtIHtudW1iZXJ9IFtwZXJjZW50YWdlcy5zcGFjZS5wZXJjZW50T2ZTcGFjZUZvckhlaWdodD0wLjFdIHRoZSBwZXJjZW50YWdlcyBvZiB0aGUgcGFyYW1hdGVyIHNwYWNlLCBvZiB3aGljaCB0aGUgU1ZHJ3MgaGVpZ2h0IHdpbGwgYmUgc2V0XG5cbiogQHJldHVybnMge09iamVjdH0gcmV0dXJucyB0aGUgc2VsZWN0aW9uIGFuZCBcImJvdW5kaW5nUmVjdHNcIiBvZiB0aGUgcGxvdCBjb250YWluZXIsIHgtYXhpcyBjb250YWluZXIgYW5kIHktYXhpcyBjb250YWluZXJcbiogYXNcbipcbioge1xuKlxuKiAgIHBsb3Q6IHtzZWxlY3Rpb246IHBsb3RTZWxlY3Rpb24sIHJlY3Q6IHBsb3RSZWN0fSxcbipcbiogICB4QXhpczp7c2VsZWN0aW9uOnhBeGlzU2VsZWN0aW9uLCByZWN0OnhBeGlzUmVjdH0sXG4qXG4qICAgeUF4aXM6IHtzZWxlY3Rpb246eUF4aXNTZWxlY3Rpb24sIHJlY3Q6eUF4aXNSZWN0fVxuKlxuKiB9XG4qXG4qIHdoZXJlIGVhY2ggcmVjdCBoYXMgZm9ybTpcbipcbioge3g6ICMsIHk6ICMsIGg6ICMsIHc6ICN9XG4qXG4qIGRlcGljdGluZyB0aGUgc3RhcnRpbmcgeCBhbmQgeSBjb29yZGluYXRlIG9mIHRoZSBjb3Jlc3BvbmRpbmcgY29udGFpbmVyIChhbHNvIHRoZWlyIGRlZmF1bHQgdHJhbnNmb3JtIHZhbHVlcykgYXMgd2VsbCB0aGVpciBoZWlnaHQgKGgpIGFucyB3aWR0aCAodylcbiovXG4vLyBleHBvcnQgZnVuY3Rpb24gc2V0dXBTdGFuZGFyZENoYXJ0Q29udGFpbmVycyggc2VsZWN0aW9uLCBuYW1lc3BhY2UsIHNwYWNlLCBtYXJnaW5zLCBwZXJjZW50YWdlcykge1xuLy8gZXhwb3J0IGZ1bmN0aW9uIHNldHVwU3RhbmRhcmRDaGFydENvbnRhaW5lcnMoXG4vLyAgIHNlbGVjdGlvbixcbi8vICAgbmFtZXNwYWNlLFxuLy8gICBzcGFjZT17dzphdmFpbGFibGVXaWR0aD13aW5kb3cuaW5uZXJXaWR0aCwgaDphdmFpbGFibGVIZWlnaHQ9d2luZG93LmlubmVySGVpZ2h0fSxcbi8vICAgbWFyZ2lucz17dG9wOjAuMDEsIGJvdHRvbTowLjAxLCBsZWZ0OjAuMDEsIHJpZ2h0OjAuMDF9LFxuLy8gICBwZXJjZW50YWdlcz17YXhlczoge3g6IHhBeGlzUGVyY2VudD0wLjEsIHk6IHlBeGlzUGVyY2VudD0wLjF9LCBzcGFjZToge3c6IHBlcmNlbnRPZlNwYWNlRm9yV2lkdGgsIGg6IHBlcmNlbnRPZlNwYWNlRm9ySGVpZ2h0fX1cbi8vICkge1xuLy8gICBpZiAoc3BhY2UgPT0gdW5kZWZpbmVkKSB7IHNwYWNlID0ge3c6IHdpbmRvdy5pbm5lcldpZHRoLCBoOiB3aW5kb3cuaW5uZXJIZWlnaHR9IH1cbi8vICAgaWYgKG1hcmdpbnMgPT0gdW5kZWZpbmVkKSB7IG1hcmdpbnMgPSB7dG9wOiAwLjAxLCBib3R0b206IDAuMDEsIGxlZnQ6IDAuMDEsIHJpZ2h0OiAwLjAxfSB9XG4vLyAgIGlmIChwZXJjZW50YWdlcyA9PSB1bmRlZmluZWQpIHsgcGVyY2VudGFnZXMgPSB7fTsgfVxuLy8gICBpZiAocGVyY2VudGFnZXMuYXhlcyA9PSB1bmRlZmluZWQpIHsgcGVyY2VudGFnZXMuYXhlcyA9IHsgeDowLjEsIHk6MC4xIH0gfVxuLy8gICBpZiAocGVyY2VudGFnZXMuc3BhY2UgPT0gdW5kZWZpbmVkKSB7IHBlcmNlbnRhZ2VzLnNwYWNlID0geyB3OiAwLjgsIGg6IDAuNiB9IH1cbi8vXG4vLyAgIC8vIFNWRyB3aWR0aCBhbmQgaGVpZ2h0XG4vLyAgIHZhciBzdmdTcGFjZSA9ICB7XG4vLyAgICAgdzogc3BhY2UudyAqIHBlcmNlbnRhZ2VzLnNwYWNlLncsXG4vLyAgICAgaDogc3BhY2UuaCAqIHBlcmNlbnRhZ2VzLnNwYWNlLmhcbi8vICAgfSxcbi8vXG4vLyAgIC8vIFNwYWNlIGFmdGVyIHJlbW92aW5nIG1hcmdpbnNcbi8vICAgY2hhcnRTcGFjZSA9IHtcbi8vICAgICB3OiBzdmdTcGFjZS53IC0gKG1hcmdpbnMubGVmdCAqIHNwYWNlLncpIC0gKG1hcmdpbnMucmlnaHQgKiBzcGFjZS53KSxcbi8vICAgICBoOiBzdmdTcGFjZS5oIC0gKG1hcmdpbnMudG9wICogc3BhY2UuaCkgLSAobWFyZ2lucy5ib3R0b20gKiBzcGFjZS5oKVxuLy8gICB9LFxuLy9cbi8vICAgLy8gbWFpbiBkaW1lbnNpb24gb2YgeCBhbmQgeSBheGllc1xuLy8gICAvLyBlLmcuIGRlZmluZXMgaG93IHRhbGwgeCBheGlzIGlzIGFzIGxlbmd0aCBpcyBkZXRlcm1pbmVkIGJ5IHBsb3RSZWN0Lndcbi8vICAgYXhlc1NwYWNlID0ge1xuLy8gICAgIHg6IGNoYXJ0U3BhY2UuaCAqIHBlcmNlbnRhZ2VzLmF4ZXMueCxcbi8vICAgICB5OiBjaGFydFNwYWNlLncgKiBwZXJjZW50YWdlcy5heGVzLnlcbi8vICAgfSxcbi8vXG4vLyAgIC8vIHNwYWNlIGxlZnQgZm9yIGRyYXdpbmcgdGhlIGNoYXJ0IHByb3Blcmx5IChlLmcuIGJhcnMsIHZpb2xpbnMsIGV0Yylcbi8vICAgZHJhd2luZ1NwYWNlID0ge1xuLy8gICAgIHg6IGNoYXJ0U3BhY2UudyAtIGF4ZXNTcGFjZS55LFxuLy8gICAgIHk6IGNoYXJ0U3BhY2UuaCAtIGF4ZXNTcGFjZS54XG4vLyAgIH0sXG4vL1xuLy9cbi8vICAgeUF4aXNSZWN0ID0ge1xuLy8gICAgIHg6IGF4ZXNTcGFjZS55ICsgKG1hcmdpbnMubGVmdCAqIHNwYWNlLncpLFxuLy8gICAgIHk6IChtYXJnaW5zLnRvcCAqIHNwYWNlLmgpLFxuLy8gICAgIHc6IGF4ZXNTcGFjZS55LFxuLy8gICAgIGg6IGRyYXdpbmdTcGFjZS55XG4vLyAgIH0sXG4vL1xuLy8gICBwbG90UmVjdCA9IHtcbi8vICAgICB4OiBheGVzU3BhY2UueSArIChtYXJnaW5zLmxlZnQgKiBzcGFjZS53KSxcbi8vICAgICB5OiAobWFyZ2lucy50b3AgKiBzcGFjZS5oKSxcbi8vICAgICB3OiBkcmF3aW5nU3BhY2UueCxcbi8vICAgICBoOiBkcmF3aW5nU3BhY2UueVxuLy8gICB9LFxuLy9cbi8vICAgeEF4aXNSZWN0ID0ge1xuLy8gICAgIHg6IGF4ZXNTcGFjZS55ICsgKG1hcmdpbnMubGVmdCAqIHNwYWNlLncpLFxuLy8gICAgIHk6IChtYXJnaW5zLnRvcCAqIHNwYWNlLmggKyBwbG90UmVjdC5oKSxcbi8vICAgICB3OiBkcmF3aW5nU3BhY2UueCxcbi8vICAgICBoOiBheGVzU3BhY2UueFxuLy8gICB9XG4vL1xuLy9cbi8vICAgdmFyIGNvbnRhaW5lciA9IHNhZmVTZWxlY3Qoc2VsZWN0aW9uLCAnc3ZnJywgbmFtZXNwYWNlKVxuLy8gICAgIC5zdHlsZSgnd2lkdGgnLCBzdmdTcGFjZS53KydweCcpXG4vLyAgICAgLnN0eWxlKCdoZWlnaHQnLCBzdmdTcGFjZS5oKydweCcpXG4vL1xuLy8gICB2YXIgYXhlcyA9IHNhZmVTZWxlY3QoY29udGFpbmVyLCAnZycsIGh5cGVuYXRlKG5hbWVzcGFjZSwgJ2F4ZXMnKSlcbi8vXG4vLyAgIC8vIC5hdHRyKCd0cmFuc2Zvcm0nLCBcInRyYW5zbGF0ZShcIitwbG90UmVjdC54K1wiLFwiK3Bsb3RSZWN0LnkrXCIpXCIpLFxuLy9cbi8vICAgdmFyIHBsb3QgPSBzYWZlU2VsZWN0KGNvbnRhaW5lciwgJ2cnLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdwbG90JykpXG4vLyAgICAgLmF0dHIoJ3RyYW5zZm9ybScsIFwidHJhbnNsYXRlKFwiK3Bsb3RSZWN0LngrXCIsXCIrcGxvdFJlY3QueStcIilcIilcbi8vXG4vLyAgIHZhciB4QXhpcyA9IHNhZmVTZWxlY3QoYXhlcywgJ2cnLCBoeXBlbmF0ZShuYW1lc3BhY2UsICd4LWF4aXMnKSlcbi8vICAgICAuYXR0cigndHJhbnNmb3JtJywgXCJ0cmFuc2xhdGUoXCIreEF4aXNSZWN0LngrXCIsXCIreEF4aXNSZWN0LnkrXCIpXCIpXG4vL1xuLy8gICB2YXIgeUF4aXMgPSBzYWZlU2VsZWN0KGF4ZXMsICdnJywgaHlwZW5hdGUobmFtZXNwYWNlLCAneS1heGlzJykpXG4vLyAgICAgLmF0dHIoJ3RyYW5zZm9ybScsIFwidHJhbnNsYXRlKFwiK3lBeGlzUmVjdC54K1wiLFwiK3lBeGlzUmVjdC55K1wiKVwiKVxuLy9cbi8vICAgcmV0dXJuIHtcbi8vICAgICBzdmc6IHtcbi8vICAgICAgIHNlbGVjdGlvbjogY29udGFpbmVyLFxuLy8gICAgICAgcmVjdDogc3ZnU3BhY2Vcbi8vICAgICB9LFxuLy8gICAgIHBsb3Q6IHtcbi8vICAgICAgIHNlbGVjdGlvbjogcGxvdCxcbi8vICAgICAgIHJlY3Q6IHBsb3RSZWN0XG4vLyAgICAgfSxcbi8vICAgICB4QXhpczoge1xuLy8gICAgICAgc2VsZWN0aW9uOiB4QXhpcyxcbi8vICAgICAgIHJlY3Q6IHhBeGlzUmVjdFxuLy8gICAgIH0sXG4vLyAgICAgeUF4aXM6IHtcbi8vICAgICAgIHNlbGVjdGlvbjogeUF4aXMsXG4vLyAgICAgICByZWN0OiB5QXhpc1JlY3Rcbi8vICAgICB9XG4vLyAgIH1cbi8vXG4vLyAgIC8vIHJldHVybiBbcGxvdCwgeEF4aXMsIHlBeGlzXVxuLy8gfVxuLy9cbi8vXG5cblxuXG5cblxuZXhwb3J0IGZ1bmN0aW9uIHNldHVwU3RhbmRhcmRDaGFydENvbnRhaW5lcnMoXG4gIHNlbGVjdGlvbixcbiAgbmFtZXNwYWNlLFxuICBjb250YWluZXIsXG4gIG1hcmdpbnM9e3RvcDowLjAxLCBib3R0b206MC4wMSwgbGVmdDowLjAxLCByaWdodDowLjAxfSxcbiAgc3ZnPXt3OjAuOCwgaDowLjZ9LCAvLyBwZXJjZW50IG9mIGNvbnRhaW5lciBzcGFjZSBmb3Igc3ZnXG4gIGF4ZXM9e3k6MC4xLCB4OjAuMX0sIC8vIHBlcmNlbnQgb2YgY29udGFpbmVyIHNwYWNlIGZvciBheGVzLFxuICBsZWc9e3g6MCwgbWFyZ2luOjAsIHBvczonbGVmdCd9IC8vIGFic29sdXRlIHdpZHRoIG9mIGxlZ2VuZCBhbmQgc3BhY2Ugb24gZWl0aGVyIHNpemVcbilcbntcbiAgaWYgKGNvbnRhaW5lciA9PSB1bmRlZmluZWQpIHtjb250YWluZXIgPSB7dzp3aW5kb3cuaW5uZXJXaWR0aCwgaDp3aW5kb3cuSGVpZ2h0fX1cbiAgLy8gU1ZHIHdpZHRoIGFuZCBoZWlnaHRcblxuICB2YXIgc3ZnU3BhY2UgPSAge1xuICAgIHc6IGNvbnRhaW5lci53ICogc3ZnLncsXG4gICAgaDogY29udGFpbmVyLmggKiBzdmcuaFxuICB9XG5cbiAgdmFyIG1hcmdQeCA9IHtcbiAgICB0b3A6IG1hcmdpbnMudG9wICogc3ZnU3BhY2UuaCxcbiAgICBib3R0b206IG1hcmdpbnMuYm90dG9tICogc3ZnU3BhY2UuaCxcbiAgICBsZWZ0OiBtYXJnaW5zLmxlZnQgKiBzdmdTcGFjZS53LFxuICAgIHJpZ2h0OiBtYXJnaW5zLnJpZ2h0ICogc3ZnU3BhY2Uud1xuICB9LFxuXG5cblxuICAvLyBTcGFjZSBhZnRlciByZW1vdmluZyBtYXJnaW5zXG4gIGNoYXJ0U3BhY2UgPSB7XG4gICAgdzogc3ZnU3BhY2UudyAtIG1hcmdQeC5sZWZ0IC0gbWFyZ1B4LnJpZ2h0LFxuICAgIGg6IHN2Z1NwYWNlLmggLSBtYXJnUHgudG9wIC0gbWFyZ1B4LmJvdHRvbVxuICB9LFxuXG4gIC8vIG1haW4gZGltZW5zaW9uIG9mIHggYW5kIHkgYXhpZXNcbiAgLy8gZS5nLiBkZWZpbmVzIGhvdyB0YWxsIHggYXhpcyBpcyBhcyBsZW5ndGggaXMgZGV0ZXJtaW5lZCBieSBwbG90UmVjdC53XG4gIGF4ZXNTcGFjZSA9IHtcbiAgICB4OiBjaGFydFNwYWNlLmggKiBheGVzLngsXG4gICAgeTogY2hhcnRTcGFjZS53ICogYXhlcy55XG4gIH0sXG5cbiAgLy8gc3BhY2UgbGVmdCBmb3IgZHJhd2luZyB0aGUgY2hhcnQgcHJvcGVybHkgKGUuZy4gYmFycywgdmlvbGlucywgZXRjKVxuICBkcmF3aW5nU3BhY2UgPSB7XG4gICAgeDogY2hhcnRTcGFjZS53IC0gYXhlc1NwYWNlLnkgLSBsZWcueCAtIDIqbGVnLm1hcmdpbixcbiAgICB5OiBjaGFydFNwYWNlLmggLSBheGVzU3BhY2UueFxuICB9LFxuXG5cbiAgbGVnUmVjdCA9IHtcbiAgICB4OiBsZWcubWFyZ2luICsgbWFyZ1B4LmxlZnQgKyAobGVnLnBvcyA9PSAnbGVmdCcgPyAwIDogZHJhd2luZ1NwYWNlLnggKyBheGVzU3BhY2UueSksXG4gICAgeTogbWFyZ1B4LnRvcCwgLy8gdGhpcyBpcyBzb29tZWhvdyBnZXR0aW5nIGNhbGN1bGF0ZWQgaW5jb3JlY3RseVxuICAgIHc6IGxlZy54LFxuICAgIGg6IGRyYXdpbmdTcGFjZS55XG4gIH0sXG5cbiAgeUF4aXNSZWN0ID0ge1xuICAgIHg6IGF4ZXNTcGFjZS55ICsgbWFyZ1B4LmxlZnQgKyAobGVnLnBvcyA9PSAnbGVmdCcgPyBsZWcueCArIDIqbGVnLm1hcmdpbiA6IDApLFxuICAgIHk6IG1hcmdQeC50b3AsXG4gICAgdzogYXhlc1NwYWNlLnksXG4gICAgaDogZHJhd2luZ1NwYWNlLnlcbiAgfSxcblxuICBwbG90UmVjdCA9IHtcbiAgICB4OiBheGVzU3BhY2UueSArIG1hcmdQeC5sZWZ0ICsgKGxlZy5wb3MgPT0gJ2xlZnQnID8gbGVnLnggKyAyKmxlZy5tYXJnaW4gOiAwKSxcbiAgICB5OiBtYXJnUHgudG9wLFxuICAgIHc6IGRyYXdpbmdTcGFjZS54LFxuICAgIGg6IGRyYXdpbmdTcGFjZS55XG4gIH0sXG5cbiAgeEF4aXNSZWN0ID0ge1xuICAgIHg6IGF4ZXNTcGFjZS55ICsgbWFyZ1B4LmxlZnQgKyAobGVnLnBvcyA9PSAnbGVmdCcgPyBsZWcueCArIDIqbGVnLm1hcmdpbiA6IDApLFxuICAgIHk6IG1hcmdQeC50b3AgKyBkcmF3aW5nU3BhY2UueSxcbiAgICB3OiBkcmF3aW5nU3BhY2UueCxcbiAgICBoOiBheGVzU3BhY2UueFxuICB9XG5cblxuXG4gIGNvbnRhaW5lciA9IGQzc20uc2FmZVNlbGVjdChzZWxlY3Rpb24sICdzdmcnLCBuYW1lc3BhY2UpXG4gICAgLnN0eWxlKCd3aWR0aCcsIHN2Z1NwYWNlLncrJ3B4JylcbiAgICAuc3R5bGUoJ2hlaWdodCcsIHN2Z1NwYWNlLmgrJ3B4JylcblxuICB2YXIgYXhlcyA9IGQzc20uc2FmZVNlbGVjdChjb250YWluZXIsICdnJywgZDNzbS5oeXBlbmF0ZShuYW1lc3BhY2UsICdheGVzJykpXG5cbiAgdmFyIGxlZyA9IGQzc20uc2FmZVNlbGVjdChjb250YWluZXIsICdnJywgZDNzbS5oeXBlbmF0ZShuYW1lc3BhY2UsICdsZWdlbmQnKSlcbiAgLmF0dHIoJ3RyYW5zZm9ybScsIFwidHJhbnNsYXRlKFwiK2xlZ1JlY3QueCtcIixcIitsZWdSZWN0LnkrXCIpXCIpXG5cbiAgdmFyIHBsb3QgPSBkM3NtLnNhZmVTZWxlY3QoY29udGFpbmVyLCAnZycsIGQzc20uaHlwZW5hdGUobmFtZXNwYWNlLCAncGxvdCcpKVxuICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBcInRyYW5zbGF0ZShcIitwbG90UmVjdC54K1wiLFwiK3Bsb3RSZWN0LnkrXCIpXCIpXG5cbiAgdmFyIHhBeGlzID0gZDNzbS5zYWZlU2VsZWN0KGF4ZXMsICdnJywgZDNzbS5oeXBlbmF0ZShuYW1lc3BhY2UsICd4LWF4aXMnKSlcbiAgICAuYXR0cigndHJhbnNmb3JtJywgXCJ0cmFuc2xhdGUoXCIreEF4aXNSZWN0LngrXCIsXCIreEF4aXNSZWN0LnkrXCIpXCIpXG5cbiAgdmFyIHlBeGlzID0gZDNzbS5zYWZlU2VsZWN0KGF4ZXMsICdnJywgZDNzbS5oeXBlbmF0ZShuYW1lc3BhY2UsICd5LWF4aXMnKSlcbiAgICAuYXR0cigndHJhbnNmb3JtJywgXCJ0cmFuc2xhdGUoXCIreUF4aXNSZWN0LngrXCIsXCIreUF4aXNSZWN0LnkrXCIpXCIpXG5cbiAgcmV0dXJuIHtcbiAgICBzdmc6IHtcbiAgICAgIHNlbGVjdGlvbjogY29udGFpbmVyLFxuICAgICAgcmVjdDogc3ZnU3BhY2VcbiAgICB9LFxuICAgIHBsb3Q6IHtcbiAgICAgIHNlbGVjdGlvbjogcGxvdCxcbiAgICAgIHJlY3Q6IHBsb3RSZWN0XG4gICAgfSxcbiAgICB4QXhpczoge1xuICAgICAgc2VsZWN0aW9uOiB4QXhpcyxcbiAgICAgIHJlY3Q6IHhBeGlzUmVjdFxuICAgIH0sXG4gICAgeUF4aXM6IHtcbiAgICAgIHNlbGVjdGlvbjogeUF4aXMsXG4gICAgICByZWN0OiB5QXhpc1JlY3RcbiAgICB9LFxuICAgIGxlZ2VuZDoge1xuICAgICAgc2VsZWN0aW9uOiBsZWcsXG4gICAgICByZWN0OiBsZWdSZWN0XG4gICAgfVxuICB9XG59XG5cblxuXG4vKipcbiogQWRkcyBhIGNsaXAtcGF0aCByZWN0IGFuZCBiaW5kcyBpdCB0byBjb250YWluZXJcbiogQHBhcmFtIHtkMy5zZWxlY3Rpb259IGNvbnRhaW5lciBpbiB3aGljaCB0byBhZGQgdGhlIGNsaXAtcGF0aCBhbmQgdG8gd2hpY2ggdG8gYmluZCB0aGUgY2xpcGluZyBwYXRoIHRvXG4qIEBwYXJhbSB7T2JqZWN0fSByZWN0IHRoZSBjb29yZGluYXRlcyAoeCwgeSwgd2lkdGgsIGhlaWdodCkgb2YgdGhlIGNsaXAtcGF0aFxuKiBAcGFyYW0ge3N0cmluZ30gbmFtZXNwYWNlXG4qIEByZXR1cm5zIHtkMy5zZWxlY3Rpb259IG9mIHRoZSBjbGlwLXBhdGggcmVjdFxuKi9cbmV4cG9ydCBmdW5jdGlvbiBjcFJlY3QoY29udGFpbmVyLCByZWN0LCBuYW1lc3BhY2UpIHtcbiAgdmFyIGRlZnMgPSBzYWZlU2VsZWN0KGNvbnRhaW5lciwgJ2RlZnMnLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdkZWZpbml0aW9ucycpKVxuICB2YXIgY3AgPSBzYWZlU2VsZWN0KGRlZnMsICdjbGlwUGF0aCcsIGh5cGVuYXRlKG5hbWVzcGFjZSwgJ2NsaXAtcGF0aCcpKVxuICAuYXR0cignaWQnLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdjbGlwLXBhdGgnKSlcblxuICB2YXIgY3BSZWN0ID0gc2FmZVNlbGVjdChjcCwgJ3JlY3QnKVxuICAuYXR0cigneCcsIHJlY3QueClcbiAgLmF0dHIoJ3knLCByZWN0LnkpXG4gIC5hdHRyKCd3aWR0aCcsIHJlY3Qud2lkdGgpXG4gIC5hdHRyKCdoZWlnaHQnLCByZWN0LmhlaWdodClcblxuICBkZWZzLnJhaXNlKClcbiAgLy8gc2V0IGNsaXBwaW5nIHBhdGggdG8gY29udGFpbmVyXG4gIGNvbnRhaW5lci5hdHRyKCdjbGlwLXBhdGgnLCAndXJsKCMnKyBoeXBlbmF0ZShuYW1lc3BhY2UsICdjbGlwLXBhdGgnKSsnKScpXG5cbiAgcmV0dXJuIGNwUmVjdFxufVxuXG5cbi8qKlxuKiBBZGRzIGEgYmFja2dyb3VuZCByZWN0IHQgdG8gY29udGFpbmVyXG4qIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBjb250YWluZXIgaW4gd2hpY2ggdG8gYWRkIHRoZSBiYWNrZ3JvdW5kIHJlY3RhbmdsZVxuKiBAcGFyYW0ge09iamVjdH0gcmVjdCB0aGUgY29vcmRpbmF0ZXMgKHgsIHksIHdpZHRoLCBoZWlnaHQpIG9mIHRoZSBiYWNrZ3JvdW5kXG4qIEBwYXJhbSB7c3RyaW5nfSBmaWxsIHRoZSBjb2xvciBvZiB0aGUgYmFja2dyb3VuZFxuKiBAcmV0dXJucyB7ZDMuc2VsZWN0aW9ufSBvZiB0aGUgYmFja2dyb3VuZCBmaWxsXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIGJnUmVjdChjb250YWluZXIsIHJlY3QsIGZpbGwpIHtcbiAgcmV0dXJuIHNhZmVTZWxlY3QoY29udGFpbmVyLCAncmVjdCcsICdiZycpXG4gIC5hdHRyKCd4JywgcmVjdC54KVxuICAuYXR0cigneScsIHJlY3QueSlcbiAgLmF0dHIoJ3dpZHRoJywgcmVjdC53aWR0aClcbiAgLmF0dHIoJ2hlaWdodCcsIHJlY3QuaGVpZ2h0KVxuICAuYXR0cignZmlsbCcsIGZpbGwpXG59XG5cblxuLyoqXG4qIFNldHMgdXAgdGhlIGNvbnRhaW5lciBmb3IgbWFraW5nIGNoYXJ0IGVsZW1lbnRzLiBUaGlzIGluY2x1ZGVzIG1ha2luZ1xuKiBhIGNsaXAtcGF0aCByZWN0IGJvdW5kIHRvIHRoZSBwYXNzZWQgY29udGFpbmVyLCBhIGJhY2tncm91bmQgcmVjdCwgYW5kXG4qIGEgZyBlbGVtZW50IHdpdGggY2xhc3MgPG5hbWVzcGFjZT4tb2JqZWN0LWNvbnRhaW5lci5cbiogQHBhcmFtIHtkMy5zZWxlY3Rpb259IGNvbnRhaW5lciBpbiB3aGljaCB0byBhZGQgdGhlIGNsaXAtcGF0aCBhbmQgYmFja2dyb3VuZFxuKiBAcGFyYW0ge3N0cmluZ30gbmFtZXNwYWNlXG4qIEBwYXJhbSB7T2JqZWN0fSByZWN0IHRoZSBjb29yZGluYXRlcyAoeCwgeSwgd2lkdGgsIGhlaWdodCkgb2YgdGhlIGJhY2tncm91bmQgYW5kIGNsaXAtcGF0aFxuKiBAcGFyYW0ge3N0cmluZ30gZmlsbCB0aGUgY29sb3Igb2YgdGhlIGJhY2tncm91bmRcbiogQHJldHVybnMge2QzLnNlbGVjdGlvbn0gb2YgZy48bmFtZXNwYWNlPi1vYmplY3QtY29udGFpbmVyXG4qXG4qIEBzZWV7QGxpbmsgYmdSZWN0fVxuKiBAc2Vle0BsaW5rIGNwUmVjdH1cbiovXG5leHBvcnQgZnVuY3Rpb24gc2V0dXBDb250YWluZXIoc2VsZWN0aW9uLCBuYW1lc3BhY2UsIHJlY3QsIGZpbGwpIHtcbiAgLy8gdGhlIGNvbnRhaW5lciBmb3IgdGhyZWUgbWFpbiBpdGVtcywgYmcsIGRlZnMsIGFuZCBvYmplY3QtY29udGFpbmVyXG4gIHZhclxuICBjb250YWluZXIgPSBzYWZlU2VsZWN0KHNlbGVjdGlvbiwgJ2cnLCBuYW1lc3BhY2UpLFxuICBiZyA9IGJnUmVjdChjb250YWluZXIsIHJlY3QsIGZpbGwpLFxuICBjcCA9IGNwUmVjdChjb250YWluZXIsIHJlY3QsIG5hbWVzcGFjZSksXG4gIG9iamVjdENvbnRhaW5lciA9IHNhZmVTZWxlY3QoY29udGFpbmVyLCAnZycsIGh5cGVuYXRlKG5hbWVzcGFjZSwgJ29iamVjdC1jb250YWluZXInKSlcbiAgcmV0dXJuIG9iamVjdENvbnRhaW5lclxufVxuXG5cbi8qKlxuKiBkZXRlcm1pbmVzIHRoZSB3aWR0aCBvZiBhbiBvYmplY3QgZm9yIHRoZSBjYWxsaW5nIHBsb3R0aW5nIGZ1bmN0aW9uXG4qIEBwYXJhbSB7bnVtYmVyfSBmcmVlU3BhY2UgaG93IG11Y2ggc3BhY2UgaXMgYXZhbGlibGVcbiogQHBhcmFtIHtudW1iZXJ9IG51bWJlck9mT2JqZWN0cyBob3cgbWFueSBvYmplY3QgZG8gd2UgbmVlZFxuKiBAcGFyYW0ge251bWJlcn0gbWluT2JqZWN0V2lkdGggaG93IHNtYWxsIGFyZSB0aGVzZSBvYmplY3RzIGFsbG93ZWQgdG8gYmVcbiogQHBhcmFtIHtudW1iZXJ9IG1heE9iamVjdFdpZHRoIGhvdyBsYXJnZSBhcmUgdGhlc2Ugb2JqZWN0IGFsbG93ZWQgdG8gYmVcbiogQHBhcmFtIHtudW1iZXJ9IHNpemVPZlNwYWNlciBwZXJjZW50IG9mIGZyZWVTcGFjZSB0aGF0IGEgc2luZ2xlIHNwYWNlciBzaG91bGQgdGFrZSB1cCAobmVlZCBudW1iZXJPZk9iamVjdHMgLSAxIHNwYWNlcnMpXG4qIEBwYXJhbSB7Ym9vbGVhbn0gb3ZlcmZsb3dRIGNhbiB3ZSBnbyBiZXlvbmQgYWxsb3RlZCBzcGFjZVxuKiBAcmV0dXJucyB7bnVtYmVyfSBob3cgbGFyZ2Ugb2JqZWN0IHNob3VsZCBiZVxuKiBmdW5jdGlvbiB0cmllcyB0byBrZWVwIG9iamVjdCB3aXRoaW4gbWluIC8gbWF4IHdpZHRoLCBidXQgd2lsIGRlZmF1bHQgdG9cbiogNWUtMTAgKHNtYWxsZXN0IGNvbnNpc3Rlbmx5IHZpc2libGUgYnkgc3ZnIHNpemUgb2YgZWxlbWVudCkgaWYgb3ZlcmZsb3dRIGlzIGZhbHNlXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3QoZnJlZVNwYWNlLCBudW1iZXJPZk9iamVjdHMsIG1pbk9iamVjdFdpZHRoLCBtYXhPYmplY3RXaWR0aCwgc2l6ZU9mU3BhY2VyLCBvdmVyZmxvd1EpIHtcbiAgdmFyIHNpemVPZlNwYWNlciA9XG4gIHNpemVPZlNwYWNlciA9PSAwIHx8IHNpemVPZlNwYWNlciA+IDFcbiAgPyBzaXplT2ZTcGFjZXJcbiAgOiBmcmVlU3BhY2UgKiBzaXplT2ZTcGFjZXJcblxuICB2YXIgbnVtYmVyT2ZTcGFjZXJzID0gbnVtYmVyT2ZPYmplY3RzIC0gMVxuICB2YXIgc3BhY2VUYWtlbkJ5U3BhY2VycyA9IG51bWJlck9mU3BhY2VycyAqIHNpemVPZlNwYWNlclxuICB2YXIgcmVtYWluaW5nU3BhY2UgPSBmcmVlU3BhY2UgLSBzcGFjZVRha2VuQnlTcGFjZXJzXG4gIHJlbWFpbmluZ1NwYWNlID0gcmVtYWluaW5nU3BhY2UgPCAwID8gMCA6IHJlbWFpbmluZ1NwYWNlXG4gIHZhciBvYmplY3RXaWR0aCA9IHJlbWFpbmluZ1NwYWNlIC8gbnVtYmVyT2ZPYmplY3RzXG5cbiAgaWYgKCBvdmVyZmxvd1EgJiYgbWluT2JqZWN0V2lkdGggIT0gdW5kZWZpbmVkICYmIG9iamVjdFdpZHRoIDwgbWluT2JqZWN0V2lkdGggKSB7IG9iamVjdFdpZHRoID0gbWluT2JqZWN0V2lkdGggfVxuICAvLyBpZiAoIG1heE9iamVjdFdpZHRoICE9IHVuZGVmaW5lZCAmJiBvYmplY3RXaWR0aCA+IG1heE9iamVjdFdpZHRoICkgeyBvYmplY3RXaWR0aCA9IG1heE9iamVjdFdpZHRoIH1cbiAgaWYgKCBvdmVyZmxvd1EgJiYgbWF4T2JqZWN0V2lkdGggIT0gdW5kZWZpbmVkICYmIG9iamVjdFdpZHRoIDwgbWF4T2JqZWN0V2lkdGggKSB7IG9iamVjdFdpZHRoID0gbWF4T2JqZWN0V2lkdGggfVxuICByZXR1cm4gTWF0aC5tYXgob2JqZWN0V2lkdGgsIDVlLTEwKVxufVxuXG4vKipcbiogQHBhcmFtIHtBcnJheVtdfSBkYXRhIGxpc3QgZGF0YSAoY2FuIGJlIG5lc3RlZCkuIElmIG5lc3RlZCB3aWxsIGNyZWF0ZSBtb3JlIGNvbXBsZXggc3BhY2VyIHNpemVcbiogQHBhcmFtIHtudW1iZXJ9IGZyZWVTcGFjZSBob3cgbXVjaCBzcGFjZSBpcyBhdmFsaWJsZVxuKiBAcGFyYW0ge251bWJlcn0gb2JqZWN0V2lkdGggQHNlZXtAbGluayBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0fVxuKiBAcGFyYW0ge251bWJlcn0gbnVtYmVyT2ZPYmplY3RzIGhvdyBtYW55IG9iamVjdCBkbyB3ZSBuZWVkXG4qIEBwYXJhbSB7bnVtYmVyfSBiYXNlU3BhY2VyU2l6ZSBwZXJjZW50IG9mIGZyZWVTcGFjZSB0aGF0IGEgc2luZ2xlIHNwYWNlciBzaG91bGQgdGFrZSB1cCAobmVlZCBudW1iZXJPZk9iamVjdHMgLSAxIHNwYWNlcnMpXG4qIEBwYXJhbSB7Ym9vbGVhbn0gb3ZlcmZsb3dRIGNhbiB3ZSBnbyBiZXlvbmQgYWxsb3RlZCBzcGFjZVxuKiBAcmV0dXJucyB7bnVtYmVyfSByZXR1cm5zIHNpemUgdGhhdCBzcGFjZXIgc2hvdWxkIGJlIGF0IGxldmVsPTBcbiovXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcihkYXRhLCBmcmVlU3BhY2UsIG9iamVjdFdpZHRoLCBudW1iZXJPZk9iamVjdHMsIGJhc2VTcGFjZXJTaXplLCBvdmVyZmxvd1EpIHtcbiAgaWYgKG92ZXJmbG93USkge1xuICAgIC8vIHZhciBsaW1pdGVkTnVtYmVyT2ZPYmplY3RzID0gbnVtYmVyT2ZPYmplY3RzID4gNiA/IDYgOiBudW1iZXJPZk9iamVjdHNcbiAgICAvLyB2YXIgc3BhY2VMZWZ0ID0gZnJlZVNwYWNlIC0gbGltaXRlZE51bWJlck9mT2JqZWN0cyAqIG9iamVjdFdpZHRoXG4gICAgLy8gcmV0dXJuIHNwYWNlTGVmdCAvIChsaW1pdGVkTnVtYmVyT2ZPYmplY3RzIC0gMSlcbiAgICByZXR1cm4gZnJlZVNwYWNlICogYmFzZVNwYWNlclNpemVcbiAgfVxuICB2YXIgc3BhY2Vyc0F0RWFjaExldmVsID0gc3BhY2Vyc05lZWRlZEF0RWFjaExldmVsKGRhdGEpXG4gIHZhciB0b3RhbFNwYWNlclBlcmNlbnQgPSB0b3RhbChzcGFjZXJzQXRFYWNoTGV2ZWwubWFwKGZ1bmN0aW9uKGUsIGkpIHtyZXR1cm4gZSAqIDEgLyAoaSsxKX0pKVxuICB2YXIgYmFzZVNwYWNlclNpemUgPSAoZnJlZVNwYWNlIC0gKG9iamVjdFdpZHRoICogbnVtYmVyT2ZPYmplY3RzKSkgLyB0b3RhbFNwYWNlclBlcmNlbnRcbiAgLy8gY29uc29sZS5sb2coZnJlZVNwYWNlLCBvYmplY3RXaWR0aCwgbnVtYmVyT2ZPYmplY3RzLCB0b3RhbFNwYWNlclBlcmNlbnQpXG4gIC8vIGNvbnNvbGUubG9nKHRvdGFsU3BhY2VyUGVyY2VudCwgYmFzZVNwYWNlclNpemUsIHRvdGFsU3BhY2VyUGVyY2VudCAqIGJhc2VTcGFjZXJTaXplKVxuICByZXR1cm4gaXNOYU4oYmFzZVNwYWNlclNpemUpID8gMCA6IGJhc2VTcGFjZXJTaXplXG59XG5cblxuLyoqXG4qIENhbGN1bGF0ZXMgbnVtYmVyIG9mIHNwYWNlcnMgbmVlZGVkIHRvIHNlcGVyYXRlIGVsZW1lbnRzIGF0IGVhY2ggbGV2ZWwuXG4qIEBwYXJhbSB7QXJyYXlbXX0gYXJyYXkgbGlzdCBkYXRhIChjYW4gYmUgbmVzdGVkKS4gSWYgbmVzdGVkIHdpbGwgY3JlYXRlIG1vcmUgY29tcGxleCBzcGFjZXIgc2l6ZVxuKiBAcGFyYW0ge251bWJlcn0gW2xldmVsPTBdIGN1cnJlbnQgbGV2ZWwsIHVzZWQgaW4gcmVjdXNyaW9uXG4qIEBwYXJhbSB7QXJyYXl9IFtsZXZlbERhdGE9W11dIGhvdyBtYW55IHNwYWNlcnMgbmVlZGVkIGF0IGEgZ2l2ZW4gbGV2ZWxcbiogQHJldHVybnMge0FycmF5fSBsZXZlbERhdGFcbipcbiogQGV4YW1wbGVcbiogYXJyYXkgPSBbWzEsMl0sIFszLDRdXVxuKiAvLyByZXR1cm5zIFsxLCAyXVxuKiBhcyBhdCBsZXZlbD0wIHRoZSBvbmx5IHNwYWNlciBuZWVkZWQgaXMgYmV0d2VlbiBbMSwyXSBhbmQgWzMsNF1cbiogYW5kIGF0IGxldmVsPTEgdGhlIG9ubHkgdHdvIHNwYWNlcnMgbmVlZGVkIGlzIGJldHdlZW4gMSBhbmQgMiBhcyB3ZWxsIGFzXG4qIDMgYW5kIDQgc2luY2UgdGhlIHNwYWNlciBiZXR3ZWVuIDIgYW5kIDMgaXMgaGFuZGxlZCBhdCBsZXZlbD0wXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIHNwYWNlcnNOZWVkZWRBdEVhY2hMZXZlbCAoYXJyYXksIGxldmVsLCBsZXZlbERhdGEgKSB7XG4gIGlmICggbGV2ZWwgPT0gdW5kZWZpbmVkICkgeyBsZXZlbCA9IDA7ICB9IGVsc2UgeyBsZXZlbCArPSAxIH1cbiAgaWYgKCBsZXZlbERhdGEgPT0gdW5kZWZpbmVkICkgeyBsZXZlbERhdGEgPSBbXTsgfVxuICBpZiAoIGxldmVsID49IGxldmVsRGF0YS5sZW5ndGggKSB7IGxldmVsRGF0YS5wdXNoKGFycmF5Lmxlbmd0aCAtIDEpIH1cbiAgZWxzZSB7IGxldmVsRGF0YVtsZXZlbF0gKz0gYXJyYXkubGVuZ3RoIC0gMSB9XG4gIGFycmF5Lm1hcChmdW5jdGlvbihlLCBpKSB7IGlmIChBcnJheS5pc0FycmF5KGUpKSB7IHNwYWNlcnNOZWVkZWRBdEVhY2hMZXZlbChlLCBsZXZlbCwgbGV2ZWxEYXRhKSB9IH0gKVxuICByZXR1cm4gbGV2ZWxEYXRhXG59XG5cblxuXG5cbi8qKlxuKiBEcmF3cyBhIHdoaXNrZXIgZm9yIEBzZWV7QGxpbmsgYm94d2hpc2tlcn1cbiogQHBhcmFtIHtib29sZWFufSBkaXIgZGlyZWN0aW9uIHRvIGRyYXcgd2hpc2tlciwgc2hvdWxkIGJlIGVpdGhlciB0cnVlICh1cCwgdG9wKSBvciBmYWxzZSAoZG93biBvciBib3R0b20pXG4qIEBwYXJhbSB7bnVtYmVyfSB4IHN0YXJ0aW5nIHggY29vcmRpbmF0ZSBpbiB3aGljaCB0byBkcmF3IHdoaXNrZXJcbiogQHBhcmFtIHtudW1iZXJ9IHkgc3RhcnRpbmcgeSBjb29yZGluYXRlIGluIHdoaWNoIHRvIGRyYXcgd2hpc2tlclxuKiBAcGFyYW0ge251bWJlcn0gdyB3aWR0aCBvZiBzcGFjZSBpbiB3aGljaCB0byBkcmF3IHdoaXNrZXJcbiogQHBhcmFtIHtudW1iZXJ9IGggaGVpZ2h0IG9mIHNwYWNlIGluIHdoaWNoIHRvIGRyYXcgd2hpc2tlclxuKiBAcGFyYW0ge251bWJlcn0gcGVyIHBlcmNlbnRhZ2Ugb2YgdyBvciBoIChkZXBlbmRzIG9uIG8pIHRvIG1ha2Ugd2hpc2tlclxuKiBAcGFyYW0ge2Jvb2xlYW59IG8gb3JpZW50YXRpb24sIHRydWUgaXMgaG9yaXpvbnRhbCBhbmQgZmFsc2UgaXMgdmVydGljYWxcbiogQHJldHVybnMge3N0cmluZ30gcmVwcmVzZW50aW5nIHRoZSBzdmcgcGF0aCAoaS5lLiB0aGUgZCBhdHRyaWJ1dGUgZm9yIGEgcGF0aCB0YWcpXG4qL1xuZXhwb3J0IGZ1bmN0aW9uIHdoaXNrZXJQYXRoKGRpciwgeCwgeSwgdywgaCwgcGVyLCBvKSB7XG4gIC8vIGQgPSBkaXJlY3Rpb24gKHRydWUgaXMgdXApLCBwID0gcGVyY2VudCB3aWR0aFxuICBpZiAoZGlyID09ICd1cCcgfHwgZGlyID09ICd0b3AnIHx8IGRpciA9PSB0cnVlKSB7ZGlyID0gdHJ1ZX1cbiAgaWYgKGRpciA9PSAnZG93bicgfHwgZGlyID09ICdib3R0b20nIHx8IGRpciA9PSBmYWxzZSkge2RpciA9IGZhbHNlfVxuICBvID0gbyA9PSB1bmRlZmluZWQgPyAnaG9yaXpvbnRhbCcgOiBvXG4gIHBlciA9IHBlciA9PSB1bmRlZmluZWQgPyAxIDogcGVyXG4gIGlmIChvICE9IFwiaG9yaXpvbnRhbFwiKSB7XG4gICAgdmFyIGhoID0gaCAqIHBlciAsXG4gICAgdyA9IGRpciA/IHcgOiAtdyAsXG4gICAgYSA9IGRpciA/IHggKyB3IDogeCAsXG4gICAgYiA9IGRpciA/IHggOiB4ICsgdyAsXG4gICAgYyA9IGRpciA/IGEgOiBiXG4gICAgcCA9IFwiTSBcIiArIGEgKyAnICcgKyAoICAgICBoIC8gMiAgICAgICkgKyAnICdcbiAgICAgICsgJ0wgJyArIGIgKyAnICcgKyAoICAgICBoIC8gMiAgICAgICkgKyAnICdcbiAgICAgICsgJ00gJyArIGMgKyAnICcgKyAoIGggLyAyIC0gaGggLyAyICkgKyAnICdcbiAgICAgICsgJ0wgJyArIGMgKyAnICcgKyAoIGggLyAyICsgaGggLyAyICkgKyAnICdcblxuICAgIHJldHVybiBwXG4gIH1cbiAgdmFyIHd3ID0gdyAqIHBlcixcbiAgYSA9IGRpciA/IHkgKyBoIDogeSAgLFxuICBiID0gZGlyID8geSA6IHkgKyBoICAsXG4gIHAgPSBcIk0gXCIgKyAoICB3IC8gMiAgKSArICcgJyArIGEgKyAnICcgLy8gc3RyYWlnaHQgbGluZSBwYXJ0XG4gICAgKyAnTCAnICsgKCAgdyAvIDIgICkgKyAnICcgKyBiICsgJyAnIC8vIHN0cmFpZ2h0IGxpbmUgcGFydFxuICAgICsgJ2ggJyArICggLXd3IC8gMiApICsgJyAnICsgMCArICcgJyAvLyBob3Jpem9udGFsIGxpbmUgcGFydFxuICAgICsgJ2ggJyArICggICAgd3cgICApICsgJyAnICsgMCArICcgJ1xuICByZXR1cm4gcFxufVxuXG5cblxuXG5cblxuXG5cblxuXG5cblxuXG5leHBvcnQgZnVuY3Rpb24gcmVzaXplRGVib3VuY2UoZiwgd2FpdCkge1xuICB2YXIgcmVzaXplID0gZGVib3VuY2UoZnVuY3Rpb24oKXtmKCl9LHdhaXQpXG4gIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCByZXNpemUpXG59XG5cblxuXG5mdW5jdGlvbiBkZWJvdW5jZShmdW5jLCB3YWl0LCBpbW1lZGlhdGUpIHtcbiAgdmFyIHRpbWVvdXQ7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgY29udGV4dCA9IHRoaXMsIGFyZ3MgPSBhcmd1bWVudHM7XG4gICAgICAgIHZhciBsYXRlciA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgdGltZW91dCA9IG51bGw7XG4gICAgICAgICAgICBpZiAoIWltbWVkaWF0ZSkgZnVuYy5hcHBseShjb250ZXh0LCBhcmdzKTtcbiAgICAgICAgfTtcbiAgICAgICAgdmFyIGNhbGxOb3cgPSBpbW1lZGlhdGUgJiYgIXRpbWVvdXQ7XG4gICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0KTtcbiAgICAgICAgdGltZW91dCA9IHNldFRpbWVvdXQobGF0ZXIsIHdhaXQpO1xuICAgICAgICBpZiAoY2FsbE5vdykgZnVuYy5hcHBseShjb250ZXh0LCBhcmdzKTtcbiAgICB9O1xufVxuIiwiaW1wb3J0IHtsb2csIHdhcm4sIGVycm9yLCBpbmZvfSBmcm9tICcuL3V0aWxzJztcbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU1BBQ0VHUk9VUElORyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKipcbiAqIFByb2R1Y2VzIGEgZnVuY3Rpb24gZm9yIHNwYWNpbmcgb2JqZWN0cyBieSBhbiBhcmJpdHJhcmx5IGNvbXBsZXggZ3JvdXBpbmdcbiAqIEByZXR1cm5zIHtyZWN1cnNpdmVseVBvc2l0aW9ufSB0aGUgZnVuY3Rpb24gZm9yIG1vdmluZyB0aGUgb2JqZWN0c1xuICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIjcmVjdXJzaXZlbHlQb3NpdGlvbn0pXG4gKiBAbmFtZXNwYWNlIGdyb3VwaW5nU3BhY2VyXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBncm91cGluZ1NwYWNlcigpIHtcbiAgdmFyXG4gIC8qQHZhciB7Ym9vbGVhbn0gaG9yaXpvbnRhbFEgQGRlZmF1bHQqL1xuXG4gIC8qKlxuICAqIFdoZXRoZXIgb3Igbm90IHRvIHNwYWNlIG9iamVjdHMgaG9yaXpvbnRhbGx5IG9yIHZlcnRpY2FsbHkuXG4gICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIuaG9yaXpvbnRhbFF9KVxuICAqIEBwYXJhbSB7Ym9vbGVhbn0gW2hvcml6b250YWxRPXRydWVdXG4gICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyI1xuICAqIEBpbnN0YW5jZVxuICAqL1xuICBob3Jpem9udGFsUSA9IHRydWUsXG4gIC8qKlxuICAqIFRoZSBzY2FsZSB0byB1c2UgdG8gcG9zaXRpb24gZWxlbWVudHMgaWYge0BsaW5rIGdyb3VwaW5nU3BhY2VyI21vdmVieX09XCJzdHJpbmdcIlxuICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyLnNjYWxlfSlcbiAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbc2NhbGU9ZDMuc2NhbGVMaW5lYXIoKV1cbiAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICogQGluc3RhbmNlXG4gICovXG4gIHNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKSxcbiAgLyoqXG4gICogSG93IGVsZW1lbnRzIGluIHRoZSBjb21wbGV4IGdyb3VwaW5nIHNob3VsZCBiZSBtb3ZlZCBvdmVyIGJ5LlxuICAqIEJ5IGRlZmF1bHQsIG1vdmVieT1cImNhdGVnb3J5XCIsIHdoaWNoIG1vdmVzIG9iamVjdHMgYnkgdGhlIGNvbXBsZXggZ3JvdXBpbmdcbiAgKiBCdXQgb2JqZWN0cyBjYW4gYWxzbyBiZSBtb3ZlZCBvdmVyIGJ5IHNjYWxlLlxuICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyLm1vdmVieX0pXG4gICogQHBhcmFtIHtzdHJpbmd9IFttb3ZlYnk9XCJjYXRlZ29yeVwiXVxuICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlciNcbiAgKiBAaW5zdGFuY2VcbiAgKi9cbiAgbW92ZWJ5ID0gJ2NhdGVnb3J5JyxcbiAgLyoqXG4gICogSG93IG1hbnkgb2JqZWN0cyBhcmUgdGhlcmUgaW4gdG90YWxcbiAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlci5udW1iZXJPZk9iamVjdHN9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbnVtYmVyT2ZPYmplY3RzPW5vbmVdXG4gICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyI1xuICAqIEBpbnN0YW5jZVxuICAqL1xuICBudW1iZXJPZk9iamVjdHMsXG4gIC8qKlxuICAqIFRoZSBjbGFzcyBnaXZlbiB0byBhbiBuZXN0ZWQgPGc+IHRhZyB3aG9zZSBwYXJlbnQocykgaGF2ZSB0aGUgY29ycmVjdCB0cmFuc2l0aW9uXG4gICogcHJvcGVydGllc1xuICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyLm51bWJlck9mT2JqZWN0c30pXG4gICogQHBhcmFtIHtzdHJpbmd9IFtudW1iZXJPZk9iamVjdHM9J2Qzc20tZ3JvdXBwZWQtaXRlbSddXG4gICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyI1xuICAqIEBpbnN0YW5jZVxuICAqL1xuICBvYmplY3RDbGFzcyA9ICdkM3NtLWdyb3VwcGVkLWl0ZW0nLFxuICAvKipcbiAgKiBUaGUgc2l6ZSBvZiB0aGUgb2JqZWN0cyBiZWluZyBwb3NpdGlvbmVkXG4gICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIub2JqZWN0U2l6ZX0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvYmplY3RTaXplPW5vbmVdXG4gICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyI1xuICAqIEBpbnN0YW5jZVxuICAqL1xuICBvYmplY3RTaXplLFxuICAvKipcbiAgKiBUaGUgc2l6ZSBvZiB0aGUgdW4tbmVzdGVkIHNwYWNlciBiZXR3ZWVuIG9iamVjdHNcbiAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlci5zcGFjZXJTaXplfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlclNpemU9bm9uZV1cbiAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICogQGluc3RhbmNlXG4gICovXG4gIHNwYWNlclNpemUsXG4gIC8qKlxuICAqIFRoZSBkdXJhdGlvbiBvZiB0cmFuc2l0aW9ucyBpbiBtc1xuICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyLnRyYW5zaXRpb25EdXJhdGlvbn0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFt0cmFuc2l0aW9uRHVyYXRpb249MTAwMF1cbiAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICogQGluc3RhbmNlXG4gICovXG4gIHRyYW5zaXRpb25EdXJhdGlvbiA9IDEwMDAsXG4gIC8qKlxuICAqIFRoZSBlYXNlIGZ1bmN0aW9uIGZvciB0aGUgdHJhbnNpdGlvbnNcbiAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlci5lYXNlRnVuY30pXG4gICogQHBhcmFtIHtkMy5lYXNlfSBbZWFzZUZ1bmM9ZDMuZWFzZVNpbl1cbiAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICogQGluc3RhbmNlXG4gICovXG4gIGVhc2VGdW5jID0gZDMuZWFzZVNpbixcbiAgLyoqXG4gICogVGhlIG5hbWVzcGFjZSBmb3IgdGhlIG9iamVjdHMgYmVpbmcgbW92ZWRcbiAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlci5uYW1lc3BhY2V9KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbbmFtZXNwYWNlPSdzcGFjZXInXVxuICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlciNcbiAgKiBAaW5zdGFuY2VcbiAgKi9cbiAgbmFtZXNwYWNlID0gJ3NwYWNlcicsXG4gIC8qKlxuICAqIFRoZSBhbmltYXRpb24gZm9yIG5ldyBvYmplY3RzIGJlaW5nIGFkZGVkXG4gICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIuZW50ZXJGdW5jdGlvbn0pXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gZW50ZXJGdW5jdGlvblxuICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlciNcbiAgKiBAaW5zdGFuY2VcbiAgKiBAZXhhbXBsZVxuICAqIC8vIGJ5IGRlZmF1bHRcbiAgKiBmdW5jdGlvbihuZXdPYmplY3RTZWxlY3Rpb24pIHtcbiAgKiAgbmV3T2JqZWN0U2VsZWN0aW9uLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpe1xuICAqICAgIHZhclxuICAqICAgIHggPSBob3Jpem9udGFsUSA/IG9iamVjdFNpemUgKiBudW1iZXJPZk9iamVjdHMgKyBzcGFjZXJTaXplICogKG51bWJlck9mT2JqZWN0cyAtIDEpIDogMCxcbiAgKiAgICB5ID0gIWhvcml6b250YWxRID8gb2JqZWN0U2l6ZSAqIG51bWJlck9mT2JqZWN0cyArIHNwYWNlclNpemUgKiAobnVtYmVyT2ZPYmplY3RzIC0gMSkgOiAwLFxuICAqICAgIHQgPSAndHJhbnNsYXRlKCcreCsnLCcreSsnKSdcbiAgKiAgICByZXR1cm4gdFxuICAqICB9KVxuICAqIH1cbiAgKi9cbiAgZW50ZXJGdW5jdGlvbiA9IGZ1bmN0aW9uKGN1cikge1xuICAgIGN1ci5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhclxuICAgICAgLy8geCA9IGhvcml6b250YWxRID8gb2JqZWN0U2l6ZSAqIG51bWJlck9mT2JqZWN0cyArIHNwYWNlclNpemUgKiAobnVtYmVyT2ZPYmplY3RzIC0gMSkgOiAwLFxuICAgICAgLy8geSA9ICFob3Jpem9udGFsUSA/IG9iamVjdFNpemUgKiBudW1iZXJPZk9iamVjdHMgKyBzcGFjZXJTaXplICogKG51bWJlck9mT2JqZWN0cyAtIDEpIDogMCxcbiAgICAgIHggPSBob3Jpem9udGFsUSA/IHdpbmRvdy5vdXRlcldpZHRoIDogMCxcbiAgICAgIHkgPSAhaG9yaXpvbnRhbFEgPyB3aW5kb3cub3V0ZXJXaWR0aCA6IDAsXG4gICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAvLyBpZih5ID09IHVuZGVmaW5lZCkge2NvbnNvbGUubG9nKGN1ci5ub2RlKCksIHksIGQpfVxuICAgICAgcmV0dXJuIHRcbiAgICB9KVxuICB9LFxuICAvKipcbiAgKiBUaGUgYW5pbWF0aW9uIGZvciBvbGQgb2JqZWN0cyBiZWluZyByZW1vdmVkXG4gICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIuZXhpdEZ1bmN0aW9ufSlcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBleGl0RnVuY3Rpb25cbiAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICogQGluc3RhbmNlXG4gICogQGV4YW1wbGVcbiAgKiAvLyBieSBkZWZhdWx0XG4gICogb2xkT2JqZWN0U2VsZWN0aW9uLnRyYW5zaXRpb24oKS5kdXJhdGlvbih0cmFuc2l0aW9uRHVyYXRpb24pLmVhc2UoZWFzZUZ1bmMpXG4gICogLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpe1xuICAqICAgICB2YXJcbiAgKiAgIHggPSBob3Jpem9udGFsUSA/IG9iamVjdFNpemUgKiBudW1iZXJPZk9iamVjdHMgKyBzcGFjZXJTaXplICogKG51bWJlck9mT2JqZWN0cyAtIDEpIDogMCxcbiAgKiAgIHkgPSAhaG9yaXpvbnRhbFEgPyBvYmplY3RTaXplICogbnVtYmVyT2ZPYmplY3RzICsgc3BhY2VyU2l6ZSAqIChudW1iZXJPZk9iamVjdHMgLSAxKSA6IDAsXG4gICogICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICogICByZXR1cm4gdFxuICAqIH0pLnJlbW92ZSgpXG4gICovXG4gIGV4aXRGdW5jdGlvbiA9IGZ1bmN0aW9uKGN1cil7XG4gICAgbG9nKFwiZ3JvdXBpbmdTcGFjZXJcIiwgXCJleGl0aW5nIHdpdGhcIiwge2N1cnJlbnQ6IGN1ciwgY3VycmVudE5vZGU6IGN1ci5ub2RlKCl9KVxuICAgIGN1ci5zZWxlY3RBbGwoJ2cnKS5jbGFzc2VkKCd0by1yZW1vdmUnLCB0cnVlKVxuXG4gICAgY3VyLnRyYW5zaXRpb24oKS5kdXJhdGlvbih0cmFuc2l0aW9uRHVyYXRpb24qMC45KS5lYXNlKGVhc2VGdW5jKVxuICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhclxuICAgICAgLy8geCA9IGhvcml6b250YWxRID8gb2JqZWN0U2l6ZSAqIG51bWJlck9mT2JqZWN0cyArIHNwYWNlclNpemUgKiAobnVtYmVyT2ZPYmplY3RzIC0gMSkgOiAwLFxuICAgICAgLy8geSA9ICFob3Jpem9udGFsUSA/IG9iamVjdFNpemUgKiBudW1iZXJPZk9iamVjdHMgKyBzcGFjZXJTaXplICogKG51bWJlck9mT2JqZWN0cyAtIDEpIDogMCxcbiAgICAgIHggPSBob3Jpem9udGFsUSA/IHdpbmRvdy5vdXRlcldpZHRoIDogMCxcbiAgICAgIHkgPSAhaG9yaXpvbnRhbFEgPyB3aW5kb3cub3V0ZXJXaWR0aCA6IDAsXG4gICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAvLyBpZih5ID09IHVuZGVmaW5lZCkge2NvbnNvbGUubG9nKGN1ci5ub2RlKCksIHksIGQpfVxuICAgICAgcmV0dXJuIHRcbiAgICB9KS5yZW1vdmUoKVxuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIGhvcml6b250YWxRICh3aGV0aGVyIG9yIG5vdCB0byBzcGFjZSBvYmplY3RzIGhvcml6b250YWxseSBvciB2ZXJ0aWNhbGx5KS5cbiAgICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIjaG9yaXpvbnRhbFF9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXJcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcmVjdXJzaXZlbHlQb3NpdGlvbi5ob3Jpem9udGFsUSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoaG9yaXpvbnRhbFEgPSBfLCByZWN1cnNpdmVseVBvc2l0aW9uKSA6IGhvcml6b250YWxRIH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzY2FsZSB0byB1c2UgdG8gcG9zaXRpb24gZWxlbWVudHMgaWYge0BsaW5rIGdyb3VwaW5nU3BhY2VyI21vdmVieX09XCJzdHJpbmdcIlxuICAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlciNzY2FsZX0pXG4gICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtncm91cGluZ1NwYWNlciB8IGQzLnNjYWxlfVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXJcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcmVjdXJzaXZlbHlQb3NpdGlvbi5zY2FsZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGUgPSBfLCByZWN1cnNpdmVseVBvc2l0aW9uKSA6IHNjYWxlIH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIG1vdmVieSAod2hldGhlciBvciBub3QgdG8gbW92ZSBieSBzY2FsZSBvciBieSBncm91cGluZykuXG4gICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyI21vdmVieX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Z3JvdXBpbmdTcGFjZXIgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlclxuICAgKiBAc3RhdGljXG4gICAqL1xuICByZWN1cnNpdmVseVBvc2l0aW9uLm1vdmVieSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobW92ZWJ5ID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiBtb3ZlYnkgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgbnVtYmVyT2ZPYmplY3RzLlxuICAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlciNudW1iZXJPZk9iamVjdHN9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXJcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcmVjdXJzaXZlbHlQb3NpdGlvbi5udW1iZXJPZk9iamVjdHMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG51bWJlck9mT2JqZWN0cyA9IF8sIHJlY3Vyc2l2ZWx5UG9zaXRpb24pIDogbnVtYmVyT2ZPYmplY3RzIH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RDbGFzcyAod2lsbCBiZSBhcHBsaWVkIHRvIDxnPiBlbGVtZW50cykuXG4gICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyI29iamVjdENsYXNzfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtncm91cGluZ1NwYWNlciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyXG4gICAqIEBzdGF0aWNcbiAgICovXG4gIHJlY3Vyc2l2ZWx5UG9zaXRpb24ub2JqZWN0Q2xhc3MgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdENsYXNzID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiBvYmplY3RDbGFzcyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0U2l6ZS5cbiAgICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIjb2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Z3JvdXBpbmdTcGFjZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlclxuICAgKiBAc3RhdGljXG4gICAqL1xuICByZWN1cnNpdmVseVBvc2l0aW9uLm9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNpemUgPSBfLCByZWN1cnNpdmVseVBvc2l0aW9uKSA6IG9iamVjdFNpemUgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHNwYWNlclNpemUuXG4gICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyI3NwYWNlclNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXJcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcmVjdXJzaXZlbHlQb3NpdGlvbi5zcGFjZXJTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZXJTaXplID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiBzcGFjZXJTaXplIH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0cmFuc2l0aW9uRHVyYXRpb24uXG4gICAqIChzZWUge0BsaW5rIGdyb3VwaW5nU3BhY2VyI3RyYW5zaXRpb25EdXJhdGlvbn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Z3JvdXBpbmdTcGFjZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlclxuICAgKiBAc3RhdGljXG4gICAqL1xuICByZWN1cnNpdmVseVBvc2l0aW9uLnRyYW5zaXRpb25EdXJhdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodHJhbnNpdGlvbkR1cmF0aW9uID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiB0cmFuc2l0aW9uRHVyYXRpb24gfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGVhc2VGdW5jLlxuICAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlciNlYXNlRnVuY30pXG4gICAqIEBwYXJhbSB7ZDMuZWFzZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgZDMuZWFzZX1cbiAgICogQG1lbWJlcm9mIGdyb3VwaW5nU3BhY2VyXG4gICAqIEBzdGF0aWNcbiAgICovXG4gIHJlY3Vyc2l2ZWx5UG9zaXRpb24uZWFzZUZ1bmMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGVhc2VGdW5jID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiBlYXNlRnVuYyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbmFtZXNwYWNlLlxuICAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlciNuYW1lc3BhY2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXJcbiAgICogQHN0YXRpY1xuICAgKi9cbiAgcmVjdXJzaXZlbHlQb3NpdGlvbi5uYW1lc3BhY2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG5hbWVzcGFjZSA9IF8sIHJlY3Vyc2l2ZWx5UG9zaXRpb24pIDogbmFtZXNwYWNlIH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBlbnRlckZ1bmN0aW9uLlxuICAgKiAoc2VlIHtAbGluayBncm91cGluZ1NwYWNlciNlbnRlckZ1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlclxuICAgKiBAc3RhdGljXG4gICAqL1xuICByZWN1cnNpdmVseVBvc2l0aW9uLmVudGVyRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGVudGVyRnVuY3Rpb24gPSBfLCByZWN1cnNpdmVseVBvc2l0aW9uKSA6IGVudGVyRnVuY3Rpb24gfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGV4aXRGdW5jdGlvbi5cbiAgICogKHNlZSB7QGxpbmsgZ3JvdXBpbmdTcGFjZXIjZXhpdEZ1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2dyb3VwaW5nU3BhY2VyIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBncm91cGluZ1NwYWNlclxuICAgKiBAc3RhdGljXG4gICAqL1xuICByZWN1cnNpdmVseVBvc2l0aW9uLmV4aXRGdW5jdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZXhpdEZ1bmN0aW9uID0gXywgcmVjdXJzaXZlbHlQb3NpdGlvbikgOiBleGl0RnVuY3Rpb24gfVxuXG5cbiAgLyoqXG4gICAqIHJlY3Vyc2l2ZWx5IHBvc2l0aW9uIHRoZSBvYmplY3RzIGluc2lkZSBvZiB0aGUgc2VsZWN0aW9uLlxuICAgKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gc2VsZWN0aW9uXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBkYXRhXG4gICAqIEBwYXJhbSB7bGV2ZWx9IFtsZXZlbD0wXSByZWN1cnNpb24gZGVwdGhcbiAgICogQHJldHVybnMge251bWJlcn0gKGhvdyBtdWNoIHRvIG1vdmUgbmV4dCBlbGVtZW50KVxuICAgKiBAbWVtYmVyb2YgZ3JvdXBpbmdTcGFjZXIjXG4gICAqL1xuICBmdW5jdGlvbiByZWN1cnNpdmVseVBvc2l0aW9uKHNlbGVjdGlvbiwgZGF0YSwgbGV2ZWwpIHtcbiAgICBpZiAoIGxldmVsID09IHVuZGVmaW5lZCApIHsgbGV2ZWwgPSAwOyAgfVxuXG4gICAgdmFyIGN1cnJlbnRTZWxlY3Rpb24gPSBzZWxlY3Rpb24uc2VsZWN0QWxsKCdnLicrbmFtZXNwYWNlKydbbGV2ZWw9XCInK2xldmVsKydcIl0nKS5kYXRhKGRhdGEpXG4gICAgdmFyIGVudGVyID0gY3VycmVudFNlbGVjdGlvbi5lbnRlcigpLmFwcGVuZCgnZycpLmF0dHIoJ2xldmVsJywgbGV2ZWwpLmF0dHIoJ2NsYXNzJywgbmFtZXNwYWNlKVxuICAgIHZhciBleGl0ID0gY3VycmVudFNlbGVjdGlvbi5leGl0KClcbiAgICBjdXJyZW50U2VsZWN0aW9uID0gY3VycmVudFNlbGVjdGlvbi5tZXJnZShlbnRlcilcblxuXG4gICAgaWYgKHR5cGVvZiBleGl0RnVuY3Rpb24gPT0gJ2Z1bmN0aW9uJyApeyBleGl0LmVhY2goZnVuY3Rpb24oZCwgaSl7IGV4aXRGdW5jdGlvbihkMy5zZWxlY3QodGhpcykpfSkgfVxuICAgIGVsc2V7ZXhpdC5yZW1vdmUoKX1cbiAgICAvLyBzcGFjZXIgZm9yIGN1cnJlbnQgbGV2ZWxcbiAgICB2YXIgbGV2ZWxTcGFjZXIgPSBzcGFjZXJTaXplIC8gKGxldmVsKzEpXG4gICAgLy8gbW92ZW1lbnQgZm9yIGN1cnJlbnQgbGV2ZWxcbiAgICB2YXIgbW92ZSA9IDBcbiAgICBjdXJyZW50U2VsZWN0aW9uLmVhY2goZnVuY3Rpb24oY3VycmVudEVsZW1lbnQsIGluZGV4KSB7XG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgICAgaWYgKHQuYXR0cigndHJhbnNmb3JtJykgPT0gdW5kZWZpbmVkICYmIHR5cGVvZiBlbnRlckZ1bmN0aW9uID09ICdmdW5jdGlvbicpIHsgZW50ZXJGdW5jdGlvbih0KSB9XG5cbiAgICAgIHQudHJhbnNpdGlvbigpLmR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbikuZWFzZShlYXNlRnVuYylcbiAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKSB7XG4gICAgICAgIHZhclxuICAgICAgICB4ID0gaG9yaXpvbnRhbFEgPyAobW92ZWJ5ID09XCJzY2FsZVwiID8gc2NhbGUoZCkgOiBtb3ZlKSA6IDAsXG4gICAgICAgIHkgPSAhaG9yaXpvbnRhbFEgPyAobW92ZWJ5ID09XCJzY2FsZVwiID8gc2NhbGUoZCkgOiBtb3ZlKTogMCxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcblxuICAgICAgaWYgKEFycmF5LmlzQXJyYXkoY3VycmVudEVsZW1lbnQpKSB7XG4gICAgICAgIG1vdmUgKz0gcmVjdXJzaXZlbHlQb3NpdGlvbih0LCBjdXJyZW50RWxlbWVudCwgbGV2ZWwrMSlcbiAgICAgICAgdmFyIHRvUmVtb3ZlID0gdC5zZWxlY3RBbGwoJ2cuJytuYW1lc3BhY2UrJ1tsZXZlbD1cIicrKGxldmVsKSsnXCJdID4gZy4nK29iamVjdENsYXNzKycuJytuYW1lc3BhY2UpXG4gICAgICAgIGlmICh0eXBlb2YgZXhpdEZ1bmN0aW9uID09ICdmdW5jdGlvbicgKXsgdG9SZW1vdmUuZWFjaChmdW5jdGlvbihkLCBpKXsgZXhpdEZ1bmN0aW9uKGQzLnNlbGVjdCh0aGlzKSl9KSB9XG4gICAgICAgIGVsc2V7dG9SZW1vdmUucmVtb3ZlKCl9XG4gICAgICB9XG4gICAgICBlbHNlIHtcbiAgICAgICAgbW92ZSArPSBvYmplY3RTaXplXG4gICAgICAgIHZhciBvYmogPSB0LnNlbGVjdCgnZy4nK25hbWVzcGFjZSsnW2xldmVsPVwiJytsZXZlbCsnXCJdID4gZy4nK29iamVjdENsYXNzKycuJytuYW1lc3BhY2UpXG4gICAgICAgIGlmIChvYmouZW1wdHkoKSkgeyBvYmogPSB0LmFwcGVuZCgnZycpLmF0dHIoJ2NsYXNzJywgb2JqZWN0Q2xhc3MpLmNsYXNzZWQobmFtZXNwYWNlLCB0cnVlKSB9XG4gICAgICAgIG9iai5hdHRyKCdwYXJlbnQtaW5kZXgnLCBpbmRleClcbiAgICAgICAgdmFyIHRvUmVtb3ZlID0gdC5zZWxlY3RBbGwoJ2cuJytuYW1lc3BhY2UrJ1tsZXZlbD1cIicrKGxldmVsKzEpKydcIl0nKVxuXG4gICAgICAgIGlmICh0eXBlb2YgZXhpdEZ1bmN0aW9uID09ICdmdW5jdGlvbicgKXsgdG9SZW1vdmUuZWFjaChmdW5jdGlvbihkLCBpKXsgZXhpdEZ1bmN0aW9uKGQzLnNlbGVjdCh0aGlzKSl9KSB9XG4gICAgICAgIGVsc2V7dG9SZW1vdmUucmVtb3ZlKCl9XG4gICAgICB9XG4gICAgICBtb3ZlICs9IChpbmRleCA9PSBjdXJyZW50U2VsZWN0aW9uLnNpemUoKS0xKSA/IDAgOiBsZXZlbFNwYWNlclxuICAgIH0pXG4gICAgcmV0dXJuIG1vdmVcbiAgfVxuICByZXR1cm4gcmVjdXJzaXZlbHlQb3NpdGlvblxufVxuIiwiaW1wb3J0IHttb2RpZnlIZXhpZGVjaW1hbENvbG9yTHVtaW5hbmNlfSBmcm9tICcuL2hlbHBlcnMnO1xuXG4vKipcbiAqIENyZWF0ZXMgYSBjb2xvckZ1bmN0aW9uXG4gKiBAY29uc3RydWN0b3IgY29sb3JGdW5jdGlvblxuICogQG5hbWVzcGFjZSBjb2xvckZ1bmN0aW9uXG4gKiBAcmV0dXJucyB7ZnVuY3Rpb259IGNvbG9yRnVuY3Rpb25cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbG9yRnVuY3Rpb24oKSB7XG4gIHZhclxuICBkYXRhLFxuXG4gIC8qKlxuICAqIERlZmF1bHQgY29sb3JzIHRvIHVzZVxuICAqIEBwYXJhbSB7bnVtYmVyW119IFtjb2xvcnM9W1wiIzJjN2JiNlwiLCBcIiMwMGE2Y2FcIiwgXCIjMDBjY2JjXCIsIFwiIzkwZWI5ZFwiLCBcIiNmZmZmOGNcIiwgXCIjZjlkMDU3XCIsIFwiI2YyOWUyZVwiLCBcIiNlNzY4MThcIiwgXCIjZDcxOTFjXCJdXVxuICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjb2xvcnMgPSBbXCIjMmM3YmI2XCIsIFwiIzAwYTZjYVwiLCBcIiMwMGNjYmNcIiwgXCIjOTBlYjlkXCIsIFwiI2ZmZmY4Y1wiLCBcIiNmOWQwNTdcIiwgXCIjZjI5ZTJlXCIsIFwiI2U3NjgxOFwiLCBcIiNkNzE5MWNcIl0sXG4gIC8qKlxuICAqIEludGVycG9sYXRvciBmb3IgY29sb3JzXG4gICogQHBhcmFtIHtkMy5pbnRlcnBvbGF0aW9ufSBbaW50ZXJwb2xhdGlvbj1kMy5pbnRlcnBvbGF0ZVJnYl1cbiAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgaW50ZXJwb2xhdGlvbiA9IGQzLmludGVycG9sYXRlUmdiLFxuICAvKipcbiAgKiBGdW5jdGlvbiBmb3IgbW9kaWZ5aW5nIGNvbG9yIGx1bWluYW5jZVxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFttb2RpZnlPcGFjaXR5PW1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2VdXG4gICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb24jXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1vZGlmeU9wYWNpdHkgPSBtb2RpZnlIZXhpZGVjaW1hbENvbG9yTHVtaW5hbmNlLFxuICAvKipcbiAgKiBIb3cgdG8gbW9kaWZ5IGNvbG9yIGZvciBzdHJva2VcbiAgKiBAcGFyYW0ge251bWJlcn0gW3N0cm9rZU9wYWNpdHk9MF1cbiAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3Ryb2tlT3BhY2l0eSA9IDAsXG4gIC8qKlxuICAqIEhvdyB0byBtb2RpZnkgY29sb3IgZm9yIGZpbGxcbiAgKiBAcGFyYW0ge251bWJlcn0gW2ZpbGxPcGFjaXR5PTAuNF1cbiAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZmlsbE9wYWNpdHkgPSAwLjQsXG4gIC8qKlxuICAqIEhvdyB0byBkZXRlcm1pbmUgdGhlIGNvbG9yIHRvIHVzZVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbY29sb3JCeT0naW5kZXgnXVxuICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjb2xvckJ5ID0gJ2luZGV4JyxcbiAgLyoqXG4gICogU2V0cyB0aGUgc2NhbGUgZm9yIGludGVycG9sYXRpbmcgdGhlIGNvbG9yc1xuICAqIEBwYXJhbSB7bnVtYmVyW119IFtkYXRhRXh0ZW50PVswLCBjb2xvcnMubGVuZ3RoIC0gMV1dXG4gICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb24jXG4gICogQHByb3BlcnR5XG4gICovXG4gIGRhdGFFeHRlbnQgPSBbMCwgY29sb3JzLmxlbmd0aCAtIDFdLFxuICAvKipcbiAgKiBFeHRyYWN0cyB0aGUgdmFsdWUgdG8gY29sb3IgYnlcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdmFsdWVFeHRyYWN0b3I9ZnVuY3Rpb24oaywgdiwgaSkge3JldHVybiB2fV1cbiAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihrLCB2LCBpKSB7cmV0dXJuIHZ9LFxuXG4gIC8qKlxuICAqIEV4dHJhY3RzIHRoZSBjYXRlZ29yeSB0byBjb2xvciBieVxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtjYXRlZ29yeUV4dHJhY3Rvcj1mdW5jdGlvbihrLCB2LCBpKSB7cmV0dXJuIHYuY2F0ZWdvcnl9XVxuICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjYXRlZ29yeUV4dHJhY3RvciA9IGZ1bmN0aW9uKGssIHYsIGkpIHtyZXR1cm4gdi5jYXRlZ29yeX0sXG5cbiAgLyoqXG4gICogVGhlIGRpZmZlcmVudCB0eXBlIG9mIGNhdGVnb3JpZXMgb2Ygd2hpY2ggdG8gY29sb3IgYnlcbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbY2F0ZWdvcmllcz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb24jXG4gICogQHByb3BlcnR5XG4gICovXG4gIGNhdGVnb3JpZXMsXG5cbiAgLyoqXG4gICogU2NhbGUgZm9yIGludGVycG9sYXRpbmcgdGhlIGNvbG9yc1xuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZT1kMy5zY2FsZUxpbmVhcigpXVxuICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcbiAgLmludGVycG9sYXRlKGludGVycG9sYXRpb24pLmRvbWFpbihkYXRhRXh0ZW50KS5yYW5nZShjb2xvcnMpLFxuICBoZWxwZXJTY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcblxuXG5cbiAgLy8gdmFyIGggPSB4ID0+ICcjJyArIHgubWF0Y2goL1xcZCsvZykubWFwKHkgPSB6ID0+ICgoK3ogPCAxNik/JzAnOicnKSArICgreikudG9TdHJpbmcoMTYpKS5qb2luKCcnKTtcbiAgdmFyIGggPSBmdW5jdGlvbih4KSB7XG4gICAgcmV0dXJuIFwiI1wiICsgeC5tYXRjaCgvXFxkKy9nKS5tYXAoXG4gICAgICBmdW5jdGlvbih5LCBpKSB7XG4gICAgICAgIHJldHVybiAgKCgreSA8IDE2KT8nMCc6JycpICsgKCt5KS50b1N0cmluZygxNilcbiAgICAgIH0pLmpvaW4oJycpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZGVmYXVsdCBjb2xvcnNcbiAgICogKHNlZSB7QGxpbmsgY29sb3JGdW5jdGlvbiNjb2xvcnN9KVxuICAgKiBAcGFyYW0ge251bWJlcltdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y29sb3JGdW5jdGlvbiB8IG51bWJlcltdfVxuICAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGNvbG9yRnVuY3Rpb24uY29sb3JzID0gZnVuY3Rpb24oXykge1xuICAgIHJldHVybiBhcmd1bWVudHMubGVuZ3RoXG4gICAgP1xuICAgICAgKFxuICAgICAgICBjb2xvcnMgPSBfLFxuICAgICAgICBzY2FsZS5yYW5nZShjb2xvcnMpLFxuICAgICAgICBjb2xvckZ1bmN0aW9uXG4gICAgICApXG4gICAgOiBjb2xvcnM7XG4gIH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGZ1bmN0aW9uIGZvciBpbnRlcnBvbGF0aW5nIHRoZSBjb2xvcnNcbiAgICogKHNlZSB7QGxpbmsgY29sb3JGdW5jdGlvbiNpbnRlcnBvbGF0aW9ufSlcbiAgICogQHBhcmFtIHtkMy5pbnRlcnBvbGF0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y29sb3JGdW5jdGlvbiB8IGQzLmludGVycG9sYXRpb259XG4gICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgY29sb3JGdW5jdGlvbi5pbnRlcnBvbGF0aW9uID0gZnVuY3Rpb24oXykge1xuICAgIHJldHVybiBhcmd1bWVudHMubGVuZ3RoXG4gICAgP1xuICAgIChcbiAgICAgIGludGVycG9sYXRpb24gPSBfLFxuICAgICAgc2NhbGUuaW50ZXJwb2xhdGUoaW50ZXJwb2xhdGlvbikucmFuZ2UoY29sb3JzKSxcbiAgICAgIGNvbG9yRnVuY3Rpb25cbiAgICApXG4gICAgOiBpbnRlcnBvbGF0aW9uO1xuICB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB2YWx1ZXMgZm9yIHRoZSBzY2FsZSB3aGljaCB0cmFuc2Zvcm1zIHRoZSB2YWx1ZSB0byBhIGNvbG9yXG4gICAqIChzZWUge0BsaW5rIGNvbG9yRnVuY3Rpb24jZGF0YUV4dGVudH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtjb2xvckZ1bmN0aW9uIHwgbnVtYmVyW119XG4gICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50ID0gZnVuY3Rpb24oXykge1xuICAgIHJldHVybiBhcmd1bWVudHMubGVuZ3RoXG4gICAgPyAoXG4gICAgICAgIGRhdGFFeHRlbnQgPSBfLFxuICAgICAgICBzY2FsZS5kb21haW4oZGF0YUV4dGVudCkuaW50ZXJwb2xhdGUoc2NhbGUuaW50ZXJwb2xhdGUoKSksXG4gICAgICAgIGNvbG9yRnVuY3Rpb25cbiAgICAgIClcbiAgICA6IGRhdGFFeHRlbnQ7XG4gIH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZ0aGUgc2NhbGUgd2hpY2ggdHJhbnNmb3JtcyB0aGUgdmFsdWUgdG8gYSBjb2xvclxuICAgKiAoc2VlIHtAbGluayBjb2xvckZ1bmN0aW9uI3NjYWxlfSlcbiAgICogQHBhcmFtIHtkMy5zY2FsZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2NvbG9yRnVuY3Rpb24gfCBkMy5zY2FsZX1cbiAgICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb25cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICBjb2xvckZ1bmN0aW9uLnNjYWxlID0gZnVuY3Rpb24oXykge1xuICAgIHJldHVybiBhcmd1bWVudHMubGVuZ3RoXG4gICAgPyAoXG4gICAgICAgIF8gPSBfLmRvbWFpbihzY2FsZS5kb21haW4oKSkuaW50ZXJwb2xhdGUoc2NhbGUuaW50ZXJwb2xhdGUoKSkucmFuZ2Uoc2NhbGUucmFuZ2UoKSksXG4gICAgICAgIHNjYWxlID0gXyxcbiAgICAgICAgY29sb3JGdW5jdGlvblxuICAgICAgKVxuICAgIDogc2NhbGU7XG4gIH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGZ1bmN0aW9uIGZvciBtb2RpZnkgb3BhY2l0eVxuICAgKiAoc2VlIHtAbGluayBjb2xvckZ1bmN0aW9uI21vZGlmeU9wYWNpdHl9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y29sb3JGdW5jdGlvbiB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGNvbG9yRnVuY3Rpb24ubW9kaWZ5T3BhY2l0eSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobW9kaWZ5T3BhY2l0eSA9IF8sIGNvbG9yRnVuY3Rpb24pIDogbW9kaWZ5T3BhY2l0eTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgdmFsdWUgdG8gbW9kaWZ5IHRoZSBjb2xvciBmb3IgdGhlIHN0cm9rZSB2aWEge0BsaW5rIGNvbG9yRnVuY3Rpb24jbW9kaWZ5T3BhY2l0eX1cbiAgICogKHNlZSB7QGxpbmsgY29sb3JGdW5jdGlvbiNzdHJva2VPcGFjaXR5fSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtjb2xvckZ1bmN0aW9uIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGNvbG9yRnVuY3Rpb24uc3Ryb2tlT3BhY2l0eSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3Ryb2tlT3BhY2l0eSA9IF8sIGNvbG9yRnVuY3Rpb24pIDogc3Ryb2tlT3BhY2l0eTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgdmFsdWUgdG8gbW9kaWZ5IHRoZSBjb2xvciBmb3IgdGhlIHN0cm9rZSB2aWEge0BsaW5rIGNvbG9yRnVuY3Rpb24jZmlsbE9wYWNpdHl9XG4gICAqIChzZWUge0BsaW5rIGNvbG9yRnVuY3Rpb24jZmlsbE9wYWNpdHl9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2NvbG9yRnVuY3Rpb24gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgY29sb3JGdW5jdGlvbi5maWxsT3BhY2l0eSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZmlsbE9wYWNpdHkgPSBfLCBjb2xvckZ1bmN0aW9uKSA6IGZpbGxPcGFjaXR5OyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB2YWx1ZSB0byBjb2xvckJ5XG4gICAqIChzZWUge0BsaW5rIGNvbG9yRnVuY3Rpb24jY29sb3JCeX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y29sb3JGdW5jdGlvbiB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb25cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICBjb2xvckZ1bmN0aW9uLmNvbG9yQnkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNvbG9yQnkgPSBfLCBjb2xvckZ1bmN0aW9uKSA6IGNvbG9yQnk7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZhbHVlIG9mIHZhbHVlRXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGNvbG9yRnVuY3Rpb24jdmFsdWVFeHRyYWN0b3J9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y29sb3JGdW5jdGlvbiB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgY29sb3JGdW5jdGlvblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGNvbG9yRnVuY3Rpb24udmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZhbHVlRXh0cmFjdG9yID0gXywgY29sb3JGdW5jdGlvbikgOiB2YWx1ZUV4dHJhY3RvcjsgfTtcblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB2YWx1ZSBvZiBjYXRlZ29yeUV4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayBjb2xvckZ1bmN0aW9uI2NhdGVnb3J5RXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2NvbG9yRnVuY3Rpb24gfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGNvbG9yRnVuY3Rpb25cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICBjb2xvckZ1bmN0aW9uLmNhdGVnb3J5RXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjYXRlZ29yeUV4dHJhY3RvciA9IF8sIGNvbG9yRnVuY3Rpb24pIDogY2F0ZWdvcnlFeHRyYWN0b3I7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZhbHVlIG9mIGNhdGVnb3J5RXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGNvbG9yRnVuY3Rpb24jY2F0ZWdvcmllc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtjb2xvckZ1bmN0aW9uIHwgc3RyaW5nW119XG4gICAqIEBtZW1iZXJvZiBjb2xvckZ1bmN0aW9uXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgY29sb3JGdW5jdGlvbi5jYXRlZ29yaWVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjYXRlZ29yaWVzID0gXywgY29sb3JGdW5jdGlvbikgOiBjYXRlZ29yaWVzOyB9O1xuXG5cbiAgZnVuY3Rpb24gY29sb3JGdW5jdGlvbihrZXksIHZhbHVlLCBpbmRleCwgdHlwZSwgaG92ZXJRKSB7XG4gICAgdmFyIGMsXG4gICAgb3BhYyA9IHR5cGUgPT0gXCJmaWxsXCIgPyBmaWxsT3BhY2l0eSA6IHN0cm9rZU9wYWNpdHk7XG5cblxuICAgIHVwZGF0ZVNjYWxlKClcblxuICAgIGlmIChjb2xvckJ5ID09IFwiaW5kZXhcIikge1xuICAgICAgYyA9ICh0eXBlICE9IHVuZGVmaW5lZCkgPyBtb2RpZnlPcGFjaXR5KGgoc2NhbGUoaW5kZXgpKSwgb3BhYykgOiBoKHNjYWxlKGluZGV4KSlcbiAgICB9XG5cbiAgICBlbHNlIGlmIChjb2xvckJ5ID09ICd2YWx1ZScpIHtcbiAgICAgIHZhciB2ID0gdmFsdWVFeHRyYWN0b3Ioa2V5LCB2YWx1ZSwgaW5kZXgpO1xuICAgICAgLy8gaWYgKHYgPCBkYXRhRXh0ZW50WzBdKSB7ZGF0YUV4dGVudFswXSA9IHY7IHVwZGF0ZVNjYWxlKCl9XG4gICAgICAvLyBpZiAodiA+IGRhdGFFeHRlbnRbMV0pIHtkYXRhRXh0ZW50WzFdID0gdjsgdXBkYXRlU2NhbGUoKX1cblxuICAgICAgYyA9ICh0eXBlICE9IHVuZGVmaW5lZCkgPyBtb2RpZnlPcGFjaXR5KGgoc2NhbGUodikpLCBvcGFjKSA6IGgoc2NhbGUodikpXG4gICAgfVxuXG4gICAgZWxzZSBpZiAoY29sb3JCeSA9PSAnY2F0ZWdvcnknICl7XG4gICAgICB2YXIgY2F0ID0gY2F0ZWdvcnlFeHRyYWN0b3Ioa2V5LCB2YWx1ZSwgaW5kZXgpO1xuICAgICAgdmFyIHYgPSBjYXRlZ29yaWVzLmluZGV4T2YoY2F0KVxuICAgICAgYyA9ICh0eXBlICE9IHVuZGVmaW5lZCkgPyBtb2RpZnlPcGFjaXR5KGgoc2NhbGUodikpLCBvcGFjKSA6IGgoc2NhbGUodikpXG5cbiAgICB9XG5cbiAgICBlbHNlIHtcbiAgICAgIGMgPSAodHlwZSAhPSB1bmRlZmluZWQpID8gbW9kaWZ5T3BhY2l0eShoKHNjYWxlKGluZGV4KSksIG9wYWMpIDogaChzY2FsZShpbmRleCkpXG4gICAgfVxuXG4gICAgcmV0dXJuIGNcbiAgfVxuXG4gIGZ1bmN0aW9uIHVwZGF0ZVNjYWxlKCl7XG5cblxuICAgIGhlbHBlclNjYWxlLmRvbWFpbihbMCwgY29sb3JzLmxlbmd0aF0pXG4gICAgaWYgKGNvbG9yQnkgPT0gJ2NhdGVnb3J5JyAmJiBjYXRlZ29yaWVzICE9IHVuZGVmaW5lZCkgeyBoZWxwZXJTY2FsZS5yYW5nZShbMCwgY2F0ZWdvcmllcy5sZW5ndGhdKSB9XG4gICAgZWxzZSB7IGhlbHBlclNjYWxlLnJhbmdlKGRhdGFFeHRlbnQpIH1cblxuXG4gICAgdmFyIGEgPSBBcnJheShjb2xvcnMubGVuZ3RoKS5maWxsKDApLm1hcChmdW5jdGlvbihkLCBpKXsgcmV0dXJuIGhlbHBlclNjYWxlKGkpIH0pXG4gICAgc2NhbGUuZG9tYWluKGEpXG4gIH1cblxuICByZXR1cm4gY29sb3JGdW5jdGlvblxufVxuIiwiaW1wb3J0IHtzYWZlU2VsZWN0LCByb3VuZH0gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7bG9nLCB3YXJuLCBpbmZvLCBlcnJvciwgY29uc29sZUdyb3VwLCBjb25zb2xlR3JvdXBFbmR9IGZyb20gJy4vdXRpbHMnO1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUT09MVElQICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qKlxuICogUHJvZHVjZXMgYSBmdW5jdGlvbiBmb3IgaGFuZGxpbmcgdGhlIHRvb2x0aXBcbiAqXG4gKiB7QGxpbmsgaHR0cHM6Ly9zdW1uZXVyb24uZ2l0bGFiLmlvL2Qzc20vZGVtb3MvdG9vbHRpcC1kZXNpZ24vaW5kZXguaHRtbCBEZW1vfVxuICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IHNlbGVjdGlvblxuICogQHJldHVybnMge3Rvb2x0aXB9XG4gKiBAbmFtZXNwYWNlIHRvb2x0aXBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHRvb2x0aXAoIHNlbGVjdGlvbiApIHtcblxuICB2YXJcbiAga2V5cyxcbiAgdmFsdWVzLFxuICBoZWFkZXIsXG4gIGRhdGEsXG4gIHNlbGVjdGlvblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUga2V5cyB0byBiZSBkaXNwbGF5ZWQgaW4gdGhlIHRvb2x0aXAuXG4gICAqIElmIG5vdCBzZXQsIHVzZXMgZDMua2V5cyhkYXRhW2tleV0pXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt0b29sdGlwIHwgc3RyaW5nW119XG4gICAqIEBtZW1iZXJvZiB0b29sdGlwXG4gICAqL1xuICB0b29sdGlwLmtleXMgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChrZXlzID0gXywgdG9vbHRpcCkgOiBrZXlzfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2YWx1ZXMgdG8gYmUgZGlzcGxheWVkIG5leHQgdG8gdGhlIGtleXMuXG4gICAqIElmIG5vdCBzZXQsIHVzZXMgZGF0YVtrZXldW2tleXNbaV1dLlxuICAgKiBJZiBhIGZ1bmN0aW9uLCBnZXRzIHBhc3NlZCBjdXJyZW50RGF0YSAoZGF0YVtrZXldKSBhbmQga2V5c1tpXS5cbiAgICogQHBhcmFtIHsqW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt0b29sdGlwIHwgKltdfVxuICAgKiBAbWVtYmVyb2YgdG9vbHRpcFxuICAgKi9cbiAgdG9vbHRpcC52YWx1ZXMgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZXMgPSBfLCB0b29sdGlwKSA6IHZhbHVlc307XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgaGVhZGVyIHRvIGJlIGRpc3BsYXllZCBpbiB0aGUgdG9vbHRpcC5cbiAgICogSWYgbm90IHNldCwgdXNlcyBrZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt0b29sdGlwIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgdG9vbHRpcFxuICAgKi9cbiAgdG9vbHRpcC5oZWFkZXIgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChoZWFkZXIgPSBfLCB0b29sdGlwKSA6IGhlYWRlcn07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZGF0YSAob3ZlciB0aGUgc2VsZWN0aW9uKSB0byBiZSB1c2VkIGZvciB0aGUgdG9vbHRpcFxuICAgKiBAcGFyYW0ge09iamVjdH0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Rvb2x0aXAgfCBPYmplY3R9XG4gICAqIEBtZW1iZXJvZiB0b29sdGlwXG4gICAqL1xuICB0b29sdGlwLmRhdGEgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkYXRhID0gXywgdG9vbHRpcCkgOiBkYXRhfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzZWxlY3Rpb24gZm9yIHRoZSB0b29sdGlwIHRvIGJlIGFwcGxpZWQgb25cbiAgICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt0b29sdGlwIHwgZDMuc2VsZWN0aW9ufVxuICAgKiBAbWVtYmVyb2YgdG9vbHRpcFxuICAgKi9cbiAgdG9vbHRpcC5zZWxlY3Rpb24gPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCB0b29sdGlwKSA6IHNlbGVjdGlvbn07XG5cbiAgLyoqXG4gICAqIEJpbmQsIHZpYSBzZWxlY3Rpb24ub24oKSwgdGhlIG1vdXNlbW92ZSBhbmQgbW91c2VvdXQgZXZlbnRzXG4gICAqIEByZXR1cm5zIHVuZGVmaW5lZFxuICAgKi9cbiAgZnVuY3Rpb24gdG9vbHRpcCggKSB7XG4gICAgc2VsZWN0aW9uLm9uKCdtb3VzZW92ZXInLCBtb3VzZW1vdmUpXG4gICAgc2VsZWN0aW9uLm9uKCdtb3VzZW1vdmUnLCBtb3VzZW1vdmUpXG4gICAgc2VsZWN0aW9uLm9uKCdtb3VzZW91dCcsIGZ1bmN0aW9uKCl7IGQzLnNlbGVjdEFsbChcIi5kM3NtLXRvb2x0aXBcIikucmVtb3ZlKCl9KVxuICB9XG5cblxuICAvKipcbiAgICogUHJvZHVjZXMgdGhlIHRvb2x0aXAgb24gbW91c2Vtb3ZlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBrZXkgb2YgdGhlIG9iamVjdCB0YXJnZXRlZCBieSB0aGUgbW91c2Vtb3ZlXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBpIChpbmRleCkgb2YgdGhlIG9iamVjdCB0YXJnZXRlZCBieSBtb3VzZW1vdmVcbiAgICogQG1lbWJlcm9mIHRvb2x0aXBcbiAgICogQHByaXZhdGVcbiAgICovXG4gIGZ1bmN0aW9uIG1vdXNlbW92ZShrZXksIGkpIHtcbiAgICBjb25zb2xlR3JvdXAoJ2Qzc20tdG9vbHRpcCcpXG4gICAgdmFyIGN1cnJlbnREYXRhID0gZGF0YVtrZXldXG5cbiAgICB2YXIgW3gsIHldID0gZDMubW91c2UoZDMuc2VsZWN0KFwiaHRtbFwiKS5ub2RlKCkpXG4gICAgbG9nKCd0b29sdGlwJywgJ21vdXNlbW92ZSBkZXRlY3RlZCcse2tleToga2V5LCBpbmRleDogaSwgeDp4LCB5Onl9KVxuICAgIGxvZygndG9vbHRpcCcsICdjdXJyZW50IGRhdGEnLCBjdXJyZW50RGF0YSlcblxuXG5cbiAgICB2YXIgZGl2ID0gc2FmZVNlbGVjdChkMy5zZWxlY3QoJ2h0bWwnKSwgJ3Rvb2x0aXAnLCAnZDNzbS10b29sdGlwJylcbiAgICAuY2xhc3NlZCgnY2FyZCcsIHRydWUpXG4gICAgLnN0eWxlKCdtYXgtd2lkdGgnLCAnMzAwcHgnKVxuICAgIC5zdHlsZSgnYmFja2dyb3VuZC1jb2xvcicsIFwiIzIxMjUyOVwiKVxuICAgIC5zdHlsZSgnY29sb3InLCAnd2hpdGUnKVxuXG5cblxuICAgIHZhciBjYXJkQm9keSA9IHNhZmVTZWxlY3QoZGl2LCAnZGl2JywgJ2NhcmQtYm9keScpXG4gICAgdmFyIGNhcmRUaXRsZSA9IHNhZmVTZWxlY3QoY2FyZEJvZHksICdoNScsICdjYXJkLXRpdGxlJylcbiAgICAudGV4dChoZWFkZXIgPT0gdW5kZWZpbmVkID8ga2V5IDogdHlwZW9mIGhlYWRlciA9PSAnZnVuY3Rpb24nID8gaGVhZGVyKGtleSwgY3VycmVudERhdGEsIGkpIDogaGVhZGVyKVxuICAgIC5zdHlsZSgnY29sb3InLCAnY3lhbicpXG5cblxuICAgIHZhciB0YWJsZSA9IHNhZmVTZWxlY3QoY2FyZEJvZHksICd0YWJsZScsICd0YWJsZScpLmNsYXNzZWQoJ3RhYmxlLWRhcmsnLCB0cnVlKVxuICAgIHZhciB0Qm9keSA9IHNhZmVTZWxlY3QodGFibGUsICd0Ym9keScpXG5cbiAgICB0Qm9keSA9IHRCb2R5LnNlbGVjdEFsbCgndHInKVxuICAgIHRCb2R5PSB0Qm9keS5kYXRhKGtleXMgPT0gdW5kZWZpbmVkID8gZDMua2V5cyhjdXJyZW50RGF0YSk6IGtleXMpXG4gICAgdEJvZHkuZXhpdCgpLnJlbW92ZSgpXG5cblxuICAgIHZhciB0ciA9IHRCb2R5LmVudGVyKCkuYXBwZW5kKCd0cicpLnN0eWxlKCdtYXgtd2lkdGgnLCAnMzAwcHgnKVxuICAgIHRyLmFwcGVuZCgndGQnKS5hdHRyKCdjbGFzcycsIGZ1bmN0aW9uKGQsIGkpe3JldHVybiAndG9vbHRpcC1rZXknfSlcbiAgICB0ci5hcHBlbmQoJ3RkJykuYXR0cignY2xhc3MnLCAgZnVuY3Rpb24oZCwgaSwgail7cmV0dXJuICd0b29sdGlwLXZhbHVlJ30pXG4gICAgLmF0dHIoJ3Rvb2x0aXAtcm93LWluZGV4JywgZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGl9KVxuXG4gICAgLy8gdEJvZHkgPSB0Qm9keS5tZXJnZSh0cilcbiAgICBjb25zb2xlR3JvdXAoJ3Rvb2x0aXAtcm93cycpXG4gICAgdEJvZHkuc2VsZWN0QWxsKCcudG9vbHRpcC1rZXknKS50ZXh0KGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkfSlcbiAgICB0Qm9keS5zZWxlY3RBbGwoJ3RyIC50b29sdGlwLXZhbHVlJylcbiAgICAudGV4dChmdW5jdGlvbihkLCBpKXtcbiAgICAgIGxvZygndG9vbHRpcCcsICd0cnlpbmcgdG8gc2V0IHZhbHVlJywge3Jvd0tleTogZCwgcm93SW5kZXg6IGl9KVxuICAgICAgdmFyIGkgPSBkMy5zZWxlY3QodGhpcykuYXR0cigndG9vbHRpcC1yb3ctaW5kZXgnKVxuICAgICAgdmFyIHYgPSBjdXJyZW50RGF0YVtkXTtcblxuXG4gICAgICBpZiAodmFsdWVzICE9IHVuZGVmaW5lZCkge3YgPSB2YWx1ZXNbaV07IGlmKHR5cGVvZiB2ID09IFwiZnVuY3Rpb25cIikge3YgPSB2KGN1cnJlbnREYXRhLCBkKX19XG4gICAgICByZXR1cm4gIHR5cGVvZiB2ID09ICdudW1iZXInID8gcm91bmQodiwgNSkgOiB2XG4gICAgfSlcbiAgICBjb25zb2xlR3JvdXBFbmQoKVxuICAgIGNvbnNvbGVHcm91cEVuZCgpXG5cbiAgICB4ICs9IDE1XG4gICAgLy8geCArPSAxNVxuICAgIHZhciBiYm94ID0gZGl2Lm5vZGUoKS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuICAgIGlmICh4ICsgYmJveC53aWR0aCA+IHdpbmRvdy5pbm5lcldpZHRoIC0gd2luZG93LnNjcm9sbFgpIHsgeCA9IGQzLmV2ZW50LnBhZ2VYIC0gYmJveC53aWR0aCAtIDE1IH1cbiAgICBpZiAoeSArIGJib3guaGVpZ2h0ID4gd2luZG93LmlubmVySGVpZ2h0ICAtIHdpbmRvdy5zY3JvbGxZKSB7IHkgPSBkMy5ldmVudC5wYWdlWSAtIGJib3guaGVpZ2h0IC0gMTUgfVxuICAgIGRpdi5zdHlsZSgncG9zaXRpb24nKSA9PSBcInJlbGF0aXZlXCJcbiAgICA/IGRpdi5zdHlsZSgncG9zaXRpb24nLCAnYWJzb2x1dGUnKS5zdHlsZSgnbGVmdCcsIHgrJ3B4Jykuc3R5bGUoJ3RvcCcsIHkrJ3B4JylcbiAgICA6IGRpdi5zdHlsZSgnbGVmdCcsIHgrJ3B4Jykuc3R5bGUoJ3RvcCcsIHkrJ3B4JylcbiAgICAvLyAudHJhbnNpdGlvbigpLmR1cmF0aW9uKDIwMCkuZWFzZShkMy5lYXNlU2luKVxuXG4gICAgLy8gaWYgKGJib3gueCArIGJib3gud2lkdGggPiB3aW5kb3cuaW5uZXJXaWR0aCkge1xuICAgIC8vICAgZGl2LnN0eWxlKCdsZWZ0JywgKGQzLmV2ZW50LnBhZ2VYLTE1LWJib3gud2lkdGgpKydweCcpXG4gICAgLy8gfVxuICAgIC8vIGlmIChiYm94LnkgKyBiYm94LmhlaWdodCA+IHdpbmRvdy5pbm5lckhlaWdodCkge1xuICAgIC8vICAgZGl2LnN0eWxlKCd0b3AnLCAoZDMuZXZlbnQucGFnZVktMTUtYmJveC5oZWlnaHQpKydweCcpXG4gICAgLy8gfVxuXG4gICAgZGl2LmF0dHIoJ3otaW5kZXgnLCAxMDAwMClcbiAgfVxuXG4gIHJldHVybiB0b29sdGlwXG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuXG5leHBvcnQgZnVuY3Rpb24gc2VsZWN0RmlsdGVyKHNlbGVjdGlvbikge1xuXG4gIHZhclxuICBkYXRhLFxuICBuYW1lc3BhY2UgPSAnZDNzbS1zZWxlY3QtZmlsdGVyJyxcbiAgc2VsZWN0aW9uTmFtZSA9ICdTZWxlY3Qgb3B0aW9uczonLFxuICBkZWZhdWx0VmFsdWUgPSB1bmRlZmluZWRcblxuXG5cblxuICB2YXIgbGFzdFZhbHVlID0gdW5kZWZpbmVkXG5cbiAgc2VsZWN0RmlsdGVyLmRhdGEgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRhdGEgPSBfLCBzZWxlY3RGaWx0ZXIpIDogZGF0YX1cbiAgc2VsZWN0RmlsdGVyLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgc2VsZWN0RmlsdGVyKSA6IG5hbWVzcGFjZX1cbiAgc2VsZWN0RmlsdGVyLnNlbGVjdGlvbk5hbWUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNlbGVjdGlvbk5hbWUgPSBfLCBzZWxlY3RGaWx0ZXIpIDogc2VsZWN0aW9uTmFtZX1cbiAgc2VsZWN0RmlsdGVyLmRlZmF1bHRWYWx1ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGVmYXVsdFZhbHVlID0gXywgc2VsZWN0RmlsdGVyKSA6IGRlZmF1bHRWYWx1ZX1cbiAgc2VsZWN0RmlsdGVyLmN1cnJlbnRPcHRpb24gPSBjdXJyZW50T3B0aW9uXG5cbiAgZnVuY3Rpb24gc2VsZWN0RmlsdGVyKCkge1xuICAgIHZhclxuICAgIGNvbnRhaW5lciA9IHNhZmVTZWxlY3Qoc2VsZWN0aW9uLCAnZGl2JywgJ2lucHV0LWdyb3VwJykuY2xhc3NlZChoeXBlbmF0ZShuYW1lc3BhY2UsJ2NvbnRhaW5lcicpLHRydWUpLFxuXG4gICAgICBzZWxlY3RQcmVwZW5kID0gc2FmZVNlbGVjdChjb250YWluZXIsICdkaXYnLCAnc2VsZWN0LXByZXBlbmQnKS5jbGFzc2VkKCdpbnB1dC1ncm91cC1wcmVwZW5kJywgdHJ1ZSksXG4gICAgICAgIHNlbGVjdFByZXBlbmRTcGFuID0gc2FmZVNlbGVjdChzZWxlY3RQcmVwZW5kLCAnc3BhbicsICdpbnB1dC1ncm91cC10ZXh0JykudGV4dChzZWxlY3Rpb25OYW1lKSxcblxuICAgICAgc2VsZWN0ID0gc2FmZVNlbGVjdChjb250YWluZXIsICdzZWxlY3QnLCAnY3VzdG9tLXNlbGVjdCcpLmNsYXNzZWQoaHlwZW5hdGUobmFtZXNwYWNlLCdzZWxlY3QnKSx0cnVlKSxcblxuICAgICAgc2VsZWN0QXBwZW5kID0gc2FmZVNlbGVjdChjb250YWluZXIsICdkaXYnLCAnc2VsZWN0LWFwcGVuZCcpLmNsYXNzZWQoJ2lucHV0LWdyb3VwLXByZXBlbmQnLCB0cnVlKSxcbiAgICAgICAgc2VsZWN0QXBwZW5kQnV0dG9uID0gc2FmZVNlbGVjdChzZWxlY3RBcHBlbmQsICdhJywgJ2ZpbHRlci1idXR0b24nKS5jbGFzc2VkKCdidG4gYnRuLW91dGxpbmUtc2Vjb25kYXJ5JywgdHJ1ZSksXG4gICAgICAgICAgZmlsdGVyQnV0dG9uSWNvbiA9IHNhZmVTZWxlY3Qoc2VsZWN0QXBwZW5kQnV0dG9uLCAnaScsICdmYSBmYS1maWx0ZXInKSxcblxuICAgICAgaW5wdXRHcm91cCA9IHNhZmVTZWxlY3QoY29udGFpbmVyLCAnZGl2JywgJ2ZpbHRlci1pbnB1dC1ncm91cCcpLmNsYXNzZWQoJ2lucHV0LWdyb3VwJyx0cnVlKS5jbGFzc2VkKCdkLW5vbmUnLCB0cnVlKSxcbiAgICAgICAgaW5wdXRQcmVwZW5kID0gc2FmZVNlbGVjdChpbnB1dEdyb3VwLCAnZGl2JywgJ2lucHV0LWdyb3VwLXByZXBlbmQnKSxcbiAgICAgICAgICBpbnB1dFByZXBlbmRTcGFuID0gc2FmZVNlbGVjdChpbnB1dFByZXBlbmQsICdzcGFuJywgJ2lucHV0LWdyb3VwLXRleHQnKS5jbGFzc2VkKCdzZWFyY2gtYnV0dG9uJywgdHJ1ZSksXG4gICAgICAgICAgICBpbnB1dFByZXBlbmRTcGFuSWNvbiA9IHNhZmVTZWxlY3QoaW5wdXRQcmVwZW5kU3BhbiwnaScsJ2ZhIGZhLXNlYXJjaCcpLFxuXG4gICAgICAgIGlucHV0ID0gc2FmZVNlbGVjdChpbnB1dEdyb3VwLCAnaW5wdXQnLCAnZm9ybS1jb250cm9sJykuYXR0cigncGxhY2Vob2xkZXInLCAnYWxsJykuYXR0cigndHlwZScsICd0ZXh0JyksXG4gICAgICAgIGlucHV0QXBwZW5kID0gc2FmZVNlbGVjdChpbnB1dEdyb3VwLCAnZGl2JywgJ2lucHV0LWdyb3VwLWFwcGVuZCcpLFxuICAgICAgICAgIGlucHV0QXBwZW5kQnV0dG9uID0gc2FmZVNlbGVjdChpbnB1dEFwcGVuZCwgJ2EnLCAnY2xvc2UtYnV0dG9uJykuY2xhc3NlZCgnYnRuIGJ0bi1vdXRsaW5lLXNlY29uZGFyeScsIHRydWUpLFxuICAgICAgICAgICAgaW5wdXRBcHBlbmRCdXR0b25JY29uID0gc2FmZVNlbGVjdChpbnB1dEFwcGVuZEJ1dHRvbiwgJ2knLCAnZmEgZmEtY2xvc2UnKVxuXG5cbiAgICB2YXIga2V5cyA9IGQzLmtleXMoZGF0YSksXG4gICAgb3B0aW9ucyA9IHNlbGVjdC5zZWxlY3RBbGwoJ29wdGlvbicpXG5cbiAgICBvcHRpb25zID0gb3B0aW9ucy5kYXRhKGQzLmtleXMoZGF0YSkpXG4gICAgb3B0aW9ucyA9IG9wdGlvbnMubWVyZ2Uob3B0aW9ucy5lbnRlcigpLmFwcGVuZCgnb3B0aW9uJykpXG4gICAgLmF0dHIoJ3ZhbHVlJywgZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGR9KVxuICAgIC50ZXh0KGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkfSlcblxuICAgIHZhclxuICAgIGZpbHRlckJ1dHRvbiA9IHNlbGVjdEFwcGVuZEJ1dHRvbixcbiAgICBjbG9zZUJ1dHRvbiA9IGlucHV0QXBwZW5kQnV0dG9uXG5cbiAgICBmaWx0ZXJCdXR0b24ub24oJ2NsaWNrJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICB2YXIgY3VycmVudFN0eWxlID0gaW5wdXRHcm91cC5jbGFzc2VkKCdkLW5vbmUnKVxuICAgICAgaW5wdXRHcm91cC5jbGFzc2VkKCdkLW5vbmUnLCAhY3VycmVudFN0eWxlKVxuICAgIH0pXG5cbiAgICBjbG9zZUJ1dHRvbi5vbignY2xpY2snLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIGlucHV0LnByb3BlcnR5KCd2YWx1ZScsICcnKS5kaXNwYXRjaCgnaW5wdXQnKVxuICAgIH0pXG5cbiAgICBpbnB1dC5vbignaW5wdXQnLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhclxuICAgICAgdmFsID0gaW5wdXQucHJvcGVydHkoJ3ZhbHVlJyksXG4gICAgICByZWcgPSBuZXcgUmVnRXhwKHZhbCwgJ2dpJyksXG4gICAgICB1c2VcblxuICAgICAgaWYgKHZhbCA9PSAnJykge3VzZSA9IGtleXN9XG4gICAgICBlbHNlIHtcbiAgICAgICAgdXNlID0gW11cbiAgICAgICAgZDMua2V5cyhkYXRhKS5tYXAoZnVuY3Rpb24ob3B0aW9uLCBqKXtcbiAgICAgICAgICB2YXIgbWF0Y2ggPSBvcHRpb24ubWF0Y2gocmVnKVxuICAgICAgICAgIGlmIChtYXRjaCA9PSBudWxsIHx8IG1hdGNoLmpvaW4oJycpID09ICcnKSB7fVxuICAgICAgICAgIGVsc2UgeyB1c2UucHVzaChvcHRpb24pIH1cbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgb3B0aW9ucyA9IHNlbGVjdC5zZWxlY3RBbGwoJ29wdGlvbicpXG4gICAgICBvcHRpb25zID0gb3B0aW9ucy5kYXRhKHVzZSlcbiAgICAgIG9wdGlvbnMuZXhpdCgpLnJlbW92ZSgpXG4gICAgICBvcHRpb25zID0gb3B0aW9ucy5tZXJnZShvcHRpb25zLmVudGVyKCkuYXBwZW5kKCdvcHRpb24nKSlcbiAgICAgIC5hdHRyKCd2YWx1ZScsIGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkfSlcbiAgICAgIC50ZXh0KGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkfSlcblxuICAgICAgdmFyIGN1cnJlbnQgPSBjdXJyZW50T3B0aW9uKClcbiAgICAgIGlmIChsYXN0VmFsdWUgIT0gY3VycmVudCkge1xuICAgICAgICBsYXN0VmFsdWUgPSBjdXJyZW50XG4gICAgICAgIHNlbGVjdC5kaXNwYXRjaCgnY2hhbmdlJylcbiAgICAgIH1cbiAgICB9KVxuXG5cbiAgfVxuXG4gIGZ1bmN0aW9uIGN1cnJlbnRPcHRpb24oKSB7XG4gICAgdmFyIHZhbCA9IHNlbGVjdGlvbi5zZWxlY3QoXCJzZWxlY3RcIikucHJvcGVydHkoJ3ZhbHVlJylcbiAgICByZXR1cm4gdmFsID09IHVuZGVmaW5lZCB8fCB2YWwgPT0gJydcbiAgICA/IGRlZmF1bHRWYWx1ZSA9PSB1bmRlZmluZWRcbiAgICAgID8gZDMua2V5cyhkYXRhKVswXVxuICAgICAgOiBkZWZhdWx0VmFsdWVcbiAgICA6IHZhbFxuICB9XG5cbiAgcmV0dXJuIHNlbGVjdEZpbHRlclxufVxuIiwiaW1wb3J0IHtoeXBlbmF0ZSwgc2FmZVNlbGVjdCwgZXVjbGlkZWFuRGlzdGFuY2V9IGZyb20gJy4vaGVscGVycyc7XG5pbXBvcnQge3NldHVwQ29udGFpbmVyLCBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0LCBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7dW5pcXVlLCBoYXNRLCBmbGF0dGVuLCB3aGljaEJpbn0gZnJvbSAnLi9hcnJheS1mdW5jdGlvbnMnO1xuaW1wb3J0IHtncm91cGluZ1NwYWNlcn0gZnJvbSAnLi9ncm91cGluZy1zcGFjZXInO1xuaW1wb3J0IHtjb2xvckZ1bmN0aW9uIGFzIENGfSBmcm9tICcuL2NvbG9yLWZ1bmN0aW9uJztcbmltcG9ydCB7dG9vbHRpcCBhcyBUVGlwfSBmcm9tICcuL3Rvb2x0aXAnO1xuaW1wb3J0ICcuL2QzLXByb3RvdHlwZXMnO1xuXG5mdW5jdGlvbiBnZXRUcmFuc2xhdGlvbihzZWxlY3Rpb24pe1xuICB2YXIgdHJhbnNmb3JtID0gc2VsZWN0aW9uLmF0dHIoJ3RyYW5zZm9ybScpXG4gIHZhciBbanVuaywgeHldID10cmFuc2Zvcm0uc3BsaXQoJ3RyYW5zbGF0ZSgnKVxuICB2YXIgW3gsIHldID0geHkuc3BsaXQoJywnKVxuICB5LCBqdW5rID0geS5zcGxpdCgnKScpXG4gIHJldHVybiBbcGFyc2VGbG9hdCh4KSwgcGFyc2VGbG9hdCh5KV1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxhc3NvKCBzZWxlY3Rpb24gKSB7XG4gIHZhclxuICBzdmcsIC8vIHN2ZyB0aGF0IGlzIHRhcmdldCBvZiBldmVudHNcbiAgb2JqZWN0Q29udGFpbmVyLCAvLyBjb250YWluZXIgd2hpY2ggaG91c2VzIG9iamVjdHMgd2UgYXJlIHNlbGVjdGluZyAoYWxsb3dzIGZvciB0cmFuc2Zvcm0gdG8gYmUgYXBwbGllZCB0byBsYXNzbylcbiAgb2JqZWN0Q2xhc3MsIC8vIGNsYXNzIG9mIG9iamVjdCB3ZSBhcmUgc2VsZWN0aW5nXG4gIG5hbWVzcGFjZT1cImQzc20tbGFzc29cIixcbiAgY2hhcnRDb250YWluZXIsXG4gIGNoYXJ0T2Zmc2V0LFxuICBvYmplY3RzT2Zmc2V0LFxuICBldmVudENhdGNoZXIsXG5cbiAgeFNjYWxlLCAvLyBvcHRpb25hbCBzY2FsZSBmb3IgdGhlIGxhc3NvIGN1cnJlbnRQb2ludHNcbiAgeVNjYWxlLCAvLyBvcHRpb25hbCBzY2FsZSBmb3IgdGhlIGxhc3NvIGN1cnJlbnRQb2ludHNcblxuICBhY3RpdmVRID0gZmFsc2UsIC8vIHdoZXRoZXIgb3Igbm90IGxhc3NvIGlzIGFjdGl2ZVxuXG4gIGN1cnJlbnRQb2ludHM9W10sIC8vIG1vdXNlIHBvaW50cyBmb3IgY3VycmVudCBsYXNzb1xuICBhbGxQb2ludHM9W10sIC8vIGxpc3Qgb2YgbGlzdHMgZm9yIGFsbCBwb2ludHMgb2YgbGFzc29zXG5cbiAgbGluZSA9IGQzLmxpbmUoKVxuICAueChmdW5jdGlvbihkLCBpKXtcbiAgICB2YXIgeFxuICAgIGlmICh4U2NhbGUgIT0gdW5kZWZpbmVkKSB7IHggPSB4U2NhbGUoZFswXSkgfVxuICAgIGVsc2Uge3ggPSBkWzBdfVxuICAgIHJldHVybiB4IC8vLSBjaGFydE9mZnNldFswXS8vIC0gb2JqZWN0c09mZnNldFswXVxuICB9KVxuICAueShmdW5jdGlvbihkLCBpKXtcbiAgICB2YXIgeVxuICAgIGlmICh5U2NhbGUgIT0gdW5kZWZpbmVkKSB7IHk9IHlTY2FsZShkWzFdKSB9XG4gICAgZWxzZSB7eSA9ICBkWzFdfVxuICAgIHJldHVybiB5Ly8gLSBjaGFydE9mZnNldFsxXS8vIC0gb2JqZWN0c09mZnNldFsxXVxuICB9KVxuICAuY3VydmUoZDMuY3VydmVMaW5lYXJDbG9zZWQpLFxuXG4gIGluc3RhbmNlPTAsICAgIC8vIGFuIGluZGVudGlmaWVyIGZvciB3aGljaCBpbnN0YW5jZSB0aGlzIGxhc3NvIGlzIHVuZGVyIHRoZSBjdXJyZW50IHN2Z1xuXG4gIHRpY2tEaXN0YW5jZSA9IDEwLFxuXG4gIC8vIHN0eWxlcyBmb3IgbGFzc28gcGF0aFxuICBjb2xvciA9ICcjMTdhMmI4JyxcbiAgYW5pbWF0aW9uUmF0ZSA9ICcxMHMnLFxuICBvcGFjaXR5PTAuMyxcbiAgZGFzaEFycmF5ID0gJzUsIDEwJyxcbiAgc3Ryb2tlID0gJ2JsYWNrJyxcbiAgc3Ryb2tlV2lkdGg9MixcblxuICAvLyBzdHlsZXMgZm9yIGxhc3NvZWQgb2JqZWN0c1xuICBsYXNzb2VkRmlsbCA9IFwid2hpdGVcIixcbiAgbGFzc29lZFN0cm9rZSA9ICdibGFjaycsXG4gIGxhc3NvZWRTdHJva2VXaWR0aCA9IDMsXG5cbiAgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMCxcbiAgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwXG5cbiAgdmFyIHBhdGhcblxuICBsYXNzby5zdmcgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHN2ZyA9IF8sIGxhc3NvKSA6IHN2ZzsgfVxuICBsYXNzby5jaGFydENvbnRhaW5lciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY2hhcnRDb250YWluZXIgPSBfLCBsYXNzbykgOiBjaGFydENvbnRhaW5lcjsgfVxuICBsYXNzby5vYmplY3RDb250YWluZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdENvbnRhaW5lciA9IF8sIGxhc3NvKSA6IG9iamVjdENvbnRhaW5lcjsgfVxuICBsYXNzby5vYmplY3RDbGFzcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3MgPSBfLCBsYXNzbykgOiBvYmplY3RDbGFzczsgfVxuICBsYXNzby5uYW1lc3BhY2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG5hbWVzcGFjZSA9IF8sIGxhc3NvKSA6IG5hbWVzcGFjZTsgfVxuICBsYXNzby54U2NhbGUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhTY2FsZSA9IF8sIGxhc3NvKSA6IHhTY2FsZTsgfVxuICBsYXNzby55U2NhbGUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlTY2FsZSA9IF8sIGxhc3NvKSA6IHlTY2FsZTsgfVxuICBsYXNzby5hY3RpdmVRID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChhY3RpdmVRID0gXywgbGFzc28pIDogYWN0aXZlUTsgfVxuICBsYXNzby5jdXJyZW50UG9pbnRzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjdXJyZW50UG9pbnRzID0gXywgbGFzc28pIDogY3VycmVudFBvaW50czsgfVxuICBsYXNzby5hbGxQb2ludHMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGFsbFBvaW50cyA9IF8sIGxhc3NvKSA6IGFsbFBvaW50czsgfVxuICBsYXNzby5pbnN0YW5jZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoaW5zdGFuY2UgPSBfLCBsYXNzbykgOiBpbnN0YW5jZTsgfVxuICBsYXNzby50aWNrRGlzdGFuY2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tEaXN0YW5jZSA9IF8sIGxhc3NvKSA6IHRpY2tEaXN0YW5jZTsgfVxuICBsYXNzby5jb2xvciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY29sb3IgPSBfLCBsYXNzbykgOiBjb2xvcjsgfVxuICBsYXNzby5hbmltYXRpb25SYXRlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChhbmltYXRpb25SYXRlID0gXywgbGFzc28pIDogYW5pbWF0aW9uUmF0ZTsgfVxuICBsYXNzby5vcGFjaXR5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvcGFjaXR5ID0gXywgbGFzc28pIDogb3BhY2l0eTsgfVxuICBsYXNzby5kYXNoQXJyYXkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRhc2hBcnJheSA9IF8sIGxhc3NvKSA6IGRhc2hBcnJheTsgfVxuICBsYXNzby5zdHJva2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHN0cm9rZSA9IF8sIGxhc3NvKSA6IHN0cm9rZTsgfVxuICBsYXNzby5sYXNzb2VkRmlsbCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobGFzc29lZEZpbGwgPSBfLCBsYXNzbykgOiBsYXNzb2VkRmlsbDsgfVxuICBsYXNzby5sYXNzb2VkU3Ryb2tlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChsYXNzb2VkU3Ryb2tlID0gXywgbGFzc28pIDogbGFzc29lZFN0cm9rZTsgfVxuICBsYXNzby5sYXNzb2VkU3Ryb2tlV2lkdGggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGxhc3NvZWRTdHJva2VXaWR0aCA9IF8sIGxhc3NvKSA6IGxhc3NvZWRTdHJva2VXaWR0aDsgfVxuICBsYXNzby5ldmVudENhdGNoZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGV2ZW50Q2F0Y2hlciA9IF8sIGxhc3NvKSA6IGV2ZW50Q2F0Y2hlcjsgfVxuXG4gIGxhc3NvLmRyYWcgPSBkcmFnXG4gIGxhc3NvLmRyYXcgPSBkcmF3XG4gIGxhc3NvLnRpY2sgPSB0aWNrXG4gIGxhc3NvLmRldGVjdCA9IGRldGVjdFxuICBsYXNzby50b2dnbGUgPSB0b2dnbGVcbiAgbGFzc28ucmVtb3ZlID0gcmVtb3ZlXG4gIGxhc3NvLnJlbmRlciA9IHJlbmRlclxuICBsYXNzby5rZXlGcmFtZXMgPSBrZXlGcmFtZXNcbiAgbGFzc28udXBkYXRlT2JqZWN0cyA9IHVwZGF0ZU9iamVjdHNcbiAgbGFzc28uYXBwbHlQYXRoQXR0cmlidXRlcyA9IGFwcGx5UGF0aEF0dHJpYnV0ZXNcbiAgbGFzc28uYXBwbHlPYmplY3RBdHRyaWJ1dGVzID0gYXBwbHlPYmplY3RBdHRyaWJ1dGVzXG5cbiAga2V5RnJhbWVzKClcblxuICBmdW5jdGlvbiBsYXNzbygpIHtcbiAgICAvLyBhZGQgYSBkYXNoIGFuaW1hdGlvbiBpZiBuZWVkZWRcbiAgICBpZiAoYWN0aXZlUSkgeyB0cmFuc2l0aW9uRHJhdygpIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHRvZ2dsZShzdGF0ZSkge1xuICAgIC8vIHVzZSBvcHRpb25hbCBwYXJhbSB0byBzZXQgc3RhdGUsIG90aGVyd2lzZSB0b2dnbGUgc3RhdGVcbiAgICBhY3RpdmVRID0gKHN0YXRlIT11bmRlZmluZWQpID8gc3RhdGUgOiAhYWN0aXZlUVxuICAgIGNoYXJ0T2Zmc2V0ID0gZ2V0VHJhbnNsYXRpb24oY2hhcnRDb250YWluZXIpXG4gICAgb2JqZWN0c09mZnNldCA9IGdldFRyYW5zbGF0aW9uKG9iamVjdENvbnRhaW5lcilcblxuICAgIGlmIChhY3RpdmVRKSB7XG4gICAgICBzdmcubm9kZSgpLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIHJlbmRlciwgdHJ1ZSlcbiAgICB9IGVsc2Uge1xuICAgICAgc3ZnLm5vZGUoKS5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZWRvd24nLCByZW5kZXIsIHRydWUpXG4gICAgICByZW1vdmUoKVxuICAgIH1cblxuICB9XG5cbiAgZnVuY3Rpb24gZHJhdygpIHtcbiAgICBjaGFydE9mZnNldCA9IGdldFRyYW5zbGF0aW9uKGNoYXJ0Q29udGFpbmVyKVxuICAgIG9iamVjdHNPZmZzZXQgPSBnZXRUcmFuc2xhdGlvbihvYmplY3RDb250YWluZXIpXG5cbiAgICB2YXIgY29udGFpbmVyID0gc2FmZVNlbGVjdChvYmplY3RDb250YWluZXIsICdnJywgJ2xhc3NvLWNvbnRhaW5lcicpXG4gICAgdmFyIHBhdGhzID0gY29udGFpbmVyLnNlbGVjdEFsbCgncGF0aFtpbnN0YW5jZT1cIicraW5zdGFuY2UrJ1wiXScpXG5cbiAgICAvLyB1cGRhdGVcbiAgICBwYXRocyA9IHBhdGhzLmRhdGEoYWxsUG9pbnRzKVxuXG4gICAgLy8gcmVtb3ZlIGV4Y2Vzc1xuICAgIHZhciBwRXhpdCA9IHBhdGhzLmV4aXQoKS5yZW1vdmUoKVxuICAgIC8vIGFkZCBuZWVkZWQgcGF0aHNcbiAgICB2YXIgcEVudGVyID0gcGF0aHMuZW50ZXIoKS5hcHBlbmQoJ3BhdGgnKVxuXG4gICAgLy8gbWVyZ2VcbiAgICBwYXRocyA9IHBhdGhzLm1lcmdlKHBFbnRlcilcblxuICAgIC8vIGFwcGx5XG4gICAgYXBwbHlQYXRoQXR0cmlidXRlcyhwYXRocylcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbW92ZSgpIHtcbiAgICB2YXIgY29udGFpbmVyID0gc2FmZVNlbGVjdChvYmplY3RDb250YWluZXIsICdnJywgJ2xhc3NvLWNvbnRhaW5lcicpXG4gICAgdmFyIHBhdGhzID0gY29udGFpbmVyLnNlbGVjdEFsbCgncGF0aFtpbnN0YW5jZT1cIicraW5zdGFuY2UrJ1wiXScpLnJlbW92ZSgpXG4gICAgY29udGFpbmVyLnJlbW92ZSgpXG4gICAgb2JqZWN0Q29udGFpbmVyLnNlbGVjdEFsbChvYmplY3RDbGFzcykuY2xhc3NlZChcImluLWxhc3NvXCIsIGZhbHNlKVxuICAgIHVwZGF0ZU9iamVjdHMoKVxuICB9XG5cbiAgZnVuY3Rpb24gcmVuZGVyKCBldmVudCApIHtcbiAgICAvLyBub3RoaW5nIGNhbiBpbnRlcmVmZXIgd2l0aCBkcmF3aW5nIHRoZSBsYXNzb1xuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7IGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuXG4gICAgdmFyIGNvbnRhaW5lciA9IHNhZmVTZWxlY3Qob2JqZWN0Q29udGFpbmVyLCAnZycsICdsYXNzby1jb250YWluZXInKVxuXG4gICAgLypcbiAgICBlYWNoIHRpbWUgdGhlIHVzZXIgcHJlc3NlcyBkb3duLCB3aGlsZSB0aGUgc3RhdGUgaXMgYWN0aXZlLCB0aGUgbGFzc29cbiAgICB0aGUgbGFzc28gc2hvdWxkIG1ha2UgYSBzZXBlcmF0ZSBzZWdtZW50LlxuICAgICovXG4gICAgY3VycmVudFBvaW50cyA9IFtdO1xuXG4gICAgc3ZnLm5vZGUoKS5hZGRFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCBkcmFnKVxuICAgIHN2Zy5ub2RlKCkuYWRkRXZlbnRMaXN0ZW5lcignbW91c2V1cCcsIGZ1bmN0aW9uKGV2ZW50KSB7XG4gICAgICBzdmcubm9kZSgpLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNlbW92ZScsIGRyYWcpXG4gICAgICBhbGxQb2ludHMucHVzaChjdXJyZW50UG9pbnRzKVxuICAgICAgLy8gQlVHOiAgc29tZWhvdyB0aGlzIGlzIHB1c2hpbmcgY3VycmVudFBvaW50cyBuIHRpbWVzIHdoZXJlIG4gaXMgdGhlIG50aCBsYXNzbyBwYXRoIGZvciB0aGUgY3VycmVudCBpbnN0YW5jZVxuICAgICAgLy8gTk9URTogYWxsUG9pbnRzID0gdW5pcXVlKGFsbFBvaW50cykgaXMgYSB0ZW1wb3JhcnkgYW5kIGluZWZmaWNpZW50IGZpeFxuICAgICAgYWxsUG9pbnRzID0gdW5pcXVlKGFsbFBvaW50cylcbiAgICB9KVxuXG4gICAgcGF0aCA9IGNvbnRhaW5lci5hcHBlbmQoJ3BhdGgnKS5kYXRhKFtjdXJyZW50UG9pbnRzXSlcbiAgICBhcHBseVBhdGhBdHRyaWJ1dGVzKHBhdGgpXG4gIH1cblxuICBmdW5jdGlvbiB0cmFuc2l0aW9uRHJhdygpIHtcbiAgICB2YXIgY29udGFpbmVyID0gc2FmZVNlbGVjdChvYmplY3RDb250YWluZXIsICdnJywgJ2xhc3NvLWNvbnRhaW5lcicpXG4gICAgdmFyIHBhdGhzID0gY29udGFpbmVyLnNlbGVjdEFsbCgncGF0aFtpbnN0YW5jZT1cIicraW5zdGFuY2UrJ1wiXScpXG5cbiAgICAvLyB1cGRhdGVcbiAgICBwYXRocyA9IHBhdGhzLmRhdGEoYWxsUG9pbnRzKVxuXG4gICAgLy8gcmVtb3ZlIGV4Y2Vzc1xuICAgIHZhciBwRXhpdCA9IHBhdGhzLmV4aXQoKS5yZW1vdmUoKVxuICAgIC8vIGFkZCBuZWVkZWQgcGF0aHNcbiAgICB2YXIgcEVudGVyID0gcGF0aHMuZW50ZXIoKS5hcHBlbmQoJ3BhdGgnKVxuXG4gICAgLy8gbWVyZ2VcbiAgICBwYXRocyA9IHBhdGhzLm1lcmdlKHBFbnRlcilcbiAgICAudHJhbnNpdGlvbigpLmR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbilcbiAgICAuZWFzZShlYXNlRnVuYylcbiAgICBhcHBseVBhdGhBdHRyaWJ1dGVzKHBhdGhzKVxuXG4gIH1cblxuICBmdW5jdGlvbiBhcHBseVBhdGhBdHRyaWJ1dGVzKHBhdGgpIHtcbiAgICBwYXRoXG4gICAgLmF0dHIoXCJjbGFzc1wiLCBoeXBlbmF0ZShuYW1lc3BhY2UsIFwibGFzc28tcGF0aFwiKSlcbiAgICAuc3R5bGUoJ29wYWNpdHknLCBvcGFjaXR5KVxuICAgIC5hdHRyKCdmaWxsJywgY29sb3IpXG4gICAgLmF0dHIoXCJkXCIsIGxpbmUpXG4gICAgLmF0dHIoJ2luc3RhbmNlJywgaW5zdGFuY2UpXG4gICAgLnN0eWxlKFwic3Ryb2tlLWRhc2hhcnJheVwiLCBkYXNoQXJyYXkpXG4gICAgLmF0dHIoXCJzdHJva2VcIiwgc3Ryb2tlKVxuICAgIC5hdHRyKFwic3Ryb2tlLXdpZHRoXCIsIHN0cm9rZVdpZHRoKVxuICAgIC5zdHlsZSgnYW5pbWF0aW9uJywgJ2xhc3NvRGFzaCAnK2FuaW1hdGlvblJhdGUrJyBsaW5lYXInKVxuICAgIC5zdHlsZShcImFuaW1hdGlvbi1pdGVyYXRpb24tY291bnRcIiwgXCJpbmZpbml0ZVwiKVxuICB9XG5cbiAgZnVuY3Rpb24gZHJhZyhldmVudCkge1xuICAgIC8qXG4gICAgZWZmZWN0aXZlbHkgY3JlYXRlIGEgbW91c2UgZG93biBhbmQgbW92ZSBldmVudCAod2hpY2ggbm9ybWFsbHkgaXMgaW50ZXBlcmF0ZWRcbiAgICBhcyAnZHJhZycgYnkgdGhlIGJyb3dzZXIpIGJ5IGR5bmFtaWNhbGx5IGFkZGluZyAvIHJlbW92aW5nIHRoaXMgZXZlbnQgb25cbiAgICBtb3VzZSBkb3duIC8gbW91c2UgdXAuXG4gICAgKi9cblxuICAgIGlmIChldmVudENhdGNoZXIgIT0gdW5kZWZpbmVkKSB7ZXZlbnRDYXRjaGVyLmRpc3BhdGNoKGh5cGVuYXRlKG5hbWVzcGFjZSxcImRyYWdcIikpfVxuICAgIC8vIGQzLmRpc3BhdGNoKGh5cGVuYXRlKG5hbWVzcGFjZSxcImRyYWdcIikpXG5cbiAgICBpZiAoZXZlbnQud2hpY2ggIT0gMSkge3JldHVybn0gLy8gZW5zdXJlcyBsZWZ0IG1vdXNlIGJ1dHRvbiBzZXRcbiAgICBkMy5ldmVudCA9IGV2ZW50XG4gICAgdmFyIHB0ID0gZDMubW91c2Uob2JqZWN0Q29udGFpbmVyLm5vZGUoKSk7XG4gICAgdmFyIHB0ID0gZDMubW91c2Uoc3ZnLm5vZGUoKSk7XG5cbiAgICBpZiAoeFNjYWxlICE9IHVuZGVmaW5lZCkge3B0WzBdID0geFNjYWxlLmludmVydChwdFswXSl9XG4gICAgaWYgKHlTY2FsZSAhPSB1bmRlZmluZWQpIHtwdFsxXSA9IHlTY2FsZS5pbnZlcnQocHRbMV0pfVxuICAgIHB0WzBdID0gcHRbMF0gLSBjaGFydE9mZnNldFswXSAtIG9iamVjdHNPZmZzZXRbMF1cbiAgICBwdFsxXSA9IHB0WzFdIC0gY2hhcnRPZmZzZXRbMV0gLSBvYmplY3RzT2Zmc2V0WzFdXG5cbiAgICAvKiBpZiB3ZSBoYXZlIGEgcG9pbnQgYWxyZWFkeSwgdGVzdCBpZiBpdCBwYXNzZXMgYSBtaW5pbXVtIGRpc3RhbmNlIHRvIHByZXZlbnQgb3ZlcndoZWxtaW5nIHdpdGggdG9vIG1hbnkgdGljayBmdW5jdGlvbnMgKi9cbiAgICBpZiAoY3VycmVudFBvaW50cy5sZW5ndGgpIHtcbiAgICAgIHZhciBsYXN0UHQgPSBjdXJyZW50UG9pbnRzW2N1cnJlbnRQb2ludHMubGVuZ3RoIC0gMV1cbiAgICAgIHZhciBhID0gW3B0WzBdLCBwdFsxXV0sIGIgPSBbbGFzdFB0WzBdLCBsYXN0UHRbMV1dXG5cbiAgICAgIGlmICh4U2NhbGUpIHtiWzBdID0geFNjYWxlKGJbMF0pOyBhWzBdID0geFNjYWxlKGFbMF0pfVxuICAgICAgaWYgKHlTY2FsZSkge2JbMV0gPSB5U2NhbGUoYlsxXSk7IGFbMV0gPSB5U2NhbGUoYVsxXSl9XG5cbiAgICAgIHZhciBkaXN0ID0gZXVjbGlkZWFuRGlzdGFuY2UoYiwgYSlcbiAgICAgIGlmIChkaXN0ID4gdGlja0Rpc3RhbmNlKSB7IHRpY2socHQpIH1cbiAgICB9XG4gICAgZWxzZSB7IHRpY2socHQpIH1cbiAgfVxuXG5cbiAgZnVuY3Rpb24gdGljayAocHQpIHtcbiAgICAvKlxuICAgIElmIGEgcG9pbnQgaXMgcHJvdmlkZWQgdXBkYXRlIGRhdGEgYW5kIG9iamVjdHMuXG4gICAgT3RoZXJ3aXNlIGp1c3QgY2FsbCBvbiBkYXRhIHdlIGFscmVhZHkgaGF2ZS5cblxuICAgIFdoeSBsaWtlIHRoaXM/OlxuICAgIDEuIGN1cnJlbnRQb2ludHMgaXMgY3VycmVudCBwb2ludHMgdG8gYWxsb3cgZGlzanVuY3QgbGFzc29zLCBjdXJyZW50UG9pbnRzIGlzIG9ubHkgcHVzaGVkIHRvXG4gICAgYWxsUG9pbnRzIGFmdGVyIG1vdXNldXAuXG4gICAgMi4gdG8gYWxsb3cgcmVuZGVyIG9mIG9iamVjdHMgaW4gdGhlIGxhc3NvIGNsYXNzIC8gdXBkYXRpbmcgdGhlIGRhdGEgbGlzdFxuICAgIGp1c3QgYnkgdG9nZ2xpbmcgdGhlIGJ1dHRvblxuICAgICovXG5cbiAgICBpZiAocHQgIT0gdW5kZWZpbmVkKSB7XG4gICAgICBjdXJyZW50UG9pbnRzLnB1c2gocHQpO1xuICAgICAgcGF0aC5hdHRyKFwiZFwiLCBsaW5lKTtcbiAgICAgIGlmIChjdXJyZW50UG9pbnRzLmxlbmd0aCA8IDMpIHtyZXR1cm59IC8vIG5lZWQgYXQgbGVhc3QgMyBwb2ludHMgdG8gZGV0ZWN0IGFueXRoaW5nLlxuICAgICAgZGV0ZWN0KGFsbFBvaW50cy5jb25jYXQoW2N1cnJlbnRQb2ludHNdKSlcbiAgICB9IGVsc2Uge1xuICAgICAgZGV0ZWN0KGFsbFBvaW50cylcbiAgICB9XG4gIH1cblxuXG4gIGZ1bmN0aW9uIGRldGVjdChsYXNzb3MpIHtcbiAgICBpZiAobGFzc29zID09IHVuZGVmaW5lZCkge2xhc3NvcyA9IGFsbFBvaW50c31cbiAgICBvYmplY3RDb250YWluZXIuc2VsZWN0QWxsKG9iamVjdENsYXNzKS5lYWNoKGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgdmFyIGN1cnJlbnQgPSBkMy5zZWxlY3QodGhpcyksXG5cbiAgICAgIGJveCA9IGN1cnJlbnQuYWJzb2x1dGVQb3NpdGlvbigpLFxuICAgICAgLy8gYm94ID0gY3VycmVudC5yZWxhdGl2ZVBvc2l0aW9uVG8ob2JqZWN0Q29udGFpbmVyLm5vZGUoKSksXG5cbiAgICAgIGJveFB0cyA9IFtcbiAgICAgICAgW1xuICAgICAgICAgIGJveC5sZWZ0IC0gY2hhcnRPZmZzZXRbMF0gLSBvYmplY3RzT2Zmc2V0WzBdLFxuICAgICAgICAgIGJveC50b3AgLSBjaGFydE9mZnNldFsxXSAtIG9iamVjdHNPZmZzZXRbMV1cbiAgICAgICAgXSxcbiAgICAgICAgW1xuICAgICAgICAgIGJveC5yaWdodCAtIGNoYXJ0T2Zmc2V0WzBdIC0gb2JqZWN0c09mZnNldFswXSxcbiAgICAgICAgICBib3gudG9wIC0gY2hhcnRPZmZzZXRbMV0gLSBvYmplY3RzT2Zmc2V0WzFdXG4gICAgICAgIF0sXG4gICAgICAgIFtcbiAgICAgICAgICBib3gubGVmdCAtIGNoYXJ0T2Zmc2V0WzBdIC0gb2JqZWN0c09mZnNldFswXSxcbiAgICAgICAgICBib3guYm90dG9tIC0gY2hhcnRPZmZzZXRbMV0gLSBvYmplY3RzT2Zmc2V0WzFdXG4gICAgICAgIF0sXG4gICAgICAgIFtcbiAgICAgICAgICBib3gucmlnaHQgLSBjaGFydE9mZnNldFswXSAtIG9iamVjdHNPZmZzZXRbMF0sXG4gICAgICAgICAgYm94LmJvdHRvbSAtIGNoYXJ0T2Zmc2V0WzFdIC0gb2JqZWN0c09mZnNldFsxXVxuICAgICAgICBdXG4gICAgICBdXG5cbiAgICAgIGlmICh4U2NhbGUgIT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGJveFB0c1swXVswXSA9IHhTY2FsZS5pbnZlcnQoYm94UHRzWzBdWzBdKVxuICAgICAgICBib3hQdHNbMV1bMF0gPSB4U2NhbGUuaW52ZXJ0KGJveFB0c1sxXVswXSlcbiAgICAgICAgYm94UHRzWzJdWzBdID0geFNjYWxlLmludmVydChib3hQdHNbMl1bMF0pXG4gICAgICAgIGJveFB0c1szXVswXSA9IHhTY2FsZS5pbnZlcnQoYm94UHRzWzNdWzBdKVxuICAgICAgfVxuICAgICAgaWYgKHlTY2FsZSAhPSB1bmRlZmluZWQpIHtcbiAgICAgICAgYm94UHRzWzBdWzFdID0geVNjYWxlLmludmVydChib3hQdHNbMF1bMV0pXG4gICAgICAgIGJveFB0c1sxXVsxXSA9IHlTY2FsZS5pbnZlcnQoYm94UHRzWzFdWzFdKVxuICAgICAgICBib3hQdHNbMl1bMV0gPSB5U2NhbGUuaW52ZXJ0KGJveFB0c1syXVsxXSlcbiAgICAgICAgYm94UHRzWzNdWzFdID0geVNjYWxlLmludmVydChib3hQdHNbM11bMV0pXG4gICAgICB9XG5cblxuICAgICAgLypcbiAgICAgIGZsYWcgbmVlZGVkIGFzIHdlIGhhdmUgdG8gdGVzdCBtdWx0aXBsZSBsYXNzbyBzZWdtZW50cywgYW5kIGlmIHRoZSBwb2ludFxuICAgICAgaXMgbm90IGluIG9uZSBzZWdtZW50LCBpdCBkb2VzIG5vdCBtZWFuIGl0IGlzIG5vdCBpbiBhbnlcbiAgICAgICovXG4gICAgICB2YXIgaW5BbnlMYXNzb1EgPSBmYWxzZTtcbiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgbGFzc29zLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHZhciBsYXNzb1BvaW50cyA9IGxhc3Nvc1tpXVxuICAgICAgICAvLyAubWFwKGZ1bmN0aW9uKHB0KXtcbiAgICAgICAgLy8gICB2YXIgeCwgeSA9IHB0XG4gICAgICAgIC8vICAgaWYgKHhTY2FsZSE9dW5kZWZpbmVkKSB7eCA9IHhTY2FsZSh4KX1cbiAgICAgICAgLy8gICBpZiAoeVNjYWxlIT11bmRlZmluZWQpIHt5ID0geVNjYWxlKHkpfVxuICAgICAgICAvLyAgIHJldHVybiBbeCwgeV1cbiAgICAgICAgLy8gfSlcbiAgICAgICAgdmFyIGJveEluTGFzc29RID0gYm94UHRzLmV2ZXJ5KGNvb3JkID0+IGQzLnBvbHlnb25Db250YWlucyhsYXNzb1BvaW50cywgY29vcmQpKVxuXG4gICAgICAgIGlmIChib3hJbkxhc3NvUSkgeyBpbkFueUxhc3NvUSA9IHRydWU7IH0gLy8gb25seSB1cGRhdGUgZmxhZyBpbiB0aGUgcG9zaXRpdmUgY2FzZS5cbiAgICAgIH1cblxuICAgICAgY3VycmVudC5jbGFzc2VkKCdpbi1sYXNzbycsIGluQW55TGFzc29RKVxuICAgICAgY3VycmVudC5jbGFzc2VkKCdpbi1sYXNzby0nK2luc3RhbmNlLCBpbkFueUxhc3NvUSlcbiAgICB9KVxuXG4gICAgdXBkYXRlT2JqZWN0cygpXG4gICAgcmV0dXJuIG9iamVjdENvbnRhaW5lci5zZWxlY3RBbGwoJy5pbi1sYXNzby0nK2luc3RhbmNlKVxuICB9XG5cblxuXG4gIGZ1bmN0aW9uIHVwZGF0ZU9iamVjdHMoKSB7XG4gICAgb2JqZWN0Q29udGFpbmVyLnNlbGVjdEFsbChvYmplY3RDbGFzcykuZWFjaChmdW5jdGlvbihkLCBpKSB7XG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgICAgYXBwbHlPYmplY3RBdHRyaWJ1dGVzKHQsIHQuY2xhc3NlZCgnaW4tbGFzc28nKSlcbiAgICB9KVxuICB9XG5cbiAgZnVuY3Rpb24gYXBwbHlPYmplY3RBdHRyaWJ1dGVzKG9iaiwgc2V0USkge1xuICAgIHZhclxuICAgIHByZUxhc3NvRmlsbCA9IG9iai5hdHRyKCdfcHJlX2xhc3NvX2ZpbGwnKSxcbiAgICBwcmVMYXNzb1N0cm9rZSA9IG9iai5hdHRyKCdfcHJlX2xhc3NvX3N0cm9rZScpLFxuICAgIHByZUxhc3NvU3Ryb2tlV2lkdGggPSBvYmouYXR0cignX3ByZV9sYXNzb19zdHJva2Utd2lkdGgnKVxuXG4gICAgaWYgKHNldFEpIHtcbiAgICAgIG9iai5jbGFzc2VkKFwiaW4tbGFzc29cIiwgdHJ1ZSlcbiAgICAgIG9iai5jbGFzc2VkKCdpbi1sYXNzby0nK2luc3RhbmNlLCB0cnVlKVxuICAgICAgaWYgKHByZUxhc3NvRmlsbCA9PSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ19wcmVfbGFzc29fZmlsbCcsIG9iai5hdHRyKCdmaWxsJykpIH1cbiAgICAgIGlmIChwcmVMYXNzb1N0cm9rZSA9PSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ19wcmVfbGFzc29fc3Ryb2tlJywgb2JqLmF0dHIoJ3N0cm9rZScpKSB9XG4gICAgICBpZiAocHJlTGFzc29TdHJva2VXaWR0aCA9PSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ19wcmVfbGFzc29fc3Ryb2tlLXdpZHRoJywgb2JqLmF0dHIoJ3N0cm9rZS13aWR0aCcpKSB9XG5cbiAgICAgIG9ialxuICAgICAgLy9CVUc6IHdoZW4gLnJhaXNlKClcbiAgICAgIC5hdHRyKCdmaWxsJywgbGFzc29lZEZpbGwpXG4gICAgICAuYXR0cignc3Ryb2tlJywgbGFzc29lZFN0cm9rZSlcbiAgICAgIC5hdHRyKCdzdG9rZS13aWR0aCcsIGxhc3NvZWRTdHJva2VXaWR0aClcblxuICAgIH0gZWxzZSB7XG4gICAgICBvYmouY2xhc3NlZChcImluLWxhc3NvXCIsIGZhbHNlKVxuICAgICAgb2JqLmNsYXNzZWQoJ2luLWxhc3NvLScraW5zdGFuY2UsIGZhbHNlKVxuICAgICAgaWYgKHByZUxhc3NvRmlsbCAhPSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ2ZpbGwnLCBwcmVMYXNzb0ZpbGwpIH1cbiAgICAgIGlmIChwcmVMYXNzb1N0cm9rZSAhPSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ3N0cm9rZScsIHByZUxhc3NvU3Ryb2tlKSB9XG4gICAgICBpZiAocHJlTGFzc29TdHJva2VXaWR0aCAhPSB1bmRlZmluZWQpIHsgb2JqLmF0dHIoJ3N0cm9rZS13aWR0aCcsIHByZUxhc3NvU3Ryb2tlV2lkdGgpIH1cbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBrZXlGcmFtZXMoKSB7XG4gICAgdmFyIHN0eWxlID1cbiAgICBkMy5zZWxlY3QoXCJodG1sXCIpLnNlbGVjdCgnc3R5bGUuJytoeXBlbmF0ZShuYW1lc3BhY2UsXCJsYXNzby1kYXNoXCIpKVxuICAgIGlmIChzdHlsZS5lbXB0eSgpKSB7XG4gICAgICBkMy5zZWxlY3QoXCJodG1sXCIpLmFwcGVuZCgnc3R5bGUnKVxuICAgICAgLmNsYXNzZWQoaHlwZW5hdGUobmFtZXNwYWNlLFwibGFzc28tZGFzaFwiKSwgdHJ1ZSlcbiAgICAgIC5odG1sKFwiQGtleWZyYW1lcyBsYXNzb0Rhc2gge3RvIHsgc3Ryb2tlLWRhc2hvZmZzZXQ6IDEwMDA7fX1cIilcbiAgICB9XG5cbiAgfVxuICByZXR1cm4gbGFzc29cbn1cbiIsImltcG9ydCB7Z2V0Q29udGFpbmluZ1NWR30gZnJvbSBcIi4vaGVscGVyc1wiO1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEQzIEVYVEVOU0lPTlMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qKlxuKiBSZWN1cnNpdmVseSBhc2NlbmRzIHBhcmVudHMgb2Ygc2VsZWN0aW9uIHVudGlsIGl0IGZpbmRzIGFuIHN2ZyB0YWdcbiogQGZ1bmN0aW9uIGQzLnNlbGVjdGlvbi50aGlzU1ZHXG4qIEBhdWdtZW50cyBkMy5zZWxlY3Rpb25cbiogQHJldHVybnMge0VsZW1lbnR9IHdoaWNoIGlzIHRoZSBzdmcgdGFnLCBub3QgdGhlIGQzIHNlbGVjdGlvbiBvZiB0aGF0IHRhZ1xuKi9cbmQzLnNlbGVjdGlvbi5wcm90b3R5cGUudGhpc1NWRyA9IGZ1bmN0aW9uKCkgeyByZXR1cm4gZ2V0Q29udGFpbmluZ1NWRyh0aGlzLm5vZGUoKSk7IH1cblxuXG4vKipcbiogSGVscGVyIGZvciBnZXR0aW5nIGFic29sdXRlIHBvc2l0aW9uIG9mIHRoZSBtb3VzZVxuKiBAZnVuY3Rpb24gZDMubW91c2UuYWJzb2x1dGVcbiogQGF1Z21lbnRzIGQzLm1vdXNlXG4qIEByZXR1cm5zIHtudW1iZXJbXX0gW3gsIHldIGFzIHRoZXkgcmVsYXRlIHRvIGBodG1sYCBub3QgdG8gbG9jYWwgc2NvcGUuXG4qL1xuZDMubW91c2UuYWJzb2x1dGUgPSBmdW5jdGlvbigpIHtcbiAgdmFyIGh0bWwgPSBkMy5zZWxlY3QoJ2h0bWwnKS5ub2RlKClcbiAgdmFyIFt4LCB5XSA9IHRoaXMoaHRtbClcbiAgcmV0dXJuIFt4LCB5XVxufVxuXG5cbi8qKlxuKiBHZXRzIHBvc2l0aW9uIG9mIHRoZSBzZWxlY3Rpb24gaW4gcmVsYXRpb24gdG8gdGhlIGNvbnRhaW5pbmcgc3ZnXG4qIEBzZWV7QGxpbmsgZ2V0Q29udGFpbmluZ1NWR31cbiogQGZ1bmN0aW9uIGQzLnNlbGVjdGlvbi5hYnNvbHV0ZVBvc2l0aW9uXG4qIEBhdWdtZW50cyBkMy5zZWxlY3Rpb25cbiogQHJldHVybnMge09iamVjdH0gd2l0aCBzdHJ1Y3R1cmUgc2ltaWxhciB0byBnZXRCb3VuZGluZ0NsaWVudFJlY3QsIGUuZy5cbiogdG9wLCBsZWZ0LCBib3R0b20sIHJpZ2h0LCBoZWlnaHQsIHdpZHRoXG4qL1xuZDMuc2VsZWN0aW9uLnByb3RvdHlwZS5hYnNvbHV0ZVBvc2l0aW9uID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGVsZW1lbnQgPSB0aGlzLm5vZGUoKTtcbiAgICB2YXIgZWxlbWVudFBvc2l0aW9uID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICB2YXIgY29udGFpbmVyU1ZHID0gZ2V0Q29udGFpbmluZ1NWRyhlbGVtZW50KVxuICAgIHZhciBzdmdQb3NpdGlvbiA9IGNvbnRhaW5lclNWRy5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcblxuICAgIHJldHVybiB7XG4gICAgICAgIHRvcDogICAgZWxlbWVudFBvc2l0aW9uLnRvcCAgICAtIHN2Z1Bvc2l0aW9uLnRvcCxcbiAgICAgICAgbGVmdDogICBlbGVtZW50UG9zaXRpb24ubGVmdCAgIC0gc3ZnUG9zaXRpb24ubGVmdCxcbiAgICAgICAgYm90dG9tOiBlbGVtZW50UG9zaXRpb24uYm90dG9tIC0gc3ZnUG9zaXRpb24udG9wLFxuICAgICAgICByaWdodDogIGVsZW1lbnRQb3NpdGlvbi5yaWdodCAgLSBzdmdQb3NpdGlvbi5sZWZ0LFxuICAgICAgICBoZWlnaHQ6IGVsZW1lbnRQb3NpdGlvbi5oZWlnaHQsXG4gICAgICAgIHdpZHRoOiAgZWxlbWVudFBvc2l0aW9uLndpZHRoXG4gICAgfTtcblxufVxuXG5cbmQzLnNlbGVjdGlvbi5wcm90b3R5cGUucmVsYXRpdmVQb3NpdGlvblRvID0gZnVuY3Rpb24oY29udGFpbmVyKSB7XG4gICAgdmFyIGVsZW1lbnQgPSB0aGlzLm5vZGUoKTtcbiAgICB2YXIgZWxlbWVudFBvc2l0aW9uID0gZWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICB2YXIgY29udGFpbmVyU1ZHID0gY29udGFpbmVyXG4gICAgdmFyIHN2Z1Bvc2l0aW9uID0gY29udGFpbmVyU1ZHLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgICAgdG9wOiAgICBlbGVtZW50UG9zaXRpb24udG9wICAgIC0gc3ZnUG9zaXRpb24udG9wLFxuICAgICAgICBsZWZ0OiAgIGVsZW1lbnRQb3NpdGlvbi5sZWZ0ICAgLSBzdmdQb3NpdGlvbi5sZWZ0LFxuICAgICAgICBib3R0b206IGVsZW1lbnRQb3NpdGlvbi5ib3R0b20gLSBzdmdQb3NpdGlvbi50b3AsXG4gICAgICAgIHJpZ2h0OiAgZWxlbWVudFBvc2l0aW9uLnJpZ2h0ICAtIHN2Z1Bvc2l0aW9uLmxlZnQsXG4gICAgICAgIGhlaWdodDogZWxlbWVudFBvc2l0aW9uLmhlaWdodCxcbiAgICAgICAgd2lkdGg6ICBlbGVtZW50UG9zaXRpb24ud2lkdGhcbiAgICB9O1xuXG59XG4iLCIvLyBJbXBvcnQgc3R5bGVzIChhdXRvbWF0aWNhbGx5IGluamVjdCBpbnRvIDxoZWFkPikuXG4vLyBpbXBvcnQgJy4uL3N0eWxlcy9tYWluLmNzcyc7XG5pbXBvcnQge2F4aXN9IGZyb20gJy4vbW9kdWxlcy9heGlzJztcbmltcG9ydCB7YmFyfSBmcm9tICcuL21vZHVsZXMvYmFyJztcbmltcG9ydCB7YnViYmxlSGVhdG1hcH0gZnJvbSAnLi9tb2R1bGVzL2J1YmJsZS1oZWF0bWFwJztcbmltcG9ydCB7aGVhdG1hcH0gZnJvbSAnLi9tb2R1bGVzL2hlYXRtYXAnO1xuaW1wb3J0IHtib3h3aGlza2VyfSBmcm9tICcuL21vZHVsZXMvYm94LXdoaXNrZXInO1xuaW1wb3J0IHtjb2xvckZ1bmN0aW9ufSBmcm9tICcuL21vZHVsZXMvY29sb3ItZnVuY3Rpb24nO1xuaW1wb3J0IHtkYXRhdG9nZ2xlfSBmcm9tICcuL21vZHVsZXMvZGF0YS10b2dnbGUnO1xuaW1wb3J0IHtncm91cGluZ1NwYWNlcn0gZnJvbSAnLi9tb2R1bGVzL2dyb3VwaW5nLXNwYWNlcic7XG5pbXBvcnQge3Rvb2x0aXB9IGZyb20gJy4vbW9kdWxlcy90b29sdGlwJztcbmltcG9ydCB7c2NhdHRlcn0gZnJvbSAnLi9tb2R1bGVzL3NjYXR0ZXInO1xuaW1wb3J0IHtwbG90Wm9vbX0gZnJvbSAnLi9tb2R1bGVzL3Bsb3Qtem9vbSc7XG5pbXBvcnQge211bHRpUGxvdFpvb219IGZyb20gJy4vbW9kdWxlcy9tdWx0aS1wbG90LXpvb20nO1xuaW1wb3J0IHt2aW9saW59IGZyb20gJy4vbW9kdWxlcy92aW9saW4nO1xuaW1wb3J0IHtudW1lcmljTGVnZW5kfSBmcm9tICcuL21vZHVsZXMvbnVtZXJpYy1sZWdlbmQnO1xuaW1wb3J0IHtjYXRlZ29yaWNMZWdlbmR9IGZyb20gJy4vbW9kdWxlcy9jYXRlZ29yaWNhbC1sZWdlbmQnO1xuaW1wb3J0IHtsYXNzb30gZnJvbSAnLi9tb2R1bGVzL2xhc3NvJztcbmltcG9ydCB7bGFzc29XaWRnZXR9IGZyb20gJy4vbW9kdWxlcy9sYXNzby13aWRnZXQnO1xuaW1wb3J0IHtzZWxlY3RGaWx0ZXJ9IGZyb20gJy4vbW9kdWxlcy9zZWxlY3QtZmlsdGVyJztcbmltcG9ydCB7dXBzZXR9IGZyb20gJy4vbW9kdWxlcy91cHNldCc7XG5pbXBvcnQge2ZpbHRlclRhYmxlfSBmcm9tICcuL21vZHVsZXMvZmlsdGVyLXRhYmxlJztcblxuaW1wb3J0IHt1bmlxdWVFbGVtZW50cywgZ2V0VHJhbnNsYXRpb24sIG1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2UsIHRpY2tSYW5nZSxcbnF1YXJ0aWxlcywgZXh0cmFjdFZpb2xpblZhbHVlcywgaHlwZW5hdGUsIHJvdW5kLCBnZXRDb250YWluaW5nU1ZHLFxuaW50ZXJwb2xhdGVDb2xvcnMsIHRydW5jYXRlVGV4dCwgc2FmZVNlbGVjdH0gZnJvbSAnLi9tb2R1bGVzL2hlbHBlcnMnO1xuXG5pbXBvcnQge1xuICBhbGwsIHRhbGx5LCBoYXNRLCBmaXJzdCwgbGFzdCwgdG90YWwsIHVuaXF1ZSwgZ2V0LCBsaXN0T2ZMaXN0c1EsXG4gIGN1dCwgZ3JvdXBCeSwgYXJyYXlFcXVhbHMsIGVsZW1lbnRzQXRMZXZlbHMsIG51bWJlck9mRWxlbWVudHMsXG4gIGZsYXR0ZW4sIHdoaWNoQmluXG59IGZyb20gJy4vbW9kdWxlcy9hcnJheS1mdW5jdGlvbnMnO1xuXG5cbmltcG9ydCB7XG4gIHNldHVwU3RhbmRhcmRDaGFydENvbnRhaW5lcnMsIGxvZyBhcyBteUxvZywgd2FybiwgaW5mbywgZXJyb3IsXG4gIGNvbnNvbGVHcm91cCwgY29uc29sZUdyb3VwRW5kLCByZXNpemVEZWJvdW5jZVxufSBmcm9tICcuL21vZHVsZXMvdXRpbHMnO1xuXG4vLyAvKiogQG1vZHVsZSBkM3NtICovXG52YXIgZDNzbSA9IHt9O1xuZDNzbS5heGlzID0gYXhpcztcbmQzc20uYmFyID0gYmFyO1xuZDNzbS5idWJibGVIZWF0bWFwID0gYnViYmxlSGVhdG1hcDtcbmQzc20uaGVhdG1hcCA9IGhlYXRtYXA7XG5kM3NtLmJveHdoaXNrZXIgPSBib3h3aGlza2VyO1xuZDNzbS5jb2xvckZ1bmN0aW9uID0gY29sb3JGdW5jdGlvbjtcbmQzc20uZGF0YXRvZ2dsZSA9IGRhdGF0b2dnbGU7XG5kM3NtLmdyb3VwaW5nU3BhY2VyID0gZ3JvdXBpbmdTcGFjZXI7XG5kM3NtLnRvb2x0aXAgPSB0b29sdGlwO1xuZDNzbS5zY2F0dGVyID0gc2NhdHRlcjtcbmQzc20ucGxvdFpvb20gPSBwbG90Wm9vbTtcbmQzc20ubXVsdGlQbG90Wm9vbSA9IG11bHRpUGxvdFpvb207XG5kM3NtLnZpb2xpbiA9IHZpb2xpbjtcbmQzc20ubnVtZXJpY0xlZ2VuZCA9IG51bWVyaWNMZWdlbmQ7XG5kM3NtLmNhdGVnb3JpY0xlZ2VuZCA9IGNhdGVnb3JpY0xlZ2VuZDtcbmQzc20ubGFzc28gPSBsYXNzbztcbmQzc20ubGFzc29XaWRnZXQgPSBsYXNzb1dpZGdldDtcbmQzc20uc2VsZWN0RmlsdGVyID0gc2VsZWN0RmlsdGVyO1xuZDNzbS51cHNldCA9IHVwc2V0O1xuZDNzbS5maWx0ZXJUYWJsZSA9IGZpbHRlclRhYmxlO1xuXG5kM3NtLnVuaXF1ZUVsZW1lbnRzID0gdW5pcXVlRWxlbWVudHM7XG5kM3NtLmdldFRyYW5zbGF0aW9uID0gZ2V0VHJhbnNsYXRpb247XG5kM3NtLm1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2UgPSBtb2RpZnlIZXhpZGVjaW1hbENvbG9yTHVtaW5hbmNlO1xuZDNzbS50aWNrUmFuZ2UgPSB0aWNrUmFuZ2U7XG5kM3NtLnF1YXJ0aWxlcyA9IHF1YXJ0aWxlcztcbmQzc20uZXh0cmFjdFZpb2xpblZhbHVlcyA9IGV4dHJhY3RWaW9saW5WYWx1ZXM7XG5kM3NtLmh5cGVuYXRlID0gaHlwZW5hdGU7XG5kM3NtLnJvdW5kID0gcm91bmQ7XG5kM3NtLmdldENvbnRhaW5pbmdTVkcgPSBnZXRDb250YWluaW5nU1ZHO1xuZDNzbS5pbnRlcnBvbGF0ZUNvbG9ycyA9IGludGVycG9sYXRlQ29sb3JzO1xuZDNzbS50cnVuY2F0ZVRleHQgPSB0cnVuY2F0ZVRleHQ7XG5kM3NtLnNhZmVTZWxlY3QgPSBzYWZlU2VsZWN0O1xuXG5kM3NtLndoaWNoQmluID0gd2hpY2hCaW47XG5kM3NtLnVuaXF1ZSA9IHVuaXF1ZTtcbmQzc20uZmxhdHRlbiA9IGZsYXR0ZW47XG5cbmQzc20uc2V0dXBTdGFuZGFyZENoYXJ0Q29udGFpbmVycyA9IHNldHVwU3RhbmRhcmRDaGFydENvbnRhaW5lcnM7XG5kM3NtLmxvZyA9IG15TG9nO1xuZDNzbS53YXJuID0gd2FybjtcbmQzc20uaW5mbyA9IGluZm87XG5kM3NtLmVycm9yID0gZXJyb3I7XG5kM3NtLmNvbnNvbGVHcm91cCA9IGNvbnNvbGVHcm91cDtcbmQzc20uY29uc29sZUdyb3VwRW5kID0gY29uc29sZUdyb3VwRW5kO1xuZDNzbS5yZXNpemVEZWJvdW5jZSA9IHJlc2l6ZURlYm91bmNlO1xuXG5kM3NtLmRlYnVnUSA9IGZhbHNlXG5cblxuXG4vLyBJbXBvcnQgYSBsb2dnZXIgZm9yIGVhc2llciBkZWJ1Z2dpbmdcbi8vIGltcG9ydCBkZWJ1ZyBmcm9tICdkZWJ1Zyc7XG4vLyBjb25zdCBsb2cgPSBkZWJ1ZygnYXBwOmxvZycpO1xuXG4vLyBUaGUgbG9nZ2VyIHNob3VsZCBvbmx5IGJlIGRpc2FibGVkIGlmIHdlJ3JlIG5vdCBpbiBwcm9kdWN0aW9uLlxuLy8gaWYgKEVOViAhPT0gJ3Byb2R1Y3Rpb24nKSB7XG4vLyAgIC8vIEVuYWJsZSB0aGUgbG9nZ2VyLlxuLy8gICBkZWJ1Zy5lbmFibGUoJyonKTtcbi8vICAgbG9nKCdMb2dnaW5nIGlzIGVuYWJsZWQhJyk7XG4vL1xuLy8gICAvLyBFbmFibGUgTGl2ZVJlbG9hZFxuLy8gICBkb2N1bWVudC53cml0ZShcbi8vICAgICAnPHNjcmlwdCBzcmM9XCJodHRwOi8vJ1xuLy8gICAgICsgKGxvY2F0aW9uLmhvc3QgfHwgJ2xvY2FsaG9zdCcpLnNwbGl0KCc6JylbMF1cbi8vICAgICArICc6MzU3MjkvbGl2ZXJlbG9hZC5qcz9zbmlwdmVyPTFcIj48Lydcbi8vICAgICArICdzY3JpcHQ+J1xuLy8gICApO1xuLy8gfSBlbHNlIHtcbi8vICAgZGVidWcuZGlzYWJsZSgpO1xuLy8gfVxuXG53aW5kb3cuZDNzbSA9IGQzc207XG5cbmV4cG9ydCBkZWZhdWx0IGQzc21cbiIsImltcG9ydCB7XG4gIGh5cGVuYXRlLCBzYWZlU2VsZWN0LCBleHRyYWN0VmlvbGluVmFsdWVzLFxuICB0aWNrUmFuZ2UsIG1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2UsIHRydW5jYXRlVGV4dCxcbiAgdHJ1bmNhdGVTdHJpbmcsXG4gIHJvdW5kXG59IGZyb20gJy4vaGVscGVycyc7XG5pbXBvcnQge3NldHVwQ29udGFpbmVyLCBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0LCBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7dW5pcXVlLCBoYXNRLCBmbGF0dGVufSBmcm9tICcuL2FycmF5LWZ1bmN0aW9ucyc7XG5pbXBvcnQge2dyb3VwaW5nU3BhY2VyfSBmcm9tICcuL2dyb3VwaW5nLXNwYWNlcic7XG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBWElTICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuXG4vKipcbiAqIENyZWF0ZXMgYW4gYXhpc1xuICpcbiAqIHtAbGluayBodHRwczovL3N1bW5ldXJvbi5naXRsYWIuaW8vZDNzbS9kZW1vcy9heGVzL2luZGV4Lmh0bWwgRGVtb31cbiAqIEBjb25zdHJ1Y3RvciBheGlzXG4gKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gc2VsZWN0aW9uXG4gKiBAbmFtZXNwYWNlIGF4aXNcbiAqIEByZXR1cm5zIHtmdW5jdGlvbn0gYXhpc1xuICovXG5leHBvcnQgZnVuY3Rpb24gYXhpcyAoIHNlbGVjdGlvbiApIHtcbiAgdmFyXG4gIC8qKlxuICAqIFRoZSBvcmllbnRhdGlvbiBvZiB0aGUgYXhpc1xuICAqIChzZWUge0BsaW5rIGF4aXMjb3JpZW50fSlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW29yaWVudD0nYm90dG9tJ11cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb3JpZW50ID0gJ2JvdHRvbScsICAgICAgIC8vIGRpcmVjdGlvbiBvZiB0aGUgYXhpc1xuXG4gIC8qKlxuICAqIEFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBheGlzIGluXG4gICogKHNlZSB7QGxpbmsgYXhpcyNzcGFjZVh9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VYPTBdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlWD0wLFxuICAvKipcbiAgKiBBbW91bnQgb2YgdmVydGljYWwgc3BhY2UgKGluIHBpeGVscykgYXZhaWJsZSB0byByZW5kZXIgdGhlIGF4aXMgaW5cbiAgKiAoc2VlIHtAbGluayBheGlzLnNwYWNlWX0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVk9MF1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VZPTAsXG5cblxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0byBhbGxvdyBheGlzIHRvIHJlbmRlciBlbGVtZW50cyBwYXNzIHRoZSBtYWluIHNwYXRpYWwgZGltZW5zaW9uXG4gICogZ2l2ZW4gdGhlIG9yaWVudGF0aW9uIChzZWUge0BsaW5rIGF4aXMjb3JpZW50fSksIHdoZXJlIHtAbGluayBheGlzI29yaWVudH09XCJib3R0b21cIiBvciB7QGxpbmsgYXhpcyNvcmllbnR9PVwidG9wXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGF4aXMjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGF4aXMjb3JpZW50fT1cImxlZnRcIiBvciB7QGxpbmsgYXhpcyNvcmllbnR9PVwicmlnaHRcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgYXhpcyNzcGFjZVl9XG4gICogQHBhcmFtIHtib29sZWFufSBbb3ZlcmZsb3dRPWZhbHNlXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvdmVyZmxvd1EgPSBmYWxzZSwgICAgLy8gd2hldGhlciBvciBub3QgdG8gYWxsb3cgb3ZlcmZsb3dcbiAgLyoqXG4gICogV2hldGhlciBvciBub3QgdGhlIGF4aXMgbGFiZWxzIGFyZSBmb3IgY2F0ZWdvcmljYWwgZGF0YS4gSWYgZmFsc2UsXG4gICogd2lsbCB1c2Uge0BsaW5rIGF4aXMjc2NhbGV9IHRvIHBvc2l0aW9uIHRpY2tzLlxuICAqIEBwYXJhbSB7Ym9vbGVhbn0gW2NhdGVnb3JpY2FsUT1mYWxzZV1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgY2F0ZWdvcmljYWxRID0gZmFsc2UsIC8vIHdoZXRoZXIgb3Igbm90IHRoZSBheGlzIGlzIHNob3dpbmcgdmFsdWVzIG9yIGdyb3Vwc1xuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0aGUgYXhpcyB0aWNrcyBzaG91bGQgaGF2ZSBndWlkZWxpbmVzXG4gICogQHBhcmFtIHtib29sZWFufSBbY2F0ZWdvcmljYWxRPWZhbHNlXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBndWlkZUxpbmVzUSA9IGZhbHNlLCAgICAvLyB3aGV0aGVyIG9yIG5vdCB0byBhbGxvdyBvdmVyZmxvd1xuXG5cbiAgLyoqXG4gICogSG93IHRvIGdyb3VwIHRoZSB0aWNrIGxhYmVsc1xuICAqIEBwYXJhbSB7QXJyYXlbXX0gW2dyb3VwaW5nPXVuZGVmaW5lZF0gbGlzdCBvZiBwdXRhdGl2ZWx5IG90aGVyIGxpc3RzLCB3aGljaCBzaG91bGQgY29ycmVzcG9uZCB0byB0aWNrTGFiZWxzXG4gICogd2lsbCBzcGFjZSB0aWNrIGxhYmVscyBpbiBuZXN0ZWQgbGlzdHMgY2xvc2VyIHRvZ2V0aGVyIHRoYW4gb3V0ZXIgbGlzdHNcbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZ3JvdXBpbmcsXG5cbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCBub24tY2F0ZWdvcmlhbCAoc2VlIHtAbGluayBheGlzI2NhdGVnb3JpY2FsUX0pIHRpY2tzIHNob3VsZCBiZSBzcGFjZWRcbiAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbc2NhbGU9ZDMuc2NhbGVMaW5lYXJdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG5cbiAgc2NhbGUgPSBkMy5zY2FsZUxpbmVhcigpLFxuICAvKipcbiAgKiBUaGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgc2NhbGUgKHNlZSB7QGxpbmsgYXhpcyNzY2FsZX0pXG4gICogQHBhcmFtIHtkMy5zY2FsZX0gW3NjYWxlPWQzLnNjYWxlTGluZWFyXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkb21haW5QYWRkaW5nID0gMC41LFxuXG5cbiAgLyoqXG4gICogRGVmYXVsdCBzcGFjZSBmb3IgdGhlIHNwYWNlciAocGVyY2VudGFnZSkgb2YgbWFpbiBkaW1lbnNpb24gZ2l2ZW4gdGhlIG9yaWVudGF0aW9uXG4gICogKHNlZSB7QGxpbmsgYXhpcyNvcmllbnR9KSwgd2hlcmUge0BsaW5rIGF4aXMjb3JpZW50fT1cImJvdHRvbVwiIG9yIHtAbGluayBheGlzI29yaWVudH09XCJ0b3BcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgYXhpcyNzcGFjZVh9IGFuZCB3aGVyZSB7QGxpbmsgYXhpcyNvcmllbnR9PVwibGVmdFwiIG9yIHtAbGluayBheGlzI29yaWVudH09XCJyaWdodFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayBheGlzI3NwYWNlWX1iZXR3ZWVuIHRpY2tzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvYmplY3RTcGFjZXI9MC4wNV1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0U3BhY2VyID0gMC4wNSxcbiAgLyoqXG4gICogVGhlIG1pbmltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmUgaWYge0BsaW5rIGF4aXMjY2F0ZWdvcmljYWxRfSBpcyBzZXQgdG8gdHJ1ZVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbWluT2JqZWN0U2l6ZT0xNV1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWluT2JqZWN0U2l6ZSA9IDE1LFxuICAvKipcbiAgKiBUaGUgbWF4aW11bSBzaXplIHRoYXQgYW4gb2JqZWN0IGNhbiBiZSBpZiB7QGxpbmsgYXhpcyNjYXRlZ29yaWNhbFF9IGlzIHNldCB0byB0cnVlXG4gICogQHBhcmFtIHtudW1iZXJ9IFttYXhPYmplY3RTaXplPTE1XVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBtYXhPYmplY3RTaXplID0gNTAsXG5cbiAgLyoqXG4gICogQ29sb3Igb2YgdGhlIGJhY2tncm91bmRcbiAgKiBAcGFyYW0ge3N0cmluZ30gW2JhY2tncm91bmRGaWxsPVwidHJhbnNwYXJlbnRcIl1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnLFxuICAvKipcbiAgKiBOYW1lc3BhY2UgZm9yIGFsbCBpdGVtcyBtYWRlIGJ5IHRoaXMgaW5zdGFuY2Ugb2YgYXhpc1xuICAqIEBwYXJhbSB7c3RyaW5nfSBbbmFtZXNwYWNlPVwiZDNzbS1heGlzXCJdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG5hbWVzcGFjZSA9ICdkM3NtLWF4aXMnLFxuICAvKipcbiAgKiBDbGFzcyBuYW1lIGZvciB0aWNrIGNvbnRhaW5lciAoPGc+IGVsZW1lbnQpXG4gICogQHBhcmFtIHtzdHJpbmd9IFtvYmplY3RDbGFzcz1cInRpY2stZ3JvdXBcIl1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0Q2xhc3MgPSAndGljay1ncm91cCcsXG5cbiAgLyoqXG4gICogVmFsdWVzIHRvIHNob3cgYXQgZWFjaCB0aWNrLiBPbmx5IHVzZWQgaWYgY2F0ZWdvcmljYWxRIGlzIHNldCB0cnVlLiBTZWUge0BsaW5rIGF4aXMjY2F0ZWdvcmljYWxRfVxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt0aWNrTGFiZWxzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdGlja0xhYmVscywgICAvLyB3aGF0IHRvIHBsYWNlIGF0IHRpY2tzXG4gIC8qKlxuICAqIFZhbHVlcyB0byBzaG93IGF0IGVhY2ggdGljay4gT25seSB1c2VkIGlmIGNhdGVnb3JpY2FsUSBpcyBzZXQgZmFsc2UuIFNlZSB7QGxpbmsgYXhpcyNjYXRlZ29yaWNhbFF9XG4gICogQHBhcmFtIHtzdHJpbmdbXSB8IG51bWJlcltdfSBbb2JqZWN0Q2xhc3M9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0aWNrVmFsdWVzLCAgIC8vIHdoZXJlIHRvIHBsYWNlIHRpY2tzIGlmIG5vdFxuICAvKipcbiAgKiBOdW1iZXIgb2YgdGlja3MgdG8gZGlzcGxheSBpZiBjYXRlZ29yaWNhbFEgaXMgZmFsc2UuIFNlZSB7QGxpbmsgYXhpcyNjYXRlZ29yaWNhbFF9XG4gICogQHBhcmFtIHtudW1iZXJ9IFtudW1iZXJPZlRpY2tzPTVdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG51bWJlck9mVGlja3MgPSA1LFxuXG5cbiAgLyoqXG4gICogU3Ryb2tlIGNvbG9yIG9mIHRoZSBtYWluIGF4aXMgbGluZVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbbGluZVN0cm9rZT0nYmxhY2snXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBsaW5lU3Ryb2tlID0gJ2JsYWNrJyxcbiAgLyoqXG4gICogU3Ryb2tlIHdpZHRoIG9mIHRoZSBtYWluIGF4aXMgbGluZVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbGluZVN0cm9rZVdpZHRoPTNdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGxpbmVTdHJva2VXaWR0aCA9IDMsXG5cblxuICAvKipcbiAgKiBTdHJva2UgY29sb3Igb2YgdGlja3NcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3RpY2tTdHJva2U9J2JsYWNrJ11cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdGlja1N0cm9rZSA9ICdibGFjaycsXG4gIC8qKlxuICAqIFN0cm9rZSBudW1iZXIgb2YgdGlja3NcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3RpY2tTdHJva2VXaWR0aD0yXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0aWNrU3Ryb2tlV2lkdGggPSAyLFxuICAvKipcbiAgKiBMZW5ndGggLSBpbiBwaXhlbHMgLSBvZiB0aWNrc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbdGlja0xlbmd0aD0xMF1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdGlja0xlbmd0aCA9IDEwLFxuXG4gIHRpY2tUaWNrTGFiZWxTcGFjZXIgPSAxMCxcbiAgdGlja0xhYmVsTWFyZ2luID0gMTAsXG5cblxuICAvKipcbiAgKiBGb250IHNpemUgb2YgdGljayBsYWJlbHNcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RpY2tMYWJlbEZvbnRTaXplPTE0XVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0aWNrTGFiZWxGb250U2l6ZSA9IDE0LFxuICAvKipcbiAgKiBNaW4gZm9udCBzaXplIG9mIHRpY2sgbGFiZWxzXG4gICogQHBhcmFtIHtudW1iZXJ9IFt0aWNrTGFiZWxNaW5Gb250U2l6ZT04XVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0aWNrTGFiZWxNaW5Gb250U2l6ZSA9IDgsXG4gIC8qKlxuICAqIE1heCBmb250IHNpemUgb2YgdGljayBsYWJlbHNcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RpY2tMYWJlbE1heEZvbnRTaXplPTIwXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0aWNrTGFiZWxNYXhGb250U2l6ZSA9IDIwLFxuXG5cbiAgLyoqXG4gICogVGV4dCBhbmNob3Igb2YgdGljayBsYWJlbHNcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3RpY2tMYWJlbFRleHRBbmNob3I9XCJtaWRkbGVcIl1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdGlja0xhYmVsVGV4dEFuY2hvcixcbiAgLyoqXG4gICogUm90YXRpb24gb2YgdGljayBsYWJlbHNcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RpY2tMYWJlbFJvdGF0aW9uPTBdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRpY2tMYWJlbFJvdGF0aW9uLFxuICAvKipcbiAgKiBPcHRpb25hbCBmdW5jdGlvbiBmb3IgZXh0cmFjdGluZyB0aGUgdGljayBsYWJlbCBmcm9tIGRhdGFcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdGlja0xhYmVsRnVuYz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRpY2tMYWJlbEZ1bmMgPSB1bmRlZmluZWQsXG5cbiAgLyoqXG4gICogT3B0aW9uYWwgZnVuY3Rpb24gZm9yIHdoYXQgdG8gZG8gd2hlbiBsYWJlbCBpcyBjbGlja2VkXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3RpY2tMYWJlbE9uQ2xpY2s9ZnVuY3Rpb24oZCwgaSl7fV1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdGlja0xhYmVsT25DbGljayA9IGZ1bmN0aW9uKGQsIGkpe30sXG5cbiAgLyoqXG4gICogT3B0aW9uYWwgZnVuY3Rpb24gZm9yIHdoYXQgdG8gZG8gd2hlbiBsYWJlbCBpcyBob3ZlcmVkXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3RpY2tMYWJlbE9uSG92ZXJGdW5jPWZ1bmN0aW9uKGQsIGkpe31dXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRpY2tMYWJlbE9uSG92ZXJGdW5jID0gZnVuY3Rpb24oZCwgaSl7XG4gICAgcmV0dXJuIFN0cmluZyhkKS5yZXBsYWNlKCctJywgJyAnKS5yZXBsYWNlKCdfJywgJyAnKVxuICB9LFxuXG5cbiAgLyoqXG4gICogTGVuZ3RoIG9mIGd1aWRlbGluZXNcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbZ3VpZGVsaW5lU3BhY2U9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBndWlkZWxpbmVTcGFjZSxcbiAgLyoqXG4gICogU3Ryb2tlIGNvbG9yIG9mIGd1aWRsaW5lc1xuICAqIEBwYXJhbSB7c3RyaW5nfSBbZ3VpZGVsaW5lU3BhY2U9XCIjMzMzMzMzXCJdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGd1aWRlTGluZVN0cm9rZSA9ICcjMzMzMzMzJyxcbiAgLyoqXG4gICogU3Ryb2tlIHdpZHRoIG9mIGd1aWRsaW5lc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbZ3VpZGVsaW5lU3BhY2U9Ml1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZ3VpZGVMaW5lU3Ryb2tlV2lkdGggPSAyLFxuXG4gIC8qKlxuICAqIER1cmF0aW9uIG9mIGFsbCB0cmFuc2l0aW9ucyBvZiB0aGlzIGVsZW1lbnRcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RyYW5zaXRpb25EdXJhdGlvbj0xMDAwXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwLFxuICAvKipcbiAgKiBFYXNpbmcgZnVuY3Rpb24gZm9yIHRyYW5zaXRpb25zXG4gICogQHBhcmFtIHtkMy5lYXNlfSBbZWFzZUZ1bmM9ZDMuZWFzZUV4cF1cbiAgKiBAbWVtYmVyb2YgYXhpcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwLFxuXG5cbiAgLyoqXG4gICogQ2xvc3VyZSB2YXJpYWJsZSBmb3IgZ2V0dGluZyBvYmplY3Qgc2l6ZSBhZnRlciBjYWxjdWxhdGlvblxuICAqIEBwYXJhbSB7bnVtYmVyfSBbb2JqZWN0U2l6ZT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGF4aXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdFNpemUsXG4gIC8qKlxuICAqIENsb3N1cmUgdmFyaWFibGUgZm9yIGdldHRpbmcgc3BhY2VyIHNpemUgYWZ0ZXIgY2FsY3VsYXRpb25cbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlclNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzcGFjZXJTaXplLFxuXG4gIC8qKlxuICAqIERlY2ltYWwgcGVyY2lzaW9uIHRvIHJvdW5kIG51bWVyaWNhbCB0aWNrIGxhYmVscyB0b1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbcm91bmRUbz0yXVxuICAqIEBtZW1iZXJvZiBheGlzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICByb3VuZFRvID0gMixcblxuICBsYWJlbCxcblxuXG4gIHJldmVyc2VTY2FsZVEgPSBmYWxzZVxuXG4gIGF4aXMubGFiZWwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGxhYmVsID0gXywgYXhpcykgOiBsYWJlbDsgfTtcbiAgYXhpcy50aWNrVGlja0xhYmVsU3BhY2VyID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0aWNrVGlja0xhYmVsU3BhY2VyID0gXywgYXhpcykgOiB0aWNrVGlja0xhYmVsU3BhY2VyOyB9O1xuICBheGlzLnRpY2tMYWJlbE1hcmdpbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xhYmVsTWFyZ2luID0gXywgYXhpcykgOiB0aWNrTGFiZWxNYXJnaW47IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHNlbGVjdGlvbiBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgZDMuc2VsZWN0aW9ufVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzZWxlY3Rpb24gPSBzZWxlY3Rpb25cbiAgICovXG5cbiAgYXhpcy5zZWxlY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNlbGVjdGlvbiA9IF8sIGF4aXMpIDogc2VsZWN0aW9uOyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIG9yaWVudGF0aW9uIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBheGlzI29yaWVudH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXSBzaG91bGQgYmUgaG9yaXpvbnRhbCBvciB2ZXJ0aWNhbFxuICAgKiBAcmV0dXJucyB7YXhpcyB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3JpZW50PVwiYm90dG9tXCJcbiAgICovXG4gIGF4aXMub3JpZW50ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvcmllbnQgPSBfLCBheGlzKSA6IG9yaWVudDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIGhvcml6b250YWwgc3BhY2UgaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIGF4aXMjc3BhY2VYfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge2F4aXMgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlWCA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYXhpcy5zcGFjZVggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWCA9IF8sIGF4aXMpIDogc3BhY2VYOyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBhbW91bnQgb2YgdmVydGljYWwgc3BhY2UgaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIGF4aXMjc3BhY2VZfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge2F4aXMgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlWSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYXhpcy5zcGFjZVkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWSA9IF8sIGF4aXMpIDogc3BhY2VZOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IGF4aXMgaXMgYWxsb3dlZCB0byBnbyBiZXlvbmQgc3BlY2lmaWVkIGRpbWVuc2lvbnNcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNzcGFjZVh9KVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgYm9vbGVhbn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3ZlcmZsb3dRID0gZmFsc2VcbiAgICovXG4gIGF4aXMub3ZlcmZsb3dRID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvdmVyZmxvd1EgPSBfLCBheGlzKSA6IG92ZXJmbG93UTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IGF4aXMgd2lsbCBkaXNwbGF5IGNhdGVnb3JpYWwgdGlja3Mgb3IgYnkgbnVtZXJpY2FsIHZhbHVlXG4gICAqIChzZWUge0BsaW5rIGF4aXMjY2F0ZWdvcmljYWxRfSlcbiAgICogQHBhcmFtIHtib29sZWFufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IGJvb2xlYW59XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGNhdGVnb3JpY2FsUSA9IGZhbHNlXG4gICAqL1xuICBheGlzLmNhdGVnb3JpY2FsUSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY2F0ZWdvcmljYWxRID0gXywgYXhpcykgOiBjYXRlZ29yaWNhbFE7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB3aGV0aGVyIG9yIG5vdCBheGlzIHRpY2tzIHNob3VsZCBoYXZlIGd1aWRlbGluZXNcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNndWlkZUxpbmVzUX0pXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBib29sZWFufVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBndWlkZUxpbmVzUSA9IGZhbHNlXG4gICAqL1xuICBheGlzLmd1aWRlTGluZXNRID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChndWlkZUxpbmVzUSA9IF8sIGF4aXMpIDogZ3VpZGVMaW5lc1E7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgaG93IHRpY2tzIHNob3VsZCBiZSBncm91cHBlZFxuICAgKiAoc2VlIHtAbGluayBheGlzI2dyb3VwaW5nfSlcbiAgICogQHBhcmFtIHtBcnJheVtdfSBbXz1ub25lXSBsaXN0IG9mIHB1dGF0aXZlbHkgb3RoZXIgbGlzdHMsIHdoaWNoIHNob3VsZCBjb3JyZXNwb25kIHRvIHRpY2tMYWJlbHNcbiAgICogd2lsbCBzcGFjZSB0aWNrIGxhYmVscyBpbiBuZXN0ZWQgbGlzdHMgY2xvc2VyIHRvZ2V0aGVyIHRoYW4gb3V0ZXIgbGlzdHNcbiAgICogQHJldHVybnMge2F4aXMgfCBBcnJheVtdfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBncm91cGluZyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYXhpcy5ncm91cGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZ3JvdXBpbmcgPSBfLCBheGlzKSA6IGdyb3VwaW5nOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzY2FsZSBmb3Igd2hpY2ggbm9uLWNhdGVnb3JpYWwgIHRpY2tzIHNob3VsZFxuICAgKiBiZSBzcGFjZWRcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNzY2FsZX0pXG4gICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgZDMuc2NhbGV9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKVxuICAgKi9cbiAgYXhpcy5zY2FsZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGUgPSBfLCBheGlzKSA6IHNjYWxlOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHBhZGRpbmcgZm9yIHRoZSBkb21haW4gb2YgdGhlIHNjYWxlXG4gICAqIChzZWUge0BsaW5rIGF4aXMjZG9tYWluUGFkZGluZ30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZG9tYWluUGFkZGluZyA9IDAuNVxuICAgKi9cbiAgYXhpcy5kb21haW5QYWRkaW5nID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkb21haW5QYWRkaW5nID0gXywgYXhpcykgOiBkb21haW5QYWRkaW5nOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIG9iamVjdFNwYWNlclxuICAgKiAoc2VlIHtAbGluayBheGlzI29iamVjdFNwYWNlcn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0U3BhY2VyID0gMC4wNVxuICAgKi9cbiAgYXhpcy5vYmplY3RTcGFjZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNwYWNlciA9IF8sIGF4aXMpIDogb2JqZWN0U3BhY2VyOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1pbk9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNtaW5PYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtaW5PYmplY3RTaXplID0gMTVcbiAgICovXG4gIGF4aXMubWluT2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWluT2JqZWN0U2l6ZSA9IF8sIGF4aXMpIDogbWluT2JqZWN0U2l6ZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtYXhPYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIGF4aXMjbWF4T2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWF4T2JqZWN0U2l6ZSA9IDUwXG4gICAqL1xuICBheGlzLm1heE9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1heE9iamVjdFNpemUgPSBfLCBheGlzKSA6IG1heE9iamVjdFNpemU7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG5hbWVzcGFjZVxuICAgKiAoc2VlIHtAbGluayBheGlzI25hbWVzcGFjZX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbmFtZXNwYWNlID0gJ2Qzc20tYXhpcydcbiAgICovXG4gIGF4aXMubmFtZXNwYWNlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChuYW1lc3BhY2UgPSBfLCBheGlzKSA6IG5hbWVzcGFjZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBiYWNrZ3JvdW5kRmlsbFxuICAgKiAoc2VlIHtAbGluayBheGlzI2JhY2tncm91bmRGaWxsfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCdcbiAgICovXG4gIGF4aXMuYmFja2dyb3VuZEZpbGwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhY2tncm91bmRGaWxsID0gXywgYXhpcykgOiBiYWNrZ3JvdW5kRmlsbDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RDbGFzc1xuICAgKiAoc2VlIHtAbGluayBheGlzI29iamVjdENsYXNzfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RDbGFzcyA9ICd0aWNrLWdyb3VwJ1xuICAgKi9cbiAgYXhpcy5vYmplY3RDbGFzcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3MgPSBfLCBheGlzKSA6IG9iamVjdENsYXNzOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0aWNrTGFiZWxzXG4gICAqIChzZWUge0BsaW5rIGF4aXMjdGlja0xhYmVsc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgc3RyaW5nW119XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRpY2tMYWJlbHMgPSB1bmRlZmluZWRcbiAgICovXG4gIGF4aXMudGlja0xhYmVscyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xhYmVscyA9IF8sIGF4aXMpIDogdGlja0xhYmVsczsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0aWNrVmFsdWVzXG4gICAqIChzZWUge0BsaW5rIGF4aXMjdGlja1ZhbHVlc30pXG4gICAqIEBwYXJhbSB7bnVtYmVyW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyW119XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRpY2tWYWx1ZXMgPSB1bmRlZmluZWRcbiAgICovXG4gIGF4aXMudGlja1ZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja1ZhbHVlcyA9IF8sIGF4aXMpIDogdGlja1ZhbHVlczsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0aWNrVmFsdWVzXG4gICAqIChzZWUge0BsaW5rIGF4aXMjbnVtYmVyT2ZUaWNrc30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbnVtYmVyT2ZUaWNrcyA9IDVcbiAgICovXG4gIGF4aXMubnVtYmVyT2ZUaWNrcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobnVtYmVyT2ZUaWNrcyA9IF8sIGF4aXMpIDogbnVtYmVyT2ZUaWNrczsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbGluZVN0cm9rZVxuICAgKiAoc2VlIHtAbGluayBheGlzI2xpbmVTdHJva2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGxpbmVTdHJva2UgPSAnYmxhY2snXG4gICAqL1xuICBheGlzLmxpbmVTdHJva2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGxpbmVTdHJva2UgPSBfLCBheGlzKSA6IGxpbmVTdHJva2U7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbGluZVN0cm9rZVdpZHRoXG4gICAqIChzZWUge0BsaW5rIGF4aXMjbGluZVN0cm9rZVdpZHRofSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBsaW5lU3Ryb2tlV2lkdGggPSAzXG4gICAqL1xuICBheGlzLmxpbmVTdHJva2VXaWR0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobGluZVN0cm9rZVdpZHRoID0gXywgYXhpcykgOiBsaW5lU3Ryb2tlV2lkdGg7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRpY2tTdHJva2VcbiAgICogKHNlZSB7QGxpbmsgYXhpcyN0aWNrU3Ryb2tlfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0aWNrU3Ryb2tlID0gJ2JsYWNrJ1xuICAgKi9cbiAgYXhpcy50aWNrU3Ryb2tlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0aWNrU3Ryb2tlID0gXywgYXhpcykgOiB0aWNrU3Ryb2tlOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRpY2tTdHJva2VXaWR0aFxuICAgKiAoc2VlIHtAbGluayBheGlzI3RpY2tTdHJva2VXaWR0aH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdGlja1N0cm9rZVdpZHRoID0gMlxuICAgKi9cbiAgYXhpcy50aWNrU3Ryb2tlV2lkdGggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tTdHJva2VXaWR0aCA9IF8sIGF4aXMpIDogdGlja1N0cm9rZVdpZHRoOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRpY2tMZW5ndGhcbiAgICogKHNlZSB7QGxpbmsgYXhpcyN0aWNrTGVuZ3RofSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0aWNrTGVuZ3RoID0gMTBcbiAgICovXG4gIGF4aXMudGlja0xlbmd0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xlbmd0aCA9IF8sIGF4aXMpIDogdGlja0xlbmd0aDsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdGlja0xhYmVsRm9udFNpemVcbiAgICogKHNlZSB7QGxpbmsgYXhpcyN0aWNrTGFiZWxGb250U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdGlja0xhYmVsRm9udFNpemUgPSAxNFxuICAgKi9cbiAgYXhpcy50aWNrTGFiZWxGb250U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xhYmVsRm9udFNpemUgPSBfLCBheGlzKSA6IHRpY2tMYWJlbEZvbnRTaXplOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRpY2tMYWJlbE1pbkZvbnRTaXplXG4gICAqIChzZWUge0BsaW5rIGF4aXMjdGlja0xhYmVsTWluRm9udFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRpY2tMYWJlbE1pbkZvbnRTaXplID0gOFxuICAgKi9cbiAgYXhpcy50aWNrTGFiZWxNaW5Gb250U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xhYmVsTWluRm9udFNpemUgPSBfLCBheGlzKSA6IHRpY2tMYWJlbE1pbkZvbnRTaXplOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRpY2tMYWJlbE1heEZvbnRTaXplXG4gICAqIChzZWUge0BsaW5rIGF4aXMjdGlja0xhYmVsTWF4Rm9udFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRpY2tMYWJlbE1heEZvbnRTaXplID0gMjBcbiAgICovXG4gIGF4aXMudGlja0xhYmVsTWF4Rm9udFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tMYWJlbE1heEZvbnRTaXplID0gXywgYXhpcykgOiB0aWNrTGFiZWxNYXhGb250U2l6ZTt9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0aWNrTGFiZWxUZXh0QW5jaG9yXG4gICAqIChzZWUge0BsaW5rIGF4aXMjdGlja0xhYmVsVGV4dEFuY2hvcn0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdGlja0xhYmVsVGV4dEFuY2hvciA9ICdjZW50ZXInXG4gICAqL1xuICBheGlzLnRpY2tMYWJlbFRleHRBbmNob3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tMYWJlbFRleHRBbmNob3IgPSBfLCBheGlzKSA6IHRpY2tMYWJlbFRleHRBbmNob3I7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdGlja0xhYmVsUm90YXRpb25cbiAgICogKHNlZSB7QGxpbmsgYXhpcyN0aWNrTGFiZWxSb3RhdGlvbn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdGlja0xhYmVsUm90YXRpb24gPSAwXG4gICAqL1xuICBheGlzLnRpY2tMYWJlbFJvdGF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0aWNrTGFiZWxSb3RhdGlvbiA9IF8sIGF4aXMpIDogdGlja0xhYmVsUm90YXRpb247IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdGlja0xhYmVsRnVuY1xuICAgKiAoc2VlIHtAbGluayBheGlzI3RpY2tMYWJlbEZ1bmN9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0aWNrTGFiZWxGdW5jID0gdW5kZWZpbmVkXG4gICAqL1xuICBheGlzLnRpY2tMYWJlbEZ1bmMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tMYWJlbEZ1bmMgPSBfLCBheGlzKSA6IHRpY2tMYWJlbEZ1bmM7IH07XG5cblxuICAvKipcbiAgKiBHZXRzIC8gc2V0cyB0aGUgdGlja0xhYmVsT25DbGlja1xuICAqIChzZWUge0BsaW5rIGF4aXMjdGlja0xhYmVsT25DbGlja30pXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgKiBAcmV0dXJucyB7YXhpcyB8IGZ1bmN0aW9ufVxuICAqIEBtZW1iZXJvZiBheGlzXG4gICogQHByb3BlcnR5XG4gICovXG4gIGF4aXMudGlja0xhYmVsT25DbGljayA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodGlja0xhYmVsT25DbGljayA9IF8sIGF4aXMpIDogdGlja0xhYmVsT25DbGljazsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZ3VpZGVsaW5lU3BhY2VcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNndWlkZWxpbmVTcGFjZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZ3VpZGVsaW5lU3BhY2UgPSB1bmRlZmluZWRcbiAgICovXG4gIGF4aXMuZ3VpZGVsaW5lU3BhY2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGd1aWRlbGluZVNwYWNlID0gXywgYXhpcykgOiBndWlkZWxpbmVTcGFjZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBndWlkZUxpbmVTdHJva2VcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNndWlkZUxpbmVTdHJva2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGd1aWRlTGluZVN0cm9rZSA9IFwiIzMzMzMzM1wiXG4gICAqL1xuICBheGlzLmd1aWRlTGluZVN0cm9rZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZ3VpZGVMaW5lU3Ryb2tlID0gXywgYXhpcykgOiBndWlkZUxpbmVTdHJva2U7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZ3VpZGVMaW5lU3Ryb2tlV2lkdGhcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNndWlkZUxpbmVTdHJva2VXaWR0aH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZ3VpZGVMaW5lU3Ryb2tlV2lkdGggPSAyXG4gICAqL1xuICBheGlzLmd1aWRlTGluZVN0cm9rZVdpZHRoID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChndWlkZUxpbmVTdHJva2VXaWR0aCA9IF8sIGF4aXMpIDogZ3VpZGVMaW5lU3Ryb2tlV2lkdGg7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRyYW5zaXRpb25EdXJhdGlvblxuICAgKiAoc2VlIHtAbGluayBheGlzI3RyYW5zaXRpb25EdXJhdGlvbn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YXhpcyB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMFxuICAgKi9cbiAgYXhpcy50cmFuc2l0aW9uRHVyYXRpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRyYW5zaXRpb25EdXJhdGlvbiA9IF8sIGF4aXMpIDogdHJhbnNpdGlvbkR1cmF0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGVhc2VGdW5jXG4gICAqIChzZWUge0BsaW5rIGF4aXMjZWFzZUZ1bmN9KVxuICAgKiBAcGFyYW0ge2QzLmVhc2V9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgZDMuZWFzZX1cbiAgICogQG1lbWJlcm9mIGF4aXNcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwXG4gICAqL1xuICBheGlzLmVhc2VGdW5jID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChlYXNlRnVuYyA9IF8sIGF4aXMpIDogZWFzZUZ1bmM7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgYXhpcyNvYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RTaXplID0gdW5kZWZpbmVkXG4gICAqL1xuICBheGlzLm9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNpemUgPSBfLCBheGlzKSA6IG9iamVjdFNpemU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgc3BhY2VyU2l6ZVxuICAgKiAoc2VlIHtAbGluayBheGlzI3NwYWNlclNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2F4aXMgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBheGlzXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlclNpemUgPSB1bmRlZmluZWRcbiAgICovXG4gIGF4aXMuc3BhY2VyU2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3BhY2VyU2l6ZSA9IF8sIGF4aXMpIDogc3BhY2VyU2l6ZTsgfTtcblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHJvdW5kVG9cbiAgICogKHNlZSB7QGxpbmsgYXhpcyNyb3VuZFRvfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtheGlzIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYXhpc1xuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCByb3VuZFRvID0gMlxuICAgKi9cbiAgIGF4aXMucm91bmRUbyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocm91bmRUbyA9IF8sIGF4aXMpIDogcm91bmRUbzsgfTtcbiAgIGF4aXMucmV2ZXJzZVNjYWxlUSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocmV2ZXJzZVNjYWxlUSA9IF8sIGF4aXMpIDogcmV2ZXJzZVNjYWxlUTsgfTtcblxuXG4gICBheGlzLnRpY2tMYWJlbE9uSG92ZXJGdW5jID0gZnVuY3Rpb24oXykge3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRpY2tMYWJlbE9uSG92ZXJGdW5jID0gXywgYXhpcykgOiB0aWNrTGFiZWxPbkhvdmVyRnVuYzsgfTtcblxuXG4gIGZ1bmN0aW9uIGF4aXMgKCkge1xuICAgIC8vIGZvciBjb252ZW5pZW5jZSBpbiBoYW5kbGluZyBvcmllbnRhdGlvbiBzcGVjaWZpYyB2YWx1ZXNcbiAgICB2YXIgaG9yaXpvbnRhbFEgPSBoYXNRKFsndG9wJywgJ2JvdHRvbScsICdob3Jpem9udGFsJ10sIG9yaWVudCkgPyB0cnVlIDogZmFsc2VcbiAgICB2YXIgdmVydGljYWxRID0gIWhvcml6b250YWxRXG5cbiAgICAvLyBiYWNrZ3JvdW5kIGNsaXBpbmcgcmVjdGFuZ2xlXG4gICAgdmFyIGJnY3BSZWN0ID0ge3g6MCwgeTowLCB3aWR0aDogc3BhY2VYLCBoZWlnaHQ6c3BhY2VZfVxuICAgIC8vIG1vZGlmeSB0aGUgcmVjdCBiYXNlZCBvbiBheGlzIG9yaWVudGF0aW9uXG4gICAgaWYgKG9yaWVudCA9PSBcImxlZnRcIikge1xuICAgICAgYmdjcFJlY3QueCAtPSBzcGFjZVg7XG4gICAgICBpZihndWlkZUxpbmVzUSkgeyBiZ2NwUmVjdC53aWR0aCArPSBndWlkZWxpbmVTcGFjZSB9O1xuICAgICAgLyogdGhlc2UgdHdvIGxpbmVzIGluY3JlYXNlIHRoZSBjbGlwcGluZyByZWN0IHRvIGFsbG93IGZvciB0ZXh0IGF0IHRoZSBlZGdlIG9mIHRoZSBheGlzICovXG4gICAgICBiZ2NwUmVjdC55IC09IHRpY2tMYWJlbE1heEZvbnRTaXplO1xuICAgICAgYmdjcFJlY3QuaGVpZ2h0ICs9IDIqdGlja0xhYmVsTWF4Rm9udFNpemVcbiAgICB9XG4gICAgaWYgKG9yaWVudCA9PSBcImJvdHRvbVwiKXtcbiAgICAgIGJnY3BSZWN0LnkgPSBiZ2NwUmVjdC55O1xuICAgICAgaWYoZ3VpZGVMaW5lc1EpIHsgYmdjcFJlY3QueSAtPSBndWlkZWxpbmVTcGFjZTsgYmdjcFJlY3QuaGVpZ2h0ICs9IGd1aWRlbGluZVNwYWNlOyB9O1xuICAgICAgLyogdGhlc2UgdHdvIGxpbmVzIGluY3JlYXNlIHRoZSBjbGlwcGluZyByZWN0IHRvIGFsbG93IGZvciB0ZXh0IGF0IHRoZSBlZGdlIG9mIHRoZSBheGlzICovXG4gICAgICBiZ2NwUmVjdC54IC09IHRpY2tMYWJlbE1heEZvbnRTaXplO1xuICAgICAgYmdjcFJlY3Qud2lkdGggKz0gMip0aWNrTGFiZWxNYXhGb250U2l6ZVxuICAgIH1cbiAgICBpZiAob3JpZW50ID09IFwidG9wXCIpIHtcbiAgICAgIGJnY3BSZWN0LnkgLT0gc3BhY2VZO1xuICAgICAgaWYoZ3VpZGVMaW5lc1EpIHsgYmdjcFJlY3QuaGVpZ2h0ICs9IGd1aWRlbGluZVNwYWNlIH07XG4gICAgICAvKiB0aGVzZSB0d28gbGluZXMgaW5jcmVhc2UgdGhlIGNsaXBwaW5nIHJlY3QgdG8gYWxsb3cgZm9yIHRleHQgYXQgdGhlIGVkZ2Ugb2YgdGhlIGF4aXMgKi9cbiAgICAgIGJnY3BSZWN0LnkgLT0gdGlja0xhYmVsTWF4Rm9udFNpemU7XG4gICAgICBiZ2NwUmVjdC5oZWlnaHQgKz0gMip0aWNrTGFiZWxNYXhGb250U2l6ZVxuICAgIH1cbiAgICBpZiAob3JpZW50ID09IFwicmlnaHRcIikgeyBiZ2NwUmVjdC54ID0gMDtcbiAgICAgIGlmKGd1aWRlTGluZXNRKSB7IGJnY3BSZWN0LndpZHRoICs9IGd1aWRlbGluZVNwYWNlOyBiZ2NwUmVjdC54IC09IGd1aWRlbGluZVNwYWNlIH07XG4gICAgICAvKiB0aGVzZSB0d28gbGluZXMgaW5jcmVhc2UgdGhlIGNsaXBwaW5nIHJlY3QgdG8gYWxsb3cgZm9yIHRleHQgYXQgdGhlIGVkZ2Ugb2YgdGhlIGF4aXMgKi9cbiAgICAgIGJnY3BSZWN0LnkgLT0gdGlja0xhYmVsTWF4Rm9udFNpemU7XG4gICAgICBiZ2NwUmVjdC5oZWlnaHQgKz0gMip0aWNrTGFiZWxNYXhGb250U2l6ZVxuICAgIH1cblxuXG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cbiAgICAvLyBkZWZhdWx0cyBmb3IgdGV4dC1hbmNob3IgYW5kIHRleHQgcm90YXRpb25cbiAgICBpZiAob3JpZW50ID09ICd0b3AnKSB7XG4gICAgICB0aWNrTGFiZWxUZXh0QW5jaG9yID0gdGlja0xhYmVsVGV4dEFuY2hvciA9PSB1bmRlZmluZWQgPyAnc3RhcnQnIDogdGlja0xhYmVsVGV4dEFuY2hvclxuICAgICAgdGlja0xhYmVsUm90YXRpb24gPSB0aWNrTGFiZWxSb3RhdGlvbiA9PSB1bmRlZmluZWQgPyAtOTAgOiB0aWNrTGFiZWxSb3RhdGlvblxuICAgIH1cbiAgICBpZiAob3JpZW50ID09ICdib3R0b20nKSB7XG4gICAgICB0aWNrTGFiZWxUZXh0QW5jaG9yID0gdGlja0xhYmVsVGV4dEFuY2hvciA9PSB1bmRlZmluZWQgPyAnZW5kJyA6IHRpY2tMYWJlbFRleHRBbmNob3JcbiAgICAgIHRpY2tMYWJlbFJvdGF0aW9uID0gdGlja0xhYmVsUm90YXRpb24gPT0gdW5kZWZpbmVkID8gLTkwIDogdGlja0xhYmVsUm90YXRpb25cbiAgICB9XG4gICAgaWYgKG9yaWVudCA9PSAnbGVmdCcpIHtcbiAgICAgIHRpY2tMYWJlbFRleHRBbmNob3IgPSB0aWNrTGFiZWxUZXh0QW5jaG9yID09IHVuZGVmaW5lZCA/ICdlbmQnIDogdGlja0xhYmVsVGV4dEFuY2hvclxuICAgICAgdGlja0xhYmVsUm90YXRpb24gPSB0aWNrTGFiZWxSb3RhdGlvbiA9PSB1bmRlZmluZWQgPyAwIDogdGlja0xhYmVsUm90YXRpb25cbiAgICB9XG4gICAgaWYgKG9yaWVudCA9PSAncmlnaHQnKSB7XG4gICAgICB0aWNrTGFiZWxUZXh0QW5jaG9yID0gdGlja0xhYmVsVGV4dEFuY2hvciA9PSB1bmRlZmluZWQgPyAnc3RhcnQnIDogdGlja0xhYmVsVGV4dEFuY2hvclxuICAgICAgdGlja0xhYmVsUm90YXRpb24gPSB0aWNrTGFiZWxSb3RhdGlvbiA9PSB1bmRlZmluZWQgPyAwIDogdGlja0xhYmVsUm90YXRpb25cbiAgICB9XG5cbiAgICAvKlxuICAgIElmIGNhdGVnb3JpY2FsOlxuICAgICAgLT4gdXNlIGdyb3VwaW5nIGlmIGRlZmluZWQsXG4gICAgICAtPiBlbHNlIHVzZSB0aGUgbGFiZWxzIHByb3ZpZGVkXG4gICAgZWxzZTpcbiAgICAgIGlmIGdyb3VwaW5nIHVuZGVmaW5lZFxuICAgICAgICBhbmQgbm8gc3BlY2lmaWVkIG51bWJlciBvZiB0aWNrZXNcbiAgICAgICAgICAtPiBtYWtlIG51bWJlck9mVGljayB0aWNrc1xuICAgICAgICAgIC0+IGVsc2UgdXNlIHByb3ZpZGVkIHRpY2sgdmFsdWVzXG4gICAgICAgIC0+IHVzZSBncm91cGluZ1xuICAgICovXG4gICAgdmFyIHRpY2tEYXRhID0gY2F0ZWdvcmljYWxRXG4gICAgPyAoZ3JvdXBpbmcgPT0gdW5kZWZpbmVkKVxuICAgICAgPyB0aWNrTGFiZWxzXG4gICAgICA6IGdyb3VwaW5nXG4gICAgOiAoZ3JvdXBpbmcgPT0gdW5kZWZpbmVkKVxuICAgICAgPyAobnVtYmVyT2ZUaWNrcyAhPSB1bmRlZmluZWQpXG4gICAgICAvLyA/ICh0aWNrVmFsdWVzLmxlbmd0aCA8IG51bWJlck9mVGlja3MpXG4gICAgICAgID8gKHRpY2tSYW5nZSguLi5kMy5leHRlbnQodGlja1ZhbHVlcyksIG51bWJlck9mVGlja3MpKVxuICAgICAgICA6IHRpY2tWYWx1ZXNcbiAgICAgIDogZ3JvdXBpbmdcblxuXG4gICAgdmFyIGZsYXRUaWNrRGF0YSA9IGZsYXR0ZW4odGlja0RhdGEpXG4gICAgdmFyIG51bWJlck9mT2JqZWN0cyA9IGZsYXRUaWNrRGF0YS5sZW5ndGhcbiAgICB2YXIgc3BhY2UgPSBob3Jpem9udGFsUSA/IHNwYWNlWCA6IHNwYWNlWVxuICAgIHZhciBleHRlbnQgPSBkMy5leHRlbnQoZmxhdFRpY2tEYXRhKVxuXG5cbiAgICBpZiAocmV2ZXJzZVNjYWxlUSkge2V4dGVudC5yZXZlcnNlKCl9XG4gICAgdmFyIGRvbWFpbiA9IHJldmVyc2VTY2FsZVFcbiAgICA/IFtleHRlbnRbMF0gKyBkb21haW5QYWRkaW5nLCBleHRlbnRbMV0gLSBkb21haW5QYWRkaW5nXVxuICAgIDogW2V4dGVudFswXSAtIGRvbWFpblBhZGRpbmcsIGV4dGVudFsxXSArIGRvbWFpblBhZGRpbmddXG5cbiAgICBzY2FsZVxuICAgIC5kb21haW4oZG9tYWluKVxuICAgIC5yYW5nZShbaG9yaXpvbnRhbFEgPyAwIDogc3BhY2VZLCBob3Jpem9udGFsUSA/IHNwYWNlWCA6IDBdKVxuXG5cbiAgICAvKlxuICAgIFNjYWxlcyBhcmUgYmFzZWQgb24gdGhlIHZhbHVlcyBvZiB0aGUgY2hhcnQgYW5kIGNvcnJlc3BvbmQgdG8gdGhlIHNwYWNpbmdzIG9mIHRoZVxuICAgIGNoYXJ0LiBJZiB0aGUgY2hhcnQgaGFzIGFscmVhZHkgYmVlbiByZW5kZXJlZCwgdGhlc2UgdmFsdWVzIChleHBlbnNpdmUgdG8gY2FsdWNsYXRlKSBjYW5cbiAgICBiZSBwYXNzZWQgdG8gYXhpcyB0byBwcmV2ZW50IHJlY2FsY3VsYXRpb24uXG4gICAgKi9cblxuICAgIC8vIGNhbGN1bGF0ZSBvYmplY3Qgc2l6ZSBpZiBuZWVkZWRcbiAgICBvYmplY3RTaXplID0gKG9iamVjdFNpemUgPT0gdW5kZWZpbmVkKVxuICAgID8gY2FsY3VsYXRlV2lkdGhPZk9iamVjdChzcGFjZSwgbnVtYmVyT2ZPYmplY3RzLCBtaW5PYmplY3RTaXplLCBtYXhPYmplY3RTaXplLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICA6IG9iamVjdFNpemVcblxuICAgIC8vIGNhbGN1bGF0ZSBzcGFjZXIgc2l6ZSBpZiBuZWVkZWRcbiAgICBzcGFjZXJTaXplID0gKHNwYWNlclNpemUgPT0gdW5kZWZpbmVkKVxuICAgID8gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcihmbGF0VGlja0RhdGEsIHNwYWNlLCBvYmplY3RTaXplLCBudW1iZXJPZk9iamVjdHMsIG9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIDogc3BhY2VyU2l6ZVxuXG5cbiAgICB2YXIgb2JqQ2xhc3MgPSBoeXBlbmF0ZShuYW1lc3BhY2UsIGNhdGVnb3JpY2FsUSA/IG9iamVjdENsYXNzKyctY2F0ZWdvcmljYWwnIDogb2JqZWN0Q2xhc3MpXG5cbiAgICB2YXIgc3BhY2VyRnVuY3Rpb24gPSBncm91cGluZ1NwYWNlcigpXG4gICAgLmhvcml6b250YWxRKGhvcml6b250YWxRKS5zY2FsZShzY2FsZSkubW92ZWJ5KChjYXRlZ29yaWNhbFE/J2NhdGVnb3J5Jzonc2NhbGUnKSkubnVtYmVyT2ZPYmplY3RzKG51bWJlck9mT2JqZWN0cylcbiAgICAub2JqZWN0Q2xhc3Mob2JqQ2xhc3MpLm9iamVjdFNpemUob2JqZWN0U2l6ZSkuc3BhY2VyU2l6ZShzcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcbiAgICAubmFtZXNwYWNlKG5hbWVzcGFjZSlcblxuICAgIHZhciB0aWNrRW50ZXJBbmltYXRpb24gPSBmdW5jdGlvbihzZWwpe1xuICAgICAgdmFyIG10ID0gc2NhbGUoc2VsLmRhdHVtKCkpLFxuICAgICAgZGlzdCA9IHNjYWxlKGV4dGVudFsxXSkgKiAyLFxuICAgICAgayA9IChtdCA8IGV4dGVudFsxXSAvIDIpID8gMSA6IC0xXG4gICAgICBrID0gaG9yaXpvbnRhbFEgPyBrICogLTEgOiBrXG4gICAgICBzZWwuYXR0cigndHJhbnNmb3JtJywgZnVuY3Rpb24gKGQsIGkpIHtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUSA/ICBkaXN0ICogayA6IDAsXG4gICAgICAgIHkgPSAhaG9yaXpvbnRhbFEgPyBkaXN0ICogayA6IDAsXG4gICAgICAgIHQgPSAndHJhbnNsYXRlKCcreCsnLCcreSsnKSdcbiAgICAgICAgcmV0dXJuIHRcbiAgICAgIH0pXG4gICAgfVxuICAgIHZhciB0aWNrRXhpdEFuaW1hdGlvbiA9IGZ1bmN0aW9uKHNlbCkge1xuICAgICAgdmFyIG10ID0gc2NhbGUoc2VsLmRhdHVtKCkpLFxuICAgICAgZGlzdCA9IHNjYWxlKGV4dGVudFsxXSkgKiAyLFxuICAgICAgayA9IChtdCA8IGV4dGVudFsxXSAvIDIpID8gMSA6IC0xXG4gICAgICBrID0gaG9yaXpvbnRhbFEgPyBrICogLTEgOiBrXG4gICAgICBzZWwudHJhbnNpdGlvbigpLmR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbikuZWFzZShlYXNlRnVuYylcbiAgICAgIC5zdHlsZSgnb3BhY2l0eScsIDApXG4gICAgICAuYXR0cigndHJhbnNmb3JtJywgZnVuY3Rpb24gKGQsIGkpIHtcbiAgICAgICAgdmFyXG5cbiAgICAgICAgeCA9IGhvcml6b250YWxRID8gIGRpc3QgKiBrICA6IDAsXG4gICAgICAgIHkgPSAhaG9yaXpvbnRhbFEgPyBkaXN0ICogayA6IDAsXG4gICAgICAgIHQgPSAndHJhbnNsYXRlKCcreCsnLCcreSsnKSdcbiAgICAgICAgcmV0dXJuIHRcbiAgICAgIH0pLnJlbW92ZSgpXG4gICAgfVxuXG4gICAgaWYgKCFjYXRlZ29yaWNhbFEpe1xuICAgICAgc3BhY2VyRnVuY3Rpb24uZW50ZXJGdW5jdGlvbih0aWNrRW50ZXJBbmltYXRpb24pXG4gICAgICBzcGFjZXJGdW5jdGlvbi5leGl0RnVuY3Rpb24odGlja0V4aXRBbmltYXRpb24pXG4gICAgfVxuXG4gICAgLy8gbW92ZSB0aWNrIGNvbnRhaW5lcnNcbiAgICBzcGFjZXJGdW5jdGlvbihjb250YWluZXIsIHRpY2tEYXRhLCAwKVxuXG4gICAgLy8gbW92ZSBieSBmb3IgeCBhbmQgeSBuZWVkZWQgdG8gY2VudGVyIGNhdGVnb3JpY2FsIHRpY2tzLCBsYWJlbHMsIGFuZCBndWlkZWxpbmVzXG4gICAgZnVuY3Rpb24gbW92ZVhCeShkLCBpLCBob3Jpem9udGFsUSwgY2F0ZWdvcmljYWxRLCBvYmplY3RTaXplKXtcbiAgICAgIHJldHVybiAoaG9yaXpvbnRhbFEpXG4gICAgICA/IChjYXRlZ29yaWNhbFEpXG4gICAgICAgID8gb2JqZWN0U2l6ZSAvIDJcbiAgICAgICAgOiAwXG4gICAgICA6IDBcbiAgICB9XG5cbiAgICBmdW5jdGlvbiBtb3ZlWUJ5KGQsIGksIHZlcnRpY2FsUSwgY2F0ZWdvcmljYWxRLCBvYmplY3RTaXplKXtcbiAgICAgIHJldHVybiAodmVydGljYWxRKVxuICAgICAgPyAoY2F0ZWdvcmljYWxRKVxuICAgICAgICA/IG9iamVjdFNpemUgLyAyXG4gICAgICAgIDogMFxuICAgICAgOiAwXG4gICAgfVxuXG5cblxuICAgIHZhciBsYWJlbE5hbWVHcm91cCA9IHNhZmVTZWxlY3Qoc2VsZWN0aW9uLCAnZycsIGh5cGVuYXRlKG5hbWVzcGFjZSwnYXhpcy1uYW1lJykpXG5cblxuXG4gICAgdmFyIGxhYmVsRWxlbWVudCA9IHNhZmVTZWxlY3QobGFiZWxOYW1lR3JvdXAsICd0ZXh0JywgaHlwZW5hdGUobmFtZXNwYWNlLCAnbmFtZScpKVxuICAgIGlmIChsYWJlbEVsZW1lbnQgIT0gdW5kZWZpbmVkKSB7XG4gICAgICBsYWJlbEVsZW1lbnQudGV4dChsYWJlbClcblxuICAgICAgaWYgKG9yaWVudCA9PSAnbGVmdCcgfHwgb3JpZW50ID09ICdyaWdodCcpIHtcbiAgICAgICAgbGFiZWxFbGVtZW50LmF0dHIoJ3RyYW5zZm9ybScsICdyb3RhdGUoLTkwKScpXG4gICAgICB9XG5cbiAgICAgIHZhciBiYm94ID0gbGFiZWxFbGVtZW50Lm5vZGUoKS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVxuICAgICAgbGFiZWxOYW1lR3JvdXAuYXR0cigndHJhbnNmb3JtJyxmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSAwLFxuICAgICAgICB5ID0gMCxcbiAgICAgICAgdFxuXG4gICAgICAgIGlmIChvcmllbnQgPT0gJ2JvdHRvbScpIHtcbiAgICAgICAgICB4ID0gc3BhY2VYIC0gYmJveC53aWR0aCAtIHRpY2tMYWJlbE1hcmdpblxuICAgICAgICAgIHkgPSAtIHRpY2tUaWNrTGFiZWxTcGFjZXJcbiAgICAgICAgfVxuICAgICAgICBlbHNlIGlmIChvcmllbnQgPT0gJ3RvcCcpIHtcbiAgICAgICAgICB4ID0gc3BhY2VYIC0gYmJveC53aWR0aCAtIHRpY2tMYWJlbE1hcmdpblxuICAgICAgICAgIHggPSB0aWNrTGFiZWxNYXJnaW5cbiAgICAgICAgICB5ID0gYmJveC5oZWlnaHQgKyB0aWNrVGlja0xhYmVsU3BhY2VyXG4gICAgICAgIH1cbiAgICAgICAgZWxzZSBpZiAob3JpZW50ID09ICdsZWZ0Jykge1xuICAgICAgICAgIHggPSBiYm94LndpZHRoICsgdGlja1RpY2tMYWJlbFNwYWNlclxuICAgICAgICAgIHkgPSBiYm94LmhlaWdodCArIHRpY2tMYWJlbE1hcmdpblxuICAgICAgICB9IGVsc2UgaWYgKG9yaWVudCA9PSAncmlnaHQnKSB7XG4gICAgICAgICAgeCA9IC0oYmJveC53aWR0aCArIHRpY2tUaWNrTGFiZWxTcGFjZXIpXG4gICAgICAgICAgeSA9IGJib3guaGVpZ2h0ICsgdGlja0xhYmVsTWFyZ2luXG4gICAgICAgIH0gZWxzZSB7XG5cbiAgICAgICAgfVxuICAgICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAgIHJldHVybiB0XG4gICAgICB9KVxuXG5cblxuICAgIH0gZWxzZSB7XG4gICAgICBsYWJlbEVsZW1lbnQucmVtb3ZlKClcbiAgICB9XG4gICAgLypcbiAgICBJZGVhIGZyb20gU3RhY2sgT3ZlcmZsb3dcbiAgICBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy81MDU3OTUzNS9kMy1qcy12NC10cnVuY2F0ZS10ZXh0LXRvLWZpdC1pbi1maXhlZC1zcGFjZS81MDU4NTAyMj9ub3JlZGlyZWN0PTEjY29tbWVudDg4MjM1NTYyXzUwNTg1MDIyXG4gICAgdG8gdXNlIGNsaXAgcGF0aCB0byBtYWtlIHRoaW5ncyBmaXQgaW4gZml4ZWQgc2l6ZS4gSGF2ZSB5ZXQgZ290IHRoaXMgdG8gd29yayBuaWNlbHkuXG4gICAgKi9cbiAgICAvLyB2YXIgZGVmcyA9IGQzLnNlbGVjdChjb250YWluZXIubm9kZSgpLnBhcmVudE5vZGUpLnNlbGVjdCgnZGVmcycpXG4gICAgLy8gdmFyIHRpY2tMYWJlbENsaXBQYXRoID0gc2FmZVNlbGVjdChkZWZzLCAnY2xpcFBhdGgnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ3RpY2stbGFiZWwtY2xpcC1wYXRoJykpLmF0dHIoJ2lkJywgIGh5cGVuYXRlKG5hbWVzcGFjZSwndGljay1sYWJlbC1jbGlwLXBhdGgnKSlcbiAgICAvLyB2YXIgdGlja0xhYmVsQ2xpcFBhdGhSZWN0ID0gc2FmZVNlbGVjdCh0aWNrTGFiZWxDbGlwUGF0aCwgJ3JlY3QnLCAgaHlwZW5hdGUobmFtZXNwYWNlLCd0aWNrLWxhYmVsLWNsaXAtcGF0aC1yZWN0JykpXG4gICAgLy8gLmF0dHIoJ3gnLCAwKVxuICAgIC8vIC5hdHRyKCd5JywgMClcbiAgICAvLyAuYXR0cignd2lkdGgnLCBmdW5jdGlvbihkLCBpKXtcbiAgICAvLyAgIGlmIChob3Jpem9udGFsUSkgeyByZXR1cm4gdGlja0xhYmVsRm9udFNpemUgfVxuICAgIC8vICAgaWYgKHZlcnRpY2FsUSkgeyByZXR1cm4gc3BhY2VYIC0gdGlja0xlbmd0aCB9XG4gICAgLy8gfSlcbiAgICAvLyAuYXR0cignaGVpZ2h0JywgZnVuY3Rpb24oZCwgaSl7XG4gICAgLy8gICBpZiAodmVydGljYWxRKSB7IHJldHVybiB0aWNrTGFiZWxGb250U2l6ZSB9XG4gICAgLy8gICBpZiAoaG9yaXpvbnRhbFEpIHsgcmV0dXJuIHNwYWNlWSAtIHRpY2tMZW5ndGggfVxuICAgIC8vIH0pXG5cblxuICAgIC8vIGZvciBlYWNoIHRpY2sgY29udGFpbmVyXG4gICAgdmFyIHRpY2tzID0gY29udGFpbmVyLnNlbGVjdEFsbCgnZzpub3QoLnRvLXJlbW92ZSkuJytvYmpDbGFzcykuZWFjaChmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhciB0aGF0ID0gZDMuc2VsZWN0KHRoaXMpLnN0eWxlKCdvcGFjaXR5JywgMSlcblxuICAgICAgLy8gbWFrZSBhbmQgbW92ZSB0aWNrXG4gICAgICB2YXIgdGljayA9IHNhZmVTZWxlY3QodGhhdCwgJ2xpbmUnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ3RpY2snKSlcbiAgICAgIC5hdHRyKFwieDFcIiwgMClcbiAgICAgIC5hdHRyKFwieDJcIiwgaG9yaXpvbnRhbFEgPyAwIDogb3JpZW50ID09IFwibGVmdFwiID8gLXRpY2tMZW5ndGggOiB0aWNrTGVuZ3RoKVxuICAgICAgLmF0dHIoXCJ5MVwiLCAwKVxuICAgICAgLmF0dHIoJ3kyJywgIHZlcnRpY2FsUSA/IDAgOiBvcmllbnQgPT0gXCJ0b3BcIiA/IC10aWNrTGVuZ3RoIDogdGlja0xlbmd0aClcbiAgICAgIC5hdHRyKCdzdHJva2UnLCB0aWNrU3Ryb2tlKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIHRpY2tTdHJva2VXaWR0aClcbiAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKSB7XG4gICAgICAgIHZhclxuICAgICAgICB4ID0gbW92ZVhCeShkLCBpLCBob3Jpem9udGFsUSwgY2F0ZWdvcmljYWxRLCBvYmplY3RTaXplKSxcbiAgICAgICAgeSA9IG1vdmVZQnkoZCwgaSwgdmVydGljYWxRLCBjYXRlZ29yaWNhbFEsIG9iamVjdFNpemUpLFxuICAgICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAgIHJldHVybiB0XG4gICAgICB9KVxuXG4gICAgICAvLyBtYWtlIGFuZCBtb3ZlIGxhYmVsXG4gICAgICB2YXIgbGFiZWwgPSBzYWZlU2VsZWN0KHRoYXQsICd0ZXh0JywgaHlwZW5hdGUobmFtZXNwYWNlLCdsYWJlbCcpKVxuICAgICAgLnRleHQoZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIHZhciBzID0gdHlwZW9mIGQgPT0gJ251bWJlcicgPyByb3VuZChkLCByb3VuZFRvKSA6IGRcbiAgICAgICAgcyA9IHRydW5jYXRlU3RyaW5nKFN0cmluZyhzKSwgKGhvcml6b250YWxRID8gc3BhY2VZIDogc3BhY2VYKSAtIHRpY2tMZW5ndGgtdGlja0xhYmVsTWFyZ2luLXRpY2tUaWNrTGFiZWxTcGFjZXIsIHRpY2tMYWJlbEZvbnRTaXplICogMC40NSlcbiAgICAgICAgcmV0dXJuIHNcbiAgICAgIH0pXG4gICAgICAuYXR0cignZm9udC1zaXplJywgdGlja0xhYmVsRm9udFNpemUpXG4gICAgICAuYXR0cigndGV4dC1hbmNob3InLCB0aWNrTGFiZWxUZXh0QW5jaG9yKVxuICAgICAgLy8gdHJ1bmNhdGVUZXh0KGxhYmVsLCBsYWJlbC50ZXh0KCksIG9yaWVudCwgdGlja0xlbmd0aCwgaG9yaXpvbnRhbFEgPyBzcGFjZVkgOiBzcGFjZVgsIG92ZXJmbG93USlcblxuICAgICAgbGFiZWwuYXR0cigndHJhbnNmb3JtJywgZnVuY3Rpb24oZCwgaSkge1xuICAgICAgICB2YXJcbiAgICAgICAgcmVjdCA9IGQzLnNlbGVjdCh0aGlzKS5ub2RlKCkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCksXG4gICAgICAgIGxlbmcgPSBkMy5zZWxlY3QodGhpcykubm9kZSgpLmdldENvbXB1dGVkVGV4dExlbmd0aCgpLFxuICAgICAgICB4ID0gbW92ZVhCeShkLCBpLCBob3Jpem9udGFsUSwgY2F0ZWdvcmljYWxRLCBvYmplY3RTaXplKSxcbiAgICAgICAgeSA9IG1vdmVZQnkoZCwgaSwgdmVydGljYWxRLCBjYXRlZ29yaWNhbFEsIG9iamVjdFNpemUpXG4gICAgICAgIC8vIG9uIHJlY2FsbCwgcmVjdCBjaGFuZ2VzIGJlY2F1c2Ugb2Ygcm90YXRpb24gc28gbmVlZCBNYXRoLm1pbihyZWN0LmhlaWdodCwgcmVjdC53aWR0aClcblxuICAgICAgICB2YXIgcyA9IE1hdGguc2luKHRpY2tMYWJlbFJvdGF0aW9uKSAqIGxlbmcgKiAwXG5cbiAgICAgICAgaWYgKG9yaWVudCA9PSAndG9wJykge1xuICAgICAgICAgIHkgPSAtKHRpY2tMZW5ndGgrdGlja1RpY2tMYWJlbFNwYWNlcik7XG4gICAgICAgICAgLy8geSA9IHRpY2tMZW5ndGgrdGlja1RpY2tMYWJlbFNwYWNlcjtcblxuICAgICAgICAgIC8vIHkgLT0gTWF0aC5tYXgocmVjdC5oZWlnaHQsIHJlY3Qud2lkdGgpO1xuICAgICAgICAgIHggKz0gTWF0aC5taW4ocmVjdC5oZWlnaHQsIHJlY3Qud2lkdGgpICogMC4yNVxuICAgICAgICAgIC8vIHggLT0gbGVuZyAqIDAuMjUgKyBzXG4gICAgICAgIH1cbiAgICAgICAgaWYgKG9yaWVudCA9PSAnYm90dG9tJykge1xuICAgICAgICAgIHkgPSB0aWNrTGVuZ3RoK3RpY2tUaWNrTGFiZWxTcGFjZXI7XG4gICAgICAgICAgeCArPSBNYXRoLm1pbihyZWN0LmhlaWdodCwgcmVjdC53aWR0aCkgKiAwLjI1XG4gICAgICAgICAgLy8geCArPSBsZW5nICogMC4yNSAtIHNcbiAgICAgICAgfVxuICAgICAgICBpZiAob3JpZW50ID09ICdsZWZ0Jykge1xuICAgICAgICAgIHggLT0gKHRpY2tMZW5ndGgrdGlja1RpY2tMYWJlbFNwYWNlcik7XG4gICAgICAgICAgLy8geSArPSByZWN0LmhlaWdodCAqIDAuNTsgeS09IHJlY3QuaGVpZ2h0LzRcbiAgICAgICAgICB5ICs9IE1hdGgubWluKHJlY3QuaGVpZ2h0LCByZWN0LndpZHRoKSAqIDAuMjVcbiAgICAgICAgICAvLyB5ICs9IGxlbmcgKiAwLjI1XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG9yaWVudCA9PSAncmlnaHQnKSB7XG4gICAgICAgICAgeCArPSAodGlja0xlbmd0aCt0aWNrVGlja0xhYmVsU3BhY2VyKTtcbiAgICAgICAgICAvLyB5ICs9IE1hdGgubWluKHJlY3QuaGVpZ2h0LCByZWN0LndpZHRoKSAqIDAuMjVcbiAgICAgICAgICAvLyB5ICs9IGxlbmcgKiAwLjI1XG4gICAgICAgICAgeSArPSByZWN0LmhlaWdodCAqIDAuNTsgeS09IHJlY3QuaGVpZ2h0LzRcbiAgICAgICAgfVxuXG4gICAgICAgIHZhclxuICAgICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknLFxuICAgICAgICByID0gJ3JvdGF0ZSgnK3RpY2tMYWJlbFJvdGF0aW9uKycpJ1xuICAgICAgICByZXR1cm4gdCArIHJcbiAgICAgIH0pXG4gICAgICAub24oJ21vdXNlbW92ZScsIGxhYmVsSG92ZXIpXG4gICAgICAub24oJ21vdXNlb3V0JywgbGFiZWxIb3Zlck9mZilcbiAgICAgIC5vbignY2xpY2snLCB0aWNrTGFiZWxPbkNsaWNrKVxuICAgICAgLy8gLmF0dHIoJ2NsaXAtcGF0aCcsICd1cmwoIycraHlwZW5hdGUobmFtZXNwYWNlLCd0aWNrLWxhYmVsLWNsaXAtcGF0aCcpKycpJylcblxuICAgICAgLy8gYWRkIGd1aWRsaW5lcyBhcyBuZWVkZWRcbiAgICAgICBpZiAoZ3VpZGVMaW5lc1EpIHtcbiAgICAgICAgIHZhciBnbGluZSA9IHNhZmVTZWxlY3QodGhhdCwgJ2xpbmUnLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdndWlkZWxpbmUnKSlcbiAgICAgICAgIC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgICAgLmF0dHIoXCJ4MVwiLCAwKVxuICAgICAgICAgLmF0dHIoXCJ4MlwiLCBob3Jpem9udGFsUSA/IDAgOiBvcmllbnQgPT0gXCJsZWZ0XCIgPyBndWlkZWxpbmVTcGFjZSA6IC1ndWlkZWxpbmVTcGFjZSlcbiAgICAgICAgIC5hdHRyKFwieTFcIiwgMClcbiAgICAgICAgIC5hdHRyKCd5MicsICB2ZXJ0aWNhbFEgPyAwIDogb3JpZW50ID09IFwidG9wXCIgPyBndWlkZWxpbmVTcGFjZSA6IC1ndWlkZWxpbmVTcGFjZSlcbiAgICAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKSB7XG4gICAgICAgICAgIHZhclxuICAgICAgICAgICB4ID0gbW92ZVhCeShkLCBpLCBob3Jpem9udGFsUSwgY2F0ZWdvcmljYWxRLCBvYmplY3RTaXplKSxcbiAgICAgICAgICAgeSA9IG1vdmVZQnkoZCwgaSwgdmVydGljYWxRLCBjYXRlZ29yaWNhbFEsIG9iamVjdFNpemUpLFxuICAgICAgICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAgICAgIHJldHVybiB0XG4gICAgICAgICB9KVxuICAgICAgIH0gZWxzZSB7IHRoYXQuc2VsZWN0KCdsaW5lLicraHlwZW5hdGUobmFtZXNwYWNlLCAnZ3VpZGVsaW5lJykpLnJlbW92ZSgpIH1cblxuICAgIH0pXG5cbiAgICAvLyBhcHBseSBhbHRlcm5hdGluZyBndWlkbGluZSB0aGlja25lc3NcbiAgICBpZiAoZ3VpZGVMaW5lc1EpIHtcbiAgICAgIGNvbnRhaW5lci5zZWxlY3RBbGwoJy4nK2h5cGVuYXRlKG5hbWVzcGFjZSwnZ3VpZGVsaW5lJykpXG4gICAgICAuYXR0cignc3Ryb2tlJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIGlmIChpICUgMiA9PSAwKSB7IHJldHVybiBtb2RpZnlIZXhpZGVjaW1hbENvbG9yTHVtaW5hbmNlKGd1aWRlTGluZVN0cm9rZSwgMC44KSB9XG4gICAgICAgIHJldHVybiBndWlkZUxpbmVTdHJva2VcbiAgICAgIH0pXG4gICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIGlmIChpICUgMiA9PSAwKSB7IHJldHVybiBndWlkZUxpbmVTdHJva2VXaWR0aCAqMC44fVxuICAgICAgICByZXR1cm4gZ3VpZGVMaW5lU3Ryb2tlV2lkdGhcbiAgICAgIH0pXG4gICAgICAuYXR0cignbWlub3InLCBmdW5jdGlvbihkLCBpKXtyZXR1cm4gaSUyID09IDB9KVxuICAgIH1cblxuXG4gICAgLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuICAgICoqIE1ha2UgdGhlIGxpbmUgb2YgdGhlIGF4aXNcbiAgICAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4gICAgdmFyIGxpbmUgPSBzYWZlU2VsZWN0KHNlbGVjdGlvbiwgJ3BhdGgnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ2xpbmUnKSlcbiAgICAvLyAuYXR0cigneDEnLCAwKVxuICAgIC8vIC5hdHRyKCd4MicsIGhvcml6b250YWxRID8gc3BhY2VYIDogMClcbiAgICAvLyAuYXR0cigneTEnLCAwKVxuICAgIC8vIC5hdHRyKCd5MicsIGhvcml6b250YWxRID8gMCA6IHNwYWNlWSlcbiAgICAuYXR0cignZCcsXG4gICAgICBob3Jpem9udGFsUVxuICAgICAgPyAnTSAwLDAgSCcgKyBzcGFjZVggKyAnLDAnXG4gICAgICA6ICdNIDAsMCBWIDAsJyArIHNwYWNlWVxuICAgIClcbiAgICAuYXR0cignc3Ryb2tlJywgbGluZVN0cm9rZSlcbiAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgbGluZVN0cm9rZVdpZHRoKVxuICAgIC5jbGFzc2VkKCdheGlzLWxpbmUnLCB0cnVlKVxuXG5cbiAgfVxuXG4gIC8vIGhvdmVyIG9mIGxhYmVsIHNob3cgZnVsbCB0ZXh0IGxhYmVsIGluIGNhc2UgaXQgaXMgdHJ1bmNhdGVkXG4gIGZ1bmN0aW9uIGxhYmVsSG92ZXIoZCwgaSl7XG4gICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcykuc3R5bGUoJ2ZpbGwnLCAncmVkJylcbiAgICBkMy5zZWxlY3QodC5ub2RlKCkucGFyZW50Tm9kZSkuc2VsZWN0KFwibGluZS5cIitoeXBlbmF0ZShuYW1lc3BhY2UsJ3RpY2snKSlcbiAgICAuYXR0cihcInN0cm9rZVwiLCAncmVkJylcbiAgICAuYXR0cihcInN0cm9rZS13aWR0aFwiLCB0aWNrU3Ryb2tlV2lkdGgqMilcblxuICAgIGlmIChndWlkZUxpbmVzUSkge1xuICAgICAgZDMuc2VsZWN0KHQubm9kZSgpLnBhcmVudE5vZGUpLnNlbGVjdCgnbGluZS4nK2h5cGVuYXRlKG5hbWVzcGFjZSwgJ2d1aWRlbGluZScpKVxuICAgICAgLmF0dHIoJ3N0cm9rZScsICdyZWQnKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIGd1aWRlTGluZVN0cm9rZVdpZHRoKjIpXG4gICAgfVxuXG4gICAgdmFyIHMgPSB0eXBlb2YgZCA9PSAnbnVtYmVyJyA/IHJvdW5kKGQsIHJvdW5kVG8pIDogZFxuXG4gICAgdmFyIG0gPSBkMy5tb3VzZShkMy5zZWxlY3QoJ2h0bWwnKS5ub2RlKCkpXG4gICAgdmFyIGRpdiA9IHNhZmVTZWxlY3QoZDMuc2VsZWN0KCdib2R5JyksICdkaXYnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ2d1aWRlbGluZS10b29sdGlwJykpXG4gICAgLmF0dHIoJ2lkJywgaHlwZW5hdGUobmFtZXNwYWNlLCdndWlkZWxpbmUtdG9vbHRpcCcpKVxuICAgIC5zdHlsZSgncG9zaXRpb24nLCAnYWJzb2x1dGUnKVxuICAgIC5zdHlsZSgnbGVmdCcsIChkMy5ldmVudC5wYWdlWCsxNSkrJ3B4JylcbiAgICAuc3R5bGUoJ3RvcCcsIChkMy5ldmVudC5wYWdlWSsxNSkrJ3B4JylcbiAgICAuc3R5bGUoJ2JhY2tncm91bmQtY29sb3InLCAnd2hpdGUnKVxuICAgIC5zdHlsZSgnYm9yZGVyLWNvbG9yJywgJ2JsYWNrJylcbiAgICAvLyAuc3R5bGUoJ21pbi13aWR0aCcsICh0aWNrTGFiZWxGb250U2l6ZSAqIChTdHJpbmcocykuc3BsaXQoJy4nKVswXS5sZW5ndGgrMykpKydweCcpXG4gICAgLy8gLnN0eWxlKCdtaW4taGVpZ2h0JywgKHRpY2tMYWJlbEZvbnRTaXplICogKFN0cmluZyhzKS5zcGxpdCgnLicpWzBdLmxlbmd0aCszKSkrJ3B4JylcbiAgICAuc3R5bGUoJ2JvcmRlci1yYWRpdXMnLCAnMTBweCcpXG4gICAgLnN0eWxlKCdkaXNwbGF5JywgJ2ZsZXgnKVxuICAgIC5zdHlsZSgnanVzdGlmeS1jb250ZW50JywgJ2NlbnRlcicpXG4gICAgLnN0eWxlKCd0ZXh0LWFsaWduJywgJ21pZGRsZScpXG4gICAgLnN0eWxlKCdwYWRkaW5nJywgNCtcInB4XCIpXG5cbiAgICAuc3R5bGUoJ2JvcmRlci1zdHlsZScsICdzb2xpZCcpXG4gICAgLnN0eWxlKCdib3JkZXItd2lkdGgnLCAyKVxuXG4gICAgdmFyIHRleHQgPSBzYWZlU2VsZWN0KGRpdiwgJ2RpdicpXG4gICAgLnRleHQodGlja0xhYmVsT25Ib3ZlckZ1bmMocywgaSkpXG4gICAgLnN0eWxlKCdjb2xvcicsICdibGFjaycpXG4gICAgLnN0eWxlKCdhbGlnbi1zZWxmJywgJ2NlbnRlcicpXG5cbiAgICB2YXIgYmJveCA9IGRpdi5ub2RlKCkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KClcbiAgICBpZiAoYmJveC54ICsgYmJveC53aWR0aCA+IHdpbmRvdy5pbm5lcldpZHRoKSB7XG4gICAgICBkaXYuc3R5bGUoJ2xlZnQnLCAoZDMuZXZlbnQucGFnZVgtMTUtMzAwKSsncHgnKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGxhYmVsSG92ZXJPZmYoZCwgaSl7XG4gICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcykuc3R5bGUoJ2ZpbGwnLCAnYmxhY2snKVxuICAgIGQzLnNlbGVjdCh0Lm5vZGUoKS5wYXJlbnROb2RlKS5zZWxlY3QoXCJsaW5lLlwiK2h5cGVuYXRlKG5hbWVzcGFjZSwndGljaycpKVxuICAgIC5hdHRyKFwic3Ryb2tlXCIsIHRpY2tTdHJva2UpXG4gICAgLmF0dHIoXCJzdHJva2Utd2lkdGhcIiwgdGlja1N0cm9rZVdpZHRoKVxuXG4gICAgaWYgKGd1aWRlTGluZXNRKSB7XG4gICAgICB2YXIgZ2xpbmUgPSBkMy5zZWxlY3QodC5ub2RlKCkucGFyZW50Tm9kZSkuc2VsZWN0KCdsaW5lLicraHlwZW5hdGUobmFtZXNwYWNlLCAnZ3VpZGVsaW5lJykpXG4gICAgICB2YXIgbWlub3JRID0gZ2xpbmUuYXR0cignbWlub3InKVxuICAgICAgZ2xpbmUuYXR0cignc3Ryb2tlJywgZnVuY3Rpb24oZCwgaWkpe1xuICAgICAgICBpZiAobWlub3JRID09ICd0cnVlJykgeyByZXR1cm4gbW9kaWZ5SGV4aWRlY2ltYWxDb2xvckx1bWluYW5jZShndWlkZUxpbmVTdHJva2UsIDAuOCkgfVxuICAgICAgICByZXR1cm4gZ3VpZGVMaW5lU3Ryb2tlXG4gICAgICB9KVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIGZ1bmN0aW9uKGQsIGlpKXtcbiAgICAgICAgaWYgKG1pbm9yUSA9PSAndHJ1ZScpIHsgcmV0dXJuIGd1aWRlTGluZVN0cm9rZVdpZHRoICowLjh9XG4gICAgICAgIHJldHVybiBndWlkZUxpbmVTdHJva2VXaWR0aFxuICAgICAgfSlcbiAgICB9XG4gICAgZDMuc2VsZWN0KFwiI1wiK2h5cGVuYXRlKG5hbWVzcGFjZSwnZ3VpZGVsaW5lLXRvb2x0aXAnKSkucmVtb3ZlKClcbiAgfVxuXG5cblxuICByZXR1cm4gYXhpc1xufVxuIiwiaW1wb3J0IHtoeXBlbmF0ZSwgc2FmZVNlbGVjdH0gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7c2V0dXBDb250YWluZXIsIGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3QsIGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXJ9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHt1bmlxdWUsIGZsYXR0ZW59IGZyb20gJy4vYXJyYXktZnVuY3Rpb25zJztcbmltcG9ydCB7Z3JvdXBpbmdTcGFjZXJ9IGZyb20gJy4vZ3JvdXBpbmctc3BhY2VyJztcbmltcG9ydCB7Y29sb3JGdW5jdGlvbiBhcyBDRn0gZnJvbSAnLi9jb2xvci1mdW5jdGlvbic7XG5pbXBvcnQge3Rvb2x0aXAgYXMgVFRpcH0gZnJvbSAnLi90b29sdGlwJztcbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCQVIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbi8qKlxuICogQ3JlYXRlcyBhIGJhclxuICpcbiAqIHtAbGluayBodHRwczovL3N1bW5ldXJvbi5naXRsYWIuaW8vZDNzbS9kZW1vcy9iYXItY2hhcnQtc2FtZS1kYXRhLWNvbXBsZXgtZ3JvdXBpbmcvaW5kZXguaHRtbCBEZW1vfVxuICogQGNvbnN0cnVjdG9yIGJhclxuICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IHNlbGVjdGlvblxuICogQG5hbWVzcGFjZSBiYXJcbiAqIEByZXR1cm5zIHtmdW5jdGlvbn0gYmFyXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBiYXIgKCBzZWxlY3Rpb24gKSB7XG4gIC8qXG4gIEFzc3VtZXMgdGhhdCBkYXRhIGlzIGxpc3QgYW4gb2JqZWN0LlxuXG4gIFRoZSBrZXlzIG9mIGRhdGEgd2lsbCBiZSBib3VuZCB0byB0aGUgYmFycy5cbiAgVGhlIHZhbHVlRXh0cmFjdG9yIGZ1bmN0aW9uIHdpbGwgZXh0cmFjdCB0aGUgdmFsdWUgZnJvbSBkYXRhW2tleV0uXG5cbiAgR3JvdXBpbmcgY2FuIGJlIHVzZWQgaWYgZGVzaXJlZC4gSXQgc2hvdWxkIGJlIGFuIGFyYml0cmFyeSBjb21wbGV4IGxpc3Qgd2hlcmVcbiAgdGhlIHZhbHVlcyBhcmUgc3RyaW5ncyBtYXRjaGluZyBrZXlzIGluIGRhdGEuXG4gICovXG4gIHZhclxuICAvKipcbiAgKiBEYXRhIHRvIHBsb3QuIEFzc3VtZWQgdG8gYmUgYSBvYmplY3QsIHdoZXJlIGVhY2gga2V5IGNvcnJlc3BvbmRzIHRvIGEgYmFyXG4gICogKHNlZSB7QGxpbmsgYmFyI2RhdGF9KVxuICAqIEBwYXJhbSB7T2JqZWN0fSBbZGF0YT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZGF0YSxcbiAgLyoqXG4gICogV2hpY2ggZGlyZWN0aW9uIHRvIHJlbmRlciB0aGUgYmFycyBpblxuICAqIChzZWUge0BsaW5rIGJhciNvcmllbnR9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbb3JpZW50PSdob3Jpem9udGFsJ11cbiAgKiBAbWVtYmVyb2YgYmFyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvcmllbnQ9J2hvcml6b250YWwnLFxuICAvKipcbiAgKiBBbW91bnQgb2YgaG9yaXpvbnRhbCBzcGFjZSAoaW4gcGl4ZWxzKSBhdmFpYmxlIHRvIHJlbmRlciB0aGUgYmFyIGluXG4gICogKHNlZSB7QGxpbmsgYmFyI3NwYWNlWH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVg9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlWCxcbiAgLyoqXG4gICogQW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBiYXIgaW5cbiAgKiAoc2VlIHtAbGluayBiYXIuc3BhY2VZfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlWT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VZLFxuXG4gIC8qKlxuICAqIFdoZXRoZXIgb3Igbm90IHRvIGFsbG93IGJhciB0byByZW5kZXIgZWxlbWVudHMgcGFzcyB0aGUgbWFpbiBzcGF0aWFsIGRpbWVuc2lvblxuICAqIGdpdmVuIHRoZSBvcmllbnRhdGlvbiAoc2VlIHtAbGluayBiYXIjb3JpZW50fSksIHdoZXJlIHtAbGluayBiYXIjb3JpZW50fT1cImhvcml6b250YWxcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgYmFyI3NwYWNlWH0gYW5kIHdoZXJlIHtAbGluayBiYXIjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJhciNzcGFjZVl9XG4gICogQHBhcmFtIHtib29sZWFufSBbb3ZlcmZsb3dRPWZhbHNlXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG92ZXJmbG93USA9IGZhbHNlLFxuXG4gIC8qKlxuICAqIEFuIGFycmF5IC0gcHV0YXRpdmVseSBvZiBvdGhlciBhcnJheXMgLSBkZXBpY3RpbmcgaG93IGJhcnMgc2hvdWxkIGJlIGFycmFuZ2VkXG4gICogQHBhcmFtIHtBcnJheVtdfSBbZ3JvdXBpbmc9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGdyb3VwaW5nLFxuXG4gIC8qKlxuICAqIEhvdyB0byBnZXQgdGhlIHZhbHVlIG9mIHRoZSBiYXJcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdmFsdWVFeHRyYWN0b3I9ZnVuY3Rpb24oa2V5LCBpbmRleCkgeyByZXR1cm4gZGF0YVtrZXldIH1dXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV0gfSxcbiAgLyoqXG4gICogSG93IHRvIHNvcnQgdGhlIGJhcnMgLSBpZiB7QGxpbmsgYmFyI2dyb3VwaW5nfSBpcyBub3QgcHJvdmlkZWQuXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3NvcnRpbmdGdW5jdGlvbj1mdW5jdGlvbihrZXlBLCBrZXlCKSB7cmV0dXJuIGQzLmRlc2NlbmRpbmcoZGF0YVtrZXlBXSwgZGF0YVtrZXlCXSl9XVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKGtleUEsIGtleUIpIHtyZXR1cm4gZDMuZGVzY2VuZGluZyhkYXRhW2tleUFdLCBkYXRhW2tleUJdKX0sXG5cbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCBiYXIgdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZT1kMy5zY2FsZUxpbmVhcl1cbiAgKiBAbWVtYmVyb2YgYmFyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKCksXG4gIC8qKlxuICAqIFRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSBzY2FsZSAoc2VlIHtAbGluayBiYXIjc2NhbGV9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbZG9tYWluUGFkZGluZz0wLjVdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZG9tYWluUGFkZGluZyA9IDAuNSxcblxuICAvKipcbiAgKiBEZWZhdWx0IHNwYWNlIGZvciB0aGUgc3BhY2VyIChwZXJjZW50YWdlKSBvZiBtYWluIGRpbWVuc2lvbiBnaXZlbiB0aGUgb3JpZW50YXRpb25cbiAgKiAoc2VlIHtAbGluayBiYXIjb3JpZW50fSksIHdoZXJlIHtAbGluayBiYXIjb3JpZW50fT1cImhvcml6b250YWxcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgYmFyI3NwYWNlWH0gYW5kIHdoZXJlIHtAbGluayBiYXIjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJhciNzcGFjZVl9IGJldHdlZW4gYmFyc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbb2JqZWN0U3BhY2VyPTAuMDVdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0U3BhY2VyID0gMC4wNSxcbiAgLyoqXG4gICogVGhlIG1pbmltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmVcbiAgKiBAcGFyYW0ge251bWJlcn0gW21pbk9iamVjdFNpemU9NTBdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWluT2JqZWN0U2l6ZSA9IDUwLFxuICAvKipcbiAgKiBUaGUgbWF4aW11bSBzaXplIHRoYXQgYW4gb2JqZWN0IGNhbiBiZVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbWF4T2JqZWN0U2l6ZT0xMDBdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWF4T2JqZWN0U2l6ZSA9IDEwMCxcblxuICAvKipcbiAgKiBUaGUgc3Ryb2tlIHdpZHRoIG9mIHRoZSBiYXJzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtiYXJTdHJva2VXaWR0aD0yXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJhclN0cm9rZVdpZHRoID0gMixcbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgQ29sb3JGdW5jdGlvblxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtjb2xvckZ1bmN0aW9uID0gY29sb3JGdW5jdGlvbigpXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGNvbG9yRnVuY3Rpb24gPSBDRigpLFxuXG5cbiAgLyoqXG4gICogQ29sb3Igb2YgdGhlIGJhY2tncm91bmRcbiAgKiBAcGFyYW0ge3N0cmluZ30gW2JhY2tncm91bmRGaWxsPVwidHJhbnNwYXJlbnRcIl1cbiAgKiBAbWVtYmVyb2YgYmFyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCcsXG4gIC8qKlxuICAqIE5hbWVzcGFjZSBmb3IgYWxsIGl0ZW1zIG1hZGUgYnkgdGhpcyBpbnN0YW5jZSBvZiBiYXJcbiAgKiBAcGFyYW0ge3N0cmluZ30gW25hbWVzcGFjZT1cImQzc20tYmFyXCJdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbmFtZXNwYWNlID0gJ2Qzc20tYmFyJyxcbiAgLyoqXG4gICogQ2xhc3MgbmFtZSBmb3IgYmFyIGNvbnRhaW5lciAoPGc+IGVsZW1lbnQpXG4gICogQHBhcmFtIHtzdHJpbmd9IFtvYmplY3RDbGFzcz1cImJhclwiXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdENsYXNzID0gJ2JhcicsXG5cbiAgLyoqXG4gICogRHVyYXRpb24gb2YgYWxsIHRyYW5zaXRpb25zIG9mIHRoaXMgZWxlbWVudFxuICAqIEBwYXJhbSB7bnVtYmVyfSBbdHJhbnNpdGlvbkR1cmF0aW9uPTEwMDBdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMCxcbiAgLyoqXG4gICogRWFzaW5nIGZ1bmN0aW9uIGZvciB0cmFuc2l0aW9uc1xuICAqIEBwYXJhbSB7ZDMuZWFzZX0gW2Vhc2VGdW5jPWQzLmVhc2VFeHBdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwLFxuXG4gIC8vIHVzZWZ1bCB2YWx1ZXMgdG8gZXh0cmFjdCB0byBwcmV2ZW50IHJlLWNhbGN1bGF0aW9uXG4gIC8qKlxuICAqIFRoZSBrZXlzIG9mIHRoZSBiYXJzXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW2JhcktleXM9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJhcktleXMsXG4gIC8qKlxuICAqIFRoZSB2YWx1ZXMgb2YgdGhlIGJhcnNcbiAgKiBAcGFyYW0ge251bWJlcltdfSBbYmFyVmFsdWVzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYmFyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBiYXJWYWx1ZXMsXG4gIC8qKlxuICAqIFRoZSBvYmplY3RTaXplIChhY3R1YWwgd2lkdGgpIHVzZWQgYnkgdGhlIGJhcnNcbiAgKiBAcGFyYW0ge251bWJlcn0gW29iamVjdFNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBiYXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdFNpemUsXG4gIC8qKlxuICAqIFRoZSBzcGFjZXJTaXplIChhY3R1YWwgd2lkdGgpIHVzZWQgYnkgdGhlIHNwYWNlcnMgYmV0d2VlbiB0aGUgYmFyc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VyU2l6ZT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VyU2l6ZSxcbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgVG9vbHRpcFxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt0b29sdGlwPXRvb2x0aXAoKV1cbiAgKiBAbWVtYmVyb2YgYmFyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0b29sdGlwID0gVFRpcCgpLFxuICBiYXJQZXJjZW50ID0gMVxuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHNlbGVjdGlvbiBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBkMy5zZWxlY3Rpb259XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2VsZWN0aW9uID0gc2VsZWN0aW9uXG4gICAqL1xuICBiYXIuc2VsZWN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCBiYXIpIDogc2VsZWN0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBkYXRhXG4gICAqIChzZWUge0BsaW5rIGJhciNkYXRhfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBvYmplY3R9XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqL1xuICBiYXIuZGF0YSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGF0YSA9IF8sIGJhcikgOiBkYXRhOyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBvcmllbnQgb2YgdGhlIGJhcnNcbiAgICogKHNlZSB7QGxpbmsgYmFyI29yaWVudH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgb2JqZWN0fVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgYmFyLm9yaWVudCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3JpZW50ID0gXywgYmFyKSA6IG9yaWVudDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIGhvcml6b250YWwgc3BhY2UgaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIGJhciNzcGFjZVh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV0gc2hvdWxkIGJlIGEgbnVtYmVyID4gMFxuICAgKiBAcmV0dXJucyB7YmFyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlWCA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmFyLnNwYWNlWCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3BhY2VYID0gXywgYmFyKSA6IHNwYWNlWDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBiYXIjc3BhY2VZfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVkgPSB1bmRlZmluZWRcbiAgICovXG4gIGJhci5zcGFjZVkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWSA9IF8sIGJhcikgOiBzcGFjZVk7IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IGJhciBpcyBhbGxvd2VkIHRvIGdvIGJleW9uZCBzcGVjaWZpZWQgZGltZW5zaW9uc1xuICAgKiAoc2VlIHtAbGluayBiYXIjc3BhY2VYfSlcbiAgICogQHBhcmFtIHtib29sZWFufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgYm9vbGVhbn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvdmVyZmxvd1EgPSBmYWxzZVxuICAgKi9cbiAgYmFyLm92ZXJmbG93USA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3ZlcmZsb3dRID0gXywgYmFyKSA6IG92ZXJmbG93UTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBncm91cGluZyBvZiB0aGUgYmFyc1xuICAgKiAoc2VlIHtAbGluayBiYXIjZ3JvdXBpbmd9KVxuICAgKiBAcGFyYW0ge0FycmF5W119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBBcnJheVtdfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGdyb3VwaW5nID0gdW5kZWZpbmVkXG4gICAqL1xuICBiYXIuZ3JvdXBpbmcgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGdyb3VwaW5nID0gXywgYmFyKSA6IGdyb3VwaW5nOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHZhbHVlRXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGJhciN2YWx1ZUV4dHJhY3Rvcn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2YWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKGtleSwgaW5kZXgpIHsgcmV0dXJuIGRhdGFba2V5XSB9LFxuICAgKi9cbiAgYmFyLnZhbHVlRXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZUV4dHJhY3RvciA9IF8sIGJhcikgOiB2YWx1ZUV4dHJhY3RvcjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzb3J0aW5nRnVuY3Rpb25cbiAgICogKHNlZSB7QGxpbmsgYmFyI3NvcnRpbmdGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihrZXlBLCBrZXlCKSB7cmV0dXJuIGQzLmRlc2NlbmRpbmcoZGF0YVtrZXlBXSwgZGF0YVtrZXlCXSl9LFxuICAgKi9cbiAgYmFyLnNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc29ydGluZ0Z1bmN0aW9uID0gXywgYmFyKSA6IHNvcnRpbmdGdW5jdGlvbjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzY2FsZSBmb3Igd2hpY2ggdGhlIGJhciB2YWx1ZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICAqIChzZWUge0BsaW5rIGJhciNzY2FsZX0pXG4gICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBkMy5zY2FsZX1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcbiAgICovXG4gIGJhci5zY2FsZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGUgPSBfLCBiYXIpIDogc2NhbGU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgc2NhbGVcbiAgICogKHNlZSB7QGxpbmsgYmFyI2RvbWFpblBhZGRpbmd9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBkb21haW5QYWRkaW5nID0gMC41XG4gICAqL1xuICBiYXIuZG9tYWluUGFkZGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZG9tYWluUGFkZGluZyA9IF8sIGJhcikgOiBkb21haW5QYWRkaW5nOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgb2JqZWN0U3BhY2VyXG4gICAqIChzZWUge0BsaW5rIGJhciNvYmplY3RTcGFjZXJ9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RTcGFjZXIgPSAwLjA1XG4gICAqL1xuICBiYXIub2JqZWN0U3BhY2VyID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RTcGFjZXIgPSBfLCBiYXIpIDogb2JqZWN0U3BhY2VyOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1pbk9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgYmFyI21pbk9iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtaW5PYmplY3RTaXplID0gNTBcbiAgICovXG4gIGJhci5taW5PYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtaW5PYmplY3RTaXplID0gXywgYmFyKSA6IG1pbk9iamVjdFNpemU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbWF4T2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBiYXIjbWF4T2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1heE9iamVjdFNpemUgPSAxMDBcbiAgICovXG4gIGJhci5tYXhPYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtYXhPYmplY3RTaXplID0gXywgYmFyKSA6IG1heE9iamVjdFNpemU7IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBiYXJTdHJva2VXaWR0aFxuICAgKiAoc2VlIHtAbGluayBiYXIjYmFyU3Ryb2tlV2lkdGh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBiYXJTdHJva2VXaWR0aCA9IDJcbiAgICovXG4gIGJhci5iYXJTdHJva2VXaWR0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFyU3Ryb2tlV2lkdGggPSBfLCBiYXIpIDogYmFyU3Ryb2tlV2lkdGg7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgY29sb3JGdW5jdGlvblxuICAgKiAoc2VlIHtAbGluayBiYXIjY29sb3JGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGNvbG9yRnVuY3Rpb24gPSBjb2xvckZ1bmN0aW9uKClcbiAgICovXG4gIGJhci5jb2xvckZ1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjb2xvckZ1bmN0aW9uID0gXywgYmFyKSA6IGNvbG9yRnVuY3Rpb247IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBiYWNrZ3JvdW5kRmlsbFxuICAgKiAoc2VlIHtAbGluayBiYXIjYmFja2dyb3VuZEZpbGx9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCdcbiAgICovXG4gIGJhci5iYWNrZ3JvdW5kRmlsbCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFja2dyb3VuZEZpbGwgPSBfLCBiYXIpIDogYmFja2dyb3VuZEZpbGw7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbmFtZXNwYWNlXG4gICAqIChzZWUge0BsaW5rIGJhciNuYW1lc3BhY2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBuYW1lc3BhY2UgPSAnZDNzbS1iYXInXG4gICAqL1xuICBiYXIubmFtZXNwYWNlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChuYW1lc3BhY2UgPSBfLCBiYXIpIDogbmFtZXNwYWNlOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG9iamVjdENsYXNzXG4gICAqIChzZWUge0BsaW5rIGJhciNvYmplY3RDbGFzc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdENsYXNzID0gJ3RpY2stZ3JvdXAnXG4gICAqL1xuICBiYXIub2JqZWN0Q2xhc3MgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdENsYXNzID0gXywgYmFyKSA6IG9iamVjdENsYXNzOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRyYW5zaXRpb25EdXJhdGlvblxuICAgKiAoc2VlIHtAbGluayBiYXIjdHJhbnNpdGlvbkR1cmF0aW9ufSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMFxuICAgKi9cbiAgYmFyLnRyYW5zaXRpb25EdXJhdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodHJhbnNpdGlvbkR1cmF0aW9uID0gXywgYmFyKSA6IHRyYW5zaXRpb25EdXJhdGlvbjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBlYXNlRnVuY1xuICAgKiAoc2VlIHtAbGluayBiYXIjZWFzZUZ1bmN9KVxuICAgKiBAcGFyYW0ge2QzLmVhc2V9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBkMy5lYXNlfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGVhc2VGdW5jID0gZDMuZWFzZUV4cFxuICAgKi9cbiAgYmFyLmVhc2VGdW5jID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChlYXNlRnVuYyA9IF8sIGJhcikgOiBlYXNlRnVuYzsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFyS2V5c1xuICAgKiAoc2VlIHtAbGluayBiYXIjYmFyS2V5c30pXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBzdHJpbmdbXX1cbiAgICogQG1lbWJlcm9mIGJhclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBiYXJLZXlzID0gdW5kZWZpbmVkXG4gICAqL1xuICBiYXIuYmFyS2V5cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFyS2V5cyA9IF8sIGJhcikgOiBiYXJLZXlzOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGJhclZhbHVlc1xuICAgKiAoc2VlIHtAbGluayBiYXIjYmFyVmFsdWVzfSlcbiAgICogQHBhcmFtIHtudW1iZXJbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JhciB8IG51bWJlcltdfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGJhclZhbHVlcyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmFyLmJhclZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFyVmFsdWVzID0gXywgYmFyKSA6IGJhclZhbHVlczsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIGJhciNvYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0U2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmFyLm9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNpemUgPSBfLCBiYXIpIDogb2JqZWN0U2l6ZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzcGFjZXJTaXplXG4gICAqIChzZWUge0BsaW5rIGJhciNzcGFjZXJTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VyU2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmFyLnNwYWNlclNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlclNpemUgPSBfLCBiYXIpIDogc3BhY2VyU2l6ZTsgfTtcblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRvb2x0aXBcbiAgICogKHNlZSB7QGxpbmsgYmFyI3Rvb2x0aXB9KVxuICAgKiBAcGFyYW0ge3Rvb2x0aXB9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtiYXIgfCB0b29sdGlwfVxuICAgKiBAbWVtYmVyb2YgYmFyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRvb2x0aXAgPSB0b29sdGlwKClcbiAgICovXG4gIGJhci50b29sdGlwID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0b29sdGlwID0gXywgYmFyKSA6IHRvb2x0aXA7IH07XG5cbiAgYmFyLmJhclBlcmNlbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhclBlcmNlbnQgPSBfLCBiYXIpIDogYmFyUGVyY2VudDsgfTtcblxuICBmdW5jdGlvbiBiYXIoKSB7XG4gICAgLy8gZm9yIGNvbnZlbmllbmNlIGluIGhhbmRsaW5nIG9yaWVudGF0aW9uIHNwZWNpZmljIHZhbHVlc1xuICAgIHZhciBob3Jpem9udGFsUSA9IChvcmllbnQgPT0gJ2hvcml6b250YWwnIHx8IG9yaWVudCA9PSAnYm90dG9tJyB8fCBvcmllbnQgPT0gJ3RvcCcpID8gdHJ1ZSA6IGZhbHNlXG4gICAgdmFyIHZlcnRpY2FsUSA9ICFob3Jpem9udGFsUVxuXG4gICAgLy8gYmFja2dyb3VuZCBjbGlwaW5nIHJlY3RhbmdsZVxuICAgIHZhciBiZ2NwUmVjdCA9IHt4OjAsIHk6MCwgd2lkdGg6IHNwYWNlWCwgaGVpZ2h0OnNwYWNlWX1cbiAgICB2YXIgY29udGFpbmVyID0gc2V0dXBDb250YWluZXIoIHNlbGVjdGlvbiwgbmFtZXNwYWNlLCBiZ2NwUmVjdCwgYmFja2dyb3VuZEZpbGwgKTtcblxuICAgIC8vIHRvIHByZXZlbnQgcmUtY2FsY3VsYXRpb24gYW5kIGdldHRlcnMgdG8gYmUgcGFzc2VkIHRvIGF4ZXNcbiAgICBiYXJLZXlzID0gZDMua2V5cyhkYXRhKVxuICAgIGJhclZhbHVlcyA9IGJhcktleXMubWFwKHZhbHVlRXh0cmFjdG9yKVxuXG4gICAgLy8gaWYgZ3JvdXBpbmcgaXMgdW5kZWZpbmVkIHNvcnQgYmFyS2V5cyBieSBzb3J0aW5nRnVuY3Rpb25cbiAgICB2YXIgb3JkZXJlZCA9IChncm91cGluZyA9PSB1bmRlZmluZWQpID8gYmFyS2V5cy5zb3J0KHNvcnRpbmdGdW5jdGlvbikgOiBncm91cGluZ1xuICAgIC8vIG9yZGVyZWQgbWlnaHQgYmUgbmVzdGVkIGRlcGVuZGluZyBvbiBncm91cGluZ1xuICAgIGJhcktleXMgPSBmbGF0dGVuKG9yZGVyZWQpXG5cbiAgICB2YXIgbnVtYmVyT2ZPYmplY3RzID0gYmFyS2V5cy5sZW5ndGhcbiAgICB2YXIgZXh0ZW50ID0gW01hdGgubWluKC4uLmJhclZhbHVlcykgLSBkb21haW5QYWRkaW5nLE1hdGgubWF4KC4uLmJhclZhbHVlcykgKyBkb21haW5QYWRkaW5nXTtcblxuXG5cbiAgICAvLyBzZXQgdGhlIHNjYWxlXG5cbiAgICBzY2FsZS5kb21haW4oZXh0ZW50KS5yYW5nZShob3Jpem9udGFsUVxuICAgICAgPyBbMCxzcGFjZVldXG4gICAgICA6IG9yaWVudCA9PSAncmlnaHQnXG4gICAgICAgID8gWzAsIHNwYWNlWF1cbiAgICAgICAgOiBbc3BhY2VYLCAwXVxuICAgIClcbiAgICB2YXIgc3BhY2UgPSBob3Jpem9udGFsUSA/IHNwYWNlWCA6IHNwYWNlWVxuICAgIC8vIGNhbGN1bGF0ZSBvYmplY3Qgc2l6ZVxuICAgIG9iamVjdFNpemUgPSAgKG9iamVjdFNpemUgPT0gdW5kZWZpbmVkKVxuICAgID8gY2FsY3VsYXRlV2lkdGhPZk9iamVjdChzcGFjZSwgbnVtYmVyT2ZPYmplY3RzLCBtaW5PYmplY3RTaXplLCBtYXhPYmplY3RTaXplLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICA6IG9iamVjdFNpemVcblxuICAgIC8vIGNhbGN1bGF0ZSBzcGFjZXIgc2l6ZSBpZiBuZWVkZWRcbiAgICBzcGFjZXJTaXplID0gKHNwYWNlclNpemUgPT0gdW5kZWZpbmVkKVxuICAgID8gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcihiYXJLZXlzLCBzcGFjZSwgb2JqZWN0U2l6ZSwgbnVtYmVyT2ZPYmplY3RzLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICA6IHNwYWNlclNpemVcbiAgICAvLyBtYWtlIHRoZSBuZXN0ZWQgZ3JvdXBzXG4gICAgdmFyIHNwYWNlckZ1bmN0aW9uID0gZ3JvdXBpbmdTcGFjZXIoKVxuICAgIC5ob3Jpem9udGFsUShob3Jpem9udGFsUSkuc2NhbGUoc2NhbGUpLm1vdmVieSgnY2F0ZWdvcnknKS5udW1iZXJPZk9iamVjdHMobnVtYmVyT2ZPYmplY3RzKVxuICAgIC5vYmplY3RDbGFzcyhvYmplY3RDbGFzcykub2JqZWN0U2l6ZShvYmplY3RTaXplKS5zcGFjZXJTaXplKHNwYWNlclNpemUpXG4gICAgLnRyYW5zaXRpb25EdXJhdGlvbih0cmFuc2l0aW9uRHVyYXRpb24pLmVhc2VGdW5jKGVhc2VGdW5jKVxuICAgIC5uYW1lc3BhY2UobmFtZXNwYWNlKVxuICAgIC8vIHNhZmUgZGVmYXVsdCBmdW5jdGlvblxuICAgIHZhciBkZWZhdWx0RXhpdCA9IHNwYWNlckZ1bmN0aW9uLmV4aXRGdW5jdGlvbigpXG5cbiAgICBzcGFjZXJGdW5jdGlvbi5leGl0RnVuY3Rpb24oZnVuY3Rpb24oc2VsKXtcbiAgICAgIC8vIHVzZSBkZWZhdWx0IHRvIG1vdmUgb2JqZWN0cyBvZmYgc2NyZWVuXG4gICAgICAvLyBjb25zb2xlLmxvZyhcIkVYSVRcIiwgc2VsLm5vZGVzKCksIG9iamVjdFNpemUsIHNjYWxlKGV4dGVudFsxXSkpXG4gICAgICBpZiAob2JqZWN0U2l6ZSA9PSB1bmRlZmluZWQpIHtjb25zb2xlLmxvZyhzZWwubm9kZXMoKSwgb2JqZWN0U2l6ZSl9XG4gICAgICBkZWZhdWx0RXhpdChzZWwpXG4gICAgICBzZWwuc2VsZWN0QWxsKCdnJykuY2xhc3NlZChcInRvLXJlbW92ZVwiLCB0cnVlKVxuICAgICAgLy8gc2hyaW5rIHJlY3RhbmdsZXMgaW4gYWRkaXRpb25cbiAgICAgIHNlbC5zZWxlY3RBbGwoJyogPiByZWN0JylcbiAgICAgIC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKVxuICAgICAgLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpIHtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUVxuICAgICAgICAgID8gMFxuICAgICAgICAgIDogMFxuICAgICAgICAsXG4gICAgICAgIHkgPSB2ZXJ0aWNhbFFcbiAgICAgICAgICA/IDBcbiAgICAgICAgICA6IHNjYWxlKGV4dGVudFsxXSlcbiAgICAgICAgLFxuICAgICAgICB0ID0gJ3RyYW5zbGF0ZSgnK3grJywnK3krJyknXG4gICAgICAgIHJldHVybiB0XG4gICAgICB9KVxuICAgICAgLmF0dHIoJ3dpZHRoJywgaG9yaXpvbnRhbFEgPyBvYmplY3RTaXplIDogMClcbiAgICAgIC5hdHRyKCdoZWlnaHQnLCB2ZXJ0aWNhbFEgPyBvYmplY3RTaXplIDogMClcbiAgICAgIC5yZW1vdmUoKVxuICAgIH0pXG5cblxuICAgIC8vIG1vdmUgc3R1ZmZcbiAgICBzcGFjZXJGdW5jdGlvbihjb250YWluZXIsIG9yZGVyZWQsIDApXG5cblxuXG5cblxuICAgIHZhciBwYXJlbnRJbmRleEFycmF5ID0gW11cbiAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnOm5vdCgudG8tcmVtb3ZlKS4nK29iamVjdENsYXNzKVxuICAgIC5lYWNoKGZ1bmN0aW9uKGQsIGkpe3BhcmVudEluZGV4QXJyYXkucHVzaChOdW1iZXIoZDMuc2VsZWN0KHRoaXMpLmF0dHIoJ3BhcmVudC1pbmRleCcpKSl9KVxuXG5cbiAgICBjb2xvckZ1bmN0aW9uID0gY29sb3JGdW5jdGlvbi5jb2xvckJ5KCkgPT0gJ2luZGV4J1xuICAgID8gY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KFswLCBNYXRoLm1heCguLi5wYXJlbnRJbmRleEFycmF5KV0pXG4gICAgOiBjb2xvckZ1bmN0aW9uLmRhdGFFeHRlbnQoZXh0ZW50KVxuXG5cblxuICAgIGNvbnRhaW5lci5zZWxlY3RBbGwoJ2cuJytvYmplY3RDbGFzcysnOm5vdCgudG8tcmVtb3ZlKScpLmVhY2goZnVuY3Rpb24oa2V5LCBpKSB7XG4gICAgICAvLyBjb25zb2xlLmxvZyhrZXksIHNjYWxlKGV4dGVudFsxXSkgLSBzY2FsZSh2YWx1ZUV4dHJhY3RvcihrZXksIGkpKSlcbiAgICAgIHZhciB0ID0gZDMuc2VsZWN0KHRoaXMpLFxuICAgICAgY3VycmVudERhdGEgPSBkYXRhW2tleV0sXG4gICAgICB2YWx1ZSA9IHZhbHVlRXh0cmFjdG9yKGtleSwgaSksXG4gICAgICBpID0gdC5hdHRyKCdwYXJlbnQtaW5kZXgnKSA9PSB1bmRlZmluZWQgPyBpIDogdC5hdHRyKCdwYXJlbnQtaW5kZXgnKSxcbiAgICAgIGZpbGxDb2xvciA9IGNvbG9yRnVuY3Rpb24oa2V5LCB2YWx1ZSwgaSwgJ2ZpbGwnKSwgLy8gcHJldmVudCBkdXBsaWNhdGUgY29tcHV0YXRpb25cbiAgICAgIHN0cm9rZUNvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIHZhbHVlLCBpLCAgJ3N0cm9rZScpXG5cblxuICAgICAgdmFyIGJhciA9IHNhZmVTZWxlY3QodCwgJ3JlY3QnLCAnYmFyLXJlY3QnKVxuXG4gICAgICBpZiAoYmFyLmF0dHIoJ3RyYW5zZm9ybScpID09IHVuZGVmaW5lZCkge1xuICAgICAgICBiYXIuYXR0cigndHJhbnNmb3JtJywgZnVuY3Rpb24oZCwgaSkge1xuICAgICAgICAgIHZhclxuICAgICAgICAgIHggPSBob3Jpem9udGFsUVxuICAgICAgICAgICAgPyAwXG4gICAgICAgICAgICA6IDBcbiAgICAgICAgICAsXG4gICAgICAgICAgeSA9IHZlcnRpY2FsUVxuICAgICAgICAgICAgPyAwXG4gICAgICAgICAgICA6IHNjYWxlKGV4dGVudFsxXSlcbiAgICAgICAgICAsXG4gICAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICAgIHJldHVybiB0XG4gICAgICAgIH0pXG4gICAgICAgIC5hdHRyKCd3aWR0aCcsIGhvcml6b250YWxRID8gb2JqZWN0U2l6ZSA6IDApXG4gICAgICAgIC5hdHRyKCdoZWlnaHQnLCB2ZXJ0aWNhbFEgPyBvYmplY3RTaXplIDogMClcblxuICAgICAgfVxuXG5cbiAgICAgIGJhci50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpIHtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUVxuICAgICAgICAgID8gb2JqZWN0U2l6ZSAtIG9iamVjdFNpemUgKiBiYXJQZXJjZW50XG4gICAgICAgICAgOiBvcmllbnQgPT0gJ3JpZ2h0J1xuICAgICAgICAgICAgPyBzY2FsZShleHRlbnRbMV0pIC0gc2NhbGUodmFsdWUpXG4gICAgICAgICAgICA6IG9iamVjdFNpemUgLSBvYmplY3RTaXplICogYmFyUGVyY2VudFxuICAgICAgICAgICxcbiAgICAgICAgeSA9IHZlcnRpY2FsUVxuICAgICAgICAgID8gb2JqZWN0U2l6ZSAtIG9iamVjdFNpemUgKiBiYXJQZXJjZW50XG4gICAgICAgICAgOiBzY2FsZShleHRlbnRbMV0pIC0gc2NhbGUodmFsdWUpXG4gICAgICAgICxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcbiAgICAgIC5hdHRyKCd3aWR0aCcsIGhvcml6b250YWxRID8gb2JqZWN0U2l6ZSAqIGJhclBlcmNlbnQgOiBzY2FsZSh2YWx1ZSkpXG4gICAgICAuYXR0cignaGVpZ2h0JywgdmVydGljYWxRID8gb2JqZWN0U2l6ZSAqIGJhclBlcmNlbnQ6IHNjYWxlKHZhbHVlKSlcbiAgICAgIC5hdHRyKCdmaWxsJywgZmlsbENvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZScsIHN0cm9rZUNvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIGJhclN0cm9rZVdpZHRoKVxuXG5cblxuICAgICAgdC5vbignbW91c2VvdmVyJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIGNvbnRhaW5lci5zZWxlY3RBbGwoJ2cuJytvYmplY3RDbGFzcykuc3R5bGUoJ29wYWNpdHknLCAwLjIpXG4gICAgICAgIHQuc3R5bGUoJ29wYWNpdHknLCAxKVxuICAgICAgICBiYXIuYXR0cignc3Ryb2tlLXdpZHRoJyxiYXJTdHJva2VXaWR0aCoyKVxuXG4gICAgICB9KVxuICAgICAgdC5vbignbW91c2VvdXQnLCBmdW5jdGlvbigpe1xuICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnLicrb2JqZWN0Q2xhc3MpLnN0eWxlKCdvcGFjaXR5JywgMSlcbiAgICAgICAgYmFyLmF0dHIoJ3N0cm9rZS13aWR0aCcsIGJhclN0cm9rZVdpZHRoKVxuICAgICAgfSlcbiAgICB9KVxuXG4gICAgdG9vbHRpcC5zZWxlY3Rpb24oY29udGFpbmVyLnNlbGVjdEFsbCgnLmJhci1yZWN0JykpXG4gICAgLmRhdGEoZGF0YSlcblxuICAgIHRvb2x0aXAoKVxuXG4gIH1cbiAgcmV0dXJuIGJhclxufVxuIiwiaW1wb3J0IHtoeXBlbmF0ZSwgc2FmZVNlbGVjdH0gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7c2V0dXBDb250YWluZXIsIGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3QsIGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXIsIGxvZ30gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQge3VuaXF1ZX0gZnJvbSAnLi9hcnJheS1mdW5jdGlvbnMnO1xuaW1wb3J0IHtncm91cGluZ1NwYWNlcn0gZnJvbSAnLi9ncm91cGluZy1zcGFjZXInO1xuaW1wb3J0IHtjb2xvckZ1bmN0aW9uIGFzIENGfSBmcm9tICcuL2NvbG9yLWZ1bmN0aW9uJztcbmltcG9ydCB7dG9vbHRpcCBhcyBUVGlwfSBmcm9tICcuL3Rvb2x0aXAnO1xuXG5cbi8qKlxuICogQ3JlYXRlcyBhIGJ1YmJsZUhlYXRtYXBcbiAqXG4gKiB7QGxpbmsgaHR0cHM6Ly9zdW1uZXVyb24uZ2l0bGFiLmlvL2Qzc20vZGVtb3MvYnViYmxlLWhlYXRtYXAvaW5kZXguaHRtbCBEZW1vfVxuICogQGNvbnN0cnVjdG9yIGJ1YmJsZUhlYXRtYXBcbiAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBzZWxlY3Rpb25cbiAqIEBuYW1lc3BhY2UgYnViYmxlSGVhdG1hcFxuICogQHJldHVybnMge2Z1bmN0aW9ufSBidWJibGVIZWF0bWFwXG4gKi9cbmZ1bmN0aW9uIGJ1YmJsZUhlYXRtYXAoIHNlbGVjdGlvbiApIHtcbiAgdmFyXG4gIC8qKlxuICAqIERhdGEgdG8gcGxvdC4gQXNzdW1lZCB0byBiZSBhIG9iamVjdCwgd2hlcmUgZWFjaCBrZXkgY29ycmVzcG9uZHMgdG8gYSBjZWxsXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNkYXRhfSlcbiAgKiBAcGFyYW0ge09iamVjdH0gW2RhdGE9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkYXRhLFxuXG4gIC8qKlxuICAqIEFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBidWJibGVIZWF0bWFwIGluXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNzcGFjZVh9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VYPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VYLFxuICAvKipcbiAgKiBBbW91bnQgb2YgdmVydGljYWwgc3BhY2UgKGluIHBpeGVscykgYXZhaWJsZSB0byByZW5kZXIgdGhlIGJ1YmJsZUhlYXRtYXAgaW5cbiAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwLnNwYWNlWX0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVk9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzcGFjZVksXG5cbiAgLyoqXG4gICogVGhlIGludGVybmFsIGtleSBvZiB0aGUgY2VsbCBzcGVjaWZpeWluZyB0byB3aGljaCB4IGF4aXMga2V5IGl0IGJlbG9uZ3NcbiAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwLnhLZXl9KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbeEtleT0neCddXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhLZXkgPSAneCcsXG4gIC8qKlxuICAqIFRoZSBpbnRlcm5hbCBrZXkgb2YgdGhlIGNlbGwgc3BlY2lmaXlpbmcgdG8gd2hpY2ggeSBheGlzIGtleSBpdCBiZWxvbmdzXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcC55S2V5fSlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3lLZXk9J3knXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5S2V5ID0gJ3knLFxuICAvKipcbiAgKiBUaGUgaW50ZXJuYWwga2V5IG9mIHRoZSBjZWxsIHNwZWNpZml5aW5nIHdoYXQgdmFsdWUgdG8gdXNlIHRvIGRldGVybWluZSB0aGUgcmFkaXVzXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcC5yS2V5fSlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3JLZXk9J3InXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICByS2V5ID0gJ3InLFxuICAvKipcbiAgKiBUaGUgaW50ZXJuYWwga2V5IG9mIHRoZSBjZWxsIHNwZWNpZml5aW5nIHdoYXQgdmFsdWUgdG8gdXNlIHRvIGRldGVybWluZSB0aGUgY29sb3JcbiAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwLnZLZXl9KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbdktleT0ndiddXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHZLZXkgPSAndicsXG5cbiAgLyoqXG4gICogRnVuY3Rpb24gZm9yIGV4dHJhY3RpbmcgdGhlIHRoZSB2YWx1ZSBmcm9tIHhLZXkuXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcC54RXh0cmFjdG9yfSlcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbeEV4dHJhY3Rvcj1mdW5jdGlvbihrZXksIGkpIHsgcmV0dXJuIGRhdGFba2V5XVt4S2V5XSB9XVxuICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGkpIHtyZXR1cm4gZGF0YVtrZXldW3hLZXldIH0sXG4gIC8qKlxuICAqIEZ1bmN0aW9uIGZvciBleHRyYWN0aW5nIHRoZSB0aGUgdmFsdWUgZnJvbSB5S2V5LlxuICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAueUV4dHJhY3Rvcn0pXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3lFeHRyYWN0b3I9ZnVuY3Rpb24oa2V5LCBpKSB7IHJldHVybiBkYXRhW2tleV1beUtleV0gfV1cbiAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5RXh0cmFjdG9yID0gZnVuY3Rpb24oa2V5LCBpKSB7IHJldHVybiBkYXRhW2tleV1beUtleV0gfSxcbiAgLyoqXG4gICogRnVuY3Rpb24gZm9yIGV4dHJhY3RpbmcgdGhlIHRoZSB2YWx1ZSBmcm9tIHJLZXkuXG4gICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcC5yRXh0cmFjdG9yfSlcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbckV4dHJhY3Rvcj1mdW5jdGlvbihrZXksIGkpIHsgcmV0dXJuIGRhdGFba2V5XVtyS2V5XSB9XVxuICAqIEByZXR1cm5zIHtudW1iZXJ9XG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHJFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGkpIHsgcmV0dXJuIGRhdGFba2V5XVtyS2V5XSB9LFxuICAvKipcbiAgKiBGdW5jdGlvbiBmb3IgZXh0cmFjdGluZyB0aGUgdGhlIHZhbHVlIGZyb20gdktleS5cbiAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwLnZFeHRyYWN0b3J9KVxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt2RXh0cmFjdG9yPWZ1bmN0aW9uKGtleSwgaSkgeyByZXR1cm4gZGF0YVtrZXldW3ZLZXldIH1dXG4gICogQHJldHVybnMge251bWJlcn1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdkV4dHJhY3RvciA9IGZ1bmN0aW9uKGtleSwgaSkgeyByZXR1cm4gZGF0YVtrZXldW3ZLZXldIH0sXG5cblxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0byBhbGxvdyBidWJibGVIZWF0bWFwIHRvIHJlbmRlciBlbGVtZW50cyBwYXNzIHRoZSBtYWluIHNwYXRpYWwgZGltZW5zaW9uXG4gICogZ2l2ZW4gdGhlIG9yaWVudGF0aW9uIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjb3JpZW50fSksIHdoZXJlIHtAbGluayBidWJibGVIZWF0bWFwI29yaWVudH09XCJib3R0b21cIiBvciB7QGxpbmsgYnViYmxlSGVhdG1hcCNvcmllbnR9PVwidG9wXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJ1YmJsZUhlYXRtYXAjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjb3JpZW50fT1cImxlZnRcIiBvciB7QGxpbmsgYnViYmxlSGVhdG1hcCNvcmllbnR9PVwicmlnaHRcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgYnViYmxlSGVhdG1hcCNzcGFjZVl9XG4gICogQHBhcmFtIHtib29sZWFufSBbb3ZlcmZsb3dRPWZhbHNlXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvdmVyZmxvd1EgPSBmYWxzZSxcblxuICAvKipcbiAgKiBUaGUgc2NhbGUgZm9yIHdoaWNoIHRoZSByYWRpdXMgdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZT1kMy5zY2FsZUxpbmVhcl1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc2NhbGUgPSBkMy5zY2FsZUxpbmVhcigpLFxuICAvKipcbiAgKiBUaGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgc2NhbGUgKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNzY2FsZX0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtkb21haW5QYWRkaW5nPTAuNV1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZG9tYWluUGFkZGluZyA9IDAuNSxcblxuICAvKipcbiAgKiBEZWZhdWx0IHNwYWNlIGZvciB0aGUgc3BhY2VyIChwZXJjZW50YWdlKSBvZiBtYWluIGRpbWVuc2lvbiBnaXZlbiB0aGUgb3JpZW50YXRpb25cbiAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI29yaWVudH0pLCB3aGVyZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNvcmllbnR9PVwiaG9yaXpvbnRhbFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayBidWJibGVIZWF0bWFwI3NwYWNlWH0gYW5kIHdoZXJlIHtAbGluayBidWJibGVIZWF0bWFwI29yaWVudH09XCJ2ZXJ0aWNhbFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayBidWJibGVIZWF0bWFwI3NwYWNlWX0gYmV0d2VlbiBidWJibGVzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvYmplY3RTcGFjZXI9MC4wXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvYmplY3RTcGFjZXIgPSAwLjAsXG4gIC8qKlxuICAqIFRoZSBtaW5pbXVtIHNpemUgdGhhdCBhbiBvYmplY3QgY2FuIGJlXG4gICogQHBhcmFtIHtudW1iZXJ9IFttaW5PYmplY3RTaXplPTUwXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBtaW5PYmplY3RTaXplID0gNTAsXG4gIC8qKlxuICAqIFRoZSBtYXhpbXVtIHNpemUgdGhhdCBhbiBvYmplY3QgY2FuIGJlXG4gICogQHBhcmFtIHtudW1iZXJ9IFttYXhPYmplY3RTaXplPTEwMF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWF4T2JqZWN0U2l6ZSA9IDEwMCxcblxuXG4gIC8qKlxuICAqIFRoZSBzdHJva2Ugd2lkdGggb2YgdGhlIGJ1YmJsZXNcbiAgKiBAcGFyYW0ge251bWJlcn0gW2J1YmJsZVN0cm9rZVdpZHRoPTJdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJ1YmJsZVN0cm9rZVdpZHRoID0gMixcbiAgLy8gY29sb3JGdW5jID0gY29sb3JGdW5jdGlvbigpLFxuXG4gIC8qKlxuICAqIENvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kXG4gICogQHBhcmFtIHtzdHJpbmd9IFtiYWNrZ3JvdW5kRmlsbD1cInRyYW5zcGFyZW50XCJdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJhY2tncm91bmRGaWxsID0gJ3RyYW5zcGFyZW50JyxcbiAgLyoqXG4gICogTmFtZXNwYWNlIGZvciBhbGwgaXRlbXMgbWFkZSBieSB0aGlzIGluc3RhbmNlIG9mIGJ1YmJsZUhlYXRtYXBcbiAgKiBAcGFyYW0ge3N0cmluZ30gW25hbWVzcGFjZT1cImQzc20tYnViYmxlXCJdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG5hbWVzcGFjZSA9ICdkM3NtLWJ1YmJsZScsXG4gIC8qKlxuICAqIENsYXNzIG5hbWUgZm9yIGJ1YmJsZSBjb250YWluZXIgKDxnPiBlbGVtZW50KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbb2JqZWN0Q2xhc3M9XCJidWJibGVcIl1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0Q2xhc3MgPSAnYnViYmxlJyxcbiAgLyoqXG4gICogRHVyYXRpb24gb2YgYWxsIHRyYW5zaXRpb25zIG9mIHRoaXMgZWxlbWVudFxuICAqIEBwYXJhbSB7bnVtYmVyfSBbdHJhbnNpdGlvbkR1cmF0aW9uPTEwMDBdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRyYW5zaXRpb25EdXJhdGlvbiA9IDEwMDAsXG4gIC8qKlxuICAqIEVhc2luZyBmdW5jdGlvbiBmb3IgdHJhbnNpdGlvbnNcbiAgKiBAcGFyYW0ge2QzLmVhc2V9IFtlYXNlRnVuYz1kMy5lYXNlRXhwXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBlYXNlRnVuYyA9IGQzLmVhc2VFeHAsXG5cbiAgLyoqXG4gICogU3RvcmVzIHRoZSBrZXlzIG9mIGFsbCB0aGUgY2VsbHNcbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGJ1YmJsZUhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFtjZWxsS2V5cz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGNlbGxLZXlzLFxuICAvKipcbiAgKiBTdG9yZXMgdGhlIGxpc3Qgb2YgdW5pcXVlIHhWYWx1ZXNcbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGJ1YmJsZUhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt4VmFsdWVzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeFZhbHVlcyxcbiAgLyoqXG4gICogU3RvcmVzIHRoZSBsaXN0IG9mIHVuaXF1ZSB5VmFsdWVzXG4gICogQ2FsY3VsYXRlZCBhZnRlciBidWJibGVIZWF0bWFwIGNhbGxlZC5cbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbeVZhbHVlcz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHlWYWx1ZXMsXG4gIC8qKlxuICAqIFN0b3JlcyB0aGUgbGlzdCBvZiB1bmlxdWUgclZhbHVlc1xuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgYnViYmxlSGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW3JWYWx1ZXM9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICByVmFsdWVzLFxuICAvKipcbiAgKiBTdG9yZXMgdGhlIGxpc3Qgb2YgdW5pcXVlIHZWYWx1ZXNcbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGJ1YmJsZUhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt2VmFsdWVzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdlZhbHVlcyxcblxuICB4S2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4geEV4dHJhY3RvcihhKSAtIHhFeHRyYWN0b3IoYikgfSxcbiAgeUtleVNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKGEsIGIpIHsgcmV0dXJuIHlFeHRyYWN0b3IoYSkgLSB5RXh0cmFjdG9yKGIpIH0sXG4gIHJLZXlTb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihhLCBiKSB7IHJldHVybiByRXh0cmFjdG9yKGEpIC0gckV4dHJhY3RvcihiKSB9LFxuICB2S2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4gdkV4dHJhY3RvcihhKSAtIHZFeHRyYWN0b3IoYikgfSxcblxuICAvKipcbiAgKiBJbnN0YW5jZSBvZiBDb2xvckZ1bmN0aW9uIHdpdGggLmNvbG9yQnkgc2V0IHRvICdjYXRlZ29yeSdcbiAgKiBAZnVuY3Rpb24gY29sb3JGdW5jdGlvblxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjb2xvckZ1bmN0aW9uID0gQ0YoKS5jb2xvckJ5KCd2YWx1ZScpLFxuICAvKipcbiAgKiBJbnN0YW5jZSBvZiBUb29sdGlwXG4gICogQGZ1bmN0aW9uIHRvb2x0aXBcbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdG9vbHRpcCA9IFRUaXAoKSxcblxuICAvKipcbiAgKiBzdG9yZSB0aGUgc2l6ZSB0aGUgYnViYmxlIGNvdWxkIGJlIGluIHRoZSB4IGRpbWVuc2lvblxuICAqIHRoZSBhY3R1YWxsIHNpemUgb2YgdGhlIGJ1YmJsZSB3aWxsIGJlIHRoZSBtaW4gb2YgeFNpemUgYW5kIHtAbGluayBidWJibGVIZWF0bWFwI3lTaXplfVxuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgYnViYmxlSGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW3hTaXplPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeFNpemUsXG4gIC8qKlxuICAqIHN0b3JlIHRoZSBzaXplIG9mIHRoZSBzcGFjZXIgaW4gdGhlIHggZGltZW5zaW9uXG4gICogQ2FsY3VsYXRlZCBhZnRlciBidWJibGVIZWF0bWFwIGNhbGxlZC5cbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbeFNwYWNlclNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB4U3BhY2VyU2l6ZSxcblxuICAvKipcbiAgKiBzdG9yZSB0aGUgc2l6ZSB0aGUgYnViYmxlIGNvdWxkIGJlIGluIHRoZSB5IGRpbWVuc2lvblxuICAqIHRoZSBhY3R1YWxsIHNpemUgb2YgdGhlIGJ1YmJsZSB3aWxsIGJlIHRoZSBtaW4gb2YgeFNpemUgYW5kIHtAbGluayBidWJibGVIZWF0bWFwI3hTaXplfVxuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgYnViYmxlSGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW3lTaXplPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeVNpemUsXG4gIC8qKlxuICAqIHN0b3JlIHRoZSBzaXplIG9mIHRoZSBzcGFjZXIgaW4gdGhlIHkgZGltZW5zaW9uLlxuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgYnViYmxlSGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW3hTcGFjZXJTaXplPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeVNwYWNlclNpemVcblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBzZWxlY3Rpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IGQzLnNlbGVjdGlvbn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2VsZWN0aW9uID0gc2VsZWN0aW9uXG4gICAqL1xuICBiaG0uc2VsZWN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCBiaG0pIDogc2VsZWN0aW9uOyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGRhdGFcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNkYXRhfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgb2JqZWN0fVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGJobS5kYXRhID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkYXRhID0gXywgYmhtKSA6IGRhdGE7IH1cbiAgLy8gYmhtLm9yaWVudCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3JpZW50ID0gXywgYmhtKSA6IG9yaWVudDsgfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBhbW91bnQgb2YgaG9yaXpvbnRhbCBzcGFjZSBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNzcGFjZVh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV0gc2hvdWxkIGJlIGEgbnVtYmVyID4gMFxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VYID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0uc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVggPSBfLCBiaG0pIDogc3BhY2VYOyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiB2ZXJ0aWNhbCBzcGFjZSBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNzcGFjZVl9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV0gc2hvdWxkIGJlIGEgbnVtYmVyID4gMFxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VZID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0uc3BhY2VZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVkgPSBfLCBiaG0pIDogc3BhY2VZOyB9XG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeEtleVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3hLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhLZXkgPSAneCdcbiAgICovXG4gIGJobS54S2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4S2V5ID0gXywgYmhtKSA6IHhLZXk7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeUtleVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3lLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHlLZXkgPSAneSdcbiAgICovXG4gIGJobS55S2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5S2V5ID0gXywgYmhtKSA6IHlLZXk7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgcktleVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3JLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHJLZXkgPSAncidcbiAgICovXG4gIGJobS5yS2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChyS2V5ID0gXywgYmhtKSA6IHJLZXk7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgdktleVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3ZLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZLZXkgPSAneSdcbiAgICovXG4gIGJobS52S2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2S2V5ID0gXywgYmhtKSA6IHZLZXk7IH1cblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBjZWxsS2V5c1xuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI2NlbGxLZXlzfSlcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmdbXX1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgY2VsbEtleXMgPSB1bmRlZmluZWRcbiAgICovXG4gIGJobS5jZWxsS2V5cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY2VsbEtleXMgPSBfLCBiaG0pIDogY2VsbEtleXM7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeFZhbHVlc1xuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3hWYWx1ZXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB4VmFsdWVzID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0ueFZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeFZhbHVlcyA9IF8sIGJobSkgOiB4VmFsdWVzOyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHlWYWx1ZXNcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCN5VmFsdWVzfSlcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmdbXX1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgeVZhbHVlcyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmhtLnlWYWx1ZXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlWYWx1ZXMgPSBfLCBiaG0pIDogeVZhbHVlczsgfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSByVmFsdWVzXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjclZhbHVlc30pXG4gICAqIEBwYXJhbSB7bnVtYmVyW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyW119XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHJWYWx1ZXMgPSB1bmRlZmluZWRcbiAgICovXG4gIGJobS5yVmFsdWVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChyVmFsdWVzID0gXywgYmhtKSA6IHJWYWx1ZXM7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgdlZhbHVlc1xuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3ZWYWx1ZXN9KVxuICAgKiBAcGFyYW0ge251bWJlcltdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IG51bWJlcltdfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2VmFsdWVzID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0udlZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodlZhbHVlcyA9IF8sIGJobSkgOiB2VmFsdWVzOyB9XG5cblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB4RXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjeEV4dHJhY3Rvcn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhFeHRyYWN0b3IgPSB1bmRlZmluZWRcbiAgICovXG4gIGJobS54RXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4RXh0cmFjdG9yID0gXywgYmhtKSA6IHhFeHRyYWN0b3I7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeUV4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3lFeHRyYWN0b3J9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5RXh0cmFjdG9yID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0ueUV4dHJhY3RvciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeUV4dHJhY3RvciA9IF8sIGJobSkgOiB5RXh0cmFjdG9yOyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHJFeHRyYWN0b3JcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNyRXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgckV4dHJhY3RvciA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmhtLnJFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHJFeHRyYWN0b3IgPSBfLCBiaG0pIDogckV4dHJhY3RvcjsgfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB2RXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjdkV4dHJhY3Rvcn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZFeHRyYWN0b3IgPSB1bmRlZmluZWRcbiAgICovXG4gIGJobS52RXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2RXh0cmFjdG9yID0gXywgYmhtKSA6IHZFeHRyYWN0b3I7IH1cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgd2hldGhlciBvciBub3QgYnViYmxlSGVhdG1hcCBpcyBhbGxvd2VkIHRvIGdvIGJleW9uZCBzcGVjaWZpZWQgZGltZW5zaW9uc1xuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3NwYWNlWH0pXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBib29sZWFufVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvdmVyZmxvd1EgPSBmYWxzZVxuICAgKi9cbiAgYmhtLm92ZXJmbG93USA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3ZlcmZsb3dRID0gXywgYmhtKSA6IG92ZXJmbG93UTsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHNjYWxlIGZvciB3aGljaCB0aGUgcmFkaXVzIG9mIGJ1YmJsZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjc2NhbGV9KVxuICAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IGQzLnNjYWxlfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcbiAgICovXG4gIGJobS5zY2FsZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGUgPSBfLCBiaG0pIDogc2NhbGU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgc2NhbGVcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNkb21haW5QYWRkaW5nfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBkb21haW5QYWRkaW5nID0gMC41XG4gICAqL1xuICBiaG0uZG9tYWluUGFkZGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZG9tYWluUGFkZGluZyA9IF8sIGJobSkgOiBkb21haW5QYWRkaW5nOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgb2JqZWN0U3BhY2VyXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjb2JqZWN0U3BhY2VyfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RTcGFjZXIgPSAwLjBcbiAgICovXG4gIGJobS5vYmplY3RTcGFjZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNwYWNlciA9IF8sIG9iamVjdFNwYWNlcikgOiBkYXRhOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbWluT2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI21pbk9iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1pbk9iamVjdFNpemUgPSA1MFxuICAgKi9cbiAgYmhtLm1pbk9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1pbk9iamVjdFNpemUgPSBfLCBiaG0pIDogbWluT2JqZWN0U2l6ZTsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1heE9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNtYXhPYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtYXhPYmplY3RTaXplID0gMTAwXG4gICAqL1xuICBiaG0ubWF4T2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWF4T2JqZWN0U2l6ZSA9IF8sIGJobSkgOiBtYXhPYmplY3RTaXplOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYnViYmxlU3Ryb2tlV2lkdGhcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNidWJibGVTdHJva2VXaWR0aH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYnViYmxlU3Ryb2tlV2lkdGggPSAyXG4gICAqL1xuICBiaG0uYnViYmxlU3Ryb2tlV2lkdGggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJ1YmJsZVN0cm9rZVdpZHRoID0gXywgYmhtKSA6IGJ1YmJsZVN0cm9rZVdpZHRoOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFja2dyb3VuZEZpbGxcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNiYWNrZ3JvdW5kRmlsbH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnXG4gICAqL1xuICBiaG0uYmFja2dyb3VuZEZpbGwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhY2tncm91bmRGaWxsID0gXywgYmhtKSA6IGJhY2tncm91bmRGaWxsOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbmFtZXNwYWNlXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjbmFtZXNwYWNlfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBuYW1lc3BhY2UgPSAnZDNzbS1idWJibGVIZWF0bWFwJ1xuICAgKi9cbiAgYmhtLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgYmhtKSA6IG5hbWVzcGFjZTsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG9iamVjdENsYXNzXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjb2JqZWN0Q2xhc3N9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdENsYXNzID0gJ3RpY2stZ3JvdXAnXG4gICAqL1xuICBiaG0ub2JqZWN0Q2xhc3MgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdENsYXNzID0gXywgYmhtKSA6IG9iamVjdENsYXNzOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdHJhbnNpdGlvbkR1cmF0aW9uXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjdHJhbnNpdGlvbkR1cmF0aW9ufSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwXG4gICAqL1xuICBiaG0udHJhbnNpdGlvbkR1cmF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0cmFuc2l0aW9uRHVyYXRpb24gPSBfLCBiaG0pIDogdHJhbnNpdGlvbkR1cmF0aW9uOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZWFzZUZ1bmNcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNlYXNlRnVuY30pXG4gICAqIEBwYXJhbSB7ZDMuZWFzZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBkMy5lYXNlfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBlYXNlRnVuYyA9IGQzLmVhc2VFeHBcbiAgICovXG4gIGJobS5lYXNlRnVuYyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZWFzZUZ1bmMgPSBfLCBiaG0pIDogZWFzZUZ1bmM7IH1cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRvb2x0aXBcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCN0b29sdGlwfSlcbiAgICogQHBhcmFtIHt0b29sdGlwfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IHRvb2x0aXB9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRvb2x0aXAgPSB0b29sdGlwKClcbiAgICovXG4gIGJobS50b29sdGlwID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0b29sdGlwID0gXywgYmhtKSA6IHRvb2x0aXA7IH1cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGNvbG9yRnVuY3Rpb25cbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCNjb2xvckZ1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtjb2xvckZ1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IGNvbG9yRnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGNvbG9yRnVuY3Rpb24gPSBjb2xvckZ1bmN0aW9uKClcbiAgICovXG4gIGJobS5jb2xvckZ1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjb2xvckZ1bmN0aW9uID0gXywgYmhtKSA6IGNvbG9yRnVuY3Rpb247IH1cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHhTaXplXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjeFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhTaXplID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0ueFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhTaXplID0gXywgYmhtKSA6IHhTaXplOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgeFNwYWNlclNpemVcbiAgICogKHNlZSB7QGxpbmsgYnViYmxlSGVhdG1hcCN4U3BhY2VyU2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YnViYmxlSGVhdG1hcCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJ1YmJsZUhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgeFNwYWNlclNpemUgPSB1bmRlZmluZWRcbiAgICovXG4gIGJobS54U3BhY2VyU2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeFNwYWNlclNpemUgPSBfLCBiaG0pIDogeFNwYWNlclNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB5U2l6ZVxuICAgKiAoc2VlIHtAbGluayBidWJibGVIZWF0bWFwI3lTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtidWJibGVIZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYnViYmxlSGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5U2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYmhtLnlTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5U2l6ZSA9IF8sIGJobSkgOiB5U2l6ZTsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHlTcGFjZXJTaXplXG4gICAqIChzZWUge0BsaW5rIGJ1YmJsZUhlYXRtYXAjeVNwYWNlclNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2J1YmJsZUhlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBidWJibGVIZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHlTcGFjZXJTaXplID0gdW5kZWZpbmVkXG4gICAqL1xuICBiaG0ueVNwYWNlclNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlTcGFjZXJTaXplID0gXywgYmhtKSA6IHlTcGFjZXJTaXplOyB9XG4gIC8vIGJobS55S2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5S2V5U29ydGluZ0Z1bmN0aW9uID0gXywgYmhtKSA6IHlLZXlTb3J0aW5nRnVuY3Rpb247IH1cbiAgLy8gYmhtLnhLZXlTb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhLZXlTb3J0aW5nRnVuY3Rpb24gPSBfLCBiaG0pIDogeEtleVNvcnRpbmdGdW5jdGlvbjsgfVxuXG5cblxuICBmdW5jdGlvbiBiaG0oKSB7XG4gICAgdmFyIGhvcml6b250YWxRID0gdHJ1ZTsgLy8gbm8gb3JpZW50YXRpb24gaW4gaGVhdG1hcHMuIEFpZHMgYXMgcGxhY2Vob2xkZXIgaW4gZnVuY3Rpb25zIHRoYXQgdGFrZSBvcmllbnQgYXMgYSBwYXJhbWV0ZXJcbiAgICB2YXIgYmdjcFJlY3QgPSB7eDowLCB5OjAsIHdpZHRoOiBzcGFjZVgsIGhlaWdodDpzcGFjZVl9XG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cbiAgICBjZWxsS2V5cyA9IGQzLmtleXMoZGF0YSk7XG4gICAgY2VsbEtleXMuc29ydChmdW5jdGlvbihhLCBiKXsgcmV0dXJuIHhLZXlTb3J0aW5nRnVuY3Rpb24oYSwgYikgfHwgeUtleVNvcnRpbmdGdW5jdGlvbihhLCBiKSB9KVxuICAgIGxvZygnYnViYmxlSGVhdG1hcCcsICdjZWxscyBhcmUgc29ydGVkIGJ5JywgY2VsbEtleXMpXG5cblxuXG4gICAgeFZhbHVlcyA9IHVuaXF1ZShjZWxsS2V5cy5tYXAoeEV4dHJhY3RvcikpO1xuICAgIHlWYWx1ZXMgPSB1bmlxdWUoY2VsbEtleXMubWFwKHlFeHRyYWN0b3IpKTtcbiAgICByVmFsdWVzID0gdW5pcXVlKGNlbGxLZXlzLm1hcChyRXh0cmFjdG9yKSk7XG4gICAgdlZhbHVlcyA9IHVuaXF1ZShjZWxsS2V5cy5tYXAodkV4dHJhY3RvcikpO1xuICAgIGxvZygnYnViYmxlSGVhdG1hcCcsICd4IGFuZCB5IGtleXMgYXJlJywge3g6IHhWYWx1ZXMsIHk6eVZhbHVlc30pXG5cblxuICAgIHZhciB4RGltID0geFZhbHVlcy5sZW5ndGgsIHlEaW0gPSB5VmFsdWVzLmxlbmd0aDtcblxuXG4gICAgdmFyIGV4dGVudCA9IFtNYXRoLm1pbiguLi5yVmFsdWVzKSAtIGRvbWFpblBhZGRpbmcsTWF0aC5tYXgoLi4uclZhbHVlcykgKyBkb21haW5QYWRkaW5nXTtcblxuXG4gICAgeVNpemUgPSBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0KHNwYWNlWSwgeURpbSwgbWluT2JqZWN0U2l6ZSwgbWF4T2JqZWN0U2l6ZSwgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgeFNpemUgPSBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0KHNwYWNlWCwgeERpbSwgbWluT2JqZWN0U2l6ZSwgbWF4T2JqZWN0U2l6ZSwgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgeVNwYWNlclNpemUgPSBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyKHlWYWx1ZXMsIHNwYWNlWSwgeVNpemUsIHlEaW0sIG9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIHhTcGFjZXJTaXplID0gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcih4VmFsdWVzLCBzcGFjZVgsIHhTaXplLCB4RGltLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICBsb2coJ2J1YmJsZUhlYXRtYXAnLCAnc2l6ZSBvZicsIHt4OiB4U2l6ZSwgeTogeVNpemV9KVxuXG5cbiAgICBzY2FsZS5kb21haW4oZXh0ZW50KS5yYW5nZShbTWF0aC5taW4obWluT2JqZWN0U2l6ZS8yLCAgIE1hdGgubWluKHlTaXplLCB4U2l6ZSkvMiksIE1hdGgubWluKHlTaXplLCB4U2l6ZSkvMl0pXG5cbiAgICB2YXIgeVNwYWNlciA9IGdyb3VwaW5nU3BhY2VyKClcbiAgICAuaG9yaXpvbnRhbFEoZmFsc2UpXG4gICAgLm1vdmVieSgnY2F0ZWdvcnknKS5udW1iZXJPZk9iamVjdHMoeURpbSlcbiAgICAub2JqZWN0Q2xhc3MoaHlwZW5hdGUob2JqZWN0Q2xhc3MsICdyb3cnKSlcbiAgICAub2JqZWN0U2l6ZSh5U2l6ZSkuc3BhY2VyU2l6ZSh5U3BhY2VyU2l6ZSlcbiAgICAudHJhbnNpdGlvbkR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbikuZWFzZUZ1bmMoZWFzZUZ1bmMpXG4gICAgLm5hbWVzcGFjZSgncm93JylcblxuICAgIHZhciB4U3BhY2VyID0gZ3JvdXBpbmdTcGFjZXIoKVxuICAgIC5ob3Jpem9udGFsUSh0cnVlKVxuICAgIC5tb3ZlYnkoJ2NhdGVnb3J5JykubnVtYmVyT2ZPYmplY3RzKHhEaW0pXG4gICAgLm9iamVjdENsYXNzKG9iamVjdENsYXNzKVxuICAgIC5vYmplY3RTaXplKHhTaXplKS5zcGFjZXJTaXplKHhTcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcblxuXG4gICAgeVNwYWNlcihjb250YWluZXIsIHlWYWx1ZXMsIDApXG4gICAgY29udGFpbmVyLnNlbGVjdEFsbCgnZy4nK2h5cGVuYXRlKG9iamVjdENsYXNzLCAncm93JykpXG4gICAgLmVhY2goZnVuY3Rpb24oZCwgaSl7XG4gICAgICB4U3BhY2VyKGQzLnNlbGVjdCh0aGlzKSwgeFZhbHVlcywgMClcbiAgICB9KVxuICAgIHZhciBjZWxscyA9IGNvbnRhaW5lci5zZWxlY3RBbGwoJ2c6bm90KC50by1yZW1vdmUpLicrb2JqZWN0Q2xhc3MpLmRhdGEoY2VsbEtleXMpO1xuXG4gICAgdmFyIHBhcmVudEluZGV4QXJyYXkgPSBbXVxuICAgIGNlbGxzLmVhY2goZnVuY3Rpb24oZCwgaSl7IHBhcmVudEluZGV4QXJyYXkucHVzaChOdW1iZXIoZDMuc2VsZWN0KHRoaXMpLmF0dHIoJ3BhcmVudC1pbmRleCcpKSkgfSlcblxuXG4gICAgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24uY29sb3JCeSgpID09ICdpbmRleCdcbiAgICA/IGNvbG9yRnVuY3Rpb24uZGF0YUV4dGVudChbMCwgTWF0aC5tYXgoLi4ucGFyZW50SW5kZXhBcnJheSldKVxuICAgIDogY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KFswLCBNYXRoLm1heCguLi52VmFsdWVzKV0pXG5cbiAgICBjZWxscy5lYWNoKGZ1bmN0aW9uKGtleSwgaSkge1xuICAgICAgbG9nKCdidWJibGVIZWF0bWFwJywgJ2VhY2ggY2VsbCcsIHtrZXk6IGtleSwgaW5kZXg6IGksIG5vZGU6IGQzLnNlbGVjdCh0aGlzKS5ub2RlKCl9KVxuXG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKSxcbiAgICAgIGN1cnJlbnREYXRhID0gZGF0YVtrZXldLFxuICAgICAgdmFsdWUgPSB2RXh0cmFjdG9yKGtleSwgaSksXG4gICAgICByYWRpdXM9IHJFeHRyYWN0b3Ioa2V5LCBpKSxcbiAgICAgIGkgPSB0LmF0dHIoJ3BhcmVudC1pbmRleCcpID09IHVuZGVmaW5lZCA/IGkgOiB0LmF0dHIoJ3BhcmVudC1pbmRleCcpLFxuICAgICAgZmlsbENvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIHZhbHVlLCBpLCAnZmlsbCcpLCAvLyBwcmV2ZW50IGR1cGxpY2F0ZSBjb21wdXRhdGlvblxuICAgICAgc3Ryb2tlQ29sb3IgPSBjb2xvckZ1bmN0aW9uKGtleSwgdmFsdWUsIGksICAnc3Ryb2tlJylcblxuICAgICAgbG9nKCdidWJibGVIZWF0bWFwJywgJ3JhZGl1cycse3JhZGl1czogcmFkaXVzLCBzY2FsZWQ6IHNjYWxlKHJhZGl1cyksIGV4dGVudDogZXh0ZW50LCByYW5nZTpzY2FsZS5yYW5nZSgpfSlcblxuICAgICAgdmFyIGMgPSBzYWZlU2VsZWN0KHQsICdjaXJjbGUnLCBoeXBlbmF0ZShvYmplY3RDbGFzcywnY2lyY2xlJykpXG4gICAgICBjLmF0dHIoJ2N4JywgeFNpemUgLyAyKVxuICAgICAgLmF0dHIoJ2N5JywgeVNpemUgLyAyIClcbiAgICAgIC5hdHRyKCdyJywgc2NhbGUocmFkaXVzKSlcbiAgICAgIC5hdHRyKCdmaWxsJywgZmlsbENvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZScsIHN0cm9rZUNvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIGJ1YmJsZVN0cm9rZVdpZHRoKVxuXG4gICAgfSlcblxuICAgIHRvb2x0aXAuc2VsZWN0aW9uKGNlbGxzLnNlbGVjdEFsbCgnY2lyY2xlLicraHlwZW5hdGUob2JqZWN0Q2xhc3MsICdjaXJjbGUnKSkpXG4gICAgLmRhdGEoZGF0YSlcbiAgICAvLyAua2V5cyhbJ3InLCAndiddKVxuICAgIC8vIC5oZWFkZXIoZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGh5cGVuYXRlKGRhdGFbZF1beEtleV0sIGRhdGFbZF1beUtleV0pIH0pXG5cbiAgICB0b29sdGlwKClcblxuXG4gIH1cblxuICByZXR1cm4gYmhtO1xufVxuXG5leHBvcnQge2J1YmJsZUhlYXRtYXB9XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHtzZXR1cENvbnRhaW5lciwgY2FsY3VsYXRlV2lkdGhPZk9iamVjdCwgY2FsY3VsYXRlV2lkdGhPZlNwYWNlciwgbG9nfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7dW5pcXVlfSBmcm9tICcuL2FycmF5LWZ1bmN0aW9ucyc7XG5pbXBvcnQge2dyb3VwaW5nU3BhY2VyfSBmcm9tICcuL2dyb3VwaW5nLXNwYWNlcic7XG5pbXBvcnQge2NvbG9yRnVuY3Rpb24gYXMgQ0Z9IGZyb20gJy4vY29sb3ItZnVuY3Rpb24nO1xuaW1wb3J0IHt0b29sdGlwIGFzIFRUaXB9IGZyb20gJy4vdG9vbHRpcCc7XG5cblxuLyoqXG4gKiBDcmVhdGVzIGEgaGVhdG1hcFxuICpcbiAqIHtAbGluayBodHRwczovL3N1bW5ldXJvbi5naXRsYWIuaW8vZDNzbS9kZW1vcy9oZWF0bWFwLWhlYXRtYXAvaW5kZXguaHRtbCBEZW1vfVxuICogQGNvbnN0cnVjdG9yIGhlYXRtYXBcbiAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBzZWxlY3Rpb25cbiAqIEBuYW1lc3BhY2UgaGVhdG1hcFxuICogQHJldHVybnMge2Z1bmN0aW9ufSBoZWF0bWFwXG4gKi9cbmZ1bmN0aW9uIGhlYXRtYXAoIHNlbGVjdGlvbiApIHtcbiAgdmFyXG4gIC8qKlxuICAqIERhdGEgdG8gcGxvdC4gQXNzdW1lZCB0byBiZSBhIG9iamVjdCwgd2hlcmUgZWFjaCBrZXkgY29ycmVzcG9uZHMgdG8gYSBjZWxsXG4gICogKHNlZSB7QGxpbmsgaGVhdG1hcCNkYXRhfSlcbiAgKiBAcGFyYW0ge09iamVjdH0gW2RhdGE9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkYXRhLFxuXG4gIC8qKlxuICAqIEFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBoZWF0bWFwIGluXG4gICogKHNlZSB7QGxpbmsgaGVhdG1hcCNzcGFjZVh9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VYPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VYLFxuICAvKipcbiAgKiBBbW91bnQgb2YgdmVydGljYWwgc3BhY2UgKGluIHBpeGVscykgYXZhaWJsZSB0byByZW5kZXIgdGhlIGhlYXRtYXAgaW5cbiAgKiAoc2VlIHtAbGluayBoZWF0bWFwLnNwYWNlWX0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVk9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzcGFjZVksXG5cbiAgLyoqXG4gICogVGhlIGludGVybmFsIGtleSBvZiB0aGUgY2VsbCBzcGVjaWZpeWluZyB0byB3aGljaCB4IGF4aXMga2V5IGl0IGJlbG9uZ3NcbiAgKiAoc2VlIHtAbGluayBoZWF0bWFwLnhLZXl9KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbeEtleT0neCddXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhLZXkgPSAneCcsXG4gIC8qKlxuICAqIFRoZSBpbnRlcm5hbCBrZXkgb2YgdGhlIGNlbGwgc3BlY2lmaXlpbmcgdG8gd2hpY2ggeSBheGlzIGtleSBpdCBiZWxvbmdzXG4gICogKHNlZSB7QGxpbmsgaGVhdG1hcC55S2V5fSlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW3lLZXk9J3knXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5S2V5ID0gJ3knLFxuXG4gIC8qKlxuICAqIFRoZSBpbnRlcm5hbCBrZXkgb2YgdGhlIGNlbGwgc3BlY2lmaXlpbmcgd2hhdCB2YWx1ZSB0byB1c2UgdG8gZGV0ZXJtaW5lIHRoZSBjb2xvclxuICAqIChzZWUge0BsaW5rIGhlYXRtYXAudktleX0pXG4gICogQHBhcmFtIHtzdHJpbmd9IFt2S2V5PSd2J11cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdktleSA9ICd2JyxcblxuICAvKipcbiAgKiBGdW5jdGlvbiBmb3IgZXh0cmFjdGluZyB0aGUgdGhlIHZhbHVlIGZyb20geEtleS5cbiAgKiAoc2VlIHtAbGluayBoZWF0bWFwLnhFeHRyYWN0b3J9KVxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt4RXh0cmFjdG9yPWZ1bmN0aW9uKGtleSwgaSkgeyByZXR1cm4gZGF0YVtrZXldW3hLZXldIH1dXG4gICogQHJldHVybnMge3N0cmluZ31cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeEV4dHJhY3RvciA9IGZ1bmN0aW9uKGtleSwgaSkge3JldHVybiBkYXRhW2tleV1beEtleV0gfSxcbiAgLyoqXG4gICogRnVuY3Rpb24gZm9yIGV4dHJhY3RpbmcgdGhlIHRoZSB2YWx1ZSBmcm9tIHlLZXkuXG4gICogKHNlZSB7QGxpbmsgaGVhdG1hcC55RXh0cmFjdG9yfSlcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbeUV4dHJhY3Rvcj1mdW5jdGlvbihrZXksIGkpIHsgcmV0dXJuIGRhdGFba2V5XVt5S2V5XSB9XVxuICAqIEByZXR1cm5zIHtzdHJpbmd9XG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHlFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGkpIHsgcmV0dXJuIGRhdGFba2V5XVt5S2V5XSB9LFxuXG4gIC8qKlxuICAqIEZ1bmN0aW9uIGZvciBleHRyYWN0aW5nIHRoZSB0aGUgdmFsdWUgZnJvbSB2S2V5LlxuICAqIChzZWUge0BsaW5rIGhlYXRtYXAudkV4dHJhY3Rvcn0pXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3ZFeHRyYWN0b3I9ZnVuY3Rpb24oa2V5LCBpKSB7IHJldHVybiBkYXRhW2tleV1bdktleV0gfV1cbiAgKiBAcmV0dXJucyB7bnVtYmVyfVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2RXh0cmFjdG9yID0gZnVuY3Rpb24oa2V5LCBpKSB7IHJldHVybiBkYXRhW2tleV1bdktleV0gfSxcblxuXG4gIC8qKlxuICAqIFdoZXRoZXIgb3Igbm90IHRvIGFsbG93IGhlYXRtYXAgdG8gcmVuZGVyIGVsZW1lbnRzIHBhc3MgdGhlIG1haW4gc3BhdGlhbCBkaW1lbnNpb25cbiAgKiBnaXZlbiB0aGUgb3JpZW50YXRpb24gKHNlZSB7QGxpbmsgaGVhdG1hcCNvcmllbnR9KSwgd2hlcmUge0BsaW5rIGhlYXRtYXAjb3JpZW50fT1cImJvdHRvbVwiIG9yIHtAbGluayBoZWF0bWFwI29yaWVudH09XCJ0b3BcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgaGVhdG1hcCNzcGFjZVh9IGFuZCB3aGVyZSB7QGxpbmsgaGVhdG1hcCNvcmllbnR9PVwibGVmdFwiIG9yIHtAbGluayBoZWF0bWFwI29yaWVudH09XCJyaWdodFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayBoZWF0bWFwI3NwYWNlWX1cbiAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvdmVyZmxvd1E9ZmFsc2VdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG92ZXJmbG93USA9IGZhbHNlLFxuXG4gIC8qKlxuICAqIERlZmF1bHQgc3BhY2UgZm9yIHRoZSBzcGFjZXIgKHBlcmNlbnRhZ2UpIG9mIG1haW4gZGltZW5zaW9uIGdpdmVuIHRoZSBvcmllbnRhdGlvblxuICAqIChzZWUge0BsaW5rIGhlYXRtYXAjb3JpZW50fSksIHdoZXJlIHtAbGluayBoZWF0bWFwI29yaWVudH09XCJob3Jpem9udGFsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGhlYXRtYXAjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGhlYXRtYXAjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGhlYXRtYXAjc3BhY2VZfSBiZXR3ZWVuIGJ1YmJsZXNcbiAgKiBAcGFyYW0ge251bWJlcn0gW29iamVjdFNwYWNlcj0wLjBdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdFNwYWNlciA9IDAuMCxcbiAgLyoqXG4gICogVGhlIG1pbmltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmUgaW4gdGhlIHkgZGltZW5zaW9uXG4gICogQHBhcmFtIHtudW1iZXJ9IFttaW5PYmplY3RTaXplPTUwXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5TWluT2JqZWN0U2l6ZSA9IDUwLFxuICAvKipcbiAgKiBUaGUgbWluaW11bSBzaXplIHRoYXQgYW4gb2JqZWN0IGNhbiBiZSBpbiB0aGUgeCBkaW1lbnNpb25cbiAgKiBAcGFyYW0ge251bWJlcn0gW21pbk9iamVjdFNpemU9NTBdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhNaW5PYmplY3RTaXplID0gNTAsXG4gIC8qKlxuICAqIFRoZSBtYXhpbXVtIHNpemUgdGhhdCBhbiBvYmplY3QgY2FuIGJlIGluIHRoZSB4IGRpbWVuc2lvblxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbWF4T2JqZWN0U2l6ZT0xMDBdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhNYXhPYmplY3RTaXplID0gMTAwLFxuICAvKipcbiAgKiBUaGUgbWF4aW11bSBzaXplIHRoYXQgYW4gb2JqZWN0IGNhbiBiZSBpbiB0aGUgeSBkaW1lbnNpb25cbiAgKiBAcGFyYW0ge251bWJlcn0gW21heE9iamVjdFNpemU9MTAwXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5TWF4T2JqZWN0U2l6ZSA9IDEwMCxcblxuXG4gIC8qKlxuICAqIFRoZSBzdHJva2Ugd2lkdGggb2YgdGhlIGJ1YmJsZXNcbiAgKiBAcGFyYW0ge251bWJlcn0gW29iamVjdFN0cm9rZVdpZHRoPTJdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdFN0cm9rZVdpZHRoID0gMixcbiAgLy8gY29sb3JGdW5jID0gY29sb3JGdW5jdGlvbigpLFxuXG4gIC8qKlxuICAqIENvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kXG4gICogQHBhcmFtIHtzdHJpbmd9IFtiYWNrZ3JvdW5kRmlsbD1cInRyYW5zcGFyZW50XCJdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJhY2tncm91bmRGaWxsID0gJ3RyYW5zcGFyZW50JyxcbiAgLyoqXG4gICogTmFtZXNwYWNlIGZvciBhbGwgaXRlbXMgbWFkZSBieSB0aGlzIGluc3RhbmNlIG9mIGhlYXRtYXBcbiAgKiBAcGFyYW0ge3N0cmluZ30gW25hbWVzcGFjZT1cImQzc20taGVhdG1hcFwiXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBuYW1lc3BhY2UgPSAnZDNzbS1oZWF0bWFwJyxcbiAgLyoqXG4gICogQ2xhc3MgbmFtZSBmb3IgaGVhdG1hcCBjb250YWluZXIgKDxnPiBlbGVtZW50KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbb2JqZWN0Q2xhc3M9XCJoZWF0bWFwXCJdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdENsYXNzID0gJ2hlYXRtYXAnLFxuICAvKipcbiAgKiBEdXJhdGlvbiBvZiBhbGwgdHJhbnNpdGlvbnMgb2YgdGhpcyBlbGVtZW50XG4gICogQHBhcmFtIHtudW1iZXJ9IFt0cmFuc2l0aW9uRHVyYXRpb249MTAwMF1cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMCxcbiAgLyoqXG4gICogRWFzaW5nIGZ1bmN0aW9uIGZvciB0cmFuc2l0aW9uc1xuICAqIEBwYXJhbSB7ZDMuZWFzZX0gW2Vhc2VGdW5jPWQzLmVhc2VFeHBdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGVhc2VGdW5jID0gZDMuZWFzZUV4cCxcblxuICAvKipcbiAgKiBTdG9yZXMgdGhlIGtleXMgb2YgYWxsIHRoZSBjZWxsc1xuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgaGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW2NlbGxLZXlzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgY2VsbEtleXMsXG4gIC8qKlxuICAqIFN0b3JlcyB0aGUgbGlzdCBvZiB1bmlxdWUgeFZhbHVlc1xuICAqIENhbGN1bGF0ZWQgYWZ0ZXIgaGVhdG1hcCBjYWxsZWQuXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW3hWYWx1ZXM9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB4VmFsdWVzLFxuICAvKipcbiAgKiBTdG9yZXMgdGhlIGxpc3Qgb2YgdW5pcXVlIHlWYWx1ZXNcbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt5VmFsdWVzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeVZhbHVlcyxcbiAgLyoqXG4gICogU3RvcmVzIHRoZSBsaXN0IG9mIHVuaXF1ZSB2VmFsdWVzXG4gICogQ2FsY3VsYXRlZCBhZnRlciBoZWF0bWFwIGNhbGxlZC5cbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbdlZhbHVlcz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHZWYWx1ZXMsXG5cbiAgeEtleVNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKGEsIGIpIHsgcmV0dXJuIHhWYWx1ZXMuaW5kZXhPZih4RXh0cmFjdG9yKGEpKSAtIHhWYWx1ZXMuaW5kZXhPZih4RXh0cmFjdG9yKGIpKSB9LFxuICB5S2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4geVZhbHVlcy5pbmRleE9mKHlFeHRyYWN0b3IoYSkpIC0geVZhbHVlcy5pbmRleE9mKHlFeHRyYWN0b3IoYikpIH0sXG4gIHZLZXlTb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihhLCBiKSB7IHJldHVybiB2VmFsdWVzLmluZGV4T2YodkV4dHJhY3RvcihhKSkgLSB5VmFsdWVzLmluZGV4T2YodkV4dHJhY3RvcihiKSkgfSxcblxuICAvKipcbiAgKiBJbnN0YW5jZSBvZiBDb2xvckZ1bmN0aW9uIHdpdGggLmNvbG9yQnkgc2V0IHRvICdjYXRlZ29yeSdcbiAgKiBAZnVuY3Rpb24gY29sb3JGdW5jdGlvblxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjb2xvckZ1bmN0aW9uID0gQ0YoKS5jb2xvckJ5KCdjYXRlZ29yeScpLFxuICAvKipcbiAgKiBJbnN0YW5jZSBvZiBUb29sdGlwXG4gICogQGZ1bmN0aW9uIHRvb2x0aXBcbiAgKiBAbWVtYmVyb2YgaGVhdG1hcCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdG9vbHRpcCA9IFRUaXAoKSxcblxuICAvKipcbiAgKiBzdG9yZSB0aGUgc2l6ZSB0aGUgaGVhdG1hcCBjb3VsZCBiZSBpbiB0aGUgeCBkaW1lbnNpb25cbiAgKiB0aGUgYWN0dWFsbCBzaXplIG9mIHRoZSBoZWF0bWFwIHdpbGwgYmUgdGhlIG1pbiBvZiB4U2l6ZSBhbmQge0BsaW5rIGhlYXRtYXAjeVNpemV9XG4gICogQ2FsY3VsYXRlZCBhZnRlciBoZWF0bWFwIGNhbGxlZC5cbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbeFNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBoZWF0bWFwI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB4U2l6ZSxcbiAgLyoqXG4gICogc3RvcmUgdGhlIHNpemUgb2YgdGhlIHNwYWNlciBpbiB0aGUgeCBkaW1lbnNpb25cbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt4U3BhY2VyU2l6ZT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhTcGFjZXJTaXplLFxuXG4gIC8qKlxuICAqIHN0b3JlIHRoZSBzaXplIHRoZSBoZWF0bWFwIGNvdWxkIGJlIGluIHRoZSB5IGRpbWVuc2lvblxuICAqIHRoZSBhY3R1YWxsIHNpemUgb2YgdGhlIGhlYXRtYXAgd2lsbCBiZSB0aGUgbWluIG9mIHhTaXplIGFuZCB7QGxpbmsgaGVhdG1hcCN4U2l6ZX1cbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt5U2l6ZT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHlTaXplLFxuICAvKipcbiAgKiBzdG9yZSB0aGUgc2l6ZSBvZiB0aGUgc3BhY2VyIGluIHRoZSB5IGRpbWVuc2lvbi5cbiAgKiBDYWxjdWxhdGVkIGFmdGVyIGhlYXRtYXAgY2FsbGVkLlxuICAqIEBwYXJhbSB7c3RyaW5nW119IFt4U3BhY2VyU2l6ZT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGhlYXRtYXAjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHlTcGFjZXJTaXplXG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgc2VsZWN0aW9uIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBkMy5zZWxlY3Rpb259XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNlbGVjdGlvbiA9IHNlbGVjdGlvblxuICAgKi9cbiAgaG0uc2VsZWN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCBobSkgOiBzZWxlY3Rpb247IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZGF0YVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI2RhdGF9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBvYmplY3R9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgaG0uZGF0YSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGF0YSA9IF8sIGhtKSA6IGRhdGE7IH1cbiAgLy8gaG0ub3JpZW50ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvcmllbnQgPSBfLCBobSkgOiBvcmllbnQ7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIGhvcml6b250YWwgc3BhY2UgaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjc3BhY2VYfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlWCA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0uc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVggPSBfLCBobSkgOiBzcGFjZVg7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3NwYWNlWX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVkgPSB1bmRlZmluZWRcbiAgICovXG4gIGhtLnNwYWNlWSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3BhY2VZID0gXywgaG0pIDogc3BhY2VZOyB9XG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeEtleVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3hLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhLZXkgPSAneCdcbiAgICovXG4gIGhtLnhLZXkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhLZXkgPSBfLCBobSkgOiB4S2V5OyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHlLZXlcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCN5S2V5fSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5S2V5ID0gJ3knXG4gICAqL1xuICBobS55S2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5S2V5ID0gXywgaG0pIDogeUtleTsgfVxuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZLZXlcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCN2S2V5fSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2S2V5ID0gJ3knXG4gICAqL1xuICBobS52S2V5ID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2S2V5ID0gXywgaG0pIDogdktleTsgfVxuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGNlbGxLZXlzXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjY2VsbEtleXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBjZWxsS2V5cyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0uY2VsbEtleXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNlbGxLZXlzID0gXywgaG0pIDogY2VsbEtleXM7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeFZhbHVlc1xuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3hWYWx1ZXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB4VmFsdWVzID0gdW5kZWZpbmVkXG4gICAqL1xuICBobS54VmFsdWVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4VmFsdWVzID0gXywgaG0pIDogeFZhbHVlczsgfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB5VmFsdWVzXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjeVZhbHVlc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgc3RyaW5nW119XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHlWYWx1ZXMgPSB1bmRlZmluZWRcbiAgICovXG4gIGhtLnlWYWx1ZXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlWYWx1ZXMgPSBfLCBobSkgOiB5VmFsdWVzOyB9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZWYWx1ZXNcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCN2VmFsdWVzfSlcbiAgICogQHBhcmFtIHtudW1iZXJbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBudW1iZXJbXX1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdlZhbHVlcyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0udlZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodlZhbHVlcyA9IF8sIGhtKSA6IHZWYWx1ZXM7IH1cblxuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHhFeHRyYWN0b3JcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCN4RXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgeEV4dHJhY3RvciA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0ueEV4dHJhY3RvciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeEV4dHJhY3RvciA9IF8sIGhtKSA6IHhFeHRyYWN0b3I7IH1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgeUV4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3lFeHRyYWN0b3J9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5RXh0cmFjdG9yID0gdW5kZWZpbmVkXG4gICAqL1xuICBobS55RXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5RXh0cmFjdG9yID0gXywgaG0pIDogeUV4dHJhY3RvcjsgfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSB2RXh0cmFjdG9yXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjdkV4dHJhY3Rvcn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZFeHRyYWN0b3IgPSB1bmRlZmluZWRcbiAgICovXG4gIGhtLnZFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZFeHRyYWN0b3IgPSBfLCBobSkgOiB2RXh0cmFjdG9yOyB9XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IGhlYXRtYXAgaXMgYWxsb3dlZCB0byBnbyBiZXlvbmQgc3BlY2lmaWVkIGRpbWVuc2lvbnNcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNzcGFjZVh9KVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgYm9vbGVhbn1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3ZlcmZsb3dRID0gZmFsc2VcbiAgICovXG4gIGhtLm92ZXJmbG93USA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3ZlcmZsb3dRID0gXywgaG0pIDogb3ZlcmZsb3dROyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyBvYmplY3RTcGFjZXJcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNvYmplY3RTcGFjZXJ9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdFNwYWNlciA9IDAuMFxuICAgKi9cbiAgaG0ub2JqZWN0U3BhY2VyID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RTcGFjZXIgPSBfLCBvYmplY3RTcGFjZXIpIDogZGF0YTsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1pbk9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNtaW5PYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtaW5PYmplY3RTaXplID0gNTBcbiAgICovXG4gIGhtLnlNaW5PYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5TWluT2JqZWN0U2l6ZSA9IF8sIGhtKSA6IHlNaW5PYmplY3RTaXplOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbWF4T2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI21heE9iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1heE9iamVjdFNpemUgPSAxMDBcbiAgICovXG4gIGhtLnlNYXhPYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5TWF4T2JqZWN0U2l6ZSA9IF8sIGhtKSA6IHlNYXhPYmplY3RTaXplOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbWluT2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI21pbk9iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2hlYXRtYXAgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1pbk9iamVjdFNpemUgPSA1MFxuICAgKi9cbiAgaG0ueE1pbk9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhNaW5PYmplY3RTaXplID0gXywgaG0pIDogeE1pbk9iamVjdFNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtYXhPYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjbWF4T2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWF4T2JqZWN0U2l6ZSA9IDEwMFxuICAgKi9cbiAgaG0ueE1heE9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhNYXhPYmplY3RTaXplID0gXywgaG0pIDogeE1heE9iamVjdFNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RTdHJva2VXaWR0aFxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI29iamVjdFN0cm9rZVdpZHRofSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RTdHJva2VXaWR0aCA9IDJcbiAgICovXG4gIGhtLm9iamVjdFN0cm9rZVdpZHRoID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RTdHJva2VXaWR0aCA9IF8sIGhtKSA6IG9iamVjdFN0cm9rZVdpZHRoOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFja2dyb3VuZEZpbGxcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNiYWNrZ3JvdW5kRmlsbH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnXG4gICAqL1xuICBobS5iYWNrZ3JvdW5kRmlsbCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFja2dyb3VuZEZpbGwgPSBfLCBobSkgOiBiYWNrZ3JvdW5kRmlsbDsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG5hbWVzcGFjZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI25hbWVzcGFjZX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbmFtZXNwYWNlID0gJ2Qzc20taGVhdG1hcCdcbiAgICovXG4gIGhtLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgaG0pIDogbmFtZXNwYWNlOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0Q2xhc3NcbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNvYmplY3RDbGFzc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0Q2xhc3MgPSAndGljay1ncm91cCdcbiAgICovXG4gIGhtLm9iamVjdENsYXNzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RDbGFzcyA9IF8sIGhtKSA6IG9iamVjdENsYXNzOyB9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdHJhbnNpdGlvbkR1cmF0aW9uXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjdHJhbnNpdGlvbkR1cmF0aW9ufSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwXG4gICAqL1xuICBobS50cmFuc2l0aW9uRHVyYXRpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRyYW5zaXRpb25EdXJhdGlvbiA9IF8sIGhtKSA6IHRyYW5zaXRpb25EdXJhdGlvbjsgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGVhc2VGdW5jXG4gICAqIChzZWUge0BsaW5rIGhlYXRtYXAjZWFzZUZ1bmN9KVxuICAgKiBAcGFyYW0ge2QzLmVhc2V9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgZDMuZWFzZX1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwXG4gICAqL1xuICBobS5lYXNlRnVuYyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZWFzZUZ1bmMgPSBfLCBobSkgOiBlYXNlRnVuYzsgfVxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdG9vbHRpcFxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3Rvb2x0aXB9KVxuICAgKiBAcGFyYW0ge3Rvb2x0aXB9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgdG9vbHRpcH1cbiAgICogQG1lbWJlcm9mIGhlYXRtYXBcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdG9vbHRpcCA9IHRvb2x0aXAoKVxuICAgKi9cbiAgaG0udG9vbHRpcCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodG9vbHRpcCA9IF8sIGhtKSA6IHRvb2x0aXA7IH1cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGNvbG9yRnVuY3Rpb25cbiAgICogKHNlZSB7QGxpbmsgaGVhdG1hcCNjb2xvckZ1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtjb2xvckZ1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7aGVhdG1hcCB8IGNvbG9yRnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBoZWF0bWFwXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGNvbG9yRnVuY3Rpb24gPSBjb2xvckZ1bmN0aW9uKClcbiAgICovXG4gIGhtLmNvbG9yRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNvbG9yRnVuY3Rpb24gPSBfLCBobSkgOiBjb2xvckZ1bmN0aW9uOyB9XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB4U2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3hTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB4U2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0ueFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhTaXplID0gXywgaG0pIDogeFNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB4U3BhY2VyU2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3hTcGFjZXJTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB4U3BhY2VyU2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0ueFNwYWNlclNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhTcGFjZXJTaXplID0gXywgaG0pIDogeFNwYWNlclNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB5U2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3lTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5U2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0ueVNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlTaXplID0gXywgaG0pIDogeVNpemU7IH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB5U3BhY2VyU2l6ZVxuICAgKiAoc2VlIHtAbGluayBoZWF0bWFwI3lTcGFjZXJTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtoZWF0bWFwIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgaGVhdG1hcFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5U3BhY2VyU2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgaG0ueVNwYWNlclNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlTcGFjZXJTaXplID0gXywgaG0pIDogeVNwYWNlclNpemU7IH1cbiAgLy8gaG0ueUtleVNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeUtleVNvcnRpbmdGdW5jdGlvbiA9IF8sIGhtKSA6IHlLZXlTb3J0aW5nRnVuY3Rpb247IH1cbiAgLy8gaG0ueEtleVNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeEtleVNvcnRpbmdGdW5jdGlvbiA9IF8sIGhtKSA6IHhLZXlTb3J0aW5nRnVuY3Rpb247IH1cblxuXG5cbiAgZnVuY3Rpb24gaG0oKSB7XG4gICAgdmFyIGhvcml6b250YWxRID0gdHJ1ZTsgLy8gbm8gb3JpZW50YXRpb24gaW4gaGVhdG1hcHMuIEFpZHMgYXMgcGxhY2Vob2xkZXIgaW4gZnVuY3Rpb25zIHRoYXQgdGFrZSBvcmllbnQgYXMgYSBwYXJhbWV0ZXJcbiAgICB2YXIgYmdjcFJlY3QgPSB7eDowLCB5OjAsIHdpZHRoOiBzcGFjZVgsIGhlaWdodDpzcGFjZVl9XG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cbiAgICBjZWxsS2V5cyA9IGQzLmtleXMoZGF0YSk7XG5cbiAgICB4VmFsdWVzID0gdW5pcXVlKGNlbGxLZXlzLm1hcCh4RXh0cmFjdG9yKSk7XG4gICAgeVZhbHVlcyA9IHVuaXF1ZShjZWxsS2V5cy5tYXAoeUV4dHJhY3RvcikpO1xuICAgIHZWYWx1ZXMgPSB1bmlxdWUoY2VsbEtleXMubWFwKHZFeHRyYWN0b3IpKTtcblxuICAgIGNlbGxLZXlzLnNvcnQoZnVuY3Rpb24oYSwgYil7IHJldHVybiB4S2V5U29ydGluZ0Z1bmN0aW9uKGEsIGIpIHx8IHlLZXlTb3J0aW5nRnVuY3Rpb24oYSwgYikgfSlcbiAgICBsb2coJ2hlYXRtYXAnLCAnY2VsbHMgYXJlIHNvcnRlZCBieScsIGNlbGxLZXlzKVxuXG5cblxuICAgIGxvZygnaGVhdG1hcCcsICd4IGFuZCB5IGtleXMgYXJlJywge3g6IHhWYWx1ZXMsIHk6eVZhbHVlc30pXG5cblxuICAgIHZhciB4RGltID0geFZhbHVlcy5sZW5ndGgsIHlEaW0gPSB5VmFsdWVzLmxlbmd0aDtcblxuXG4gICAgeVNpemUgPSBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0KHNwYWNlWSwgeURpbSwgeU1pbk9iamVjdFNpemUsIHlNYXhPYmplY3RTaXplLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICB4U2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3Qoc3BhY2VYLCB4RGltLCB4TWluT2JqZWN0U2l6ZSwgeE1heE9iamVjdFNpemUsIG9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIHlTcGFjZXJTaXplID0gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcih5VmFsdWVzLCBzcGFjZVksIHlTaXplLCB5RGltLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICB4U3BhY2VyU2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXIoeFZhbHVlcywgc3BhY2VYLCB4U2l6ZSwgeERpbSwgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgLy8gY29uc29sZS50YWJsZSh7XG4gICAgLy8gICAgIHg6e1xuICAgIC8vICAgICAgIG9iamVjdDogeFNpemUsXG4gICAgLy8gICAgICAgc3BhY2VyOiB4U3BhY2VyU2l6ZSxcbiAgICAvLyAgICAgICBkaW06IHhEaW1cbiAgICAvLyAgICAgfSxcbiAgICAvLyAgICAgeTp7XG4gICAgLy8gICAgICAgb2JqZWN0OiB5U2l6ZSxcbiAgICAvLyAgICAgICBzcGFjZXI6IHlTcGFjZXJTaXplLFxuICAgIC8vICAgICAgIGRpbTogeURpbVxuICAgIC8vICAgICB9XG4gICAgLy9cbiAgICAvLyB9KVxuICAgIGxvZygnaGVhdG1hcCcsICdzaXplIG9mJywge3g6IHhTaXplLCB5OiB5U2l6ZX0pXG5cblxuXG4gICAgdmFyIHlTcGFjZXIgPSBncm91cGluZ1NwYWNlcigpXG4gICAgLmhvcml6b250YWxRKGZhbHNlKVxuICAgIC5tb3ZlYnkoJ2NhdGVnb3J5JylcbiAgICAubnVtYmVyT2ZPYmplY3RzKHlEaW0pXG4gICAgLm9iamVjdENsYXNzKGh5cGVuYXRlKG9iamVjdENsYXNzLCAncm93JykpXG4gICAgLm9iamVjdFNpemUoeVNpemUgKyB5U3BhY2VyU2l6ZSlcbiAgICAuc3BhY2VyU2l6ZSgwKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKVxuICAgIC5lYXNlRnVuYyhlYXNlRnVuYylcbiAgICAubmFtZXNwYWNlKCdyb3cnKVxuXG4gICAgdmFyIHhTcGFjZXIgPSBncm91cGluZ1NwYWNlcigpXG4gICAgLmhvcml6b250YWxRKHRydWUpXG4gICAgLm1vdmVieSgnY2F0ZWdvcnknKVxuICAgIC5udW1iZXJPZk9iamVjdHMoeERpbSlcbiAgICAub2JqZWN0Q2xhc3Mob2JqZWN0Q2xhc3MpXG4gICAgLm9iamVjdFNpemUoeFNpemUgKyB4U3BhY2VyU2l6ZSlcbiAgICAuc3BhY2VyU2l6ZSgwKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKVxuICAgIC5lYXNlRnVuYyhlYXNlRnVuYylcblxuXG4gICAgeVNwYWNlcihjb250YWluZXIsIHlWYWx1ZXMsIDApXG4gICAgY29udGFpbmVyLnNlbGVjdEFsbCgnZy4nK2h5cGVuYXRlKG9iamVjdENsYXNzLCAncm93JykpXG4gICAgLmVhY2goZnVuY3Rpb24oZCwgaSl7IHhTcGFjZXIoZDMuc2VsZWN0KHRoaXMpLCB4VmFsdWVzLCAwKSB9KVxuXG4gICAgdmFyIGNlbGxzID0gY29udGFpbmVyLnNlbGVjdEFsbCgnZzpub3QoLnRvLXJlbW92ZSkuJytvYmplY3RDbGFzcylcblxuXG4gICAgaWYgKGNlbGxLZXlzLmxlbmd0aCAhPSB5VmFsdWVzLmxlbmd0aCAqIHhWYWx1ZXMubGVuZ3RoKSB7XG4gICAgICB2YXIgbG9va3VwID0ge31cbiAgICAgIGNlbGxLZXlzLm1hcChmdW5jdGlvbihrLCBpKXtcbiAgICAgICAgbG9va3VwW3hFeHRyYWN0b3IoaykrJzo6Jyt5RXh0cmFjdG9yKGspXSA9IGtcbiAgICAgIH0pXG5cbiAgICAgIHZhciBwb3NpdGlvbmVkQ2VsbEtleXMgPSBbXVxuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCB5VmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgeFZhbHVlcy5sZW5ndGg7IGorKykge1xuICAgICAgICAgIHZhciBsb29rdXBWYWx1ZSA9IGxvb2t1cFt4VmFsdWVzW2pdK1wiOjpcIit5VmFsdWVzW2ldXVxuICAgICAgICAgIGlmIChsb29rdXBWYWx1ZSA9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHBvc2l0aW9uZWRDZWxsS2V5cy5wdXNoKHVuZGVmaW5lZClcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcG9zaXRpb25lZENlbGxLZXlzLnB1c2gobG9va3VwVmFsdWUpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNlbGxzLmRhdGEocG9zaXRpb25lZENlbGxLZXlzKTtcblxuICAgICAgLy8gbWF5YmUgYnJlYWtzIHRoaXNcbiAgICAgIC8vICEhISEhISBJTVBPUlRBTlQgTk9URSBUT0RPIExPT0sgSEVSRSBCVUdcbiAgICAgIGNlbGxLZXlzID0gcG9zaXRpb25lZENlbGxLZXlzXG4gICAgfSBlbHNlIHtcbiAgICAgIGNlbGxzLmRhdGEoY2VsbEtleXMpO1xuICAgIH1cblxuXG5cbiAgICB2YXIgcGFyZW50SW5kZXhBcnJheSA9IFtdXG4gICAgY2VsbHMuZWFjaChmdW5jdGlvbihkLCBpKXsgcGFyZW50SW5kZXhBcnJheS5wdXNoKE51bWJlcihkMy5zZWxlY3QodGhpcykuYXR0cigncGFyZW50LWluZGV4JykpKSB9KVxuXG4gICAgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24uY29sb3JCeSgpID09ICdpbmRleCdcbiAgICA/IGNvbG9yRnVuY3Rpb24uZGF0YUV4dGVudChbMCwgTWF0aC5tYXgoLi4ucGFyZW50SW5kZXhBcnJheSldKVxuICAgIDogY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KFswLCBNYXRoLm1heCguLi52VmFsdWVzKV0pXG5cbiAgICBjZWxscy5lYWNoKGZ1bmN0aW9uKGtleSwgaSkge1xuICAgICAgbG9nKCdoZWF0bWFwJywgJ2VhY2ggY2VsbCcsIHtrZXk6IGtleSwgaW5kZXg6IGksIG5vZGU6IGQzLnNlbGVjdCh0aGlzKS5ub2RlKCl9KVxuXG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgICAgaWYgKGtleSA9PSB1bmRlZmluZWQpIHtyZXR1cm59XG4gICAgICB2YXIgY3VycmVudERhdGEgPSBkYXRhW2tleV0sXG4gICAgICB2YWx1ZSA9IHZFeHRyYWN0b3Ioa2V5LCBpKSxcbiAgICAgIGkgPSB0LmF0dHIoJ3BhcmVudC1pbmRleCcpID09IHVuZGVmaW5lZCA/IGkgOiB0LmF0dHIoJ3BhcmVudC1pbmRleCcpLFxuICAgICAgZmlsbENvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIHZhbHVlLCBpLCAnZmlsbCcpLCAvLyBwcmV2ZW50IGR1cGxpY2F0ZSBjb21wdXRhdGlvblxuICAgICAgc3Ryb2tlQ29sb3IgPSBjb2xvckZ1bmN0aW9uKGtleSwgdmFsdWUsIGksICAnc3Ryb2tlJylcblxuICAgICAgdmFyIGMgPSBzYWZlU2VsZWN0KHQsICdyZWN0JywgaHlwZW5hdGUob2JqZWN0Q2xhc3MsJ3JlY3QnKSlcbiAgICAgIGMuYXR0cignd2lkdGgnLCB4U2l6ZSArIHhTcGFjZXJTaXplIC0gb2JqZWN0U3Ryb2tlV2lkdGgpXG4gICAgICAuYXR0cignaGVpZ2h0JywgeVNpemUgKyB5U3BhY2VyU2l6ZSAtIG9iamVjdFN0cm9rZVdpZHRoKVxuICAgICAgLmF0dHIoJ2ZpbGwnLCBmaWxsQ29sb3IpXG4gICAgICAuYXR0cigneCcsIG9iamVjdFN0cm9rZVdpZHRoLzIpXG4gICAgICAuYXR0cigneScsIG9iamVjdFN0cm9rZVdpZHRoLzIpXG4gICAgICAuYXR0cignc3Ryb2tlJywgXCIjMDAwXCIpXG4gICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgb2JqZWN0U3Ryb2tlV2lkdGgpXG5cbiAgICB9KVxuXG4gICAgdG9vbHRpcC5zZWxlY3Rpb24oY2VsbHMuc2VsZWN0QWxsKCdyZWN0LicraHlwZW5hdGUob2JqZWN0Q2xhc3MsICdyZWN0JykpKVxuICAgIC5kYXRhKGRhdGEpXG4gICAgLy8gLmtleXMoWydyJywgJ3YnXSlcbiAgICAvLyAuaGVhZGVyKGZ1bmN0aW9uKGQsIGkpe3JldHVybiBoeXBlbmF0ZShkYXRhW2RdW3hLZXldLCBkYXRhW2RdW3lLZXldKSB9KVxuXG4gICAgdG9vbHRpcCgpXG5cblxuICB9XG5cbiAgcmV0dXJuIGhtO1xufVxuXG5leHBvcnQge2hlYXRtYXB9XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHtzZXR1cENvbnRhaW5lciwgY2FsY3VsYXRlV2lkdGhPZk9iamVjdCwgY2FsY3VsYXRlV2lkdGhPZlNwYWNlciwgd2hpc2tlclBhdGh9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHt1bmlxdWUsIGhhc1EsIGZsYXR0ZW59IGZyb20gJy4vYXJyYXktZnVuY3Rpb25zJztcbmltcG9ydCB7Z3JvdXBpbmdTcGFjZXJ9IGZyb20gJy4vZ3JvdXBpbmctc3BhY2VyJztcbmltcG9ydCB7Y29sb3JGdW5jdGlvbiBhcyBDRn0gZnJvbSAnLi9jb2xvci1mdW5jdGlvbic7XG5pbXBvcnQge3Rvb2x0aXAgYXMgVFRpcH0gZnJvbSAnLi90b29sdGlwJztcbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCT1ggQU5EIFdISVNLRVIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKipcbiAqIENyZWF0ZXMgYSBib3h3aGlza2VyXG4gKlxuICoge0BsaW5rIGh0dHBzOi8vc3VtbmV1cm9uLmdpdGxhYi5pby9kM3NtL2RlbW9zL2JveC13aGlza2Vycy9pbmRleC5odG1sIERlbW99XG4gKiBAY29uc3RydWN0b3IgYm94d2hpc2tlclxuICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IHNlbGVjdGlvblxuICogQG5hbWVzcGFjZSBib3h3aGlza2VyXG4gKiBAcmV0dXJucyB7ZnVuY3Rpb259IGJveHdoaXNrZXJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJveHdoaXNrZXIoIHNlbGVjdGlvbiApIHtcbiAgdmFyXG4gIC8qKlxuICAqIERhdGEgdG8gcGxvdC4gQXNzdW1lZCB0byBiZSBhIG9iamVjdCwgd2hlcmUgZWFjaCBrZXkgY29ycmVzcG9uZHMgdG8gYSBib3hcbiAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI2RhdGF9KVxuICAqIEBwYXJhbSB7T2JqZWN0fSBbZGF0YT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGRhdGEsXG4gIC8qKlxuICAqIFdoaWNoIGRpcmVjdGlvbiB0byByZW5kZXIgdGhlIGJveGVzIGluXG4gICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNvcmllbnR9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbb3JpZW50PSdob3Jpem9udGFsJ11cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb3JpZW50ID0gJ2hvcml6b250YWwnLFxuICAvKipcbiAgKiBBbW91bnQgb2YgaG9yaXpvbnRhbCBzcGFjZSAoaW4gcGl4ZWxzKSBhdmFpYmxlIHRvIHJlbmRlciB0aGUgYm94d2hpc2tlciBpblxuICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VYfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlWD11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlWCxcbiAgLyoqXG4gICogQW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBib3h3aGlza2VyIGluXG4gICogKHNlZSB7QGxpbmsgYm94d2hpc2tlci5zcGFjZVl9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VZPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VZLFxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0byBhbGxvdyBib3h3aGlza2VyIHRvIHJlbmRlciBlbGVtZW50cyBwYXNzIHRoZSBtYWluIHNwYXRpYWwgZGltZW5zaW9uXG4gICogZ2l2ZW4gdGhlIG9yaWVudGF0aW9uIChzZWUge0BsaW5rIGJveHdoaXNrZXIjb3JpZW50fSksIHdoZXJlIHtAbGluayBib3h3aGlza2VyI29yaWVudH09XCJob3Jpem9udGFsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGJveHdoaXNrZXIjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VZfVxuICAqIEBwYXJhbSB7Ym9vbGVhbn0gW292ZXJmbG93UT1mYWxzZV1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb3ZlcmZsb3dRID0gdHJ1ZSxcblxuICAvKipcbiAgKiBBbiBhcnJheSAtIHB1dGF0aXZlbHkgb2Ygb3RoZXIgYXJyYXlzIC0gZGVwaWN0aW5nIGhvdyBib3hlcyBzaG91bGQgYmUgYXJyYW5nZWRcbiAgKiBAcGFyYW0ge0FycmF5W119IFtncm91cGluZz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGdyb3VwaW5nLFxuICBxdWFydGlsZXNLZXkgPSAncXVhcnRpbGVzJywgLy8ga2V5IGluIG9iamVjdCB3aGVyZSBxdWFydGlsZXMgYXJlIHN0b3JlZFxuICBxdWFydGlsZXNLZXlzID0gW1wiMC4wMFwiLCBcIjAuMjVcIiwgXCIwLjUwXCIsIFwiMC43NVwiLCBcIjEuMDBcIl0sIC8vIGtleXMgaW4gcXVhcnRpbGVzIG9iamVjdCBtYXBwaW5nIHZhbHVlcyB0byBtaW4sIHExLCBxMiwgcTMgYW5kIG1heFxuXG5cbiAgLyoqXG4gICogSG93IHRvIGdldCB0aGUgdmFsdWUgb2YgdGhlIGJveHdoaXNrZXJcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdmFsdWVFeHRyYWN0b3I9ZnVuY3Rpb24oa2V5LCBpbmRleCkgeyByZXR1cm4gZGF0YVtrZXldW3F1YXJ0aWxlc0tleV0gfV1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV1bcXVhcnRpbGVzS2V5XSB9LFxuICAvKipcbiAgKiBIb3cgdG8gc29ydCB0aGUgYm94ZXMgLSBpZiB7QGxpbmsgYm94d2hpc2tlciNncm91cGluZ30gaXMgbm90IHByb3ZpZGVkLlxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtzb3J0aW5nRnVuY3Rpb249ZGVzY2VuZGluZ11cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKiBkZWZhdWx0XG4gICogZnVuY3Rpb24oa2V5QSwga2V5Qikge3JldHVybiBkMy5kZXNjZW5kaW5nKFxuICAqICAgdmFsdWVFeHRyYWN0b3Ioa2V5QSlbcXVhcnRpbGVzS2V5c1s0XV0sXG4gICogICB2YWx1ZUV4dHJhY3RvcihrZXlCKVtxdWFydGlsZXNLZXlzWzRdXVxuICAqICl9XG4gICovXG4gIHNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKGtleUEsIGtleUIpIHtyZXR1cm4gZDMuZGVzY2VuZGluZyhcbiAgICB2YWx1ZUV4dHJhY3RvcihrZXlBKVtxdWFydGlsZXNLZXlzWzRdXSxcbiAgICB2YWx1ZUV4dHJhY3RvcihrZXlCKVtxdWFydGlsZXNLZXlzWzRdXVxuICApfSxcbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCBib3h3aGlza2VyIHZhbHVlcyBzaG91bGQgYmUgdHJhbnNmb3JtZWQgYnlcbiAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbc2NhbGU9ZDMuc2NhbGVMaW5lYXJdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKSxcbiAgLyoqXG4gICogVGhlIHBhZGRpbmcgZm9yIHRoZSBkb21haW4gb2YgdGhlIHNjYWxlIChzZWUge0BsaW5rIGJveHdoaXNrZXIjc2NhbGV9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbZG9tYWluUGFkZGluZz0wLjVdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGRvbWFpblBhZGRpbmcgPSAwLjUsXG4gIC8qKlxuICAqIERlZmF1bHQgc3BhY2UgZm9yIHRoZSBzcGFjZXIgKHBlcmNlbnRhZ2UpIG9mIG1haW4gZGltZW5zaW9uIGdpdmVuIHRoZSBvcmllbnRhdGlvblxuICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjb3JpZW50fSksIHdoZXJlIHtAbGluayBib3h3aGlza2VyI29yaWVudH09XCJob3Jpem9udGFsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGJveHdoaXNrZXIjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VZfSBiZXR3ZWVuIGJveGVzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvYmplY3RTcGFjZXI9MC4wNV1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0U3BhY2VyID0gMC4wNSxcbiAgLyoqXG4gICogVGhlIG1pbmltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmVcbiAgKiBAcGFyYW0ge251bWJlcn0gW21pbk9iamVjdFNpemU9MTVdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1pbk9iamVjdFNpemUgPSAxNSxcbiAgLyoqXG4gICogVGhlIG1heGltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmVcbiAgKiBAcGFyYW0ge251bWJlcn0gW21heE9iamVjdFNpemU9NTBdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1heE9iamVjdFNpemUgPSA1MCxcbiAgLyoqXG4gICogUGVyY2VudCBvZiBib3ggYW5kIHdoaXNrZXIgc2l6ZSB0aGF0IHdoaXNrZXJzIHdpbGwgYmUgcmVuZGVyZWRcbiAgKiBzZWUge0BsaW5rIGJveHdoaXNrZXIjb2JqZWN0U2l6ZX1cbiAgKiBAcGFyYW0ge251bWJlcn0gW3doaXNrZXJXaWR0aFBlcmNlbnQ9MC42XVxuICAqIEBtZW1iZXJvZiBib3h3aGlza2VyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB3aGlza2VyV2lkdGhQZXJjZW50ID0gLjYsXG4gIC8qKlxuICAqIEluc3RhbmNlIG9mIENvbG9yRnVuY3Rpb25cbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKV1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgY29sb3JGdW5jdGlvbiA9IENGKCksXG4gIC8qKlxuICAqIFRoZSBzdHJva2Ugd2lkdGggb2YgdGhlIGJveGVzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtib3hTdHJva2VXaWR0aD0yXVxuICAqIEBtZW1iZXJvZiBib3h3aGlza2VyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBib3hTdHJva2VXaWR0aCA9IDIsXG4gIC8qKlxuICAqIFRoZSBzdHJva2Ugd2lkdGggb2YgdGhlIHdoaXNrZXJzXG4gICogQHBhcmFtIHtudW1iZXJ9IFt3aGlza2VyU3Ryb2tlV2lkdGg9Ml1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgd2hpc2tlclN0cm9rZVdpZHRoID0gMixcblxuICAvKipcbiAgKiBDb2xvciBvZiB0aGUgYmFja2dyb3VuZFxuICAqIEBwYXJhbSB7c3RyaW5nfSBbYmFja2dyb3VuZEZpbGw9XCJ0cmFuc3BhcmVudFwiXVxuICAqIEBtZW1iZXJvZiBib3h3aGlza2VyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCcsXG4gIC8qKlxuICAqIE5hbWVzcGFjZSBmb3IgYWxsIGl0ZW1zIG1hZGUgYnkgdGhpcyBpbnN0YW5jZSBvZiBib3h3aGlza2VyXG4gICogQHBhcmFtIHtzdHJpbmd9IFtuYW1lc3BhY2U9XCJkM3NtLWJveC13aGlza2VyXCJdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG5hbWVzcGFjZSA9ICdkM3NtLWJveC13aGlza2VyJyxcbiAgLyoqXG4gICogQ2xhc3MgbmFtZSBmb3IgYm94d2hpc2tlciBjb250YWluZXIgKDxnPiBlbGVtZW50KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbb2JqZWN0Q2xhc3M9XCJib3gtd2hpc2tcIl1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0Q2xhc3MgPSAnYm94LXdoaXNrJyxcblxuICAvKipcbiAgKiBEdXJhdGlvbiBvZiBhbGwgdHJhbnNpdGlvbnMgb2YgdGhpcyBlbGVtZW50XG4gICogQHBhcmFtIHtudW1iZXJ9IFt0cmFuc2l0aW9uRHVyYXRpb249MTAwMF1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdHJhbnNpdGlvbkR1cmF0aW9uID0gMTAwMCxcbiAgLyoqXG4gICogRWFzaW5nIGZ1bmN0aW9uIGZvciB0cmFuc2l0aW9uc1xuICAqIEBwYXJhbSB7ZDMuZWFzZX0gW2Vhc2VGdW5jPWQzLmVhc2VFeHBdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGVhc2VGdW5jID0gZDMuZWFzZUV4cCxcblxuICAvKipcbiAgKiBUaGUga2V5cyBvZiB0aGUgYm94ZXNcbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbYm94S2V5cz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJveEtleXMsXG4gIC8qKlxuICAqIFRoZSB2YWx1ZXMgb2YgdGhlIGJveGVzXG4gICogQHBhcmFtIHtzdHJpbmdbXX0gW2JveFZhbHVlcz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGJveHdoaXNrZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJveFZhbHVlcyxcbiAgLyoqXG4gICogVGhlIG9iamVjdFNpemUgKGFjdHVhbCB3aWR0aCkgdXNlZCBieSB0aGUgYm94ZXNcbiAgKiBAcGFyYW0ge251bWJlcn0gW29iamVjdFNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBib3h3aGlza2VyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvYmplY3RTaXplLFxuICAvKipcbiAgKiBUaGUgc3BhY2VyU2l6ZSAoYWN0dWFsIHdpZHRoKSB1c2VkIGJ5IHRoZSBzcGFjZXJzIGJldHdlZW4gdGhlIGJveGVzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZXJTaXplPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VyU2l6ZSxcbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgVG9vbHRpcFxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt0b29sdGlwPXRvb2x0aXAoKV1cbiAgKiBAbWVtYmVyb2YgYm94d2hpc2tlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdG9vbHRpcCA9IFRUaXAoKVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBzZWxlY3Rpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IGQzLnNlbGVjdGlvbn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2VsZWN0aW9uID0gc2VsZWN0aW9uXG4gICAqL1xuICBib3h3aGlza2VyLnNlbGVjdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2VsZWN0aW9uID0gXywgYm94d2hpc2tlcikgOiBzZWxlY3Rpb247IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGRhdGFcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNkYXRhfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgb2JqZWN0fVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGJveHdoaXNrZXIuZGF0YSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGF0YSA9IF8sIGJveHdoaXNrZXIpIDogZGF0YTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgb3JpZW50IG9mIHRoZSBib3hlc1xuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI29yaWVudH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IG9iamVjdH1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqL1xuICBib3h3aGlza2VyLm9yaWVudCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3JpZW50ID0gXywgYm94d2hpc2tlcikgOiBvcmllbnQ7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3NwYWNlWH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVggPSB1bmRlZmluZWRcbiAgICovXG4gIGJveHdoaXNrZXIuc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVggPSBfLCBib3h3aGlza2VyKSA6IHNwYWNlWDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgYW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3NwYWNlWX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVkgPSB1bmRlZmluZWRcbiAgICovXG4gIGJveHdoaXNrZXIuc3BhY2VZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVkgPSBfLCBib3h3aGlza2VyKSA6IHNwYWNlWTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IGJveHdoaXNrZXIgaXMgYWxsb3dlZCB0byBnbyBiZXlvbmQgc3BlY2lmaWVkIGRpbWVuc2lvbnNcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNvdmVyZmxvd1F9KVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgYm9vbGVhbn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3ZlcmZsb3dRID0gZmFsc2VcbiAgICovXG4gIGJveHdoaXNrZXIub3ZlcmZsb3dRID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvdmVyZmxvd1EgPSBfLCBib3h3aGlza2VyKSA6IG92ZXJmbG93UTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBncm91cGluZyBvZiB0aGUgYm94ZXNcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNncm91cGluZ30pXG4gICAqIEBwYXJhbSB7QXJyYXlbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBBcnJheVtdfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBncm91cGluZyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYm94d2hpc2tlci5ncm91cGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZ3JvdXBpbmcgPSBfLCBib3h3aGlza2VyKSA6IGdyb3VwaW5nOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHF1YXJ0aWxlc0tleVxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3F1YXJ0aWxlc0tleX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgcXVhcnRpbGVzS2V5ID0gcXVhcnRpbGVzXG4gICAqL1xuICBib3h3aGlza2VyLnF1YXJ0aWxlc0tleSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocXVhcnRpbGVzS2V5ID0gXywgYm94d2hpc2tlcikgOiBxdWFydGlsZXNLZXk7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcXVhcnRpbGVzS2V5c1xuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3F1YXJ0aWxlc0tleXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBxdWFydGlsZXNLZXlzID0gW1EwLCBRMSwgUTIsIFEzLCBRNF1cbiAgICovXG4gIGJveHdoaXNrZXIucXVhcnRpbGVzS2V5cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocXVhcnRpbGVzS2V5cyA9IF8sIGJveHdoaXNrZXIpIDogcXVhcnRpbGVzS2V5czsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2YWx1ZUV4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3ZhbHVlRXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV1bcXVhcnRpbGVzS2V5XSB9LFxuICAgKi9cblxuICBib3h3aGlza2VyLnZhbHVlRXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZUV4dHJhY3RvciA9IF8sIGJveHdoaXNrZXIpIDogdmFsdWVFeHRyYWN0b3I7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgc29ydGluZ0Z1bmN0aW9uXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjc29ydGluZ0Z1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oa2V5QSwga2V5Qikge3JldHVybiBkMy5kZXNjZW5kaW5nKFxuICAgKiAgIHZhbHVlRXh0cmFjdG9yKGtleUEpW3F1YXJ0aWxlc0tleXNbNF1dLFxuICAgKiAgIHZhbHVlRXh0cmFjdG9yKGtleUIpW3F1YXJ0aWxlc0tleXNbNF1dXG4gICAqICl9LFxuICAgKi9cbiAgYm94d2hpc2tlci5zb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNvcnRpbmdGdW5jdGlvbiA9IF8sIGJveHdoaXNrZXIpIDogc29ydGluZ0Z1bmN0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHNjYWxlIGZvciB3aGljaCB0aGUgYm94d2hpc2tlciB2YWx1ZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjc2NhbGV9KVxuICAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IGQzLnNjYWxlfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcbiAgICovXG4gIGJveHdoaXNrZXIuc2NhbGUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNjYWxlID0gXywgYm94d2hpc2tlcikgOiBzY2FsZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSBzY2FsZVxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI2RvbWFpblBhZGRpbmd9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGRvbWFpblBhZGRpbmcgPSAwLjVcbiAgICovXG4gIGJveHdoaXNrZXIuZG9tYWluUGFkZGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZG9tYWluUGFkZGluZyA9IF8sIGJveHdoaXNrZXIpIDogZG9tYWluUGFkZGluZzsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIG9iamVjdFNwYWNlclxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI29iamVjdFNwYWNlcn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0U3BhY2VyID0gMC4wNVxuICAgKi9cbiAgYm94d2hpc2tlci5vYmplY3RTcGFjZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNwYWNlciA9IF8sIGJveHdoaXNrZXIpIDogb2JqZWN0U3BhY2VyOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1pbk9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNtaW5PYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtaW5PYmplY3RTaXplID0gMTVcbiAgICovXG4gIGJveHdoaXNrZXIubWluT2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWluT2JqZWN0U2l6ZSA9IF8sIGJveHdoaXNrZXIpIDogbWluT2JqZWN0U2l6ZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtYXhPYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjbWF4T2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWF4T2JqZWN0U2l6ZSA9IDUwXG4gICAqL1xuICBib3h3aGlza2VyLm1heE9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1heE9iamVjdFNpemUgPSBfLCBib3h3aGlza2VyKSA6IG1heE9iamVjdFNpemU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgd2hpc2tlcldpZHRoUGVyY2VudFxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI3doaXNrZXJXaWR0aFBlcmNlbnR9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1heE9iamVjdFNpemUgPSAwLjZcbiAgICovXG4gIGJveHdoaXNrZXIud2hpc2tlcldpZHRoUGVyY2VudCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAod2hpc2tlcldpZHRoUGVyY2VudCA9IF8sIGJveHdoaXNrZXIpIDogd2hpc2tlcldpZHRoUGVyY2VudDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBjb2xvckZ1bmN0aW9uXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjY29sb3JGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7Y29sb3JGdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBjb2xvckZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBjb2xvckZ1bmN0aW9uID0gY29sb3JGdW5jdGlvbigpXG4gICAqL1xuICBib3h3aGlza2VyLmNvbG9yRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNvbG9yRnVuY3Rpb24gPSBfLCBib3h3aGlza2VyKSA6IGNvbG9yRnVuY3Rpb247IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYm94U3Ryb2tlV2lkdGhcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNib3hTdHJva2VXaWR0aH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYm94U3Ryb2tlV2lkdGggPSAyXG4gICAqL1xuICBib3h3aGlza2VyLmJveFN0cm9rZVdpZHRoID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChib3hTdHJva2VXaWR0aCA9IF8sIGJveHdoaXNrZXIpIDogYm94U3Ryb2tlV2lkdGg7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgd2hpc2tlclN0cm9rZVdpZHRoXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjd2hpc2tlclN0cm9rZVdpZHRofSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB3aGlza2VyU3Ryb2tlV2lkdGggPSAyXG4gICAqL1xuICBib3h3aGlza2VyLndoaXNrZXJTdHJva2VXaWR0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAod2hpc2tlclN0cm9rZVdpZHRoID0gXywgYm94d2hpc2tlcikgOiB3aGlza2VyU3Ryb2tlV2lkdGg7IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBiYWNrZ3JvdW5kRmlsbFxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI2JhY2tncm91bmRGaWxsfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtib3h3aGlza2VyIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCdcbiAgICovXG4gIGJveHdoaXNrZXIuYmFja2dyb3VuZEZpbGwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhY2tncm91bmRGaWxsID0gXywgYm94d2hpc2tlcikgOiBiYWNrZ3JvdW5kRmlsbDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBuYW1lc3BhY2VcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNuYW1lc3BhY2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG5hbWVzcGFjZSA9ICdkM3NtLWJveHdoaXNrZXInXG4gICAqL1xuICBib3h3aGlza2VyLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgYm94d2hpc2tlcikgOiBuYW1lc3BhY2U7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0Q2xhc3NcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciNvYmplY3RDbGFzc30pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0Q2xhc3MgPSAndGljay1ncm91cCdcbiAgICovXG4gIGJveHdoaXNrZXIub2JqZWN0Q2xhc3MgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdENsYXNzID0gXywgYm94d2hpc2tlcikgOiBvYmplY3RDbGFzczsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB0cmFuc2l0aW9uRHVyYXRpb25cbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciN0cmFuc2l0aW9uRHVyYXRpb259KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRyYW5zaXRpb25EdXJhdGlvbiA9IDEwMDBcbiAgICovXG4gIGJveHdoaXNrZXIudHJhbnNpdGlvbkR1cmF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0cmFuc2l0aW9uRHVyYXRpb24gPSBfLCBib3h3aGlza2VyKSA6IHRyYW5zaXRpb25EdXJhdGlvbjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBlYXNlRnVuY1xuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI2Vhc2VGdW5jfSlcbiAgICogQHBhcmFtIHtkMy5lYXNlfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IGQzLmVhc2V9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGVhc2VGdW5jID0gZDMuZWFzZUV4cFxuICAgKi9cbiAgYm94d2hpc2tlci5lYXNlRnVuYyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZWFzZUZ1bmMgPSBfLCBib3h3aGlza2VyKSA6IGVhc2VGdW5jOyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFyS2V5c1xuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI2JveEtleXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgYm94d2hpc2tlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBib3hLZXlzID0gdW5kZWZpbmVkXG4gICAqL1xuICBib3h3aGlza2VyLmJveEtleXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJveEtleXMgPSBfLCBib3h3aGlza2VyKSA6IGJveEtleXM7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYm94VmFsdWVzXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjYm94VmFsdWVzfSlcbiAgICogQHBhcmFtIHtudW1iZXJbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBudW1iZXJbXX1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYm94VmFsdWVzID0gdW5kZWZpbmVkXG4gICAqL1xuICBib3h3aGlza2VyLmJveFZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYm94VmFsdWVzID0gXywgYm94d2hpc2tlcikgOiBib3hWYWx1ZXM7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBib3h3aGlza2VyI29iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2JveHdoaXNrZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdFNpemUgPSB1bmRlZmluZWRcbiAgICovXG4gIGJveHdoaXNrZXIub2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0U2l6ZSA9IF8sIGJveHdoaXNrZXIpIDogb2JqZWN0U2l6ZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzcGFjZXJTaXplXG4gICAqIChzZWUge0BsaW5rIGJveHdoaXNrZXIjc3BhY2VyU2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGJveHdoaXNrZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VyU2l6ZSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgYm94d2hpc2tlci5zcGFjZXJTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZXJTaXplID0gXywgYm94d2hpc2tlcikgOiBzcGFjZXJTaXplOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRvb2x0aXBcbiAgICogKHNlZSB7QGxpbmsgYm94d2hpc2tlciN0b29sdGlwfSlcbiAgICogQHBhcmFtIHt0b29sdGlwfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Ym94d2hpc2tlciB8IHRvb2x0aXB9XG4gICAqIEBtZW1iZXJvZiBib3h3aGlza2VyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRvb2x0aXAgPSB0b29sdGlwKClcbiAgICovXG4gIGJveHdoaXNrZXIudG9vbHRpcCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodG9vbHRpcCA9IF8sIGJveHdoaXNrZXIpIDogdG9vbHRpcDsgfTtcblxuXG4gIGZ1bmN0aW9uIGJveHdoaXNrZXIoKSB7XG4gICAgLy8gZm9yIGNvbnZlbmllbmNlIGluIGhhbmRsaW5nIG9yaWVudGF0aW9uIHNwZWNpZmljIHZhbHVlc1xuICAgIHZhciBob3Jpem9udGFsUSA9IChvcmllbnQgPT0gJ2hvcml6b250YWwnKSA/IHRydWUgOiBmYWxzZVxuICAgIHZhciB2ZXJ0aWNhbFEgPSAhaG9yaXpvbnRhbFFcblxuICAgIC8vIGJhY2tncm91bmQgY2xpcGluZyByZWN0YW5nbGVcbiAgICB2YXIgYmdjcFJlY3QgPSB7eDowLCB5OjAsIHdpZHRoOiBzcGFjZVgsIGhlaWdodDpzcGFjZVl9XG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cbiAgICAvLyBpZiBncm91cGluZyBpcyB1bmRlZmluZWQgc29ydCBrZXlzIGJ5IHNvcnRpbmcgZnVuY3RcbiAgICB2YXIgb3JkZXJlZCA9IChncm91cGluZyA9PSB1bmRlZmluZWQpID8gZDMua2V5cyhkYXRhKS5zb3J0KHNvcnRpbmdGdW5jdGlvbikgOiBncm91cGluZ1xuICAgIC8vIHRvIHByZXZlbnQgcmUtY2FsY3VsYXRpb24gYW5kIGdldHRlcnMgdG8gYmUgcGFzc2VkIHRvIGF4ZXNcbiAgICBib3hLZXlzID0gZmxhdHRlbihvcmRlcmVkKVxuICAgIGJveFZhbHVlcyA9IGJveEtleXMubWFwKHZhbHVlRXh0cmFjdG9yKVxuXG5cbiAgICB2YXIgbnVtYmVyT2ZPYmplY3RzID0gYm94S2V5cy5sZW5ndGhcbiAgICB2YXIgZXh0ZW50ID0gW1xuICAgICAgTWF0aC5taW4oLi4uYm94VmFsdWVzLm1hcChmdW5jdGlvbihkLGkpe3JldHVybiBkW3F1YXJ0aWxlc0tleXNbMF1dfSkpIC0gZG9tYWluUGFkZGluZyxcbiAgICAgIE1hdGgubWF4KC4uLmJveFZhbHVlcy5tYXAoZnVuY3Rpb24oZCxpKXtyZXR1cm4gZFtxdWFydGlsZXNLZXlzWzRdXX0pKSArIGRvbWFpblBhZGRpbmdcbiAgICBdO1xuXG4gICAgLy8gc2V0IHRoZSBzY2FsZVxuICAgIHNjYWxlLmRvbWFpbihleHRlbnQpLnJhbmdlKGhvcml6b250YWxRID8gWzAsc3BhY2VZXSA6IFtzcGFjZVgsIDBdKVxuICAgIHZhciBzcGFjZSA9IGhvcml6b250YWxRID8gc3BhY2VYIDogc3BhY2VZXG4gICAgLy8gY2FsY3VsYXRlIG9iamVjdCBzaXplXG4gICAgb2JqZWN0U2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3Qoc3BhY2UsIG51bWJlck9mT2JqZWN0cywgbWluT2JqZWN0U2l6ZSwgbWF4T2JqZWN0U2l6ZSwgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgLy8gY2FsY3VsYXRlIHNwYWNlciBzaXplIGlmIG5lZWRlZFxuICAgIHNwYWNlclNpemUgPSBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyKGJveEtleXMsIHNwYWNlLCBvYmplY3RTaXplLCBudW1iZXJPZk9iamVjdHMsIG9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIC8vIG1ha2UgdGhlIG5lc3RlZCBncm91cHNcbiAgICB2YXIgc3BhY2VyRnVuY3Rpb24gPSBncm91cGluZ1NwYWNlcigpXG4gICAgLmhvcml6b250YWxRKGhvcml6b250YWxRKS5zY2FsZShzY2FsZSkubW92ZWJ5KCdjYXRlZ29yeScpLm51bWJlck9mT2JqZWN0cyhudW1iZXJPZk9iamVjdHMpXG4gICAgLm9iamVjdENsYXNzKG9iamVjdENsYXNzKS5vYmplY3RTaXplKG9iamVjdFNpemUpLnNwYWNlclNpemUoc3BhY2VyU2l6ZSlcbiAgICAudHJhbnNpdGlvbkR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbikuZWFzZUZ1bmMoZWFzZUZ1bmMpXG4gICAgLm5hbWVzcGFjZShuYW1lc3BhY2UpXG5cbiAgICAvLyBtb3ZlIHN0dWZmXG4gICAgc3BhY2VyRnVuY3Rpb24oY29udGFpbmVyLCBvcmRlcmVkLCAwKVxuXG4gICAgdmFyIHBhcmVudEluZGV4QXJyYXkgPSBbXVxuICAgIGNvbnRhaW5lci5zZWxlY3RBbGwoJ2c6bm90KC50by1yZW1vdmUpLicrb2JqZWN0Q2xhc3MpXG4gICAgLmVhY2goZnVuY3Rpb24oZCwgaSl7aWYgKGhhc1EoYm94S2V5cywgZCkpeyBwYXJlbnRJbmRleEFycmF5LnB1c2goTnVtYmVyKGQzLnNlbGVjdCh0aGlzKS5hdHRyKCdwYXJlbnQtaW5kZXgnKSkpfX0pXG5cblxuXG4gICAgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24uY29sb3JCeSgpID09ICdpbmRleCdcbiAgICA/IGNvbG9yRnVuY3Rpb24uZGF0YUV4dGVudChbMCwgTWF0aC5tYXgoLi4ucGFyZW50SW5kZXhBcnJheSldKVxuICAgIDogY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KGV4dGVudClcblxuXG4gICAgLy8gc2V0IGF0dHJpYnV0ZXMgZm9yIGJveCBhbmQgd2hpc2tlcnNcbiAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnOm5vdCgudG8tcmVtb3ZlKS4nK29iamVjdENsYXNzKS5lYWNoKGZ1bmN0aW9uKGtleSwgaSkge1xuICAgICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcyksXG4gICAgICBjdXJyZW50RGF0YSA9IGRhdGFba2V5XSxcblxuICAgICAgcXVhcnRpbGVzID0gdmFsdWVFeHRyYWN0b3Ioa2V5LCBpKSxcbiAgICAgIHEwID0gcXVhcnRpbGVzW3F1YXJ0aWxlc0tleXNbMF1dLFxuICAgICAgcTEgPSBxdWFydGlsZXNbcXVhcnRpbGVzS2V5c1sxXV0sXG4gICAgICBxMiA9IHF1YXJ0aWxlc1txdWFydGlsZXNLZXlzWzJdXSxcbiAgICAgIHEzID0gcXVhcnRpbGVzW3F1YXJ0aWxlc0tleXNbM11dLFxuICAgICAgcTQgPSBxdWFydGlsZXNbcXVhcnRpbGVzS2V5c1s0XV1cblxuICAgICAgdmFyIGkgPSB0LmF0dHIoJ3BhcmVudC1pbmRleCcpID09IHVuZGVmaW5lZCA/IGkgOiB0LmF0dHIoJ3BhcmVudC1pbmRleCcpLFxuICAgICAgZmlsbENvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIHEyLCBpLCAnZmlsbCcpLCAvLyBwcmV2ZW50IGR1cGxpY2F0ZSBjb21wdXRhdGlvblxuICAgICAgc3Ryb2tlQ29sb3IgPSBjb2xvckZ1bmN0aW9uKGtleSwgcTIsIGksICAnc3Ryb2tlJylcblxuXG4gICAgICB2YXJcbiAgICAgIHdoaXNrID0gc2FmZVNlbGVjdCh0LCAnZycsICd3aGlza2VyJyksXG4gICAgICB1V2hpc2sgPSBzYWZlU2VsZWN0KHdoaXNrLCAncGF0aCcsICd1cHBlcicpLFxuICAgICAgbFdoaXNrID0gc2FmZVNlbGVjdCh3aGlzaywgJ3BhdGgnLCAnbG93ZXInKSxcbiAgICAgIHF1YXJ0ID0gc2FmZVNlbGVjdCh0LCAnZycsICdxdWFydGlsZScpLFxuICAgICAgdVF1YXJ0ID0gc2FmZVNlbGVjdChxdWFydCwgJ3JlY3QnLCAndXBwZXInKSxcbiAgICAgIGxRdWFydCA9IHNhZmVTZWxlY3QocXVhcnQsICdyZWN0JywgJ2xvd2VyJyksXG4gICAgICBtUXVhcnQgPSBzYWZlU2VsZWN0KHF1YXJ0LCAnY2lyY2xlJywgJ21lZGlhbicpXG5cblxuICAgICAgLy8gc2V0IHVwcGVyIHF1YXJ0aWxlIChxMylcbiAgICAgIHVRdWFydC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ3dpZHRoJywgaG9yaXpvbnRhbFEgPyBvYmplY3RTaXplIDogc2NhbGUocTMpIC0gc2NhbGUocTIpKVxuICAgICAgLmF0dHIoJ2hlaWdodCcsIHZlcnRpY2FsUSA/IG9iamVjdFNpemUgOiBzY2FsZShxMykgLSBzY2FsZShxMikpXG4gICAgICAuYXR0cignZmlsbCcsIGZpbGxDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2UnLCBzdHJva2VDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2Utd2lkdGgnLCBib3hTdHJva2VXaWR0aClcbiAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUSA/IDAgOiBzY2FsZShxMiksXG4gICAgICAgIHkgPSB2ZXJ0aWNhbFEgPyAwIDogc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHEzKSxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcblxuICAgICAgLy8gc2V0IGxvd2VyIHF1YXJ0aWxlIChxMSlcbiAgICAgIGxRdWFydC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ3dpZHRoJywgaG9yaXpvbnRhbFEgPyBvYmplY3RTaXplIDogc2NhbGUocTIpIC0gc2NhbGUocTEpKVxuICAgICAgLmF0dHIoJ2hlaWdodCcsIHZlcnRpY2FsUSA/IG9iamVjdFNpemUgOiBzY2FsZShxMikgLSBzY2FsZShxMSkpXG4gICAgICAuYXR0cignZmlsbCcsIGZpbGxDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2UnLCBzdHJva2VDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2Utd2lkdGgnLCBib3hTdHJva2VXaWR0aClcbiAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUSA/IDAgOiBzY2FsZShxMSksXG4gICAgICAgIHkgPSB2ZXJ0aWNhbFEgPyAwIDogc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHEyKSxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcblxuXG4gICAgICAvLyBzZXQgbWVkaWFuIChxMilcbiAgICAgIG1RdWFydC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ3InLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyIHIgPSBvYmplY3RTaXplIC8gMlxuICAgICAgICB2YXIgZGlmID0gKHNjYWxlKHEzKSAtIHNjYWxlKHExKSkgLyAyXG4gICAgICAgIHJldHVybiAociA+IGRpZikgPyBkaWYgOiByXG4gICAgICB9KVxuICAgICAgLmF0dHIoJ2ZpbGwnLCBmaWxsQ29sb3IpXG4gICAgICAuYXR0cignc3Ryb2tlJywgc3Ryb2tlQ29sb3IpXG4gICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgYm94U3Ryb2tlV2lkdGgpXG4gICAgICAuYXR0cigndHJhbnNmb3JtJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIHZhclxuICAgICAgICB4ID0gaG9yaXpvbnRhbFEgPyBvYmplY3RTaXplIC8gMiA6IHNjYWxlKHEyKSxcbiAgICAgICAgeSA9IHZlcnRpY2FsUSA/IG9iamVjdFNpemUgLyAyIDogc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHEyKSxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcblxuICAgICAgLy8gc2V0IGxvd2VyIHdoaXNrZXIgKG1pbilcbiAgICAgIGxXaGlzay50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ2QnLCBmdW5jdGlvbihkZCwgaWkpe1xuICAgICAgICB2YXJcbiAgICAgICAgZGlyID0gZmFsc2UsXG4gICAgICAgIHggPSAwLFxuICAgICAgICB5ID0gMCxcbiAgICAgICAgaCA9IGhvcml6b250YWxRID8gc2NhbGUocTEpIC0gc2NhbGUocTApIDogb2JqZWN0U2l6ZSxcbiAgICAgICAgdyA9IHZlcnRpY2FsUSA/IHNjYWxlKHExKSAtIHNjYWxlKHEwKSA6IG9iamVjdFNpemVcbiAgICAgICAgcmV0dXJuIHdoaXNrZXJQYXRoKGRpciwgeCwgeSwgdywgaCwgd2hpc2tlcldpZHRoUGVyY2VudCwgb3JpZW50KVxuICAgICAgfSlcbiAgICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBob3Jpem9udGFsUSA/IDAgOiBzY2FsZShxMSksXG4gICAgICAgIHkgPSB2ZXJ0aWNhbFEgPyAwIDogc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHExKSxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcbiAgICAgIC5hdHRyKCdzdHJva2UnLCAnYmxhY2snKS5hdHRyKCdzdHJva2Utd2lkdGgnLCB3aGlza2VyU3Ryb2tlV2lkdGgpXG4gICAgICAuYXR0cignZmlsbCcsICdub25lJylcblxuICAgICAgLy8gc2V0IHVwcGVyIHdoaXNrZXIgKG1heClcbiAgICAgIHVXaGlzay50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ2QnLCBmdW5jdGlvbihkZCwgaWkpe1xuICAgICAgICB2YXJcbiAgICAgICAgZGlyID0gdHJ1ZSxcbiAgICAgICAgeCA9IDAsXG4gICAgICAgIHkgPSAwLFxuICAgICAgICBoID0gaG9yaXpvbnRhbFEgPyBzY2FsZShxNCkgLSBzY2FsZShxMykgOiBvYmplY3RTaXplLFxuICAgICAgICB3ID0gdmVydGljYWxRID8gc2NhbGUocTQpIC0gc2NhbGUocTMpIDogb2JqZWN0U2l6ZVxuICAgICAgICByZXR1cm4gd2hpc2tlclBhdGgoZGlyLCB4LCB5LCB3LCBoLCB3aGlza2VyV2lkdGhQZXJjZW50LCBvcmllbnQpXG4gICAgICB9KVxuICAgICAgLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgICB2YXJcbiAgICAgICAgeCA9IGhvcml6b250YWxRID8gMCA6IHNjYWxlKHEzKSxcbiAgICAgICAgeSA9IHZlcnRpY2FsUSA/IDAgOiAgc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHE0KSxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcbiAgICAgIC5hdHRyKCdzdHJva2UnLCAnYmxhY2snKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIHdoaXNrZXJTdHJva2VXaWR0aClcbiAgICAgIC5hdHRyKCdmaWxsJywgJ25vbmUnKVxuXG4gICAgfSlcblxuICAgIHRvb2x0aXAuc2VsZWN0aW9uKGNvbnRhaW5lci5zZWxlY3RBbGwoJ2c6bm90KC50by1yZW1vdmUpLicrb2JqZWN0Q2xhc3MpKVxuICAgIC5kYXRhKGRhdGEpXG4gICAgdG9vbHRpcCgpXG5cblxuICB9XG5cbiAgcmV0dXJuIGJveHdoaXNrZXJcbn1cbiIsImltcG9ydCB7aHlwZW5hdGUsIHNhZmVTZWxlY3R9IGZyb20gJy4vaGVscGVycyc7XG5pbXBvcnQge3NlbGVjdEZpbHRlcn0gZnJvbSAnLi9zZWxlY3QtZmlsdGVyJztcblxuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERBVEFUT0dHTEUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qKlxuICogQ3JlYXRlcyBhIGRhdGF0b2dnbGVcbiAqIEBjb25zdHJ1Y3RvciBkYXRhdG9nZ2xlXG4gKiBAcGFyYW0ge2QzLnNlbGVjdGlvbn0gc2VsZWN0aW9uXG4gKiBAbmFtZXNwYWNlIGRhdGF0b2dnbGVcbiAqIEByZXR1cm5zIHtmdW5jdGlvbn0gZGF0YXRvZ2dsZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZGF0YXRvZ2dsZSggc2VsZWN0aW9uICkge1xuICB2YXJcbiAgLyoqXG4gICogS2V5cyB0byBtYWtlIHRvZ2dsZS1hYmxlIG9wdGlvbnNcbiAgKiAoc2VlIHtAbGluayBkYXRhdG9nZ2xlI2tleXN9KVxuICAqIEBwYXJhbSB7c3RyaW5nW119IFtrZXlzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgZGF0YXRvZ2dsZSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAga2V5cyxcbiAgLyoqXG4gICogV2hhdCB0byBkbyB3aGVuIGEgZGlmZmVyZW50IGtleSBpcyBjbGlja2VkXG4gICogKHNlZSB7QGxpbmsgZGF0YXRvZ2dsZSN1cGRhdGVGdW5jdGlvbn0pXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3VwZGF0ZUZ1bmN0aW9uPWZ1bmN0aW9uKCl7fV1cbiAgKiBAbWVtYmVyb2YgZGF0YXRvZ2dsZSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdXBkYXRlRnVuY3Rpb24gPSBmdW5jdGlvbigpe30sXG4gIC8qKlxuICAqIE5hbWVzcGFjZSBmb3IgYWxsIGl0ZW1zIG1hZGUgYnkgdGhpcyBpbnN0YW5jZSBvZiBkYXRhdG9nZ2xlXG4gICogQHBhcmFtIHtzdHJpbmd9IFtuYW1lc3BhY2U9XCJkM3NtLWRhdGFiYXJcIl1cbiAgKiBAbWVtYmVyb2YgZGF0YXRvZ2dsZSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbmFtZXNwYWNlPSdkM3NtLWRhdGFiYXInLFxuICAvKipcbiAgKiBDdXJyZW50bHkgdG9nZ2xlZCBrZXlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW2N1cnJlbnRLZXk9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBkYXRhdG9nZ2xlI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjdXJyZW50S2V5LFxuXG4gIHhBeGlzU2VsZWN0USA9IGZhbHNlLFxuICB4QXhpc09wdGlvbnMsXG4gIHlBeGlzU2VsZWN0USA9IGZhbHNlLFxuICB5QXhpc09wdGlvbnMsXG4gIGRhdGFcbiAgdG9nZ2xlLnhBeGlzU2VsZWN0USA9IGZ1bmN0aW9uKF8pe3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhBeGlzU2VsZWN0USA9IF8sIHRvZ2dsZSkgOiB4QXhpc1NlbGVjdFF9XG4gIHRvZ2dsZS55QXhpc1NlbGVjdFEgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5QXhpc1NlbGVjdFEgPSBfLCB0b2dnbGUpIDogeUF4aXNTZWxlY3RRfVxuICB0b2dnbGUueEF4aXNPcHRpb25zID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeEF4aXNPcHRpb25zID0gXywgdG9nZ2xlKSA6IHhBeGlzT3B0aW9uc31cbiAgdG9nZ2xlLnlBeGlzT3B0aW9ucyA9IGZ1bmN0aW9uKF8pe3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlBeGlzT3B0aW9ucyA9IF8sIHRvZ2dsZSkgOiB5QXhpc09wdGlvbnN9XG4gIHRvZ2dsZS5kYXRhID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGF0YSA9IF8sIHRvZ2dsZSkgOiBkYXRhfVxuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB1cGRhdGVGdW5jdGlvblxuICAgKiAoc2VlIHtAbGluayBkYXRhdG9nZ2xlI3VwZGF0ZUZ1bmN0aW9ufSlcbiAgICogQGZ1bmN0aW9uIGRhdGF0b2dnbGUudXBkYXRlRnVuY3Rpb25cbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2RhdGF0b2dnbGUgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIGRhdGF0b2dnbGVcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdXBkYXRlRnVuY3Rpb24gPSBmdW5jdGlvbigpe31cbiAgICovXG4gIHRvZ2dsZS51cGRhdGVGdW5jdGlvbiA9IGZ1bmN0aW9uKF8pe3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHVwZGF0ZUZ1bmN0aW9uID0gXywgdG9nZ2xlKSA6IHVwZGF0ZUZ1bmN0aW9ufVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG5hbWVzcGFjZVxuICAgKiAoc2VlIHtAbGluayBkYXRhdG9nZ2xlI25hbWVzcGFjZX0pXG4gICAqIEBmdW5jdGlvbiBkYXRhdG9nZ2xlLm5hbWVzcGFjZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge2RhdGF0b2dnbGUgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBkYXRhdG9nZ2xlXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG5hbWVzcGFjZSA9ICdkM3NtLWRhdGFiYXInXG4gICAqL1xuICB0b2dnbGUubmFtZXNwYWNlID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgdG9nZ2xlKSA6IG5hbWVzcGFjZX1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBjdXJyZW50S2V5XG4gICAqIChzZWUge0BsaW5rIGRhdGF0b2dnbGUjY3VycmVudEtleX0pXG4gICAqIEBmdW5jdGlvbiBkYXRhdG9nZ2xlLmN1cnJlbnRLZXlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtkYXRhdG9nZ2xlIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgZGF0YXRvZ2dsZVxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBjdXJyZW50S2V5ID0gdW5kZWZpbmVkXG4gICAqL1xuICB0b2dnbGUuY3VycmVudEtleXMgPVxuICBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHZhbHMgPSB7fVxuICAgIGQzLmtleXMoZmlsdGVyU2VsZWN0cykubWFwKGZ1bmN0aW9uKGssIGkpe1xuICAgICAgdmFsc1trXT0gZmlsdGVyU2VsZWN0c1trXS5jdXJyZW50T3B0aW9uKClcbiAgICB9KVxuICAgIHJldHVybiB2YWxzXG4gIH1cblxuICB2YXIgZmlsdGVyU2VsZWN0cyA9IHt9XG4gIGZ1bmN0aW9uIHRvZ2dsZSgpIHtcbiAgICAvLyBzZWxlY3Rpb24gb3B0aW9uc1xuXG4gICAgLy8gc2VsZWN0aW9uLmNsYXNzZWQoJ2QtZmxleCBmbGV4LXJvdycsIHRydWUpXG4gICAgLy8gdmFyIGZpbHRlckJ1dHRvbiA9IHNhZmVTZWxlY3Qoc2VsZWN0aW9uLCAnYScsICdzbGlkZXItYnV0dG9ucycpXG4gICAgLy8gdmFyIGZpbHRlckkgPSBzYWZlU2VsZWN0KGZpbHRlckJ1dHRvbiwgJ2knLCAnZmEgZmEtc2xpZGVycycpXG5cbiAgICAvKkJVRzogdW5leHBlY3RlZCBiZWhhdmlvci5cbiAgICAgIC0gd2hlbiB1c2luZyBib290c3RyYXAtZXF1ZSB3YXkgZm9yIGNvbGxhcHNlLCBjbGlja2luZyBidXR0b24gc3VibWl0cyB0b1xuICAgICAgdGhlIHNhbWUgcGFnZSBpbiBhcHBsaWNhdGlvbnMgKGJ1dCBub3QgaW4gZGVtbyksIHNvIHVzaW5nIGFuY2hvciAoPGE+KVxuICAgICAgLSB3aGVuIHVzaW5nIGFuY2hvciwgY2F1c2UgcGFnZSB0byBqdW1wIHRvIHRoYXQgbG9jYXRpb25cbiAgICAgIC0gd2hlbiB1c2luZyBzaG93LCB0aGUgZmlyc3Qgb3BlbiB3b3JrcywgYnV0IHRoZSBjbG9zZSBkb2VzIG5vdC5cbiAgICAqL1xuICAgIC8vIGZpbHRlckJ1dHRvbi5hdHRyKCdjbGFzcycsICdidG4gYnRuLXNlY29uZGFyeScpXG4gICAgLy8gLmF0dHIoJ2RhdGEtdG9nZ2xlJywgJ2NvbGxhcHNlJylcbiAgICAvLyAuYXR0cignaHJlZicsICcjJytoeXBlbmF0ZShuYW1lc3BhY2UsICdkYXRhLXRvZ2dsZScpKVxuICAgIC8vIC5hdHRyKCd0YXJnZXQnLCAnX2JsYW5rJylcbiAgICAvLyAuaHRtbChmaWx0ZXJCdXR0b24uaHRtbCgpKycgRmlsdGVycycpXG4gICAgLy8gLm9uKCdjbGljaycsIGZ1bmN0aW9uKGQsIGkpe1xuICAgIC8vICAgZDMuZXZlbnQucHJldmVudERlZmF1bHQoKVxuICAgIC8vICAgZDMuZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcbiAgICAvLyAgIHZhciBkdCA9IGQzLnNlbGVjdChcIiNcIitoeXBlbmF0ZShuYW1lc3BhY2UsICdkYXRhLXRvZ2dsZScpKVxuICAgIC8vICAgZHQuY2xhc3NlZCgnc2hvdycsICFkdC5jbGFzc2VkKCdzaG93JykpXG4gICAgLy8gICBkdC5jbGFzc2VkKCdkLWlubGluZS1mbGV4JywgZHQuY2xhc3NlZCgnc2hvdycpKVxuICAgIC8vXG4gICAgLy8gICBmaWx0ZXJCdXR0b24uY2xhc3NlZCgnYnRuLXByaW1hcnknLCBkdC5jbGFzc2VkKCdzaG93JykpXG4gICAgLy8gICBmaWx0ZXJCdXR0b24uY2xhc3NlZCgnYnRuLXNlY29uZGFyeScsICFkdC5jbGFzc2VkKCdzaG93JykpXG4gICAgLy8gfSlcbiAgICAvLyAuY2xhc3NlZCgnZC1pbmxpbmUtZmxleCcsIHRydWUpXG4gICAgLy8gLnN0eWxlKFwibWFyZ2luLXJpZ2h0XCIsICcxMHB4JylcblxuXG5cbiAgICAvLyB2YXIgZGF0YXRvZ2dsZUNvbGxhcHNlID0gc2FmZVNlbGVjdChzZWxlY3Rpb24sICdkaXYnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ2NvbGxhcHNlJykpXG4gICAgLy8gLmF0dHIoJ2lkJywgaHlwZW5hdGUobmFtZXNwYWNlLCAnZGF0YS10b2dnbGUnKSlcbiAgICAvLyAuY2xhc3NlZCgnY29sbGFwc2UnLCB0cnVlKVxuXG4gICAgLy8gdmFyIGZsZXhSb3cgPSBzYWZlU2VsZWN0KGRhdGF0b2dnbGVDb2xsYXBzZSwgJ2RpdicsICdkLWlubGluZS1mbGV4IGZsZXgtcm93IGZsZXgtd3JhcCcpXG4gICAgdmFyIGZsZXhSb3cgPSBzYWZlU2VsZWN0KHNlbGVjdGlvbiwgJ2RpdicsICdkLWlubGluZS1mbGV4IGZsZXgtcm93IGZsZXgtd3JhcCcpXG5cbiAgICB2YXIgZGF0YW9wdHMgPSBmbGV4Um93LnNlbGVjdEFsbCgnZGl2LicraHlwZW5hdGUobmFtZXNwYWNlLCdzZWxlY3QtZmlsdGVyJykpXG4gICAgLy8gcmVtb3ZlIGV4Y2Vzc1xuICAgIGRhdGFvcHRzLmV4aXQoKS5yZW1vdmUoKVxuICAgIC8vIGJpbmQgZGF0YVxuICAgIGRhdGFvcHRzID0gZGF0YW9wdHMuZGF0YShkMy5rZXlzKGRhdGEpKVxuICAgIC8vZW50ZXJcbiAgICB2YXIgZG9FbnRlciA9IGRhdGFvcHRzLmVudGVyKCkuYXBwZW5kKCdkaXYnKVxuICAgIC5hdHRyKCdjbGFzcycsICdzZWxlY3QtZmlsdGVyJylcblxuICAgIGRhdGFvcHRzID0gZGF0YW9wdHMubWVyZ2UoZG9FbnRlcikuc3R5bGUoJ21hcmdpbi1yaWdodCcsIFwiMTBweFwiKVxuXG4gICAgZGF0YW9wdHMuZWFjaChmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhciB0ID0gZDMuc2VsZWN0KHRoaXMpXG4gICAgICB2YXIgc2YgPSBzZWxlY3RGaWx0ZXIodClcbiAgICAgIC5kYXRhKGRhdGFbZF0pXG4gICAgICAubmFtZXNwYWNlKGh5cGVuYXRlKG5hbWVzcGFjZSwgZCkpXG4gICAgICAuc2VsZWN0aW9uTmFtZShkKVxuICAgICAgc2YoKVxuICAgICAgZmlsdGVyU2VsZWN0c1tkXSA9IHNmXG4gICAgfSlcblxuXG4gICAgc2VsZWN0aW9uLnNlbGVjdEFsbCgnc2VsZWN0JylcbiAgICAub24oJ2NoYW5nZScsIGZ1bmN0aW9uKCl7dXBkYXRlRnVuY3Rpb24oKX0pXG4gICAgLy8gYmluZCB1cGRhdGUgZnVuY3Rpb25cbiAgICAvLyBkMy5zZWxlY3RBbGxcbiAgICByZXR1cm4gdG9nZ2xlXG4gIH1cblxuXG4gIGZ1bmN0aW9uIG9ubHlPbmUoKSB7XG4gICAgLy8gZDMuZXZlbnQucHJldmVudERlZmF1bHQoKVxuICAgIC8vIGQzLmV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXG4gICAgdmFyIGRhdGFvcHRzID0gc2VsZWN0aW9uLnNlbGVjdEFsbCgnZGl2LmRhdGEtb3B0aW9uJylcbiAgICBjdXJyZW50S2V5ID0gZGF0YW9wdHMuc2VsZWN0KCc6Y2hlY2tlZCcpLmRhdHVtKClcbiAgICB1cGRhdGVGdW5jdGlvbigpXG5cbiAgfVxuXG4gIGZ1bmN0aW9uIGF4aXNTZWxlY3RGaWx0ZXIoc2VsZWN0aW9uLCBheGlzPVwieC1heGlzXCIsIGF4aXNEYXRhKSB7XG4gICAgaWYgKGF4aXNEYXRhID09IHVuZGVmaW5lZCkge1xuICAgICAgYXhpc0RhdGEgPSB7XG4gICAgICAgICdsaW5lYXInOiBkMy5zY2FsZUxpbmVhcigpLFxuICAgICAgICAnbG9nJzogZDMuc2NhbGVMb2coKSxcbiAgICAgICAgJ3Bvdyc6IGQzLnNjYWxlUG93KCksXG4gICAgICAgICdzcXJ0JzogZDMuc2NhbGVTcXJ0KClcbiAgICAgIH1cbiAgICB9XG5cbiAgICB2YXIgc2YgPSBzZWxlY3RGaWx0ZXIoc2VsZWN0aW9uKVxuICAgIC5kYXRhKGF4aXNEYXRhKVxuICAgIC5uYW1lc3BhY2UoYXhpcylcbiAgICAuc2VsZWN0aW9uTmFtZShheGlzKycgc2NhbGUnKVxuICAgIC5kZWZhdWx0VmFsdWUoZDMuc2NhbGVMaW5lYXIoKSlcblxuXG4gICAgc2YoKVxuXG4gIH1cblxuICByZXR1cm4gdG9nZ2xlXG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHtzZXR1cENvbnRhaW5lciwgY2FsY3VsYXRlV2lkdGhPZk9iamVjdCwgY2FsY3VsYXRlV2lkdGhPZlNwYWNlcn0gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQge3VuaXF1ZSwgZmxhdHRlbn0gZnJvbSAnLi9hcnJheS1mdW5jdGlvbnMnO1xuaW1wb3J0IHtncm91cGluZ1NwYWNlcn0gZnJvbSAnLi9ncm91cGluZy1zcGFjZXInO1xuaW1wb3J0IHtjb2xvckZ1bmN0aW9uIGFzIENGfSBmcm9tICcuL2NvbG9yLWZ1bmN0aW9uJztcbmltcG9ydCB7dG9vbHRpcCBhcyBUVGlwfSBmcm9tICcuL3Rvb2x0aXAnO1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTQ0FUVEVSICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKi9cbi8qKlxuICogQ3JlYXRlcyBhIHNjYXR0ZXJcbiAqXG4gKiB7QGxpbmsgaHR0cHM6Ly9zdW1uZXVyb24uZ2l0bGFiLmlvL2Qzc20vZGVtb3Mvc2NhdHRlci9pbmRleC5odG1sIERlbW99XG4gKiBAY29uc3RydWN0b3Igc2NhdHRlclxuICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IHNlbGVjdGlvblxuICogQG5hbWVzcGFjZSBzY2F0dGVyXG4gKiBAcmV0dXJucyB7ZnVuY3Rpb259IHNjYXR0ZXJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNjYXR0ZXIgKCBzZWxlY3Rpb24gKSB7XG5cbiAgdmFyXG4gIC8qKlxuICAqIERhdGEgdG8gcGxvdC4gQXNzdW1lZCB0byBiZSBhIG9iamVjdCwgd2hlcmUgZWFjaCBrZXkgY29ycmVzcG9uZHMgdG8gYSBwb2ludFxuICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjZGF0YX0pXG4gICogQHBhcmFtIHtPYmplY3R9IFtkYXRhPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZGF0YSxcbiAgLyoqXG4gICogQW1vdW50IG9mIGhvcml6b250YWwgc3BhY2UgKGluIHBpeGVscykgYXZhaWJsZSB0byByZW5kZXIgdGhlIHNjYXR0ZXIgaW5cbiAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3NwYWNlWH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVg9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzcGFjZVgsXG4gIC8qKlxuICAqIEFtb3VudCBvZiB2ZXJ0aWNhbCBzcGFjZSAoaW4gcGl4ZWxzKSBhdmFpYmxlIHRvIHJlbmRlciB0aGUgc2NhdHRlciBpblxuICAqIChzZWUge0BsaW5rIHNjYXR0ZXIuc3BhY2VZfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlWT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlWSxcblxuICAvKipcbiAgKiBUaGUgc2NhbGUgZm9yIHdoaWNoIHNjYXR0ZXIgeCB2YWx1ZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICogQHBhcmFtIHtkMy5zY2FsZX0gW3NjYWxlWD1kMy5zY2FsZUxpbmVhcl1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc2NhbGVYID0gZDMuc2NhbGVMaW5lYXIoKSxcbiAgLyoqXG4gICogVGhlIHBhZGRpbmcgZm9yIHRoZSBkb21haW4gb2YgdGhlIHNjYWxlWCAoc2VlIHtAbGluayBzY2F0dGVyI3NjYWxlWH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtkb21haW5QYWRkaW5nWD0wLjVdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGRvbWFpblBhZGRpbmdYID0gMC41LFxuICAvKipcbiAgKiBUaGUgZnVuY3Rpb24gZm9yIGdldHRpbmcgdGhlIHggdmFsdWUgb2YgdGhlIGN1cnJlbnQgcG9pbnRcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdmFsdWVFeHRyYWN0b3JYPWZ1bmN0aW9uKGQsIGkpe3JldHVybiBkYXRhW2RdWyd4J119XVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2YWx1ZUV4dHJhY3RvclggPSBmdW5jdGlvbihkLCBpKSB7cmV0dXJuIGRhdGFbZF1bJ3gnXX0sXG5cbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCBzY2F0dGVyIHkgdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZVk9ZDMuc2NhbGVMaW5lYXJdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNjYWxlWSA9IGQzLnNjYWxlTGluZWFyKCksXG4gIC8qKlxuICAqIFRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSBzY2FsZVkgKHNlZSB7QGxpbmsgc2NhdHRlciNzY2FsZVl9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbZG9tYWluUGFkZGluZ1k9MC41XVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkb21haW5QYWRkaW5nWSA9IDAuNSxcbiAgLyoqXG4gICogVGhlIGZ1bmN0aW9uIGZvciBnZXR0aW5nIHRoZSB5IHZhbHVlIG9mIHRoZSBjdXJyZW50IHBvaW50XG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3ZhbHVlRXh0cmFjdG9yWT1mdW5jdGlvbihkLCBpKXtyZXR1cm4gZGF0YVtkXVsneSddfV1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmFsdWVFeHRyYWN0b3JZID0gZnVuY3Rpb24oZCwgaSkge3JldHVybiBkYXRhW2RdWyd5J119LFxuXG5cbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCBzY2F0dGVyIHIgdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZVI9ZDMuc2NhbGVMaW5lYXJdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNjYWxlUiA9IGQzLnNjYWxlTGluZWFyKCksXG4gIC8qKlxuICAqIFRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSBzY2FsZVIgKHNlZSB7QGxpbmsgc2NhdHRlciNzY2FsZVJ9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbZG9tYWluUGFkZGluZ1I9MC41XVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkb21haW5QYWRkaW5nUiA9IDAuNSxcbiAgLyoqXG4gICogVGhlIGZ1bmN0aW9uIGZvciBnZXR0aW5nIHRoZSByIHZhbHVlIG9mIHRoZSBjdXJyZW50IHBvaW50XG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3ZhbHVlRXh0cmFjdG9yUj1mdW5jdGlvbihkLCBpKXtyZXR1cm4gMn1dXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHZhbHVlRXh0cmFjdG9yUiA9IGZ1bmN0aW9uKGQsIGkpIHtyZXR1cm4gMn0sXG4gIC8qKlxuICAqIFRoZSBtaW4gcmFkaXVzIGEgcG9pbnQgY2FuIGhhdmVcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbbWluUmFkaXVzPTJdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1pblJhZGl1cyA9IDIsXG4gIC8qKlxuICAqIFRoZSBtaW4gcmFkaXVzIGEgcG9pbnQgY2FuIGhhdmVcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbbWF4UmFkaXVzPTEwXVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBtYXhSYWRpdXMgPSAxMCxcblxuICAvKipcbiAgKiBUaGUgc3Ryb2tlIHdpZHRoIG9mIHRoZSBwb2ludHNcbiAgKiBAcGFyYW0ge251bWJlcn0gW3BvaW50U3Ryb2tlV2lkdGg9Ml1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgcG9pbnRTdHJva2VXaWR0aCA9IDIsXG4gIC8qKlxuICAqIEluc3RhbmNlIG9mIENvbG9yRnVuY3Rpb25cbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKV1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgY29sb3JGdW5jdGlvbiA9IENGKCksXG4gIC8qKlxuICAqIENvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kXG4gICogQHBhcmFtIHtzdHJpbmd9IFtiYWNrZ3JvdW5kRmlsbD1cInRyYW5zcGFyZW50XCJdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJhY2tncm91bmRGaWxsID0gJ3RyYW5zcGFyZW50JyxcbiAgLyoqXG4gICogTmFtZXNwYWNlIGZvciBhbGwgaXRlbXMgbWFkZSBieSB0aGlzIGluc3RhbmNlIG9mIHNjYXR0ZXJcbiAgKiBAcGFyYW0ge3N0cmluZ30gW25hbWVzcGFjZT1cImQzc20tc2NhdHRlclwiXVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBuYW1lc3BhY2UgPSAnZDNzbS1zY2F0dGVyJyxcbiAgLyoqXG4gICogQ2xhc3MgbmFtZSBmb3Igc2NhdHRlciBjb250YWluZXIgKDxjaXJjbGU+IGVsZW1lbnQpXG4gICogQHBhcmFtIHtzdHJpbmd9IFtvYmplY3RDbGFzcz1cInNjYXR0ZXItcG9pbnRcIl1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0Q2xhc3MgPSAnc2NhdHRlci1wb2ludCcsXG4gIC8qKlxuICAqIER1cmF0aW9uIG9mIGFsbCB0cmFuc2l0aW9ucyBvZiB0aGlzIGVsZW1lbnRcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RyYW5zaXRpb25EdXJhdGlvbj0xMDAwXVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwLFxuICAvKipcbiAgKiBFYXNpbmcgZnVuY3Rpb24gZm9yIHRyYW5zaXRpb25zXG4gICogQHBhcmFtIHtkMy5lYXNlfSBbZWFzZUZ1bmM9ZDMuZWFzZUV4cF1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwLFxuXG4gIC8vIHVzZWZ1bCB2YWx1ZXMgdG8gZXh0cmFjdCB0byBwcmV2ZW50IHJlLWNhbGN1bGF0aW9uXG4gIC8qKlxuICAqIFRoZSBrZXlzIG9mIHRoZSBwb2ludHNcbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbcG9pbnRLZXlzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgcG9pbnRLZXlzLFxuICAvKipcbiAgKiBUaGUgeCB2YWx1ZXMgb2YgdGhlIHBvaW50c1xuICAqIEBwYXJhbSB7bnVtYmVyW119IFt2YWx1ZXNYPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2Ygc2NhdHRlciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmFsdWVzWCxcbiAgLyoqXG4gICogVGhlIHkgdmFsdWVzIG9mIHRoZSBwb2ludHNcbiAgKiBAcGFyYW0ge251bWJlcltdfSBbdmFsdWVzWT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHZhbHVlc1ksXG4gIC8qKlxuICAqIFRoZSByIHZhbHVlcyBvZiB0aGUgcG9pbnRzXG4gICogQHBhcmFtIHtudW1iZXJbXX0gW3ZhbHVlc1I9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiBzY2F0dGVyI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2YWx1ZXNSLFxuXG4gIC8qKlxuICAqIEluc3RhbmNlIG9mIFRvb2x0aXBcbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbdG9vbHRpcD10b29sdGlwKCldXG4gICogQG1lbWJlcm9mIHNjYXR0ZXIjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRvb2x0aXAgPSBUVGlwKClcblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBzZWxlY3Rpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IGQzLnNlbGVjdGlvbn1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2VsZWN0aW9uID0gc2VsZWN0aW9uXG4gICAqL1xuICBzY2F0dGVyLnNlbGVjdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2VsZWN0aW9uID1fLCBzY2F0dGVyKSA6IHNlbGVjdGlvbn1cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZGF0YVxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI2RhdGF9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBvYmplY3R9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgc2NhdHRlci5kYXRhID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkYXRhID1fLCBzY2F0dGVyKSA6IGRhdGF9XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3NwYWNlWH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVggPSB1bmRlZmluZWRcbiAgICovXG4gIHNjYXR0ZXIuc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVggPV8sIHNjYXR0ZXIpIDogc3BhY2VYfVxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBhbW91bnQgb2YgdmVydGljYWwgc3BhY2UgaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjc3BhY2VZfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlWSA9IHVuZGVmaW5lZFxuICAgKi9cbiAgc2NhdHRlci5zcGFjZVkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWSA9Xywgc2NhdHRlcikgOiBzcGFjZVl9XG5cblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgeCBzY2FsZSBmb3Igd2hpY2ggdGhlIHNjYXR0ZXIgeCB2YWx1ZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjc2NhbGVYfSlcbiAgICogQHBhcmFtIHtkMy5zY2FsZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBkMy5zY2FsZX1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2NhbGVYID0gZDMuc2NhbGVMaW5lYXIoKVxuICAgKi9cbiAgc2NhdHRlci5zY2FsZVggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNjYWxlWCA9Xywgc2NhdHRlcikgOiBzY2FsZVh9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgeCBzY2FsZVxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI2RvbWFpblBhZGRpbmdYfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBkb21haW5QYWRkaW5nWCA9IDAuNVxuICAgKi9cbiAgc2NhdHRlci5kb21haW5QYWRkaW5nWCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZG9tYWluUGFkZGluZ1ggPV8sIHNjYXR0ZXIpIDogZG9tYWluUGFkZGluZ1h9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdmFsdWVFeHRyYWN0b3JYXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjdmFsdWVFeHRyYWN0b3JYfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdmFsdWVFeHRyYWN0b3JYID0gZnVuY3Rpb24oa2V5LCBpbmRleCkgeyByZXR1cm4gZGF0YVtrZXldWyd4J10gfVxuICAgKi9cbiAgc2NhdHRlci52YWx1ZUV4dHJhY3RvclggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZhbHVlRXh0cmFjdG9yWCA9Xywgc2NhdHRlcikgOiB2YWx1ZUV4dHJhY3Rvclh9XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHkgc2NhbGUgZm9yIHdoaWNoIHRoZSBzY2F0dGVyIHkgdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3NjYWxlWX0pXG4gICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgZDMuc2NhbGV9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNjYWxlWSA9IGQzLnNjYWxlTGluZWFyKClcbiAgICovXG4gIHNjYXR0ZXIuc2NhbGVZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzY2FsZVkgPV8sIHNjYXR0ZXIpIDogc2NhbGVZfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHBhZGRpbmcgZm9yIHRoZSBkb21haW4gb2YgdGhlIHkgc2NhbGVcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNkb21haW5QYWRkaW5nWX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZG9tYWluUGFkZGluZ1kgPSAwLjVcbiAgICovXG4gIHNjYXR0ZXIuZG9tYWluUGFkZGluZ1kgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRvbWFpblBhZGRpbmdZID1fLCBzY2F0dGVyKSA6IGRvbWFpblBhZGRpbmdZfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHZhbHVlRXh0cmFjdG9yWVxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3ZhbHVlRXh0cmFjdG9yWX0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZhbHVlRXh0cmFjdG9yWSA9IGZ1bmN0aW9uKGtleSwgaW5kZXgpIHsgcmV0dXJuIGRhdGFba2V5XVsneSddIH1cbiAgICovXG4gIHNjYXR0ZXIudmFsdWVFeHRyYWN0b3JZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZUV4dHJhY3RvclkgPV8sIHNjYXR0ZXIpIDogdmFsdWVFeHRyYWN0b3JZfVxuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSByIHNjYWxlIGZvciB3aGljaCB0aGUgc2NhdHRlciByIHZhbHVlcyBzaG91bGQgYmUgdHJhbnNmb3JtZWQgYnlcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNzY2FsZVJ9KVxuICAgKiBAcGFyYW0ge2QzLnNjYWxlfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IGQzLnNjYWxlfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzY2FsZVIgPSBkMy5zY2FsZUxpbmVhcigpXG4gICAqL1xuICBzY2F0dGVyLnNjYWxlUiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGVSID1fLCBzY2F0dGVyKSA6IHNjYWxlUn1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSByIHNjYWxlXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjZG9tYWluUGFkZGluZ1J9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGRvbWFpblBhZGRpbmdSID0gMC41XG4gICAqL1xuICBzY2F0dGVyLmRvbWFpblBhZGRpbmdSID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkb21haW5QYWRkaW5nUiA9Xywgc2NhdHRlcikgOiBkb21haW5QYWRkaW5nUn1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2YWx1ZUV4dHJhY3RvclJcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciN2YWx1ZUV4dHJhY3RvclJ9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2YWx1ZUV4dHJhY3RvclIgPSBmdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV1bJ3InXSB9XG4gICAqL1xuICBzY2F0dGVyLnZhbHVlRXh0cmFjdG9yUiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodmFsdWVFeHRyYWN0b3JSID1fLCBzY2F0dGVyKSA6IHZhbHVlRXh0cmFjdG9yUn1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtaW5SYWRpdXNcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNtaW5SYWRpdXN9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1pblJhZGl1cyA9IDJcbiAgICovXG4gIHNjYXR0ZXIubWluUmFkaXVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtaW5SYWRpdXMgPV8sIHNjYXR0ZXIpIDogbWluUmFkaXVzfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1heFJhZGl1c1xuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI21heFJhZGl1c30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWF4UmFkaXVzID0gMTBcbiAgICovXG4gIHNjYXR0ZXIubWF4UmFkaXVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtYXhSYWRpdXMgPV8sIHNjYXR0ZXIpIDogbWF4UmFkaXVzfVxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcG9pbnRTdHJva2VXaWR0aFxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3BvaW50U3Ryb2tlV2lkdGh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHBvaW50U3Ryb2tlV2lkdGggPSAyXG4gICAqL1xuICBzY2F0dGVyLnBvaW50U3Ryb2tlV2lkdGggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHBvaW50U3Ryb2tlV2lkdGggPV8sIHNjYXR0ZXIpIDogcG9pbnRTdHJva2VXaWR0aH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBjb2xvckZ1bmN0aW9uXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjY29sb3JGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKVxuICAgKi9cbiAgc2NhdHRlci5jb2xvckZ1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjb2xvckZ1bmN0aW9uID1fLCBzY2F0dGVyKSA6IGNvbG9yRnVuY3Rpb259XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFja2dyb3VuZEZpbGxcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNiYWNrZ3JvdW5kRmlsbH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnXG4gICAqL1xuICBzY2F0dGVyLmJhY2tncm91bmRGaWxsID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChiYWNrZ3JvdW5kRmlsbCA9Xywgc2NhdHRlcikgOiBiYWNrZ3JvdW5kRmlsbH1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBuYW1lc3BhY2VcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNuYW1lc3BhY2V9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG5hbWVzcGFjZSA9ICdkM3NtLXNjYXR0ZXInXG4gICAqL1xuICBzY2F0dGVyLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID1fLCBzY2F0dGVyKSA6IG5hbWVzcGFjZX1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RDbGFzc1xuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI29iamVjdENsYXNzfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RDbGFzcyA9ICd0aWNrLWdyb3VwJ1xuICAgKi9cbiAgc2NhdHRlci5vYmplY3RDbGFzcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3MgPV8sIHNjYXR0ZXIpIDogb2JqZWN0Q2xhc3N9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdHJhbnNpdGlvbkR1cmF0aW9uXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjdHJhbnNpdGlvbkR1cmF0aW9ufSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwXG4gICAqL1xuICBzY2F0dGVyLnRyYW5zaXRpb25EdXJhdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodHJhbnNpdGlvbkR1cmF0aW9uID1fLCBzY2F0dGVyKSA6IHRyYW5zaXRpb25EdXJhdGlvbn1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBlYXNlRnVuY1xuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI2Vhc2VGdW5jfSlcbiAgICogQHBhcmFtIHtkMy5lYXNlfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IGQzLmVhc2V9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGVhc2VGdW5jID0gZDMuZWFzZUV4cFxuICAgKi9cbiAgc2NhdHRlci5lYXNlRnVuYyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZWFzZUZ1bmMgPV8sIHNjYXR0ZXIpIDogZWFzZUZ1bmN9XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBwb2ludEtleXNcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciNwb2ludEtleXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBwb2ludEtleXMgPSB1bmRlZmluZWRcbiAgICovXG4gIHNjYXR0ZXIucG9pbnRLZXlzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChwb2ludEtleXMgPV8sIHNjYXR0ZXIpIDogcG9pbnRLZXlzfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHZhbHVlc1hcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciN2YWx1ZXNYfSlcbiAgICogQHBhcmFtIHtudW1iZXJbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge3NjYXR0ZXIgfCBudW1iZXJbXX1cbiAgICogQG1lbWJlcm9mIHNjYXR0ZXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdmFsdWVzWCA9IHVuZGVmaW5lZFxuICAgKi9cbiAgc2NhdHRlci52YWx1ZXNYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZXNYID1fLCBzY2F0dGVyKSA6IHZhbHVlc1h9XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdmFsdWVzWVxuICAgKiAoc2VlIHtAbGluayBzY2F0dGVyI3ZhbHVlc1l9KVxuICAgKiBAcGFyYW0ge251bWJlcltdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IG51bWJlcltdfVxuICAgKiBAbWVtYmVyb2Ygc2NhdHRlclxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2YWx1ZXNZID0gdW5kZWZpbmVkXG4gICAqL1xuICBzY2F0dGVyLnZhbHVlc1kgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZhbHVlc1kgPV8sIHNjYXR0ZXIpIDogdmFsdWVzWX1cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2YWx1ZXNSXG4gICAqIChzZWUge0BsaW5rIHNjYXR0ZXIjdmFsdWVzUn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyW119IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtzY2F0dGVyIHwgbnVtYmVyW119XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZhbHVlc1IgPSB1bmRlZmluZWRcbiAgICovXG4gIHNjYXR0ZXIudmFsdWVzUiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodmFsdWVzUiA9Xywgc2NhdHRlcikgOiB2YWx1ZXNSfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHRvb2x0aXBcbiAgICogKHNlZSB7QGxpbmsgc2NhdHRlciN0b29sdGlwfSlcbiAgICogQHBhcmFtIHt0b29sdGlwfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7c2NhdHRlciB8IHRvb2x0aXB9XG4gICAqIEBtZW1iZXJvZiBzY2F0dGVyXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHRvb2x0aXAgPSB0b29sdGlwKClcbiAgICovXG5cbiAgc2NhdHRlci50b29sdGlwID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0b29sdGlwID1fLCBzY2F0dGVyKSA6IHRvb2x0aXB9XG5cblxuICBmdW5jdGlvbiBzY2F0dGVyKCkge1xuICAgIC8vIGJhY2tncm91bmQgY2xpcGluZyByZWN0YW5nbGVcbiAgICB2YXIgYmdjcFJlY3QgPSB7eDowLCB5OjAsIHdpZHRoOiBzcGFjZVgsIGhlaWdodDpzcGFjZVl9XG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cblxuICAgIHBvaW50S2V5cyA9IGQzLmtleXMoZGF0YSlcbiAgICB2YWx1ZXNYID0gcG9pbnRLZXlzLm1hcCh2YWx1ZUV4dHJhY3RvclgpXG4gICAgdmFsdWVzWSA9IHBvaW50S2V5cy5tYXAodmFsdWVFeHRyYWN0b3JZKVxuICAgIHZhbHVlc1IgPSBwb2ludEtleXMubWFwKHZhbHVlRXh0cmFjdG9yUilcblxuICAgIHZhciBudW1iZXJPZk9iamVjdHMgPSBwb2ludEtleXMubGVuZ3RoXG4gICAgdmFyIGV4dGVudFggPSBbTWF0aC5taW4oLi4udmFsdWVzWCkgLSBkb21haW5QYWRkaW5nWCwgTWF0aC5tYXgoLi4udmFsdWVzWCkgKyBkb21haW5QYWRkaW5nWF1cbiAgICB2YXIgZXh0ZW50WSA9IFtNYXRoLm1pbiguLi52YWx1ZXNZKSAtIGRvbWFpblBhZGRpbmdZLCBNYXRoLm1heCguLi52YWx1ZXNZKSArIGRvbWFpblBhZGRpbmdZXVxuICAgIHZhciBleHRlbnRSID0gW01hdGgubWluKC4uLnZhbHVlc1IpIC0gZG9tYWluUGFkZGluZ1IsIE1hdGgubWF4KC4uLnZhbHVlc1IpICsgZG9tYWluUGFkZGluZ1JdXG5cbiAgICBzY2FsZVguZG9tYWluKGV4dGVudFgpLnJhbmdlKFswLCBzcGFjZVhdKVxuICAgIHNjYWxlWS5kb21haW4oZXh0ZW50WSkucmFuZ2UoW3NwYWNlWSwgMF0pXG4gICAgc2NhbGVSLmRvbWFpbihleHRlbnRSKS5yYW5nZShbbWluUmFkaXVzLCBtYXhSYWRpdXNdKVxuXG4gICAgdmFyIHBvaW50cyA9IGNvbnRhaW5lci5zZWxlY3RBbGwoJy4nK29iamVjdENsYXNzKVxuICAgIHBvaW50cyA9IHBvaW50cy5kYXRhKHBvaW50S2V5cylcbiAgICB2YXIgcEVudGVyID0gcG9pbnRzLmVudGVyKCkuYXBwZW5kKCdjaXJjbGUnKVxuICAgIC5hdHRyKCdjbGFzcycsIG9iamVjdENsYXNzKVxuICAgIC5hdHRyKCdjeCcsIDApLmF0dHIoJ2N5Jywgc3BhY2VZKS5hdHRyKCdyJywgMClcblxuICAgIHZhciBwRXhpdCA9IHBvaW50cy5leGl0KClcblxuICAgIHBvaW50cyA9IHBvaW50cy5tZXJnZShwRW50ZXIpXG5cbiAgICBwb2ludHMuZWFjaChmdW5jdGlvbihrZXksIGkpe1xuICAgICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcyksXG4gICAgICBjdXJyZW50RGF0YSA9IGRhdGFba2V5XSxcbiAgICAgIHggPSB2YWx1ZXNYW2ldLFxuICAgICAgeSA9IHZhbHVlc1lbaV0sXG4gICAgICByID0gdmFsdWVzUltpXSxcbiAgICAgIGZpbGxDb2xvciA9IGNvbG9yRnVuY3Rpb24oa2V5LCBjdXJyZW50RGF0YSwgaSwgJ2ZpbGwnKSxcbiAgICAgIHN0cm9rZUNvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIGN1cnJlbnREYXRhLCBpLCAnc3Ryb2tlJylcblxuICAgICAgdC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgLmF0dHIoJ2N4Jywgc2NhbGVYKHgpKVxuICAgICAgLmF0dHIoJ2N5Jywgc2NhbGVZKHkpKVxuICAgICAgLmF0dHIoJ3InLCBzY2FsZVIocikpXG4gICAgICAuYXR0cignZmlsbCcsIGZpbGxDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2UnLCBzdHJva2VDb2xvcilcbiAgICAgIC5hdHRyKCdzdHJva2Utd2lkdGgnLCBwb2ludFN0cm9rZVdpZHRoKVxuXG5cblxuICAgICAgdC5vbignbW91c2VvdmVyJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIHBvaW50cy5zdHlsZSgnb3BhY2l0eScsIDAuMilcbiAgICAgICAgdC5zdHlsZSgnb3BhY2l0eScsIDEpXG4gICAgICAgIHQudHJhbnNpdGlvbigpLmR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbi8yKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgcG9pbnRTdHJva2VXaWR0aCoyKVxuICAgICAgICAuYXR0cigncicsIHNjYWxlUihyKSAqIDEuNSlcblxuICAgICAgfSlcbiAgICAgIHQubm9kZSgpLmFkZEV2ZW50TGlzdGVuZXIoJ21vdXNlb3V0JywgZnVuY3Rpb24oKXtcbiAgICAgICAgY29udGFpbmVyLnNlbGVjdEFsbCgnLicrb2JqZWN0Q2xhc3MpLnN0eWxlKCdvcGFjaXR5JywgMSlcbiAgICAgICAgdC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uLzIpLmVhc2UoZWFzZUZ1bmMpXG4gICAgICAgIC5hdHRyKCdzdHJva2Utd2lkdGgnLCBwb2ludFN0cm9rZVdpZHRoKVxuICAgICAgICAuYXR0cigncicsIHNjYWxlUihyKSlcblxuICAgICAgfSlcblxuICAgIH0pXG5cblxuXG4gICAgcEV4aXQudHJhbnNpdGlvbigpLmR1cmF0aW9uKHRyYW5zaXRpb25EdXJhdGlvbikuZWFzZShlYXNlRnVuYylcbiAgICAuYXR0cignY3gnLCAwKS5hdHRyKCdjeScsIHNwYWNlWSkuYXR0cigncicsIDApXG4gICAgLnJlbW92ZSgpXG5cbiAgICB0b29sdGlwLnNlbGVjdGlvbihwb2ludHMpXG4gICAgLmRhdGEoZGF0YSlcblxuICAgIHRvb2x0aXAoKVxuICB9XG5cblxuICByZXR1cm4gc2NhdHRlclxufVxuIiwiaW1wb3J0IHtoeXBlbmF0ZSwgc2FmZVNlbGVjdCwgZ2V0VHJhbnNsYXRpb259IGZyb20gJy4vaGVscGVycyc7XG5pbXBvcnQge2xvZywgd2FybiwgZXJyb3IsIGluZm99IGZyb20gJy4vdXRpbHMnO1xuLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKipcblxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUExPVFpPT00gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqL1xuLyoqXG4gKiBDcmVhdGVzIGFuIHBsb3Rab29tIGluc3RhbmNlLCB3aGljaCBjYW4gaGFuZGxlIGRyYWcgYW5kIHNjcm9sbCBldmVudHNcbiAqIEBjb25zdHJ1Y3RvciBwbG90Wm9vbVxuICogQHBhcmFtIHtmdW5jdGlvbn0gY2hhcnQgYSBmdW5jdGlvbiBpbnN0YW5jZSBvZiBvbmUgb2YgdGhlIGQzc20gcGxvdHMgKGUuZy4gYmFyLCBib3h3aGlza2VyLCBidWJibGVIZWF0bWFwLCB2aW9saW4sIGV0YylcbiAqIEBwYXJhbSB7YXhpc30gIHhBeGlzIHRoZSBheGlzIGluc3RhbmNlIHJlc3BvbnNpYmxlIGZvciB0aGUgeCBheGlzXG4gKiBAcGFyYW0ge2F4aXN9IHlBeGlzIHRoZSBheGlzIGluc3RhbmNlIHJlc3BvbnNpYmxlIGZvciB0aGUgeSBheGlzXG4gKiBAbmFtZXNwYWNlIHBsb3Rab29tXG4gKiBAcmV0dXJucyB7ZnVuY3Rpb259IHpvb21cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBsb3Rab29tKCBjaGFydCwgeEF4aXMsIHlBeGlzICkge1xuICB2YXJcbiAgLyoqXG4gICogVGhlIGV2ZW50IG9uIHdoaWNoIHRvIGZpcmVcbiAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSNldmVudFR5cGV9KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBldmVudFR5cGUgd2hpY2ggZXZlbnQgaXQgc2hvdWxkIGhhbmRsZS4gQ3VycmVudGx5IHN1cHBvcnRzIHNjcm9sbCBhbmQgd2hlZWxcbiAgKiBAbWVtYmVyb2YgcGxvdFpvb20jXG4gICogQHByb3BlcnR5XG4gICovXG4gIGV2ZW50VHlwZSxcbiAgLyoqXG4gICogQSBzY2FsaW5nIGZhY3RvciBmb3IgdGhlIHdoZWVsIFwic3BlZWRcIlxuICAqIChzZWUge0BsaW5rIHBsb3Rab29tI3doZWVsU3BlZWR9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbd2hlZWxTcGVlZD0yMF0gc2NhbGVzIHRoZSB3aGVlbCB0cmFuc2xhdGlvbiBieSB3aGVlbFNwZWVkXG4gICogQG1lbWJlcm9mIHBsb3Rab29tI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB3aGVlbFNwZWVkID0gMjAsXG4gIC8qKlxuICAqIFRoZSBvcmllbnRhdGlvbiBpbiB3aGljaCB0byBhbGxvdyBzY3JvbGxpbmc6ICdob3Jpem9udGFsJywgJ3ZlcnRpY2FsJywgb3IgJzJEJ1xuICAqIChzZWUge0BsaW5rIHBsb3Rab29tI29yaWVudH0pXG4gICogQHBhcmFtIHtzdHJpbmd9IFtvcmllbnQ9Y2hhcnQub3JpZW50KCkgfHwgJ2hvcml6b250YWwnXVxuICAqIEBtZW1iZXJvZiBwbG90Wm9vbSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb3JpZW50ID0gKGNoYXJ0Lm9yaWVudCA9PSB1bmRlZmluZWQpID8gJ2hvcml6b250YWwnIDogY2hhcnQub3JpZW50KCksXG4gIC8qKlxuICAqIFRoZSBtYXggZGlzdGFuY2UgYWxsb3dlZCB0byBzY3JvbGwgaW4gdGhlIHggZGlyZWN0aW9uXG4gICogKHNlZSB7QGxpbmsgcGxvdFpvb20jeExvY2t9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbeExvY2s9Y2hhcnQuc3BhY2VYKCldIGlkZWFsbHkgY2hhcnQub3ZlcmZsb3dRKCkgPT0gdHJ1ZSBhbmQgdGhpcyB2YWx1ZSBpcyB0aGVcbiAgKiBib3VuZGluZyByZWN0IGFjcm9zcyBhbGwgZWxlbWVudHMgaW4gdGhlIGNoYXJ0ICBtaW51cyB0aGUgc3BhY2UgaW4gd2hpY2ggdG8gc2hvdy5cbiAgKiBAbWVtYmVyb2YgcGxvdFpvb20jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHhMb2NrPWNoYXJ0LnNwYWNlWCgpLFxuICAvKipcbiAgKiBUaGUgbWF4IGRpc3RhbmNlIGFsbG93ZWQgdG8gc2Nyb2xsIGluIHRoZSB5IGRpcmVjdGlvblxuICAqIChzZWUge0BsaW5rIHBsb3Rab29tI3lMb2NrfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3lMb2NrPWNoYXJ0LnNwYWNlWSgpXSBpZGVhbGx5IGNoYXJ0Lm92ZXJmbG93USgpID09IHRydWUgYW5kIHRoaXMgdmFsdWUgaXMgdGhlXG4gICogYm91bmRpbmcgcmVjdCBhY3Jvc3MgYWxsIGVsZW1lbnRzIGluIHRoZSBjaGFydCAgbWludXMgdGhlIHNwYWNlIGluIHdoaWNoIHRvIHNob3cuXG4gICogQG1lbWJlcm9mIHBsb3Rab29tI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB5TG9jaz1jaGFydC5zcGFjZVkoKSxcblxuICBjaGFydFNlbCA9IGNoYXJ0LnNlbGVjdGlvbigpLFxuICB4QXhpc1NlbCA9IHhBeGlzLnNlbGVjdGlvbigpLFxuICB5QXhpc1NlbCA9IHlBeGlzLnNlbGVjdGlvbigpLFxuICBzdmcgPSBkMy5zZWxlY3QoY2hhcnRTZWwudGhpc1NWRygpKVxuXG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZXZlbnQgdHlwZSBpbiB3aGljaCB0byByZXNwb25kXG4gICAqIChzZWUge0BsaW5rIHBsb3Rab29tI2V2ZW50VHlwZX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXSBzaG91bGQgYmUgJ2RyYWcnIG9yICd3aGVlbCdcbiAgICogQHJldHVybnMge3pvb20gfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBwbG90Wm9vbVxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBwbG90Wm9vbT11bmRlZmluZWRcbiAgICovXG4gIHpvb20uZXZlbnRUeXBlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChldmVudFR5cGUgPSBfLCB6b29tKSA6IGV2ZW50VHlwZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgd2hlZWwgc3BlZWQgaW4gd2hpY2ggdG8gc2NhbGUgdGhlIHdoZWVsIHNjcm9sbCB0cmFuc2Zvcm1cbiAgICogKHNlZSB7QGxpbmsgcGxvdFpvb20jd2hlZWxTcGVlZH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7em9vbSB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHBsb3Rab29tXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHdoZWVsU3BlZWQ9MjBcbiAgICovXG4gIHpvb20ud2hlZWxTcGVlZCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAod2hlZWxTcGVlZCA9IF8sIHpvb20pIDogd2hlZWxTcGVlZDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgb3JpZW50YXRpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIHBsb3Rab29tI29yaWVudH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXSBzaG91bGQgYmUgaG9yaXpvbnRhbCwgdmVydGljYWwsIG9yIDJEXG4gICAqIEByZXR1cm5zIHt6b29tIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgcGxvdFpvb21cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3JpZW50PWNoYXJ0Lm9yaWVudCgpIHx8ICdob3Jpem9udGFsJ1xuICAgKi9cbiAgem9vbS5vcmllbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9yaWVudCA9IF8sIHpvb20pIDogb3JpZW50OyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIG1heCBkaXN0YW5jZSBpbiB3aGljaCB0byBzY3JvbGwgWFxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN4TG9ja30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBwb3NpdGl2ZSB2YWx1ZVxuICAgKiBAcmV0dXJucyB7em9vbSB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHBsb3Rab29tXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhMb2NrPWNoYXJ0LnNwYWNlWCgpXG4gICAqL1xuICB6b29tLnhMb2NrID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4TG9jayA9IF8sIHpvb20pIDogeExvY2s7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIG1heCBkaXN0YW5jZSBpbiB3aGljaCB0byBzY3JvbGwgWVxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN5TG9ja30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSAgc2hvdWxkIGJlIGEgcG9zaXRpdmUgdmFsdWVcbiAgICogQHJldHVybnMge3pvb20gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBwbG90Wm9vbVxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5TG9jaz1jaGFydC5zcGFjZVkoKVxuICAgKi9cbiAgem9vbS55TG9jayA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeUxvY2sgPSBfLCB6b29tKSA6IHlMb2NrOyB9O1xuXG5cbiAgZnVuY3Rpb24gc2V0TG9ja3MoKSB7XG4gICAgdmFyIGNoYXJ0T2JqU2VsID0gY2hhcnRTZWwuc2VsZWN0KCcuJytoeXBlbmF0ZShjaGFydC5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuICAgIHZhciBjaGFydE9ialRyYW5zID0gZ2V0VHJhbnNsYXRpb24oY2hhcnRPYmpTZWwuYXR0cigndHJhbnNmb3JtJykpXG4gICAgdmFyIGNvcyA9IGNoYXJ0T2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoMCwwKScpXG4gICAgeExvY2sgPSBjaGFydFNlbC5ub2RlKCkuZ2V0QkJveCgpLndpZHRoIC0gY2hhcnQuc3BhY2VYKCkgKiAuOVxuICAgIHlMb2NrID0gY2hhcnRTZWwubm9kZSgpLmdldEJCb3goKS5oZWlnaHQgLSBjaGFydC5zcGFjZVkoKSAqIC45XG4gICAgY29zLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoJytjaGFydE9ialRyYW5zWzBdKycsJytjaGFydE9ialRyYW5zWzFdKycpJylcbiAgICBsb2coJ3Bsb3Rab29tJywgJ3NldExvY2tzJywge3hMb2NrOnhMb2NrLCB5TG9jazp5TG9ja30pXG4gIH1cblxuXG4gIC8qKlxuICAgKiBTZXRzIHRoZSB4IGFuZCB5IGxvY2tzIChob3cgZmFyIG9uZSBjYW4gc2Nyb2xsKVxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN4TG9ja30gYW5kIHtAbGluayBwbG90Wm9vbSN5TG9ja30pXG4gICAqIEBmdW5jdGlvbiBwbG90Wm9vbS5zZXRMb2Nrc1xuICAgKiBAcmV0dXJucyB7dW5kZWZpbmVkfVxuICAgKiBAbWVtYmVyb2YgcGxvdFpvb21cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICB6b29tLnNldExvY2tzID0gc2V0TG9ja3NcblxuICBmdW5jdGlvbiB6b29tKCkge1xuICAgIHNldExvY2tzKClcblxuICAgIHZhciBob3Jpem9udGFsUSwgdmVydGljYWxRXG4gICAgaWYgKG9yaWVudCA9PSAnMkQnKSB7aG9yaXpvbnRhbFEgPSB0cnVlOyB2ZXJ0aWNhbFEgPSB0cnVlO31cbiAgICBpZiAob3JpZW50ID09ICdob3Jpem9udGFsJykge2hvcml6b250YWxRID0gdHJ1ZTsgdmVydGljYWxRID0gZmFsc2U7fVxuICAgIGlmIChvcmllbnQgPT0gJ3ZlcnRpY2FsJykge3ZlcnRpY2FsUSA9IHRydWU7IGhvcml6b250YWxRID0gZmFsc2U7fVxuXG4gICAgLy8gY2FwdHVyZSB0cmFuc2Zvcm0gZXZlbnRcbiAgICB2YXIgdHJhbnNmb3JtID0gZDMuZXZlbnQudHJhbnNmb3JtXG5cbiAgICB2YXIgY2hhcnRCb3ggPSBjaGFydFNlbC5ub2RlKCkuZ2V0QkJveCgpXG4gICAgdmFyIHhBeGlzQm94ID0geEF4aXNTZWwubm9kZSgpLmdldEJCb3goKVxuICAgIHZhciB5QXhpc0JveCA9IHhBeGlzU2VsLm5vZGUoKS5nZXRCQm94KClcblxuICAgIHZhciBjaGFydFdpZHRoID0gY2hhcnRCb3gud2lkdGggLSBjaGFydEJveC54XG4gICAgdmFyIGNoYXJ0SGVpZ2h0ID0gY2hhcnRCb3guaGVpZ2h0IC0gY2hhcnRCb3gueVxuICAgIHZhciB4QXhpc1dpZHRoID0geEF4aXNCb3gud2lkdGgvLyAtIHhBeGlzQm94LnhcbiAgICB2YXIgeEF4aXNIZWlnaHQgPSB4QXhpc0JveC5oZWlnaHQvLyAtIHhBeGlzQm94LnlcbiAgICB2YXIgeUF4aXNXaWR0aCA9IHlBeGlzQm94LndpZHRoLy8gLSB5QXhpc0JveC54XG4gICAgdmFyIHlBeGlzSGVpZ2h0ID0geUF4aXNCb3guaGVpZ2h0Ly8gLXlBeGlzQm94LnlcblxuICAgIC8vIGVuYWJsZSB3aGVlbCBldmVudFxuICAgIGlmIChldmVudFR5cGUgPT0gXCJ3aGVlbFwiKSB7XG4gICAgICB2YXIgZSA9IGQzLmV2ZW50XG4gICAgICAvLyBwcmV2ZW50IHBhZ2Ugc2Nyb2xsaW5nXG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIC8vIGV2ZW50IGRlbHRhIGlzIHZlcnkgdmVyeSBzbG93LCBzbyBzcGVlZCBpdCB1cCB3aXRoIHdoZWVsIHNwZWVkXG4gICAgICB2YXIgdyA9IGQzLmV2ZW50LmRlbHRhWSAqIHdoZWVsU3BlZWRcbiAgICAgIHZhciBzaGlmdFEgPSBkMy5ldmVudC5zaGlmdEtleVxuXG4gICAgICAvLyBkMyBoYXMgbm8gd2F5IHRvIG1ha2UgY3VzdG9tIHRyYW5zZm9ybSwgc28gbWFrZSBhbiBvYmplY3QgYW5kIGFkZFxuICAgICAgLy8gdGhlIG5lY2Vzc2FyeSBmdW5jdGlvbnMgdG8gbWFrZSB3aGVlbCBldmVudCBjb21wYXRpYmxlIHdpdGggZHJhZyBldmVudHNcbiAgICAgIGlmIChvcmllbnQgPT0gJzJEJykge1xuICAgICAgICB0cmFuc2Zvcm0gPSBzaGlmdFEgPyB7azogMSwgeDogdywgeTogMH0gOiB7azogMSwgeDogMCwgeTogd31cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRyYW5zZm9ybSA9IGhvcml6b250YWxRID8ge2s6IDEsIHg6IHcsIHk6IDB9IDoge2s6IDEsIHg6IDAsIHk6IHd9XG4gICAgICB9XG4gICAgICAvLyB0aGUgKiAtMSBpbnZlcnRzIHRoZSBkaXJlY3Rpb25cbiAgICAgIHRyYW5zZm9ybS5hcHBseVggPSBmdW5jdGlvbih4KSB7IHJldHVybiB4ICogdGhpcy5rICsgdGhpcy54ICogLTE7IH1cbiAgICAgIHRyYW5zZm9ybS5hcHBseVkgPSAgZnVuY3Rpb24oeSkgeyByZXR1cm4geSAqIHRoaXMuayArIHRoaXMueSAqIC0xOyB9XG4gICAgfVxuXG5cblxuICAgIHZhciBjaGFydE9ialNlbCA9IGNoYXJ0U2VsLnNlbGVjdCgnLicraHlwZW5hdGUoY2hhcnQubmFtZXNwYWNlKCksJ29iamVjdC1jb250YWluZXInKSlcbiAgICB2YXIgeEF4aXNPYmpTZWwgPSB4QXhpc1NlbC5zZWxlY3QoJy4nK2h5cGVuYXRlKHhBeGlzLm5hbWVzcGFjZSgpLCdvYmplY3QtY29udGFpbmVyJykpXG4gICAgdmFyIHlBeGlzT2JqU2VsID0geUF4aXNTZWwuc2VsZWN0KCcuJytoeXBlbmF0ZSh5QXhpcy5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuXG4gICAgLy8geExvY2sgPSBjaGFydFNlbC5ub2RlKCkuZ2V0QkJveCgpLndpZHRoIC0gY2hhcnQuc3BhY2VYKCkgLSBjaGFydFNlbC5ub2RlKCkuZ2V0QkJveCgpLnhcbiAgICAvLyB5TG9jayA9IGNoYXJ0U2VsLm5vZGUoKS5nZXRCQm94KCkuaGVpZ2h0IC0gY2hhcnQuc3BhY2VZKClcbiAgICAvLyBjb25zb2xlLnRhYmxlKHsneExvY2snOnhMb2NrLCBcInlMb2NrXCI6eUxvY2t9KVxuICAgIC8vIGJobS5zZWxlY3Rpb24oKS5ub2RlKCkuZ2V0QkJveCgpLndpZHRoIC0gYmhtLnNwYWNlWCgpXG5cblxuICAgIHZhciBjaGFydE9ialRyYW5zID0gZ2V0VHJhbnNsYXRpb24oY2hhcnRPYmpTZWwuYXR0cigndHJhbnNmb3JtJykpXG4gICAgdmFyIHhBeGlzT2JqVHJhbnMgPSBnZXRUcmFuc2xhdGlvbih4QXhpc09ialNlbC5hdHRyKCd0cmFuc2Zvcm0nKSlcbiAgICB2YXIgeUF4aXNPYmpUcmFucyA9IGdldFRyYW5zbGF0aW9uKHlBeGlzT2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScpKVxuXG5cbiAgICB2YXIgeCA9IGhvcml6b250YWxRID8gdHJhbnNmb3JtLmFwcGx5WChjaGFydE9ialRyYW5zWzBdKSA6IDBcbiAgICBpZiAoaG9yaXpvbnRhbFEpIHt4ID0geCA8IC14TG9jayA/ICh0cmFuc2Zvcm0ueCA9IDAsIC14TG9jaykgOiAodHJhbnNmb3JtLnggPSAwLCBNYXRoLm1pbih4LCAwKSkgfVxuXG4gICAgdmFyIHkgPSB2ZXJ0aWNhbFEgPyB0cmFuc2Zvcm0uYXBwbHlZKGNoYXJ0T2JqVHJhbnNbMV0pIDogMFxuICAgIGlmICh2ZXJ0aWNhbFEpIHt5ID0geSA8IC15TG9jayA/ICh0cmFuc2Zvcm0ueSA9IDAsIC15TG9jayk6ICh0cmFuc2Zvcm0ueSA9IDAsIE1hdGgubWluKHksIDApKX1cblxuICAgIGNoYXJ0T2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJylcbiAgICBpZiAoaG9yaXpvbnRhbFEpIHsgeEF4aXNPYmpTZWwuYXR0cigndHJhbnNmb3JtJywgJ3RyYW5zbGF0ZSgnK3grJywnKzArJyknKSB9XG4gICAgaWYgKHZlcnRpY2FsUSkgeyB5QXhpc09ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcrMCsnLCcreSsnKScpIH1cblxuICAgIC8vIHZhciBsYXNzbyA9IHN2Zy5zZWxlY3QoXCIubGFzc28tY29udGFpbmVyXCIpXG4gICAgLy8gaWYgKCFsYXNzby5lbXB0eSgpKSB7XG4gICAgLy8gICBsYXNzby5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcreCsnLCcreSsnKScpXG4gICAgLy8gfVxuXG4gIH1cblxuICB6b29tLnJlc2V0ID0gZnVuY3Rpb24oKSB7XG4gICAgdmFyIGhvcml6b250YWxRLCB2ZXJ0aWNhbFFcbiAgICBpZiAob3JpZW50ID09ICcyRCcpIHtob3Jpem9udGFsUSA9IHRydWU7IHZlcnRpY2FsUSA9IHRydWU7fVxuICAgIGlmIChvcmllbnQgPT0gJ2hvcml6b250YWwnKSB7aG9yaXpvbnRhbFEgPSB0cnVlOyB2ZXJ0aWNhbFEgPSBmYWxzZTt9XG4gICAgaWYgKG9yaWVudCA9PSAndmVydGljYWwnKSB7dmVydGljYWxRID0gdHJ1ZTsgaG9yaXpvbnRhbFEgPSBmYWxzZTt9XG5cbiAgICB2YXIgY2hhcnRPYmpTZWwgPSBjaGFydFNlbC5zZWxlY3QoJy4nK2h5cGVuYXRlKGNoYXJ0Lm5hbWVzcGFjZSgpLCdvYmplY3QtY29udGFpbmVyJykpXG4gICAgdmFyIHhBeGlzT2JqU2VsID0geEF4aXNTZWwuc2VsZWN0KCcuJytoeXBlbmF0ZSh4QXhpcy5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuICAgIHZhciB5QXhpc09ialNlbCA9IHlBeGlzU2VsLnNlbGVjdCgnLicraHlwZW5hdGUoeUF4aXMubmFtZXNwYWNlKCksJ29iamVjdC1jb250YWluZXInKSlcbiAgICBjaGFydE9ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcrMCsnLCcrMCsnKScpXG4gICAgeEF4aXNPYmpTZWwuYXR0cigndHJhbnNmb3JtJywgJ3RyYW5zbGF0ZSgnKzArJywnKzArJyknKVxuICAgIHlBeGlzT2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoJyswKycsJyswKycpJylcbiAgfVxuXG4gIHJldHVybiB6b29tXG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0LCBnZXRUcmFuc2xhdGlvbn0gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7bG9nLCB3YXJuLCBlcnJvciwgaW5mb30gZnJvbSAnLi91dGlscyc7XG4vKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxuXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQTE9UWk9PTSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG4vKipcbiAqIENyZWF0ZXMgYW4gcGxvdFpvb20gaW5zdGFuY2UsIHdoaWNoIGNhbiBoYW5kbGUgZHJhZyBhbmQgc2Nyb2xsIGV2ZW50c1xuICogQGNvbnN0cnVjdG9yIHBsb3Rab29tXG4gKiBAcGFyYW0ge2Z1bmN0aW9ufSBjaGFydCBhIGZ1bmN0aW9uIGluc3RhbmNlIG9mIG9uZSBvZiB0aGUgZDNzbSBwbG90cyAoZS5nLiBiYXIsIGJveHdoaXNrZXIsIGJ1YmJsZUhlYXRtYXAsIHZpb2xpbiwgZXRjKVxuICogQHBhcmFtIHtheGlzfSAgeEF4aXMgdGhlIGF4aXMgaW5zdGFuY2UgcmVzcG9uc2libGUgZm9yIHRoZSB4IGF4aXNcbiAqIEBwYXJhbSB7YXhpc30geUF4aXMgdGhlIGF4aXMgaW5zdGFuY2UgcmVzcG9uc2libGUgZm9yIHRoZSB5IGF4aXNcbiAqIEBuYW1lc3BhY2UgcGxvdFpvb21cbiAqIEByZXR1cm5zIHtmdW5jdGlvbn0gem9vbVxuICovXG5leHBvcnQgZnVuY3Rpb24gbXVsdGlQbG90Wm9vbSggY2hhcnQgKSB7XG4gIHZhclxuICAvKipcbiAgKiBUaGUgZXZlbnQgb24gd2hpY2ggdG8gZmlyZVxuICAqIChzZWUge0BsaW5rIHBsb3Rab29tI2V2ZW50VHlwZX0pXG4gICogQHBhcmFtIHtzdHJpbmd9IGV2ZW50VHlwZSB3aGljaCBldmVudCBpdCBzaG91bGQgaGFuZGxlLiBDdXJyZW50bHkgc3VwcG9ydHMgc2Nyb2xsIGFuZCB3aGVlbFxuICAqIEBtZW1iZXJvZiBwbG90Wm9vbSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZXZlbnRUeXBlLFxuICAvKipcbiAgKiBBIHNjYWxpbmcgZmFjdG9yIGZvciB0aGUgd2hlZWwgXCJzcGVlZFwiXG4gICogKHNlZSB7QGxpbmsgcGxvdFpvb20jd2hlZWxTcGVlZH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFt3aGVlbFNwZWVkPTIwXSBzY2FsZXMgdGhlIHdoZWVsIHRyYW5zbGF0aW9uIGJ5IHdoZWVsU3BlZWRcbiAgKiBAbWVtYmVyb2YgcGxvdFpvb20jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHdoZWVsU3BlZWQgPSAyMCxcbiAgLyoqXG4gICogVGhlIG9yaWVudGF0aW9uIGluIHdoaWNoIHRvIGFsbG93IHNjcm9sbGluZzogJ2hvcml6b250YWwnLCAndmVydGljYWwnLCBvciAnMkQnXG4gICogKHNlZSB7QGxpbmsgcGxvdFpvb20jb3JpZW50fSlcbiAgKiBAcGFyYW0ge3N0cmluZ30gW29yaWVudD1jaGFydC5vcmllbnQoKSB8fCAnaG9yaXpvbnRhbCddXG4gICogQG1lbWJlcm9mIHBsb3Rab29tI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvcmllbnQgPSAoY2hhcnQub3JpZW50ID09IHVuZGVmaW5lZCkgPyAnaG9yaXpvbnRhbCcgOiBjaGFydC5vcmllbnQoKSxcbiAgLyoqXG4gICogVGhlIG1heCBkaXN0YW5jZSBhbGxvd2VkIHRvIHNjcm9sbCBpbiB0aGUgeCBkaXJlY3Rpb25cbiAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN4TG9ja30pXG4gICogQHBhcmFtIHtudW1iZXJ9IFt4TG9jaz1jaGFydC5zcGFjZVgoKV0gaWRlYWxseSBjaGFydC5vdmVyZmxvd1EoKSA9PSB0cnVlIGFuZCB0aGlzIHZhbHVlIGlzIHRoZVxuICAqIGJvdW5kaW5nIHJlY3QgYWNyb3NzIGFsbCBlbGVtZW50cyBpbiB0aGUgY2hhcnQgIG1pbnVzIHRoZSBzcGFjZSBpbiB3aGljaCB0byBzaG93LlxuICAqIEBtZW1iZXJvZiBwbG90Wm9vbSNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgeExvY2s9Y2hhcnQuc3BhY2VYKCksXG4gIC8qKlxuICAqIFRoZSBtYXggZGlzdGFuY2UgYWxsb3dlZCB0byBzY3JvbGwgaW4gdGhlIHkgZGlyZWN0aW9uXG4gICogKHNlZSB7QGxpbmsgcGxvdFpvb20jeUxvY2t9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbeUxvY2s9Y2hhcnQuc3BhY2VZKCldIGlkZWFsbHkgY2hhcnQub3ZlcmZsb3dRKCkgPT0gdHJ1ZSBhbmQgdGhpcyB2YWx1ZSBpcyB0aGVcbiAgKiBib3VuZGluZyByZWN0IGFjcm9zcyBhbGwgZWxlbWVudHMgaW4gdGhlIGNoYXJ0ICBtaW51cyB0aGUgc3BhY2UgaW4gd2hpY2ggdG8gc2hvdy5cbiAgKiBAbWVtYmVyb2YgcGxvdFpvb20jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHlMb2NrPWNoYXJ0LnNwYWNlWSgpLFxuXG4gIGNoYXJ0U2VsID0gY2hhcnQuc2VsZWN0aW9uKCksXG5cbiAgc3ZnID0gZDMuc2VsZWN0KGNoYXJ0U2VsLnRoaXNTVkcoKSksXG5cbiAgeENvbXBvbmVudHMgPSBbXSxcbiAgeUNvbXBvbmVudHMgPSBbXVxuXG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZXZlbnQgdHlwZSBpbiB3aGljaCB0byByZXNwb25kXG4gICAqIChzZWUge0BsaW5rIHBsb3Rab29tI2V2ZW50VHlwZX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXSBzaG91bGQgYmUgJ2RyYWcnIG9yICd3aGVlbCdcbiAgICogQHJldHVybnMge3pvb20gfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBwbG90Wm9vbVxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBwbG90Wm9vbT11bmRlZmluZWRcbiAgICovXG4gIHpvb20uZXZlbnRUeXBlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChldmVudFR5cGUgPSBfLCB6b29tKSA6IGV2ZW50VHlwZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgd2hlZWwgc3BlZWQgaW4gd2hpY2ggdG8gc2NhbGUgdGhlIHdoZWVsIHNjcm9sbCB0cmFuc2Zvcm1cbiAgICogKHNlZSB7QGxpbmsgcGxvdFpvb20jd2hlZWxTcGVlZH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7em9vbSB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHBsb3Rab29tXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHdoZWVsU3BlZWQ9MjBcbiAgICovXG4gIHpvb20ud2hlZWxTcGVlZCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAod2hlZWxTcGVlZCA9IF8sIHpvb20pIDogd2hlZWxTcGVlZDsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgb3JpZW50YXRpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIChzZWUge0BsaW5rIHBsb3Rab29tI29yaWVudH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXSBzaG91bGQgYmUgaG9yaXpvbnRhbCwgdmVydGljYWwsIG9yIDJEXG4gICAqIEByZXR1cm5zIHt6b29tIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgcGxvdFpvb21cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3JpZW50PWNoYXJ0Lm9yaWVudCgpIHx8ICdob3Jpem9udGFsJ1xuICAgKi9cbiAgem9vbS5vcmllbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9yaWVudCA9IF8sIHpvb20pIDogb3JpZW50OyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIG1heCBkaXN0YW5jZSBpbiB3aGljaCB0byBzY3JvbGwgWFxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN4TG9ja30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBwb3NpdGl2ZSB2YWx1ZVxuICAgKiBAcmV0dXJucyB7em9vbSB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHBsb3Rab29tXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHhMb2NrPWNoYXJ0LnNwYWNlWCgpXG4gICAqL1xuICB6b29tLnhMb2NrID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4TG9jayA9IF8sIHpvb20pIDogeExvY2s7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIG1heCBkaXN0YW5jZSBpbiB3aGljaCB0byBzY3JvbGwgWVxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN5TG9ja30pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSAgc2hvdWxkIGJlIGEgcG9zaXRpdmUgdmFsdWVcbiAgICogQHJldHVybnMge3pvb20gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBwbG90Wm9vbVxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB5TG9jaz1jaGFydC5zcGFjZVkoKVxuICAgKi9cbiAgem9vbS55TG9jayA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeUxvY2sgPSBfLCB6b29tKSA6IHlMb2NrOyB9O1xuXG4gIHpvb20ueENvbXBvbmVudHMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhDb21wb25lbnRzID0gXywgem9vbSkgOiB4Q29tcG9uZW50czsgfTtcbiAgem9vbS55Q29tcG9uZW50cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeUNvbXBvbmVudHMgPSBfLCB6b29tKSA6IHlDb21wb25lbnRzOyB9O1xuXG5cbiAgZnVuY3Rpb24gc2V0TG9ja3MoKSB7XG4gICAgdmFyIGNoYXJ0T2JqU2VsID0gY2hhcnRTZWwuc2VsZWN0KCcuJytoeXBlbmF0ZShjaGFydC5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuICAgIHZhciBjaGFydE9ialRyYW5zID0gZ2V0VHJhbnNsYXRpb24oY2hhcnRPYmpTZWwuYXR0cigndHJhbnNmb3JtJykpXG4gICAgdmFyIGNvcyA9IGNoYXJ0T2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoMCwwKScpXG5cbiAgICB4TG9jayA9IGNoYXJ0U2VsLm5vZGUoKS5nZXRCQm94KCkud2lkdGggLSBjaGFydC5zcGFjZVgoKS8vICogLjlcbiAgICB5TG9jayA9IGNoYXJ0U2VsLm5vZGUoKS5nZXRCQm94KCkuaGVpZ2h0IC0gY2hhcnQuc3BhY2VZKCkvLyAqIC45XG4gICAgY29zLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoJytjaGFydE9ialRyYW5zWzBdKycsJytjaGFydE9ialRyYW5zWzFdKycpJylcbiAgICBsb2coJ3Bsb3Rab29tJywgJ3NldExvY2tzJywge3hMb2NrOnhMb2NrLCB5TG9jazp5TG9ja30pXG4gIH1cblxuXG4gIC8qKlxuICAgKiBTZXRzIHRoZSB4IGFuZCB5IGxvY2tzIChob3cgZmFyIG9uZSBjYW4gc2Nyb2xsKVxuICAgKiAoc2VlIHtAbGluayBwbG90Wm9vbSN4TG9ja30gYW5kIHtAbGluayBwbG90Wm9vbSN5TG9ja30pXG4gICAqIEBmdW5jdGlvbiBwbG90Wm9vbS5zZXRMb2Nrc1xuICAgKiBAcmV0dXJucyB7dW5kZWZpbmVkfVxuICAgKiBAbWVtYmVyb2YgcGxvdFpvb21cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICB6b29tLnNldExvY2tzID0gc2V0TG9ja3NcblxuICBmdW5jdGlvbiB6b29tKCkge1xuICAgIHNldExvY2tzKClcblxuICAgIHZhclxuICAgIHhDb21wb25lbnRzU2VsID0geENvbXBvbmVudHMubWFwKGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkLnNlbGVjdGlvbigpfSksXG4gICAgeUNvbXBvbmVudHNTZWwgPSB5Q29tcG9uZW50cy5tYXAoZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGQuc2VsZWN0aW9uKCl9KVxuXG4gICAgdmFyIGhvcml6b250YWxRLCB2ZXJ0aWNhbFFcbiAgICBpZiAob3JpZW50ID09ICcyRCcpIHtob3Jpem9udGFsUSA9IHRydWU7IHZlcnRpY2FsUSA9IHRydWU7fVxuICAgIGlmIChvcmllbnQgPT0gJ2hvcml6b250YWwnKSB7aG9yaXpvbnRhbFEgPSB0cnVlOyB2ZXJ0aWNhbFEgPSBmYWxzZTt9XG4gICAgaWYgKG9yaWVudCA9PSAndmVydGljYWwnKSB7dmVydGljYWxRID0gdHJ1ZTsgaG9yaXpvbnRhbFEgPSBmYWxzZTt9XG5cbiAgICAvLyBjYXB0dXJlIHRyYW5zZm9ybSBldmVudFxuICAgIHZhciB0cmFuc2Zvcm0gPSBkMy5ldmVudC50cmFuc2Zvcm1cblxuICAgIHZhciBjaGFydEJveCA9IGNoYXJ0U2VsLm5vZGUoKS5nZXRCQm94KClcbiAgICB2YXIgeENvbXBvbmVudHNCb3ggPSB4Q29tcG9uZW50c1NlbC5tYXAoZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGQubm9kZSgpLmdldEJCb3goKX0pXG4gICAgdmFyIHlDb21wb25lbnRzQm94ID0geENvbXBvbmVudHNTZWwubWFwKGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkLm5vZGUoKS5nZXRCQm94KCl9KVxuXG5cbiAgICB2YXIgY2hhcnRXaWR0aCA9IGNoYXJ0Qm94LndpZHRoIC0gY2hhcnRCb3gueFxuICAgIHZhciBjaGFydEhlaWdodCA9IGNoYXJ0Qm94LmhlaWdodCAtIGNoYXJ0Qm94LnlcblxuICAgIC8vIGVuYWJsZSB3aGVlbCBldmVudFxuICAgIGlmIChldmVudFR5cGUgPT0gXCJ3aGVlbFwiKSB7XG4gICAgICB2YXIgZSA9IGQzLmV2ZW50XG4gICAgICAvLyBwcmV2ZW50IHBhZ2Ugc2Nyb2xsaW5nXG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIC8vIGV2ZW50IGRlbHRhIGlzIHZlcnkgdmVyeSBzbG93LCBzbyBzcGVlZCBpdCB1cCB3aXRoIHdoZWVsIHNwZWVkXG4gICAgICB2YXIgdyA9IGQzLmV2ZW50LmRlbHRhWSAqIHdoZWVsU3BlZWRcbiAgICAgIHZhciBzaGlmdFEgPSBkMy5ldmVudC5zaGlmdEtleVxuXG4gICAgICAvLyBkMyBoYXMgbm8gd2F5IHRvIG1ha2UgY3VzdG9tIHRyYW5zZm9ybSwgc28gbWFrZSBhbiBvYmplY3QgYW5kIGFkZFxuICAgICAgLy8gdGhlIG5lY2Vzc2FyeSBmdW5jdGlvbnMgdG8gbWFrZSB3aGVlbCBldmVudCBjb21wYXRpYmxlIHdpdGggZHJhZyBldmVudHNcbiAgICAgIGlmIChvcmllbnQgPT0gJzJEJykge1xuICAgICAgICB0cmFuc2Zvcm0gPSBzaGlmdFEgPyB7azogMSwgeDogdywgeTogMH0gOiB7azogMSwgeDogMCwgeTogd31cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRyYW5zZm9ybSA9IGhvcml6b250YWxRID8ge2s6IDEsIHg6IHcsIHk6IDB9IDoge2s6IDEsIHg6IDAsIHk6IHd9XG4gICAgICB9XG4gICAgICAvLyAqIC0xIGlzIGludmVydFxuICAgICAgdHJhbnNmb3JtLmFwcGx5WCA9IGZ1bmN0aW9uKHgpIHsgcmV0dXJuIHggKiB0aGlzLmsgKyB0aGlzLnggKi0xOyB9XG4gICAgICB0cmFuc2Zvcm0uYXBwbHlZID0gIGZ1bmN0aW9uKHkpIHsgcmV0dXJuIHkgKiB0aGlzLmsgKyB0aGlzLnkgKi0xOyB9XG4gICAgfVxuXG5cblxuICAgIHZhciBjaGFydE9ialNlbCA9IGNoYXJ0U2VsLnNlbGVjdCgnLicraHlwZW5hdGUoY2hhcnQubmFtZXNwYWNlKCksJ29iamVjdC1jb250YWluZXInKSlcbiAgICB2YXIgeENvbXBvbmVudE9ialNlbCA9IHhDb21wb25lbnRzU2VsLm1hcChmdW5jdGlvbihkLCBpKXtcbiAgICAgIHJldHVybiBkLnNlbGVjdCgnLicraHlwZW5hdGUoeENvbXBvbmVudHNbaV0ubmFtZXNwYWNlKCksJ29iamVjdC1jb250YWluZXInKSlcbiAgICB9KVxuICAgIHZhciB5Q29tcG9uZW50T2JqU2VsID0geUNvbXBvbmVudHNTZWwubWFwKGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgcmV0dXJuIGQuc2VsZWN0KCcuJytoeXBlbmF0ZSh5Q29tcG9uZW50c1tpXS5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuICAgIH0pXG5cbiAgICB2YXIgY2hhcnRPYmpUcmFucyA9IGdldFRyYW5zbGF0aW9uKGNoYXJ0T2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScpKVxuICAgIHZhciB4Q29tcG9uZW50c09ialRyYW5zID0geENvbXBvbmVudE9ialNlbC5tYXAoZnVuY3Rpb24oZCwgaSl7XG4gICAgICByZXR1cm4gZ2V0VHJhbnNsYXRpb24oZC5hdHRyKCd0cmFuc2Zvcm0nKSlcbiAgICB9KVxuICAgIHZhciB5Q29tcG9uZW50c09ialRyYW5zID0geUNvbXBvbmVudE9ialNlbC5tYXAoZnVuY3Rpb24oZCwgaSl7XG4gICAgICByZXR1cm4gZ2V0VHJhbnNsYXRpb24oZC5hdHRyKCd0cmFuc2Zvcm0nKSlcbiAgICB9KVxuXG4gICAgdmFyIHggPSBob3Jpem9udGFsUSA/IHRyYW5zZm9ybS5hcHBseVgoY2hhcnRPYmpUcmFuc1swXSkgOiAwXG4gICAgaWYgKGhvcml6b250YWxRKSB7eCA9IHggPCAteExvY2sgPyAodHJhbnNmb3JtLnggPSAwLCAteExvY2spIDogKHRyYW5zZm9ybS54ID0gMCwgTWF0aC5taW4oeCwgMCkpIH1cblxuICAgIHZhciB5ID0gdmVydGljYWxRID8gdHJhbnNmb3JtLmFwcGx5WShjaGFydE9ialRyYW5zWzFdKSA6IDBcbiAgICBpZiAodmVydGljYWxRKSB7eSA9IHkgPCAteUxvY2sgPyAodHJhbnNmb3JtLnkgPSAwLCAteUxvY2spOiAodHJhbnNmb3JtLnkgPSAwLCBNYXRoLm1pbih5LCAwKSl9XG5cbiAgICBjaGFydE9ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcreCsnLCcreSsnKScpXG4gICAgaWYgKGhvcml6b250YWxRKSB7XG4gICAgICAvLyB4QXhpc09ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcreCsnLCcrMCsnKScpXG4gICAgICB4Q29tcG9uZW50T2JqU2VsLm1hcChmdW5jdGlvbihkLCBpKXsgZC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcreCsnLCcrMCsnKScpICB9KVxuICAgIH1cbiAgICBpZiAodmVydGljYWxRKSB7XG4gICAgICAvLyB5QXhpc09ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcrMCsnLCcreSsnKScpXG4gICAgICB5Q29tcG9uZW50T2JqU2VsLm1hcChmdW5jdGlvbihkLCBpKXsgZC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcrMCsnLCcreSsnKScpICB9KVxuXG4gICAgfVxuXG5cbiAgfVxuXG4gIHpvb20ucmVzZXQgPSBmdW5jdGlvbigpIHtcbiAgICB2YXIgaG9yaXpvbnRhbFEsIHZlcnRpY2FsUVxuICAgIGlmIChvcmllbnQgPT0gJzJEJykge2hvcml6b250YWxRID0gdHJ1ZTsgdmVydGljYWxRID0gdHJ1ZTt9XG4gICAgaWYgKG9yaWVudCA9PSAnaG9yaXpvbnRhbCcpIHtob3Jpem9udGFsUSA9IHRydWU7IHZlcnRpY2FsUSA9IGZhbHNlO31cbiAgICBpZiAob3JpZW50ID09ICd2ZXJ0aWNhbCcpIHt2ZXJ0aWNhbFEgPSB0cnVlOyBob3Jpem9udGFsUSA9IGZhbHNlO31cblxuICAgIHZhciBjaGFydE9ialNlbCA9IGNoYXJ0U2VsLnNlbGVjdCgnLicraHlwZW5hdGUoY2hhcnQubmFtZXNwYWNlKCksJ29iamVjdC1jb250YWluZXInKSlcbiAgICB2YXIgeEF4aXNPYmpTZWwgPSB4QXhpc1NlbC5zZWxlY3QoJy4nK2h5cGVuYXRlKHhBeGlzLm5hbWVzcGFjZSgpLCdvYmplY3QtY29udGFpbmVyJykpXG4gICAgdmFyIHlBeGlzT2JqU2VsID0geUF4aXNTZWwuc2VsZWN0KCcuJytoeXBlbmF0ZSh5QXhpcy5uYW1lc3BhY2UoKSwnb2JqZWN0LWNvbnRhaW5lcicpKVxuICAgIGNoYXJ0T2JqU2VsLmF0dHIoJ3RyYW5zZm9ybScsICd0cmFuc2xhdGUoJyswKycsJyswKycpJylcbiAgICB4QXhpc09ialNlbC5hdHRyKCd0cmFuc2Zvcm0nLCAndHJhbnNsYXRlKCcrMCsnLCcrMCsnKScpXG4gICAgeUF4aXNPYmpTZWwuYXR0cigndHJhbnNmb3JtJywgJ3RyYW5zbGF0ZSgnKzArJywnKzArJyknKVxuICB9XG5cbiAgcmV0dXJuIHpvb21cbn1cbiIsImltcG9ydCB7aHlwZW5hdGUsIHNhZmVTZWxlY3QsIG1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2UsIGV4dHJhY3RWaW9saW5WYWx1ZXMsIHF1YXJ0aWxlc30gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7c2V0dXBDb250YWluZXIsIGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3QsIGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXJ9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHt1bmlxdWUsIGhhc1EsIGZsYXR0ZW4sIHdoaWNoQmlufSBmcm9tICcuL2FycmF5LWZ1bmN0aW9ucyc7XG5pbXBvcnQge2dyb3VwaW5nU3BhY2VyfSBmcm9tICcuL2dyb3VwaW5nLXNwYWNlcic7XG5pbXBvcnQge2NvbG9yRnVuY3Rpb24gYXMgQ0Z9IGZyb20gJy4vY29sb3ItZnVuY3Rpb24nO1xuaW1wb3J0IHt0b29sdGlwIGFzIFRUaXB9IGZyb20gJy4vdG9vbHRpcCc7XG5cbi8qKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVklPTElOICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICoqXG4qKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqKlxuKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKipcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiovXG5cbi8qKlxuICogQ3JlYXRlcyBhIHZpb2xpblxuICpcbiAqIHtAbGluayBodHRwczovL3N1bW5ldXJvbi5naXRsYWIuaW8vZDNzbS9kZW1vcy9iYXNpYy12aW9saW5zL2luZGV4Lmh0bWwgRGVtb31cbiAqIEBjb25zdHJ1Y3RvciB2aW9saW5cbiAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBzZWxlY3Rpb25cbiAqIEBuYW1lc3BhY2UgdmlvbGluXG4gKiBAcmV0dXJucyB7ZnVuY3Rpb259IHZpb2xpblxuICovXG5leHBvcnQgZnVuY3Rpb24gdmlvbGluKCBzZWxlY3Rpb24gKSB7XG4gIHZhclxuICAvKipcbiAgKiBEYXRhIHRvIHBsb3QuIEFzc3VtZWQgdG8gYmUgYSBvYmplY3QsIHdoZXJlIGVhY2gga2V5IGNvcnJlc3BvbmRzIHRvIGEgdmlvbGluXG4gICogKHNlZSB7QGxpbmsgdmlvbGluI2RhdGF9KVxuICAqIEBwYXJhbSB7T2JqZWN0fSBbZGF0YT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZGF0YSxcbiAgLyoqXG4gICogV2hpY2ggZGlyZWN0aW9uIHRvIHJlbmRlciB0aGUgYmFycyBpblxuICAqIChzZWUge0BsaW5rIHZpb2xpbiNvcmllbnR9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbb3JpZW50PSdob3Jpem9udGFsJ11cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvcmllbnQ9J2hvcml6b250YWwnLFxuICAvKipcbiAgKiBBbW91bnQgb2YgaG9yaXpvbnRhbCBzcGFjZSAoaW4gcGl4ZWxzKSBhdmFpYmxlIHRvIHJlbmRlciB0aGUgdmlvbGluIGluXG4gICogKHNlZSB7QGxpbmsgdmlvbGluI3NwYWNlWH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtzcGFjZVg9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlWCxcbiAgLyoqXG4gICogQW1vdW50IG9mIHZlcnRpY2FsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSB2aW9saW4gaW5cbiAgKiAoc2VlIHtAbGluayB2aW9saW4uc3BhY2VZfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlWT11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VZLFxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0byBhbGxvdyB2aW9saW4gdG8gcmVuZGVyIGVsZW1lbnRzIHBhc3MgdGhlIG1haW4gc3BhdGlhbCBkaW1lbnNpb25cbiAgKiBnaXZlbiB0aGUgb3JpZW50YXRpb24gKHNlZSB7QGxpbmsgdmlvbGluI29yaWVudH0pLCB3aGVyZSB7QGxpbmsgdmlvbGluI29yaWVudH09XCJob3Jpem9udGFsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIHZpb2xpbiNzcGFjZVh9IGFuZCB3aGVyZSB7QGxpbmsgdmlvbGluI29yaWVudH09XCJ2ZXJ0aWNhbFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayB2aW9saW4jc3BhY2VZfVxuICAqIEBwYXJhbSB7Ym9vbGVhbn0gW292ZXJmbG93UT1mYWxzZV1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvdmVyZmxvd1EgPSB0cnVlLFxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0byBkaXNwbGF5IHBvaW50cyBpbnNpZGUgdGhlIHBvaW50c1xuICAqIEBwYXJhbSB7Ym9vbGVhbn0gW3BvaW50c1E9ZmFsc2VdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgcG9pbnRzUSA9IHRydWUsXG4gIC8qKlxuICAqIEFuIGFycmF5IC0gcHV0YXRpdmVseSBvZiBvdGhlciBhcnJheXMgLSBkZXBpY3RpbmcgaG93IGJhcnMgc2hvdWxkIGJlIGFycmFuZ2VkXG4gICogQHBhcmFtIHtBcnJheVtdfSBbZ3JvdXBpbmc9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIGdyb3VwaW5nLFxuICAvKipcbiAgKiBIb3cgdG8gZ2V0IHRoZSB2YWx1ZSBvZiB0aGUgdmlvbGluXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3ZhbHVlRXh0cmFjdG9yPWZ1bmN0aW9uKGtleSwgaW5kZXgpIHsgcmV0dXJuIGRhdGFba2V5XSB9XVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHZhbHVlRXh0cmFjdG9yID0gZnVuY3Rpb24oa2V5LCBpbmRleCkge3JldHVybiBkYXRhW2tleV0gfSxcbiAgLyoqXG4gICogSG93IHRvIHNvcnQgdGhlIGJhcnMgLSBpZiB7QGxpbmsgdmlvbGluI2dyb3VwaW5nfSBpcyBub3QgcHJvdmlkZWQuXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3NvcnRpbmdGdW5jdGlvbj1mdW5jdGlvbihrZXlBLCBrZXlCKSB7cmV0dXJuIGQzLmRlc2NlbmRpbmcoZGF0YVtrZXlBXSwgZGF0YVtrZXlCXSl9XVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNvcnRpbmdGdW5jdGlvbiA9IGZ1bmN0aW9uKGtleUEsIGtleUIpIHtyZXR1cm4gZDMuZGVzY2VuZGluZyhkYXRhW2tleUFdLCBkYXRhW2tleUJdKX0sXG5cbiAgLyoqXG4gICogVGhlIHNjYWxlIGZvciB3aGljaCB2aW9saW4gdmFsdWVzIHNob3VsZCBiZSB0cmFuc2Zvcm1lZCBieVxuICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtzY2FsZT1kMy5zY2FsZUxpbmVhcl1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKCksXG4gIC8qKlxuICAqIFRoZSBwYWRkaW5nIGZvciB0aGUgZG9tYWluIG9mIHRoZSBzY2FsZSAoc2VlIHtAbGluayB2aW9saW4jc2NhbGV9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbZG9tYWluUGFkZGluZz0wLjVdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZG9tYWluUGFkZGluZyA9IDAuNSxcbiAgLyoqXG4gICogRGVmYXVsdCBzcGFjZSBmb3IgdGhlIHNwYWNlciAocGVyY2VudGFnZSkgb2YgbWFpbiBkaW1lbnNpb24gZ2l2ZW4gdGhlIG9yaWVudGF0aW9uXG4gICogKHNlZSB7QGxpbmsgdmlvbGluI29yaWVudH0pLCB3aGVyZSB7QGxpbmsgdmlvbGluI29yaWVudH09XCJob3Jpem9udGFsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIHZpb2xpbiNzcGFjZVh9IGFuZCB3aGVyZSB7QGxpbmsgdmlvbGluI29yaWVudH09XCJ2ZXJ0aWNhbFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayB2aW9saW4jc3BhY2VZfSBiZXR3ZWVuIGJhcnNcbiAgKiBAcGFyYW0ge251bWJlcn0gW29iamVjdFNwYWNlcj0wLjA1XVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdFNwYWNlciA9IDAuMDUsXG4gIC8qKlxuICAqIFRoZSBtaW5pbXVtIHNpemUgdGhhdCBhbiBvYmplY3QgY2FuIGJlXG4gICogQHBhcmFtIHtudW1iZXJ9IFttaW5PYmplY3RTaXplPTUwXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1pbk9iamVjdFNpemUgPSA1MCxcbiAgLyoqXG4gICogVGhlIG1heGltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmVcbiAgKiBAcGFyYW0ge251bWJlcn0gW21heE9iamVjdFNpemU9MTAwXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIG1heE9iamVjdFNpemUgPSAxMDAsXG5cbiAgLyoqXG4gICogVGhlIHN0cm9rZSB3aWR0aCBvZiB0aGUgYmFyc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbYmFyU3Ryb2tlV2lkdGg9Ml1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvYmplY3RTdHJva2VXaWR0aCA9IDIsXG4gIC8qKlxuICAqIEluc3RhbmNlIG9mIENvbG9yRnVuY3Rpb25cbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKV1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBjb2xvckZ1bmN0aW9uID0gQ0YoKSxcbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgQ29sb3JGdW5jdGlvbiBtb2RpZmllZCBieSBhIHNjYWxlIGZvciB0aGUgcG9pbnRzXG4gICogQHBhcmFtIHtmdW5jdGlvbn0gW3BvaW50Q29sb3JGdW5jID0gY29sb3JGdW5jdGlvbigpXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHBvaW50Q29sb3JGdW5jID0gZnVuY3Rpb24gKGQsIHR5cGUsIGJhc2UsIG1pbiwgbWF4KSB7XG4gICAgdmFyIG1pbk1heEhleFNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKS5kb21haW4oW21pbiwgbWF4XSkucmFuZ2UoWy0wLjI1LCAwLjA1XSlcbiAgICB2YXIgc2NhbGVkQ29sb3IgPSBtb2RpZnlIZXhpZGVjaW1hbENvbG9yTHVtaW5hbmNlKGJhc2UucmVwbGFjZSgnIycsICcnKSwgbWluTWF4SGV4U2NhbGUoZCkpXG4gICAgdmFyIG1vZCA9IHR5cGUgPT0gXCJzdHJva2VcIiA/IDAgOiAwLjI1XG4gICAgcmV0dXJuIG1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2Uoc2NhbGVkQ29sb3IucmVwbGFjZSgnIycsICcnKSwgbW9kKVxuICB9LFxuXG4gIC8qKlxuICAqIFRoZSByYWRpdXMgb2YgYSBwb2ludFxuICAqIEBwYXJhbSB7bnVtYmVyfSBbcG9pbnRSYWRpdXM9M11cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBwb2ludFJhZGl1cyA9IDMsXG4gIC8qKlxuICAqIFRoZSBzdHJva2Ugd2lkdGggb2YgdGhlIG9pbnRzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtwb2ludFN0cm9rZVdpZHRoPTJdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgcG9pbnRTdHJva2VXaWR0aCA9IDIsXG5cbiAgLyoqXG4gICogQ29sb3Igb2YgdGhlIGJhY2tncm91bmRcbiAgKiBAcGFyYW0ge3N0cmluZ30gW2JhY2tncm91bmRGaWxsPVwidHJhbnNwYXJlbnRcIl1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCcsXG4gIC8qKlxuICAqIE5hbWVzcGFjZSBmb3IgYWxsIGl0ZW1zIG1hZGUgYnkgdGhpcyBpbnN0YW5jZSBvZiB2aW9saW5cbiAgKiBAcGFyYW0ge3N0cmluZ30gW25hbWVzcGFjZT1cImQzc20tdmlvbGluXCJdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbmFtZXNwYWNlID0gJ2Qzc20tdmlvbGluJyxcbiAgLyoqXG4gICogQ2xhc3MgbmFtZSBmb3IgdmlvbGluIGNvbnRhaW5lciAoPGc+IGVsZW1lbnQpXG4gICogQHBhcmFtIHtzdHJpbmd9IFtvYmplY3RDbGFzcz1cInZpb2xpblwiXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9iamVjdENsYXNzID0gJ3Zpb2xpbicsXG4gIC8qKlxuICAqIER1cmF0aW9uIG9mIGFsbCB0cmFuc2l0aW9ucyBvZiB0aGlzIGVsZW1lbnRcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RyYW5zaXRpb25EdXJhdGlvbj0xMDAwXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRyYW5zaXRpb25EdXJhdGlvbiA9IDEwMDAsXG4gIC8qKlxuICAqIEVhc2luZyBmdW5jdGlvbiBmb3IgdHJhbnNpdGlvbnNcbiAgKiBAcGFyYW0ge2QzLmVhc2V9IFtlYXNlRnVuYz1kMy5lYXNlRXhwXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIGVhc2VGdW5jID0gZDMuZWFzZUV4cCxcblxuICAvKipcbiAgKiBUaGUga2V5IGNvbnRhaW5pbmcgdGhlIHF1YXJ0aWxlc1xuICAqIEBwYXJhbSB7c3RyaW5nfSBbcXVhcnRpbGVzS2V5PXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBxdWFydGlsZXNLZXkgPSBcInF1YXJ0aWxlc1wiLFxuICAvKipcbiAgKiBUaGUga2V5cyBjb3JyZXNwb25kaW5nIHRvIGVhY2ggcXVhcnRpbGVcbiAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbcXVhcnRpbGVLZXlzPVtcIlEwXCIsIFwiUTFcIiwgXCJRMlwiLCBcIlEzXCIsIFwiUTRcIl1dXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgcXVhcnRpbGVLZXlzID0gW1wiUTBcIiwgXCJRMVwiLCBcIlEyXCIsIFwiUTNcIiwgXCJRNFwiXSxcblxuICAvKipcbiAgKiBUaGUga2V5cyBvZiB0aGUgYmFyc1xuICAqIEBwYXJhbSB7c3RyaW5nW119IFt2aW9saW5LZXlzPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2aW9saW5LZXlzLFxuICAvKipcbiAgKiBUaGUgdmFsdWVzIG9mIHRoZSBiYXJzXG4gICogQHBhcmFtIHtudW1iZXJbXX0gW3Zpb2xpblZhbHVlcz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmlvbGluVmFsdWVzLFxuICAvKipcbiAgKiBUaGUgb2JqZWN0U2l6ZSAoYWN0dWFsIHdpZHRoKSB1c2VkIGJ5IHRoZSBiYXJzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvYmplY3RTaXplPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvYmplY3RTaXplLFxuICAvKipcbiAgKiBUaGUgc3BhY2VyU2l6ZSAoYWN0dWFsIHdpZHRoKSB1c2VkIGJ5IHRoZSBzcGFjZXJzIGJldHdlZW4gdGhlIGJhcnNcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlclNpemU9dW5kZWZpbmVkXVxuICAqIEBtZW1iZXJvZiB2aW9saW4jXG4gICogQHByb3BlcnR5XG4gICovXG4gIHNwYWNlclNpemUsXG5cbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgVG9vbHRpcFxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt0b29sdGlwPXRvb2x0aXAoKV1cbiAgKiBAbWVtYmVyb2YgdmlvbGluI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB0b29sdGlwID0gVFRpcCgpLmtleXMoW3F1YXJ0aWxlS2V5c1s0XSwgcXVhcnRpbGVLZXlzWzNdLCBxdWFydGlsZUtleXNbMl0sIHF1YXJ0aWxlS2V5c1sxXSwgcXVhcnRpbGVLZXlzWzBdXSksXG4gIHBvaW50c1Rvb2x0aXAgPSBUVGlwKCksXG4gIC8vIHBvaW50S2V5RXh0cmFjdG9yID0gZnVuY3Rpb24odmlvbGluS2V5LCB2aW9saW5EYXRhLCB2aW9saW5WYWx1ZXMpIHtyZXR1cm4gZDMua2V5cyh2aW9saW5WYWx1ZXNbdmlvbGluS2V5XS52YWx1ZXMpfSxcbiAgLy8gcG9pbnRWYWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKHBvaW50S2V5LCB2aW9saW5LZXksIHZpb2xpbkRhdGEsIHZpb2xpblZhbHVlcykge3JldHVybiB2aW9saW5WYWx1ZXNbdmlvbGluS2V5XS52YWx1ZXNbcG9pbnRLZXldfSxcblxuXG4gIC8qKlxuICAqIEZ1bmN0aW9uIHdoaWNoIGdpdmVuIHRoZSBrZXkgb2YgdGhlIHZpb2xpbiBhbmQgdGhhdCBrZXkncyBhc3NvY2lhdGVkIHZhbHVlXG4gICogcmV0dXJucyB0aGUgb2JqZWN0IGNvbnNpdGluZyBvZiB0aGUgcG9pbnRzIG9mIHRoZSB2aW9saW5cbiAgKiAoc2VlIHtAbGluayB2aW9saW4jdmlvbGluUG9pbnRzRXh0cmFjdG9yfSlcbiAgKiBAcGFyYW0ge09iamVjdH0gW3Zpb2xpblBvaW50c0V4dHJhY3Rvcj1mdW5jdGlvbih2aW9saW5LZXksIHZpb2xpbkRhdGEpIHsgcmV0dXJuIHZpb2xpbkRhdGEucG9pbnRzIH1dXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmlvbGluUG9pbnRzRXh0cmFjdG9yID0gZnVuY3Rpb24gKHZpb2xpbktleSwgdmlvbGluRGF0YSkge3JldHVybiB2aW9saW5EYXRhLnBvaW50cyB9LFxuICAvKipcbiAgKiBGdW5jdGlvbiB3aGljaCBnaXZlbiB0aGUga2V5IG9mIHRoZSBjdXJyZW50IHBvaW50IGFuZCB0aGUgb2JqZWN0IG9mIHBvaW50cyBmb3IgdGhlXG4gICogdmlvbGluLCByZXR1cm5zIHRoZSBudW1lcmljYWwgdmFsdWUgb2YgdGhlIHBvaW50XG4gICogKHNlZSB7QGxpbmsgdmlvbGluI3Zpb2xpblBvaW50VmFsdWVFeHRyYWN0b3J9KVxuICAqIEBwYXJhbSB7T2JqZWN0fSBbdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3Rvcj1mdW5jdGlvbih2aW9saW5Qb2ludEtleSwgdmlvbGluUG9pbnREYXRhKSB7IHJldHVybiB2aW9saW5Qb2ludERhdGFbdmlvbGluUG9pbnRLZXldLnZhbHVlIH1dXG4gICogQG1lbWJlcm9mIHZpb2xpbiNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKHZpb2xpblBvaW50S2V5LCB2aW9saW5Qb2ludERhdGEpIHsgcmV0dXJuIHZpb2xpblBvaW50RGF0YVt2aW9saW5Qb2ludEtleV0udmFsdWUgfVxuXG5cbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgdmlvbGluUG9pbnRzRXh0cmFjdG9yXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB2aW9saW5Qb2ludHNFeHRyYWN0b3IgPSBmdW5jdGlvbih2aW9saW5LZXksIHZpb2xpbkRhdGEpIHsgcmV0dXJuIHZpb2xpbkRhdGEucG9pbnRzIH1cbiAgICovXG4gIHZpb2xpbi52aW9saW5Qb2ludHNFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZpb2xpblBvaW50c0V4dHJhY3RvciA9IF8sIHZpb2xpbikgOiB2aW9saW5Qb2ludHNFeHRyYWN0b3I7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3JcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZpb2xpblBvaW50c0V4dHJhY3RvciA9IGZ1bmN0aW9uKHBvaW50S2V5LCB2aW9saW5LZXksIHZpb2xpbkRhdGEsIHZpb2xpblZhbHVlcykge3JldHVybiB2aW9saW5WYWx1ZXNbdmlvbGluS2V5XS52YWx1ZXNbcG9pbnRLZXldfVxuICAgKi9cbiAgdmlvbGluLnZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3IgPSBfLCB2aW9saW4pIDogdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3RvcjsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIHNlbGVjdGlvbiBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogQHBhcmFtIHtkMy5zZWxlY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBkMy5zZWxlY3Rpb259XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc2VsZWN0aW9uID0gc2VsZWN0aW9uXG4gICAqL1xuICB2aW9saW4uc2VsZWN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCB2aW9saW4pIDogc2VsZWN0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBkYXRhXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNkYXRhfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBvYmplY3R9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqL1xuICB2aW9saW4uZGF0YSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZGF0YSA9IF8sIHZpb2xpbikgOiBkYXRhOyB9O1xuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBvcmllbnQgb2YgdGhlIGJveGVzXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNvcmllbnR9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG9iamVjdH1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIHZpb2xpbi5vcmllbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9yaWVudCA9IF8sIHZpb2xpbikgOiBvcmllbnQ7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jc3BhY2VYfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVggPSB1bmRlZmluZWRcbiAgICovXG4gIHZpb2xpbi5zcGFjZVggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWCA9IF8sIHZpb2xpbikgOiBzcGFjZVg7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiB2ZXJ0aWNhbCBzcGFjZSBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3NwYWNlWX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VZID0gdW5kZWZpbmVkXG4gICAqL1xuICB2aW9saW4uc3BhY2VZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVkgPSBfLCB2aW9saW4pIDogc3BhY2VZOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IHZpb2xpbiBpcyBhbGxvd2VkIHRvIGdvIGJleW9uZCBzcGVjaWZpZWQgZGltZW5zaW9uc1xuICAgKiAoc2VlIHtAbGluayB2aW9saW4jb3ZlcmZsb3dRfSlcbiAgICogQHBhcmFtIHtib29sZWFufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgYm9vbGVhbn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvdmVyZmxvd1EgPSBmYWxzZVxuICAgKi9cbiAgdmlvbGluLm92ZXJmbG93USA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob3ZlcmZsb3dRID0gXywgdmlvbGluKSA6IG92ZXJmbG93UTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHdoZXRoZXIgb3Igbm90IHRvIHBsb3QgcG9pbnRzIHdpdGggdGhlIHZpb2xpbnNcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3BvaW50c1F9KVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBib29sZWFufVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHBvaW50c1EgPSBmYWxzZVxuICAgKi9cbiAgdmlvbGluLnBvaW50c1EgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHBvaW50c1EgPSBfLCB2aW9saW4pIDogcG9pbnRzUTsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZ3JvdXBpbmcgb2YgdGhlIGJveGVzXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNncm91cGluZ30pXG4gICAqIEBwYXJhbSB7QXJyYXlbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IEFycmF5W119XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZ3JvdXBpbmcgPSB1bmRlZmluZWRcbiAgICovXG4gIHZpb2xpbi5ncm91cGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZ3JvdXBpbmcgPSBfLCB2aW9saW4pIDogZ3JvdXBpbmc7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdmFsdWVFeHRyYWN0b3JcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3ZhbHVlRXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgdmlvbGluLnZhbHVlRXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2YWx1ZUV4dHJhY3RvciA9IF8sIHZpb2xpbikgOiB2YWx1ZUV4dHJhY3RvcjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzb3J0aW5nRnVuY3Rpb25cbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3NvcnRpbmdGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBmdW5jdGlvbn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIHZpb2xpbi5zb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNvcnRpbmdGdW5jdGlvbiA9IF8sIHZpb2xpbikgOiBzb3J0aW5nRnVuY3Rpb247IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBzY2FsZSBmb3Igd2hpY2ggdGhlIHZpb2xpbiB2YWx1ZXMgc2hvdWxkIGJlIHRyYW5zZm9ybWVkIGJ5XG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNzY2FsZX0pXG4gICAqIEBwYXJhbSB7ZDMuc2NhbGV9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBkMy5zY2FsZX1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzY2FsZSA9IGQzLnNjYWxlTGluZWFyKClcbiAgICovXG4gIHZpb2xpbi5zY2FsZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2NhbGUgPSBfLCB2aW9saW4pIDogc2NhbGU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcGFkZGluZyBmb3IgdGhlIGRvbWFpbiBvZiB0aGUgc2NhbGVcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI2RvbWFpblBhZGRpbmd9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBkb21haW5QYWRkaW5nID0gMC41XG4gICAqL1xuICB2aW9saW4uZG9tYWluUGFkZGluZyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoZG9tYWluUGFkZGluZyA9IF8sIHZpb2xpbikgOiBkb21haW5QYWRkaW5nOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIG9iamVjdFNwYWNlclxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jb2JqZWN0U3BhY2VyfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0U3BhY2VyID0gMC4wNVxuICAgKi9cbiAgdmlvbGluLm9iamVjdFNwYWNlciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0U3BhY2VyID0gXywgdmlvbGluKSA6IG9iamVjdFNwYWNlcjsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtaW5PYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNtaW5PYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWluT2JqZWN0U2l6ZSA9IDE1XG4gICAqL1xuICB2aW9saW4ubWluT2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWluT2JqZWN0U2l6ZSA9IF8sIHZpb2xpbikgOiBtaW5PYmplY3RTaXplOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG1heE9iamVjdFNpemVcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI21heE9iamVjdFNpemV9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBtYXhPYmplY3RTaXplID0gNTBcbiAgICovXG4gIHZpb2xpbi5tYXhPYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtYXhPYmplY3RTaXplID0gXywgdmlvbGluKSA6IG1heE9iamVjdFNpemU7IH07XG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RTdHJva2VXaWR0aFxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jb2JqZWN0U3Ryb2tlV2lkdGh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RTdHJva2VXaWR0aCA9IDJcbiAgICovXG4gIHZpb2xpbi5vYmplY3RTdHJva2VXaWR0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0U3Ryb2tlV2lkdGggPSBfLCB2aW9saW4pIDogb2JqZWN0U3Ryb2tlV2lkdGg7IH07XG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGNvbG9yRnVuY3Rpb25cbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI2NvbG9yRnVuY3Rpb259KVxuICAgKiBAcGFyYW0ge2NvbG9yRnVuY3Rpb259IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBjb2xvckZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGNvbG9yRnVuY3Rpb24gPSBjb2xvckZ1bmN0aW9uKClcbiAgICovXG4gIHZpb2xpbi5jb2xvckZ1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjb2xvckZ1bmN0aW9uID0gXywgdmlvbGluKSA6IGNvbG9yRnVuY3Rpb247IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgY29sb3JGdW5jdGlvblxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jY29sb3JGdW5jdGlvbn0pXG4gICAqIEBwYXJhbSB7Y29sb3JGdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IGNvbG9yRnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKVxuICAgKi9cbiAgdmlvbGluLnBvaW50Q29sb3JGdW5jID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChwb2ludENvbG9yRnVuYyA9IF8sIHZpb2xpbikgOiBwb2ludENvbG9yRnVuYzsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcG9pbnRSYWRpdXNcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3BvaW50UmFkaXVzfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgcG9pbnRSYWRpdXMgPSAyXG4gICAqL1xuICB2aW9saW4ucG9pbnRSYWRpdXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHBvaW50UmFkaXVzID0gXywgdmlvbGluKSA6IHBvaW50UmFkaXVzOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHBvaW50U3Ryb2tlV2lkdGhcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3BvaW50U3Ryb2tlV2lkdGh9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBwb2ludFN0cm9rZVdpZHRoID0gMlxuICAgKi9cbiAgdmlvbGluLnBvaW50U3Ryb2tlV2lkdGggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHBvaW50U3Ryb2tlV2lkdGggPSBfLCB2aW9saW4pIDogcG9pbnRTdHJva2VXaWR0aDsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgYmFja2dyb3VuZEZpbGxcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI2JhY2tncm91bmRGaWxsfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnXG4gICAqL1xuICB2aW9saW4uYmFja2dyb3VuZEZpbGwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhY2tncm91bmRGaWxsID0gXywgdmlvbGluKSA6IGJhY2tncm91bmRGaWxsOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIG5hbWVzcGFjZVxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jbmFtZXNwYWNlfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHt2aW9saW4gfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbmFtZXNwYWNlID0gJ2Qzc20tdmlvbGluJ1xuICAgKi9cbiAgdmlvbGluLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgdmlvbGluKSA6IG5hbWVzcGFjZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBvYmplY3RDbGFzc1xuICAgKiAoc2VlIHtAbGluayB2aW9saW4jb2JqZWN0Q2xhc3N9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBvYmplY3RDbGFzcyA9ICd0aWNrLWdyb3VwJ1xuICAgKi9cbiAgdmlvbGluLm9iamVjdENsYXNzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RDbGFzcyA9IF8sIHZpb2xpbikgOiBvYmplY3RDbGFzczsgfTtcblxuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdHJhbnNpdGlvbkR1cmF0aW9uXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiN0cmFuc2l0aW9uRHVyYXRpb259KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwXG4gICAqL1xuICB2aW9saW4udHJhbnNpdGlvbkR1cmF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0cmFuc2l0aW9uRHVyYXRpb24gPSBfLCB2aW9saW4pIDogdHJhbnNpdGlvbkR1cmF0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGVhc2VGdW5jXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNlYXNlRnVuY30pXG4gICAqIEBwYXJhbSB7ZDMuZWFzZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IGQzLmVhc2V9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwXG4gICAqL1xuICB2aW9saW4uZWFzZUZ1bmMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGVhc2VGdW5jID0gXywgdmlvbGluKSA6IGVhc2VGdW5jOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBxdWFydGlsZUtleVxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jcXVhcnRpbGVLZXl9KVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IHN0cmluZ31cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBxdWFydGlsZUtleSA9IFwicXVhcnRpbGVzXCJcbiAgICovXG4gIHZpb2xpbi5xdWFydGlsZUtleSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocXVhcnRpbGVLZXkgPSBfLCB2aW9saW4pIDogcXVhcnRpbGVLZXk7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgcXVhcnRpbGVLZXlzXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNxdWFydGlsZUtleXN9KVxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgc3RyaW5nW119XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgcXVhcnRpbGVLZXlzID0gW1wiUTBcIixcIlExXCIsXCJRMlwiLFwiUTNcIixcIlE0XCJdXG4gICAqL1xuICB2aW9saW4ucXVhcnRpbGVLZXlzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChxdWFydGlsZUtleXMgPSBfLCB2aW9saW4pIDogcXVhcnRpbGVLZXlzOyB9O1xuXG5cbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2aW9saW5LZXlzXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiN2aW9saW5LZXlzfSlcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHZpb2xpbktleXMgPSB1bmRlZmluZWRcbiAgICovXG4gIHZpb2xpbi52aW9saW5LZXlzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2aW9saW5LZXlzID0gXywgdmlvbGluKSA6IHZpb2xpbktleXM7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdmlvbGluVmFsdWVzXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiN2aW9saW5WYWx1ZXN9KVxuICAgKiBAcGFyYW0ge09iamVjdFtdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgT2JqZWN0W119XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdmlvbGluVmFsdWVzID0gdW5kZWZpbmVkXG4gICAqL1xuICB2aW9saW4udmlvbGluVmFsdWVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh2aW9saW5WYWx1ZXMgPSBfLCB2aW9saW4pIDogdmlvbGluVmFsdWVzOyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jb2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdFNpemUgPSB1bmRlZmluZWRcbiAgICovXG4gIHZpb2xpbi5vYmplY3RTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChvYmplY3RTaXplID0gXywgdmlvbGluKSA6IG9iamVjdFNpemU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgc3BhY2VyU2l6ZVxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jc3BhY2VyU2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgdmlvbGluXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNwYWNlclNpemUgPSB1bmRlZmluZWRcbiAgICovXG4gIHZpb2xpbi5zcGFjZXJTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZXJTaXplID0gXywgdmlvbGluKSA6IHNwYWNlclNpemU7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdG9vbHRpcFxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jdG9vbHRpcH0pXG4gICAqIEBwYXJhbSB7dG9vbHRpcH0gW189bm9uZV1cbiAgICogQHJldHVybnMge3Zpb2xpbiB8IHRvb2x0aXB9XG4gICAqIEBtZW1iZXJvZiB2aW9saW5cbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdG9vbHRpcCA9IHRvb2x0aXAoKVxuICAgKi9cbiAgdmlvbGluLnRvb2x0aXAgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHRvb2x0aXAgPSBfLCB2aW9saW4pIDogdG9vbHRpcDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBwb2ludHNUb29sdGlwXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNwb2ludHNUb29sdGlwfSlcbiAgICogQHBhcmFtIHt0b29sdGlwfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7dmlvbGluIHwgdG9vbHRpcH1cbiAgICogQG1lbWJlcm9mIHZpb2xpblxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBwb2ludHNUb29sdGlwID0gdG9vbHRpcCgpXG4gICAqL1xuICB2aW9saW4ucG9pbnRzVG9vbHRpcCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocG9pbnRzVG9vbHRpcCA9IF8sIHZpb2xpbikgOiBwb2ludHNUb29sdGlwOyB9O1xuXG5cblxuXG5cbiAgZnVuY3Rpb24gdmlvbGluICgpIHtcbiAgICAvLyBmb3IgY29udmVuaWVuY2UgaW4gaGFuZGxpbmcgb3JpZW50YXRpb24gc3BlY2lmaWMgdmFsdWVzXG4gICAgdmFyIGhvcml6b250YWxRID0gKG9yaWVudCA9PSAnaG9yaXpvbnRhbCcpID8gdHJ1ZSA6IGZhbHNlXG4gICAgdmFyIHZlcnRpY2FsUSA9ICFob3Jpem9udGFsUVxuXG4gICAgLy8gYmFja2dyb3VuZCBjbGlwaW5nIHJlY3RhbmdsZVxuICAgIHZhciBiZ2NwUmVjdCA9IHt4OjAsIHk6MCwgd2lkdGg6IHNwYWNlWCwgaGVpZ2h0OnNwYWNlWX1cbiAgICB2YXIgY29udGFpbmVyID0gc2V0dXBDb250YWluZXIoIHNlbGVjdGlvbiwgbmFtZXNwYWNlLCBiZ2NwUmVjdCwgYmFja2dyb3VuZEZpbGwgKTtcblxuICAgIC8vIGlmIGdyb3VwaW5nIGlzIHVuZGVmaW5lZCBzb3J0IHZpb2xpbktleXMgYnkgc29ydGluZ0Z1bmN0aW9uXG4gICAgdmFyIG9yZGVyZWQgPSAoZ3JvdXBpbmcgPT0gdW5kZWZpbmVkKSA/IGQzLmtleXMoZGF0YSkuc29ydChzb3J0aW5nRnVuY3Rpb24pIDogZ3JvdXBpbmdcblxuICAgIC8vIGNvbnNvbGUubG9nKG9yZGVyZWQpXG5cbiAgICB2aW9saW5LZXlzID0gZmxhdHRlbihvcmRlcmVkKVxuXG5cbiAgICB2YXIgY2FsY1ZhbHVlcyA9IG5lZWRlZFZpb2xpblZhbHVlcygpXG4gICAgLmhvcml6b250YWxRKGhvcml6b250YWxRKVxuICAgIC5xdWFydGlsZUtleXMocXVhcnRpbGVLZXlzKVxuICAgIC52aW9saW5Qb2ludHNFeHRyYWN0b3IodmlvbGluUG9pbnRzRXh0cmFjdG9yKVxuICAgIC52aW9saW5Qb2ludFZhbHVlRXh0cmFjdG9yKHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3IpXG5cblxuXG4gICAgLy8gYXVnbWVudCB2YWx1c1xuICAgIHZpb2xpbktleXMubWFwKGZ1bmN0aW9uKHZrLCBpKXsgY2FsY1ZhbHVlcyh2aywgZGF0YSkgfSlcblxuICAgIHZhciBudW1iZXJPZk9iamVjdHMgPSB2aW9saW5LZXlzLmxlbmd0aFxuXG4gICAgdmFyIG1pbiA9IFtdLmNvbmNhdCguLi52aW9saW5LZXlzLm1hcChmdW5jdGlvbihrLCBpKXtyZXR1cm4gZGF0YVtrXS5xdWFydGlsZXNbcXVhcnRpbGVLZXlzWzBdXX0pKVxuICAgIHZhciBtYXggPSBbXS5jb25jYXQoLi4udmlvbGluS2V5cy5tYXAoZnVuY3Rpb24oaywgaSl7cmV0dXJuIGRhdGFba10ucXVhcnRpbGVzW3F1YXJ0aWxlS2V5c1txdWFydGlsZUtleXMubGVuZ3RoIC0gMV1dfSkpXG4gICAgdmFyIGV4dGVudCA9IFtNYXRoLm1pbiguLi5taW4pIC0gZG9tYWluUGFkZGluZywgTWF0aC5tYXgoLi4ubWF4KSArIGRvbWFpblBhZGRpbmddXG4gICAgLy8gY29uc29sZS5sb2coZXh0ZW50LCB2aW9saW5WYWx1ZXMsIG9yZGVyZWQpXG5cbiAgICAvLyBzZXQgdGhlIHNjYWxlXG4gICAgc2NhbGUuZG9tYWluKGV4dGVudCkucmFuZ2UoaG9yaXpvbnRhbFEgPyBbMCxzcGFjZVldIDogWzAsIHNwYWNlWF0pXG4gICAgdmFyIHNwYWNlID0gaG9yaXpvbnRhbFEgPyBzcGFjZVggOiBzcGFjZVlcbiAgICAvLyBjYWxjdWxhdGUgb2JqZWN0IHNpemVcbiAgICBvYmplY3RTaXplID0gY2FsY3VsYXRlV2lkdGhPZk9iamVjdChzcGFjZSwgbnVtYmVyT2ZPYmplY3RzLCBtaW5PYmplY3RTaXplLCBtYXhPYmplY3RTaXplLCBvYmplY3RTcGFjZXIsIG92ZXJmbG93USlcbiAgICAvLyBjYWxjdWxhdGUgc3BhY2VyIHNpemUgaWYgbmVlZGVkXG4gICAgc3BhY2VyU2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXIob3JkZXJlZCwgc3BhY2UsIG9iamVjdFNpemUsIG51bWJlck9mT2JqZWN0cywgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgLy8gbWFrZSB0aGUgbmVzdGVkIGdyb3Vwc1xuICAgIHZhciBzcGFjZXJGdW5jdGlvbiA9IGdyb3VwaW5nU3BhY2VyKClcbiAgICAuaG9yaXpvbnRhbFEoaG9yaXpvbnRhbFEpLnNjYWxlKHNjYWxlKS5tb3ZlYnkoJ2NhdGVnb3J5JykubnVtYmVyT2ZPYmplY3RzKG51bWJlck9mT2JqZWN0cylcbiAgICAub2JqZWN0Q2xhc3Mob2JqZWN0Q2xhc3MpLm9iamVjdFNpemUob2JqZWN0U2l6ZSkuc3BhY2VyU2l6ZShzcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcbiAgICAubmFtZXNwYWNlKG5hbWVzcGFjZSlcblxuICAgIC8vIG1vdmUgc3R1ZmZcbiAgICBzcGFjZXJGdW5jdGlvbihjb250YWluZXIsIG9yZGVyZWQsIDApXG4gICAgLy8gY29uc29sZS5sb2codmlvbGluS2V5cywgb3JkZXJlZCwgY29udGFpbmVyLnNlbGVjdEFsbCgnZzpub3QoLnRvLXJlbW92ZSkuJytvYmplY3RDbGFzcykubm9kZXMoKSlcblxuICAgIC8vIGZvciBjb2xvciBmdW5jdGlvblxuICAgIHZhciBwYXJlbnRJbmRleEFycmF5ID0gW11cbiAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnOm5vdCgudG8tcmVtb3ZlKS4nK29iamVjdENsYXNzKVxuICAgIC5lYWNoKGZ1bmN0aW9uKGQsIGkpe2lmIChoYXNRKHZpb2xpbktleXMsIGQpKXsgcGFyZW50SW5kZXhBcnJheS5wdXNoKE51bWJlcihkMy5zZWxlY3QodGhpcykuYXR0cigncGFyZW50LWluZGV4JykpKX19KVxuXG4gICAgLy8gdXBkYXRlIGNvbG9yIGZ1bmN0aW9uXG4gICAgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24uY29sb3JCeSgpID09ICdpbmRleCdcbiAgICA/IGNvbG9yRnVuY3Rpb24uZGF0YUV4dGVudChbMCwgTWF0aC5tYXgoLi4ucGFyZW50SW5kZXhBcnJheSldKVxuICAgIDogY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KGV4dGVudClcblxuICAgIC8qIHZpb2xpaW4gc3BlY2lmaWMgbmVlZHMgKi9cblxuXG4gICAgdmFyIGZyZXF1ZW5jeU1heCA9IE1hdGgubWF4KC4uLltdLmNvbmNhdCguLi52aW9saW5LZXlzLm1hcChmdW5jdGlvbihrLCBpKXtyZXR1cm4gZDMubWF4KGRhdGFba10uZnJlcXVlbmNpZXMpfSkpKVxuICAgIHZhciB2U2NhbGUgPSBkMy5zY2FsZUxpbmVhcigpLmRvbWFpbihbMCwgZnJlcXVlbmN5TWF4XSkucmFuZ2UoWzAsIG9iamVjdFNpemUgLyAyXSlcblxuICAgIHZhciBsQXJlYSA9IGQzLmxpbmUoKVxuICAgIC54KGZ1bmN0aW9uKGQsIGkpeyByZXR1cm4gaG9yaXpvbnRhbFEgPyAtdlNjYWxlKGQueCkgOiBzY2FsZShkLngpfSlcbiAgICAueShmdW5jdGlvbihkLCBpKXsgcmV0dXJuIGhvcml6b250YWxRID8gc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKGQueSkgOiAtdlNjYWxlKGQueSl9KVxuICAgIC5jdXJ2ZShkMy5jdXJ2ZUJhc2lzKVxuICAgIHZhciByQXJlYSA9IGQzLmxpbmUoKVxuICAgIC54KGZ1bmN0aW9uKGQsIGkpeyByZXR1cm4gaG9yaXpvbnRhbFEgPyB2U2NhbGUoZC54KSA6IHNjYWxlKGQueCl9KVxuICAgIC55KGZ1bmN0aW9uKGQsIGkpeyByZXR1cm4gaG9yaXpvbnRhbFEgPyBzY2FsZShleHRlbnRbMV0pIC0gc2NhbGUoZC55KSA6IHZTY2FsZShkLnkpfSlcbiAgICAuY3VydmUoZDMuY3VydmVCYXNpcylcblxuXG5cblxuXG5cbiAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnOm5vdCgudG8tcmVtb3ZlKS4nK29iamVjdENsYXNzKS5lYWNoKGZ1bmN0aW9uKGtleSwgaSl7XG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKSxcbiAgICAgIGN1cnJlbnREYXRhID0gZGF0YVtrZXldXG4gICAgICAvLyBuZWVkZWQgYmVjYXVzZSBidWcgaW4gc2VsZWN0aW5nIC50by1yZW1vdmVcbiAgICAgIGlmICghaGFzUSh2aW9saW5LZXlzLCBrZXkpKSB7cmV0dXJufVxuICAgICAgdmFyXG4gICAgICBpID0gdC5hdHRyKCdwYXJlbnQtaW5kZXgnKSA9PSB1bmRlZmluZWQgPyBpIDogdC5hdHRyKCdwYXJlbnQtaW5kZXgnKSxcbiAgICAgIGZpbGxDb2xvciA9IGNvbG9yRnVuY3Rpb24oa2V5LCBjdXJyZW50RGF0YSwgaSwgJ2ZpbGwnKSwgLy8gcHJldmVudCBkdXBsaWNhdGUgY29tcHV0YXRpb25cbiAgICAgIHN0cm9rZUNvbG9yID0gY29sb3JGdW5jdGlvbihrZXksIGN1cnJlbnREYXRhLCBpLCAnc3Ryb2tlJyksXG4gICAgICBhcmVhID0gc2FmZVNlbGVjdCh0LCAnZycsICdhcmVhJyksXG4gICAgICBsYSA9IHNhZmVTZWxlY3QoYXJlYSwgJ3BhdGgnLCAnbGVmdCcpLFxuICAgICAgcmEgPSBzYWZlU2VsZWN0KGFyZWEsICdwYXRoJywgJ3JpZ2h0JyksXG4gICAgICBxdWFydHMgPSBzYWZlU2VsZWN0KHQsICdnJywgJ3F1YXJ0cycpLFxuICAgICAgbHEzID0gc2FmZVNlbGVjdChxdWFydHMsICdsaW5lJywgJ3EzJyksXG4gICAgICBscTEgPSBzYWZlU2VsZWN0KHF1YXJ0cywgJ2xpbmUnLCAncTEnKSxcbiAgICAgIHEzID0gY3VycmVudERhdGEucXVhcnRpbGVzW3F1YXJ0aWxlS2V5c1szXV0sXG4gICAgICBxMiA9IGN1cnJlbnREYXRhLnF1YXJ0aWxlc1txdWFydGlsZUtleXNbMl1dLFxuICAgICAgcTEgPSBjdXJyZW50RGF0YS5xdWFydGlsZXNbcXVhcnRpbGVLZXlzWzFdXVxuXG4gICAgICB0LmF0dHIoJ3RyYW5zZm9ybScsIGhvcml6b250YWxRID8gJ3RyYW5zbGF0ZSgnK29iamVjdFNpemUgLyAyKycsMCknIDogJ3RyYW5zbGF0ZSgwLCcrb2JqZWN0U2l6ZSAvIDIrJyknICApXG4gICAgICAvLyBkcmF3IGN1cnZlXG4gICAgICBsYS50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5hdHRyKCdkJywgZnVuY3Rpb24oZGQsIGlpKXsgcmV0dXJuIGxBcmVhKGN1cnJlbnREYXRhLmNvbnRvdXIpfSlcbiAgICAgIC5hdHRyKCdmaWxsJywgZmlsbENvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZScsIHN0cm9rZUNvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIG9iamVjdFN0cm9rZVdpZHRoKVxuXG4gICAgICByYS50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5hdHRyKCdkJywgZnVuY3Rpb24oZGQsIGlpKXsgcmV0dXJuIHJBcmVhKGN1cnJlbnREYXRhLmNvbnRvdXIpfSlcbiAgICAgIC5hdHRyKCdmaWxsJywgZmlsbENvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZScsIHN0cm9rZUNvbG9yKVxuICAgICAgLmF0dHIoJ3N0cm9rZS13aWR0aCcsIG9iamVjdFN0cm9rZVdpZHRoKVxuXG4gICAgICBhcmVhLm5vZGUoKS5hZGRFdmVudExpc3RlbmVyKCdtb3VzZW92ZXInLCBmdW5jdGlvbihkZCwgaWkpe1xuICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnLicrb2JqZWN0Q2xhc3MpLnN0eWxlKCdvcGFjaXR5JywgMC4yKVxuICAgICAgICB0LnN0eWxlKCdvcGFjaXR5JywgMSlcbiAgICAgICAgbGEuYXR0cignc3Ryb2tlLXdpZHRoJyxvYmplY3RTdHJva2VXaWR0aCoyKVxuICAgICAgICByYS5hdHRyKCdzdHJva2Utd2lkdGgnLG9iamVjdFN0cm9rZVdpZHRoKjIpXG4gICAgICB9KVxuICAgICAgYXJlYS5ub2RlKCkuYWRkRXZlbnRMaXN0ZW5lcignbW91c2VvdXQnLCBmdW5jdGlvbihkZCwgaWkpe1xuICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnLicrb2JqZWN0Q2xhc3MpLnN0eWxlKCdvcGFjaXR5JywgMSlcbiAgICAgICAgbGEuYXR0cignc3Ryb2tlLXdpZHRoJyxvYmplY3RTdHJva2VXaWR0aClcbiAgICAgICAgcmEuYXR0cignc3Ryb2tlLXdpZHRoJyxvYmplY3RTdHJva2VXaWR0aClcbiAgICAgIH0pXG5cbiAgICAgIGlmIChwb2ludHNRKSB7XG4gICAgICAgIHZhciBwdHNDb250YWluZXIgPSBzYWZlU2VsZWN0KHQsICdnJywgJ3BvaW50cycpXG4gICAgICAgIHZhciBwdHMgPSBwdHNDb250YWluZXIuc2VsZWN0QWxsKCcucG9pbnQnKS5kYXRhKGN1cnJlbnREYXRhLnBvaW50S2V5cylcbiAgICAgICAgcHRzLm9uKCdtb3VzZW92ZXInLCBudWxsKVxuXG5cbiAgICAgICAgdmFyIHB0c0V4aXQgPSBwdHMuZXhpdCgpLnRyYW5zaXRpb24oKS5lYXNlKGVhc2VGdW5jKS5kdXJhdGlvbih0cmFuc2l0aW9uRHVyYXRpb24pXG4gICAgICAgIC5hdHRyKCdyJywgMClcbiAgICAgICAgLmF0dHIoJ2N5JywgaG9yaXpvbnRhbFEgPyBzY2FsZShleHRlbnRbMV0pIC0gc2NhbGUocTIpIDogdlNjYWxlKDApKVxuICAgICAgICAuYXR0cignY3gnLCBob3Jpem9udGFsUSA/IHZTY2FsZSgwKSA6IHNjYWxlKHEyKSkucmVtb3ZlKClcblxuICAgICAgICB2YXIgcHRzRW50ZXIgPSBwdHMuZW50ZXIoKS5hcHBlbmQoJ2NpcmNsZScpLmF0dHIoJ2NsYXNzJywgJ3BvaW50JykuYXR0cigncicsIDApXG4gICAgICAgIC5hdHRyKCdjeCcsIGhvcml6b250YWxRID8gMCA6IHNjYWxlKHEyKSlcbiAgICAgICAgLmF0dHIoJ2N5JywgaG9yaXpvbnRhbFEgPyBzY2FsZShxMikgOiAwKVxuXG4gICAgICAgIHB0cyA9IHB0cy5tZXJnZShwdHNFbnRlcilcblxuICAgICAgICAvLyBjb25zb2xlLmxvZyhwb2ludHNUb29sdGlwLmhlYWRlcigpKVxuXG4gICAgICAgIHZhciBwVFRpcHMgPSBUVGlwKCkuc2VsZWN0aW9uKHB0cykuZGF0YSh2aW9saW5Qb2ludHNFeHRyYWN0b3Ioa2V5LCBjdXJyZW50RGF0YSkpXG4gICAgICAgIC5oZWFkZXIocG9pbnRzVG9vbHRpcC5oZWFkZXIoKSlcbiAgICAgICAgLmtleXMocG9pbnRzVG9vbHRpcC5rZXlzKCkpXG4gICAgICAgIC52YWx1ZXMocG9pbnRzVG9vbHRpcC52YWx1ZXMoKSlcblxuICAgICAgICBwVFRpcHMoKVxuXG4gICAgICAgIHZhciBwTWluID0gZDMubWluKGN1cnJlbnREYXRhLnBvaW50VmFsdWVzKSwgcE1heCA9IGQzLm1heChjdXJyZW50RGF0YS5wb2ludFZhbHVlcylcblxuXG5cbiAgICAgICAgcHRzLnRyYW5zaXRpb24oKS5kdXJhdGlvbih0cmFuc2l0aW9uRHVyYXRpb24pLmVhc2UoZWFzZUZ1bmMpLmF0dHIoJ3InLCBwb2ludFJhZGl1cylcbiAgICAgICAgLmF0dHIoJ2N5JywgZnVuY3Rpb24ocG9pbnRLZXksIGlpKXtcbiAgICAgICAgICB2YXIgZGQgPSBjdXJyZW50RGF0YS5wb2ludFZhbHVlc1tpaV1cbiAgICAgICAgICBpZiAoaG9yaXpvbnRhbFEpIHsgcmV0dXJuIHNjYWxlKGV4dGVudFsxXSkgLSBzY2FsZShkZCkgfVxuICAgICAgICAgIHZhciBqID0gd2hpY2hCaW4oY3VycmVudERhdGEuYmlubmVkLCBkZClcbiAgICAgICAgICB2YXIgciA9IE1hdGgucmFuZG9tKClcbiAgICAgICAgICB2YXIgbiA9IHZTY2FsZShyICogY3VycmVudERhdGEuZnJlcXVlbmNpZXNbal0gKiAwLjUpXG4gICAgICAgICAgdmFyIGsgPSBNYXRoLnJhbmRvbSgpID4gMC41ID8gbiA6IC1uXG4gICAgICAgICAgcmV0dXJuIGtcbiAgICAgICAgfSlcbiAgICAgICAgLmF0dHIoJ2N4JywgZnVuY3Rpb24ocG9pbnRLZXksIGlpKXtcbiAgICAgICAgICB2YXIgZGQgPSBjdXJyZW50RGF0YS5wb2ludFZhbHVlc1tpaV1cbiAgICAgICAgICBpZiAoaG9yaXpvbnRhbFEpIHtcbiAgICAgICAgICAgIHZhciBqID0gd2hpY2hCaW4oY3VycmVudERhdGEuYmlubmVkLCBkZClcbiAgICAgICAgICAgIHZhciByID0gTWF0aC5yYW5kb20oKVxuICAgICAgICAgICAgdmFyIG4gPSB2U2NhbGUociAqIGN1cnJlbnREYXRhLmZyZXF1ZW5jaWVzW2pdICogMC41KVxuICAgICAgICAgICAgdmFyIGsgPSBNYXRoLnJhbmRvbSgpID4gMC41ID8gbiA6IC1uXG4gICAgICAgICAgICByZXR1cm4ga1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gc2NhbGUoZGQpXG4gICAgICAgIH0pXG4gICAgICAgIC5hdHRyKCdzdHJva2UnLCBmdW5jdGlvbihkZCwgaWkpIHsgdmFyIGRkID0gY3VycmVudERhdGEucG9pbnRWYWx1ZXNbaWldOyByZXR1cm4gcG9pbnRDb2xvckZ1bmMoZGQsICdzdHJva2UnLCBzdHJva2VDb2xvciwgcE1pbiwgcE1heCkgfSlcbiAgICAgICAgLmF0dHIoJ2ZpbGwnICAsIGZ1bmN0aW9uKGRkLCBpaSkgeyB2YXIgZGQgPSBjdXJyZW50RGF0YS5wb2ludFZhbHVlc1tpaV07IHJldHVybiBwb2ludENvbG9yRnVuYyhkZCwgJ2ZpbGwnICAsIHN0cm9rZUNvbG9yLCBwTWluLCBwTWF4KSB9KVxuICAgICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgcG9pbnRTdHJva2VXaWR0aClcblxuICAgICAgICBwdHNDb250YWluZXIuc2VsZWN0QWxsKCdjaXJjbGUucG9pbnQnKS5vbignbW91c2VvdmVyJywgZnVuY3Rpb24oZGQsIGlpKXtcbiAgICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnLicrb2JqZWN0Q2xhc3MpLnN0eWxlKCdvcGFjaXR5JywgMC4yKVxuICAgICAgICAgIHQuc3R5bGUoJ29wYWNpdHknLCAxKVxuICAgICAgICAgIGxhLmF0dHIoJ3N0cm9rZS13aWR0aCcsb2JqZWN0U3Ryb2tlV2lkdGgqMilcbiAgICAgICAgICByYS5hdHRyKCdzdHJva2Utd2lkdGgnLG9iamVjdFN0cm9rZVdpZHRoKjIpXG5cbiAgICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCcucG9pbnQnKS5zdHlsZSgnb3BhY2l0eScsIDAuMilcbiAgICAgICAgICBkMy5zZWxlY3QodGhpcykuc3R5bGUoJ29wYWNpdHknLCAxKS5hdHRyKCdyJywgcG9pbnRSYWRpdXMgKiAyKS5hdHRyKCdzdHJva2Utd2lkdGgnLHBvaW50U3Ryb2tlV2lkdGgqMilcbiAgICAgICAgfSlcbiAgICAgICAgcHRzQ29udGFpbmVyLnNlbGVjdEFsbCgnY2lyY2xlLnBvaW50Jykub24oJ21vdXNlb3V0JywgZnVuY3Rpb24oZGQsIGlpKXtcbiAgICAgICAgICB2YXIgZSA9IGRvY3VtZW50LmNyZWF0ZUV2ZW50KCdTVkdFdmVudHMnKVxuICAgICAgICAgIGUuaW5pdEV2ZW50KCdtb3VzZW91dCcsdHJ1ZSx0cnVlKTtcbiAgICAgICAgICBhcmVhLm5vZGUoKS5kaXNwYXRjaEV2ZW50KGUpXG5cbiAgICAgICAgICBjb250YWluZXIuc2VsZWN0QWxsKCcucG9pbnQnKS5zdHlsZSgnb3BhY2l0eScsIDEpXG4gICAgICAgICAgZDMuc2VsZWN0KHRoaXMpLmF0dHIoJ3N0cm9rZS13aWR0aCcsIHBvaW50U3Ryb2tlV2lkdGgpLmF0dHIoJ3InLCBwb2ludFJhZGl1cylcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICAgIGVsc2Uge1xuICAgICAgICBjVi5zZWxlY3RBbGwoJy5wb2ludCcpXG4gICAgICAgIC50cmFuc2l0aW9uKCkuZHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlKGVhc2VGdW5jKVxuICAgICAgICAuYXR0cigncicsIDApXG4gICAgICAgIC5hdHRyKCdjeScsIGhvcml6b250YWxRID8gc2NhbGUoZXh0ZW50WzFdKSAtIHNjYWxlKHEyKSA6IHZTY2FsZSgwKSlcbiAgICAgICAgLmF0dHIoJ2N4JywgaG9yaXpvbnRhbFEgPyB2U2NhbGUoMCkgOiBzY2FsZShxMikpXG4gICAgICAgIC5yZW1vdmUoKVxuICAgICAgfVxuXG5cbiAgICB9KVxuXG5cbiAgICB0b29sdGlwLnNlbGVjdGlvbihjb250YWluZXIuc2VsZWN0QWxsKCdnOm5vdCgudG8tcmVtb3ZlKS4nK29iamVjdENsYXNzICsgJyAuYXJlYScpKVxuICAgIGlmICh0b29sdGlwLmRhdGEoKSA9PSB1bmRlZmluZWQpIHt0b29sdGlwLmRhdGEoZGF0YSl9XG4gICAgdG9vbHRpcCgpXG4gICAgaWYgKHRvb2x0aXAudmFsdWVzKCkgPT0gdW5kZWZpbmVkKSB7XG4gICAgICB0b29sdGlwLnZhbHVlcyhbXG4gICAgICAgIGZ1bmN0aW9uKGN1cnJlbnREYXRhLCB0b29sdGlwS2V5KXsgcmV0dXJuIGN1cnJlbnREYXRhWydxdWFydGlsZXMnXVt0b29sdGlwS2V5XSB9LFxuICAgICAgICBmdW5jdGlvbihjdXJyZW50RGF0YSwgdG9vbHRpcEtleSl7IHJldHVybiBjdXJyZW50RGF0YVsncXVhcnRpbGVzJ11bdG9vbHRpcEtleV0gfSxcbiAgICAgICAgZnVuY3Rpb24oY3VycmVudERhdGEsIHRvb2x0aXBLZXkpeyByZXR1cm4gY3VycmVudERhdGFbJ3F1YXJ0aWxlcyddW3Rvb2x0aXBLZXldIH0sXG4gICAgICAgIGZ1bmN0aW9uKGN1cnJlbnREYXRhLCB0b29sdGlwS2V5KXsgcmV0dXJuIGN1cnJlbnREYXRhWydxdWFydGlsZXMnXVt0b29sdGlwS2V5XSB9LFxuICAgICAgICBmdW5jdGlvbihjdXJyZW50RGF0YSwgdG9vbHRpcEtleSl7IHJldHVybiBjdXJyZW50RGF0YVsncXVhcnRpbGVzJ11bdG9vbHRpcEtleV0gfVxuICAgICAgXSlcblxuICAgIH1cblxuICB9XG5cbiAgcmV0dXJuIHZpb2xpblxufVxuXG5cblxuXG5cbi8qKlxuKiBQcm9kdWNlcyB0aGUgZnVuY3Rpb24gd2hpY2ggbWFuaXB1bGF0ZXMgdGhlIHZpb2xpbiBkYXRhIHRvIGhhdmUgdmFsdWVzIG5lZWRlZFxuKiBmb3IgcmVuZGVyaW5nIHRoZSB2aW9saW5zIGFzIHN2Zy5cbiogQHJldHVybnMge2Z1bmN0aW9ufSBjYWxjdWxhdGVWaW9saW5WYWx1ZXNcbiogQG5hbWVzcGFjZSBuZWVkZWRWaW9saW5WYWx1ZXNcbiovXG5mdW5jdGlvbiBuZWVkZWRWaW9saW5WYWx1ZXMoKSB7XG4gIHZhclxuICAvKipcbiAgKiBXaGV0aGVyIG9yIG5vdCB0aGUgb3JpZW50YXRpb24gb2YgdGhlIHZpb2xpbnMgYXJlIGhvcml6b250YWxcbiAgKiAoc2VlIHtAbGluayB2aW9saW4jb3JpZW50fSlcbiAgKiBAcGFyYW0ge09iamVjdH0gW2hvcml6b250YWxRPXRydWVdXG4gICogQG1lbWJlcm9mIG5lZWRlZFZpb2xpblZhbHVlcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgaG9yaXpvbnRhbFEgPSB0cnVlLFxuICAvKipcbiAgKiBLZXlzIHRvIGJlIHB1dCBpbnRvIHRoZSBxdWFydGlsZXMgaWYgdGhleSBuZWVkIHRvIGJlIGNhbGN1bGF0ZWQuXG4gICogKHNlZSB7QGxpbmsgdmlvbGluI3F1YXJ0aWxlS2V5c30pXG4gICogQHBhcmFtIHtPYmplY3R9IFtxdWFydGlsZUtleXM9WydRMCcsICdRMScsICdRMicsICdRMycsICdRNCddXVxuICAqIEBtZW1iZXJvZiBuZWVkZWRWaW9saW5WYWx1ZXMjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHF1YXJ0aWxlS2V5cyA9IFsnUTAnLCAnUTEnLCAnUTInLCAnUTMnLCAnUTQnXSxcbiAgLyoqXG4gICogRnVuY3Rpb24gd2hpY2ggZ2l2ZW4gdGhlIGtleSBvZiB0aGUgdmlvbGluIGFuZCB0aGF0IGtleSdzIGFzc29jaWF0ZWQgdmFsdWVcbiAgKiByZXR1cm5zIHRoZSBvYmplY3QgY29uc2l0aW5nIG9mIHRoZSBwb2ludHMgb2YgdGhlIHZpb2xpblxuICAqIChzZWUge0BsaW5rIHZpb2xpbiN2aW9saW5Qb2ludHNFeHRyYWN0b3J9KVxuICAqIEBwYXJhbSB7T2JqZWN0fSBbdmlvbGluUG9pbnRzRXh0cmFjdG9yPWZ1bmN0aW9uKHZpb2xpbktleSwgdmlvbGluRGF0YSkgeyByZXR1cm4gdmlvbGluRGF0YS5wb2ludHMgfV1cbiAgKiBAbWVtYmVyb2YgbmVlZGVkVmlvbGluVmFsdWVzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2aW9saW5Qb2ludHNFeHRyYWN0b3IsXG4gIC8qKlxuICAqIEZ1bmN0aW9uIHdoaWNoIGdpdmVuIHRoZSBrZXkgb2YgdGhlIGN1cnJlbnQgcG9pbnQgYW5kIHRoZSBvYmplY3Qgb2YgcG9pbnRzIGZvciB0aGVcbiAgKiB2aW9saW4sIHJldHVybnMgdGhlIG51bWVyaWNhbCB2YWx1ZSBvZiB0aGUgcG9pbnRcbiAgKiAoc2VlIHtAbGluayB2aW9saW4jdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3Rvcn0pXG4gICogQHBhcmFtIHtPYmplY3R9IFt2aW9saW5Qb2ludFZhbHVlRXh0cmFjdG9yPWZ1bmN0aW9uKHZpb2xpblBvaW50S2V5LCB2aW9saW5Qb2ludERhdGEpIHsgcmV0dXJuIHZpb2xpblBvaW50RGF0YVt2aW9saW5Qb2ludEtleV0udmFsdWUgfV1cbiAgKiBAbWVtYmVyb2YgbmVlZGVkVmlvbGluVmFsdWVzI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2aW9saW5Qb2ludFZhbHVlRXh0cmFjdG9yXG5cblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGhvcml6b250YWxRXG4gICAqIChzZWUge0BsaW5rIHZpb2xpbiNvcmllbnR9KVxuICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtjYWxjdWxhdGVWaW9saW5WYWx1ZXMgfCBib29sZWFufVxuICAgKiBAbWVtYmVyb2YgY2FsY3VsYXRlVmlvbGluVmFsdWVzXG4gICAqIEBwcm9wZXJ0eVxuICAgKlxuICAgKiBieSBkZWZhdWx0IGhvcml6b250YWxRID0gdHJ1ZVxuICAgKi9cbiAgY2FsY3VsYXRlVmlvbGluVmFsdWVzLmhvcml6b250YWxRID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChob3Jpem9udGFsUT1fLCBjYWxjdWxhdGVWaW9saW5WYWx1ZXMpIDogaG9yaXpvbnRhbFEgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHF1YXJ0aWxlS2V5c1xuICAgKiAoc2VlIHtAbGluayB2aW9saW4jcXVhcnRpbGVLZXlzfSlcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2NhbGN1bGF0ZVZpb2xpblZhbHVlcyB8IHN0cmluZ1tdfVxuICAgKiBAbWVtYmVyb2YgY2FsY3VsYXRlVmlvbGluVmFsdWVzXG4gICAqIEBwcm9wZXJ0eVxuICAgKlxuICAgKiBieSBkZWZhdWx0IHF1YXJ0aWxlS2V5cyA9IFtcIlEwXCIsXCJRMVwiLFwiUTJcIixcIlEzXCIsXCJRNFwiXVxuICAgKi9cbiAgY2FsY3VsYXRlVmlvbGluVmFsdWVzLnF1YXJ0aWxlS2V5cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocXVhcnRpbGVLZXlzPV8sIGNhbGN1bGF0ZVZpb2xpblZhbHVlcykgOiBxdWFydGlsZUtleXMgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHZpb2xpblBvaW50c0V4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayB2aW9saW4jdmlvbGluUG9pbnRzRXh0cmFjdG9yfSlcbiAgICogQHBhcmFtIHtmdW5jdGlvbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2NhbGN1bGF0ZVZpb2xpblZhbHVlcyB8IGZ1bmN0aW9ufVxuICAgKiBAbWVtYmVyb2YgY2FsY3VsYXRlVmlvbGluVmFsdWVzXG4gICAqIEBwcm9wZXJ0eVxuICAgKlxuICAgKiBieSBkZWZhdWx0IHZpb2xpblBvaW50c0V4dHJhY3RvciA9IGZ1bmN0aW9uKHZpb2xpbktleSwgdmlvbGluRGF0YSkgeyByZXR1cm4gdmlvbGluRGF0YS5wb2ludHMgfVxuICAgKi9cbiAgY2FsY3VsYXRlVmlvbGluVmFsdWVzLnZpb2xpblBvaW50c0V4dHJhY3RvciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodmlvbGluUG9pbnRzRXh0cmFjdG9yPV8sIGNhbGN1bGF0ZVZpb2xpblZhbHVlcykgOiB2aW9saW5Qb2ludHNFeHRyYWN0b3IgfVxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3JcbiAgICogKHNlZSB7QGxpbmsgdmlvbGluI3Zpb2xpblBvaW50VmFsdWVFeHRyYWN0b3J9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7Y2FsY3VsYXRlVmlvbGluVmFsdWVzIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBjYWxjdWxhdGVWaW9saW5WYWx1ZXNcbiAgICogQHByb3BlcnR5XG4gICAqXG4gICAqIGJ5IGRlZmF1bHQgdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKHZpb2xpblBvaW50S2V5LCB2aW9saW5Qb2ludERhdGEpIHsgcmV0dXJuIHZpb2xpblBvaW50RGF0YVt2aW9saW5Qb2ludEtleV0udmFsdWUgfVxuICAgKi9cbiAgY2FsY3VsYXRlVmlvbGluVmFsdWVzLnZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3I9XywgY2FsY3VsYXRlVmlvbGluVmFsdWVzKSA6IHZpb2xpblBvaW50VmFsdWVFeHRyYWN0b3IgfVxuXG5cbiAgLyoqXG4gICogVGhlIGZ1bmN0aW9uIHByb2R1Y2VkIGJ5IG5lZWRlZFZpb2xpblZhbHVlcy5cbiAgKlxuICAqIEFkZHMgdGhlIGRhdGEgbmVlZCB0byByZW5kZXIgdGhlIHZpb2xpbiBhcyBhbiBzdmdcbiAgKiBAcGFyYW0ge3N0cmluZ30gdmlvbGluS2V5IHRoZSBrZXkgb2YgdGhlIGN1cnJlbnQgdmlvbGluXG4gICogQHBhcmFtIHtvYmplY3R9IGRhdGEgdGhlIG9iamVjdCBjb25zaXN0aW5nIG9mIHZpb2xpbktleSAtIHZhbHVlcyAodmlvbGluIGRhdGEpIHBhaXJzXG4gICogQHJldHVybnMge25vbmV9IHRoaXMgZnVuY3Rpb24gbWFuaXB1bGF0ZXMgdGhlIHBhc3NlZCBkYXRhIG9iamVjdCBhZGRpbmcgdGhlIGZvbGxvd2luZyBrZXlzXG4gICpcbiAgKiBkYXRhW3Zpb2xpbktleV0uYmlubmVkIC8vIHRoZSBiaW5uZWQgdmFsdWVzIG9mIHRoZSBwb2ludHNcbiAgKlxuICAqIGRhdGFbdmlvbGluS2V5XS5mcmVxdWVuY2llcyAvLyB0aGUgbGlzdCBjb25zaXN0aW5nIG9mIHRoZSBsZW5ndGggb2YgZWFjaCBiaW5cbiAgKlxuICAqIGRhdGFbdmlvbGluS2V5XS5jb250b3VyIC8vIHRoZSBwb2ludHMgZGVwaWN0aW5nIHRoZSBjb250b3VyIG9mIDEvMiBvZiB0aGUgdmlvbGluXG4gICpcbiAgKiBkYXRhW3Zpb2xpbktleV0ucXVhcnRpbGVzIC8vIHRoZSBxdWFydGlsZXMgb2YgdGhlIHBvaW50cycgdmFsdWVzXG4gICpcbiAgKiBkYXRhW3Zpb2xpbktleV0ucG9pbnRLZXlzIC8vIHRoZSBrZXlzIGFzc29jaWF0ZWQgd2l0aCB0aGUgcG9pbnRzXG4gICpcbiAgKiBkYXRhW3Zpb2xpbktleV0ucG9pbnRWYWx1ZXMgLy8gdGhlIG51bWVyaWNhbCB2YWx1ZXMgb2YgdGhlIHBvaW50c1xuICAqXG4gICogQG1lbWJlcm9mIG5lZWRlZFZpb2xpblZhbHVlcyNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZnVuY3Rpb24gY2FsY3VsYXRlVmlvbGluVmFsdWVzKHZpb2xpbktleSwgZGF0YSkge1xuICAgIC8vIGRhdGEgZm9yIHRoZSBjdXJyZW50IHZpb2xpblxuICAgIHZhciB2aW9saW5EYXRhID0gZGF0YVt2aW9saW5LZXldO1xuICAgIC8vIHRoZSBvYmplY3Qgb2YgcG9pbnRzXG4gICAgdmFyIHZpb2xpblBvaW50cyA9IHZpb2xpblBvaW50c0V4dHJhY3Rvcih2aW9saW5LZXksIHZpb2xpbkRhdGEpO1xuICAgIC8vXG4gICAgdmFyIHZpb2xpblBvaW50c0tleXMgPSBkMy5rZXlzKHZpb2xpblBvaW50cyk7XG4gICAgLy8gdGhlIG51bWVyaWNhbCB2YWx1ZXMgb2YgdGhvc2UgcG9pbnRzXG4gICAgdmFyIHZpb2xpblBvaW50c1ZhbHVlcyA9IHZpb2xpblBvaW50c0tleXMubWFwKGZ1bmN0aW9uKHBrLCBpKXtyZXR1cm4gdmlvbGluUG9pbnRWYWx1ZUV4dHJhY3RvcihwaywgdmlvbGluUG9pbnRzKX0pXG5cbiAgICAvLyBxdWFydGlsZXMgb2YgdGhvc2UgcG9pbnRzXG4gICAgdmFyIHBvaW50UXVhcnRpbGVzID0gcXVhcnRpbGVzKHZpb2xpblBvaW50c1ZhbHVlcywgcXVhcnRpbGVLZXlzKVxuXG4gICAgLy8gYmlubmVkIHBvaW50c1xuICAgIHZhciBiaW5uZWQgPSBkMy5oaXN0b2dyYW0oKSh2aW9saW5Qb2ludHNWYWx1ZXMpXG4gICAgLy8gbGVuZ3RoIG9mIGJpbnNcbiAgICB2YXIgZnJlcXVlbmNpZXMgPSBiaW5uZWQubWFwKGJpbj0+YmluLmxlbmd0aClcbiAgICAvLyBtaW4gYW5kIG1heCBjb3VudG91ciBwb2ludHMgZm9yIG5pY2UgZHJhd2luZ3NcbiAgICB2YXIgbWluQ29udG91clBvaW50ID0gaG9yaXpvbnRhbFEgPyB7eDogMCwgeTogZDMubWluKHZpb2xpblBvaW50c1ZhbHVlcyl9IDogIHt4OiBkMy5taW4odmlvbGluUG9pbnRzVmFsdWVzKSwgeTogMH1cbiAgICB2YXIgbWF4Q29udG91clBvaW50ID0gaG9yaXpvbnRhbFEgPyB7eDogMCwgeTogZDMubWF4KHZpb2xpblBvaW50c1ZhbHVlcyl9IDogIHt4OiBkMy5tYXgodmlvbGluUG9pbnRzVmFsdWVzKSwgeTogMH1cbiAgICB2YXIgdmlvbGluQ29udG91clBvaW50cyA9IGJpbm5lZC5tYXAoZnVuY3Rpb24oYmluLCBpKSB7XG4gICAgICAgIHJldHVybiBob3Jpem9udGFsUVxuICAgICAgICA/IHt5OiAoYmluLmxlbmd0aCkgPyBkMy5tZWRpYW4oYmluKTogZDMubWVkaWFuKFtiaW4ueDAsIGJpbi54MV0pLCB4OiBmcmVxdWVuY2llc1tpXX1cbiAgICAgICAgOiB7eDogKGJpbi5sZW5ndGgpID8gZDMubWVkaWFuKGJpbik6IGQzLm1lZGlhbihbYmluLngwLCBiaW4ueDFdKSwgeTogZnJlcXVlbmNpZXNbaV19XG4gICAgICB9KVxuICAgIC8vIHBvaW50cyBhbG9uZyB3aGljaCB0byBkcmF3IHRoZSB2aW9saW4gc2hwZVxuICAgIHZpb2xpbkNvbnRvdXJQb2ludHMgPSBbbWluQ29udG91clBvaW50XS5jb25jYXQodmlvbGluQ29udG91clBvaW50cykuY29uY2F0KFttYXhDb250b3VyUG9pbnRdKVxuXG4gICAgLy8gc2V0IGRhdGFcbiAgICB2aW9saW5EYXRhLmJpbm5lZCA9IGJpbm5lZDtcbiAgICB2aW9saW5EYXRhLmZyZXF1ZW5jaWVzID0gZnJlcXVlbmNpZXNcbiAgICB2aW9saW5EYXRhLmNvbnRvdXIgPSB2aW9saW5Db250b3VyUG9pbnRzXG4gICAgdmlvbGluRGF0YS5xdWFydGlsZXMgPSBwb2ludFF1YXJ0aWxlc1xuICAgIHZpb2xpbkRhdGEucG9pbnRLZXlzID0gdmlvbGluUG9pbnRzS2V5c1xuICAgIHZpb2xpbkRhdGEucG9pbnRWYWx1ZXMgPSB2aW9saW5Qb2ludHNWYWx1ZXNcbiAgfVxuXG4gIHJldHVybiBjYWxjdWxhdGVWaW9saW5WYWx1ZXNcbn1cbiIsImltcG9ydCB7aHlwZW5hdGUsIHNhZmVTZWxlY3QsIHJvdW5kLCBpbnRlcnBvbGF0ZUNvbG9yc30gZnJvbSAnLi9oZWxwZXJzJztcbmltcG9ydCB7c2V0dXBDb250YWluZXJ9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHtjb2xvckZ1bmN0aW9uIGFzIENGfSBmcm9tICcuL2NvbG9yLWZ1bmN0aW9uJztcblxuXG5leHBvcnQgZnVuY3Rpb24gbnVtZXJpY0xlZ2VuZCggc2VsZWN0aW9uICkge1xuXG4gIHZhclxuICBtaW49MCxcbiAgbWF4PTEsXG4gIHNwYWNlWCxcbiAgc3BhY2VZLFxuICBjb2xvckZ1bmN0aW9uID0gQ0YoKSxcbiAgbmFtZXNwYWNlPSdkM3NtLWxpbmVhci12ZXJ0aWNhbC1ncmFkaWVudCcsXG4gIGZvbnRTaXplID0gMTIsXG4gIGJhY2tncm91bmRGaWxsID0gJ3RyYW5zcGFyZW50JyxcbiAgdGV4dENvbG9yID0gJ2JsYWNrJyxcbiAgcm91bmRUbyA9IDJcblxuXG4gIGxlZ2VuZC5taW4gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1pbj1fLCBsZWdlbmQpIDogbWluIH1cbiAgbGVnZW5kLm1heCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWF4PV8sIGxlZ2VuZCkgOiBtYXggfVxuICBsZWdlbmQuc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVg9XywgbGVnZW5kKSA6IHNwYWNlWCB9XG4gIGxlZ2VuZC5zcGFjZVkgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWT1fLCBsZWdlbmQpIDogc3BhY2VZIH1cbiAgbGVnZW5kLm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlPV8sIGxlZ2VuZCkgOiBuYW1lc3BhY2UgfVxuICBsZWdlbmQuZm9udFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGZvbnRTaXplPV8sIGxlZ2VuZCkgOiBmb250U2l6ZSB9XG4gIGxlZ2VuZC5iYWNrZ3JvdW5kRmlsbCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoYmFja2dyb3VuZEZpbGw9XywgbGVnZW5kKSA6IGJhY2tncm91bmRGaWxsIH1cbiAgbGVnZW5kLmNvbG9yRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNvbG9yRnVuY3Rpb249XywgbGVnZW5kKSA6IGNvbG9yRnVuY3Rpb24gfVxuICBsZWdlbmQudGV4dENvbG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0ZXh0Q29sb3I9XywgbGVnZW5kKSA6IHRleHRDb2xvciB9XG4gIGxlZ2VuZC5yb3VuZFRvID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChyb3VuZFRvPV8sIGxlZ2VuZCkgOiByb3VuZFRvIH1cblxuICBmdW5jdGlvbiBsZWdlbmQoKSB7XG4gICAgLy8gYmFja2dyb3VuZCBjbGlwaW5nIHJlY3RhbmdsZVxuICAgIHZhciBiZ2NwUmVjdCA9IHt4OjAsIHk6MCwgd2lkdGg6IHNwYWNlWCwgaGVpZ2h0OnNwYWNlWX1cbiAgICB2YXIgY29udGFpbmVyID0gc2V0dXBDb250YWluZXIoIHNlbGVjdGlvbiwgbmFtZXNwYWNlLCBiZ2NwUmVjdCwgYmFja2dyb3VuZEZpbGwgKTtcblxuICAgIHZhciBkZWZzID0gc2FmZVNlbGVjdChzZWxlY3Rpb24sICdkZWZzJylcbiAgICB2YXIgbGluZWFyR3JhZGllbnQgPSBzYWZlU2VsZWN0KGRlZnMsICdsaW5lYXJHcmFkaWVudCcpXG4gICAgLmF0dHIoXCJ4MVwiLCBcIjAlXCIpXG4gICAgLmF0dHIoXCJ5MVwiLCBcIjEwMCVcIilcbiAgICAuYXR0cihcIngyXCIsIFwiMCVcIilcbiAgICAuYXR0cihcInkyXCIsIFwiMCVcIilcbiAgICAuYXR0cignaWQnLCBoeXBlbmF0ZShuYW1lc3BhY2UsJ251bWVyaWNhbC1sZWdlbmQtZ3JhZGllbnQnKSlcblxuXG4gICAgY29sb3JGdW5jdGlvbi5kYXRhRXh0ZW50KFttaW4sIG1heF0pXG4gICAgLmNvbG9yQnkoJ3ZhbHVlJylcbiAgICAudmFsdWVFeHRyYWN0b3IoZnVuY3Rpb24oaywgdiwgaSl7cmV0dXJuIHZ9KVxuXG5cbiAgICBsaW5lYXJHcmFkaWVudC5zZWxlY3RBbGwoJ3N0b3AnKVxuICAgIC5kYXRhKCBjb2xvckZ1bmN0aW9uLmNvbG9ycygpIClcbiAgICAuZW50ZXIoKVxuICAgIC5hcHBlbmQoJ3N0b3AnKVxuICAgIC5hdHRyKFwib2Zmc2V0XCIsIGZ1bmN0aW9uKGQsIGkpeyByZXR1cm4gaSAvIChjb2xvckZ1bmN0aW9uLmNvbG9ycygpLmxlbmd0aCAtIDEpIH0pXG4gICAgLmF0dHIoJ3N0b3AtY29sb3InLCBmdW5jdGlvbihkKSB7cmV0dXJuIGR9KVxuXG5cblxuXG4gICAgdmFyIHJlY3QgPSBzYWZlU2VsZWN0KGNvbnRhaW5lciwgJ3JlY3QnLCAnbGVnZW5kJylcbiAgICAuYXR0cigndHJhbnNmb3JtJywgJ3RyYW5zbGF0ZSgwLCcrZm9udFNpemUrJyknKVxuICAgIC5zdHlsZShcImZpbGxcIiwgXCJ1cmwoI1wiK2h5cGVuYXRlKG5hbWVzcGFjZSwnbnVtZXJpY2FsLWxlZ2VuZC1ncmFkaWVudCcpK1wiKVwiKVxuICAgIC5hdHRyKCd4JywgMClcbiAgICAuYXR0cigneScsIDApXG4gICAgLmF0dHIoJ3dpZHRoJywgc3BhY2VYKVxuICAgIC5hdHRyKCdoZWlnaHQnLCBzcGFjZVkgLSBmb250U2l6ZSoyKVxuICAgIC5vbignbW91c2Vtb3ZlJywgZnVuY3Rpb24oZCwgaSl7bGVnZW5kTW91c2Vtb3ZlKGQsIGksIHJlY3QsIGQzLnNlbGVjdCh0aGlzKSl9KVxuICAgIC5vbignbW91c2VvdXQnLCBmdW5jdGlvbihkLCBpKXsgZDMuc2VsZWN0KFwiI1wiK2h5cGVuYXRlKG5hbWVzcGFjZSwnbGVnZW5kLXRvb2x0aXAnKSkucmVtb3ZlKCkgfSlcblxuICAgIHZhciBtaW5UZXh0ID0gc2FmZVNlbGVjdChjb250YWluZXIsICd0ZXh0JywgJ21pbicpXG4gICAgLnRleHQocm91bmQobWluLCAyKSlcbiAgICAuYXR0cigndGV4dC1hbmNob3InLCAnbWlkZGxlJylcbiAgICAuYXR0cihcImZvbnQtc2l6ZVwiLCBmb250U2l6ZSsncHgnKVxuICAgIC5hdHRyKCd0cmFuc2Zvcm0nLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhclxuICAgICAgeCA9IHNwYWNlWCAvIDIsXG4gICAgICB5ID0gc3BhY2VZIC0gZm9udFNpemUgLyA0LFxuICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgcmV0dXJuIHRcbiAgICB9KVxuXG4gICAgdmFyIG1heFRleHQgPSBzYWZlU2VsZWN0KGNvbnRhaW5lciwgJ3RleHQnLCAnbWF4JylcbiAgICAudGV4dChyb3VuZChtYXgsIDIpKVxuICAgIC5hdHRyKCd0ZXh0LWFuY2hvcicsICdtaWRkbGUnKVxuICAgIC5hdHRyKFwiZm9udC1zaXplXCIsIGZvbnRTaXplKydweCcpXG4gICAgLmF0dHIoJ3RyYW5zZm9ybScsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgdmFyXG4gICAgICB4ID0gc3BhY2VYIC8gMixcbiAgICAgIHkgPSBmb250U2l6ZSxcbiAgICAgIHQgPSAndHJhbnNsYXRlKCcreCsnLCcreSsnKSdcbiAgICAgIHJldHVybiB0XG4gICAgfSlcblxuXG5cblxuICB9XG5cbiAgZnVuY3Rpb24gbGVnZW5kTW91c2Vtb3ZlKGQsIGksIHJlY3QsIHQpIHtcbiAgICB2YXIgcyA9IGQzLnNjYWxlTGluZWFyKClcbiAgICAuZG9tYWluKFswLCByZWN0LmF0dHIoJ2hlaWdodCcpXSlcbiAgICAucmFuZ2UoW21heCwgbWluXSlcbiAgICB2YXIgbSA9IGQzLm1vdXNlKHJlY3Qubm9kZSgpKVxuICAgIHZhciB2ID0gcm91bmQocyhtWzFdKSxyb3VuZFRvKVxuXG4gICAgdmFyIHN0cm9rZUNvbG9yID0gY29sb3JGdW5jdGlvbih1bmRlZmluZWQsIHYsIHVuZGVmaW5lZCwgJ3N0cm9rZScpXG4gICAgdmFyIGZpbGxDb2xvciA9IGNvbG9yRnVuY3Rpb24odW5kZWZpbmVkLCB2LCB1bmRlZmluZWQsICdmaWxsJylcblxuICAgIHZhciBkaXYgPSBzYWZlU2VsZWN0KGQzLnNlbGVjdCgnYm9keScpLCAnZGl2JywgaHlwZW5hdGUobmFtZXNwYWNlLCdsZWdlbmQtdG9vbHRpcCcpKVxuICAgIC5hdHRyKCdpZCcsIGh5cGVuYXRlKG5hbWVzcGFjZSwnbGVnZW5kLXRvb2x0aXAnKSlcbiAgICAuc3R5bGUoJ3Bvc2l0aW9uJywgJ2Fic29sdXRlJylcbiAgICAuc3R5bGUoJ2xlZnQnLCAoZDMuZXZlbnQucGFnZVgrMTUpKydweCcpXG4gICAgLnN0eWxlKCd0b3AnLCAoZDMuZXZlbnQucGFnZVkrMTUpKydweCcpXG4gICAgLnN0eWxlKCdiYWNrZ3JvdW5kLWNvbG9yJywgZmlsbENvbG9yKVxuICAgIC5zdHlsZSgnYm9yZGVyLWNvbG9yJywgc3Ryb2tlQ29sb3IpXG5cbiAgICAuc3R5bGUoJ21pbi13aWR0aCcsIChmb250U2l6ZSAqIChTdHJpbmcobWF4KS5zcGxpdCgnLicpWzBdLmxlbmd0aCszKSkrJ3B4JylcbiAgICAuc3R5bGUoJ21pbi1oZWlnaHQnLCAoZm9udFNpemUgKiAoU3RyaW5nKG1heCkuc3BsaXQoJy4nKVswXS5sZW5ndGgrMykpKydweCcpXG4gICAgLnN0eWxlKCdib3JkZXItcmFkaXVzJywgJzUwJScpXG4gICAgLnN0eWxlKCdib3JkZXItcmFkaXVzJywgJzUwMDBweCcpXG5cbiAgICAuc3R5bGUoJ2Rpc3BsYXknLCAnZmxleCcpXG4gICAgLnN0eWxlKCdqdXN0aWZ5LWNvbnRlbnQnLCAnY2VudGVyJylcbiAgICAuc3R5bGUoJ3RleHQtYWxpZ24nLCAnbWlkZGxlJylcbiAgICAuc3R5bGUoJ3BhZGRpbmcnLCAyK1wicHhcIilcblxuICAgIC5zdHlsZSgnYm9yZGVyLXN0eWxlJywgJ3NvbGlkJylcbiAgICAuc3R5bGUoJ2JvcmRlci13aWR0aCcsIDIpXG5cbiAgICB2YXIgdGV4dCA9IHNhZmVTZWxlY3QoZGl2LCAnZGl2JylcbiAgICAudGV4dCh2KVxuICAgIC5zdHlsZSgnY29sb3InLCB0ZXh0Q29sb3IpXG4gICAgLnN0eWxlKCdhbGlnbi1zZWxmJywgJ2NlbnRlcicpXG4gIH1cblxuICByZXR1cm4gbGVnZW5kXG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0LCByb3VuZCwgaW50ZXJwb2xhdGVDb2xvcnN9IGZyb20gJy4vaGVscGVycyc7XG5pbXBvcnQge3NldHVwQ29udGFpbmVyLCBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0LCBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7Y29sb3JGdW5jdGlvbiBhcyBDRn0gZnJvbSAnLi9jb2xvci1mdW5jdGlvbic7XG5pbXBvcnQge2dyb3VwaW5nU3BhY2VyfSBmcm9tICcuL2dyb3VwaW5nLXNwYWNlcic7XG5pbXBvcnQge3VuaXF1ZSwgZmxhdHRlbn0gZnJvbSAnLi9hcnJheS1mdW5jdGlvbnMnO1xuXG5leHBvcnQgZnVuY3Rpb24gY2F0ZWdvcmljTGVnZW5kKCBzZWxlY3Rpb24gKSB7XG4gIHZhclxuICBjYXRlZ29yaWVzLFxuXG4gIC8qKlxuICAqIERhdGEgdG8gcGxvdC4gQXNzdW1lZCB0byBiZSBhIG9iamVjdCwgd2hlcmUgZWFjaCBrZXkgY29ycmVzcG9uZHMgdG8gYSBsZWdlbmRcbiAgKiAoc2VlIHtAbGluayBsZWdlbmQjZGF0YX0pXG4gICogQHBhcmFtIHtPYmplY3R9IFtkYXRhPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgbGVnZW5kI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBkYXRhLFxuICAvKipcbiAgKiBXaGljaCBkaXJlY3Rpb24gdG8gcmVuZGVyIHRoZSBiYXJzIGluXG4gICogKHNlZSB7QGxpbmsgbGVnZW5kI29yaWVudH0pXG4gICogQHBhcmFtIHtudW1iZXJ9IFtvcmllbnQ9J2hvcml6b250YWwnXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG9yaWVudD0naG9yaXpvbnRhbCcsXG4gIC8qKlxuICAqIEFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIChpbiBwaXhlbHMpIGF2YWlibGUgdG8gcmVuZGVyIHRoZSBsZWdlbmQgaW5cbiAgKiAoc2VlIHtAbGluayBsZWdlbmQjc3BhY2VYfSlcbiAgKiBAcGFyYW0ge251bWJlcn0gW3NwYWNlWD11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc3BhY2VYLFxuICAvKipcbiAgKiBBbW91bnQgb2YgdmVydGljYWwgc3BhY2UgKGluIHBpeGVscykgYXZhaWJsZSB0byByZW5kZXIgdGhlIGxlZ2VuZCBpblxuICAqIChzZWUge0BsaW5rIGxlZ2VuZC5zcGFjZVl9KVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbc3BhY2VZPXVuZGVmaW5lZF1cbiAgKiBAbWVtYmVyb2YgbGVnZW5kI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBzcGFjZVksXG5cbiAgLyoqXG4gICogV2hldGhlciBvciBub3QgdG8gYWxsb3cgbGVnZW5kIHRvIHJlbmRlciBlbGVtZW50cyBwYXNzIHRoZSBtYWluIHNwYXRpYWwgZGltZW5zaW9uXG4gICogZ2l2ZW4gdGhlIG9yaWVudGF0aW9uIChzZWUge0BsaW5rIGxlZ2VuZCNvcmllbnR9KSwgd2hlcmUge0BsaW5rIGxlZ2VuZCNvcmllbnR9PVwiaG9yaXpvbnRhbFwiXG4gICogdGhlIG1haW4gZGltZW5zaW9uIGlzIHtAbGluayBsZWdlbmQjc3BhY2VYfSBhbmQgd2hlcmUge0BsaW5rIGxlZ2VuZCNvcmllbnR9PVwidmVydGljYWxcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgbGVnZW5kI3NwYWNlWX1cbiAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvdmVyZmxvd1E9ZmFsc2VdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb3ZlcmZsb3dRID0gZmFsc2UsXG5cbiAgLyoqXG4gICogQW4gYXJyYXkgLSBwdXRhdGl2ZWx5IG9mIG90aGVyIGFycmF5cyAtIGRlcGljdGluZyBob3cgYmFycyBzaG91bGQgYmUgYXJyYW5nZWRcbiAgKiBAcGFyYW0ge0FycmF5W119IFtncm91cGluZz11bmRlZmluZWRdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgZ3JvdXBpbmcsXG5cbiAgLyoqXG4gICogSG93IHRvIGdldCB0aGUgdmFsdWUgb2YgdGhlIGxlZ2VuZFxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFt2YWx1ZUV4dHJhY3Rvcj1mdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV0gfV1cbiAgKiBAbWVtYmVyb2YgbGVnZW5kI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICB2YWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKGtleSwgaW5kZXgpIHsgcmV0dXJuIGRhdGFba2V5XSB9LFxuICAvKipcbiAgKiBIb3cgdG8gc29ydCB0aGUgYmFycyAtIGlmIHtAbGluayBiYXIjZ3JvdXBpbmd9IGlzIG5vdCBwcm92aWRlZC5cbiAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbc29ydGluZ0Z1bmN0aW9uPWZ1bmN0aW9uKGtleUEsIGtleUIpIHtyZXR1cm4gZDMuZGVzY2VuZGluZyhkYXRhW2tleUFdLCBkYXRhW2tleUJdKX1dXG4gICogQG1lbWJlcm9mIGJhciNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgc29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oa2V5QSwga2V5Qikge3JldHVybiBkMy5hc2NlbmRpbmcoa2V5QSwga2V5Qil9LFxuICAvKipcbiAgKiBEZWZhdWx0IHNwYWNlIGZvciB0aGUgc3BhY2VyIChwZXJjZW50YWdlKSBvZiBtYWluIGRpbWVuc2lvbiBnaXZlbiB0aGUgb3JpZW50YXRpb25cbiAgKiAoc2VlIHtAbGluayBsZWdlbmQjb3JpZW50fSksIHdoZXJlIHtAbGluayBsZWdlbmQjb3JpZW50fT1cImhvcml6b250YWxcIlxuICAqIHRoZSBtYWluIGRpbWVuc2lvbiBpcyB7QGxpbmsgbGVnZW5kI3NwYWNlWH0gYW5kIHdoZXJlIHtAbGluayBsZWdlbmQjb3JpZW50fT1cInZlcnRpY2FsXCJcbiAgKiB0aGUgbWFpbiBkaW1lbnNpb24gaXMge0BsaW5rIGxlZ2VuZCNzcGFjZVl9IGJldHdlZW4gYmFyc1xuICAqIEBwYXJhbSB7bnVtYmVyfSBbb2JqZWN0U3BhY2VyPTAuMDVdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgb2JqZWN0U3BhY2VyID0gMC4wNSxcbiAgLyoqXG4gICogVGhlIG1pbmltdW0gc2l6ZSB0aGF0IGFuIG9iamVjdCBjYW4gYmVcbiAgKiBAcGFyYW0ge251bWJlcn0gW21pbk9iamVjdFNpemU9NTBdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWluT2JqZWN0U2l6ZSA9IDEwLFxuICAvKipcbiAgKiBUaGUgbWF4aW11bSBzaXplIHRoYXQgYW4gb2JqZWN0IGNhbiBiZVxuICAqIEBwYXJhbSB7bnVtYmVyfSBbbWF4T2JqZWN0U2l6ZT0xMDBdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgbWF4T2JqZWN0U2l6ZSA9IDEwMCxcblxuICAvKipcbiAgKiBUaGUgc3Ryb2tlIHdpZHRoIG9mIHRoZSBiYXJzXG4gICogQHBhcmFtIHtudW1iZXJ9IFtiYXJTdHJva2VXaWR0aD0yXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGJ1YmJsZVN0cm9rZVdpZHRoID0gMixcbiAgLyoqXG4gICogSW5zdGFuY2Ugb2YgQ29sb3JGdW5jdGlvblxuICAqIEBwYXJhbSB7ZnVuY3Rpb259IFtjb2xvckZ1bmN0aW9uID0gY29sb3JGdW5jdGlvbigpXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGNvbG9yRnVuY3Rpb24gPSBDRigpLFxuXG4gIC8qKlxuICAqIENvbG9yIG9mIHRoZSBiYWNrZ3JvdW5kXG4gICogQHBhcmFtIHtzdHJpbmd9IFtiYWNrZ3JvdW5kRmlsbD1cInRyYW5zcGFyZW50XCJdXG4gICogQG1lbWJlcm9mIGxlZ2VuZCNcbiAgKiBAcHJvcGVydHlcbiAgKi9cbiAgYmFja2dyb3VuZEZpbGwgPSAndHJhbnNwYXJlbnQnLFxuICAvKipcbiAgKiBOYW1lc3BhY2UgZm9yIGFsbCBpdGVtcyBtYWRlIGJ5IHRoaXMgaW5zdGFuY2Ugb2YgbGVnZW5kXG4gICogQHBhcmFtIHtzdHJpbmd9IFtuYW1lc3BhY2U9XCJkM3NtLWxlZ2VuZFwiXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIG5hbWVzcGFjZSA9ICdkM3NtLWxlZ2VuZCcsXG4gIC8qKlxuICAqIENsYXNzIG5hbWUgZm9yIGxlZ2VuZCBjb250YWluZXIgKDxnPiBlbGVtZW50KVxuICAqIEBwYXJhbSB7c3RyaW5nfSBbb2JqZWN0Q2xhc3M9XCJsZWdlbmRcIl1cbiAgKiBAbWVtYmVyb2YgbGVnZW5kI1xuICAqIEBwcm9wZXJ0eVxuICAqL1xuICBvYmplY3RDbGFzcyA9ICdsZWdlbmQnLFxuXG4gIC8qKlxuICAqIER1cmF0aW9uIG9mIGFsbCB0cmFuc2l0aW9ucyBvZiB0aGlzIGVsZW1lbnRcbiAgKiBAcGFyYW0ge251bWJlcn0gW3RyYW5zaXRpb25EdXJhdGlvbj0xMDAwXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIHRyYW5zaXRpb25EdXJhdGlvbiA9IDEwMDAsXG4gIC8qKlxuICAqIEVhc2luZyBmdW5jdGlvbiBmb3IgdHJhbnNpdGlvbnNcbiAgKiBAcGFyYW0ge2QzLmVhc2V9IFtlYXNlRnVuYz1kMy5lYXNlRXhwXVxuICAqIEBtZW1iZXJvZiBsZWdlbmQjXG4gICogQHByb3BlcnR5XG4gICovXG4gIGVhc2VGdW5jID0gZDMuZWFzZUV4cFxuXG5cbiAgbGVnZW5kLmNhdGVnb3JpZXMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNhdGVnb3JpZXM9XywgbGVnZW5kKSA6IGNhdGVnb3JpZXMgfTtcblxuICAvKipcbiAgICogR2V0cyBvciBzZXRzIHRoZSBzZWxlY3Rpb24gaW4gd2hpY2ggaXRlbXMgYXJlIG1hbmlwdWxhdGVkXG4gICAqIEBwYXJhbSB7ZDMuc2VsZWN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgZDMuc2VsZWN0aW9ufVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IHNlbGVjdGlvbiA9IHNlbGVjdGlvblxuICAgKi9cbiAgbGVnZW5kLnNlbGVjdGlvbiA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc2VsZWN0aW9uID0gXywgbGVnZW5kKSA6IHNlbGVjdGlvbjsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgZGF0YVxuICAgKiAoc2VlIHtAbGluayBsZWdlbmQjZGF0YX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgb2JqZWN0fVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKi9cbiAgbGVnZW5kLmRhdGEgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRhdGEgPSBfLCBsZWdlbmQpIDogZGF0YTsgfTtcbiAgLyoqXG4gICAqIEdldHMgb3Igc2V0cyB0aGUgb3JpZW50IG9mIHRoZSBiYXJzXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNvcmllbnR9KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2xlZ2VuZCB8IG9iamVjdH1cbiAgICogQG1lbWJlcm9mIGxlZ2VuZFxuICAgKiBAcHJvcGVydHlcbiAgICovXG4gIGxlZ2VuZC5vcmllbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9yaWVudCA9IF8sIGxlZ2VuZCkgOiBvcmllbnQ7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiBob3Jpem9udGFsIHNwYWNlIGluIHdoaWNoIGl0ZW1zIGFyZSBtYW5pcHVsYXRlZFxuICAgKiAoc2VlIHtAbGluayBsZWdlbmQjc3BhY2VYfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdIHNob3VsZCBiZSBhIG51bWJlciA+IDBcbiAgICogQHJldHVybnMge2xlZ2VuZCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGxlZ2VuZFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBzcGFjZVggPSB1bmRlZmluZWRcbiAgICovXG4gIGxlZ2VuZC5zcGFjZVggPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNwYWNlWCA9IF8sIGxlZ2VuZCkgOiBzcGFjZVg7IH07XG4gIC8qKlxuICAgKiBHZXRzIG9yIHNldHMgdGhlIGFtb3VudCBvZiB2ZXJ0aWNhbCBzcGFjZSBpbiB3aGljaCBpdGVtcyBhcmUgbWFuaXB1bGF0ZWRcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI3NwYWNlWX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXSBzaG91bGQgYmUgYSBudW1iZXIgPiAwXG4gICAqIEByZXR1cm5zIHtsZWdlbmQgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc3BhY2VZID0gdW5kZWZpbmVkXG4gICAqL1xuICBsZWdlbmQuc3BhY2VZID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVkgPSBfLCBsZWdlbmQpIDogc3BhY2VZOyB9O1xuXG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB3aGV0aGVyIG9yIG5vdCBsZWdlbmQgaXMgYWxsb3dlZCB0byBnbyBiZXlvbmQgc3BlY2lmaWVkIGRpbWVuc2lvbnNcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI3NwYWNlWH0pXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2xlZ2VuZCB8IGJvb2xlYW59XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb3ZlcmZsb3dRID0gZmFsc2VcbiAgICovXG4gIGxlZ2VuZC5vdmVyZmxvd1EgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG92ZXJmbG93USA9IF8sIGxlZ2VuZCkgOiBvdmVyZmxvd1E7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgZ3JvdXBpbmcgb2YgdGhlIGJhcnNcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI2dyb3VwaW5nfSlcbiAgICogQHBhcmFtIHtBcnJheVtdfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgQXJyYXlbXX1cbiAgICogQG1lbWJlcm9mIGxlZ2VuZFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCBncm91cGluZyA9IHVuZGVmaW5lZFxuICAgKi9cbiAgbGVnZW5kLmdyb3VwaW5nID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChncm91cGluZyA9IF8sIGxlZ2VuZCkgOiBncm91cGluZzsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSB2YWx1ZUV4dHJhY3RvclxuICAgKiAoc2VlIHtAbGluayBsZWdlbmQjdmFsdWVFeHRyYWN0b3J9KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgdmFsdWVFeHRyYWN0b3IgPSBmdW5jdGlvbihrZXksIGluZGV4KSB7IHJldHVybiBkYXRhW2tleV0gfSxcbiAgICovXG4gIGxlZ2VuZC52YWx1ZUV4dHJhY3RvciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAodmFsdWVFeHRyYWN0b3IgPSBfLCBsZWdlbmQpIDogdmFsdWVFeHRyYWN0b3I7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgc29ydGluZ0Z1bmN0aW9uXG4gICAqIChzZWUge0BsaW5rIGJhciNzb3J0aW5nRnVuY3Rpb259KVxuICAgKiBAcGFyYW0ge2Z1bmN0aW9ufSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7YmFyIHwgZnVuY3Rpb259XG4gICAqIEBtZW1iZXJvZiBiYXJcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgc29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oa2V5QSwga2V5Qikge3JldHVybiBkMy5kZXNjZW5kaW5nKGRhdGFba2V5QV0sIGRhdGFba2V5Ql0pfSxcbiAgICovXG4gIGxlZ2VuZC5zb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNvcnRpbmdGdW5jdGlvbiA9IF8sIGxlZ2VuZCkgOiBzb3J0aW5nRnVuY3Rpb247IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyBvYmplY3RTcGFjZXJcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI29iamVjdFNwYWNlcn0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG9iamVjdFNwYWNlciA9IDAuMDVcbiAgICovXG4gIGxlZ2VuZC5vYmplY3RTcGFjZXIgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9iamVjdFNwYWNlciA9IF8sIGxlZ2VuZCkgOiBvYmplY3RTcGFjZXI7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgbWluT2JqZWN0U2l6ZVxuICAgKiAoc2VlIHtAbGluayBsZWdlbmQjbWluT2JqZWN0U2l6ZX0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG1pbk9iamVjdFNpemUgPSA1MFxuICAgKi9cbiAgbGVnZW5kLm1pbk9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1pbk9iamVjdFNpemUgPSBfLCBsZWdlbmQpIDogbWluT2JqZWN0U2l6ZTsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBtYXhPYmplY3RTaXplXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNtYXhPYmplY3RTaXplfSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtsZWdlbmQgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgbWF4T2JqZWN0U2l6ZSA9IDEwMFxuICAgKi9cbiAgbGVnZW5kLm1heE9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1heE9iamVjdFNpemUgPSBfLCBsZWdlbmQpIDogbWF4T2JqZWN0U2l6ZTsgfTtcblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGJhclN0cm9rZVdpZHRoXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNiYXJTdHJva2VXaWR0aH0pXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgbnVtYmVyfVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGJhclN0cm9rZVdpZHRoID0gMlxuICAgKi9cbiAgbGVnZW5kLmJ1YmJsZVN0cm9rZVdpZHRoID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChidWJibGVTdHJva2VXaWR0aCA9IF8sIGxlZ2VuZCkgOiBidWJibGVTdHJva2VXaWR0aDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBjb2xvckZ1bmN0aW9uXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNjb2xvckZ1bmN0aW9ufSlcbiAgICogQHBhcmFtIHtudW1iZXJ9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtsZWdlbmQgfCBudW1iZXJ9XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgY29sb3JGdW5jdGlvbiA9IGNvbG9yRnVuY3Rpb24oKVxuICAgKi9cbiAgbGVnZW5kLmNvbG9yRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGNvbG9yRnVuY3Rpb24gPSBfLCBsZWdlbmQpIDogY29sb3JGdW5jdGlvbjsgfTtcblxuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGJhY2tncm91bmRGaWxsXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNiYWNrZ3JvdW5kRmlsbH0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IGJhY2tncm91bmRGaWxsID0gJ3RyYW5zcGFyZW50J1xuICAgKi9cbiAgbGVnZW5kLmJhY2tncm91bmRGaWxsID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChiYWNrZ3JvdW5kRmlsbCA9IF8sIGxlZ2VuZCkgOiBiYWNrZ3JvdW5kRmlsbDsgfTtcbiAgLyoqXG4gICAqIEdldHMgLyBzZXRzIHRoZSBuYW1lc3BhY2VcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI25hbWVzcGFjZX0pXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbXz1ub25lXVxuICAgKiBAcmV0dXJucyB7bGVnZW5kIHwgc3RyaW5nfVxuICAgKiBAbWVtYmVyb2YgbGVnZW5kXG4gICAqIEBwcm9wZXJ0eVxuICAgKiBieSBkZWZhdWx0IG5hbWVzcGFjZSA9ICdkM3NtLWxlZ2VuZCdcbiAgICovXG4gIGxlZ2VuZC5uYW1lc3BhY2UgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG5hbWVzcGFjZSA9IF8sIGxlZ2VuZCkgOiBuYW1lc3BhY2U7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgb2JqZWN0Q2xhc3NcbiAgICogKHNlZSB7QGxpbmsgbGVnZW5kI29iamVjdENsYXNzfSlcbiAgICogQHBhcmFtIHtzdHJpbmd9IFtfPW5vbmVdXG4gICAqIEByZXR1cm5zIHtsZWdlbmQgfCBzdHJpbmd9XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgb2JqZWN0Q2xhc3MgPSAndGljay1ncm91cCdcbiAgICovXG4gIGxlZ2VuZC5vYmplY3RDbGFzcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3MgPSBfLCBsZWdlbmQpIDogb2JqZWN0Q2xhc3M7IH07XG4gIC8qKlxuICAgKiBHZXRzIC8gc2V0cyB0aGUgdHJhbnNpdGlvbkR1cmF0aW9uXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCN0cmFuc2l0aW9uRHVyYXRpb259KVxuICAgKiBAcGFyYW0ge251bWJlcn0gW189bm9uZV1cbiAgICogQHJldHVybnMge2xlZ2VuZCB8IG51bWJlcn1cbiAgICogQG1lbWJlcm9mIGxlZ2VuZFxuICAgKiBAcHJvcGVydHlcbiAgICogYnkgZGVmYXVsdCB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwXG4gICAqL1xuICBsZWdlbmQudHJhbnNpdGlvbkR1cmF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0cmFuc2l0aW9uRHVyYXRpb24gPSBfLCBsZWdlbmQpIDogdHJhbnNpdGlvbkR1cmF0aW9uOyB9O1xuICAvKipcbiAgICogR2V0cyAvIHNldHMgdGhlIGVhc2VGdW5jXG4gICAqIChzZWUge0BsaW5rIGxlZ2VuZCNlYXNlRnVuY30pXG4gICAqIEBwYXJhbSB7ZDMuZWFzZX0gW189bm9uZV1cbiAgICogQHJldHVybnMge2xlZ2VuZCB8IGQzLmVhc2V9XG4gICAqIEBtZW1iZXJvZiBsZWdlbmRcbiAgICogQHByb3BlcnR5XG4gICAqIGJ5IGRlZmF1bHQgZWFzZUZ1bmMgPSBkMy5lYXNlRXhwXG4gICAqL1xuICBsZWdlbmQuZWFzZUZ1bmMgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGVhc2VGdW5jID0gXywgbGVnZW5kKSA6IGVhc2VGdW5jOyB9O1xuXG5cbiAgZnVuY3Rpb24gbGVnZW5kKCkge1xuICAgIHZhciBob3Jpem9udGFsUSA9IChvcmllbnQgPT0gJ2hvcml6b250YWwnKSA/IHRydWUgOiBmYWxzZVxuICAgIHZhciB2ZXJ0aWNhbFEgPSAhaG9yaXpvbnRhbFFcbiAgICAvLyBiYWNrZ3JvdW5kIGNsaXBpbmcgcmVjdGFuZ2xlXG4gICAgdmFyIGJnY3BSZWN0ID0ge3g6MCwgeTowLCB3aWR0aDogc3BhY2VYLCBoZWlnaHQ6c3BhY2VZfVxuICAgIHZhciBjb250YWluZXIgPSBzZXR1cENvbnRhaW5lciggc2VsZWN0aW9uLCBuYW1lc3BhY2UsIGJnY3BSZWN0LCBiYWNrZ3JvdW5kRmlsbCApO1xuXG5cbiAgICBjb2xvckZ1bmN0aW9uLmRhdGFFeHRlbnQoWzAsIGNhdGVnb3JpZXMubGVuZ3RoIC0gMV0pXG4gICAgLmNvbG9yQnkoJ2NhdGVnb3JpZXMnKVxuICAgIC5jYXRlZ29yeUV4dHJhY3RvcihmdW5jdGlvbihrLCB2LCBpKXtyZXR1cm4gdn0pXG5cbiAgICB2YXIgciA9IE1hdGgubWluKHNwYWNlWCwgc3BhY2VZKSAvIDJcbiAgICB2YXIgbnVtYmVyT2ZPYmplY3RzID0gY2F0ZWdvcmllcy5sZW5ndGhcblxuICAgIC8vIGlmIGdyb3VwaW5nIGlzIHVuZGVmaW5lZCBzb3J0IGJhcktleXMgYnkgc29ydGluZ0Z1bmN0aW9uXG4gICAgdmFyIG9yZGVyZWQgPSAoZ3JvdXBpbmcgPT0gdW5kZWZpbmVkKSA/IGNhdGVnb3JpZXMuc29ydChzb3J0aW5nRnVuY3Rpb24pIDogZ3JvdXBpbmdcbiAgICAvLyBvcmRlcmVkIG1pZ2h0IGJlIG5lc3RlZCBkZXBlbmRpbmcgb24gZ3JvdXBpbmdcbiAgICB2YXIgY2F0S2V5cyA9IGZsYXR0ZW4ob3JkZXJlZClcblxuICAgIHZhciBzcGFjZSA9IGhvcml6b250YWxRID8gc3BhY2VYIDogc3BhY2VZXG4gICAgLy8gY2FsY3VsYXRlIG9iamVjdCBzaXplXG4gICAgdmFyIG9iamVjdFNpemUgPSBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0KHNwYWNlLCBudW1iZXJPZk9iamVjdHMsIG1pbk9iamVjdFNpemUsIG1heE9iamVjdFNpemUsIG9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIC8vIGNhbGN1bGF0ZSBzcGFjZXIgc2l6ZSBpZiBuZWVkZWRcbiAgICB2YXIgc3BhY2VyU2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZTcGFjZXIoY2F0S2V5cywgc3BhY2UsIG9iamVjdFNpemUsIG51bWJlck9mT2JqZWN0cywgb2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgLy8gbWFrZSB0aGUgbmVzdGVkIGdyb3Vwc1xuICAgIHZhciBzcGFjZXJGdW5jdGlvbiA9IGdyb3VwaW5nU3BhY2VyKClcbiAgICAuaG9yaXpvbnRhbFEoaG9yaXpvbnRhbFEpLnNjYWxlKHNjYWxlKS5tb3ZlYnkoJ2NhdGVnb3J5JykubnVtYmVyT2ZPYmplY3RzKG51bWJlck9mT2JqZWN0cylcbiAgICAub2JqZWN0Q2xhc3Mob2JqZWN0Q2xhc3MpLm9iamVjdFNpemUob2JqZWN0U2l6ZSkuc3BhY2VyU2l6ZShzcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcbiAgICAubmFtZXNwYWNlKG5hbWVzcGFjZSlcblxuICAgIHNwYWNlckZ1bmN0aW9uKGNvbnRhaW5lciwgb3JkZXJlZCwgMClcbiAgICB2YXIgciA9IE1hdGgubWluKG9iamVjdFNpemUsIHNwYWNlWCwgc3BhY2VZKSAvIDIgLSBidWJibGVTdHJva2VXaWR0aFxuXG4gICAgY29udGFpbmVyLnNlbGVjdEFsbCgnZzpub3QoLnRvLXJlbW92ZSkuJytvYmplY3RDbGFzcykuZWFjaChmdW5jdGlvbihjYXQsIGkpIHtcbiAgICAgIHZhciB0ID0gZDMuc2VsZWN0KHRoaXMpXG4gICAgICB2YXIgYyA9IHNhZmVTZWxlY3QodCwgJ2NpcmNsZScpXG4gICAgICB2YXIgZmlsbENvbG9yID0gY29sb3JGdW5jdGlvbih1bmRlZmluZWQsIGNhdCwgaSwgJ2ZpbGwnKSwgLy8gcHJldmVudCBkdXBsaWNhdGUgY29tcHV0YXRpb25cbiAgICAgIHN0cm9rZUNvbG9yID0gY29sb3JGdW5jdGlvbih1bmRlZmluZWQsIGNhdCwgaSwgICdzdHJva2UnKVxuXG4gICAgICB2YXIgY3ggPSBob3Jpem9udGFsUVxuICAgICAgICA/IHIrYnViYmxlU3Ryb2tlV2lkdGhcbiAgICAgICAgOiAoc3BhY2VYIC0gcioyKSAvIDIgKyByXG4gICAgICB2YXIgY3kgPSB2ZXJ0aWNhbFFcbiAgICAgICAgPyByK2J1YmJsZVN0cm9rZVdpZHRoXG4gICAgICAgIDogKHNwYWNlWCAtIHIqMikgLyAyICsgclxuXG4gICAgICBjLmF0dHIoXCJyXCIsIHIpXG4gICAgICAuYXR0cignY3gnLCBjeClcbiAgICAgIC5hdHRyKCdjeScsIGN5KVxuICAgICAgLmF0dHIoJ2ZpbGwnLCBmaWxsQ29sb3IpXG4gICAgICAuYXR0cignc3Ryb2tlJywgc3Ryb2tlQ29sb3IpXG4gICAgICAuYXR0cignc3Ryb2tlLXdpZHRoJywgYnViYmxlU3Ryb2tlV2lkdGgpXG5cbiAgICAgIHZhciB0ZXh0ID0gc2FmZVNlbGVjdCh0LCAndGV4dCcpXG4gICAgICB0ZXh0LnRleHQoY2F0KVxuICAgICAgLmF0dHIoJ3RleHQtYW5jaG9yJywgJ21pZGRsZScpXG4gICAgICAuYXR0cihcInRyYW5zZm9ybVwiLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgICAgdmFyXG4gICAgICAgIHggPSBjeCxcbiAgICAgICAgeSA9IGN5ICsgdGV4dC5ub2RlKCkuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkuaGVpZ2h0IC8gNCxcbiAgICAgICAgdCA9ICd0cmFuc2xhdGUoJyt4KycsJyt5KycpJ1xuICAgICAgICByZXR1cm4gdFxuICAgICAgfSlcblxuICAgIH0pXG5cblxuICB9XG5cbiAgcmV0dXJuIGxlZ2VuZFxufVxuIiwiaW1wb3J0IHtoeXBlbmF0ZSwgc2FmZVNlbGVjdCwgbW9kaWZ5SGV4aWRlY2ltYWxDb2xvckx1bWluYW5jZSwgZXh0cmFjdFZpb2xpblZhbHVlcywgcXVhcnRpbGVzfSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHtzZXR1cENvbnRhaW5lciwgY2FsY3VsYXRlV2lkdGhPZk9iamVjdCwgY2FsY3VsYXRlV2lkdGhPZlNwYWNlcn0gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQge3VuaXF1ZSwgaGFzUSwgZmxhdHRlbiwgd2hpY2hCaW59IGZyb20gJy4vYXJyYXktZnVuY3Rpb25zJztcbmltcG9ydCB7Z3JvdXBpbmdTcGFjZXJ9IGZyb20gJy4vZ3JvdXBpbmctc3BhY2VyJztcbmltcG9ydCB7Y29sb3JGdW5jdGlvbiBhcyBDRn0gZnJvbSAnLi9jb2xvci1mdW5jdGlvbic7XG5pbXBvcnQge3Rvb2x0aXAgYXMgVFRpcH0gZnJvbSAnLi90b29sdGlwJztcbmltcG9ydCB7bGFzc299IGZyb20gJy4vbGFzc28nO1xuaW1wb3J0ICcuL2QzLXByb3RvdHlwZXMnO1xuXG5leHBvcnQgZnVuY3Rpb24gbGFzc29XaWRnZXQoIHNlbGVjdGlvbiApIHtcbiAgdmFyXG4gIG5hbWVzcGFjZSA9ICdkM3NtLWxhc3NvJyxcbiAgc2VsZWN0aW9uID0gc2VsZWN0aW9uLFxuICBzdmcsXG4gIGNoYXJ0Q29udGFpbmVyLFxuICBvYmplY3RDb250YWluZXIsXG4gIG9iamVjdENsYXNzLFxuICBsYXNzb0xpbmUgPSBkMy5saW5lKClcbiAgLngoZnVuY3Rpb24oZCwgaSl7cmV0dXJuIGRbMF19KVxuICAueShmdW5jdGlvbihkLCBpKXtyZXR1cm4gZFsxXX0pXG4gIC5jdXJ2ZShkMy5jdXJ2ZUxpbmVhckNsb3NlZCksXG4gIHBhdGgsXG4gIGNvbG9yRnVuY3Rpb24gPSBDRigpLFxuICBtYXhOdW1iZXJPZkdyb3VwcyxcbiAgZGF0YUV4dHJhY3RvcixcbiAgeFNjYWxlLFxuICB5U2NhbGVcblxuICBmdW5jdGlvbiBvbkVycm9yKG1zZywgc3R5bGUpe1xuICAgIGNvbnNvbGUubG9nKG1zZywgc3R5bGUpXG4gIH1cbiAgLy8gc2V0dXAgdGhlIGNvbnRhaW5lclxuICBzZXR1cERhdGFzZWxlY3RDb250YWluZXIoKVxuXG4gIGxhc3NvV2lkZ2V0Lm9iamVjdENsYXNzID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3M9Jy4nK18ucmVwbGFjZSgnLicsJycpLCBsYXNzb1dpZGdldCkgOiBvYmplY3RDbGFzc31cbiAgbGFzc29XaWRnZXQuc3ZnID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3ZnPV8sIGxhc3NvV2lkZ2V0KSA6IHN2Z31cbiAgbGFzc29XaWRnZXQuc3VibWl0ID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3VibWl0PV8sIGxhc3NvV2lkZ2V0KSA6IHN1Ym1pdH1cbiAgbGFzc29XaWRnZXQubWF4TnVtYmVyT2ZHcm91cHMgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChtYXhOdW1iZXJPZkdyb3Vwcz1fLCBsYXNzb1dpZGdldCkgOiBtYXhOdW1iZXJPZkdyb3Vwc31cbiAgbGFzc29XaWRnZXQub25FcnJvciA9IGZ1bmN0aW9uKF8pe3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9uRXJyb3I9XywgbGFzc29XaWRnZXQpIDogb25FcnJvcn1cbiAgbGFzc29XaWRnZXQub2JqZWN0Q29udGFpbmVyID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q29udGFpbmVyPV8sIGxhc3NvV2lkZ2V0KSA6IG9iamVjdENvbnRhaW5lcn1cbiAgbGFzc29XaWRnZXQuY2hhcnRDb250YWluZXIgPSBmdW5jdGlvbihfKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChjaGFydENvbnRhaW5lcj1fLCBsYXNzb1dpZGdldCkgOiBjaGFydENvbnRhaW5lcn1cbiAgbGFzc29XaWRnZXQuZGF0YUV4dHJhY3RvciA9IGZ1bmN0aW9uKF8pe3JldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRhdGFFeHRyYWN0b3I9XywgbGFzc29XaWRnZXQpIDogZGF0YUV4dHJhY3Rvcn1cbiAgbGFzc29XaWRnZXQueFNjYWxlID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeFNjYWxlPV8sIGxhc3NvV2lkZ2V0KSA6IHhTY2FsZX1cbiAgbGFzc29XaWRnZXQueVNjYWxlID0gZnVuY3Rpb24oXyl7cmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeVNjYWxlPV8sIGxhc3NvV2lkZ2V0KSA6IHlTY2FsZX1cblxuICBmdW5jdGlvbiBzdHlsZXMoKSB7XG4gICAgdmFyIHMgPSBkMy5zZWxlY3QoXCJodG1sXCIpLnNlbGVjdChcInN0eWxlLlwiK25hbWVzcGFjZSsnbGFzc28td2lkZ2V0JylcbiAgICBpZiAocy5lbXB0eSgpKSB7XG4gICAgICBkMy5zZWxlY3QoXCJodG1sXCIpLmFwcGVuZChcInN0eWxlXCIpXG4gICAgICAuY2xhc3NlZChuYW1lc3BhY2UrJ2xhc3NvLXdpZGdldCcsIHRydWUpXG4gICAgICAuaHRtbChcbiAgICAgICAgXCIuXCIraHlwZW5hdGUobmFtZXNwYWNlLCBcImRhdGEtdGFibGVcIikgKyBcIntcXFxuICAgICAgICAgIGhlaWdodDoxMDBweDtcXFxuICAgICAgICAgIG92ZXJmbG93OmF1dG87XFxcbiAgICAgICAgfVwiXG4gICAgICApXG4gICAgfVxuICB9XG5cblxuICBmdW5jdGlvbiBsYXNzb1dpZGdldCgpIHtcbiAgICBzdHlsZXMoKVxuXG4gIH1cblxuICBmdW5jdGlvbiBzdWJtaXQoZGF0YSkge1xuICAgIGNvbnNvbGUubG9nKGRhdGEpXG4gIH1cblxuICBmdW5jdGlvbiBwbHVzVGFiKHRhYkxpc3QpIHtcbiAgICB2YXIgdGFiID0gc2FmZVNlbGVjdCh0YWJMaXN0LCAnbGknLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdwbHVzLXRhYicpKVxuICAgIC5jbGFzc2VkKCdtbC1hdXRvJywgdHJ1ZSlcbiAgICAuY2xhc3NlZCgnbmF2LWl0ZW0nLCB0cnVlKVxuICAgIC5vbignY2xpY2snLCBtYWtlTmV3R3JvdXApLFxuXG4gICAgYW5jaG9yID0gc2FmZVNlbGVjdCh0YWIsICdhJywgJ25hdi1saW5rJyksXG4gICAgaWNvbiA9IHNhZmVTZWxlY3QoYW5jaG9yLCAnaScsICdmYScpXG4gICAgLmNsYXNzZWQoJ2ZhLXBsdXMgZmEtMnggdGV4dC1zdWNjZXNzJywgdHJ1ZSlcbiAgfVxuXG4gIGZ1bmN0aW9uIHNlbmRUYWIodGFiTGlzdCkge1xuICAgIHZhciB0YWIgPSBzYWZlU2VsZWN0KHRhYkxpc3QsICdsaScsIGh5cGVuYXRlKG5hbWVzcGFjZSwgJ3NlbmQtdGFiJykpXG4gICAgLmNsYXNzZWQoJ21sLWF1dG8nLCB0cnVlKVxuICAgIC5jbGFzc2VkKCduYXYtaXRlbScsIHRydWUpXG4gICAgLm9uKCdjbGljaycsIGNsaWNrU2VuZClcbiAgICAsXG5cbiAgICBhbmNob3IgPSBzYWZlU2VsZWN0KHRhYiwgJ2EnLCAnbmF2LWxpbmsnKSxcbiAgICBpY29uID0gc2FmZVNlbGVjdChhbmNob3IsICdpJywgJ2ZhJylcbiAgICAuY2xhc3NlZCgnZmEtcGFwZXItcGxhbmUtbyBmYS0yeCB0ZXh0LXByaW1hcnknLCB0cnVlKVxuICB9XG5cbiAgZnVuY3Rpb24gY2xpY2tTZW5kKCkge1xuICAgIHZhciBkYXRhID0gZ2F0aGVyRGF0YUxpc3RzKClcbiAgICBzdWJtaXQoZGF0YSlcbiAgfVxuXG4gIGZ1bmN0aW9uIGNsb3NlVGFiKHRhYkxpc3QpIHtcbiAgICB2YXIgdGFiID0gc2FmZVNlbGVjdCh0YWJMaXN0LCAnbGknLCBoeXBlbmF0ZShuYW1lc3BhY2UsICdjbG9zZS10YWInKSlcbiAgICAuY2xhc3NlZCgnbWwtYXV0bycsIHRydWUpXG4gICAgLmNsYXNzZWQoJ25hdi1pdGVtJywgdHJ1ZSlcbiAgICAub24oJ2NsaWNrJywgZnVuY3Rpb24oKXtcbiAgICAgIHZhciBtID0gZDMubW91c2UoZDMuc2VsZWN0KFwiaHRtbFwiKS5ub2RlKCkpXG4gICAgICBzZWxlY3Rpb24uc2VsZWN0KCdkaXYuY2FyZCcpLnN0eWxlKFwicG9zaXRpb25cIiwgJ3JlbGF0aXZlJylcbiAgICAgIC50cmFuc2l0aW9uKCkuZHVyYXRpb24oMTAwMClcbiAgICAgIC5lYXNlKGQzLmVhc2VCYWNrKVxuICAgICAgLnN0eWxlKCdsZWZ0Jywgd2luZG93Lm91dGVyV2lkdGggKydweCcpXG4gICAgICAucmVtb3ZlKClcbiAgICB9KVxuICAgICxcblxuICAgIGFuY2hvciA9IHNhZmVTZWxlY3QodGFiLCAnYScsICduYXYtbGluaycpLFxuICAgIGljb24gPSBzYWZlU2VsZWN0KGFuY2hvciwgJ2knLCAnZmEnKVxuICAgIC5jbGFzc2VkKCdmYS13aW5kb3ctY2xvc2UtbyBmYS0yeCB0ZXh0LWRhbmdlcicsIHRydWUpXG4gIH1cblxuXG5cbiAgZnVuY3Rpb24gc2V0dXBEYXRhc2VsZWN0Q29udGFpbmVyKCkge1xuICAgIHZhclxuICAgIGNhcmQgPSBzYWZlU2VsZWN0KHNlbGVjdGlvbiwgJ2RpdicsICdjYXJkJyksXG4gICAgY2FyZEhlYWRlciA9IHNhZmVTZWxlY3QoY2FyZCwgJ2RpdicsICdjYXJkLWhlYWRlcicpLFxuXG4gICAgdGFiTGlzdCA9IHNhZmVTZWxlY3QoY2FyZEhlYWRlciwgJ3VsJywgJ25hdicpXG4gICAgLmNsYXNzZWQoJ25hdi10YWJzIGNhcmQtaGVhZGVyLXRhYnMnLCB0cnVlKVxuICAgIC5jbGFzc2VkKGh5cGVuYXRlKG5hbWVzcGFjZSwgJ3RhYi1saXN0JyksIHRydWUpXG4gICAgLmF0dHIoJ3JvbGUnLCAndGFibGlzdCcpLFxuXG4gICAgY2FyZEJvZHkgPSBzYWZlU2VsZWN0KGNhcmQsICdkaXYnLCAnY2FyZC1ib2R5JyksXG4gICAgdGFiQ29udGVudCA9IHNhZmVTZWxlY3QoY2FyZEJvZHksICdkaXYnLCAndGFiLWNvbnRlbnQnKVxuICAgIC5jbGFzc2VkKGh5cGVuYXRlKG5hbWVzcGFjZSwgJ3RhYi1jb250ZW50JyksIHRydWUpLFxuXG4gICAgLy8gbGVmdFRhYnMgPSBzYWZlU2VsZWN0KHRhYkxpc3QsICdkaXYnLCAnbmF2IG1yLWF1dG8gbGVmdC1hbGlnbmVkLXRhYnMnKVxuICAgIHJpZ2h0VGFicyA9IHNhZmVTZWxlY3QodGFiTGlzdCwgJ2RpdicsICdyaWdodC1hbGlnbmVkLXRhYnMnKS5jbGFzc2VkKCduYXYgbWwtYXV0byAnLCB0cnVlKVxuXG4gICAgcGx1c1RhYihyaWdodFRhYnMpXG4gICAgc2VuZFRhYihyaWdodFRhYnMpXG4gICAgY2xvc2VUYWIocmlnaHRUYWJzKVxuICAgIHZhclxuICAgIGRlZmF1bHRUYWIgPSBzYWZlU2VsZWN0KHRhYkNvbnRlbnQsICdkaXYnLCAndGFiLXBhbmUnKVxuICAgIC5jbGFzc2VkKGh5cGVuYXRlKG5hbWVzcGFjZSwnZGVmYXVsdC10YWInKSwgdHJ1ZSlcbiAgICAuY2xhc3NlZChcImFjdGl2ZVwiLCB0cnVlKVxuICAgIC5jbGFzc2VkKCd0ZXh0LWxlZnQnLCB0cnVlKSxcblxuICAgIHAgPSBzYWZlU2VsZWN0KGRlZmF1bHRUYWIsICdkaXYnKVxuICAgIC5odG1sKFxuICAgICAgXCJDbGljayA8a2JkPjxpIGNsYXNzPSdmYSBmYS1wbHVzIHRleHQtc3VjY2Vzcyc+PC9pPjwva2JkPiB0byBhZGQgYSBuZXcgZ3JvdXAuPGJyPlwiK1xuICAgICAgXCJDbGljayA8a2JkPjxpIGNsYXNzPSdmYSBmYS1wYXBlci1wbGFuZS1vIHRleHQtcHJpbWFyeSc+PC9pPjwva2JkPiB0byBzdWJtaXQgZm9yIHJlLWFuYWx5c2lzLjxicj5cIitcbiAgICAgIFwiQ2xpY2sgPGtiZD48aSBjbGFzcz0nZmEgZmEtd2luZG93LWNsb3NlLW8gdGV4dC1kYW5nZXInPjwvaT48L2tiZD4gdG8gY2xvc2UgdGhlIGRhdGFzZWxlY3Qgd2lkZ2V0LlwiXG4gICAgKVxuICAgIC8vIC50ZXh0KCdDbGljayB0aGUgZ3JlZW4gcGx1cyB0byBhZGQgYSBuZXcgZ3JvdXAuJylcbiAgfVxuXG5cbiAgZnVuY3Rpb24gbWFrZVJlbW92ZUJ1dHRvbihwYW5lQnRuTGlzdCkge1xuICAgIHZhclxuICAgIGJ0biA9IHNhZmVTZWxlY3QocGFuZUJ0bkxpc3QsICdidXR0b24nLCAncmVtb3ZlLWJ0bicpXG4gICAgLmNsYXNzZWQoJ2J0biBidG4tZGFuZ2VyJywgdHJ1ZSlcbiAgICAub24oJ2NsaWNrJywgcmVtb3ZlQnRuQ2xpY2tUb1JlbW92ZSksXG4gICAgaSA9IHNhZmVTZWxlY3QoYnRuLCAnaScsICdmYScpLmNsYXNzZWQoJ2ZhLXRyYXNoLW8nLCB0cnVlKSxcbiAgICBzID0gc2FmZVNlbGVjdChidG4sICdzcGFuJykudGV4dCgnUmVtb3ZlIGdyb3VwJylcbiAgICByZXR1cm4gYnRuXG4gIH1cblxuICBmdW5jdGlvbiByZW1vdmVCdG5DbGlja1RvUmVtb3ZlKGQsIGkpIHtcbiAgICB2YXJcbiAgICB0YWIgPSBzZWxlY3Rpb24uc2VsZWN0KCcjJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicsZCkpLFxuICAgIHBhbmUgPSBzZWxlY3Rpb24uc2VsZWN0KCcjJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicsJ3BhbmUnLGQpKVxuXG4gICAgdGFiLnJlbW92ZSgpXG4gICAgcGFuZS5yZW1vdmUoKVxuXG4gICAgc2hvd1JlbWFpbmluZ1BhbmVzKClcbiAgICB1cGRhdGVUYWJBbmRQYW5lTnVtYmVycygpXG4gIH1cblxuXG4gIGZ1bmN0aW9uIHRvZ2dsZVBhbmVCeUlkKGlkKSB7XG4gICAgdmFyIHBhbmVzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWNvbnRlbnQnKSlcbiAgICBwYW5lcy5zZWxlY3RBbGwoJ2Rpdi50YWItcGFuZScpXG4gICAgLmVhY2goZnVuY3Rpb24oZCwgaSl7XG4gICAgICBkMy5zZWxlY3QodGhpcykuY2xhc3NlZCgnYWN0aXZlIHNob3cnLCBkMy5zZWxlY3QodGhpcykuYXR0cignaWQnKSA9PSBpZClcbiAgICB9KVxuICB9XG5cbiAgZnVuY3Rpb24gaW5zZXJ0VGFiKHRhYnMsIG4pIHtcblxuICAgIC8vIHZhciBsaSA9IHRhYnMuaW5zZXJ0KFwibGlcIiwgJzpudGgtY2hpbGQoJysobikrJyknKVxuICAgIHZhciBsaSA9IHRhYnMuaW5zZXJ0KFwibGlcIiwgJy5yaWdodC1hbGlnbmVkLXRhYnMnKVxuICAgIC5jbGFzc2VkKCduYXYtaXRlbScsIHRydWUpXG4gICAgLmNsYXNzZWQoJ2FsZXJ0JywgdHJ1ZSlcbiAgICAuY2xhc3NlZCgnYWxlcnQtc2Vjb25kYXJ5JywgdHJ1ZSlcbiAgICAuY2xhc3NlZCgnYWxlcnQtZGlzbWlzc2FibGUnLCB0cnVlKVxuICAgIC5jbGFzc2VkKCdmYWRlJywgdHJ1ZSlcbiAgICAuY2xhc3NlZCgnc2hvdycsIHRydWUpXG4gICAgLy8gLmNsYXNzZWQoJ21yLWF1dG8nLCB0cnVlKVxuICAgIC5hdHRyKFwicm9sZVwiLCAnYWxlcnQnKVxuICAgIC5hdHRyKFwiaWRcIiwgaHlwZW5hdGUobmFtZXNwYWNlLCd0YWInLG4pKVxuICAgIC5jbGFzc2VkKGh5cGVuYXRlKG5hbWVzcGFjZSwndGFiJyksIHRydWUpXG5cbiAgICB2YXIgYSA9IHNhZmVTZWxlY3QobGksICdhJylcbiAgICAuYXR0cignZGF0YS10b2dnbGUnLCAndGFiJylcbiAgICAudGV4dCgnR3JvdXAgJyArIG4pXG4gICAgLmF0dHIoJ2hyZWYnLCAnIycraHlwZW5hdGUobmFtZXNwYWNlLCd0YWInLCdwYW5lJyxuKSlcbiAgICAub24oJ2RibGNsaWNrJywgZnVuY3Rpb24oZCwgaSl7XG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgICAgdC5hdHRyKCdjb250ZW50ZWRpdGFibGUnLCB0cnVlKVxuICAgICAgZDMuc2VsZWN0KHQubm9kZSgpLnBhcmVudE5vZGUpXG4gICAgICAuY2xhc3NlZCgnYWxlcnQtc2Vjb25kYXJ5JywgZmFsc2UpXG4gICAgICAuY2xhc3NlZCgnYWxlcnQtd2FybmluZycsIHRydWUpXG5cbiAgICAgIC8vIGQzLnNlbGVjdChcImh0bWxcIikub24oXCJjbGlja1wiLCBkaXNwYXRjaEJsdXIpXG4gICAgICAvL1xuICAgICAgLy8gZnVuY3Rpb24gZGlzcGF0Y2hCbHVyKGQsIGkpIHtcbiAgICAgIC8vICAgaWYgKGQzLmV2ZW50LnRhcmdldCAhPSBkMy5zZWxlY3QodGhpcykpIHtcbiAgICAgIC8vICAgICBkMy5zZWxlY3QodGhpcykuZGlzcGF0Y2goXCJibHVyXCIpXG4gICAgICAvLyAgIH1cbiAgICAgIC8vIH1cblxuXG4gICAgfSlcbiAgICAub24oJ2JsdXInLCBmdW5jdGlvbihkLCBpKXtcblxuICAgICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcylcbiAgICAgIHQuYXR0cignY29udGVudGVkaXRhYmxlJywgZmFsc2UpXG4gICAgICBkMy5zZWxlY3QodC5ub2RlKCkucGFyZW50Tm9kZSlcbiAgICAgIC5jbGFzc2VkKCdhbGVydC1zZWNvbmRhcnknLCB0cnVlKVxuICAgICAgLmNsYXNzZWQoJ2FsZXJ0LXdhcm5pbmcnLCBmYWxzZSlcblxuICAgIH0pXG4gICAgLm9uKCdpbnB1dCcsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcylcbiAgICAgIHZhciB0eHQgPSB0LnRleHQoKVxuICAgICAgZDMuc2VsZWN0KHQuYXR0cignaHJlZicpKS5zZWxlY3QoXCJwLmxlYWRcIikudGV4dCh0eHQpXG4gICAgfSlcblxuXG4gICAgcmV0dXJuIGxpXG4gIH1cblxuICBmdW5jdGlvbiBtYWtlVGFiUGFuZShwYW5lcywgbikge1xuICAgIHZhciBwYW5lID0gcGFuZXMuYXBwZW5kKCdkaXYnKVxuICAgIC5kYXR1bShuKVxuICAgIC5hdHRyKCdyb2xlJywgJ3RhYnBhbmVsJylcbiAgICAuY2xhc3NlZCgndGFiLXBhbmUnLCB0cnVlKVxuICAgIC5jbGFzc2VkKCd0ZXh0LWxlZnQnLCB0cnVlKVxuICAgIC5jbGFzc2VkKGh5cGVuYXRlKG5hbWVzcGFjZSwndGFiJywncGFuZScpLCB0cnVlKVxuICAgIC5hdHRyKCdpZCcsIGh5cGVuYXRlKG5hbWVzcGFjZSwndGFiJywncGFuZScsbikpXG4gICAgcmV0dXJuIHBhbmVcbiAgfVxuXG4gIGZ1bmN0aW9uIHBvcHVsYXRlUGFuZShwYW5lLCBuKSB7XG4gICAgdmFyXG4gICAgbGVhZCA9IHNhZmVTZWxlY3QocGFuZSwgJ3AnLCAnbGVhZCcpLnRleHQoJ0dyb3VwICcrbiksXG5cbiAgICB0YWJsZUNvbnRhaW5lciA9IHNhZmVTZWxlY3QocGFuZSwgJ2RpdicsICd0YWJsZS1yZXNwb25zaXZlJylcbiAgICAuYXR0cihcImNsYXNzXCIsIGh5cGVuYXRlKG5hbWVzcGFjZSwgXCJkYXRhLXRhYmxlXCIpKSxcblxuICAgIHRhYmxlID0gc2FmZVNlbGVjdCh0YWJsZUNvbnRhaW5lciwgXCJ0YWJsZVwiLCBcInRhYmxlXCIpXG4gICAgLmNsYXNzZWQoXCJ0YWJsZS1zbVwiLCB0cnVlKS5jbGFzc2VkKFwidGFibGUtaG92ZXJcIiwgdHJ1ZSksXG5cbiAgICBjYXB0aW9uID0gc2FmZVNlbGVjdCh0YWJsZSwgXCJjYXB0aW9uXCIsIFwiY2FwdGlvblwiKS5odG1sKFwiTGlzdCBvZiBzZWxlY3RlZFwiKSxcblxuICAgIGJ0bnMgPSBzYWZlU2VsZWN0KHBhbmUsICdkaXYnLCAndGV4dC1yaWdodCcpLFxuXG4gICAgY04gPSBuICUgY29sb3JGdW5jdGlvbi5jb2xvcnMoKS5sZW5ndGhcbiAgICBpZiAobiAlIDIgIT0gMCkgeyBjTiA9IChjb2xvckZ1bmN0aW9uLmNvbG9ycygpLmxlbmd0aCAtIDEpIC0gY04gfVxuXG5cblxuXG5cbiAgICB2YXIgbGFzc29CdG4gPSBtYWtlTGFzc29CdXR0b24oYnRucylcbiAgICB2YXIgY3VycmVudExhc3NvID0gbWFrZUxhc3NvRnVuY3Rpb24obiwgY29sb3JGdW5jdGlvbi5jb2xvcnMoKVtjTl0pXG4gICAgdmFyIGNsZWFyQnRuID0gbWFrZUNsZWFyQnV0dG9uKGJ0bnMpXG5cbiAgICBiaW5kQnV0dG9ucyhsYXNzb0J0biwgY2xlYXJCdG4sIGN1cnJlbnRMYXNzbywgdGFibGUpXG5cbiAgICB2YXIgcmVtb3ZlQnRuID0gbWFrZVJlbW92ZUJ1dHRvbihidG5zKVxuXG4gICAgbGVhZC5vbihcIm1vdXNlb3ZlclwiLCBmdW5jdGlvbigpe1xuICAgICAgY3VycmVudExhc3NvLmRyYXcoKVxuICAgIH0pXG4gICAgbGVhZC5vbihcIm1vdXNlb3V0XCIsIGZ1bmN0aW9uKCl7XG4gICAgICBjdXJyZW50TGFzc28ucmVtb3ZlKClcbiAgICB9KVxuXG4gIH1cblxuICBmdW5jdGlvbiBtYWtlTGFzc29CdXR0b24oYnRucykge1xuICAgIHZhciBidG4gPSBzYWZlU2VsZWN0KGJ0bnMsICdidXR0b24nLCAnbGFzc28tYnRuJylcbiAgICAuY2xhc3NlZCgnYnRuIGJ0bi1pbmZvJywgdHJ1ZSlcbiAgICAuY2xhc3NlZChuYW1lc3BhY2UsIHRydWUpXG4gICAgLy8gLmRhdHVtKFtsYXNzb10pXG4gICAgdmFyIGkgPSBzYWZlU2VsZWN0KGJ0biwgJ2knLCAnZmEnKS5jbGFzc2VkKCdmYS1oYW5kLXBvaW50ZXItbycsIHRydWUpXG4gICAgdmFyIHMgPSBzYWZlU2VsZWN0KGJ0biwgJ3NwYW4nKS50ZXh0KCdMYXNzbyBzZWxlY3QnKVxuICAgIHJldHVybiBidG5cblxuICB9XG5cbiAgZnVuY3Rpb24gbWFrZUNsZWFyQnV0dG9uKGJ0bnMpIHtcbiAgICB2YXIgYnRuID0gc2FmZVNlbGVjdChidG5zLCAnYnV0dG9uJywgJ2NsZWFyLWJ0bicpXG4gICAgLmNsYXNzZWQoJ2J0biBidG4tbGlnaHQnLCB0cnVlKVxuICAgIC5jbGFzc2VkKG5hbWVzcGFjZSwgdHJ1ZSlcbiAgICB2YXIgaSA9IHNhZmVTZWxlY3QoYnRuLCAnaScsICdmYScpLmNsYXNzZWQoJ2ZhLWVyYXNlcicsIHRydWUpXG4gICAgdmFyIHMgPSBzYWZlU2VsZWN0KGJ0biwgJ3NwYW4nKS50ZXh0KCdDbGVhciBzZWxlY3Rpb24nKVxuICAgIHJldHVybiBidG5cbiAgfVxuXG4gIGZ1bmN0aW9uIG1ha2VMYXNzb0Z1bmN0aW9uKGluc3RhbmNlLCBjb2xvcikge1xuICAgIHZhciBjdXJyZW50TGFzc28gPSBsYXNzbygpXG4gICAgLm5hbWVzcGFjZShuYW1lc3BhY2UpXG4gICAgLnN2ZyhzdmcpXG4gICAgLm9iamVjdENsYXNzKG9iamVjdENsYXNzKVxuICAgIC5jaGFydENvbnRhaW5lcihjaGFydENvbnRhaW5lcilcbiAgICAub2JqZWN0Q29udGFpbmVyKG9iamVjdENvbnRhaW5lcilcbiAgICAuaW5zdGFuY2UoaW5zdGFuY2UpXG4gICAgLmNvbG9yKGNvbG9yKVxuICAgIC55U2NhbGUoeVNjYWxlKVxuICAgIC54U2NhbGUoeFNjYWxlKVxuXG4gICAgcmV0dXJuIGN1cnJlbnRMYXNzb1xuICB9XG5cbiAgZnVuY3Rpb24gYmluZEJ1dHRvbnMobGFzc29CdG4sIGNsZWFyQnRuLCBjdXJyZW50TGFzc28sIHRhYmxlKSB7XG4gICAgY3VycmVudExhc3NvLmV2ZW50Q2F0Y2hlcihsYXNzb0J0bilcbiAgICBsYXNzb0J0bi5ub2RlKCkuYWRkRXZlbnRMaXN0ZW5lcihcImNsaWNrXCIsIGxhc3NvQnRuVG9nZ2xlKVxuXG4gICAgbGFzc29CdG4uZGF0dW0oW2N1cnJlbnRMYXNzb10pXG4gICAgLm9uKGh5cGVuYXRlKG5hbWVzcGFjZSwncmVuZGVyJyksIGZ1bmN0aW9uKCl7XG4gICAgICAgIGQzLnNlbGVjdCh0aGlzKS5kYXR1bSgpWzBdLmRyYXcoKVxuICAgIH0pXG4gICAgLm9uKGh5cGVuYXRlKG5hbWVzcGFjZSxcImRyYWdcIiksIGZ1bmN0aW9uKGxhcyl7XG4gICAgICB2YXIgbm9kZXMgPSBjaGFydENvbnRhaW5lci5zZWxlY3RBbGwoXCIuaW4tbGFzc28tXCIrbGFzWzBdLmluc3RhbmNlKCkpLm5vZGVzKClcbiAgICAgIHZhciB0YWJsZURhdGEgPSBub2Rlcy5tYXAoZnVuY3Rpb24oZCwgaSl7XG4gICAgICAgIHZhciBleHRyYWN0ZWQgPSBkYXRhRXh0cmFjdG9yKGQzLnNlbGVjdChkKS5kYXR1bSgpKVxuICAgICAgICBleHRyYWN0ZWRbXCJfX25vZGVcIl0gPSBkXG4gICAgICAgIHJldHVybiBleHRyYWN0ZWRcbiAgICAgIH0pXG5cblxuICAgICAgbWFrZVRhYmxlKHRhYmxlLCB0YWJsZURhdGEsIGN1cnJlbnRMYXNzbylcbiAgICB9KVxuXG4gICAgY2xlYXJCdG4ub24oJ2NsaWNrJywgZnVuY3Rpb24oKXtcbiAgICAgIGN1cnJlbnRMYXNzby5hbGxQb2ludHMoW10pXG4gICAgICBjdXJyZW50TGFzc28uY3VycmVudFBvaW50cyhbXSlcbiAgICAgIGxhc3NvQnRuLmRpc3BhdGNoKGh5cGVuYXRlKG5hbWVzcGFjZSxcImRyYWdcIikpXG5cbiAgICB9KVxuXG4gIH1cblxuICBmdW5jdGlvbiBsYXNzb0J0blRvZ2dsZSgpIHtcbiAgICB2YXIgdGhhdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgIHZhciBsYXMgPSB0aGF0LmRhdHVtKClbMF1cblxuICAgIGxhcy50b2dnbGUoKVxuICAgIHZhciBhY3RpdmVRID0gbGFzLmFjdGl2ZVEoKVxuXG4gICAgaWYgKGxhcy5hY3RpdmVRKCkpIHtcbiAgICAgIHRoYXQuY2xhc3NlZCgnYnRuLWluZm8nLCAhYWN0aXZlUSlcbiAgICAgIHRoYXQuY2xhc3NlZCgnYnRuLXdhcm5pbmcnLCBhY3RpdmVRKVxuICAgICAgdGhhdC5zZWxlY3QoXCJzcGFuXCIpLnRleHQoXCJMYXNzbyBzZWxlY3QgKGFjdGl2ZSlcIilcbiAgICAgIHNlbGVjdGlvbi5zZWxlY3RBbGwoXCIuXCIrbmFtZXNwYWNlK1wiLmxhc3NvLWJ0blwiKS5kaXNwYXRjaChoeXBlbmF0ZShuYW1lc3BhY2UsJ3JlbmRlcicpKVxuICAgICAgZDMuc2VsZWN0KFwiaHRtbFwiKS5ub2RlKCkuYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vkb3duJywgbW9uaXRvckxhc3NvQnV0dG9uU3RhdGUpXG4gICAgfSBlbHNlIHtcbiAgICAgIHRoYXQuY2xhc3NlZCgnYnRuLWluZm8nLCAhYWN0aXZlUSlcbiAgICAgIHRoYXQuY2xhc3NlZCgnYnRuLXdhcm5pbmcnLCBhY3RpdmVRKVxuICAgICAgdGhhdC5zZWxlY3QoXCJzcGFuXCIpLnRleHQoXCJMYXNzbyBzZWxlY3RcIilcbiAgICAgIGQzLnNlbGVjdChcImh0bWxcIikubm9kZSgpLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNlZG93bicsIG1vbml0b3JMYXNzb0J1dHRvblN0YXRlKVxuICAgIH1cblxuICAgIGZ1bmN0aW9uIG1vbml0b3JMYXNzb0J1dHRvblN0YXRlKGV2ZW50KSB7XG4gICAgICAvKlxuICAgICAgYWN0aXZlTGFzc28gc3RvcHMgZXZlbnQgc3RvcFByb3BhZ2F0aW9uLCBzbyB0aGlzIGV2ZW50IHdpbGwgbm90IHJlZ2lzdGVyIGluXG4gICAgICB0aGF0IGNhc2UuIFRodXMgd2Ugb25seSBuZWVkIHRvIGVuc3VyZSB0aGF0IHRoZSB1c3JlIGlzIGNsaWNraW5nIG9uIHRoZSBsYXNzbyBidXR0b25cbiAgICAgIG90aGVyd2lzZSwgZGUtc3Bhd24gbGFzc28uXG4gICAgICAqL1xuICAgICAgaWYgKFxuICAgICAgICBldmVudC50YXJnZXQgIT0gdGhhdC5ub2RlKCkgJiZcbiAgICAgICAgZXZlbnQudGFyZ2V0ICE9IHRoYXQuc2VsZWN0KFwic3BhblwiKS5ub2RlKCkgJiZcbiAgICAgICAgZXZlbnQudGFyZ2V0ICE9IHRoYXQuc2VsZWN0KFwiaVwiKS5ub2RlKClcbiAgICAgICkge1xuICAgICAgICAvLyBldmVudC5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgIC8vIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXG4gICAgICAgIGxhcy50b2dnbGUoZmFsc2UpXG4gICAgICAgIHRoYXQuY2xhc3NlZCgnYnRuLWluZm8nLCB0cnVlKVxuICAgICAgICB0aGF0LmNsYXNzZWQoJ2J0bi13YXJuaW5nJywgZmFsc2UpXG4gICAgICAgIHRoYXQuc2VsZWN0KFwic3BhblwiKS50ZXh0KFwiTGFzc28gc2VsZWN0XCIpXG4gICAgICAgIC8vIGNvbnNvbGUubG9nKHRoYXQsIHRoYXQubm9kZSgpKVxuICAgICAgICAvLyB0aGF0LmRpc3BhdGNoKFwiZm9jdXNvdXRcIilcbiAgICAgIH1cbiAgICB9XG5cbiAgfVxuXG5cbiAgZnVuY3Rpb24gdXBkYXRlVGFibGVIZWFkZXJDb2x1bW5zKGhlYWRSLCB0YWJsZURhdGEpIHtcbiAgICB2YXIgaGVhZGVyS2V5cyA9IGQzLmtleXModGFibGVEYXRhWzBdKS5maWx0ZXIoaz0+ayE9XCJfX25vZGVcIilcbiAgICBpZiAoaGVhZGVyS2V5cy5sZW5ndGggPiAwKSB7XG4gICAgICAvLyBoZWFkZXJLZXlzID0gW1wicmVtb3ZlXCJdLmNvbmNhdChoZWFkZXJLZXlzKVxuICAgICAgaGVhZGVyS2V5cy5wdXNoKFwicmVtb3ZlXCIpXG4gICAgfVxuXG4gICAgdmFyIGhlYWRlckNvbHMgPSBoZWFkUi5zZWxlY3RBbGwoXCJ0aFwiKVxuXG4gICAgaGVhZGVyQ29scyA9IGhlYWRlckNvbHMuZGF0YShoZWFkZXJLZXlzKVxuICAgIGhlYWRlckNvbHMuZXhpdCgpLnJlbW92ZSgpXG4gICAgaGVhZGVyQ29scyA9IGhlYWRlckNvbHMubWVyZ2UoaGVhZGVyQ29scy5lbnRlcigpLmFwcGVuZChcInRoXCIpLmF0dHIoXCJzY29wZVwiLFwiY29sXCIpKVxuICAgIC50ZXh0KGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkfSlcblxuICAgIHJldHVybiBoZWFkZXJLZXlzXG4gIH1cblxuICBmdW5jdGlvbiB1cGRhdGVUYWJsZVJvd3MoYm9keSwgdGFibGVEYXRhKSB7XG4gICAgdmFyIGJvZHlSb3dzID0gYm9keS5zZWxlY3RBbGwoXCJ0clwiKVxuXG4gICAgYm9keVJvd3MgPSBib2R5Um93cy5kYXRhKHRhYmxlRGF0YSlcbiAgICBib2R5Um93cy5leGl0KCkucmVtb3ZlKClcbiAgICBib2R5Um93cyA9IGJvZHlSb3dzLm1lcmdlKGJvZHlSb3dzLmVudGVyKCkuYXBwZW5kKFwidHJcIikpXG5cbiAgICByZXR1cm4gYm9keVJvd3NcbiAgfVxuXG4gIGZ1bmN0aW9uIHVwZGF0ZVRhYmxlUm93Q29sdW1ucyhjb2xzLCBoZWFkZXJLZXlzLCByb3dEYXRhLCB0YWJsZURhdGEsIGxhc3NvLCB0YWJsZSkge1xuICAgIGNvbHMgPSBjb2xzLmRhdGEoaGVhZGVyS2V5cylcbiAgICBjb2xzLmV4aXQoKS5yZW1vdmUoKVxuICAgIGNvbHMgPSBjb2xzLm1lcmdlKGNvbHMuZW50ZXIoKS5hcHBlbmQoXCJ0ZFwiKSlcblxuICAgIGNvbHMuaHRtbChmdW5jdGlvbihkLCBpKXtcbiAgICAgIGlmIChkICE9IFwicmVtb3ZlXCIpIHsgcmV0dXJuIHJvd0RhdGFbZF0gfVxuICAgICAgcmV0dXJuIFwiPGkgY2xhc3M9J2ZhIGZhLWNsb3NlJz48L2k+XCJcbiAgICB9KS5jbGFzc2VkKFwidGV4dC1sZWZ0XCIsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgaWYgKGQgIT0gXCJyZW1vdmVcIikgeyByZXR1cm4gZmFsc2UgfVxuICAgICAgcmV0dXJuIHRydWVcbiAgICB9KVxuICAgIC8vIC5hdHRyKFwic2NvcGVcIixmdW5jdGlvbihkLCBpKXtcbiAgICAvLyAgIGlmIChkICE9IFwicmVtb3ZlXCIpIHsgcmV0dXJuIGZhbHNlIH1cbiAgICAvLyAgIHJldHVybiB0cnVlXG4gICAgLy8gfSlcblxuICAgIGNvbHMuc2VsZWN0KFwiaS5mYS1jbG9zZVwiKS5vbihcImNsaWNrXCIsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgdmFyIG5vZGUgPSByb3dEYXRhW1wiX19ub2RlXCJdLFxuICAgICAgbiA9IGQzLnNlbGVjdChub2RlKVxuICAgICAgbi5jbGFzc2VkKFwiaW4tbGFzc29cIiwgZmFsc2UpXG4gICAgICBuLmNsYXNzZWQoXCJpbi1sYXNzby1cIitsYXNzby5pbnN0YW5jZSgpLCBmYWxzZSlcblxuICAgICAgdGFibGVEYXRhLm1hcChmdW5jdGlvbihkZCwgail7XG4gICAgICAgIGlmIChkZFtcIl9fbm9kZVwiXSA9PSBub2RlKSB7XG4gICAgICAgICAgdGFibGVEYXRhLnNwbGljZShqLCAxKVxuICAgICAgICAgIG1ha2VUYWJsZSh0YWJsZSwgdGFibGVEYXRhLCBsYXNzbylcbiAgICAgICAgfVxuICAgICAgfSlcblxuICAgIH0pXG4gIH1cblxuICBmdW5jdGlvbiBtYWtlVGFibGUodGFibGUsIHRhYmxlRGF0YSwgbGFzc28pIHtcbiAgICB2YXJcbiAgICBoZWFkID0gc2FmZVNlbGVjdCh0YWJsZSwgXCJ0aGVhZFwiKSxcbiAgICBoZWFkUiA9IHNhZmVTZWxlY3QoaGVhZCwgXCJ0clwiKSxcbiAgICBib2R5ID0gc2FmZVNlbGVjdCh0YWJsZSwgXCJ0Ym9keVwiKSxcblxuICAgIGhlYWRlcktleXMgPSB1cGRhdGVUYWJsZUhlYWRlckNvbHVtbnMoaGVhZFIsIHRhYmxlRGF0YSksXG5cbiAgICBib2R5Um93cyA9IHVwZGF0ZVRhYmxlUm93cyhib2R5LCB0YWJsZURhdGEpXG5cbiAgICBib2R5Um93cy5lYWNoKGZ1bmN0aW9uKHJvd0RhdGEsIGkpe1xuICAgICAgdmFyIHQgPSBkMy5zZWxlY3QodGhpcylcbiAgICAgIHZhciBjb2xzID0gdC5zZWxlY3RBbGwoXCJ0ZFwiKVxuXG4gICAgICB1cGRhdGVUYWJsZVJvd0NvbHVtbnMoY29scywgaGVhZGVyS2V5cywgcm93RGF0YSwgdGFibGVEYXRhLCBsYXNzbywgdGFibGUpXG5cbiAgICAgIHQub24oXCJtb3VzZW92ZXJcIiwgZnVuY3Rpb24oZCwgaSkge1xuICAgICAgICBsYXNzby5hcHBseU9iamVjdEF0dHJpYnV0ZXMoZDMuc2VsZWN0KGRbXCJfX25vZGVcIl0pLHRydWUpXG4gICAgICB9KVxuICAgICAgLm9uKFwibW91c2VvdXRcIiwgZnVuY3Rpb24oZCwgaSkge1xuICAgICAgICBsYXNzby5hcHBseU9iamVjdEF0dHJpYnV0ZXMoZDMuc2VsZWN0KGRbXCJfX25vZGVcIl0pLGZhbHNlKVxuICAgICAgfSlcbiAgICB9KVxuXG5cbiAgfVxuXG5cblxuXG5cblxuXG4gIGZ1bmN0aW9uIG1ha2VOZXdHcm91cChkLCBpKSB7XG5cbiAgICB2YXIgdGFicyA9IHNlbGVjdGlvbi5zZWxlY3QoJy4nK2h5cGVuYXRlKG5hbWVzcGFjZSwgJ3RhYi1saXN0JykpLFxuICAgIHBhbmVzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWNvbnRlbnQnKSksXG4gICAgbiA9IG5ld0dyb3VwTnVtYmVyKClcblxuICAgIGlmICh0YWJzLnNlbGVjdEFsbCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCd0YWInKSkuc2l6ZSgpID09IG1heE51bWJlck9mR3JvdXBzKSB7XG4gICAgICBvbkVycm9yKCdvbmx5ICcrbWF4TnVtYmVyT2ZHcm91cHMrJyBhbGxvd2VkLicsICd3YXJuaW5nJylcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHZhclxuICAgIHRhYiA9IGluc2VydFRhYih0YWJzLCBuKSxcbiAgICBwYW5lID0gbWFrZVRhYlBhbmUocGFuZXMsIG4pXG5cbiAgICBwb3B1bGF0ZVBhbmUocGFuZSwgbilcbiAgICB0b2dnbGVQYW5lQnlJZChwYW5lLmF0dHIoJ2lkJykpXG5cbiAgfVxuXG4gIGZ1bmN0aW9uIG5ld0dyb3VwTnVtYmVyKCkge1xuICAgIHZhciB0YWJzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWxpc3QnKSkvLyxcbiAgICB2YXJcbiAgICBuID0gdGFicy5zZWxlY3RBbGwoJ2xpJykuc2l6ZSgpIC0gMywgLy8gbWludXMgMSBmb3IgcGx1cyB0YWJcbiAgICBzID0gJ0dyb3VwICcgKyBuLFxuICAgIGdzID0gW11cbiAgICB0YWJzLmVhY2goZnVuY3Rpb24oZCwgaSl7IHJldHVybiBncy5wdXNoKGQzLnNlbGVjdCh0aGlzKS5zZWxlY3QoXCJhXCIpLnRleHQoKSkgfSlcbiAgICB3aGlsZSAoZ3MuaW5jbHVkZXMocykpIHsgbis9MTsgcyA9ICdHcm91cCAnICsgbjsgfVxuICAgIHJldHVybiBuXG4gIH1cblxuICBmdW5jdGlvbiB1cGRhdGVUYWJBbmRQYW5lTnVtYmVycygpIHtcbiAgICB2YXJcbiAgICB0YWJzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWxpc3QnKSksXG4gICAgcGFuZXMgPSBzZWxlY3Rpb24uc2VsZWN0KCcuJytoeXBlbmF0ZShuYW1lc3BhY2UsICd0YWItY29udGVudCcpKVxuXG4gICAgdGFicyA9IHRhYnMuc2VsZWN0QWxsKCcuJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicpKVxuICAgIHBhbmVzID0gcGFuZXMuc2VsZWN0QWxsKCcuJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicsICdwYW5lJykpXG5cbiAgICB0YWJzLmVhY2goZnVuY3Rpb24oZCwgaSl7XG4gICAgICBkMy5zZWxlY3QodGhpcykuZGF0dW0oaSlcbiAgICAgIC5hdHRyKFwiaWRcIiwgaHlwZW5hdGUobmFtZXNwYWNlLCd0YWInLGkpKVxuICAgICAgLnNlbGVjdCgnYScpXG4gICAgICAuYXR0cignaHJlZicsICcjJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicsJ3BhbmUnLGkpKVxuICAgICAgLnRleHQoZnVuY3Rpb24oZGQsIGlpKXtcbiAgICAgICAgdmFyIGN1clRleHQgPSBkMy5zZWxlY3QodGhpcykudGV4dCgpO1xuICAgICAgICBpZiAoY3VyVGV4dC5zcGxpdCgnICcpWzBdID09ICdHcm91cCcpIHtcbiAgICAgICAgICAgIHJldHVybiAnR3JvdXAgJyArIGlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gY3VyVGV4dFxuICAgICAgfSlcbiAgICB9KVxuXG4gICAgcGFuZXMuZWFjaChmdW5jdGlvbihkLCBpKXtcbiAgICAgIGQzLnNlbGVjdCh0aGlzKS5kYXR1bShpKVxuICAgICAgLmF0dHIoJ2lkJywgaHlwZW5hdGUobmFtZXNwYWNlLCd0YWInLCdwYW5lJyxpKSlcbiAgICAgIHNhZmVTZWxlY3QoZDMuc2VsZWN0KHRoaXMpLCAncCcsICdsZWFkJylcbiAgICAgIC50ZXh0KGZ1bmN0aW9uKGRkLCBpaSl7XG4gICAgICAgIHZhciBjdXJUZXh0ID0gZDMuc2VsZWN0KHRoaXMpLnRleHQoKTtcbiAgICAgICAgaWYgKGN1clRleHQuc3BsaXQoJyAnKVswXSA9PSAnR3JvdXAnKSB7XG4gICAgICAgICAgICByZXR1cm4gJ0dyb3VwICcgKyBpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGN1clRleHRcbiAgICAgIH0pXG4gICAgICBzYWZlU2VsZWN0KGQzLnNlbGVjdCh0aGlzKSwgJ2J1dHRvbicsICdyZW1vdmUtYnRuJylcbiAgICAgIC5vbignY2xpY2snLCByZW1vdmVCdG5DbGlja1RvUmVtb3ZlKVxuICAgIH0pXG5cbiAgfVxuXG4gIGZ1bmN0aW9uIGdhdGhlckRhdGFMaXN0cygpIHtcbiAgICB2YXIgdGFicyA9IHNlbGVjdGlvbi5zZWxlY3QoJy4nK2h5cGVuYXRlKG5hbWVzcGFjZSwgJ3RhYi1saXN0JykpXG4gICAgdmFyIHBhbmVzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWNvbnRlbnQnKSlcbiAgICB2YXIgdGFibGVzID0gcGFuZXMuc2VsZWN0QWxsKCcuJytoeXBlbmF0ZShuYW1lc3BhY2UsIFwiZGF0YS10YWJsZVwiKSlcbiAgICB2YXIgZGF0YSA9IHt9XG5cbiAgICB2YXIgdGV4dEdyb3VwcyA9IHRhYnMuc2VsZWN0QWxsKCdsaS4nK2h5cGVuYXRlKG5hbWVzcGFjZSwndGFiJykgKyAnID4gYScpXG4gICAgLm5vZGVzKCkubWFwKGZ1bmN0aW9uKGQsIGkpe3JldHVybiBkMy5zZWxlY3QoZCkudGV4dCgpfSlcblxuICAgIHRleHRHcm91cHMubWFwKGZ1bmN0aW9uKGUsIGkpe1xuICAgICAgZGF0YVtlXSA9IFtdXG4gICAgfSlcblxuXG4gICAgdGFibGVzLmVhY2goZnVuY3Rpb24oZCwgaSl7XG4gICAgICB2YXIgdGFibGUgPSBkMy5zZWxlY3QodGhpcykuc2VsZWN0KFwidGJvZHlcIilcbiAgICAgIGRhdGFbdGV4dEdyb3Vwc1tpXV0gPSB0YWJsZS5zZWxlY3RBbGwoJ3RyJykuZGF0YSgpXG4gICAgfSlcblxuICAgIHJldHVybiBkYXRhXG4gIH1cblxuICBmdW5jdGlvbiBzaG93UmVtYWluaW5nUGFuZXMoKSB7XG4gICAgdmFyXG4gICAgdGFicyA9IHNlbGVjdGlvbi5zZWxlY3QoJy4nK2h5cGVuYXRlKG5hbWVzcGFjZSwgJ3RhYi1saXN0JykpLFxuICAgIHBhbmVzID0gc2VsZWN0aW9uLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCAndGFiLWNvbnRlbnQnKSksXG4gICAgcmVtYWluaW5nVGFicyA9IHRhYnMuc2VsZWN0QWxsKCcuJytoeXBlbmF0ZShuYW1lc3BhY2UsJ3RhYicpKVxuXG4gICAgaWYgKHJlbWFpbmluZ1RhYnMuc2l6ZSgpID09IDApIHtcbiAgICAgIHBhbmVzLnNlbGVjdCgnLicraHlwZW5hdGUobmFtZXNwYWNlLCdkZWZhdWx0LXRhYicpKVxuICAgICAgLmNsYXNzZWQoXCJhY3RpdmVcIiwgdHJ1ZSlcbiAgICAgIC5jbGFzc2VkKCd0ZXh0LWxlZnQnLCB0cnVlKVxuICAgIH1cbiAgICBlbHNlIHtcbiAgICAgIHZhciBsYXN0VGFiID0gcmVtYWluaW5nVGFicy5ub2RlcygpW3JlbWFpbmluZ1RhYnMuc2l6ZSgpLTFdLFxuICAgICAgbGFzdFBhbmVJZCA9IGQzLnNlbGVjdChsYXN0VGFiKS5zZWxlY3QoJ2EnKS5hdHRyKCdocmVmJylcbiAgICAgIHBhbmVzLnNlbGVjdChsYXN0UGFuZUlkKVxuICAgICAgLmNsYXNzZWQoXCJhY3RpdmVcIiwgdHJ1ZSlcbiAgICAgIC5jbGFzc2VkKCd0ZXh0LWxlZnQnLCB0cnVlKVxuICAgIH1cbiAgfVxuXG5cbiAgcmV0dXJuIGxhc3NvV2lkZ2V0XG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuaW1wb3J0IHtzZXR1cENvbnRhaW5lciwgY2FsY3VsYXRlV2lkdGhPZk9iamVjdCwgY2FsY3VsYXRlV2lkdGhPZlNwYWNlciwgbG9nfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7dW5pcXVlLCBmbGF0dGVufSBmcm9tICcuL2FycmF5LWZ1bmN0aW9ucyc7XG5pbXBvcnQge2dyb3VwaW5nU3BhY2VyfSBmcm9tICcuL2dyb3VwaW5nLXNwYWNlcic7XG5pbXBvcnQge2NvbG9yRnVuY3Rpb24gYXMgQ0Z9IGZyb20gJy4vY29sb3ItZnVuY3Rpb24nO1xuaW1wb3J0IHt0b29sdGlwIGFzIFRUaXB9IGZyb20gJy4vdG9vbHRpcCc7XG5leHBvcnQgZnVuY3Rpb24gdXBzZXQgKCBzZWxlY3Rpb24gKSB7XG4gIHZhclxuICBkYXRhLFxuICBvcmllbnQ9J2hvcml6b250YWwnLFxuICBzcGFjZVgsXG4gIHNwYWNlWSxcbiAgb3ZlcmZsb3dRPWZhbHNlLFxuICBtaW5PYmplY3RTaXplPTIwLFxuICBtYXhPYmplY3RTaXplPTUwLFxuICBjaXJjbGVTdHJva2VXaWR0aD0yLFxuICAvLyBjb2xvckZ1bmN0aW9uPVxuICBiYWNrZ3JvdW5kRmlsbCA9ICd0cmFuc3BhcmVudCcsXG4gIG5hbWVzcGFjZT0nZDNzbS11cHNldCcsXG4gIG9iamVjdENsYXNzID0gJ3Vwc2V0JyxcblxuICB0cmFuc2l0aW9uRHVyYXRpb24gPSAxMDAwLFxuICBlYXNlRnVuYyA9IGQzLmVhc2VFeHAsXG5cbiAgc2V0S2V5ID0gXCJzZXRcIixcbiAgaW50ZXJzZWN0aW9uS2V5ID0gXCJpbnRlcnNlY3Rpb25cIixcbiAgZWxlbWVudHNLZXkgPSBcImVsZW1lbnRzXCIsXG5cbiAgc2V0RXh0cmFjdG9yID0gZnVuY3Rpb24oa2V5LCBpKSB7cmV0dXJuIGRhdGFba2V5XVtzZXRLZXldfSxcbiAgaW50ZXJzZWN0aW9uRXh0cmFjdG9yID0gZnVuY3Rpb24oa2V5LCBpKSB7cmV0dXJuIGRhdGFba2V5XVtpbnRlcnNlY3Rpb25LZXldfSxcbiAgZWxlbWVudEV4dHJhY3RvciA9IGZ1bmN0aW9uKGtleSwgaSkge3JldHVybiBkYXRhW2tleV1bZWxlbWVudHNLZXldfSxcblxuICBjZWxsS2V5cyxcbiAgc2V0VmFsdWVzLFxuICBpbnRlcnNlY3Rpb25WYWx1ZXMsXG4gIGVsZW1lbnRWYWx1ZXMsXG5cbiAgeE9iamVjdFNwYWNlciA9IDAuMDUsXG4gIHlPYmplY3RTcGFjZXIgPSAwLjA1LFxuICByYWRpdXMsXG5cbiAgLy8gbGlzdERlbGltID0gJzsnXG5cbiAgeU9iamVjdFNpemUsXG4gIHlTcGFjZXJTaXplLFxuICB4T2JqZWN0U2l6ZSxcbiAgeFNwYWNlclNpemUsXG5cbiAgc2V0S2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4gc2V0VmFsdWVzLmluZGV4T2Yoc2V0RXh0cmFjdG9yKGEpKSAtIHNldFZhbHVlcy5pbmRleE9mKHNldEV4dHJhY3RvcihiKSkgfSxcbiAgaW50ZXJzZWN0aW9uS2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oYSwgYikgeyByZXR1cm4gaW50ZXJzZWN0aW9uVmFsdWVzLmluZGV4T2YoaW50ZXJzZWN0aW9uRXh0cmFjdG9yKGEpKSAtIGludGVyc2VjdGlvblZhbHVlcy5pbmRleE9mKGludGVyc2VjdGlvbkV4dHJhY3RvcihiKSkgfVxuXG5cbiAgdXBzZXQuc2VsZWN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZWxlY3Rpb24gPSBfLCB1cHNldCkgOiBzZWxlY3Rpb247IH07XG4gIHVwc2V0LmRhdGEgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGRhdGEgPSBfLCB1cHNldCkgOiBkYXRhOyB9O1xuICB1cHNldC5vcmllbnQgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG9yaWVudCA9IF8sIHVwc2V0KSA6IG9yaWVudDsgfTtcbiAgdXBzZXQuc3BhY2VYID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzcGFjZVggPSBfLCB1cHNldCkgOiBzcGFjZVg7IH07XG4gIHVwc2V0LnNwYWNlWSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoc3BhY2VZID0gXywgdXBzZXQpIDogc3BhY2VZOyB9O1xuICB1cHNldC5vdmVyZmxvd1EgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG92ZXJmbG93USA9IF8sIHVwc2V0KSA6IG92ZXJmbG93UTsgfTtcbiAgdXBzZXQubWluT2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobWluT2JqZWN0U2l6ZSA9IF8sIHVwc2V0KSA6IG1pbk9iamVjdFNpemU7IH07XG4gIHVwc2V0Lm1heE9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKG1heE9iamVjdFNpemUgPSBfLCB1cHNldCkgOiBtYXhPYmplY3RTaXplOyB9O1xuICB1cHNldC5jaXJjbGVTdHJva2VXaWR0aCA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY2lyY2xlU3Ryb2tlV2lkdGggPSBfLCB1cHNldCkgOiBjaXJjbGVTdHJva2VXaWR0aDsgfTtcbiAgdXBzZXQuYmFja2dyb3VuZEZpbGwgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGJhY2tncm91bmRGaWxsID0gXywgdXBzZXQpIDogYmFja2dyb3VuZEZpbGw7IH07XG4gIHVwc2V0Lm5hbWVzcGFjZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAobmFtZXNwYWNlID0gXywgdXBzZXQpIDogbmFtZXNwYWNlOyB9O1xuICB1cHNldC5vYmplY3RDbGFzcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAob2JqZWN0Q2xhc3MgPSBfLCB1cHNldCkgOiBvYmplY3RDbGFzczsgfTtcbiAgdXBzZXQudHJhbnNpdGlvbkR1cmF0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh0cmFuc2l0aW9uRHVyYXRpb24gPSBfLCB1cHNldCkgOiB0cmFuc2l0aW9uRHVyYXRpb247IH07XG4gIHVwc2V0LmVhc2VGdW5jID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChlYXNlRnVuYyA9IF8sIHVwc2V0KSA6IGVhc2VGdW5jOyB9O1xuICB1cHNldC5jZWxsS2V5cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoY2VsbEtleXMgPSBfLCB1cHNldCkgOiBjZWxsS2V5czsgfTtcbiAgdXBzZXQuc2V0VmFsdWVzID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChzZXRWYWx1ZXMgPSBfLCB1cHNldCkgOiBzZXRWYWx1ZXM7IH07XG4gIHVwc2V0LmludGVyc2VjdGlvblZhbHVlcyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoaW50ZXJzZWN0aW9uVmFsdWVzID0gXywgdXBzZXQpIDogaW50ZXJzZWN0aW9uVmFsdWVzOyB9O1xuICB1cHNldC54T2JqZWN0U3BhY2VyID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh4T2JqZWN0U3BhY2VyID0gXywgdXBzZXQpIDogeE9iamVjdFNwYWNlcjsgfTtcbiAgdXBzZXQueU9iamVjdFNwYWNlciA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeU9iamVjdFNwYWNlciA9IF8sIHVwc2V0KSA6IHlPYmplY3RTcGFjZXI7IH07XG4gIHVwc2V0LnJhZGl1cyA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAocmFkaXVzID0gXywgdXBzZXQpIDogcmFkaXVzOyB9O1xuICB1cHNldC5zZXRFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNldEV4dHJhY3RvciA9IF8sIHVwc2V0KSA6IHNldEV4dHJhY3RvcjsgfTtcbiAgdXBzZXQuaW50ZXJzZWN0aW9uRXh0cmFjdG9yID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChpbnRlcnNlY3Rpb25FeHRyYWN0b3IgPSBfLCB1cHNldCkgOiBpbnRlcnNlY3Rpb25FeHRyYWN0b3I7IH07XG4gIHVwc2V0LmVsZW1lbnRFeHRyYWN0b3IgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGVsZW1lbnRFeHRyYWN0b3IgPSBfLCB1cHNldCkgOiBlbGVtZW50RXh0cmFjdG9yOyB9O1xuICB1cHNldC5zZXRLZXlTb3J0aW5nRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHNldEtleVNvcnRpbmdGdW5jdGlvbiA9IF8sIHVwc2V0KSA6IHNldEtleVNvcnRpbmdGdW5jdGlvbjsgfTtcbiAgdXBzZXQuaW50ZXJzZWN0aW9uS2V5U29ydGluZ0Z1bmN0aW9uID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChpbnRlcnNlY3Rpb25LZXlTb3J0aW5nRnVuY3Rpb24gPSBfLCB1cHNldCkgOiBpbnRlcnNlY3Rpb25LZXlTb3J0aW5nRnVuY3Rpb247IH07XG5cbiAgdXBzZXQueU9iamVjdFNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHlPYmplY3RTaXplID0gXywgdXBzZXQpIDogeU9iamVjdFNpemU7IH07XG4gIHVwc2V0LnlTcGFjZXJTaXplID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/ICh5U3BhY2VyU2l6ZSA9IF8sIHVwc2V0KSA6IHlTcGFjZXJTaXplOyB9O1xuICB1cHNldC54T2JqZWN0U2l6ZSA9IGZ1bmN0aW9uKF8pIHsgcmV0dXJuIGFyZ3VtZW50cy5sZW5ndGggPyAoeE9iamVjdFNpemUgPSBfLCB1cHNldCkgOiB4T2JqZWN0U2l6ZTsgfTtcbiAgdXBzZXQueFNwYWNlclNpemUgPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKHhTcGFjZXJTaXplID0gXywgdXBzZXQpIDogeFNwYWNlclNpemU7IH07XG5cbiAgZnVuY3Rpb24gdXBzZXQoKSB7XG4gICAgLy8gZm9yIGNvbnZlbmllbmNlIGluIGhhbmRsaW5nIG9yaWVudGF0aW9uIHNwZWNpZmljIHZhbHVlc1xuICAgIHZhciBob3Jpem9udGFsUSA9IChvcmllbnQgPT0gJ2hvcml6b250YWwnKSA/IHRydWUgOiBmYWxzZVxuICAgIHZhciB2ZXJ0aWNhbFEgPSAhaG9yaXpvbnRhbFFcblxuICAgIC8vIGJhY2tncm91bmQgY2xpcGluZyByZWN0YW5nbGVcbiAgICB2YXIgYmdjcFJlY3QgPSB7eDowLCB5OjAsIHdpZHRoOiBzcGFjZVgsIGhlaWdodDpzcGFjZVl9XG4gICAgdmFyIGNvbnRhaW5lciA9IHNldHVwQ29udGFpbmVyKCBzZWxlY3Rpb24sIG5hbWVzcGFjZSwgYmdjcFJlY3QsIGJhY2tncm91bmRGaWxsICk7XG5cblxuICAgIGNlbGxLZXlzID0gZDMua2V5cyhkYXRhKVxuICAgIHNldFZhbHVlcyA9IHVuaXF1ZShjZWxsS2V5cy5tYXAoc2V0RXh0cmFjdG9yKSkuc29ydCgpXG4gICAgaW50ZXJzZWN0aW9uVmFsdWVzID0gdW5pcXVlKGNlbGxLZXlzLm1hcChpbnRlcnNlY3Rpb25FeHRyYWN0b3IpKS5zb3J0KCkuc29ydChmdW5jdGlvbihhLCBiKXtcbiAgICAgIHJldHVybiBhLnNwbGl0KCc7JykubGVuZ3RoIC0gYi5zcGxpdCgnOycpLmxlbmd0aFxuICAgIH0pXG5cbiAgICBpZiAoIWhvcml6b250YWxRKSB7XG4gICAgICBjZWxsS2V5cy5zb3J0KGZ1bmN0aW9uKGEsIGIpeyByZXR1cm4gc2V0S2V5U29ydGluZ0Z1bmN0aW9uKGEsIGIpIHx8IGludGVyc2VjdGlvbktleVNvcnRpbmdGdW5jdGlvbihhLCBiKSB9KVxuICAgIH0gZWxzZSB7XG4gICAgICBjZWxsS2V5cy5zb3J0KGZ1bmN0aW9uKGEsIGIpeyByZXR1cm4gaW50ZXJzZWN0aW9uS2V5U29ydGluZ0Z1bmN0aW9uKGEsIGIpIHx8IHNldEtleVNvcnRpbmdGdW5jdGlvbihhLCBiKSB9KVxuICAgIH1cblxuXG5cblxuICAgIHZhclxuICAgIHhWYWx1ZXMgPSBob3Jpem9udGFsUSA/IGludGVyc2VjdGlvblZhbHVlcyA6IHNldFZhbHVlcyxcbiAgICB5VmFsdWVzID0gaG9yaXpvbnRhbFEgPyBzZXRWYWx1ZXMgOiBpbnRlcnNlY3Rpb25WYWx1ZXMsXG4gICAgeERpbSA9IGhvcml6b250YWxRID8geFZhbHVlcy5sZW5ndGggOiB5VmFsdWVzLmxlbmd0aCxcbiAgICB5RGltID0gaG9yaXpvbnRhbFEgPyB5VmFsdWVzLmxlbmd0aCA6IHhWYWx1ZXMubGVuZ3RoXG5cbiAgICAvLyBjb25zb2xlLmxvZyh4VmFsdWVzLCB5VmFsdWVzKVxuXG5cbiAgICB4T2JqZWN0U2l6ZSA9IGNhbGN1bGF0ZVdpZHRoT2ZPYmplY3Qoc3BhY2VYLCB4RGltLCBtaW5PYmplY3RTaXplLCBtYXhPYmplY3RTaXplLCB4T2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgeU9iamVjdFNpemUgPSBjYWxjdWxhdGVXaWR0aE9mT2JqZWN0KHNwYWNlWSwgeURpbSwgbWluT2JqZWN0U2l6ZSwgbWF4T2JqZWN0U2l6ZSwgeU9iamVjdFNwYWNlciwgb3ZlcmZsb3dRKVxuICAgIHhTcGFjZXJTaXplID0gY2FsY3VsYXRlV2lkdGhPZlNwYWNlcih4VmFsdWVzLCBzcGFjZVgsIHhPYmplY3RTaXplLCB4RGltLCB4T2JqZWN0U3BhY2VyLCBvdmVyZmxvd1EpXG4gICAgeVNwYWNlclNpemUgPSBjYWxjdWxhdGVXaWR0aE9mU3BhY2VyKHlWYWx1ZXMsIHNwYWNlWSwgeU9iamVjdFNpemUsIHlEaW0sIHlPYmplY3RTcGFjZXIsIG92ZXJmbG93USlcblxuICAgIHZhciB5U3BhY2VyID0gZ3JvdXBpbmdTcGFjZXIoKVxuICAgIC5ob3Jpem9udGFsUShmYWxzZSlcbiAgICAubW92ZWJ5KCdjYXRlZ29yeScpLm51bWJlck9mT2JqZWN0cyh5RGltKVxuICAgIC5vYmplY3RTaXplKHlPYmplY3RTaXplKS5zcGFjZXJTaXplKHlTcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcblxuICAgIHZhciB4U3BhY2VyID0gZ3JvdXBpbmdTcGFjZXIoKVxuICAgIC5ob3Jpem9udGFsUSh0cnVlKVxuICAgIC5tb3ZlYnkoJ2NhdGVnb3J5JykubnVtYmVyT2ZPYmplY3RzKHhEaW0pXG4gICAgLm9iamVjdENsYXNzKG9iamVjdENsYXNzKVxuICAgIC5vYmplY3RTaXplKHhPYmplY3RTaXplKS5zcGFjZXJTaXplKHhTcGFjZXJTaXplKVxuICAgIC50cmFuc2l0aW9uRHVyYXRpb24odHJhbnNpdGlvbkR1cmF0aW9uKS5lYXNlRnVuYyhlYXNlRnVuYylcblxuXG5cbiAgICBpZiAodmVydGljYWxRKSB7XG4gICAgICB4U3BhY2VyLm9iamVjdENsYXNzKG9iamVjdENsYXNzKVxuICAgICAgeVNwYWNlci5uYW1lc3BhY2UoJ2Fjcm9zcycpLm9iamVjdENsYXNzKGh5cGVuYXRlKG9iamVjdENsYXNzLCAnYWNyb3NzJykpXG5cbiAgICAgIHlTcGFjZXIoY29udGFpbmVyLCB5VmFsdWVzLCAwKVxuICAgICAgY29udGFpbmVyLnNlbGVjdEFsbCgnZy4nK2h5cGVuYXRlKG9iamVjdENsYXNzLCAnYWNyb3NzJykpXG4gICAgICAuZWFjaChmdW5jdGlvbihkLCBpKXsgeFNwYWNlcihkMy5zZWxlY3QodGhpcyksIHhWYWx1ZXMsIDApIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHhTcGFjZXIubmFtZXNwYWNlKCdhY3Jvc3MnKS5vYmplY3RDbGFzcyhoeXBlbmF0ZShvYmplY3RDbGFzcywgJ2Fjcm9zcycpKVxuICAgICAgeVNwYWNlci5vYmplY3RDbGFzcyhvYmplY3RDbGFzcylcblxuICAgICAgeFNwYWNlcihjb250YWluZXIsIHhWYWx1ZXMsIDApXG4gICAgICBjb250YWluZXIuc2VsZWN0QWxsKCdnLicraHlwZW5hdGUob2JqZWN0Q2xhc3MsICdhY3Jvc3MnKSlcbiAgICAgIC5lYWNoKGZ1bmN0aW9uKGQsIGkpeyB5U3BhY2VyKGQzLnNlbGVjdCh0aGlzKSwgeVZhbHVlcywgMCkgfSlcbiAgICB9XG5cblxuICAgIHZhciBjZWxscyA9IGNvbnRhaW5lci5zZWxlY3RBbGwoJ2c6bm90KC50by1yZW1vdmUpLicrb2JqZWN0Q2xhc3MpXG4gICAgdmFyIGxvb2t1cCA9IHt9XG4gICAgY2VsbEtleXMubWFwKGZ1bmN0aW9uKGssIGkpe1xuICAgICAgbG9va3VwW3NldEV4dHJhY3RvcihrKSsnOjonK2ludGVyc2VjdGlvbkV4dHJhY3RvcihrKV0gPSBrXG4gICAgfSlcblxuICAgIC8vIHZhciBwb3NpdGlvbmVkQ2VsbEtleXMgPSBbXVxuICAgIC8vIGZvciAodmFyIGkgPSAwOyBpIDwgc2V0VmFsdWVzLmxlbmd0aDsgaSsrKSB7XG4gICAgLy8gICBmb3IgKHZhciBqID0gMDsgaiA8IGludGVyc2VjdGlvblZhbHVlcy5sZW5ndGg7IGorKykge1xuICAgIC8vICAgICB2YXIgbG9va3VwVmFsdWUgPSBsb29rdXBbc2V0VmFsdWVzW2pdK1wiOjpcIitpbnRlcnNlY3Rpb25WYWx1ZXNbaV1dXG4gICAgLy8gICAgIGlmIChsb29rdXBWYWx1ZSA9PSB1bmRlZmluZWQpIHtcbiAgICAvLyAgICAgICBwb3NpdGlvbmVkQ2VsbEtleXMucHVzaCh1bmRlZmluZWQpXG4gICAgLy8gICAgIH0gZWxzZSB7XG4gICAgLy8gICAgICAgcG9zaXRpb25lZENlbGxLZXlzLnB1c2gobG9va3VwVmFsdWUpXG4gICAgLy8gICAgIH1cbiAgICAvLyAgICAgY29uc29sZS5sb2coaSwgaiwgbG9va3VwVmFsdWUpXG4gICAgLy8gICB9XG4gICAgLy8gfVxuICAgIC8vXG4gICAgLy8gLy8gY29uc29sZS5sb2cocG9zaXRpb25lZENlbGxLZXlzKVxuICAgIC8vXG4gICAgLy8gY2VsbHMuZGF0YShwb3NpdGlvbmVkQ2VsbEtleXMpO1xuXG5cbiAgICBjZWxscy5kYXRhKGNlbGxLZXlzKTtcblxuICAgIGNlbGxzLmVhY2goZnVuY3Rpb24oa2V5LCBpKSB7XG4gICAgICB2YXIgdCA9IGQzLnNlbGVjdCh0aGlzKVxuICAgICAgaWYgKGtleSA9PSB1bmRlZmluZWQpIHtyZXR1cm4gfVxuICAgICAgdmFyXG4gICAgICBjdXJyZW50RGF0YSA9IGRhdGFba2V5XVxuICAgICAgLy8gY29uc29sZS5sb2coa2V5LCBjdXJyZW50RGF0YSlcbiAgICAgIHZhclxuICAgICAgc2V0ID0gc2V0RXh0cmFjdG9yKGtleSwgaSksXG4gICAgICBpbnRlcnNlY3Rpb24gPSBpbnRlcnNlY3Rpb25FeHRyYWN0b3Ioa2V5LCBpKVxuXG4gICAgICAvLyBjb25zb2xlLmxvZyhzZXQsIGludGVyc2VjdGlvbilcblxuICAgICAgdC5jbGFzc2VkKGludGVyc2VjdGlvbiwgdHJ1ZSlcbiAgICAgIHQuY2xhc3NlZChzZXQsIHRydWUpXG5cbiAgICAgIHZhciBjID0gc2FmZVNlbGVjdCh0LCAnY2lyY2xlJywgaHlwZW5hdGUob2JqZWN0Q2xhc3MsJ2NpcmNsZScpKVxuICAgICAgYy5hdHRyKCdjeCcsIHhPYmplY3RTaXplIC8gMilcbiAgICAgIC5hdHRyKCdjeScsIHlPYmplY3RTaXplIC8gMiApXG4gICAgICAuYXR0cigncicsIHJhZGl1cyA9PSB1bmRlZmluZWQgPyBNYXRoLm1pbih4T2JqZWN0U2l6ZSwgeU9iamVjdFNpemUpIC8gMiA6IHJhZGl1cylcbiAgICAgIC5hdHRyKCdmaWxsJywgaW50ZXJzZWN0aW9uLmluY2x1ZGVzKHNldCkgPyBcImJsYWNrXCI6ICdyZ2IoMjMzLDIzMywyMzMpJylcbiAgICAgIC5hdHRyKCdzdHJva2UnLCBcImJsYWNrXCIpXG4gICAgICAuYXR0cihcImluLWludGVyc2VjdGlvblwiLCBpbnRlcnNlY3Rpb24uaW5jbHVkZXMoc2V0KSlcbiAgICB9KVxuXG5cblxuICB9XG5cbiAgZnVuY3Rpb24gaW50ZXJzZWN0aW9uVG90YWxzKCkge1xuICAgIHZhciB0b3RhbHMgPSB7fVxuICAgIC8vIGludGVyc2VjdGlvblZhbHVlcy5zb3J0KGZ1bmN0aW9uKGEsIGIpeyByZXR1cm4gaW50ZXJzZWN0aW9uS2V5U29ydGluZ0Z1bmN0aW9uKGEsIGIpIH0pXG5cbiAgICBpbnRlcnNlY3Rpb25WYWx1ZXMubWFwKGZ1bmN0aW9uKGssIGkpeyB0b3RhbHNba10gPSB7J3RvdGFsJzogMH0gfSlcbiAgICBjZWxsS2V5cy5tYXAoZnVuY3Rpb24oaywgaSl7XG4gICAgICB2YXIgZSA9IGVsZW1lbnRFeHRyYWN0b3IoaywgaSk7XG4gICAgICBpZiAodG90YWxzW2ludGVyc2VjdGlvbkV4dHJhY3RvcihrLCBpKV1bJ3RvdGFsJ10gPT0gMCkge1xuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShlKSkge1xuICAgICAgICAgIHRvdGFsc1tpbnRlcnNlY3Rpb25FeHRyYWN0b3IoaywgaSldWyd0b3RhbCddKz0gZS5sZW5ndGhcbiAgICAgICAgICB0b3RhbHNbaW50ZXJzZWN0aW9uRXh0cmFjdG9yKGssIGkpXVsndmFsdWVzJ10gPSBlXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdG90YWxzW2ludGVyc2VjdGlvbkV4dHJhY3RvcihrLCBpKV1bJ3RvdGFsJ10rPSBlXG4gICAgICAgIH1cblxuICAgICAgfVxuICAgIH0pXG4gICAgcmV0dXJuIHRvdGFsc1xuICB9XG5cbiAgZnVuY3Rpb24gc2V0VG90YWxzKCl7XG4gICAgdmFyIHRvdGFscyA9IHt9XG4gICAgLy8gaW50ZXJzZWN0aW9uVmFsdWVzLnNvcnQoZnVuY3Rpb24oYSwgYil7IHJldHVybiBpbnRlcnNlY3Rpb25LZXlTb3J0aW5nRnVuY3Rpb24oYSwgYikgfSlcblxuICAgIHNldFZhbHVlcy5tYXAoZnVuY3Rpb24oaywgaSl7IHRvdGFsc1trXSA9IHsndG90YWwnOiAwfSB9KVxuXG4gICAgY2VsbEtleXMubWFwKGZ1bmN0aW9uKGssIGkpe1xuICAgICAgdmFyIGUgPSBlbGVtZW50RXh0cmFjdG9yKGssIGkpO1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkoZSkpIHtcbiAgICAgICAgdG90YWxzW3NldEV4dHJhY3RvcihrLCBpKV1bJ3RvdGFsJ10rPSBlLmxlbmd0aFxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdG90YWxzW3NldEV4dHJhY3RvcihrLCBpKV1bJ3RvdGFsJ10rPSBlXG4gICAgICB9XG4gICAgfSlcbiAgICByZXR1cm4gdG90YWxzXG4gIH1cblxuICB1cHNldC5pbnRlcnNlY3Rpb25Ub3RhbHMgPSBpbnRlcnNlY3Rpb25Ub3RhbHNcbiAgdXBzZXQuc2V0VG90YWxzID0gc2V0VG90YWxzXG5cbiAgcmV0dXJuIHVwc2V0XG59XG4iLCJpbXBvcnQge2h5cGVuYXRlLCBzYWZlU2VsZWN0fSBmcm9tICcuL2hlbHBlcnMnO1xuZXhwb3J0IGZ1bmN0aW9uIGZpbHRlclRhYmxlKHNlbGVjdGlvbikge1xuICB2YXJcbiAgZGF0YSxcbiAgbmFtZXNwYWNlID0gJ2Qzc20tZmlsdGVyLXRhYmxlJyxcbiAgc29ydFEgPSBmYWxzZSxcbiAgZmllbGRGdW5jdGlvbiA9IGZ1bmN0aW9uKHJlY29yZCwgY29sdW1uS2V5LCByZWNvcmRWYWx1ZSkge3JldHVybiByZWNvcmRWYWx1ZX1cblxuICBmaWx0ZXJUYWJsZS5kYXRhID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChkYXRhID0gXywgZmlsdGVyVGFibGUpIDogZGF0YX1cbiAgZmlsdGVyVGFibGUubmFtZXNwYWNlID0gZnVuY3Rpb24oXykgeyByZXR1cm4gYXJndW1lbnRzLmxlbmd0aCA/IChuYW1lc3BhY2UgPSBfLCBmaWx0ZXJUYWJsZSkgOiBuYW1lc3BhY2V9XG4gIGZpbHRlclRhYmxlLmZpZWxkRnVuY3Rpb24gPSBmdW5jdGlvbihfKSB7IHJldHVybiBhcmd1bWVudHMubGVuZ3RoID8gKGZpZWxkRnVuY3Rpb24gPSBfLCBmaWx0ZXJUYWJsZSkgOiBmaWVsZEZ1bmN0aW9ufVxuXG4gIGZ1bmN0aW9uIGZpbHRlclRhYmxlICgpIHtcblxuICAgIHZhclxuICAgIGNvbnRhaW5lciA9IHNhZmVTZWxlY3Qoc2VsZWN0aW9uLCAnZGl2JywgJ2ZpbHRlci10YWJsZScpLmNsYXNzZWQoaHlwZW5hdGUobmFtZXNwYWNlLCdjb250YWluZXInKSx0cnVlKSxcblxuICAgIGlucHV0R3JvdXAgPSBzYWZlU2VsZWN0KGNvbnRhaW5lciwgJ2RpdicsICdmaWx0ZXItaW5wdXQtZ3JvdXAnKS5jbGFzc2VkKCdpbnB1dC1ncm91cCcsdHJ1ZSksXG4gICAgICBpbnB1dFByZXBlbmQgPSBzYWZlU2VsZWN0KGlucHV0R3JvdXAsICdkaXYnLCAnaW5wdXQtZ3JvdXAtcHJlcGVuZCcpLFxuICAgICAgICBpbnB1dFByZXBlbmRTcGFuID0gc2FmZVNlbGVjdChpbnB1dFByZXBlbmQsICdzcGFuJywgJ2lucHV0LWdyb3VwLXRleHQnKS5jbGFzc2VkKCdzZWFyY2gtYnV0dG9uJywgdHJ1ZSksXG4gICAgICAgICAgaW5wdXRQcmVwZW5kU3Bhbkljb24gPSBzYWZlU2VsZWN0KGlucHV0UHJlcGVuZFNwYW4sJ2knLCdmYSBmYS1zZWFyY2gnKSxcblxuICAgICAgaW5wdXQgPSBzYWZlU2VsZWN0KGlucHV0R3JvdXAsICdpbnB1dCcsICdmb3JtLWNvbnRyb2wnKS5hdHRyKCdwbGFjZWhvbGRlcicsICdmaWx0ZXInKS5hdHRyKCd0eXBlJywgJ3RleHQnKSxcbiAgICAgIGlucHV0QXBwZW5kID0gc2FmZVNlbGVjdChpbnB1dEdyb3VwLCAnZGl2JywgJ2lucHV0LWdyb3VwLWFwcGVuZCcpLFxuICAgICAgICBpbnB1dEFwcGVuZEJ1dHRvbiA9IHNhZmVTZWxlY3QoaW5wdXRBcHBlbmQsICdhJywgJ2Nsb3NlLWJ1dHRvbicpLmNsYXNzZWQoJ2J0biBidG4tb3V0bGluZS1zZWNvbmRhcnknLCB0cnVlKSxcbiAgICAgICAgICBpbnB1dEFwcGVuZEJ1dHRvbkljb24gPSBzYWZlU2VsZWN0KGlucHV0QXBwZW5kQnV0dG9uLCAnaScsICdmYSBmYS1jbG9zZScpLFxuXG5cbiAgICBjbG9zZUJ1dHRvbiA9IGlucHV0QXBwZW5kQnV0dG9uLFxuXG4gICAgclRhYmxlID0gc2FmZVNlbGVjdChjb250YWluZXIsICdkaXYnLCAndGFibGUtcmVzcG9uc2l2ZScpLFxuICAgICAgdGFibGUgPSBzYWZlU2VsZWN0KHJUYWJsZSwgJ3RhYmxlJywgJ3RhYmxlJylcbiAgICAgIC5jbGFzc2VkKCd0YWJsZS1ob3ZlcicsIHRydWUpXG4gICAgICAuY2xhc3NlZCgndGFibGUtYm9yZGVyZWQnLCB0cnVlKVxuICAgICAgLmNsYXNzZWQoXCJ0YWJsZS1zdHJpcGVkXCIsIHRydWUpLFxuICAgICAgICB0SGVhZCA9IHNhZmVTZWxlY3QodGFibGUsICd0aGVhZCcpXG4gICAgICAgIC5jbGFzc2VkKFwidGhlYWQtZGFya1wiLCB0cnVlKSxcbiAgICAgICAgICBoZWFkZXIgPSBzYWZlU2VsZWN0KHRIZWFkLCAndHInKSxcbiAgICAgICAgdEJvZHkgPSBzYWZlU2VsZWN0KHRhYmxlLCAndGJvZHknKVxuXG5cbiAgICB2YXJcbiAgICByb3dzID0gZDMua2V5cyhkYXRhKSxcbiAgICBjb2xzID0gZDMua2V5cyhkYXRhW3Jvd3NbMF1dKVxuXG4gICAgdmFyIHRoID0gbWFrZUNvbHVtbnMoaGVhZGVyLCBjb2xzKVxuICAgIHZhciB0ciA9IG1ha2VSb3dzKHRCb2R5LCByb3dzKVxuICAgIHZhciB0ciA9IHBvcHVsYXRlUmVjb3Jkcyh0ciwgY29scylcblxuXG5cbiAgICBpbnB1dC5vbignaW5wdXQnLCBmdW5jdGlvbihkLCBpKXtcbiAgICAgIHZhclxuICAgICAgdmFsID0gaW5wdXQucHJvcGVydHkoJ3ZhbHVlJyksXG4gICAgICByZWcgPSBuZXcgUmVnRXhwKHZhbCwgJ2dpJyksXG4gICAgICB1c2VcbiAgICAgIGlmICh2YWwgPT0gJycpIHt1c2UgPSByb3dzfSBlbHNlIHtcbiAgICAgICAgdXNlID0gW11cbiAgICAgICAgcm93cy5tYXAoZnVuY3Rpb24ociwgaSl7XG4gICAgICAgICAgdmFyIHJvdyA9IGRhdGFbcl1cbiAgICAgICAgICB2YXIgZmllbGRKb2luID0gY29scy5tYXAoZnVuY3Rpb24oYywgail7XG4gICAgICAgICAgICByZXR1cm4gZmllbGRGdW5jdGlvbihyb3csIGMsIHJvd1tjXSlcbiAgICAgICAgICAgIC8vIHJldHVybiByb3dbY11cbiAgICAgICAgICB9KS5qb2luKCcnKVxuICAgICAgICAgIHZhciBtYXRjaCA9IGZpZWxkSm9pbi5tYXRjaChyZWcpXG4gICAgICAgICAgaWYgKG1hdGNoID09IG51bGwgfHwgbWF0Y2guam9pbignJykgPT0gJycpIHt9XG4gICAgICAgICAgZWxzZSB7IHVzZS5wdXNoKHIpIH1cbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgdHIgPSBtYWtlUm93cyh0Qm9keSwgdXNlKVxuICAgICAgdHIgPSBwb3B1bGF0ZVJlY29yZHModHIsIGNvbHMpXG4gICAgfSlcblxuICAgIGNsb3NlQnV0dG9uLm9uKCdjbGljaycsIGZ1bmN0aW9uKGQsIGkpe1xuICAgICAgaW5wdXQucHJvcGVydHkoJ3ZhbHVlJywgJycpLmRpc3BhdGNoKCdpbnB1dCcpXG4gICAgfSlcblxuICB9XG5cbiAgZnVuY3Rpb24gbWFrZUNvbHVtbnMoaGVhZGVyLCBjb2xzKSB7XG4gICAgdmFyIHRoID0gaGVhZGVyLnNlbGVjdEFsbCgndGgnKVxuICAgIHRoID0gdGguZGF0YShjb2xzKVxuICAgIHRoLmV4aXQoKS5yZW1vdmUoKVxuICAgIHRoID0gdGgubWVyZ2UodGguZW50ZXIoKS5hcHBlbmQoJ3RoJykpXG4gICAgdGguYXR0cignc2NvcGUnLCAnY29sJykudGV4dChmdW5jdGlvbihkLCBpKXtyZXR1cm4gZH0pXG4gICAgcmV0dXJuIHRoXG4gIH1cblxuICBmdW5jdGlvbiBtYWtlUm93cyhib2R5LCByb3dzKSB7XG4gICAgdmFyIHRyID0gYm9keS5zZWxlY3RBbGwoJ3RyJylcbiAgICB0ciA9IHRyLmRhdGEocm93cylcbiAgICB0ci5leGl0KCkucmVtb3ZlKClcbiAgICB0ciA9IHRyLm1lcmdlKHRyLmVudGVyKCkuYXBwZW5kKCd0cicpKVxuICAgIHJldHVybiB0clxuICB9XG5cbiAgZnVuY3Rpb24gcG9wdWxhdGVSZWNvcmRzKHJvd3MsIGNvbHMpIHtcbiAgICByb3dzLmVhY2goZnVuY3Rpb24ociwgaSl7XG4gICAgICB2YXIgcmVjb3JkID0gZGF0YVtyXSxcbiAgICAgIHQgPSBkMy5zZWxlY3QodGhpcylcblxuICAgICAgdmFyIGZpZWxkcyA9IHQuc2VsZWN0QWxsKCd0ZCcpXG4gICAgICBmaWVsZHMgPSBmaWVsZHMuZGF0YShjb2xzKVxuICAgICAgZmllbGRzLmV4aXQoKS5yZW1vdmUoKVxuICAgICAgZmllbGRzID0gZmllbGRzLm1lcmdlKGZpZWxkcy5lbnRlcigpLmFwcGVuZCgndGQnKSlcblxuICAgICAgZmllbGRzLmF0dHIoJ3Njb3BlJywgZnVuY3Rpb24oZiwgail7IHJldHVybiBqID09IDAgfSlcbiAgICAgIC5odG1sKGZ1bmN0aW9uKGYsIGopeyByZXR1cm4gZmllbGRGdW5jdGlvbihyZWNvcmQsIGYsIHJlY29yZFtmXSkgfSlcblxuICAgIH0pXG4gICAgcmV0dXJuIHJvd3NcbiAgfVxuXG5cblxuICByZXR1cm4gZmlsdGVyVGFibGVcbn1cbiJdLCJuYW1lcyI6WyJ1bmlxdWVFbGVtZW50cyIsInZhbHVlIiwiaW5kZXgiLCJzZWxmIiwiaW5kZXhPZiIsImdldFRyYW5zbGF0aW9uIiwidHJhbnNmb3JtIiwiZyIsImRvY3VtZW50IiwiY3JlYXRlRWxlbWVudE5TIiwidW5kZWZpbmVkIiwic2V0QXR0cmlidXRlTlMiLCJtYXRyaXgiLCJiYXNlVmFsIiwiY29uc29saWRhdGUiLCJlIiwiZiIsIm1vZGlmeUhleGlkZWNpbWFsQ29sb3JMdW1pbmFuY2UiLCJoZXgiLCJsdW0iLCJTdHJpbmciLCJyZXBsYWNlIiwibGVuZ3RoIiwiYyIsImkiLCJyZ2IiLCJwYXJzZUludCIsInN1YnN0ciIsIk1hdGgiLCJyb3VuZCIsIm1pbiIsIm1heCIsInRvU3RyaW5nIiwicXVhcnRpbGVzIiwiZGF0YSIsInFLZXlzIiwicTIiLCJkMyIsIm1lZGlhbiIsImxvd2VyIiwiZmlsdGVyIiwieCIsInVwcGVyIiwicTEiLCJxMCIsInEzIiwicTQiLCJrMCIsImsxIiwiazIiLCJrMyIsIms0Iiwib2JqIiwiaHlwZW5hdGUiLCJBcnJheSIsInByb3RvdHlwZSIsInNsaWNlIiwiY2FsbCIsImFyZ3VtZW50cyIsImpvaW4iLCJudW1iZXIiLCJwcmVjaXNpb24iLCJzaGlmdCIsInJldmVyc2VTaGlmdCIsIm51bUFycmF5Iiwic3BsaXQiLCJnZXRDb250YWluaW5nU1ZHIiwiZWxlbWVudCIsInBhcmVudCIsInBhcmVudEVsZW1lbnQiLCJ0YWciLCJ0YWdOYW1lIiwidG9Mb3dlckNhc2UiLCJzYWZlU2VsZWN0Iiwic2VsIiwiY2xzIiwiY2xzU3RyIiwic1NlbCIsInNlbGVjdCIsImVtcHR5IiwiYXBwZW5kIiwiY2xhc3NlZCIsImF0dHIiLCJ0aWNrUmFuZ2UiLCJuIiwiYSIsInMiLCJwdXNoIiwiaGFzUSIsImFycmF5IiwiaXRlbSIsImluY2x1ZGVzIiwidW5pcXVlIiwiZmxhdHRlbiIsImZsYXQiLCJtYXAiLCJpc0FycmF5IiwiY29uY2F0Iiwid2hpY2hCaW4iLCJiaW5zIiwiaiIsImNvbnNvbGVHcm91cCIsIm5hbWUiLCJ3aW5kb3ciLCJkM3NtIiwiZGVidWdRIiwiZ3JvdXAiLCJjb25zb2xlR3JvdXBFbmQiLCJncm91cEVuZCIsImxvZyIsImZ1bmMiLCJtc2ciLCJ0YWJsZSIsInNldHVwQ29udGFpbmVyIiwic2VsZWN0aW9uIiwibmFtZXNwYWNlIiwicmVjdCIsImZpbGwiLCJjb250YWluZXIiLCJ5Iiwid2lkdGgiLCJoZWlnaHQiLCJiZ1JlY3QiLCJkZWZzIiwiY3BSZWN0IiwicmFpc2UiLCJjYWxjdWxhdGVXaWR0aE9mT2JqZWN0IiwiZnJlZVNwYWNlIiwibnVtYmVyT2ZPYmplY3RzIiwibWluT2JqZWN0V2lkdGgiLCJtYXhPYmplY3RXaWR0aCIsInNpemVPZlNwYWNlciIsIm92ZXJmbG93USIsInJlbWFpbmluZ1NwYWNlIiwib2JqZWN0V2lkdGgiLCJjYWxjdWxhdGVXaWR0aE9mU3BhY2VyIiwiYmFzZVNwYWNlclNpemUiLCJzcGFjZXJzQXRFYWNoTGV2ZWwiLCJzcGFjZXJzTmVlZGVkQXRFYWNoTGV2ZWwiLCJsZXZlbCIsImxldmVsRGF0YSIsInJlZHVjZSIsImIiLCJpc05hTiIsIndoaXNrZXJQYXRoIiwiZGlyIiwidyIsImgiLCJwZXIiLCJvIiwiaGgiLCJ3dyIsInAiLCJncm91cGluZ1NwYWNlciIsInNjYWxlTGluZWFyIiwiZWFzZVNpbiIsImN1ciIsImQiLCJob3Jpem9udGFsUSIsIm91dGVyV2lkdGgiLCJjdXJyZW50IiwiY3VycmVudE5vZGUiLCJub2RlIiwic2VsZWN0QWxsIiwidHJhbnNpdGlvbiIsImR1cmF0aW9uIiwidHJhbnNpdGlvbkR1cmF0aW9uIiwiZWFzZSIsImVhc2VGdW5jIiwicmVtb3ZlIiwicmVjdXJzaXZlbHlQb3NpdGlvbiIsImN1cnJlbnRTZWxlY3Rpb24iLCJlbnRlciIsImV4aXQiLCJtZXJnZSIsImV4aXRGdW5jdGlvbiIsImVhY2giLCJ0aGlzIiwibGV2ZWxTcGFjZXIiLCJzcGFjZXJTaXplIiwibW92ZSIsImN1cnJlbnRFbGVtZW50IiwidCIsImVudGVyRnVuY3Rpb24iLCJtb3ZlYnkiLCJzY2FsZSIsInRvUmVtb3ZlIiwib2JqZWN0Q2xhc3MiLCJvYmplY3RTaXplIiwic2l6ZSIsIl8iLCJjb2xvckZ1bmN0aW9uIiwiaW50ZXJwb2xhdGVSZ2IiLCJjb2xvcnMiLCJrIiwidiIsImNhdGVnb3J5IiwiaW50ZXJwb2xhdGUiLCJpbnRlcnBvbGF0aW9uIiwiZG9tYWluIiwiZGF0YUV4dGVudCIsInJhbmdlIiwiaGVscGVyU2NhbGUiLCJtYXRjaCIsImtleSIsInR5cGUiLCJob3ZlclEiLCJvcGFjIiwiZmlsbE9wYWNpdHkiLCJzdHJva2VPcGFjaXR5IiwiY29sb3JCeSIsImNhdGVnb3JpZXMiLCJtb2RpZnlPcGFjaXR5IiwidmFsdWVFeHRyYWN0b3IiLCJjYXQiLCJjYXRlZ29yeUV4dHJhY3RvciIsInRvb2x0aXAiLCJrZXlzIiwidmFsdWVzIiwiaGVhZGVyIiwib24iLCJtb3VzZW1vdmUiLCJjdXJyZW50RGF0YSIsIm1vdXNlIiwiZGl2Iiwic3R5bGUiLCJjYXJkQm9keSIsInRCb2R5IiwidGV4dCIsInRyIiwicm93S2V5Iiwicm93SW5kZXgiLCJiYm94IiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwiaW5uZXJXaWR0aCIsInNjcm9sbFgiLCJldmVudCIsInBhZ2VYIiwiaW5uZXJIZWlnaHQiLCJzY3JvbGxZIiwicGFnZVkiLCJzZWxlY3RGaWx0ZXIiLCJzZWxlY3Rpb25OYW1lIiwiZGVmYXVsdFZhbHVlIiwibGFzdFZhbHVlIiwic2VsZWN0QXBwZW5kQnV0dG9uIiwiaW5wdXRHcm91cCIsImlucHV0IiwiaW5wdXRBcHBlbmRCdXR0b24iLCJvcHRpb25zIiwiY2xvc2VCdXR0b24iLCJjdXJyZW50U3R5bGUiLCJwcm9wZXJ0eSIsImRpc3BhdGNoIiwidXNlIiwidmFsIiwicmVnIiwiUmVnRXhwIiwib3B0aW9uIiwiY3VycmVudE9wdGlvbiIsInBhcnNlRmxvYXQiLCJsYXNzbyIsInN2ZyIsImNoYXJ0Q29udGFpbmVyIiwiY2hhcnRPZmZzZXQiLCJvYmplY3RzT2Zmc2V0IiwiZXZlbnRDYXRjaGVyIiwieFNjYWxlIiwicGF0aCIsImxpbmUiLCJ5U2NhbGUiLCJjdXJ2ZSIsImN1cnZlTGluZWFyQ2xvc2VkIiwiaW5zdGFuY2UiLCJhbmltYXRpb25SYXRlIiwib3BhY2l0eSIsImRhc2hBcnJheSIsInN0cm9rZSIsInN0cm9rZVdpZHRoIiwibGFzc29lZFN0cm9rZSIsImxhc3NvZWRTdHJva2VXaWR0aCIsImVhc2VFeHAiLCJwYXRocyIsInBFbnRlciIsImFjdGl2ZVEiLCJvYmplY3RDb250YWluZXIiLCJhbGxQb2ludHMiLCJyZW5kZXIiLCJwcmV2ZW50RGVmYXVsdCIsInN0b3BQcm9wYWdhdGlvbiIsImFkZEV2ZW50TGlzdGVuZXIiLCJkcmFnIiwicmVtb3ZlRXZlbnRMaXN0ZW5lciIsImN1cnJlbnRQb2ludHMiLCJhcHBseVBhdGhBdHRyaWJ1dGVzIiwiY29sb3IiLCJ3aGljaCIsInB0IiwiaW52ZXJ0IiwibGFzdFB0IiwicDEiLCJwMiIsInNxcnQiLCJldWNsaWRlYW5EaXN0YW5jZSIsInRpY2tEaXN0YW5jZSIsInRpY2siLCJkZXRlY3QiLCJsYXNzb3MiLCJib3giLCJhYnNvbHV0ZVBvc2l0aW9uIiwibGVmdCIsInRvcCIsInJpZ2h0IiwiYm90dG9tIiwiYm94UHRzIiwiaW5BbnlMYXNzb1EiLCJsYXNzb1BvaW50cyIsImV2ZXJ5IiwicG9seWdvbkNvbnRhaW5zIiwiY29vcmQiLCJ1cGRhdGVPYmplY3RzIiwiYXBwbHlPYmplY3RBdHRyaWJ1dGVzIiwic2V0USIsInByZUxhc3NvRmlsbCIsInByZUxhc3NvU3Ryb2tlIiwicHJlTGFzc29TdHJva2VXaWR0aCIsImxhc3NvZWRGaWxsIiwia2V5RnJhbWVzIiwiaHRtbCIsImRyYXciLCJ0b2dnbGUiLCJzdGF0ZSIsInRoaXNTVkciLCJhYnNvbHV0ZSIsImVsZW1lbnRQb3NpdGlvbiIsInN2Z1Bvc2l0aW9uIiwicmVsYXRpdmVQb3NpdGlvblRvIiwiYXhpcyIsImxhYmVsIiwidGlja1RpY2tMYWJlbFNwYWNlciIsInRpY2tMYWJlbE1hcmdpbiIsInJldmVyc2VTY2FsZVEiLCJvcmllbnQiLCJ2ZXJ0aWNhbFEiLCJiZ2NwUmVjdCIsInNwYWNlWCIsInNwYWNlWSIsImd1aWRlTGluZXNRIiwiZ3VpZGVsaW5lU3BhY2UiLCJ0aWNrTGFiZWxNYXhGb250U2l6ZSIsImJhY2tncm91bmRGaWxsIiwidGlja0xhYmVsVGV4dEFuY2hvciIsInRpY2tMYWJlbFJvdGF0aW9uIiwidGlja0RhdGEiLCJjYXRlZ29yaWNhbFEiLCJncm91cGluZyIsInRpY2tMYWJlbHMiLCJudW1iZXJPZlRpY2tzIiwiZXh0ZW50IiwidGlja1ZhbHVlcyIsImZsYXRUaWNrRGF0YSIsInNwYWNlIiwicmV2ZXJzZSIsImRvbWFpblBhZGRpbmciLCJtaW5PYmplY3RTaXplIiwibWF4T2JqZWN0U2l6ZSIsIm9iamVjdFNwYWNlciIsIm9iakNsYXNzIiwic3BhY2VyRnVuY3Rpb24iLCJtb3ZlWEJ5IiwibW92ZVlCeSIsIm10IiwiZGF0dW0iLCJkaXN0IiwibGFiZWxOYW1lR3JvdXAiLCJsYWJlbEVsZW1lbnQiLCJ0aGF0IiwidGlja0xlbmd0aCIsInRpY2tTdHJva2UiLCJ0aWNrU3Ryb2tlV2lkdGgiLCJzdHJpbmciLCJjaGFycyIsInJvdW5kVG8iLCJ0aWNrTGFiZWxGb250U2l6ZSIsImdldENvbXB1dGVkVGV4dExlbmd0aCIsImxhYmVsSG92ZXIiLCJsYWJlbEhvdmVyT2ZmIiwidGlja0xhYmVsT25DbGljayIsImd1aWRlTGluZVN0cm9rZSIsImd1aWRlTGluZVN0cm9rZVdpZHRoIiwibGluZVN0cm9rZSIsImxpbmVTdHJva2VXaWR0aCIsInBhcmVudE5vZGUiLCJ0aWNrTGFiZWxPbkhvdmVyRnVuYyIsImdsaW5lIiwibWlub3JRIiwiaWkiLCJ0aWNrTGFiZWxNaW5Gb250U2l6ZSIsInRpY2tMYWJlbEZ1bmMiLCJiYXIiLCJrZXlBIiwia2V5QiIsImRlc2NlbmRpbmciLCJDRiIsIlRUaXAiLCJiYXJQZXJjZW50IiwiYmFyS2V5cyIsIm9yZGVyZWQiLCJzb3J0Iiwic29ydGluZ0Z1bmN0aW9uIiwiYmFyVmFsdWVzIiwiZGVmYXVsdEV4aXQiLCJub2RlcyIsInBhcmVudEluZGV4QXJyYXkiLCJOdW1iZXIiLCJmaWxsQ29sb3IiLCJzdHJva2VDb2xvciIsImJhclN0cm9rZVdpZHRoIiwiYnViYmxlSGVhdG1hcCIsInhLZXkiLCJ5S2V5IiwicktleSIsInZLZXkiLCJ4S2V5U29ydGluZ0Z1bmN0aW9uIiwieEV4dHJhY3RvciIsInlLZXlTb3J0aW5nRnVuY3Rpb24iLCJ5RXh0cmFjdG9yIiwiYmhtIiwiY2VsbEtleXMiLCJyRXh0cmFjdG9yIiwidkV4dHJhY3RvciIsInhWYWx1ZXMiLCJ5VmFsdWVzIiwieERpbSIsInlEaW0iLCJyVmFsdWVzIiwieVNpemUiLCJ4U2l6ZSIsInlTcGFjZXIiLCJ5U3BhY2VyU2l6ZSIsInhTcGFjZXIiLCJ4U3BhY2VyU2l6ZSIsImNlbGxzIiwidlZhbHVlcyIsInJhZGl1cyIsInNjYWxlZCIsImJ1YmJsZVN0cm9rZVdpZHRoIiwiaGVhdG1hcCIsImhtIiwieU1pbk9iamVjdFNpemUiLCJ5TWF4T2JqZWN0U2l6ZSIsInhNaW5PYmplY3RTaXplIiwieE1heE9iamVjdFNpemUiLCJsb29rdXAiLCJwb3NpdGlvbmVkQ2VsbEtleXMiLCJsb29rdXBWYWx1ZSIsIm9iamVjdFN0cm9rZVdpZHRoIiwiYm94d2hpc2tlciIsInF1YXJ0aWxlc0tleSIsInF1YXJ0aWxlc0tleXMiLCJib3hLZXlzIiwiYm94VmFsdWVzIiwid2hpc2siLCJ1V2hpc2siLCJsV2hpc2siLCJxdWFydCIsInVRdWFydCIsImxRdWFydCIsIm1RdWFydCIsImJveFN0cm9rZVdpZHRoIiwiciIsImRpZiIsImRkIiwid2hpc2tlcldpZHRoUGVyY2VudCIsIndoaXNrZXJTdHJva2VXaWR0aCIsImRhdGF0b2dnbGUiLCJ4QXhpc09wdGlvbnMiLCJ5QXhpc09wdGlvbnMiLCJ5QXhpc1NlbGVjdFEiLCJ4QXhpc1NlbGVjdFEiLCJ1cGRhdGVGdW5jdGlvbiIsImN1cnJlbnRLZXlzIiwidmFscyIsImZpbHRlclNlbGVjdHMiLCJkYXRhb3B0cyIsImRvRW50ZXIiLCJzZiIsInNjYXR0ZXIiLCJwb2ludEtleXMiLCJ2YWx1ZUV4dHJhY3RvclgiLCJ2YWx1ZUV4dHJhY3RvclkiLCJ2YWx1ZUV4dHJhY3RvclIiLCJleHRlbnRYIiwidmFsdWVzWCIsImRvbWFpblBhZGRpbmdYIiwiZXh0ZW50WSIsInZhbHVlc1kiLCJkb21haW5QYWRkaW5nWSIsImV4dGVudFIiLCJ2YWx1ZXNSIiwiZG9tYWluUGFkZGluZ1IiLCJtaW5SYWRpdXMiLCJtYXhSYWRpdXMiLCJwb2ludHMiLCJwRXhpdCIsInNjYWxlWCIsInNjYWxlWSIsInNjYWxlUiIsInBvaW50U3Ryb2tlV2lkdGgiLCJwbG90Wm9vbSIsImNoYXJ0IiwieEF4aXMiLCJ5QXhpcyIsImNoYXJ0U2VsIiwieEF4aXNTZWwiLCJ5QXhpc1NlbCIsInNldExvY2tzIiwiY2hhcnRPYmpTZWwiLCJjaGFydE9ialRyYW5zIiwiY29zIiwiZ2V0QkJveCIsInhMb2NrIiwieUxvY2siLCJ6b29tIiwiY2hhcnRCb3giLCJ4QXhpc0JveCIsInlBeGlzQm94IiwiZXZlbnRUeXBlIiwiZGVsdGFZIiwid2hlZWxTcGVlZCIsInNoaWZ0USIsInNoaWZ0S2V5IiwiYXBwbHlYIiwiYXBwbHlZIiwieEF4aXNPYmpTZWwiLCJ5QXhpc09ialNlbCIsInJlc2V0IiwibXVsdGlQbG90Wm9vbSIsInhDb21wb25lbnRzIiwieUNvbXBvbmVudHMiLCJ4Q29tcG9uZW50c1NlbCIsInlDb21wb25lbnRzU2VsIiwieENvbXBvbmVudE9ialNlbCIsInlDb21wb25lbnRPYmpTZWwiLCJ2aW9saW4iLCJiYXNlIiwibWluTWF4SGV4U2NhbGUiLCJzY2FsZWRDb2xvciIsIm1vZCIsInF1YXJ0aWxlS2V5cyIsInBvaW50c1Rvb2x0aXAiLCJ2aW9saW5LZXkiLCJ2aW9saW5EYXRhIiwidmlvbGluUG9pbnRLZXkiLCJ2aW9saW5Qb2ludERhdGEiLCJjYWxjVmFsdWVzIiwiY2FsY3VsYXRlVmlvbGluVmFsdWVzIiwidmlvbGluUG9pbnRzIiwidmlvbGluUG9pbnRzRXh0cmFjdG9yIiwidmlvbGluUG9pbnRzS2V5cyIsInZpb2xpblBvaW50c1ZhbHVlcyIsInBrIiwidmlvbGluUG9pbnRWYWx1ZUV4dHJhY3RvciIsInBvaW50UXVhcnRpbGVzIiwiYmlubmVkIiwiaGlzdG9ncmFtIiwiZnJlcXVlbmNpZXMiLCJiaW4iLCJtaW5Db250b3VyUG9pbnQiLCJtYXhDb250b3VyUG9pbnQiLCJ2aW9saW5Db250b3VyUG9pbnRzIiwieDAiLCJ4MSIsImNvbnRvdXIiLCJwb2ludFZhbHVlcyIsIm5lZWRlZFZpb2xpblZhbHVlcyIsInZrIiwidmlvbGluS2V5cyIsImZyZXF1ZW5jeU1heCIsInZTY2FsZSIsImxBcmVhIiwiY3VydmVCYXNpcyIsInJBcmVhIiwiYXJlYSIsImxhIiwicmEiLCJxdWFydHMiLCJwb2ludHNRIiwicHRzQ29udGFpbmVyIiwicHRzIiwicHRzRW50ZXIiLCJwTWluIiwicE1heCIsInBvaW50UmFkaXVzIiwicG9pbnRLZXkiLCJyYW5kb20iLCJwb2ludENvbG9yRnVuYyIsImNyZWF0ZUV2ZW50IiwiaW5pdEV2ZW50IiwiZGlzcGF0Y2hFdmVudCIsInRvb2x0aXBLZXkiLCJxdWFydGlsZUtleSIsInZpb2xpblZhbHVlcyIsIm51bWVyaWNMZWdlbmQiLCJmb250U2l6ZSIsInRleHRDb2xvciIsImxlZ2VuZCIsImxpbmVhckdyYWRpZW50IiwibSIsImNhdGVnb3JpY0xlZ2VuZCIsImFzY2VuZGluZyIsImNhdEtleXMiLCJjeCIsImN5IiwibGFzc29XaWRnZXQiLCJtYXhOdW1iZXJPZkdyb3VwcyIsImRhdGFFeHRyYWN0b3IiLCJvbkVycm9yIiwic3VibWl0IiwiY2xpY2tTZW5kIiwicmVtb3ZlQnRuQ2xpY2tUb1JlbW92ZSIsInRhYnMiLCJwYW5lcyIsInJlbWFpbmluZ1RhYnMiLCJsYXN0VGFiIiwiY3VyVGV4dCIsInBvcHVsYXRlUGFuZSIsInBhbmUiLCJsZWFkIiwiYnRucyIsImNOIiwiYnRuIiwibGFzc29CdG4iLCJtYWtlTGFzc29CdXR0b24iLCJjdXJyZW50TGFzc28iLCJjbGVhckJ0biIsImxhc3NvQnRuVG9nZ2xlIiwibGFzIiwidGFibGVEYXRhIiwiZXh0cmFjdGVkIiwibWFrZUNsZWFyQnV0dG9uIiwibW9uaXRvckxhc3NvQnV0dG9uU3RhdGUiLCJ0YXJnZXQiLCJtYWtlVGFibGUiLCJoZWFkUiIsImJvZHkiLCJoZWFkZXJLZXlzIiwiaGVhZGVyQ29scyIsInVwZGF0ZVRhYmxlSGVhZGVyQ29sdW1ucyIsImJvZHlSb3dzIiwidXBkYXRlVGFibGVSb3dzIiwicm93RGF0YSIsImNvbHMiLCJzcGxpY2UiLCJtYWtlTmV3R3JvdXAiLCJncyIsIm5ld0dyb3VwTnVtYmVyIiwibGkiLCJpbnNlcnQiLCJ0eHQiLCJpbnNlcnRUYWIiLCJtYWtlVGFiUGFuZSIsImNhcmQiLCJ0YWJMaXN0IiwidGFiQ29udGVudCIsInJpZ2h0VGFicyIsInVwc2V0Iiwic2V0VmFsdWVzIiwiaW50ZXJzZWN0aW9uVmFsdWVzIiwieE9iamVjdFNpemUiLCJjaXJjbGVTdHJva2VXaWR0aCIsInNldEV4dHJhY3RvciIsImludGVyc2VjdGlvbkV4dHJhY3RvciIsImVsZW1lbnRFeHRyYWN0b3IiLCJlbGVtZW50VmFsdWVzIiwieU9iamVjdFNwYWNlciIsInNldEtleVNvcnRpbmdGdW5jdGlvbiIsImludGVyc2VjdGlvbktleVNvcnRpbmdGdW5jdGlvbiIsInhPYmplY3RTcGFjZXIiLCJ5T2JqZWN0U2l6ZSIsInNldCIsImludGVyc2VjdGlvbiIsImludGVyc2VjdGlvblRvdGFscyIsInRvdGFscyIsInRvdGFsIiwic2V0VG90YWxzIiwiZmlsdGVyVGFibGUiLCJzb3J0USIsInJlY29yZCIsImNvbHVtbktleSIsInJlY29yZFZhbHVlIiwicm93cyIsInRoIiwibWFrZUNvbHVtbnMiLCJwb3B1bGF0ZVJlY29yZHMiLCJtYWtlUm93cyIsInJvdyIsImZpZWxkRnVuY3Rpb24iLCJmaWVsZHMiLCJleHRyYWN0VmlvbGluVmFsdWVzIiwidmFsdWVFeHRyYWN0b3JGdW5jdGlvbiIsInFLZXkiLCJtaW5Qb2ludCIsIm1heFBvaW50IiwiaW50ZXJwb2xhdGVDb2xvcnMiLCJpbnRlcnBvbGF0ZVJnYkJhc2lzIiwidHJ1bmNhdGVUZXh0Iiwic2V0dXBTdGFuZGFyZENoYXJ0Q29udGFpbmVycyIsIm1hcmdpbnMiLCJheGVzIiwibGVnIiwibWFyZ2luIiwicG9zIiwiSGVpZ2h0Iiwic3ZnU3BhY2UiLCJtYXJnUHgiLCJjaGFydFNwYWNlIiwiYXhlc1NwYWNlIiwibGVnUmVjdCIsImRyYXdpbmdTcGFjZSIsInlBeGlzUmVjdCIsInBsb3RSZWN0IiwieEF4aXNSZWN0IiwibXlMb2ciLCJ3YXJuIiwiY29uc29sZSIsImluZm8iLCJlcnJvciIsInJlc2l6ZURlYm91bmNlIiwid2FpdCIsInJlc2l6ZSIsImltbWVkaWF0ZSIsInRpbWVvdXQiLCJjb250ZXh0IiwiYXJncyIsImNhbGxOb3ciLCJzZXRUaW1lb3V0IiwiYXBwbHkiLCJkZWJvdW5jZSJdLCJtYXBwaW5ncyI6ImtDQWVPLFNBQVNBLEVB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 diff --git a/build/js/d3sm.v0.0.3.js b/build/js/d3sm.v0.0.3.js deleted file mode 100644 index 7b31405..0000000 --- a/build/js/d3sm.v0.0.3.js +++ /dev/null @@ -1,10613 +0,0 @@ -var d3sm = (function (exports) { - 'use strict'; - - // import {hasQ} from './array-functions'; - /******************************************************************************* - ** ** - ** ** - ** HELPERS ** - ** ** - ** ** - *******************************************************************************/ - /** - * Helper function for Array.filter to get unique elements of the array - * @param {*} value current value as mapping over array (self) - * @param {number} index current index in the array - * @param {Array} self passed array from Array.filter method - * @returns {boolean} whether or not value is the first of its kind (i.e. indexOf(value) == index) - */ - function uniqueElements(value, index, self) { - return self.indexOf(value) === index; - } - - /** - * Extracts x and y of translate from transform property - * @param {string} transform transform property of svg element - * @returns {number[]} x, y of translate(x, y) - */ - function getTranslation(transform) { - // Create a dummy g for calculation purposes only. This will never - // be appended to the DOM and will be discarded once this function - // returns. - var g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - // Set the transform attribute to the provided string value. - transform = transform == undefined ? 'translate(0,0)' : transform; - g.setAttributeNS(null, 'transform', transform); - // consolidate the SVGTransformList containing all transformations - // to a single SVGTransform of type SVG_TRANSFORM_MATRIX and get - // its SVGMatrix. - var matrix = g.transform.baseVal.consolidate().matrix; - // As per definition values e and f are the ones for the translation. - return [matrix.e, matrix.f]; - } - - /** - * Modifies luminance of hexidecimal number - * @param {string} hex should be hexidecimal value with or without the proceeding octotrope - * @param {number} lum value to increase or decrease luminosity by - * @returns {string} updated hexidecimal value without the proceeding octotrope - */ - function modifyHexidecimalColorLuminance(hex, lum) { - // validate hex string - var hex = String(hex).replace(/[^0-9a-f]/gi, ''); - - if (hex.length < 6) { - hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; - } - lum = lum || 0; - - // convert to decimal and change luminosity - var rgb = '#', - c, - i; - for (i = 0; i < 3; i++) { - c = parseInt(hex.substr(i * 2, 2), 16); - c = Math.round(Math.min(Math.max(0, c + c * lum), 255)).toString(16); - rgb += ('00' + c).substr(c.length); - } - - return rgb; - } - - /** - * Calculated the quartiles of the passed data and stores them with qKeys - * @param {number[]} data list of numerical values - * @param {string[]} [qKeys=['q0', 'q1', 'q2', 'q3', 'q4']] how returned object with quartiles should be stored - * @returns {Object} with keys qKeys giving only the numerical values for the quartiles - */ - function quartiles(data, qKeys) { - var q2 = d3.median(data), - lower = data.filter(function (x) { - return x < q2; - }), - upper = data.filter(function (x) { - return x > q2; - }), - q1 = d3.median(lower), - q1 = q1 == undefined ? q2 : q1, - q0 = d3.min(lower), - q0 = q0 == undefined ? q1 : q0, - q3 = d3.median(upper), - q3 = q3 == undefined ? q2 : q3, - q4 = d3.max(upper), - q4 = q4 == undefined ? q3 : q4, - k0 = 'q0', - k1 = 'q1', - k2 = 'q2', - k3 = 'q3', - k4 = 'q4', - obj = {}; - if (qKeys != undefined && qKeys.length == 5) { - k0 = qKeys[0];k1 = qKeys[1];k2 = qKeys[2];k3 = qKeys[3];k4 = qKeys[4]; - } - obj[k0] = q0;obj[k1] = q1;obj[k2] = q2;obj[k3] = q3;obj[k4] = q4; - - return obj; - } - - /** - * Helper function to get all values needed in making violin plots - * @param {string[]} violinKeys - * @param {number[]} data - * @param {Function} valueExtractorFunction how to get values from data[violinKeys[i]] - * @param {boolean} horizontalQ whether or not violins will be rendered horizontally or vertically - * @param {string} qKey how the object containing the quartiles should be labeled as - * @param {string[]} qKeys how each quartile should be labeled as - * @returns {Object} required for @see{@link violin} containing keys values, binnned, frequencies, points, and quartiles - * @see{@link quartiles} - */ - function extractViolinValues(violinKeys, data, valueExtractorFunction, horizontalQ, qKey, qKeys) { - var obj = {}; - violinKeys.map(function (k, i) { - var d = valueExtractorFunction(k, i, data), - binned = d3.histogram()(d), - frequencies = binned.map(function (x) { - return x.length; - }), - minPoint = horizontalQ ? { y: d3.min(d), x: 0 } : { x: d3.min(d), y: 0 }, - maxPoint = horizontalQ ? { y: d3.max(d), x: 0 } : { x: d3.max(d), y: 0 }, - points = binned.map(function (bin, i) { - return horizontalQ ? { y: bin.length ? d3.median(bin) : d3.median([bin.x0, bin.x1]), x: frequencies[i] } : { x: bin.length ? d3.median(bin) : d3.median([bin.x0, bin.x1]), y: frequencies[i] }; - }), - quarts = quartiles(d, qKeys), - o = { - values: d, - binned: binned, - frequencies: frequencies, - points: [minPoint].concat(points).concat([maxPoint]) - }; - o[qKey] = quarts; - obj[k] = o; - }); - return obj; - } - - /** - * Hypenates all strings together - * @param {string[]} arguments - * @returns {string} "arg1-arg2-...-argn" - */ - function hypenate() { - return Array.prototype.slice.call(arguments).join('-'); - } - - /** - * Rounds decimals of number to precision - * @param {number} number - * @param {number} precision - * @returns {number} rounded to precision - */ - function round(number, precision) { - var shift = function shift(number, precision, reverseShift) { - if (reverseShift) { - precision = -precision; - } - var numArray = ('' + number).split('e'); - return +(numArray[0] + 'e' + (numArray[1] ? +numArray[1] + precision : precision)); - }; - return shift(Math.round(shift(number, precision, false)), precision, true); - } - - /** - * recursively ascends element.parentElement to find a svg tag - * @param {Element} element - * @returns {Element | undefined} - */ - function getContainingSVG(element) { - var parent = element.parentElement; - var tag = parent.tagName.toLowerCase(); - if (tag === 'svg') { - return parent; - } - if (tag === 'html') { - return undefined; - } - return getContainingSVG(parent); - } - - /** - * Maps arguments in to d3.interpolateRgbBasis - * @param arguments - * @returns {Function} - */ - function interpolateColors() { - return d3.interpolateRgbBasis(arguments); - } - - /** - * Trys to reduce text to fit in specified area, made for tick labels as called by - * @see{@link axis} - * @param {d3.selection} t container for specific axis tick - * @param {string} text to be the label of the passed axis tick - * @param {boolean} orient of the axis, true is horizontal, false is vertical - * @param {number} tickLength is the length of the text - * @param {number} space is the amount of availble space for the text and the tick to fit in - * @param {boolean} overflowQ whether or not allowed to go over the alloted space - * @returns {none} - */ - function truncateText(t, text, orient, tickLength, space, overflowQ) { - var rect = t.node().getBoundingClientRect(); - t.text(text); - while (Math.max(rect.width, rect.height) > space - tickLength) { - text = String(text); - text = text.slice(0, text.length - 1); - t.text(text + '...'); - rect = t.node().getBoundingClientRect(); - if (text.length == 0) break; - } - } - - function truncateString(string, space, font) { - var chars = space / font; - if (chars < string.length) { - return string.slice(0, Math.round(chars - 5)) + '...'; - } else { - return string; - } - } - - /** - * Trys to use d3.selection to get element, if it doesnt exist, makes one - * @param {d3.selection} sel selection in which to try and find object - * @param {string} tag tag of which to try and select - * @param {string} [cls=''] class of tag to try and grab - * @returns {d3.selection} of either append or selected tag.cls within sel - */ - function safeSelect(sel, tag, cls) { - var clsStr = cls == undefined ? '' : '.' + cls; - var sSel = sel.select(tag + clsStr).empty() ? sel.append(tag) : sel.select(tag + clsStr); - return sSel.classed(clsStr.replace('.', ''), true).attr('transform', sSel.attr('transform') == undefined ? 'translate(0,0)' : sSel.attr('transform')); - } - - /** - * evenly partitions the range [min, max] into n parts - * @param {number} min - * @param {number} max - * @param {number} n - * @returns {number[]} array of length n evenly partitioned between min and max - */ - function tickRange(min, max, n) { - var a = [min]; - var d = max - min; - var s = d / (n - 1); - for (var i = 0; i < n - 2; i++) { - a.push(min + s * (i + 1)); - } - a.push(max); - return a; - } - - function euclideanDistance(p1, p2) { - var a = p1[0] - p2[0], - b = p1[1] - p2[1]; - return Math.sqrt(a * a + b * b); - } - - /** - * Short-hand for array.includes(item); - * @param {Array} array - * @param {*} item to test if contained in {array} - * @returns {boolean} - */ - function hasQ(array, item) { - return array.includes(item); - } - - /** - * Calculates the total value of numbers in passed array - * @param {number[]} array of numerical values - * @returns {number} sum over elements in array - */ - function total(array) { - return array.reduce(function (a, b) { - return a + b; - }, 0); - } - /** - * Removes duplicates in array - * @param {Array} array of items - * @returns {Array} of items such that item_i != item_j for all i < j - * @see{@link uniqueElements} for the filtering function - */ - function unique(array) { - return array.filter(uniqueElements); - } - - /** - * Concats all nested arrays in passed array to form a single array - * @param {Array} array of putatively nested arrays - * @param {Array} [flat=[]] current flattened array - * @returns {Array} with every element in the same level - */ - function flatten(array, flat) { - flat = flat == undefined ? [] : flat; - array.map(function (e, i) { - if (Array.isArray(e)) { - flat = flat.concat(flatten(e)); - } else { - flat.push(e); - } - }); - return flat; - } - - /** - * Search of list of lists to find which - if any - passed value is in - * @param {Array[]} bins list of lists of values - * @param {*} value item to test if in any of the bins - * @returns {number} indicating the index of the bin in which value was found - */ - function whichBin(bins, value) { - var i = -1; - for (var j = 0; j < bins.length; j++) { - if (hasQ(bins[j], value)) { - return j; - } - } - return i; - } - - /** - * calls console.group if d3sm.debugQ == true - * @param {string} name of the group - * @returns {undefined} - */ - function consoleGroup(name) { - if (window.d3sm.debugQ === true) { - console.group(name); - } - } - - /** - * calls console.groupEnd if d3sm.debugQ == true - * @returns {undefined} - */ - function consoleGroupEnd() { - if (window.d3sm.debugQ === true) { - console.groupEnd(); - } - } - - /** - * Calls console.log if d3sm.debugQ == true - * @param {string} func name of the function logging - * @param {string} msg to log - * @param {Object} data to be logged along side the message - * @returns {undefined} - */ - function log(func, msg, data) { - if (window.d3sm.debugQ === true) { - console.log('%c[d3sm::' + func + ']:\t' + msg, ['background: #6cd1ef', 'border-radius: 5000px', 'padding: 0px 2px', 'font-size: 14px'].join(';')); - console.table(data); - // console.trace() - } - } - - /** - * Calls console.warn if d3sm.debugQ == true - * @param {string} func name of the function warning - * @param {string} msg to display - * @param {Object} data to be displayed along side the message - * @returns {undefined} - */ - function warn(func, msg, data) { - if (window.d3sm.debugQ === true) console.warn('%c[d3sm::' + func + ']:\t' + msg, ['background: #ffd53e', 'border-radius: 5000px', 'padding: 0px 2px', 'font-size: 14px'].join(';')); - console.table(data); - } - /** - * Calls the console.info if d3sm.debugQ == true - * @param {string} func name of the function providing info - * @param {string} msg to display - * @param {Object} data to be displayed along side the message - * @returns {undefined} - */ - function info(func, msg, data) { - if (window.d3sm.debugQ) console.info('%c[d3sm::' + func + ']:\t' + msg, ['background: #009ccd', 'border-radius: 5000px', 'padding: 0px 2px', 'font-size: 14px'].join(';')); - console.table(data); - } - - /** - * Calls console.error if d3sm.debugQ == true - * @param {string} func name of the function which sends the error - * @param {string} msg to display - * @param {Object} data to be displayed along side the message - * @returns {undefined} - */ - function error(func, msg, data) { - if (window.d3sm.debugQ) console.error('[d3sm::' + func + ']:\t' + msg + '\t%o', data); - } - - /** - * Function for setting up containers for most plots with the y axis container - * positioned on the left and the x axis container positioned on the bottom - * @param {d3.selection} selection selection of container in which the svg is or should be made - * @param {string} namespace namespace of the chart - * @param {Object} [space={w:window.innerWidth, h:window.innerHeight}] the width (w) and height (h) availble - * @param {number} [space.w=window.innerWidth] the available width in which to render the chart - * @param {number} [space.h=window.innerHeight] the available height in which to render the chart - - * @param {Object} [margins={top: 0.01, bottom: 0.01, left: 0.01, right: 0.01}] the margins for the chart - * @param {number} [margins.top=0.01] the top margin of the chart - * @param {number} [margins.bottom=0.01] the bottom margin of the chart - * @param {number} [margins.left=0.01] the left margin of the chart - * @param {number} [margins.right=0.01] the right margin of the chart - - - * @param {Object} [percentages = {axes:{x:0.1,y:0.1},space:{w:0.8,h:0.6}}] percentages of the paramater space of which to make the x and y axes as well as the percent of the availble space in which to render the plot - * @param {Object} [percentages.axes={x:0.1,y:0.1}] the percentages of the paramater space, of which the x and y axes will take up - * @param {number} [percentages.axes.xAxisPercent=0.1] the percentages of the paramater space, of which the x axis will take up - * @param {number} [percentages.axes.yAxisPercent=0.1] the percentages of the paramater space, of which the y axis will take up - - * @param {Object} [percentages.space={w:0.8,h:0.6}] the percentages of the paramater space, of which the SVG's width and height will be set - * @param {number} [percentages.space.percentOfSpaceForWidth=0.1] the percentages of the paramater space, of which the SVG's width will be set - * @param {number} [percentages.space.percentOfSpaceForHeight=0.1] the percentages of the paramater space, of which the SVG's height will be set - - * @returns {Object} returns the selection and "boundingRects" of the plot container, x-axis container and y-axis container - * as - * - * { - * - * plot: {selection: plotSelection, rect: plotRect}, - * - * xAxis:{selection:xAxisSelection, rect:xAxisRect}, - * - * yAxis: {selection:yAxisSelection, rect:yAxisRect} - * - * } - * - * where each rect has form: - * - * {x: #, y: #, h: #, w: #} - * - * depicting the starting x and y coordinate of the coresponding container (also their default transform values) as well their height (h) ans width (w) - */ - // export function setupStandardChartContainers( selection, namespace, space, margins, percentages) { - // export function setupStandardChartContainers( - // selection, - // namespace, - // space={w:availableWidth=window.innerWidth, h:availableHeight=window.innerHeight}, - // margins={top:0.01, bottom:0.01, left:0.01, right:0.01}, - // percentages={axes: {x: xAxisPercent=0.1, y: yAxisPercent=0.1}, space: {w: percentOfSpaceForWidth, h: percentOfSpaceForHeight}} - // ) { - // if (space == undefined) { space = {w: window.innerWidth, h: window.innerHeight} } - // if (margins == undefined) { margins = {top: 0.01, bottom: 0.01, left: 0.01, right: 0.01} } - // if (percentages == undefined) { percentages = {}; } - // if (percentages.axes == undefined) { percentages.axes = { x:0.1, y:0.1 } } - // if (percentages.space == undefined) { percentages.space = { w: 0.8, h: 0.6 } } - // - // // SVG width and height - // var svgSpace = { - // w: space.w * percentages.space.w, - // h: space.h * percentages.space.h - // }, - // - // // Space after removing margins - // chartSpace = { - // w: svgSpace.w - (margins.left * space.w) - (margins.right * space.w), - // h: svgSpace.h - (margins.top * space.h) - (margins.bottom * space.h) - // }, - // - // // main dimension of x and y axies - // // e.g. defines how tall x axis is as length is determined by plotRect.w - // axesSpace = { - // x: chartSpace.h * percentages.axes.x, - // y: chartSpace.w * percentages.axes.y - // }, - // - // // space left for drawing the chart properly (e.g. bars, violins, etc) - // drawingSpace = { - // x: chartSpace.w - axesSpace.y, - // y: chartSpace.h - axesSpace.x - // }, - // - // - // yAxisRect = { - // x: axesSpace.y + (margins.left * space.w), - // y: (margins.top * space.h), - // w: axesSpace.y, - // h: drawingSpace.y - // }, - // - // plotRect = { - // x: axesSpace.y + (margins.left * space.w), - // y: (margins.top * space.h), - // w: drawingSpace.x, - // h: drawingSpace.y - // }, - // - // xAxisRect = { - // x: axesSpace.y + (margins.left * space.w), - // y: (margins.top * space.h + plotRect.h), - // w: drawingSpace.x, - // h: axesSpace.x - // } - // - // - // var container = safeSelect(selection, 'svg', namespace) - // .style('width', svgSpace.w+'px') - // .style('height', svgSpace.h+'px') - // - // var axes = safeSelect(container, 'g', hypenate(namespace, 'axes')) - // - // // .attr('transform', "translate("+plotRect.x+","+plotRect.y+")"), - // - // var plot = safeSelect(container, 'g', hypenate(namespace, 'plot')) - // .attr('transform', "translate("+plotRect.x+","+plotRect.y+")") - // - // var xAxis = safeSelect(axes, 'g', hypenate(namespace, 'x-axis')) - // .attr('transform', "translate("+xAxisRect.x+","+xAxisRect.y+")") - // - // var yAxis = safeSelect(axes, 'g', hypenate(namespace, 'y-axis')) - // .attr('transform', "translate("+yAxisRect.x+","+yAxisRect.y+")") - // - // return { - // svg: { - // selection: container, - // rect: svgSpace - // }, - // plot: { - // selection: plot, - // rect: plotRect - // }, - // xAxis: { - // selection: xAxis, - // rect: xAxisRect - // }, - // yAxis: { - // selection: yAxis, - // rect: yAxisRect - // } - // } - // - // // return [plot, xAxis, yAxis] - // } - // - // - - - function setupStandardChartContainers(selection, namespace, container) { - var margins = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : { top: 0.01, bottom: 0.01, left: 0.01, right: 0.01 }; - var svg = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : { w: 0.8, h: 0.6 }; - var axes = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : { y: 0.1, x: 0.1 }; - var leg = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : { x: 0, margin: 0, pos: 'left' // absolute width of legend and space on either size - }; - - if (container == undefined) { - container = { w: window.innerWidth, h: window.Height }; - } - // SVG width and height - - var svgSpace = { - w: container.w * svg.w, - h: container.h * svg.h - }; - - var margPx = { - top: margins.top * svgSpace.h, - bottom: margins.bottom * svgSpace.h, - left: margins.left * svgSpace.w, - right: margins.right * svgSpace.w - }, - - - // Space after removing margins - chartSpace = { - w: svgSpace.w - margPx.left - margPx.right, - h: svgSpace.h - margPx.top - margPx.bottom - }, - - - // main dimension of x and y axies - // e.g. defines how tall x axis is as length is determined by plotRect.w - axesSpace = { - x: chartSpace.h * axes.x, - y: chartSpace.w * axes.y - }, - - - // space left for drawing the chart properly (e.g. bars, violins, etc) - drawingSpace = { - x: chartSpace.w - axesSpace.y - leg.x - 2 * leg.margin, - y: chartSpace.h - axesSpace.x - }, - legRect = { - x: leg.margin + margPx.left + (leg.pos == 'left' ? 0 : drawingSpace.x + axesSpace.y), - y: margPx.top, // this is soomehow getting calculated incorectly - w: leg.x, - h: drawingSpace.y - }, - yAxisRect = { - x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2 * leg.margin : 0), - y: margPx.top, - w: axesSpace.y, - h: drawingSpace.y - }, - plotRect = { - x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2 * leg.margin : 0), - y: margPx.top, - w: drawingSpace.x, - h: drawingSpace.y - }, - xAxisRect = { - x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2 * leg.margin : 0), - y: margPx.top + drawingSpace.y, - w: drawingSpace.x, - h: axesSpace.x - }; - - container = d3sm.safeSelect(selection, 'svg', namespace).style('width', svgSpace.w + 'px').style('height', svgSpace.h + 'px'); - - var axes = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'axes')); - - var leg = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'legend')).attr('transform', "translate(" + legRect.x + "," + legRect.y + ")"); - - var plot = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'plot')).attr('transform', "translate(" + plotRect.x + "," + plotRect.y + ")"); - - var xAxis = d3sm.safeSelect(axes, 'g', d3sm.hypenate(namespace, 'x-axis')).attr('transform', "translate(" + xAxisRect.x + "," + xAxisRect.y + ")"); - - var yAxis = d3sm.safeSelect(axes, 'g', d3sm.hypenate(namespace, 'y-axis')).attr('transform', "translate(" + yAxisRect.x + "," + yAxisRect.y + ")"); - - return { - svg: { - selection: container, - rect: svgSpace - }, - plot: { - selection: plot, - rect: plotRect - }, - xAxis: { - selection: xAxis, - rect: xAxisRect - }, - yAxis: { - selection: yAxis, - rect: yAxisRect - }, - legend: { - selection: leg, - rect: legRect - } - }; - } - - /** - * Adds a clip-path rect and binds it to container - * @param {d3.selection} container in which to add the clip-path and to which to bind the cliping path to - * @param {Object} rect the coordinates (x, y, width, height) of the clip-path - * @param {string} namespace - * @returns {d3.selection} of the clip-path rect - */ - function cpRect(container, rect, namespace) { - var defs = safeSelect(container, 'defs', hypenate(namespace, 'definitions')); - var cp = safeSelect(defs, 'clipPath', hypenate(namespace, 'clip-path')).attr('id', hypenate(namespace, 'clip-path')); - - var cpRect = safeSelect(cp, 'rect').attr('x', rect.x).attr('y', rect.y).attr('width', rect.width).attr('height', rect.height); - - defs.raise(); - // set clipping path to container - container.attr('clip-path', 'url(#' + hypenate(namespace, 'clip-path') + ')'); - - return cpRect; - } - - /** - * Adds a background rect t to container - * @param {d3.selection} container in which to add the background rectangle - * @param {Object} rect the coordinates (x, y, width, height) of the background - * @param {string} fill the color of the background - * @returns {d3.selection} of the background fill - */ - function bgRect(container, rect, fill) { - return safeSelect(container, 'rect', 'bg').attr('x', rect.x).attr('y', rect.y).attr('width', rect.width).attr('height', rect.height).attr('fill', fill); - } - - /** - * Sets up the container for making chart elements. This includes making - * a clip-path rect bound to the passed container, a background rect, and - * a g element with class -object-container. - * @param {d3.selection} container in which to add the clip-path and background - * @param {string} namespace - * @param {Object} rect the coordinates (x, y, width, height) of the background and clip-path - * @param {string} fill the color of the background - * @returns {d3.selection} of g.-object-container - * - * @see{@link bgRect} - * @see{@link cpRect} - */ - function setupContainer(selection, namespace, rect, fill) { - // the container for three main items, bg, defs, and object-container - var container = safeSelect(selection, 'g', namespace), - bg = bgRect(container, rect, fill), - cp = cpRect(container, rect, namespace), - objectContainer = safeSelect(container, 'g', hypenate(namespace, 'object-container')); - return objectContainer; - } - - /** - * determines the width of an object for the calling plotting function - * @param {number} freeSpace how much space is avalible - * @param {number} numberOfObjects how many object do we need - * @param {number} minObjectWidth how small are these objects allowed to be - * @param {number} maxObjectWidth how large are these object allowed to be - * @param {number} sizeOfSpacer percent of freeSpace that a single spacer should take up (need numberOfObjects - 1 spacers) - * @param {boolean} overflowQ can we go beyond alloted space - * @returns {number} how large object should be - * function tries to keep object within min / max width, but wil default to - * 5e-10 (smallest consistenly visible by svg size of element) if overflowQ is false - */ - function calculateWidthOfObject(freeSpace, numberOfObjects, minObjectWidth, maxObjectWidth, sizeOfSpacer, overflowQ) { - var sizeOfSpacer = sizeOfSpacer == 0 || sizeOfSpacer > 1 ? sizeOfSpacer : freeSpace * sizeOfSpacer; - - var numberOfSpacers = numberOfObjects - 1; - var spaceTakenBySpacers = numberOfSpacers * sizeOfSpacer; - var remainingSpace = freeSpace - spaceTakenBySpacers; - remainingSpace = remainingSpace < 0 ? 0 : remainingSpace; - var objectWidth = remainingSpace / numberOfObjects; - - if (overflowQ && minObjectWidth != undefined && objectWidth < minObjectWidth) { - objectWidth = minObjectWidth; - } - // if ( maxObjectWidth != undefined && objectWidth > maxObjectWidth ) { objectWidth = maxObjectWidth } - if (overflowQ && maxObjectWidth != undefined && objectWidth < maxObjectWidth) { - objectWidth = maxObjectWidth; - } - return Math.max(objectWidth, 5e-10); - } - - /** - * @param {Array[]} data list data (can be nested). If nested will create more complex spacer size - * @param {number} freeSpace how much space is avalible - * @param {number} objectWidth @see{@link calculateWidthOfObject} - * @param {number} numberOfObjects how many object do we need - * @param {number} baseSpacerSize percent of freeSpace that a single spacer should take up (need numberOfObjects - 1 spacers) - * @param {boolean} overflowQ can we go beyond alloted space - * @returns {number} returns size that spacer should be at level=0 - */ - function calculateWidthOfSpacer(data, freeSpace, objectWidth, numberOfObjects, baseSpacerSize, overflowQ) { - if (overflowQ) { - // var limitedNumberOfObjects = numberOfObjects > 6 ? 6 : numberOfObjects - // var spaceLeft = freeSpace - limitedNumberOfObjects * objectWidth - // return spaceLeft / (limitedNumberOfObjects - 1) - return freeSpace * baseSpacerSize; - } - var spacersAtEachLevel = spacersNeededAtEachLevel(data); - var totalSpacerPercent = total(spacersAtEachLevel.map(function (e, i) { - return e * 1 / (i + 1); - })); - var baseSpacerSize = (freeSpace - objectWidth * numberOfObjects) / totalSpacerPercent; - // console.log(freeSpace, objectWidth, numberOfObjects, totalSpacerPercent) - // console.log(totalSpacerPercent, baseSpacerSize, totalSpacerPercent * baseSpacerSize) - return isNaN(baseSpacerSize) ? 0 : baseSpacerSize; - } - - /** - * Calculates number of spacers needed to seperate elements at each level. - * @param {Array[]} array list data (can be nested). If nested will create more complex spacer size - * @param {number} [level=0] current level, used in recusrion - * @param {Array} [levelData=[]] how many spacers needed at a given level - * @returns {Array} levelData - * - * @example - * array = [[1,2], [3,4]] - * // returns [1, 2] - * as at level=0 the only spacer needed is between [1,2] and [3,4] - * and at level=1 the only two spacers needed is between 1 and 2 as well as - * 3 and 4 since the spacer between 2 and 3 is handled at level=0 - */ - function spacersNeededAtEachLevel(array, level, levelData) { - if (level == undefined) { - level = 0; - } else { - level += 1; - } - if (levelData == undefined) { - levelData = []; - } - if (level >= levelData.length) { - levelData.push(array.length - 1); - } else { - levelData[level] += array.length - 1; - } - array.map(function (e, i) { - if (Array.isArray(e)) { - spacersNeededAtEachLevel(e, level, levelData); - } - }); - return levelData; - } - - /** - * Draws a whisker for @see{@link boxwhisker} - * @param {boolean} dir direction to draw whisker, should be either true (up, top) or false (down or bottom) - * @param {number} x starting x coordinate in which to draw whisker - * @param {number} y starting y coordinate in which to draw whisker - * @param {number} w width of space in which to draw whisker - * @param {number} h height of space in which to draw whisker - * @param {number} per percentage of w or h (depends on o) to make whisker - * @param {boolean} o orientation, true is horizontal and false is vertical - * @returns {string} representing the svg path (i.e. the d attribute for a path tag) - */ - function whiskerPath(dir, x, y, w, h, per, o) { - // d = direction (true is up), p = percent width - if (dir == 'up' || dir == 'top' || dir == true) { - dir = true; - } - if (dir == 'down' || dir == 'bottom' || dir == false) { - dir = false; - } - o = o == undefined ? 'horizontal' : o; - per = per == undefined ? 1 : per; - if (o != "horizontal") { - var hh = h * per, - w = dir ? w : -w, - a = dir ? x + w : x, - b = dir ? x : x + w, - c = dir ? a : b; - p = "M " + a + ' ' + h / 2 + ' ' + 'L ' + b + ' ' + h / 2 + ' ' + 'M ' + c + ' ' + (h / 2 - hh / 2) + ' ' + 'L ' + c + ' ' + (h / 2 + hh / 2) + ' '; - - return p; - } - var ww = w * per, - a = dir ? y + h : y, - b = dir ? y : y + h, - p = "M " + w / 2 + ' ' + a + ' ' // straight line part - + 'L ' + w / 2 + ' ' + b + ' ' // straight line part - + 'h ' + -ww / 2 + ' ' + 0 + ' ' // horizontal line part - + 'h ' + ww + ' ' + 0 + ' '; - return p; - } - - function resizeDebounce(f, wait) { - var resize = debounce(function () { - f(); - }, wait); - window.addEventListener('resize', resize); - } - - function debounce(func, wait, immediate) { - var timeout; - return function () { - var context = this, - args = arguments; - var later = function later() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; - } - - /******************************************************************************* - ** ** - ** ** - ** SPACEGROUPING ** - ** ** - ** ** - *******************************************************************************/ - /** - * Produces a function for spacing objects by an arbitrarly complex grouping - * @returns {recursivelyPosition} the function for moving the objects - * (see {@link groupingSpacer#recursivelyPosition}) - * @namespace groupingSpacer - */ - function groupingSpacer() { - var - /*@var {boolean} horizontalQ @default*/ - - /** - * Whether or not to space objects horizontally or vertically. - * (see {@link groupingSpacer.horizontalQ}) - * @param {boolean} [horizontalQ=true] - * @memberof groupingSpacer# - * @instance - */ - horizontalQ = true, - - /** - * The scale to use to position elements if {@link groupingSpacer#moveby}="string" - * (see {@link groupingSpacer.scale}) - * @param {d3.scale} [scale=d3.scaleLinear()] - * @memberof groupingSpacer# - * @instance - */ - scale = d3.scaleLinear(), - - /** - * How elements in the complex grouping should be moved over by. - * By default, moveby="category", which moves objects by the complex grouping - * But objects can also be moved over by scale. - * (see {@link groupingSpacer.moveby}) - * @param {string} [moveby="category"] - * @memberof groupingSpacer# - * @instance - */ - moveby = 'category', - - /** - * How many objects are there in total - * (see {@link groupingSpacer.numberOfObjects}) - * @param {number} [numberOfObjects=none] - * @memberof groupingSpacer# - * @instance - */ - numberOfObjects, - - /** - * The class given to an nested tag whose parent(s) have the correct transition - * properties - * (see {@link groupingSpacer.numberOfObjects}) - * @param {string} [numberOfObjects='d3sm-groupped-item'] - * @memberof groupingSpacer# - * @instance - */ - objectClass = 'd3sm-groupped-item', - - /** - * The size of the objects being positioned - * (see {@link groupingSpacer.objectSize}) - * @param {number} [objectSize=none] - * @memberof groupingSpacer# - * @instance - */ - objectSize, - - /** - * The size of the un-nested spacer between objects - * (see {@link groupingSpacer.spacerSize}) - * @param {number} [spacerSize=none] - * @memberof groupingSpacer# - * @instance - */ - spacerSize, - - /** - * The duration of transitions in ms - * (see {@link groupingSpacer.transitionDuration}) - * @param {number} [transitionDuration=1000] - * @memberof groupingSpacer# - * @instance - */ - transitionDuration = 1000, - - /** - * The ease function for the transitions - * (see {@link groupingSpacer.easeFunc}) - * @param {d3.ease} [easeFunc=d3.easeSin] - * @memberof groupingSpacer# - * @instance - */ - easeFunc = d3.easeSin, - - /** - * The namespace for the objects being moved - * (see {@link groupingSpacer.namespace}) - * @param {string} [namespace='spacer'] - * @memberof groupingSpacer# - * @instance - */ - namespace = 'spacer', - - /** - * The animation for new objects being added - * (see {@link groupingSpacer.enterFunction}) - * @param {function} enterFunction - * @memberof groupingSpacer# - * @instance - * @example - * // by default - * function(newObjectSelection) { - * newObjectSelection.attr('transform', function(d, i){ - * var - * x = horizontalQ ? objectSize * numberOfObjects + spacerSize * (numberOfObjects - 1) : 0, - * y = !horizontalQ ? objectSize * numberOfObjects + spacerSize * (numberOfObjects - 1) : 0, - * t = 'translate('+x+','+y+')' - * return t - * }) - * } - */ - enterFunction = function enterFunction(cur) { - cur.attr('transform', function (d, i) { - var - // x = horizontalQ ? objectSize * numberOfObjects + spacerSize * (numberOfObjects - 1) : 0, - // y = !horizontalQ ? objectSize * numberOfObjects + spacerSize * (numberOfObjects - 1) : 0, - x = horizontalQ ? window.outerWidth : 0, - y = !horizontalQ ? window.outerWidth : 0, - t = 'translate(' + x + ',' + y + ')'; - // if(y == undefined) {console.log(cur.node(), y, d)} - return t; - }); - }, - - /** - * The animation for old objects being removed - * (see {@link groupingSpacer.exitFunction}) - * @param {function} exitFunction - * @memberof groupingSpacer# - * @instance - * @example - * // by default - * oldObjectSelection.transition().duration(transitionDuration).ease(easeFunc) - * .attr('transform', function(d, i){ - * var - * x = horizontalQ ? objectSize * numberOfObjects + spacerSize * (numberOfObjects - 1) : 0, - * y = !horizontalQ ? objectSize * numberOfObjects + spacerSize * (numberOfObjects - 1) : 0, - * t = 'translate('+x+','+y+')' - * return t - * }).remove() - */ - exitFunction = function exitFunction(cur) { - log("groupingSpacer", "exiting with", { current: cur, currentNode: cur.node() }); - cur.selectAll('g').classed('to-remove', true); - - cur.transition().duration(transitionDuration * 0.9).ease(easeFunc).attr('transform', function (d, i) { - var - // x = horizontalQ ? objectSize * numberOfObjects + spacerSize * (numberOfObjects - 1) : 0, - // y = !horizontalQ ? objectSize * numberOfObjects + spacerSize * (numberOfObjects - 1) : 0, - x = horizontalQ ? window.outerWidth : 0, - y = !horizontalQ ? window.outerWidth : 0, - t = 'translate(' + x + ',' + y + ')'; - // if(y == undefined) {console.log(cur.node(), y, d)} - return t; - }).remove(); - }; - - /** - * Gets / sets horizontalQ (whether or not to space objects horizontally or vertically). - * (see {@link groupingSpacer#horizontalQ}) - * @param {string} [_=none] - * @returns {groupingSpacer | string} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.horizontalQ = function (_) { - return arguments.length ? (horizontalQ = _, recursivelyPosition) : horizontalQ; - }; - /** - * Gets / sets the scale to use to position elements if {@link groupingSpacer#moveby}="string" - * (see {@link groupingSpacer#scale}) - * @param {d3.scale} [_=none] - * @returns {groupingSpacer | d3.scale} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.scale = function (_) { - return arguments.length ? (scale = _, recursivelyPosition) : scale; - }; - /** - * Gets / sets moveby (whether or not to move by scale or by grouping). - * (see {@link groupingSpacer#moveby}) - * @param {string} [_=none] - * @returns {groupingSpacer | string} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.moveby = function (_) { - return arguments.length ? (moveby = _, recursivelyPosition) : moveby; - }; - /** - * Gets / sets numberOfObjects. - * (see {@link groupingSpacer#numberOfObjects}) - * @param {number} [_=none] - * @returns {groupingSpacer | number} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.numberOfObjects = function (_) { - return arguments.length ? (numberOfObjects = _, recursivelyPosition) : numberOfObjects; - }; - /** - * Gets / sets the objectClass (will be applied to elements). - * (see {@link groupingSpacer#objectClass}) - * @param {string} [_=none] - * @returns {groupingSpacer | string} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.objectClass = function (_) { - return arguments.length ? (objectClass = _, recursivelyPosition) : objectClass; - }; - /** - * Gets / sets the objectSize. - * (see {@link groupingSpacer#objectSize}) - * @param {number} [_=none] - * @returns {groupingSpacer | number} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.objectSize = function (_) { - return arguments.length ? (objectSize = _, recursivelyPosition) : objectSize; - }; - /** - * Gets / sets the spacerSize. - * (see {@link groupingSpacer#spacerSize}) - * @param {number} [_=none] - * @returns {groupingSpacer | number} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.spacerSize = function (_) { - return arguments.length ? (spacerSize = _, recursivelyPosition) : spacerSize; - }; - /** - * Gets / sets the transitionDuration. - * (see {@link groupingSpacer#transitionDuration}) - * @param {number} [_=none] - * @returns {groupingSpacer | number} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.transitionDuration = function (_) { - return arguments.length ? (transitionDuration = _, recursivelyPosition) : transitionDuration; - }; - /** - * Gets / sets the easeFunc. - * (see {@link groupingSpacer#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {groupingSpacer | d3.ease} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.easeFunc = function (_) { - return arguments.length ? (easeFunc = _, recursivelyPosition) : easeFunc; - }; - /** - * Gets / sets the namespace. - * (see {@link groupingSpacer#namespace}) - * @param {string} [_=none] - * @returns {groupingSpacer | string} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.namespace = function (_) { - return arguments.length ? (namespace = _, recursivelyPosition) : namespace; - }; - /** - * Gets / sets the enterFunction. - * (see {@link groupingSpacer#enterFunction}) - * @param {function} [_=none] - * @returns {groupingSpacer | function} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.enterFunction = function (_) { - return arguments.length ? (enterFunction = _, recursivelyPosition) : enterFunction; - }; - /** - * Gets / sets the exitFunction. - * (see {@link groupingSpacer#exitFunction}) - * @param {function} [_=none] - * @returns {groupingSpacer | function} - * @memberof groupingSpacer - * @static - */ - recursivelyPosition.exitFunction = function (_) { - return arguments.length ? (exitFunction = _, recursivelyPosition) : exitFunction; - }; - - /** - * recursively position the objects inside of the selection. - * @param {d3.selection} selection - * @param {Object} data - * @param {level} [level=0] recursion depth - * @returns {number} (how much to move next element) - * @memberof groupingSpacer# - */ - function recursivelyPosition(selection, data, level) { - if (level == undefined) { - level = 0; - } - - var currentSelection = selection.selectAll('g.' + namespace + '[level="' + level + '"]').data(data); - var enter = currentSelection.enter().append('g').attr('level', level).attr('class', namespace); - var exit = currentSelection.exit(); - currentSelection = currentSelection.merge(enter); - - if (typeof exitFunction == 'function') { - exit.each(function (d, i) { - exitFunction(d3.select(this)); - }); - } else { - exit.remove(); - } - // spacer for current level - var levelSpacer = spacerSize / (level + 1); - // movement for current level - var move = 0; - currentSelection.each(function (currentElement, index) { - var t = d3.select(this); - if (t.attr('transform') == undefined && typeof enterFunction == 'function') { - enterFunction(t); - } - - t.transition().duration(transitionDuration).ease(easeFunc).attr('transform', function (d, i) { - var x = horizontalQ ? moveby == "scale" ? scale(d) : move : 0, - y = !horizontalQ ? moveby == "scale" ? scale(d) : move : 0, - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - - if (Array.isArray(currentElement)) { - move += recursivelyPosition(t, currentElement, level + 1); - var toRemove = t.selectAll('g.' + namespace + '[level="' + level + '"] > g.' + objectClass + '.' + namespace); - if (typeof exitFunction == 'function') { - toRemove.each(function (d, i) { - exitFunction(d3.select(this)); - }); - } else { - toRemove.remove(); - } - } else { - move += objectSize; - var obj = t.select('g.' + namespace + '[level="' + level + '"] > g.' + objectClass + '.' + namespace); - if (obj.empty()) { - obj = t.append('g').attr('class', objectClass).classed(namespace, true); - } - obj.attr('parent-index', index); - var toRemove = t.selectAll('g.' + namespace + '[level="' + (level + 1) + '"]'); - - if (typeof exitFunction == 'function') { - toRemove.each(function (d, i) { - exitFunction(d3.select(this)); - }); - } else { - toRemove.remove(); - } - } - move += index == currentSelection.size() - 1 ? 0 : levelSpacer; - }); - return move; - } - return recursivelyPosition; - } - - var slicedToArray = function () { - function sliceIterator(arr, i) { - var _arr = []; - var _n = true; - var _d = false; - var _e = undefined; - - try { - for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { - _arr.push(_s.value); - - if (i && _arr.length === i) break; - } - } catch (err) { - _d = true; - _e = err; - } finally { - try { - if (!_n && _i["return"]) _i["return"](); - } finally { - if (_d) throw _e; - } - } - - return _arr; - } - - return function (arr, i) { - if (Array.isArray(arr)) { - return arr; - } else if (Symbol.iterator in Object(arr)) { - return sliceIterator(arr, i); - } else { - throw new TypeError("Invalid attempt to destructure non-iterable instance"); - } - }; - }(); - - var toConsumableArray = function (arr) { - if (Array.isArray(arr)) { - for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; - - return arr2; - } else { - return Array.from(arr); - } - }; - - /******************************************************************************* - ** ** - ** ** - ** AXIS ** - ** ** - ** ** - *******************************************************************************/ - - /** - * Creates an axis - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/axes/index.html Demo} - * @constructor axis - * @param {d3.selection} selection - * @namespace axis - * @returns {function} axis - */ - function axis(selection) { - var - /** - * The orientation of the axis - * (see {@link axis#orient}) - * @param {string} [orient='bottom'] - * @memberof axis# - * @property - */ - orient = 'bottom', - // direction of the axis - - /** - * Amount of horizontal space (in pixels) avaible to render the axis in - * (see {@link axis#spaceX}) - * @param {number} [spaceX=0] - * @memberof axis# - * @property - */ - spaceX = 0, - - /** - * Amount of vertical space (in pixels) avaible to render the axis in - * (see {@link axis.spaceY}) - * @param {number} [spaceY=0] - * @memberof axis# - * @property - */ - spaceY = 0, - - - /** - * Whether or not to allow axis to render elements pass the main spatial dimension - * given the orientation (see {@link axis#orient}), where {@link axis#orient}="bottom" or {@link axis#orient}="top" - * the main dimension is {@link axis#spaceX} and where {@link axis#orient}="left" or {@link axis#orient}="right" - * the main dimension is {@link axis#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof axis# - * @property - */ - overflowQ = false, - // whether or not to allow overflow - /** - * Whether or not the axis labels are for categorical data. If false, - * will use {@link axis#scale} to position ticks. - * @param {boolean} [categoricalQ=false] - * @memberof axis# - * @property - */ - categoricalQ = false, - // whether or not the axis is showing values or groups - /** - * Whether or not the axis ticks should have guidelines - * @param {boolean} [categoricalQ=false] - * @memberof axis# - * @property - */ - guideLinesQ = false, - // whether or not to allow overflow - - - /** - * How to group the tick labels - * @param {Array[]} [grouping=undefined] list of putatively other lists, which should correspond to tickLabels - * will space tick labels in nested lists closer together than outer lists - * @memberof axis# - * @property - */ - grouping, - - - /** - * The scale for which non-categorial (see {@link axis#categoricalQ}) ticks should be spaced - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof axis# - * @property - */ - - scale = d3.scaleLinear(), - - /** - * The padding for the domain of the scale (see {@link axis#scale}) - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof axis# - * @property - */ - domainPadding = 0.5, - - - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link axis#orient}), where {@link axis#orient}="bottom" or {@link axis#orient}="top" - * the main dimension is {@link axis#spaceX} and where {@link axis#orient}="left" or {@link axis#orient}="right" - * the main dimension is {@link axis#spaceY}between ticks - * @param {number} [objectSpacer=0.05] - * @memberof axis# - * @property - */ - objectSpacer = 0.05, - - /** - * The minimum size that an object can be if {@link axis#categoricalQ} is set to true - * @param {number} [minObjectSize=15] - * @memberof axis# - * @property - */ - minObjectSize = 15, - - /** - * The maximum size that an object can be if {@link axis#categoricalQ} is set to true - * @param {number} [maxObjectSize=15] - * @memberof axis# - * @property - */ - maxObjectSize = 50, - - - /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof axis# - * @property - */ - backgroundFill = 'transparent', - - /** - * Namespace for all items made by this instance of axis - * @param {string} [namespace="d3sm-axis"] - * @memberof axis# - * @property - */ - namespace = 'd3sm-axis', - - /** - * Class name for tick container ( element) - * @param {string} [objectClass="tick-group"] - * @memberof axis# - * @property - */ - objectClass = 'tick-group', - - - /** - * Values to show at each tick. Only used if categoricalQ is set true. See {@link axis#categoricalQ} - * @param {string[]} [tickLabels=undefined] - * @memberof axis# - * @property - */ - tickLabels, - // what to place at ticks - /** - * Values to show at each tick. Only used if categoricalQ is set false. See {@link axis#categoricalQ} - * @param {string[] | number[]} [objectClass=undefined] - * @memberof axis# - * @property - */ - tickValues, - // where to place ticks if not - /** - * Number of ticks to display if categoricalQ is false. See {@link axis#categoricalQ} - * @param {number} [numberOfTicks=5] - * @memberof axis# - * @property - */ - numberOfTicks = 5, - - - /** - * Stroke color of the main axis line - * @param {string} [lineStroke='black'] - * @memberof axis# - * @property - */ - lineStroke = 'black', - - /** - * Stroke width of the main axis line - * @param {number} [lineStrokeWidth=3] - * @memberof axis# - * @property - */ - lineStrokeWidth = 3, - - - /** - * Stroke color of ticks - * @param {string} [tickStroke='black'] - * @memberof axis# - * @property - */ - tickStroke = 'black', - - /** - * Stroke number of ticks - * @param {string} [tickStrokeWidth=2] - * @memberof axis# - * @property - */ - tickStrokeWidth = 2, - - /** - * Length - in pixels - of ticks - * @param {number} [tickLength=10] - * @memberof axis# - * @property - */ - tickLength = 10, - tickTickLabelSpacer = 10, - tickLabelMargin = 10, - - - /** - * Font size of tick labels - * @param {number} [tickLabelFontSize=14] - * @memberof axis# - * @property - */ - tickLabelFontSize = 14, - - /** - * Min font size of tick labels - * @param {number} [tickLabelMinFontSize=8] - * @memberof axis# - * @property - */ - tickLabelMinFontSize = 8, - - /** - * Max font size of tick labels - * @param {number} [tickLabelMaxFontSize=20] - * @memberof axis# - * @property - */ - tickLabelMaxFontSize = 20, - - - /** - * Text anchor of tick labels - * @param {string} [tickLabelTextAnchor="middle"] - * @memberof axis# - * @property - */ - tickLabelTextAnchor, - - /** - * Rotation of tick labels - * @param {number} [tickLabelRotation=0] - * @memberof axis# - * @property - */ - tickLabelRotation, - - /** - * Optional function for extracting the tick label from data - * @param {function} [tickLabelFunc=undefined] - * @memberof axis# - * @property - */ - tickLabelFunc = undefined, - - - /** - * Optional function for what to do when label is clicked - * @param {function} [tickLabelOnClick=function(d, i){}] - * @memberof axis# - * @property - */ - tickLabelOnClick = function tickLabelOnClick(d, i) {}, - - - /** - * Optional function for what to do when label is hovered - * @param {function} [tickLabelOnHoverFunc=function(d, i){}] - * @memberof axis# - * @property - */ - tickLabelOnHoverFunc = function tickLabelOnHoverFunc(d, i) { - return String(d).replace('-', ' ').replace('_', ' '); - }, - - - /** - * Length of guidelines - * @param {function} [guidelineSpace=undefined] - * @memberof axis# - * @property - */ - guidelineSpace, - - /** - * Stroke color of guidlines - * @param {string} [guidelineSpace="#333333"] - * @memberof axis# - * @property - */ - guideLineStroke = '#333333', - - /** - * Stroke width of guidlines - * @param {number} [guidelineSpace=2] - * @memberof axis# - * @property - */ - guideLineStrokeWidth = 2, - - - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof axis# - * @property - */ - transitionDuration = 1000, - - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof axis# - * @property - */ - easeFunc = d3.easeExp, - - - /** - * Closure variable for getting object size after calculation - * @param {number} [objectSize=undefined] - * @memberof axis# - * @property - */ - objectSize, - - /** - * Closure variable for getting spacer size after calculation - * @param {number} [spacerSize=undefined] - * @memberof axis# - * @property - */ - spacerSize, - - - /** - * Decimal percision to round numerical tick labels to - * @param {number} [roundTo=2] - * @memberof axis# - * @property - */ - roundTo = 2, - label, - reverseScaleQ = false; - - axis.label = function (_) { - return arguments.length ? (label = _, axis) : label; - }; - axis.tickTickLabelSpacer = function (_) { - return arguments.length ? (tickTickLabelSpacer = _, axis) : tickTickLabelSpacer; - }; - axis.tickLabelMargin = function (_) { - return arguments.length ? (tickLabelMargin = _, axis) : tickLabelMargin; - }; - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {axis | d3.selection} - * @memberof axis - * @property - * by default selection = selection - */ - - axis.selection = function (_) { - return arguments.length ? (selection = _, axis) : selection; - }; - - /** - * Gets or sets the orientation in which items are manipulated - * (see {@link axis#orient}) - * @param {string} [_=none] should be horizontal or vertical - * @returns {axis | string} - * @memberof axis - * @property - * by default orient="bottom" - */ - axis.orient = function (_) { - return arguments.length ? (orient = _, axis) : orient; - }; - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link axis#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {axis | number} - * @memberof axis - * @property - * by default spaceX = undefined - */ - axis.spaceX = function (_) { - return arguments.length ? (spaceX = _, axis) : spaceX; - }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link axis#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {axis | number} - * @memberof axis - * @property - * by default spaceY = undefined - */ - axis.spaceY = function (_) { - return arguments.length ? (spaceY = _, axis) : spaceY; - }; - - /** - * Gets / sets whether or not axis is allowed to go beyond specified dimensions - * (see {@link axis#spaceX}) - * @param {boolean} [_=none] - * @returns {axis | boolean} - * @memberof axis - * @property - * by default overflowQ = false - */ - axis.overflowQ = function (_) { - return arguments.length ? (overflowQ = _, axis) : overflowQ; - }; - /** - * Gets / sets whether or not axis will display categorial ticks or by numerical value - * (see {@link axis#categoricalQ}) - * @param {boolean} [_=none] - * @returns {axis | boolean} - * @memberof axis - * @property - * by default categoricalQ = false - */ - axis.categoricalQ = function (_) { - return arguments.length ? (categoricalQ = _, axis) : categoricalQ; - }; - /** - * Gets / sets whether or not axis ticks should have guidelines - * (see {@link axis#guideLinesQ}) - * @param {boolean} [_=none] - * @returns {axis | boolean} - * @memberof axis - * @property - * by default guideLinesQ = false - */ - axis.guideLinesQ = function (_) { - return arguments.length ? (guideLinesQ = _, axis) : guideLinesQ; - }; - - /** - * Gets / sets how ticks should be groupped - * (see {@link axis#grouping}) - * @param {Array[]} [_=none] list of putatively other lists, which should correspond to tickLabels - * will space tick labels in nested lists closer together than outer lists - * @returns {axis | Array[]} - * @memberof axis - * @property - * by default grouping = undefined - */ - axis.grouping = function (_) { - return arguments.length ? (grouping = _, axis) : grouping; - }; - - /** - * Gets / sets the scale for which non-categorial ticks should - * be spaced - * (see {@link axis#scale}) - * @param {d3.scale} [_=none] - * @returns {axis | d3.scale} - * @memberof axis - * @property - * by default scale = d3.scaleLinear() - */ - axis.scale = function (_) { - return arguments.length ? (scale = _, axis) : scale; - }; - /** - * Gets / sets the padding for the domain of the scale - * (see {@link axis#domainPadding}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default domainPadding = 0.5 - */ - axis.domainPadding = function (_) { - return arguments.length ? (domainPadding = _, axis) : domainPadding; - }; - - /** - * Gets / sets objectSpacer - * (see {@link axis#objectSpacer}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default objectSpacer = 0.05 - */ - axis.objectSpacer = function (_) { - return arguments.length ? (objectSpacer = _, axis) : objectSpacer; - }; - /** - * Gets / sets the minObjectSize - * (see {@link axis#minObjectSize}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default minObjectSize = 15 - */ - axis.minObjectSize = function (_) { - return arguments.length ? (minObjectSize = _, axis) : minObjectSize; - }; - /** - * Gets / sets the maxObjectSize - * (see {@link axis#maxObjectSize}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default maxObjectSize = 50 - */ - axis.maxObjectSize = function (_) { - return arguments.length ? (maxObjectSize = _, axis) : maxObjectSize; - }; - - /** - * Gets / sets the namespace - * (see {@link axis#namespace}) - * @param {string} [_=none] - * @returns {axis | string} - * @memberof axis - * @property - * by default namespace = 'd3sm-axis' - */ - axis.namespace = function (_) { - return arguments.length ? (namespace = _, axis) : namespace; - }; - /** - * Gets / sets the backgroundFill - * (see {@link axis#backgroundFill}) - * @param {string} [_=none] - * @returns {axis | string} - * @memberof axis - * @property - * by default backgroundFill = 'transparent' - */ - axis.backgroundFill = function (_) { - return arguments.length ? (backgroundFill = _, axis) : backgroundFill; - }; - /** - * Gets / sets the objectClass - * (see {@link axis#objectClass}) - * @param {string} [_=none] - * @returns {axis | string} - * @memberof axis - * @property - * by default objectClass = 'tick-group' - */ - axis.objectClass = function (_) { - return arguments.length ? (objectClass = _, axis) : objectClass; - }; - - /** - * Gets / sets the tickLabels - * (see {@link axis#tickLabels}) - * @param {string[]} [_=none] - * @returns {axis | string[]} - * @memberof axis - * @property - * by default tickLabels = undefined - */ - axis.tickLabels = function (_) { - return arguments.length ? (tickLabels = _, axis) : tickLabels; - }; - /** - * Gets / sets the tickValues - * (see {@link axis#tickValues}) - * @param {number[]} [_=none] - * @returns {axis | number[]} - * @memberof axis - * @property - * by default tickValues = undefined - */ - axis.tickValues = function (_) { - return arguments.length ? (tickValues = _, axis) : tickValues; - }; - /** - * Gets / sets the tickValues - * (see {@link axis#numberOfTicks}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default numberOfTicks = 5 - */ - axis.numberOfTicks = function (_) { - return arguments.length ? (numberOfTicks = _, axis) : numberOfTicks; - }; - - /** - * Gets / sets the lineStroke - * (see {@link axis#lineStroke}) - * @param {string} [_=none] - * @returns {axis | string} - * @memberof axis - * @property - * by default lineStroke = 'black' - */ - axis.lineStroke = function (_) { - return arguments.length ? (lineStroke = _, axis) : lineStroke; - }; - /** - * Gets / sets the lineStrokeWidth - * (see {@link axis#lineStrokeWidth}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default lineStrokeWidth = 3 - */ - axis.lineStrokeWidth = function (_) { - return arguments.length ? (lineStrokeWidth = _, axis) : lineStrokeWidth; - }; - - /** - * Gets / sets the tickStroke - * (see {@link axis#tickStroke}) - * @param {string} [_=none] - * @returns {axis | string} - * @memberof axis - * @property - * by default tickStroke = 'black' - */ - axis.tickStroke = function (_) { - return arguments.length ? (tickStroke = _, axis) : tickStroke; - }; - /** - * Gets / sets the tickStrokeWidth - * (see {@link axis#tickStrokeWidth}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default tickStrokeWidth = 2 - */ - axis.tickStrokeWidth = function (_) { - return arguments.length ? (tickStrokeWidth = _, axis) : tickStrokeWidth; - }; - /** - * Gets / sets the tickLength - * (see {@link axis#tickLength}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default tickLength = 10 - */ - axis.tickLength = function (_) { - return arguments.length ? (tickLength = _, axis) : tickLength; - }; - - /** - * Gets / sets the tickLabelFontSize - * (see {@link axis#tickLabelFontSize}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default tickLabelFontSize = 14 - */ - axis.tickLabelFontSize = function (_) { - return arguments.length ? (tickLabelFontSize = _, axis) : tickLabelFontSize; - }; - /** - * Gets / sets the tickLabelMinFontSize - * (see {@link axis#tickLabelMinFontSize}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default tickLabelMinFontSize = 8 - */ - axis.tickLabelMinFontSize = function (_) { - return arguments.length ? (tickLabelMinFontSize = _, axis) : tickLabelMinFontSize; - }; - /** - * Gets / sets the tickLabelMaxFontSize - * (see {@link axis#tickLabelMaxFontSize}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default tickLabelMaxFontSize = 20 - */ - axis.tickLabelMaxFontSize = function (_) { - return arguments.length ? (tickLabelMaxFontSize = _, axis) : tickLabelMaxFontSize; - }; - - /** - * Gets / sets the tickLabelTextAnchor - * (see {@link axis#tickLabelTextAnchor}) - * @param {string} [_=none] - * @returns {axis | string} - * @memberof axis - * @property - * by default tickLabelTextAnchor = 'center' - */ - axis.tickLabelTextAnchor = function (_) { - return arguments.length ? (tickLabelTextAnchor = _, axis) : tickLabelTextAnchor; - }; - /** - * Gets / sets the tickLabelRotation - * (see {@link axis#tickLabelRotation}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default tickLabelRotation = 0 - */ - axis.tickLabelRotation = function (_) { - return arguments.length ? (tickLabelRotation = _, axis) : tickLabelRotation; - }; - /** - * Gets / sets the tickLabelFunc - * (see {@link axis#tickLabelFunc}) - * @param {function} [_=none] - * @returns {axis | function} - * @memberof axis - * @property - * by default tickLabelFunc = undefined - */ - axis.tickLabelFunc = function (_) { - return arguments.length ? (tickLabelFunc = _, axis) : tickLabelFunc; - }; - - /** - * Gets / sets the tickLabelOnClick - * (see {@link axis#tickLabelOnClick}) - * @param {function} [_=none] - * @returns {axis | function} - * @memberof axis - * @property - */ - axis.tickLabelOnClick = function (_) { - return arguments.length ? (tickLabelOnClick = _, axis) : tickLabelOnClick; - }; - - /** - * Gets / sets the guidelineSpace - * (see {@link axis#guidelineSpace}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default guidelineSpace = undefined - */ - axis.guidelineSpace = function (_) { - return arguments.length ? (guidelineSpace = _, axis) : guidelineSpace; - }; - /** - * Gets / sets the guideLineStroke - * (see {@link axis#guideLineStroke}) - * @param {string} [_=none] - * @returns {axis | string} - * @memberof axis - * @property - * by default guideLineStroke = "#333333" - */ - axis.guideLineStroke = function (_) { - return arguments.length ? (guideLineStroke = _, axis) : guideLineStroke; - }; - /** - * Gets / sets the guideLineStrokeWidth - * (see {@link axis#guideLineStrokeWidth}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default guideLineStrokeWidth = 2 - */ - axis.guideLineStrokeWidth = function (_) { - return arguments.length ? (guideLineStrokeWidth = _, axis) : guideLineStrokeWidth; - }; - - /** - * Gets / sets the transitionDuration - * (see {@link axis#transitionDuration}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default transitionDuration = 1000 - */ - axis.transitionDuration = function (_) { - return arguments.length ? (transitionDuration = _, axis) : transitionDuration; - }; - /** - * Gets / sets the easeFunc - * (see {@link axis#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {axis | d3.ease} - * @memberof axis - * @property - * by default easeFunc = d3.easeExp - */ - axis.easeFunc = function (_) { - return arguments.length ? (easeFunc = _, axis) : easeFunc; - }; - - /** - * Gets / sets the objectSize - * (see {@link axis#objectSize}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default objectSize = undefined - */ - axis.objectSize = function (_) { - return arguments.length ? (objectSize = _, axis) : objectSize; - }; - /** - * Gets / sets the spacerSize - * (see {@link axis#spacerSize}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default spacerSize = undefined - */ - axis.spacerSize = function (_) { - return arguments.length ? (spacerSize = _, axis) : spacerSize; - }; - - /** - * Gets / sets the roundTo - * (see {@link axis#roundTo}) - * @param {number} [_=none] - * @returns {axis | number} - * @memberof axis - * @property - * by default roundTo = 2 - */ - axis.roundTo = function (_) { - return arguments.length ? (roundTo = _, axis) : roundTo; - }; - axis.reverseScaleQ = function (_) { - return arguments.length ? (reverseScaleQ = _, axis) : reverseScaleQ; - }; - - axis.tickLabelOnHoverFunc = function (_) { - return arguments.length ? (tickLabelOnHoverFunc = _, axis) : tickLabelOnHoverFunc; - }; - - function axis() { - // for convenience in handling orientation specific values - var horizontalQ = hasQ(['top', 'bottom', 'horizontal'], orient) ? true : false; - var verticalQ = !horizontalQ; - - // background cliping rectangle - var bgcpRect = { x: 0, y: 0, width: spaceX, height: spaceY - // modify the rect based on axis orientation - };if (orient == "left") { - bgcpRect.x -= spaceX; - if (guideLinesQ) { - bgcpRect.width += guidelineSpace; - } /* these two lines increase the clipping rect to allow for text at the edge of the axis */ - bgcpRect.y -= tickLabelMaxFontSize; - bgcpRect.height += 2 * tickLabelMaxFontSize; - } - if (orient == "bottom") { - bgcpRect.y = bgcpRect.y; - if (guideLinesQ) { - bgcpRect.y -= guidelineSpace;bgcpRect.height += guidelineSpace; - } /* these two lines increase the clipping rect to allow for text at the edge of the axis */ - bgcpRect.x -= tickLabelMaxFontSize; - bgcpRect.width += 2 * tickLabelMaxFontSize; - } - if (orient == "top") { - bgcpRect.y -= spaceY; - if (guideLinesQ) { - bgcpRect.height += guidelineSpace; - } /* these two lines increase the clipping rect to allow for text at the edge of the axis */ - bgcpRect.y -= tickLabelMaxFontSize; - bgcpRect.height += 2 * tickLabelMaxFontSize; - } - if (orient == "right") { - bgcpRect.x = 0; - if (guideLinesQ) { - bgcpRect.width += guidelineSpace;bgcpRect.x -= guidelineSpace; - } /* these two lines increase the clipping rect to allow for text at the edge of the axis */ - bgcpRect.y -= tickLabelMaxFontSize; - bgcpRect.height += 2 * tickLabelMaxFontSize; - } - - var container = setupContainer(selection, namespace, bgcpRect, backgroundFill); - - // defaults for text-anchor and text rotation - if (orient == 'top') { - tickLabelTextAnchor = tickLabelTextAnchor == undefined ? 'start' : tickLabelTextAnchor; - tickLabelRotation = tickLabelRotation == undefined ? -90 : tickLabelRotation; - } - if (orient == 'bottom') { - tickLabelTextAnchor = tickLabelTextAnchor == undefined ? 'end' : tickLabelTextAnchor; - tickLabelRotation = tickLabelRotation == undefined ? -90 : tickLabelRotation; - } - if (orient == 'left') { - tickLabelTextAnchor = tickLabelTextAnchor == undefined ? 'end' : tickLabelTextAnchor; - tickLabelRotation = tickLabelRotation == undefined ? 0 : tickLabelRotation; - } - if (orient == 'right') { - tickLabelTextAnchor = tickLabelTextAnchor == undefined ? 'start' : tickLabelTextAnchor; - tickLabelRotation = tickLabelRotation == undefined ? 0 : tickLabelRotation; - } - - /* - If categorical: - -> use grouping if defined, - -> else use the labels provided - else: - if grouping undefined - and no specified number of tickes - -> make numberOfTick ticks - -> else use provided tick values - -> use grouping - */ - var tickData = categoricalQ ? grouping == undefined ? tickLabels : grouping : grouping == undefined ? numberOfTicks != undefined ? - // ? (tickValues.length < numberOfTicks) - tickRange.apply(undefined, toConsumableArray(d3.extent(tickValues)).concat([numberOfTicks])) : tickValues : grouping; - - var flatTickData = flatten(tickData); - var numberOfObjects = flatTickData.length; - var space = horizontalQ ? spaceX : spaceY; - var extent = d3.extent(flatTickData); - - if (reverseScaleQ) { - extent.reverse(); - } - var domain = reverseScaleQ ? [extent[0] + domainPadding, extent[1] - domainPadding] : [extent[0] - domainPadding, extent[1] + domainPadding]; - - scale.domain(domain).range([horizontalQ ? 0 : spaceY, horizontalQ ? spaceX : 0]); - - /* - Scales are based on the values of the chart and correspond to the spacings of the - chart. If the chart has already been rendered, these values (expensive to caluclate) can - be passed to axis to prevent recalculation. - */ - - // calculate object size if needed - objectSize = objectSize == undefined ? calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) : objectSize; - - // calculate spacer size if needed - spacerSize = spacerSize == undefined ? calculateWidthOfSpacer(flatTickData, space, objectSize, numberOfObjects, objectSpacer, overflowQ) : spacerSize; - - var objClass = hypenate(namespace, categoricalQ ? objectClass + '-categorical' : objectClass); - - var spacerFunction = groupingSpacer().horizontalQ(horizontalQ).scale(scale).moveby(categoricalQ ? 'category' : 'scale').numberOfObjects(numberOfObjects).objectClass(objClass).objectSize(objectSize).spacerSize(spacerSize).transitionDuration(transitionDuration).easeFunc(easeFunc).namespace(namespace); - - var tickEnterAnimation = function tickEnterAnimation(sel) { - var mt = scale(sel.datum()), - dist = scale(extent[1]) * 2, - k = mt < extent[1] / 2 ? 1 : -1; - k = horizontalQ ? k * -1 : k; - sel.attr('transform', function (d, i) { - var x = horizontalQ ? dist * k : 0, - y = !horizontalQ ? dist * k : 0, - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - }; - var tickExitAnimation = function tickExitAnimation(sel) { - var mt = scale(sel.datum()), - dist = scale(extent[1]) * 2, - k = mt < extent[1] / 2 ? 1 : -1; - k = horizontalQ ? k * -1 : k; - sel.transition().duration(transitionDuration).ease(easeFunc).style('opacity', 0).attr('transform', function (d, i) { - var x = horizontalQ ? dist * k : 0, - y = !horizontalQ ? dist * k : 0, - t = 'translate(' + x + ',' + y + ')'; - return t; - }).remove(); - }; - - if (!categoricalQ) { - spacerFunction.enterFunction(tickEnterAnimation); - spacerFunction.exitFunction(tickExitAnimation); - } - - // move tick containers - spacerFunction(container, tickData, 0); - - // move by for x and y needed to center categorical ticks, labels, and guidelines - function moveXBy(d, i, horizontalQ, categoricalQ, objectSize) { - return horizontalQ ? categoricalQ ? objectSize / 2 : 0 : 0; - } - - function moveYBy(d, i, verticalQ, categoricalQ, objectSize) { - return verticalQ ? categoricalQ ? objectSize / 2 : 0 : 0; - } - - var labelNameGroup = safeSelect(selection, 'g', hypenate(namespace, 'axis-name')); - - var labelElement = safeSelect(labelNameGroup, 'text', hypenate(namespace, 'name')); - if (labelElement != undefined) { - labelElement.text(label); - - if (orient == 'left' || orient == 'right') { - labelElement.attr('transform', 'rotate(-90)'); - } - - var bbox = labelElement.node().getBoundingClientRect(); - labelNameGroup.attr('transform', function (d, i) { - var x = 0, - y = 0, - t; - - if (orient == 'bottom') { - x = spaceX - bbox.width - tickLabelMargin; - y = -tickTickLabelSpacer; - } else if (orient == 'top') { - x = spaceX - bbox.width - tickLabelMargin; - x = tickLabelMargin; - y = bbox.height + tickTickLabelSpacer; - } else if (orient == 'left') { - x = bbox.width + tickTickLabelSpacer; - y = bbox.height + tickLabelMargin; - } else if (orient == 'right') { - x = -(bbox.width + tickTickLabelSpacer); - y = bbox.height + tickLabelMargin; - } - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - } else { - labelElement.remove(); - } - /* - Idea from Stack Overflow - https://stackoverflow.com/questions/50579535/d3-js-v4-truncate-text-to-fit-in-fixed-space/50585022?noredirect=1#comment88235562_50585022 - to use clip path to make things fit in fixed size. Have yet got this to work nicely. - */ - // var defs = d3.select(container.node().parentNode).select('defs') - // var tickLabelClipPath = safeSelect(defs, 'clipPath', hypenate(namespace,'tick-label-clip-path')).attr('id', hypenate(namespace,'tick-label-clip-path')) - // var tickLabelClipPathRect = safeSelect(tickLabelClipPath, 'rect', hypenate(namespace,'tick-label-clip-path-rect')) - // .attr('x', 0) - // .attr('y', 0) - // .attr('width', function(d, i){ - // if (horizontalQ) { return tickLabelFontSize } - // if (verticalQ) { return spaceX - tickLength } - // }) - // .attr('height', function(d, i){ - // if (verticalQ) { return tickLabelFontSize } - // if (horizontalQ) { return spaceY - tickLength } - // }) - - - // for each tick container - var ticks = container.selectAll('g:not(.to-remove).' + objClass).each(function (d, i) { - var that = d3.select(this).style('opacity', 1); - - // make and move tick - var tick = safeSelect(that, 'line', hypenate(namespace, 'tick')).attr("x1", 0).attr("x2", horizontalQ ? 0 : orient == "left" ? -tickLength : tickLength).attr("y1", 0).attr('y2', verticalQ ? 0 : orient == "top" ? -tickLength : tickLength).attr('stroke', tickStroke).attr('stroke-width', tickStrokeWidth).attr('transform', function (d, i) { - var x = moveXBy(d, i, horizontalQ, categoricalQ, objectSize), - y = moveYBy(d, i, verticalQ, categoricalQ, objectSize), - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - - // make and move label - var label = safeSelect(that, 'text', hypenate(namespace, 'label')).text(function (d, i) { - var s = typeof d == 'number' ? round(d, roundTo) : d; - s = truncateString(String(s), (horizontalQ ? spaceY : spaceX) - tickLength - tickLabelMargin - tickTickLabelSpacer, tickLabelFontSize * 0.45); - return s; - }).attr('font-size', tickLabelFontSize).attr('text-anchor', tickLabelTextAnchor); - // truncateText(label, label.text(), orient, tickLength, horizontalQ ? spaceY : spaceX, overflowQ) - - label.attr('transform', function (d, i) { - var rect = d3.select(this).node().getBoundingClientRect(), - leng = d3.select(this).node().getComputedTextLength(), - x = moveXBy(d, i, horizontalQ, categoricalQ, objectSize), - y = moveYBy(d, i, verticalQ, categoricalQ, objectSize); - - if (orient == 'top') { - y = -(tickLength + tickTickLabelSpacer); - // y = tickLength+tickTickLabelSpacer; - - // y -= Math.max(rect.height, rect.width); - x += Math.min(rect.height, rect.width) * 0.25; - // x -= leng * 0.25 + s - } - if (orient == 'bottom') { - y = tickLength + tickTickLabelSpacer; - x += Math.min(rect.height, rect.width) * 0.25; - // x += leng * 0.25 - s - } - if (orient == 'left') { - x -= tickLength + tickTickLabelSpacer; - // y += rect.height * 0.5; y-= rect.height/4 - y += Math.min(rect.height, rect.width) * 0.25; - // y += leng * 0.25 - } - if (orient == 'right') { - x += tickLength + tickTickLabelSpacer; - // y += Math.min(rect.height, rect.width) * 0.25 - // y += leng * 0.25 - y += rect.height * 0.5;y -= rect.height / 4; - } - - var t = 'translate(' + x + ',' + y + ')', - r = 'rotate(' + tickLabelRotation + ')'; - return t + r; - }).on('mousemove', labelHover).on('mouseout', labelHoverOff).on('click', tickLabelOnClick); - // .attr('clip-path', 'url(#'+hypenate(namespace,'tick-label-clip-path')+')') - - // add guidlines as needed - if (guideLinesQ) { - var gline = safeSelect(that, 'line', hypenate(namespace, 'guideline')).transition().duration(transitionDuration).ease(easeFunc).attr("x1", 0).attr("x2", horizontalQ ? 0 : orient == "left" ? guidelineSpace : -guidelineSpace).attr("y1", 0).attr('y2', verticalQ ? 0 : orient == "top" ? guidelineSpace : -guidelineSpace).attr('transform', function (d, i) { - var x = moveXBy(d, i, horizontalQ, categoricalQ, objectSize), - y = moveYBy(d, i, verticalQ, categoricalQ, objectSize), - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - } else { - that.select('line.' + hypenate(namespace, 'guideline')).remove(); - } - }); - - // apply alternating guidline thickness - if (guideLinesQ) { - container.selectAll('.' + hypenate(namespace, 'guideline')).attr('stroke', function (d, i) { - if (i % 2 == 0) { - return modifyHexidecimalColorLuminance(guideLineStroke, 0.8); - } - return guideLineStroke; - }).attr('stroke-width', function (d, i) { - if (i % 2 == 0) { - return guideLineStrokeWidth * 0.8; - } - return guideLineStrokeWidth; - }).attr('minor', function (d, i) { - return i % 2 == 0; - }); - } - - /*************************************************************************** - ** Make the line of the axis - ***************************************************************************/ - var line = safeSelect(selection, 'path', hypenate(namespace, 'line')) - // .attr('x1', 0) - // .attr('x2', horizontalQ ? spaceX : 0) - // .attr('y1', 0) - // .attr('y2', horizontalQ ? 0 : spaceY) - .attr('d', horizontalQ ? 'M 0,0 H' + spaceX + ',0' : 'M 0,0 V 0,' + spaceY).attr('stroke', lineStroke).attr('stroke-width', lineStrokeWidth).classed('axis-line', true); - } - - // hover of label show full text label in case it is truncated - function labelHover(d, i) { - var t = d3.select(this).style('fill', 'red'); - d3.select(t.node().parentNode).select("line." + hypenate(namespace, 'tick')).attr("stroke", 'red').attr("stroke-width", tickStrokeWidth * 2); - - if (guideLinesQ) { - d3.select(t.node().parentNode).select('line.' + hypenate(namespace, 'guideline')).attr('stroke', 'red').attr('stroke-width', guideLineStrokeWidth * 2); - } - - var s = typeof d == 'number' ? round(d, roundTo) : d; - - var m = d3.mouse(d3.select('html').node()); - var div = safeSelect(d3.select('body'), 'div', hypenate(namespace, 'guideline-tooltip')).attr('id', hypenate(namespace, 'guideline-tooltip')).style('position', 'absolute').style('left', d3.event.pageX + 15 + 'px').style('top', d3.event.pageY + 15 + 'px').style('background-color', 'white').style('border-color', 'black') - // .style('min-width', (tickLabelFontSize * (String(s).split('.')[0].length+3))+'px') - // .style('min-height', (tickLabelFontSize * (String(s).split('.')[0].length+3))+'px') - .style('border-radius', '10px').style('display', 'flex').style('justify-content', 'center').style('text-align', 'middle').style('padding', 4 + "px").style('border-style', 'solid').style('border-width', 2); - - var text = safeSelect(div, 'div').text(tickLabelOnHoverFunc(s, i)).style('color', 'black').style('align-self', 'center'); - - var bbox = div.node().getBoundingClientRect(); - if (bbox.x + bbox.width > window.innerWidth) { - div.style('left', d3.event.pageX - 15 - 300 + 'px'); - } - } - - function labelHoverOff(d, i) { - var t = d3.select(this).style('fill', 'black'); - d3.select(t.node().parentNode).select("line." + hypenate(namespace, 'tick')).attr("stroke", tickStroke).attr("stroke-width", tickStrokeWidth); - - if (guideLinesQ) { - var gline = d3.select(t.node().parentNode).select('line.' + hypenate(namespace, 'guideline')); - var minorQ = gline.attr('minor'); - gline.attr('stroke', function (d, ii) { - if (minorQ == 'true') { - return modifyHexidecimalColorLuminance(guideLineStroke, 0.8); - } - return guideLineStroke; - }).attr('stroke-width', function (d, ii) { - if (minorQ == 'true') { - return guideLineStrokeWidth * 0.8; - } - return guideLineStrokeWidth; - }); - } - d3.select("#" + hypenate(namespace, 'guideline-tooltip')).remove(); - } - - return axis; - } - - /** - * Creates a colorFunction - * @constructor colorFunction - * @namespace colorFunction - * @returns {function} colorFunction - */ - function colorFunction() { - var /** - * Default colors to use - * @param {number[]} [colors=["#2c7bb6", "#00a6ca", "#00ccbc", "#90eb9d", "#ffff8c", "#f9d057", "#f29e2e", "#e76818", "#d7191c"]] - * @memberof colorFunction# - * @property - */ - colors = ["#2c7bb6", "#00a6ca", "#00ccbc", "#90eb9d", "#ffff8c", "#f9d057", "#f29e2e", "#e76818", "#d7191c"], - - /** - * Interpolator for colors - * @param {d3.interpolation} [interpolation=d3.interpolateRgb] - * @memberof colorFunction# - * @property - */ - interpolation = d3.interpolateRgb, - - /** - * Function for modifying color luminance - * @param {function} [modifyOpacity=modifyHexidecimalColorLuminance] - * @memberof colorFunction# - * @property - */ - modifyOpacity = modifyHexidecimalColorLuminance, - - /** - * How to modify color for stroke - * @param {number} [strokeOpacity=0] - * @memberof colorFunction# - * @property - */ - strokeOpacity = 0, - - /** - * How to modify color for fill - * @param {number} [fillOpacity=0.4] - * @memberof colorFunction# - * @property - */ - fillOpacity = 0.4, - - /** - * How to determine the color to use - * @param {string} [colorBy='index'] - * @memberof colorFunction# - * @property - */ - colorBy = 'index', - - /** - * Sets the scale for interpolating the colors - * @param {number[]} [dataExtent=[0, colors.length - 1]] - * @memberof colorFunction# - * @property - */ - dataExtent = [0, colors.length - 1], - - /** - * Extracts the value to color by - * @param {function} [valueExtractor=function(k, v, i) {return v}] - * @memberof colorFunction# - * @property - */ - valueExtractor = function valueExtractor(k, v, i) { - return v; - }, - - - /** - * Extracts the category to color by - * @param {function} [categoryExtractor=function(k, v, i) {return v.category}] - * @memberof colorFunction# - * @property - */ - categoryExtractor = function categoryExtractor(k, v, i) { - return v.category; - }, - - - /** - * The different type of categories of which to color by - * @param {string[]} [categories=undefined] - * @memberof colorFunction# - * @property - */ - categories, - - - /** - * Scale for interpolating the colors - * @param {d3.scale} [scale=d3.scaleLinear()] - * @memberof colorFunction# - * @property - */ - scale = d3.scaleLinear().interpolate(interpolation).domain(dataExtent).range(colors), - helperScale = d3.scaleLinear(); - - // var h = x => '#' + x.match(/\d+/g).map(y = z => ((+z < 16)?'0':'') + (+z).toString(16)).join(''); - var h = function h(x) { - return "#" + x.match(/\d+/g).map(function (y, i) { - return (+y < 16 ? '0' : '') + (+y).toString(16); - }).join(''); - }; - - /** - * Gets or sets the default colors - * (see {@link colorFunction#colors}) - * @param {number[]} [_=none] - * @returns {colorFunction | number[]} - * @memberof colorFunction - * @property - */ - colorFunction.colors = function (_) { - return arguments.length ? (colors = _, scale.range(colors), colorFunction) : colors; - }; - /** - * Gets or sets the function for interpolating the colors - * (see {@link colorFunction#interpolation}) - * @param {d3.interpolation} [_=none] - * @returns {colorFunction | d3.interpolation} - * @memberof colorFunction - * @property - */ - colorFunction.interpolation = function (_) { - return arguments.length ? (interpolation = _, scale.interpolate(interpolation).range(colors), colorFunction) : interpolation; - }; - /** - * Gets or sets the values for the scale which transforms the value to a color - * (see {@link colorFunction#dataExtent}) - * @param {number[]} [_=none] - * @returns {colorFunction | number[]} - * @memberof colorFunction - * @property - */ - colorFunction.dataExtent = function (_) { - return arguments.length ? (dataExtent = _, scale.domain(dataExtent).interpolate(scale.interpolate()), colorFunction) : dataExtent; - }; - /** - * Gets or sets the vthe scale which transforms the value to a color - * (see {@link colorFunction#scale}) - * @param {d3.scale} [_=none] - * @returns {colorFunction | d3.scale} - * @memberof colorFunction - * @property - */ - colorFunction.scale = function (_) { - return arguments.length ? (_ = _.domain(scale.domain()).interpolate(scale.interpolate()).range(scale.range()), scale = _, colorFunction) : scale; - }; - /** - * Gets or sets the function for modify opacity - * (see {@link colorFunction#modifyOpacity}) - * @param {function} [_=none] - * @returns {colorFunction | function} - * @memberof colorFunction - * @property - */ - colorFunction.modifyOpacity = function (_) { - return arguments.length ? (modifyOpacity = _, colorFunction) : modifyOpacity; - }; - /** - * Gets or sets the value to modify the color for the stroke via {@link colorFunction#modifyOpacity} - * (see {@link colorFunction#strokeOpacity}) - * @param {number} [_=none] - * @returns {colorFunction | number} - * @memberof colorFunction - * @property - */ - colorFunction.strokeOpacity = function (_) { - return arguments.length ? (strokeOpacity = _, colorFunction) : strokeOpacity; - }; - /** - * Gets or sets the value to modify the color for the stroke via {@link colorFunction#fillOpacity} - * (see {@link colorFunction#fillOpacity}) - * @param {number} [_=none] - * @returns {colorFunction | number} - * @memberof colorFunction - * @property - */ - colorFunction.fillOpacity = function (_) { - return arguments.length ? (fillOpacity = _, colorFunction) : fillOpacity; - }; - /** - * Gets or sets the value to colorBy - * (see {@link colorFunction#colorBy}) - * @param {string} [_=none] - * @returns {colorFunction | string} - * @memberof colorFunction - * @property - */ - colorFunction.colorBy = function (_) { - return arguments.length ? (colorBy = _, colorFunction) : colorBy; - }; - /** - * Gets or sets the value of valueExtractor - * (see {@link colorFunction#valueExtractor}) - * @param {function} [_=none] - * @returns {colorFunction | function} - * @memberof colorFunction - * @property - */ - colorFunction.valueExtractor = function (_) { - return arguments.length ? (valueExtractor = _, colorFunction) : valueExtractor; - }; - - /** - * Gets or sets the value of categoryExtractor - * (see {@link colorFunction#categoryExtractor}) - * @param {function} [_=none] - * @returns {colorFunction | function} - * @memberof colorFunction - * @property - */ - colorFunction.categoryExtractor = function (_) { - return arguments.length ? (categoryExtractor = _, colorFunction) : categoryExtractor; - }; - /** - * Gets or sets the value of categoryExtractor - * (see {@link colorFunction#categories}) - * @param {string[]} [_=none] - * @returns {colorFunction | string[]} - * @memberof colorFunction - * @property - */ - colorFunction.categories = function (_) { - return arguments.length ? (categories = _, colorFunction) : categories; - }; - - function colorFunction(key, value, index, type, hoverQ) { - var c, - opac = type == "fill" ? fillOpacity : strokeOpacity; - - updateScale(); - - if (colorBy == "index") { - c = type != undefined ? modifyOpacity(h(scale(index)), opac) : h(scale(index)); - } else if (colorBy == 'value') { - var v = valueExtractor(key, value, index); - // if (v < dataExtent[0]) {dataExtent[0] = v; updateScale()} - // if (v > dataExtent[1]) {dataExtent[1] = v; updateScale()} - - c = type != undefined ? modifyOpacity(h(scale(v)), opac) : h(scale(v)); - } else if (colorBy == 'category') { - var cat = categoryExtractor(key, value, index); - var v = categories.indexOf(cat); - c = type != undefined ? modifyOpacity(h(scale(v)), opac) : h(scale(v)); - } else { - c = type != undefined ? modifyOpacity(h(scale(index)), opac) : h(scale(index)); - } - - return c; - } - - function updateScale() { - - helperScale.domain([0, colors.length]); - if (colorBy == 'category' && categories != undefined) { - helperScale.range([0, categories.length]); - } else { - helperScale.range(dataExtent); - } - - var a = Array(colors.length).fill(0).map(function (d, i) { - return helperScale(i); - }); - scale.domain(a); - } - - return colorFunction; - } - - /******************************************************************************* - ** ** - ** ** - ** TOOLTIP ** - ** ** - ** ** - *******************************************************************************/ - /** - * Produces a function for handling the tooltip - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/tooltip-design/index.html Demo} - * @param {d3.selection} selection - * @returns {tooltip} - * @namespace tooltip - */ - function tooltip(selection) { - - var keys, values, header, data, selection; - - /** - * Gets / sets the keys to be displayed in the tooltip. - * If not set, uses d3.keys(data[key]) - * @param {string[]} [_=none] - * @returns {tooltip | string[]} - * @memberof tooltip - */ - tooltip.keys = function (_) { - return arguments.length ? (keys = _, tooltip) : keys; - }; - /** - * Gets / sets the values to be displayed next to the keys. - * If not set, uses data[key][keys[i]]. - * If a function, gets passed currentData (data[key]) and keys[i]. - * @param {*[]} [_=none] - * @returns {tooltip | *[]} - * @memberof tooltip - */ - tooltip.values = function (_) { - return arguments.length ? (values = _, tooltip) : values; - }; - /** - * Gets / sets the header to be displayed in the tooltip. - * If not set, uses key - * @param {string} [_=none] - * @returns {tooltip | string} - * @memberof tooltip - */ - tooltip.header = function (_) { - return arguments.length ? (header = _, tooltip) : header; - }; - /** - * Gets / sets the data (over the selection) to be used for the tooltip - * @param {Object} [_=none] - * @returns {tooltip | Object} - * @memberof tooltip - */ - tooltip.data = function (_) { - return arguments.length ? (data = _, tooltip) : data; - }; - /** - * Gets / sets the selection for the tooltip to be applied on - * @param {d3.selection} [_=none] - * @returns {tooltip | d3.selection} - * @memberof tooltip - */ - tooltip.selection = function (_) { - return arguments.length ? (selection = _, tooltip) : selection; - }; - - /** - * Bind, via selection.on(), the mousemove and mouseout events - * @returns undefined - */ - function tooltip() { - selection.on('mouseover', mousemove); - selection.on('mousemove', mousemove); - selection.on('mouseout', function () { - d3.selectAll(".d3sm-tooltip").remove(); - }); - } - - /** - * Produces the tooltip on mousemove - * @param {string} key of the object targeted by the mousemove - * @param {number} i (index) of the object targeted by mousemove - * @memberof tooltip - * @private - */ - function mousemove(key, i) { - consoleGroup('d3sm-tooltip'); - var currentData = data[key]; - - var _d3$mouse = d3.mouse(d3.select("html").node()), - _d3$mouse2 = slicedToArray(_d3$mouse, 2), - x = _d3$mouse2[0], - y = _d3$mouse2[1]; - - log('tooltip', 'mousemove detected', { key: key, index: i, x: x, y: y }); - log('tooltip', 'current data', currentData); - - var div = safeSelect(d3.select('html'), 'tooltip', 'd3sm-tooltip').classed('card', true).style('max-width', '300px').style('background-color', "#212529").style('color', 'white'); - - var cardBody = safeSelect(div, 'div', 'card-body'); - var cardTitle = safeSelect(cardBody, 'h5', 'card-title').text(header == undefined ? key : typeof header == 'function' ? header(key, currentData, i) : header).style('color', 'cyan'); - - var table = safeSelect(cardBody, 'table', 'table').classed('table-dark', true); - var tBody = safeSelect(table, 'tbody'); - - tBody = tBody.selectAll('tr'); - tBody = tBody.data(keys == undefined ? d3.keys(currentData) : keys); - tBody.exit().remove(); - - var tr = tBody.enter().append('tr').style('max-width', '300px'); - tr.append('td').attr('class', function (d, i) { - return 'tooltip-key'; - }); - tr.append('td').attr('class', function (d, i, j) { - return 'tooltip-value'; - }).attr('tooltip-row-index', function (d, i) { - return i; - }); - - // tBody = tBody.merge(tr) - consoleGroup('tooltip-rows'); - tBody.selectAll('.tooltip-key').text(function (d, i) { - return d; - }); - tBody.selectAll('tr .tooltip-value').text(function (d, i) { - log('tooltip', 'trying to set value', { rowKey: d, rowIndex: i }); - var i = d3.select(this).attr('tooltip-row-index'); - var v = currentData[d]; - - if (values != undefined) { - v = values[i];if (typeof v == "function") { - v = v(currentData, d); - } - } - return typeof v == 'number' ? round(v, 5) : v; - }); - consoleGroupEnd(); - consoleGroupEnd(); - - x += 15; - // x += 15 - var bbox = div.node().getBoundingClientRect(); - if (x + bbox.width > window.innerWidth - window.scrollX) { - x = d3.event.pageX - bbox.width - 15; - } - if (y + bbox.height > window.innerHeight - window.scrollY) { - y = d3.event.pageY - bbox.height - 15; - } - div.style('position') == "relative" ? div.style('position', 'absolute').style('left', x + 'px').style('top', y + 'px') : div.style('left', x + 'px').style('top', y + 'px'); - // .transition().duration(200).ease(d3.easeSin) - - // if (bbox.x + bbox.width > window.innerWidth) { - // div.style('left', (d3.event.pageX-15-bbox.width)+'px') - // } - // if (bbox.y + bbox.height > window.innerHeight) { - // div.style('top', (d3.event.pageY-15-bbox.height)+'px') - // } - - div.attr('z-index', 10000); - } - - return tooltip; - } - - /******************************************************************************* - ** ** - ** ** - ** BAR ** - ** ** - ** ** - *******************************************************************************/ - - /** - * Creates a bar - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/bar-chart-same-data-complex-grouping/index.html Demo} - * @constructor bar - * @param {d3.selection} selection - * @namespace bar - * @returns {function} bar - */ - function bar(selection) { - /* - Assumes that data is list an object. - The keys of data will be bound to the bars. - The valueExtractor function will extract the value from data[key]. - Grouping can be used if desired. It should be an arbitrary complex list where - the values are strings matching keys in data. - */ - var - /** - * Data to plot. Assumed to be a object, where each key corresponds to a bar - * (see {@link bar#data}) - * @param {Object} [data=undefined] - * @memberof bar# - * @property - */ - data, - - /** - * Which direction to render the bars in - * (see {@link bar#orient}) - * @param {number} [orient='horizontal'] - * @memberof bar# - * @property - */ - orient = 'horizontal', - - /** - * Amount of horizontal space (in pixels) avaible to render the bar in - * (see {@link bar#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof bar# - * @property - */ - spaceX, - - /** - * Amount of vertical space (in pixels) avaible to render the bar in - * (see {@link bar.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof bar# - * @property - */ - spaceY, - - - /** - * Whether or not to allow bar to render elements pass the main spatial dimension - * given the orientation (see {@link bar#orient}), where {@link bar#orient}="horizontal" - * the main dimension is {@link bar#spaceX} and where {@link bar#orient}="vertical" - * the main dimension is {@link bar#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof bar# - * @property - */ - overflowQ = false, - - - /** - * An array - putatively of other arrays - depicting how bars should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof bar# - * @property - */ - grouping, - - - /** - * How to get the value of the bar - * @param {function} [valueExtractor=function(key, index) { return data[key] }] - * @memberof bar# - * @property - */ - valueExtractor = function valueExtractor(key, index) { - return data[key]; - }, - - /** - * How to sort the bars - if {@link bar#grouping} is not provided. - * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] - * @memberof bar# - * @property - */ - sortingFunction = function sortingFunction(keyA, keyB) { - return d3.descending(data[keyA], data[keyB]); - }, - - - /** - * The scale for which bar values should be transformed by - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof bar# - * @property - */ - scale = d3.scaleLinear(), - - /** - * The padding for the domain of the scale (see {@link bar#scale}) - * @param {number} [domainPadding=0.5] - * @memberof bar# - * @property - */ - domainPadding = 0.5, - - - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link bar#orient}), where {@link bar#orient}="horizontal" - * the main dimension is {@link bar#spaceX} and where {@link bar#orient}="vertical" - * the main dimension is {@link bar#spaceY} between bars - * @param {number} [objectSpacer=0.05] - * @memberof bar# - * @property - */ - objectSpacer = 0.05, - - /** - * The minimum size that an object can be - * @param {number} [minObjectSize=50] - * @memberof bar# - * @property - */ - minObjectSize = 50, - - /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof bar# - * @property - */ - maxObjectSize = 100, - - - /** - * The stroke width of the bars - * @param {number} [barStrokeWidth=2] - * @memberof bar# - * @property - */ - barStrokeWidth = 2, - - /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof bar# - * @property - */ - colorFunction$$1 = colorFunction(), - - - /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof bar# - * @property - */ - backgroundFill = 'transparent', - - /** - * Namespace for all items made by this instance of bar - * @param {string} [namespace="d3sm-bar"] - * @memberof bar# - * @property - */ - namespace = 'd3sm-bar', - - /** - * Class name for bar container ( element) - * @param {string} [objectClass="bar"] - * @memberof bar# - * @property - */ - objectClass = 'bar', - - - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof bar# - * @property - */ - transitionDuration = 1000, - - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof bar# - * @property - */ - easeFunc = d3.easeExp, - - - // useful values to extract to prevent re-calculation - /** - * The keys of the bars - * @param {string[]} [barKeys=undefined] - * @memberof bar# - * @property - */ - barKeys, - - /** - * The values of the bars - * @param {number[]} [barValues=undefined] - * @memberof bar# - * @property - */ - barValues, - - /** - * The objectSize (actual width) used by the bars - * @param {number} [objectSize=undefined] - * @memberof bar# - * @property - */ - objectSize, - - /** - * The spacerSize (actual width) used by the spacers between the bars - * @param {number} [spacerSize=undefined] - * @memberof bar# - * @property - */ - spacerSize, - - /** - * Instance of Tooltip - * @param {function} [tooltip=tooltip()] - * @memberof bar# - * @property - */ - tooltip$$1 = tooltip(), - barPercent = 1; - - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {bar | d3.selection} - * @memberof bar - * @property - * by default selection = selection - */ - bar.selection = function (_) { - return arguments.length ? (selection = _, bar) : selection; - }; - /** - * Gets or sets the data - * (see {@link bar#data}) - * @param {number} [_=none] - * @returns {bar | object} - * @memberof bar - * @property - */ - bar.data = function (_) { - return arguments.length ? (data = _, bar) : data; - }; - /** - * Gets or sets the orient of the bars - * (see {@link bar#orient}) - * @param {number} [_=none] - * @returns {bar | object} - * @memberof bar - * @property - */ - bar.orient = function (_) { - return arguments.length ? (orient = _, bar) : orient; - }; - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link bar#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {bar | number} - * @memberof bar - * @property - * by default spaceX = undefined - */ - bar.spaceX = function (_) { - return arguments.length ? (spaceX = _, bar) : spaceX; - }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link bar#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {bar | number} - * @memberof bar - * @property - * by default spaceY = undefined - */ - bar.spaceY = function (_) { - return arguments.length ? (spaceY = _, bar) : spaceY; - }; - - /** - * Gets / sets whether or not bar is allowed to go beyond specified dimensions - * (see {@link bar#spaceX}) - * @param {boolean} [_=none] - * @returns {bar | boolean} - * @memberof bar - * @property - * by default overflowQ = false - */ - bar.overflowQ = function (_) { - return arguments.length ? (overflowQ = _, bar) : overflowQ; - }; - /** - * Gets / sets the grouping of the bars - * (see {@link bar#grouping}) - * @param {Array[]} [_=none] - * @returns {bar | Array[]} - * @memberof bar - * @property - * by default grouping = undefined - */ - bar.grouping = function (_) { - return arguments.length ? (grouping = _, bar) : grouping; - }; - /** - * Gets / sets the valueExtractor - * (see {@link bar#valueExtractor}) - * @param {function} [_=none] - * @returns {bar | function} - * @memberof bar - * @property - * by default valueExtractor = function(key, index) { return data[key] }, - */ - bar.valueExtractor = function (_) { - return arguments.length ? (valueExtractor = _, bar) : valueExtractor; - }; - /** - * Gets / sets the sortingFunction - * (see {@link bar#sortingFunction}) - * @param {function} [_=none] - * @returns {bar | function} - * @memberof bar - * @property - * by default sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, - */ - bar.sortingFunction = function (_) { - return arguments.length ? (sortingFunction = _, bar) : sortingFunction; - }; - /** - * Gets / sets the scale for which the bar values should be transformed by - * (see {@link bar#scale}) - * @param {d3.scale} [_=none] - * @returns {bar | d3.scale} - * @memberof bar - * @property - * by default scale = d3.scaleLinear() - */ - bar.scale = function (_) { - return arguments.length ? (scale = _, bar) : scale; - }; - /** - * Gets / sets the padding for the domain of the scale - * (see {@link bar#domainPadding}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar - * @property - * by default domainPadding = 0.5 - */ - bar.domainPadding = function (_) { - return arguments.length ? (domainPadding = _, bar) : domainPadding; - }; - /** - * Gets / sets objectSpacer - * (see {@link bar#objectSpacer}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar - * @property - * by default objectSpacer = 0.05 - */ - bar.objectSpacer = function (_) { - return arguments.length ? (objectSpacer = _, bar) : objectSpacer; - }; - /** - * Gets / sets the minObjectSize - * (see {@link bar#minObjectSize}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar - * @property - * by default minObjectSize = 50 - */ - bar.minObjectSize = function (_) { - return arguments.length ? (minObjectSize = _, bar) : minObjectSize; - }; - /** - * Gets / sets the maxObjectSize - * (see {@link bar#maxObjectSize}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar - * @property - * by default maxObjectSize = 100 - */ - bar.maxObjectSize = function (_) { - return arguments.length ? (maxObjectSize = _, bar) : maxObjectSize; - }; - - /** - * Gets / sets the barStrokeWidth - * (see {@link bar#barStrokeWidth}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar - * @property - * by default barStrokeWidth = 2 - */ - bar.barStrokeWidth = function (_) { - return arguments.length ? (barStrokeWidth = _, bar) : barStrokeWidth; - }; - /** - * Gets / sets the colorFunction - * (see {@link bar#colorFunction}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar - * @property - * by default colorFunction = colorFunction() - */ - bar.colorFunction = function (_) { - return arguments.length ? (colorFunction$$1 = _, bar) : colorFunction$$1; - }; - - /** - * Gets / sets the backgroundFill - * (see {@link bar#backgroundFill}) - * @param {string} [_=none] - * @returns {bar | string} - * @memberof bar - * @property - * by default backgroundFill = 'transparent' - */ - bar.backgroundFill = function (_) { - return arguments.length ? (backgroundFill = _, bar) : backgroundFill; - }; - /** - * Gets / sets the namespace - * (see {@link bar#namespace}) - * @param {string} [_=none] - * @returns {bar | string} - * @memberof bar - * @property - * by default namespace = 'd3sm-bar' - */ - bar.namespace = function (_) { - return arguments.length ? (namespace = _, bar) : namespace; - }; - /** - * Gets / sets the objectClass - * (see {@link bar#objectClass}) - * @param {string} [_=none] - * @returns {bar | string} - * @memberof bar - * @property - * by default objectClass = 'tick-group' - */ - bar.objectClass = function (_) { - return arguments.length ? (objectClass = _, bar) : objectClass; - }; - /** - * Gets / sets the transitionDuration - * (see {@link bar#transitionDuration}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar - * @property - * by default transitionDuration = 1000 - */ - bar.transitionDuration = function (_) { - return arguments.length ? (transitionDuration = _, bar) : transitionDuration; - }; - /** - * Gets / sets the easeFunc - * (see {@link bar#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {bar | d3.ease} - * @memberof bar - * @property - * by default easeFunc = d3.easeExp - */ - bar.easeFunc = function (_) { - return arguments.length ? (easeFunc = _, bar) : easeFunc; - }; - - /** - * Gets / sets the barKeys - * (see {@link bar#barKeys}) - * @param {string[]} [_=none] - * @returns {bar | string[]} - * @memberof bar - * @property - * by default barKeys = undefined - */ - bar.barKeys = function (_) { - return arguments.length ? (barKeys = _, bar) : barKeys; - }; - /** - * Gets / sets the barValues - * (see {@link bar#barValues}) - * @param {number[]} [_=none] - * @returns {bar | number[]} - * @memberof bar - * @property - * by default barValues = undefined - */ - bar.barValues = function (_) { - return arguments.length ? (barValues = _, bar) : barValues; - }; - /** - * Gets / sets the objectSize - * (see {@link bar#objectSize}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar - * @property - * by default objectSize = undefined - */ - bar.objectSize = function (_) { - return arguments.length ? (objectSize = _, bar) : objectSize; - }; - /** - * Gets / sets the spacerSize - * (see {@link bar#spacerSize}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar - * @property - * by default spacerSize = undefined - */ - bar.spacerSize = function (_) { - return arguments.length ? (spacerSize = _, bar) : spacerSize; - }; - - /** - * Gets / sets the tooltip - * (see {@link bar#tooltip}) - * @param {tooltip} [_=none] - * @returns {bar | tooltip} - * @memberof bar - * @property - * by default tooltip = tooltip() - */ - bar.tooltip = function (_) { - return arguments.length ? (tooltip$$1 = _, bar) : tooltip$$1; - }; - - bar.barPercent = function (_) { - return arguments.length ? (barPercent = _, bar) : barPercent; - }; - - function bar() { - // for convenience in handling orientation specific values - var horizontalQ = orient == 'horizontal' || orient == 'bottom' || orient == 'top' ? true : false; - var verticalQ = !horizontalQ; - - // background cliping rectangle - var bgcpRect = { x: 0, y: 0, width: spaceX, height: spaceY }; - var container = setupContainer(selection, namespace, bgcpRect, backgroundFill); - - // to prevent re-calculation and getters to be passed to axes - barKeys = d3.keys(data); - barValues = barKeys.map(valueExtractor); - - // if grouping is undefined sort barKeys by sortingFunction - var ordered = grouping == undefined ? barKeys.sort(sortingFunction) : grouping; - // ordered might be nested depending on grouping - barKeys = flatten(ordered); - - var numberOfObjects = barKeys.length; - var extent = [Math.min.apply(Math, toConsumableArray(barValues)) - domainPadding, Math.max.apply(Math, toConsumableArray(barValues)) + domainPadding]; - - // set the scale - - scale.domain(extent).range(horizontalQ ? [0, spaceY] : orient == 'right' ? [0, spaceX] : [spaceX, 0]); - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - objectSize = objectSize == undefined ? calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) : objectSize; - - // calculate spacer size if needed - spacerSize = spacerSize == undefined ? calculateWidthOfSpacer(barKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) : spacerSize; - // make the nested groups - var spacerFunction = groupingSpacer().horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects).objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize).transitionDuration(transitionDuration).easeFunc(easeFunc).namespace(namespace); - // safe default function - var defaultExit = spacerFunction.exitFunction(); - - spacerFunction.exitFunction(function (sel) { - // use default to move objects off screen - // console.log("EXIT", sel.nodes(), objectSize, scale(extent[1])) - if (objectSize == undefined) { - console.log(sel.nodes(), objectSize); - } - defaultExit(sel); - sel.selectAll('g').classed("to-remove", true); - // shrink rectangles in addition - sel.selectAll('* > rect').transition().duration(transitionDuration).attr('transform', function (d, i) { - var x = horizontalQ ? 0 : 0, - y = verticalQ ? 0 : scale(extent[1]), - t = 'translate(' + x + ',' + y + ')'; - return t; - }).attr('width', horizontalQ ? objectSize : 0).attr('height', verticalQ ? objectSize : 0).remove(); - }); - - // move stuff - spacerFunction(container, ordered, 0); - - var parentIndexArray = []; - container.selectAll('g:not(.to-remove).' + objectClass).each(function (d, i) { - parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); - }); - - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' ? colorFunction$$1.dataExtent([0, Math.max.apply(Math, parentIndexArray)]) : colorFunction$$1.dataExtent(extent); - - container.selectAll('g.' + objectClass + ':not(.to-remove)').each(function (key, i) { - // console.log(key, scale(extent[1]) - scale(valueExtractor(key, i))) - var t = d3.select(this), - currentData = data[key], - value = valueExtractor(key, i), - i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, value, i, 'fill'), - // prevent duplicate computation - strokeColor = colorFunction$$1(key, value, i, 'stroke'); - - var bar = safeSelect(t, 'rect', 'bar-rect'); - - if (bar.attr('transform') == undefined) { - bar.attr('transform', function (d, i) { - var x = horizontalQ ? 0 : 0, - y = verticalQ ? 0 : scale(extent[1]), - t = 'translate(' + x + ',' + y + ')'; - return t; - }).attr('width', horizontalQ ? objectSize : 0).attr('height', verticalQ ? objectSize : 0); - } - - bar.transition().duration(transitionDuration).ease(easeFunc).attr('transform', function (d, i) { - var x = horizontalQ ? objectSize - objectSize * barPercent : orient == 'right' ? scale(extent[1]) - scale(value) : objectSize - objectSize * barPercent, - y = verticalQ ? objectSize - objectSize * barPercent : scale(extent[1]) - scale(value), - t = 'translate(' + x + ',' + y + ')'; - return t; - }).attr('width', horizontalQ ? objectSize * barPercent : scale(value)).attr('height', verticalQ ? objectSize * barPercent : scale(value)).attr('fill', fillColor).attr('stroke', strokeColor).attr('stroke-width', barStrokeWidth); - - t.on('mouseover', function (d, i) { - container.selectAll('g.' + objectClass).style('opacity', 0.2); - t.style('opacity', 1); - bar.attr('stroke-width', barStrokeWidth * 2); - }); - t.on('mouseout', function () { - container.selectAll('g.' + objectClass).style('opacity', 1); - bar.attr('stroke-width', barStrokeWidth); - }); - }); - - tooltip$$1.selection(container.selectAll('.bar-rect')).data(data); - - tooltip$$1(); - } - return bar; - } - - /** - * Creates a bubbleHeatmap - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/bubble-heatmap/index.html Demo} - * @constructor bubbleHeatmap - * @param {d3.selection} selection - * @namespace bubbleHeatmap - * @returns {function} bubbleHeatmap - */ - function bubbleHeatmap(selection) { - var - /** - * Data to plot. Assumed to be a object, where each key corresponds to a cell - * (see {@link bubbleHeatmap#data}) - * @param {Object} [data=undefined] - * @memberof bubbleHeatmap# - * @property - */ - data, - - - /** - * Amount of horizontal space (in pixels) avaible to render the bubbleHeatmap in - * (see {@link bubbleHeatmap#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof bubbleHeatmap# - * @property - */ - spaceX, - - /** - * Amount of vertical space (in pixels) avaible to render the bubbleHeatmap in - * (see {@link bubbleHeatmap.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof bubbleHeatmap# - * @property - */ - spaceY, - - - /** - * The internal key of the cell specifiying to which x axis key it belongs - * (see {@link bubbleHeatmap.xKey}) - * @param {string} [xKey='x'] - * @memberof bubbleHeatmap# - * @property - */ - xKey = 'x', - - /** - * The internal key of the cell specifiying to which y axis key it belongs - * (see {@link bubbleHeatmap.yKey}) - * @param {string} [yKey='y'] - * @memberof bubbleHeatmap# - * @property - */ - yKey = 'y', - - /** - * The internal key of the cell specifiying what value to use to determine the radius - * (see {@link bubbleHeatmap.rKey}) - * @param {string} [rKey='r'] - * @memberof bubbleHeatmap# - * @property - */ - rKey = 'r', - - /** - * The internal key of the cell specifiying what value to use to determine the color - * (see {@link bubbleHeatmap.vKey}) - * @param {string} [vKey='v'] - * @memberof bubbleHeatmap# - * @property - */ - vKey = 'v', - - - /** - * Function for extracting the the value from xKey. - * (see {@link bubbleHeatmap.xExtractor}) - * @param {function} [xExtractor=function(key, i) { return data[key][xKey] }] - * @returns {string} - * @memberof bubbleHeatmap# - * @property - */ - xExtractor = function xExtractor(key, i) { - return data[key][xKey]; - }, - - /** - * Function for extracting the the value from yKey. - * (see {@link bubbleHeatmap.yExtractor}) - * @param {function} [yExtractor=function(key, i) { return data[key][yKey] }] - * @returns {string} - * @memberof bubbleHeatmap# - * @property - */ - yExtractor = function yExtractor(key, i) { - return data[key][yKey]; - }, - - /** - * Function for extracting the the value from rKey. - * (see {@link bubbleHeatmap.rExtractor}) - * @param {function} [rExtractor=function(key, i) { return data[key][rKey] }] - * @returns {number} - * @memberof bubbleHeatmap# - * @property - */ - rExtractor = function rExtractor(key, i) { - return data[key][rKey]; - }, - - /** - * Function for extracting the the value from vKey. - * (see {@link bubbleHeatmap.vExtractor}) - * @param {function} [vExtractor=function(key, i) { return data[key][vKey] }] - * @returns {number} - * @memberof bubbleHeatmap# - * @property - */ - vExtractor = function vExtractor(key, i) { - return data[key][vKey]; - }, - - - /** - * Whether or not to allow bubbleHeatmap to render elements pass the main spatial dimension - * given the orientation (see {@link bubbleHeatmap#orient}), where {@link bubbleHeatmap#orient}="bottom" or {@link bubbleHeatmap#orient}="top" - * the main dimension is {@link bubbleHeatmap#spaceX} and where {@link bubbleHeatmap#orient}="left" or {@link bubbleHeatmap#orient}="right" - * the main dimension is {@link bubbleHeatmap#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof bubbleHeatmap# - * @property - */ - overflowQ = false, - - - /** - * The scale for which the radius values should be transformed by - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof bubbleHeatmap# - * @property - */ - scale = d3.scaleLinear(), - - /** - * The padding for the domain of the scale (see {@link bubbleHeatmap#scale}) - * @param {number} [domainPadding=0.5] - * @memberof bubbleHeatmap# - * @property - */ - domainPadding = 0.5, - - - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link bubbleHeatmap#orient}), where {@link bubbleHeatmap#orient}="horizontal" - * the main dimension is {@link bubbleHeatmap#spaceX} and where {@link bubbleHeatmap#orient}="vertical" - * the main dimension is {@link bubbleHeatmap#spaceY} between bubbles - * @param {number} [objectSpacer=0.0] - * @memberof bubbleHeatmap# - * @property - */ - objectSpacer = 0.0, - - /** - * The minimum size that an object can be - * @param {number} [minObjectSize=50] - * @memberof bubbleHeatmap# - * @property - */ - minObjectSize = 50, - - /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof bubbleHeatmap# - * @property - */ - maxObjectSize = 100, - - - /** - * The stroke width of the bubbles - * @param {number} [bubbleStrokeWidth=2] - * @memberof bubbleHeatmap# - * @property - */ - bubbleStrokeWidth = 2, - - // colorFunc = colorFunction(), - - /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof bubbleHeatmap# - * @property - */ - backgroundFill = 'transparent', - - /** - * Namespace for all items made by this instance of bubbleHeatmap - * @param {string} [namespace="d3sm-bubble"] - * @memberof bubbleHeatmap# - * @property - */ - namespace = 'd3sm-bubble', - - /** - * Class name for bubble container ( element) - * @param {string} [objectClass="bubble"] - * @memberof bubbleHeatmap# - * @property - */ - objectClass = 'bubble', - - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof bubbleHeatmap# - * @property - */ - transitionDuration = 1000, - - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof bubbleHeatmap# - * @property - */ - easeFunc = d3.easeExp, - - - /** - * Stores the keys of all the cells - * Calculated after bubbleHeatmap called. - * @param {string[]} [cellKeys=undefined] - * @memberof bubbleHeatmap# - * @property - */ - cellKeys, - - /** - * Stores the list of unique xValues - * Calculated after bubbleHeatmap called. - * @param {string[]} [xValues=undefined] - * @memberof bubbleHeatmap# - * @property - */ - xValues, - - /** - * Stores the list of unique yValues - * Calculated after bubbleHeatmap called. - * @param {string[]} [yValues=undefined] - * @memberof bubbleHeatmap# - * @property - */ - yValues, - - /** - * Stores the list of unique rValues - * Calculated after bubbleHeatmap called. - * @param {string[]} [rValues=undefined] - * @memberof bubbleHeatmap# - * @property - */ - rValues, - - /** - * Stores the list of unique vValues - * Calculated after bubbleHeatmap called. - * @param {string[]} [vValues=undefined] - * @memberof bubbleHeatmap# - * @property - */ - vValues, - xKeySortingFunction = function xKeySortingFunction(a, b) { - return xExtractor(a) - xExtractor(b); - }, - yKeySortingFunction = function yKeySortingFunction(a, b) { - return yExtractor(a) - yExtractor(b); - }, - /** - * Instance of ColorFunction with .colorBy set to 'category' - * @function colorFunction - * @memberof bubbleHeatmap# - * @property - */ - colorFunction$$1 = colorFunction().colorBy('value'), - - /** - * Instance of Tooltip - * @function tooltip - * @memberof bubbleHeatmap# - * @property - */ - tooltip$$1 = tooltip(), - - - /** - * store the size the bubble could be in the x dimension - * the actuall size of the bubble will be the min of xSize and {@link bubbleHeatmap#ySize} - * Calculated after bubbleHeatmap called. - * @param {string[]} [xSize=undefined] - * @memberof bubbleHeatmap# - * @property - */ - xSize, - - /** - * store the size of the spacer in the x dimension - * Calculated after bubbleHeatmap called. - * @param {string[]} [xSpacerSize=undefined] - * @memberof bubbleHeatmap# - * @property - */ - xSpacerSize, - - - /** - * store the size the bubble could be in the y dimension - * the actuall size of the bubble will be the min of xSize and {@link bubbleHeatmap#xSize} - * Calculated after bubbleHeatmap called. - * @param {string[]} [ySize=undefined] - * @memberof bubbleHeatmap# - * @property - */ - ySize, - - /** - * store the size of the spacer in the y dimension. - * Calculated after bubbleHeatmap called. - * @param {string[]} [xSpacerSize=undefined] - * @memberof bubbleHeatmap# - * @property - */ - ySpacerSize; - - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {bubbleHeatmap | d3.selection} - * @memberof bubbleHeatmap - * @property - * by default selection = selection - */ - bhm.selection = function (_) { - return arguments.length ? (selection = _, bhm) : selection; - }; - /** - * Gets or sets the data - * (see {@link bubbleHeatmap#data}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | object} - * @memberof bubbleHeatmap - * @property - */ - bhm.data = function (_) { - return arguments.length ? (data = _, bhm) : data; - }; - // bhm.orient = function(_) { return arguments.length ? (orient = _, bhm) : orient; } - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link bubbleHeatmap#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default spaceX = undefined - */ - bhm.spaceX = function (_) { - return arguments.length ? (spaceX = _, bhm) : spaceX; - }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link bubbleHeatmap#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default spaceY = undefined - */ - bhm.spaceY = function (_) { - return arguments.length ? (spaceY = _, bhm) : spaceY; - }; - - /** - * Gets or sets the xKey - * (see {@link bubbleHeatmap#xKey}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap - * @property - * by default xKey = 'x' - */ - bhm.xKey = function (_) { - return arguments.length ? (xKey = _, bhm) : xKey; - }; - /** - * Gets or sets the yKey - * (see {@link bubbleHeatmap#yKey}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap - * @property - * by default yKey = 'y' - */ - bhm.yKey = function (_) { - return arguments.length ? (yKey = _, bhm) : yKey; - }; - /** - * Gets or sets the rKey - * (see {@link bubbleHeatmap#rKey}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap - * @property - * by default rKey = 'r' - */ - bhm.rKey = function (_) { - return arguments.length ? (rKey = _, bhm) : rKey; - }; - /** - * Gets or sets the vKey - * (see {@link bubbleHeatmap#vKey}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap - * @property - * by default vKey = 'y' - */ - bhm.vKey = function (_) { - return arguments.length ? (vKey = _, bhm) : vKey; - }; - - /** - * Gets or sets the cellKeys - * (see {@link bubbleHeatmap#cellKeys}) - * @param {string[]} [_=none] - * @returns {bubbleHeatmap | string[]} - * @memberof bubbleHeatmap - * @property - * by default cellKeys = undefined - */ - bhm.cellKeys = function (_) { - return arguments.length ? (cellKeys = _, bhm) : cellKeys; - }; - /** - * Gets or sets the xValues - * (see {@link bubbleHeatmap#xValues}) - * @param {string[]} [_=none] - * @returns {bubbleHeatmap | string[]} - * @memberof bubbleHeatmap - * @property - * by default xValues = undefined - */ - bhm.xValues = function (_) { - return arguments.length ? (xValues = _, bhm) : xValues; - }; - /** - * Gets or sets the yValues - * (see {@link bubbleHeatmap#yValues}) - * @param {string[]} [_=none] - * @returns {bubbleHeatmap | string[]} - * @memberof bubbleHeatmap - * @property - * by default yValues = undefined - */ - bhm.yValues = function (_) { - return arguments.length ? (yValues = _, bhm) : yValues; - }; - /** - * Gets or sets the rValues - * (see {@link bubbleHeatmap#rValues}) - * @param {number[]} [_=none] - * @returns {bubbleHeatmap | number[]} - * @memberof bubbleHeatmap - * @property - * by default rValues = undefined - */ - bhm.rValues = function (_) { - return arguments.length ? (rValues = _, bhm) : rValues; - }; - /** - * Gets or sets the vValues - * (see {@link bubbleHeatmap#vValues}) - * @param {number[]} [_=none] - * @returns {bubbleHeatmap | number[]} - * @memberof bubbleHeatmap - * @property - * by default vValues = undefined - */ - bhm.vValues = function (_) { - return arguments.length ? (vValues = _, bhm) : vValues; - }; - - /** - * Gets or sets the xExtractor - * (see {@link bubbleHeatmap#xExtractor}) - * @param {function} [_=none] - * @returns {bubbleHeatmap | function} - * @memberof bubbleHeatmap - * @property - * by default xExtractor = undefined - */ - bhm.xExtractor = function (_) { - return arguments.length ? (xExtractor = _, bhm) : xExtractor; - }; - /** - * Gets or sets the yExtractor - * (see {@link bubbleHeatmap#yExtractor}) - * @param {function} [_=none] - * @returns {bubbleHeatmap | function} - * @memberof bubbleHeatmap - * @property - * by default yExtractor = undefined - */ - bhm.yExtractor = function (_) { - return arguments.length ? (yExtractor = _, bhm) : yExtractor; - }; - /** - * Gets or sets the rExtractor - * (see {@link bubbleHeatmap#rExtractor}) - * @param {function} [_=none] - * @returns {bubbleHeatmap | function} - * @memberof bubbleHeatmap - * @property - * by default rExtractor = undefined - */ - bhm.rExtractor = function (_) { - return arguments.length ? (rExtractor = _, bhm) : rExtractor; - }; - /** - * Gets or sets the vExtractor - * (see {@link bubbleHeatmap#vExtractor}) - * @param {function} [_=none] - * @returns {bubbleHeatmap | function} - * @memberof bubbleHeatmap - * @property - * by default vExtractor = undefined - */ - bhm.vExtractor = function (_) { - return arguments.length ? (vExtractor = _, bhm) : vExtractor; - }; - - /** - * Gets / sets whether or not bubbleHeatmap is allowed to go beyond specified dimensions - * (see {@link bubbleHeatmap#spaceX}) - * @param {boolean} [_=none] - * @returns {bubbleHeatmap | boolean} - * @memberof bubbleHeatmap - * @property - * by default overflowQ = false - */ - bhm.overflowQ = function (_) { - return arguments.length ? (overflowQ = _, bhm) : overflowQ; - }; - /** - * Gets / sets the scale for which the radius of bubbles should be transformed by - * (see {@link bubbleHeatmap#scale}) - * @param {d3.scale} [_=none] - * @returns {bubbleHeatmap | d3.scale} - * @memberof bubbleHeatmap - * @property - * by default scale = d3.scaleLinear() - */ - bhm.scale = function (_) { - return arguments.length ? (scale = _, bhm) : scale; - }; - /** - * Gets / sets the padding for the domain of the scale - * (see {@link bubbleHeatmap#domainPadding}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default domainPadding = 0.5 - */ - bhm.domainPadding = function (_) { - return arguments.length ? (domainPadding = _, bhm) : domainPadding; - }; - /** - * Gets / sets objectSpacer - * (see {@link bubbleHeatmap#objectSpacer}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default objectSpacer = 0.0 - */ - bhm.objectSpacer = function (_) { - return arguments.length ? (objectSpacer = _, objectSpacer) : data; - }; - /** - * Gets / sets the minObjectSize - * (see {@link bubbleHeatmap#minObjectSize}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default minObjectSize = 50 - */ - bhm.minObjectSize = function (_) { - return arguments.length ? (minObjectSize = _, bhm) : minObjectSize; - }; - /** - * Gets / sets the maxObjectSize - * (see {@link bubbleHeatmap#maxObjectSize}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default maxObjectSize = 100 - */ - bhm.maxObjectSize = function (_) { - return arguments.length ? (maxObjectSize = _, bhm) : maxObjectSize; - }; - /** - * Gets / sets the bubbleStrokeWidth - * (see {@link bubbleHeatmap#bubbleStrokeWidth}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default bubbleStrokeWidth = 2 - */ - bhm.bubbleStrokeWidth = function (_) { - return arguments.length ? (bubbleStrokeWidth = _, bhm) : bubbleStrokeWidth; - }; - /** - * Gets / sets the backgroundFill - * (see {@link bubbleHeatmap#backgroundFill}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap - * @property - * by default backgroundFill = 'transparent' - */ - bhm.backgroundFill = function (_) { - return arguments.length ? (backgroundFill = _, bhm) : backgroundFill; - }; - /** - * Gets / sets the namespace - * (see {@link bubbleHeatmap#namespace}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap - * @property - * by default namespace = 'd3sm-bubbleHeatmap' - */ - bhm.namespace = function (_) { - return arguments.length ? (namespace = _, bhm) : namespace; - }; - /** - * Gets / sets the objectClass - * (see {@link bubbleHeatmap#objectClass}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap - * @property - * by default objectClass = 'tick-group' - */ - bhm.objectClass = function (_) { - return arguments.length ? (objectClass = _, bhm) : objectClass; - }; - /** - * Gets / sets the transitionDuration - * (see {@link bubbleHeatmap#transitionDuration}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default transitionDuration = 1000 - */ - bhm.transitionDuration = function (_) { - return arguments.length ? (transitionDuration = _, bhm) : transitionDuration; - }; - /** - * Gets / sets the easeFunc - * (see {@link bubbleHeatmap#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {bubbleHeatmap | d3.ease} - * @memberof bubbleHeatmap - * @property - * by default easeFunc = d3.easeExp - */ - bhm.easeFunc = function (_) { - return arguments.length ? (easeFunc = _, bhm) : easeFunc; - }; - - /** - * Gets / sets the tooltip - * (see {@link bubbleHeatmap#tooltip}) - * @param {tooltip} [_=none] - * @returns {bubbleHeatmap | tooltip} - * @memberof bubbleHeatmap - * @property - * by default tooltip = tooltip() - */ - bhm.tooltip = function (_) { - return arguments.length ? (tooltip$$1 = _, bhm) : tooltip$$1; - }; - - /** - * Gets / sets the colorFunction - * (see {@link bubbleHeatmap#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {bubbleHeatmap | colorFunction} - * @memberof bubbleHeatmap - * @property - * by default colorFunction = colorFunction() - */ - bhm.colorFunction = function (_) { - return arguments.length ? (colorFunction$$1 = _, bhm) : colorFunction$$1; - }; - - /** - * Gets / sets the xSize - * (see {@link bubbleHeatmap#xSize}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default xSize = undefined - */ - bhm.xSize = function (_) { - return arguments.length ? (xSize = _, bhm) : xSize; - }; - /** - * Gets / sets the xSpacerSize - * (see {@link bubbleHeatmap#xSpacerSize}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default xSpacerSize = undefined - */ - bhm.xSpacerSize = function (_) { - return arguments.length ? (xSpacerSize = _, bhm) : xSpacerSize; - }; - /** - * Gets / sets the ySize - * (see {@link bubbleHeatmap#ySize}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default ySize = undefined - */ - bhm.ySize = function (_) { - return arguments.length ? (ySize = _, bhm) : ySize; - }; - /** - * Gets / sets the ySpacerSize - * (see {@link bubbleHeatmap#ySpacerSize}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default ySpacerSize = undefined - */ - bhm.ySpacerSize = function (_) { - return arguments.length ? (ySpacerSize = _, bhm) : ySpacerSize; - }; - // bhm.yKeySortingFunction = function(_) { return arguments.length ? (yKeySortingFunction = _, bhm) : yKeySortingFunction; } - // bhm.xKeySortingFunction = function(_) { return arguments.length ? (xKeySortingFunction = _, bhm) : xKeySortingFunction; } - - - function bhm() { - var bgcpRect = { x: 0, y: 0, width: spaceX, height: spaceY }; - var container = setupContainer(selection, namespace, bgcpRect, backgroundFill); - - cellKeys = d3.keys(data); - cellKeys.sort(function (a, b) { - return xKeySortingFunction(a, b) || yKeySortingFunction(a, b); - }); - log('bubbleHeatmap', 'cells are sorted by', cellKeys); - - xValues = unique(cellKeys.map(xExtractor)); - yValues = unique(cellKeys.map(yExtractor)); - rValues = unique(cellKeys.map(rExtractor)); - vValues = unique(cellKeys.map(vExtractor)); - log('bubbleHeatmap', 'x and y keys are', { x: xValues, y: yValues }); - - var xDim = xValues.length, - yDim = yValues.length; - - var extent = [Math.min.apply(Math, toConsumableArray(rValues)) - domainPadding, Math.max.apply(Math, toConsumableArray(rValues)) + domainPadding]; - - ySize = calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - xSize = calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - ySpacerSize = calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); - xSpacerSize = calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); - log('bubbleHeatmap', 'size of', { x: xSize, y: ySize }); - - scale.domain(extent).range([Math.min(minObjectSize / 2, Math.min(ySize, xSize) / 2), Math.min(ySize, xSize) / 2]); - - var ySpacer = groupingSpacer().horizontalQ(false).moveby('category').numberOfObjects(yDim).objectClass(hypenate(objectClass, 'row')).objectSize(ySize).spacerSize(ySpacerSize).transitionDuration(transitionDuration).easeFunc(easeFunc).namespace('row'); - - var xSpacer = groupingSpacer().horizontalQ(true).moveby('category').numberOfObjects(xDim).objectClass(objectClass).objectSize(xSize).spacerSize(xSpacerSize).transitionDuration(transitionDuration).easeFunc(easeFunc); - - ySpacer(container, yValues, 0); - container.selectAll('g.' + hypenate(objectClass, 'row')).each(function (d, i) { - xSpacer(d3.select(this), xValues, 0); - }); - var cells = container.selectAll('g:not(.to-remove).' + objectClass).data(cellKeys); - - var parentIndexArray = []; - cells.each(function (d, i) { - parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); - }); - - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' ? colorFunction$$1.dataExtent([0, Math.max.apply(Math, parentIndexArray)]) : colorFunction$$1.dataExtent([0, Math.max.apply(Math, toConsumableArray(vValues))]); - - cells.each(function (key, i) { - log('bubbleHeatmap', 'each cell', { key: key, index: i, node: d3.select(this).node() }); - - var t = d3.select(this), - currentData = data[key], - value = vExtractor(key, i), - radius = rExtractor(key, i), - i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, value, i, 'fill'), - // prevent duplicate computation - strokeColor = colorFunction$$1(key, value, i, 'stroke'); - - log('bubbleHeatmap', 'radius', { radius: radius, scaled: scale(radius), extent: extent, range: scale.range() }); - - var c = safeSelect(t, 'circle', hypenate(objectClass, 'circle')); - c.attr('cx', xSize / 2).attr('cy', ySize / 2).attr('r', scale(radius)).attr('fill', fillColor).attr('stroke', strokeColor).attr('stroke-width', bubbleStrokeWidth); - }); - - tooltip$$1.selection(cells.selectAll('circle.' + hypenate(objectClass, 'circle'))).data(data); - // .keys(['r', 'v']) - // .header(function(d, i){return hypenate(data[d][xKey], data[d][yKey]) }) - - tooltip$$1(); - } - - return bhm; - } - - /** - * Creates a heatmap - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/heatmap-heatmap/index.html Demo} - * @constructor heatmap - * @param {d3.selection} selection - * @namespace heatmap - * @returns {function} heatmap - */ - function heatmap(selection) { - var - /** - * Data to plot. Assumed to be a object, where each key corresponds to a cell - * (see {@link heatmap#data}) - * @param {Object} [data=undefined] - * @memberof heatmap# - * @property - */ - data, - - - /** - * Amount of horizontal space (in pixels) avaible to render the heatmap in - * (see {@link heatmap#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof heatmap# - * @property - */ - spaceX, - - /** - * Amount of vertical space (in pixels) avaible to render the heatmap in - * (see {@link heatmap.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof heatmap# - * @property - */ - spaceY, - - - /** - * The internal key of the cell specifiying to which x axis key it belongs - * (see {@link heatmap.xKey}) - * @param {string} [xKey='x'] - * @memberof heatmap# - * @property - */ - xKey = 'x', - - /** - * The internal key of the cell specifiying to which y axis key it belongs - * (see {@link heatmap.yKey}) - * @param {string} [yKey='y'] - * @memberof heatmap# - * @property - */ - yKey = 'y', - - - /** - * The internal key of the cell specifiying what value to use to determine the color - * (see {@link heatmap.vKey}) - * @param {string} [vKey='v'] - * @memberof heatmap# - * @property - */ - vKey = 'v', - - - /** - * Function for extracting the the value from xKey. - * (see {@link heatmap.xExtractor}) - * @param {function} [xExtractor=function(key, i) { return data[key][xKey] }] - * @returns {string} - * @memberof heatmap# - * @property - */ - xExtractor = function xExtractor(key, i) { - return data[key][xKey]; - }, - - /** - * Function for extracting the the value from yKey. - * (see {@link heatmap.yExtractor}) - * @param {function} [yExtractor=function(key, i) { return data[key][yKey] }] - * @returns {string} - * @memberof heatmap# - * @property - */ - yExtractor = function yExtractor(key, i) { - return data[key][yKey]; - }, - - - /** - * Function for extracting the the value from vKey. - * (see {@link heatmap.vExtractor}) - * @param {function} [vExtractor=function(key, i) { return data[key][vKey] }] - * @returns {number} - * @memberof heatmap# - * @property - */ - vExtractor = function vExtractor(key, i) { - return data[key][vKey]; - }, - - - /** - * Whether or not to allow heatmap to render elements pass the main spatial dimension - * given the orientation (see {@link heatmap#orient}), where {@link heatmap#orient}="bottom" or {@link heatmap#orient}="top" - * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="left" or {@link heatmap#orient}="right" - * the main dimension is {@link heatmap#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof heatmap# - * @property - */ - overflowQ = false, - - - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link heatmap#orient}), where {@link heatmap#orient}="horizontal" - * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="vertical" - * the main dimension is {@link heatmap#spaceY} between bubbles - * @param {number} [objectSpacer=0.0] - * @memberof heatmap# - * @property - */ - objectSpacer = 0.0, - - /** - * The minimum size that an object can be in the y dimension - * @param {number} [minObjectSize=50] - * @memberof heatmap# - * @property - */ - yMinObjectSize = 50, - - /** - * The minimum size that an object can be in the x dimension - * @param {number} [minObjectSize=50] - * @memberof heatmap# - * @property - */ - xMinObjectSize = 50, - - /** - * The maximum size that an object can be in the x dimension - * @param {number} [maxObjectSize=100] - * @memberof heatmap# - * @property - */ - xMaxObjectSize = 100, - - /** - * The maximum size that an object can be in the y dimension - * @param {number} [maxObjectSize=100] - * @memberof heatmap# - * @property - */ - yMaxObjectSize = 100, - - - /** - * The stroke width of the bubbles - * @param {number} [objectStrokeWidth=2] - * @memberof heatmap# - * @property - */ - objectStrokeWidth = 2, - - // colorFunc = colorFunction(), - - /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof heatmap# - * @property - */ - backgroundFill = 'transparent', - - /** - * Namespace for all items made by this instance of heatmap - * @param {string} [namespace="d3sm-heatmap"] - * @memberof heatmap# - * @property - */ - namespace = 'd3sm-heatmap', - - /** - * Class name for heatmap container ( element) - * @param {string} [objectClass="heatmap"] - * @memberof heatmap# - * @property - */ - objectClass = 'heatmap', - - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof heatmap# - * @property - */ - transitionDuration = 1000, - - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof heatmap# - * @property - */ - easeFunc = d3.easeExp, - - - /** - * Stores the keys of all the cells - * Calculated after heatmap called. - * @param {string[]} [cellKeys=undefined] - * @memberof heatmap# - * @property - */ - cellKeys, - - /** - * Stores the list of unique xValues - * Calculated after heatmap called. - * @param {string[]} [xValues=undefined] - * @memberof heatmap# - * @property - */ - xValues, - - /** - * Stores the list of unique yValues - * Calculated after heatmap called. - * @param {string[]} [yValues=undefined] - * @memberof heatmap# - * @property - */ - yValues, - - /** - * Stores the list of unique vValues - * Calculated after heatmap called. - * @param {string[]} [vValues=undefined] - * @memberof heatmap# - * @property - */ - vValues, - xKeySortingFunction = function xKeySortingFunction(a, b) { - return xValues.indexOf(xExtractor(a)) - xValues.indexOf(xExtractor(b)); - }, - yKeySortingFunction = function yKeySortingFunction(a, b) { - return yValues.indexOf(yExtractor(a)) - yValues.indexOf(yExtractor(b)); - }, - /** - * Instance of ColorFunction with .colorBy set to 'category' - * @function colorFunction - * @memberof heatmap# - * @property - */ - colorFunction$$1 = colorFunction().colorBy('category'), - - /** - * Instance of Tooltip - * @function tooltip - * @memberof heatmap# - * @property - */ - tooltip$$1 = tooltip(), - - - /** - * store the size the heatmap could be in the x dimension - * the actuall size of the heatmap will be the min of xSize and {@link heatmap#ySize} - * Calculated after heatmap called. - * @param {string[]} [xSize=undefined] - * @memberof heatmap# - * @property - */ - xSize, - - /** - * store the size of the spacer in the x dimension - * Calculated after heatmap called. - * @param {string[]} [xSpacerSize=undefined] - * @memberof heatmap# - * @property - */ - xSpacerSize, - - - /** - * store the size the heatmap could be in the y dimension - * the actuall size of the heatmap will be the min of xSize and {@link heatmap#xSize} - * Calculated after heatmap called. - * @param {string[]} [ySize=undefined] - * @memberof heatmap# - * @property - */ - ySize, - - /** - * store the size of the spacer in the y dimension. - * Calculated after heatmap called. - * @param {string[]} [xSpacerSize=undefined] - * @memberof heatmap# - * @property - */ - ySpacerSize; - - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {heatmap | d3.selection} - * @memberof heatmap - * @property - * by default selection = selection - */ - hm.selection = function (_) { - return arguments.length ? (selection = _, hm) : selection; - }; - /** - * Gets or sets the data - * (see {@link heatmap#data}) - * @param {number} [_=none] - * @returns {heatmap | object} - * @memberof heatmap - * @property - */ - hm.data = function (_) { - return arguments.length ? (data = _, hm) : data; - }; - // hm.orient = function(_) { return arguments.length ? (orient = _, hm) : orient; } - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link heatmap#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default spaceX = undefined - */ - hm.spaceX = function (_) { - return arguments.length ? (spaceX = _, hm) : spaceX; - }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link heatmap#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default spaceY = undefined - */ - hm.spaceY = function (_) { - return arguments.length ? (spaceY = _, hm) : spaceY; - }; - - /** - * Gets or sets the xKey - * (see {@link heatmap#xKey}) - * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap - * @property - * by default xKey = 'x' - */ - hm.xKey = function (_) { - return arguments.length ? (xKey = _, hm) : xKey; - }; - /** - * Gets or sets the yKey - * (see {@link heatmap#yKey}) - * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap - * @property - * by default yKey = 'y' - */ - hm.yKey = function (_) { - return arguments.length ? (yKey = _, hm) : yKey; - }; - - /** - * Gets or sets the vKey - * (see {@link heatmap#vKey}) - * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap - * @property - * by default vKey = 'y' - */ - hm.vKey = function (_) { - return arguments.length ? (vKey = _, hm) : vKey; - }; - - /** - * Gets or sets the cellKeys - * (see {@link heatmap#cellKeys}) - * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap - * @property - * by default cellKeys = undefined - */ - hm.cellKeys = function (_) { - return arguments.length ? (cellKeys = _, hm) : cellKeys; - }; - /** - * Gets or sets the xValues - * (see {@link heatmap#xValues}) - * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap - * @property - * by default xValues = undefined - */ - hm.xValues = function (_) { - return arguments.length ? (xValues = _, hm) : xValues; - }; - /** - * Gets or sets the yValues - * (see {@link heatmap#yValues}) - * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap - * @property - * by default yValues = undefined - */ - hm.yValues = function (_) { - return arguments.length ? (yValues = _, hm) : yValues; - }; - /** - * Gets or sets the vValues - * (see {@link heatmap#vValues}) - * @param {number[]} [_=none] - * @returns {heatmap | number[]} - * @memberof heatmap - * @property - * by default vValues = undefined - */ - hm.vValues = function (_) { - return arguments.length ? (vValues = _, hm) : vValues; - }; - - /** - * Gets or sets the xExtractor - * (see {@link heatmap#xExtractor}) - * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap - * @property - * by default xExtractor = undefined - */ - hm.xExtractor = function (_) { - return arguments.length ? (xExtractor = _, hm) : xExtractor; - }; - /** - * Gets or sets the yExtractor - * (see {@link heatmap#yExtractor}) - * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap - * @property - * by default yExtractor = undefined - */ - hm.yExtractor = function (_) { - return arguments.length ? (yExtractor = _, hm) : yExtractor; - }; - /** - * Gets or sets the vExtractor - * (see {@link heatmap#vExtractor}) - * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap - * @property - * by default vExtractor = undefined - */ - hm.vExtractor = function (_) { - return arguments.length ? (vExtractor = _, hm) : vExtractor; - }; - - /** - * Gets / sets whether or not heatmap is allowed to go beyond specified dimensions - * (see {@link heatmap#spaceX}) - * @param {boolean} [_=none] - * @returns {heatmap | boolean} - * @memberof heatmap - * @property - * by default overflowQ = false - */ - hm.overflowQ = function (_) { - return arguments.length ? (overflowQ = _, hm) : overflowQ; - }; - /** - * Gets / sets objectSpacer - * (see {@link heatmap#objectSpacer}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default objectSpacer = 0.0 - */ - hm.objectSpacer = function (_) { - return arguments.length ? (objectSpacer = _, objectSpacer) : data; - }; - /** - * Gets / sets the minObjectSize - * (see {@link heatmap#minObjectSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default minObjectSize = 50 - */ - hm.yMinObjectSize = function (_) { - return arguments.length ? (yMinObjectSize = _, hm) : yMinObjectSize; - }; - /** - * Gets / sets the maxObjectSize - * (see {@link heatmap#maxObjectSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default maxObjectSize = 100 - */ - hm.yMaxObjectSize = function (_) { - return arguments.length ? (yMaxObjectSize = _, hm) : yMaxObjectSize; - }; - /** - * Gets / sets the minObjectSize - * (see {@link heatmap#minObjectSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default minObjectSize = 50 - */ - hm.xMinObjectSize = function (_) { - return arguments.length ? (xMinObjectSize = _, hm) : xMinObjectSize; - }; - /** - * Gets / sets the maxObjectSize - * (see {@link heatmap#maxObjectSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default maxObjectSize = 100 - */ - hm.xMaxObjectSize = function (_) { - return arguments.length ? (xMaxObjectSize = _, hm) : xMaxObjectSize; - }; - /** - * Gets / sets the objectStrokeWidth - * (see {@link heatmap#objectStrokeWidth}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default objectStrokeWidth = 2 - */ - hm.objectStrokeWidth = function (_) { - return arguments.length ? (objectStrokeWidth = _, hm) : objectStrokeWidth; - }; - /** - * Gets / sets the backgroundFill - * (see {@link heatmap#backgroundFill}) - * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap - * @property - * by default backgroundFill = 'transparent' - */ - hm.backgroundFill = function (_) { - return arguments.length ? (backgroundFill = _, hm) : backgroundFill; - }; - /** - * Gets / sets the namespace - * (see {@link heatmap#namespace}) - * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap - * @property - * by default namespace = 'd3sm-heatmap' - */ - hm.namespace = function (_) { - return arguments.length ? (namespace = _, hm) : namespace; - }; - /** - * Gets / sets the objectClass - * (see {@link heatmap#objectClass}) - * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap - * @property - * by default objectClass = 'tick-group' - */ - hm.objectClass = function (_) { - return arguments.length ? (objectClass = _, hm) : objectClass; - }; - /** - * Gets / sets the transitionDuration - * (see {@link heatmap#transitionDuration}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default transitionDuration = 1000 - */ - hm.transitionDuration = function (_) { - return arguments.length ? (transitionDuration = _, hm) : transitionDuration; - }; - /** - * Gets / sets the easeFunc - * (see {@link heatmap#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {heatmap | d3.ease} - * @memberof heatmap - * @property - * by default easeFunc = d3.easeExp - */ - hm.easeFunc = function (_) { - return arguments.length ? (easeFunc = _, hm) : easeFunc; - }; - - /** - * Gets / sets the tooltip - * (see {@link heatmap#tooltip}) - * @param {tooltip} [_=none] - * @returns {heatmap | tooltip} - * @memberof heatmap - * @property - * by default tooltip = tooltip() - */ - hm.tooltip = function (_) { - return arguments.length ? (tooltip$$1 = _, hm) : tooltip$$1; - }; - - /** - * Gets / sets the colorFunction - * (see {@link heatmap#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {heatmap | colorFunction} - * @memberof heatmap - * @property - * by default colorFunction = colorFunction() - */ - hm.colorFunction = function (_) { - return arguments.length ? (colorFunction$$1 = _, hm) : colorFunction$$1; - }; - - /** - * Gets / sets the xSize - * (see {@link heatmap#xSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default xSize = undefined - */ - hm.xSize = function (_) { - return arguments.length ? (xSize = _, hm) : xSize; - }; - /** - * Gets / sets the xSpacerSize - * (see {@link heatmap#xSpacerSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default xSpacerSize = undefined - */ - hm.xSpacerSize = function (_) { - return arguments.length ? (xSpacerSize = _, hm) : xSpacerSize; - }; - /** - * Gets / sets the ySize - * (see {@link heatmap#ySize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default ySize = undefined - */ - hm.ySize = function (_) { - return arguments.length ? (ySize = _, hm) : ySize; - }; - /** - * Gets / sets the ySpacerSize - * (see {@link heatmap#ySpacerSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default ySpacerSize = undefined - */ - hm.ySpacerSize = function (_) { - return arguments.length ? (ySpacerSize = _, hm) : ySpacerSize; - }; - // hm.yKeySortingFunction = function(_) { return arguments.length ? (yKeySortingFunction = _, hm) : yKeySortingFunction; } - // hm.xKeySortingFunction = function(_) { return arguments.length ? (xKeySortingFunction = _, hm) : xKeySortingFunction; } - - - function hm() { - var bgcpRect = { x: 0, y: 0, width: spaceX, height: spaceY }; - var container = setupContainer(selection, namespace, bgcpRect, backgroundFill); - - cellKeys = d3.keys(data); - - xValues = unique(cellKeys.map(xExtractor)); - yValues = unique(cellKeys.map(yExtractor)); - vValues = unique(cellKeys.map(vExtractor)); - - cellKeys.sort(function (a, b) { - return xKeySortingFunction(a, b) || yKeySortingFunction(a, b); - }); - log('heatmap', 'cells are sorted by', cellKeys); - - log('heatmap', 'x and y keys are', { x: xValues, y: yValues }); - - var xDim = xValues.length, - yDim = yValues.length; - - ySize = calculateWidthOfObject(spaceY, yDim, yMinObjectSize, yMaxObjectSize, objectSpacer, overflowQ); - xSize = calculateWidthOfObject(spaceX, xDim, xMinObjectSize, xMaxObjectSize, objectSpacer, overflowQ); - ySpacerSize = calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); - xSpacerSize = calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); - // console.table({ - // x:{ - // object: xSize, - // spacer: xSpacerSize, - // dim: xDim - // }, - // y:{ - // object: ySize, - // spacer: ySpacerSize, - // dim: yDim - // } - // - // }) - log('heatmap', 'size of', { x: xSize, y: ySize }); - - var ySpacer = groupingSpacer().horizontalQ(false).moveby('category').numberOfObjects(yDim).objectClass(hypenate(objectClass, 'row')).objectSize(ySize + ySpacerSize).spacerSize(0).transitionDuration(transitionDuration).easeFunc(easeFunc).namespace('row'); - - var xSpacer = groupingSpacer().horizontalQ(true).moveby('category').numberOfObjects(xDim).objectClass(objectClass).objectSize(xSize + xSpacerSize).spacerSize(0).transitionDuration(transitionDuration).easeFunc(easeFunc); - - ySpacer(container, yValues, 0); - container.selectAll('g.' + hypenate(objectClass, 'row')).each(function (d, i) { - xSpacer(d3.select(this), xValues, 0); - }); - - var cells = container.selectAll('g:not(.to-remove).' + objectClass); - - if (cellKeys.length != yValues.length * xValues.length) { - var lookup = {}; - cellKeys.map(function (k, i) { - lookup[xExtractor(k) + '::' + yExtractor(k)] = k; - }); - - var positionedCellKeys = []; - for (var i = 0; i < yValues.length; i++) { - for (var j = 0; j < xValues.length; j++) { - var lookupValue = lookup[xValues[j] + "::" + yValues[i]]; - if (lookupValue == undefined) { - positionedCellKeys.push(undefined); - } else { - positionedCellKeys.push(lookupValue); - } - } - } - - cells.data(positionedCellKeys); - - // maybe breaks this - // !!!!!! IMPORTANT NOTE TODO LOOK HERE BUG - cellKeys = positionedCellKeys; - } else { - cells.data(cellKeys); - } - - var parentIndexArray = []; - cells.each(function (d, i) { - parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); - }); - - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' ? colorFunction$$1.dataExtent([0, Math.max.apply(Math, parentIndexArray)]) : colorFunction$$1.dataExtent([0, Math.max.apply(Math, toConsumableArray(vValues))]); - - cells.each(function (key, i) { - log('heatmap', 'each cell', { key: key, index: i, node: d3.select(this).node() }); - - var t = d3.select(this); - if (key == undefined) { - return; - } - var currentData = data[key], - value = vExtractor(key, i), - i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, value, i, 'fill'), - // prevent duplicate computation - strokeColor = colorFunction$$1(key, value, i, 'stroke'); - - var c = safeSelect(t, 'rect', hypenate(objectClass, 'rect')); - c.attr('width', xSize + xSpacerSize - objectStrokeWidth).attr('height', ySize + ySpacerSize - objectStrokeWidth).attr('fill', fillColor).attr('x', objectStrokeWidth / 2).attr('y', objectStrokeWidth / 2).attr('stroke', "#000").attr('stroke-width', objectStrokeWidth); - }); - - tooltip$$1.selection(cells.selectAll('rect.' + hypenate(objectClass, 'rect'))).data(data); - // .keys(['r', 'v']) - // .header(function(d, i){return hypenate(data[d][xKey], data[d][yKey]) }) - - tooltip$$1(); - } - - return hm; - } - - /******************************************************************************* - ** ** - ** ** - ** BOX AND WHISKER ** - ** ** - ** ** - *******************************************************************************/ - /** - * Creates a boxwhisker - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/box-whiskers/index.html Demo} - * @constructor boxwhisker - * @param {d3.selection} selection - * @namespace boxwhisker - * @returns {function} boxwhisker - */ - function boxwhisker(selection) { - var - /** - * Data to plot. Assumed to be a object, where each key corresponds to a box - * (see {@link boxwhisker#data}) - * @param {Object} [data=undefined] - * @memberof boxwhisker# - * @property - */ - data, - - /** - * Which direction to render the boxes in - * (see {@link boxwhisker#orient}) - * @param {number} [orient='horizontal'] - * @memberof boxwhisker# - * @property - */ - orient = 'horizontal', - - /** - * Amount of horizontal space (in pixels) avaible to render the boxwhisker in - * (see {@link boxwhisker#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof boxwhisker# - * @property - */ - spaceX, - - /** - * Amount of vertical space (in pixels) avaible to render the boxwhisker in - * (see {@link boxwhisker.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof boxwhisker# - * @property - */ - spaceY, - - /** - * Whether or not to allow boxwhisker to render elements pass the main spatial dimension - * given the orientation (see {@link boxwhisker#orient}), where {@link boxwhisker#orient}="horizontal" - * the main dimension is {@link boxwhisker#spaceX} and where {@link boxwhisker#orient}="vertical" - * the main dimension is {@link boxwhisker#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof boxwhisker# - * @property - */ - overflowQ = true, - - - /** - * An array - putatively of other arrays - depicting how boxes should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof boxwhisker# - * @property - */ - grouping, - quartilesKey = 'quartiles', - // key in object where quartiles are stored - quartilesKeys = ["0.00", "0.25", "0.50", "0.75", "1.00"], - // keys in quartiles object mapping values to min, q1, q2, q3 and max - - - /** - * How to get the value of the boxwhisker - * @param {function} [valueExtractor=function(key, index) { return data[key][quartilesKey] }] - * @memberof boxwhisker# - * @property - */ - valueExtractor = function valueExtractor(key, index) { - return data[key][quartilesKey]; - }, - - /** - * How to sort the boxes - if {@link boxwhisker#grouping} is not provided. - * @param {function} [sortingFunction=descending] - * @memberof boxwhisker# - * @property - * default - * function(keyA, keyB) {return d3.descending( - * valueExtractor(keyA)[quartilesKeys[4]], - * valueExtractor(keyB)[quartilesKeys[4]] - * )} - */ - sortingFunction = function sortingFunction(keyA, keyB) { - return d3.descending(valueExtractor(keyA)[quartilesKeys[4]], valueExtractor(keyB)[quartilesKeys[4]]); - }, - - /** - * The scale for which boxwhisker values should be transformed by - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof boxwhisker# - * @property - */ - scale = d3.scaleLinear(), - - /** - * The padding for the domain of the scale (see {@link boxwhisker#scale}) - * @param {number} [domainPadding=0.5] - * @memberof boxwhisker# - * @property - */ - domainPadding = 0.5, - - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link boxwhisker#orient}), where {@link boxwhisker#orient}="horizontal" - * the main dimension is {@link boxwhisker#spaceX} and where {@link boxwhisker#orient}="vertical" - * the main dimension is {@link boxwhisker#spaceY} between boxes - * @param {number} [objectSpacer=0.05] - * @memberof boxwhisker# - * @property - */ - objectSpacer = 0.05, - - /** - * The minimum size that an object can be - * @param {number} [minObjectSize=15] - * @memberof boxwhisker# - * @property - */ - minObjectSize = 15, - - /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=50] - * @memberof boxwhisker# - * @property - */ - maxObjectSize = 50, - - /** - * Percent of box and whisker size that whiskers will be rendered - * see {@link boxwhisker#objectSize} - * @param {number} [whiskerWidthPercent=0.6] - * @memberof boxwhisker# - * @property - */ - whiskerWidthPercent = .6, - - /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof boxwhisker# - * @property - */ - colorFunction$$1 = colorFunction(), - - /** - * The stroke width of the boxes - * @param {number} [boxStrokeWidth=2] - * @memberof boxwhisker# - * @property - */ - boxStrokeWidth = 2, - - /** - * The stroke width of the whiskers - * @param {number} [whiskerStrokeWidth=2] - * @memberof boxwhisker# - * @property - */ - whiskerStrokeWidth = 2, - - - /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof boxwhisker# - * @property - */ - backgroundFill = 'transparent', - - /** - * Namespace for all items made by this instance of boxwhisker - * @param {string} [namespace="d3sm-box-whisker"] - * @memberof boxwhisker# - * @property - */ - namespace = 'd3sm-box-whisker', - - /** - * Class name for boxwhisker container ( element) - * @param {string} [objectClass="box-whisk"] - * @memberof boxwhisker# - * @property - */ - objectClass = 'box-whisk', - - - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof boxwhisker# - * @property - */ - transitionDuration = 1000, - - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof boxwhisker# - * @property - */ - easeFunc = d3.easeExp, - - - /** - * The keys of the boxes - * @param {string[]} [boxKeys=undefined] - * @memberof boxwhisker# - * @property - */ - boxKeys, - - /** - * The values of the boxes - * @param {string[]} [boxValues=undefined] - * @memberof boxwhisker# - * @property - */ - boxValues, - - /** - * The objectSize (actual width) used by the boxes - * @param {number} [objectSize=undefined] - * @memberof boxwhisker# - * @property - */ - objectSize, - - /** - * The spacerSize (actual width) used by the spacers between the boxes - * @param {number} [spacerSize=undefined] - * @memberof boxwhisker# - * @property - */ - spacerSize, - - /** - * Instance of Tooltip - * @param {function} [tooltip=tooltip()] - * @memberof boxwhisker# - * @property - */ - tooltip$$1 = tooltip(); - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {boxwhisker | d3.selection} - * @memberof boxwhisker - * @property - * by default selection = selection - */ - boxwhisker.selection = function (_) { - return arguments.length ? (selection = _, boxwhisker) : selection; - }; - /** - * Gets or sets the data - * (see {@link boxwhisker#data}) - * @param {number} [_=none] - * @returns {boxwhisker | object} - * @memberof boxwhisker - * @property - */ - boxwhisker.data = function (_) { - return arguments.length ? (data = _, boxwhisker) : data; - }; - /** - * Gets or sets the orient of the boxes - * (see {@link boxwhisker#orient}) - * @param {number} [_=none] - * @returns {boxwhisker | object} - * @memberof boxwhisker - * @property - */ - boxwhisker.orient = function (_) { - return arguments.length ? (orient = _, boxwhisker) : orient; - }; - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link boxwhisker#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default spaceX = undefined - */ - boxwhisker.spaceX = function (_) { - return arguments.length ? (spaceX = _, boxwhisker) : spaceX; - }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link boxwhisker#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default spaceY = undefined - */ - boxwhisker.spaceY = function (_) { - return arguments.length ? (spaceY = _, boxwhisker) : spaceY; - }; - /** - * Gets / sets whether or not boxwhisker is allowed to go beyond specified dimensions - * (see {@link boxwhisker#overflowQ}) - * @param {boolean} [_=none] - * @returns {boxwhisker | boolean} - * @memberof boxwhisker - * @property - * by default overflowQ = false - */ - boxwhisker.overflowQ = function (_) { - return arguments.length ? (overflowQ = _, boxwhisker) : overflowQ; - }; - /** - * Gets / sets the grouping of the boxes - * (see {@link boxwhisker#grouping}) - * @param {Array[]} [_=none] - * @returns {boxwhisker | Array[]} - * @memberof boxwhisker - * @property - * by default grouping = undefined - */ - boxwhisker.grouping = function (_) { - return arguments.length ? (grouping = _, boxwhisker) : grouping; - }; - /** - * Gets / sets the quartilesKey - * (see {@link boxwhisker#quartilesKey}) - * @param {string} [_=none] - * @returns {boxwhisker | string} - * @memberof boxwhisker - * @property - * by default quartilesKey = quartiles - */ - boxwhisker.quartilesKey = function (_) { - return arguments.length ? (quartilesKey = _, boxwhisker) : quartilesKey; - }; - /** - * Gets / sets the quartilesKeys - * (see {@link boxwhisker#quartilesKeys}) - * @param {string[]} [_=none] - * @returns {boxwhisker | string[]} - * @memberof boxwhisker - * @property - * by default quartilesKeys = [Q0, Q1, Q2, Q3, Q4] - */ - boxwhisker.quartilesKeys = function (_) { - return arguments.length ? (quartilesKeys = _, boxwhisker) : quartilesKeys; - }; - /** - * Gets / sets the valueExtractor - * (see {@link boxwhisker#valueExtractor}) - * @param {function} [_=none] - * @returns {boxwhisker | function} - * @memberof boxwhisker - * @property - * by default valueExtractor = function(key, index) { return data[key][quartilesKey] }, - */ - - boxwhisker.valueExtractor = function (_) { - return arguments.length ? (valueExtractor = _, boxwhisker) : valueExtractor; - }; - /** - * Gets / sets the sortingFunction - * (see {@link boxwhisker#sortingFunction}) - * @param {function} [_=none] - * @returns {boxwhisker | function} - * @memberof boxwhisker - * @property - * by default sortingFunction = function(keyA, keyB) {return d3.descending( - * valueExtractor(keyA)[quartilesKeys[4]], - * valueExtractor(keyB)[quartilesKeys[4]] - * )}, - */ - boxwhisker.sortingFunction = function (_) { - return arguments.length ? (sortingFunction = _, boxwhisker) : sortingFunction; - }; - /** - * Gets / sets the scale for which the boxwhisker values should be transformed by - * (see {@link boxwhisker#scale}) - * @param {d3.scale} [_=none] - * @returns {boxwhisker | d3.scale} - * @memberof boxwhisker - * @property - * by default scale = d3.scaleLinear() - */ - boxwhisker.scale = function (_) { - return arguments.length ? (scale = _, boxwhisker) : scale; - }; - /** - * Gets / sets the padding for the domain of the scale - * (see {@link boxwhisker#domainPadding}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default domainPadding = 0.5 - */ - boxwhisker.domainPadding = function (_) { - return arguments.length ? (domainPadding = _, boxwhisker) : domainPadding; - }; - /** - * Gets / sets objectSpacer - * (see {@link boxwhisker#objectSpacer}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default objectSpacer = 0.05 - */ - boxwhisker.objectSpacer = function (_) { - return arguments.length ? (objectSpacer = _, boxwhisker) : objectSpacer; - }; - /** - * Gets / sets the minObjectSize - * (see {@link boxwhisker#minObjectSize}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default minObjectSize = 15 - */ - boxwhisker.minObjectSize = function (_) { - return arguments.length ? (minObjectSize = _, boxwhisker) : minObjectSize; - }; - /** - * Gets / sets the maxObjectSize - * (see {@link boxwhisker#maxObjectSize}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default maxObjectSize = 50 - */ - boxwhisker.maxObjectSize = function (_) { - return arguments.length ? (maxObjectSize = _, boxwhisker) : maxObjectSize; - }; - /** - * Gets / sets the whiskerWidthPercent - * (see {@link boxwhisker#whiskerWidthPercent}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default maxObjectSize = 0.6 - */ - boxwhisker.whiskerWidthPercent = function (_) { - return arguments.length ? (whiskerWidthPercent = _, boxwhisker) : whiskerWidthPercent; - }; - /** - * Gets / sets the colorFunction - * (see {@link boxwhisker#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {boxwhisker | colorFunction} - * @memberof boxwhisker - * @property - * by default colorFunction = colorFunction() - */ - boxwhisker.colorFunction = function (_) { - return arguments.length ? (colorFunction$$1 = _, boxwhisker) : colorFunction$$1; - }; - /** - * Gets / sets the boxStrokeWidth - * (see {@link boxwhisker#boxStrokeWidth}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default boxStrokeWidth = 2 - */ - boxwhisker.boxStrokeWidth = function (_) { - return arguments.length ? (boxStrokeWidth = _, boxwhisker) : boxStrokeWidth; - }; - /** - * Gets / sets the whiskerStrokeWidth - * (see {@link boxwhisker#whiskerStrokeWidth}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default whiskerStrokeWidth = 2 - */ - boxwhisker.whiskerStrokeWidth = function (_) { - return arguments.length ? (whiskerStrokeWidth = _, boxwhisker) : whiskerStrokeWidth; - }; - - /** - * Gets / sets the backgroundFill - * (see {@link boxwhisker#backgroundFill}) - * @param {string} [_=none] - * @returns {boxwhisker | string} - * @memberof boxwhisker - * @property - * by default backgroundFill = 'transparent' - */ - boxwhisker.backgroundFill = function (_) { - return arguments.length ? (backgroundFill = _, boxwhisker) : backgroundFill; - }; - /** - * Gets / sets the namespace - * (see {@link boxwhisker#namespace}) - * @param {string} [_=none] - * @returns {boxwhisker | string} - * @memberof boxwhisker - * @property - * by default namespace = 'd3sm-boxwhisker' - */ - boxwhisker.namespace = function (_) { - return arguments.length ? (namespace = _, boxwhisker) : namespace; - }; - /** - * Gets / sets the objectClass - * (see {@link boxwhisker#objectClass}) - * @param {string} [_=none] - * @returns {boxwhisker | string} - * @memberof boxwhisker - * @property - * by default objectClass = 'tick-group' - */ - boxwhisker.objectClass = function (_) { - return arguments.length ? (objectClass = _, boxwhisker) : objectClass; - }; - /** - * Gets / sets the transitionDuration - * (see {@link boxwhisker#transitionDuration}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default transitionDuration = 1000 - */ - boxwhisker.transitionDuration = function (_) { - return arguments.length ? (transitionDuration = _, boxwhisker) : transitionDuration; - }; - /** - * Gets / sets the easeFunc - * (see {@link boxwhisker#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {boxwhisker | d3.ease} - * @memberof boxwhisker - * @property - * by default easeFunc = d3.easeExp - */ - boxwhisker.easeFunc = function (_) { - return arguments.length ? (easeFunc = _, boxwhisker) : easeFunc; - }; - - /** - * Gets / sets the barKeys - * (see {@link boxwhisker#boxKeys}) - * @param {string[]} [_=none] - * @returns {boxwhisker | string[]} - * @memberof boxwhisker - * @property - * by default boxKeys = undefined - */ - boxwhisker.boxKeys = function (_) { - return arguments.length ? (boxKeys = _, boxwhisker) : boxKeys; - }; - /** - * Gets / sets the boxValues - * (see {@link boxwhisker#boxValues}) - * @param {number[]} [_=none] - * @returns {boxwhisker | number[]} - * @memberof boxwhisker - * @property - * by default boxValues = undefined - */ - boxwhisker.boxValues = function (_) { - return arguments.length ? (boxValues = _, boxwhisker) : boxValues; - }; - /** - * Gets / sets the objectSize - * (see {@link boxwhisker#objectSize}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default objectSize = undefined - */ - boxwhisker.objectSize = function (_) { - return arguments.length ? (objectSize = _, boxwhisker) : objectSize; - }; - /** - * Gets / sets the spacerSize - * (see {@link boxwhisker#spacerSize}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default spacerSize = undefined - */ - boxwhisker.spacerSize = function (_) { - return arguments.length ? (spacerSize = _, boxwhisker) : spacerSize; - }; - /** - * Gets / sets the tooltip - * (see {@link boxwhisker#tooltip}) - * @param {tooltip} [_=none] - * @returns {boxwhisker | tooltip} - * @memberof boxwhisker - * @property - * by default tooltip = tooltip() - */ - boxwhisker.tooltip = function (_) { - return arguments.length ? (tooltip$$1 = _, boxwhisker) : tooltip$$1; - }; - - function boxwhisker() { - // for convenience in handling orientation specific values - var horizontalQ = orient == 'horizontal' ? true : false; - var verticalQ = !horizontalQ; - - // background cliping rectangle - var bgcpRect = { x: 0, y: 0, width: spaceX, height: spaceY }; - var container = setupContainer(selection, namespace, bgcpRect, backgroundFill); - - // if grouping is undefined sort keys by sorting funct - var ordered = grouping == undefined ? d3.keys(data).sort(sortingFunction) : grouping; - // to prevent re-calculation and getters to be passed to axes - boxKeys = flatten(ordered); - boxValues = boxKeys.map(valueExtractor); - - var numberOfObjects = boxKeys.length; - var extent = [Math.min.apply(Math, toConsumableArray(boxValues.map(function (d, i) { - return d[quartilesKeys[0]]; - }))) - domainPadding, Math.max.apply(Math, toConsumableArray(boxValues.map(function (d, i) { - return d[quartilesKeys[4]]; - }))) + domainPadding]; - - // set the scale - scale.domain(extent).range(horizontalQ ? [0, spaceY] : [spaceX, 0]); - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - objectSize = calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - // calculate spacer size if needed - spacerSize = calculateWidthOfSpacer(boxKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); - // make the nested groups - var spacerFunction = groupingSpacer().horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects).objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize).transitionDuration(transitionDuration).easeFunc(easeFunc).namespace(namespace); - - // move stuff - spacerFunction(container, ordered, 0); - - var parentIndexArray = []; - container.selectAll('g:not(.to-remove).' + objectClass).each(function (d, i) { - if (hasQ(boxKeys, d)) { - parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); - } - }); - - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' ? colorFunction$$1.dataExtent([0, Math.max.apply(Math, parentIndexArray)]) : colorFunction$$1.dataExtent(extent); - - // set attributes for box and whiskers - container.selectAll('g:not(.to-remove).' + objectClass).each(function (key, i) { - var t = d3.select(this), - currentData = data[key], - quartiles$$1 = valueExtractor(key, i), - q0 = quartiles$$1[quartilesKeys[0]], - q1 = quartiles$$1[quartilesKeys[1]], - q2 = quartiles$$1[quartilesKeys[2]], - q3 = quartiles$$1[quartilesKeys[3]], - q4 = quartiles$$1[quartilesKeys[4]]; - - var i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, q2, i, 'fill'), - // prevent duplicate computation - strokeColor = colorFunction$$1(key, q2, i, 'stroke'); - - var whisk = safeSelect(t, 'g', 'whisker'), - uWhisk = safeSelect(whisk, 'path', 'upper'), - lWhisk = safeSelect(whisk, 'path', 'lower'), - quart = safeSelect(t, 'g', 'quartile'), - uQuart = safeSelect(quart, 'rect', 'upper'), - lQuart = safeSelect(quart, 'rect', 'lower'), - mQuart = safeSelect(quart, 'circle', 'median'); - - // set upper quartile (q3) - uQuart.transition().duration(transitionDuration).ease(easeFunc).attr('width', horizontalQ ? objectSize : scale(q3) - scale(q2)).attr('height', verticalQ ? objectSize : scale(q3) - scale(q2)).attr('fill', fillColor).attr('stroke', strokeColor).attr('stroke-width', boxStrokeWidth).attr('transform', function (d, i) { - var x = horizontalQ ? 0 : scale(q2), - y = verticalQ ? 0 : scale(extent[1]) - scale(q3), - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - - // set lower quartile (q1) - lQuart.transition().duration(transitionDuration).ease(easeFunc).attr('width', horizontalQ ? objectSize : scale(q2) - scale(q1)).attr('height', verticalQ ? objectSize : scale(q2) - scale(q1)).attr('fill', fillColor).attr('stroke', strokeColor).attr('stroke-width', boxStrokeWidth).attr('transform', function (d, i) { - var x = horizontalQ ? 0 : scale(q1), - y = verticalQ ? 0 : scale(extent[1]) - scale(q2), - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - - // set median (q2) - mQuart.transition().duration(transitionDuration).ease(easeFunc).attr('r', function (d, i) { - var r = objectSize / 2; - var dif = (scale(q3) - scale(q1)) / 2; - return r > dif ? dif : r; - }).attr('fill', fillColor).attr('stroke', strokeColor).attr('stroke-width', boxStrokeWidth).attr('transform', function (d, i) { - var x = horizontalQ ? objectSize / 2 : scale(q2), - y = verticalQ ? objectSize / 2 : scale(extent[1]) - scale(q2), - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - - // set lower whisker (min) - lWhisk.transition().duration(transitionDuration).ease(easeFunc).attr('d', function (dd, ii) { - var dir = false, - x = 0, - y = 0, - h = horizontalQ ? scale(q1) - scale(q0) : objectSize, - w = verticalQ ? scale(q1) - scale(q0) : objectSize; - return whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient); - }).attr('transform', function (d, i) { - var x = horizontalQ ? 0 : scale(q1), - y = verticalQ ? 0 : scale(extent[1]) - scale(q1), - t = 'translate(' + x + ',' + y + ')'; - return t; - }).attr('stroke', 'black').attr('stroke-width', whiskerStrokeWidth).attr('fill', 'none'); - - // set upper whisker (max) - uWhisk.transition().duration(transitionDuration).ease(easeFunc).attr('d', function (dd, ii) { - var dir = true, - x = 0, - y = 0, - h = horizontalQ ? scale(q4) - scale(q3) : objectSize, - w = verticalQ ? scale(q4) - scale(q3) : objectSize; - return whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient); - }).attr('transform', function (d, i) { - var x = horizontalQ ? 0 : scale(q3), - y = verticalQ ? 0 : scale(extent[1]) - scale(q4), - t = 'translate(' + x + ',' + y + ')'; - return t; - }).attr('stroke', 'black').attr('stroke-width', whiskerStrokeWidth).attr('fill', 'none'); - }); - - tooltip$$1.selection(container.selectAll('g:not(.to-remove).' + objectClass)).data(data); - tooltip$$1(); - } - - return boxwhisker; - } - - function selectFilter(selection) { - - var data, - namespace = 'd3sm-select-filter', - selectionName = 'Select options:', - defaultValue = undefined; - - var lastValue = undefined; - - selectFilter.data = function (_) { - return arguments.length ? (data = _, selectFilter) : data; - }; - selectFilter.namespace = function (_) { - return arguments.length ? (namespace = _, selectFilter) : namespace; - }; - selectFilter.selectionName = function (_) { - return arguments.length ? (selectionName = _, selectFilter) : selectionName; - }; - selectFilter.defaultValue = function (_) { - return arguments.length ? (defaultValue = _, selectFilter) : defaultValue; - }; - selectFilter.currentOption = currentOption; - - function selectFilter() { - var container = safeSelect(selection, 'div', 'input-group').classed(hypenate(namespace, 'container'), true), - selectPrepend = safeSelect(container, 'div', 'select-prepend').classed('input-group-prepend', true), - selectPrependSpan = safeSelect(selectPrepend, 'span', 'input-group-text').text(selectionName), - select = safeSelect(container, 'select', 'custom-select').classed(hypenate(namespace, 'select'), true), - selectAppend = safeSelect(container, 'div', 'select-append').classed('input-group-prepend', true), - selectAppendButton = safeSelect(selectAppend, 'a', 'filter-button').classed('btn btn-outline-secondary', true), - filterButtonIcon = safeSelect(selectAppendButton, 'i', 'fa fa-filter'), - inputGroup = safeSelect(container, 'div', 'filter-input-group').classed('input-group', true).classed('d-none', true), - inputPrepend = safeSelect(inputGroup, 'div', 'input-group-prepend'), - inputPrependSpan = safeSelect(inputPrepend, 'span', 'input-group-text').classed('search-button', true), - inputPrependSpanIcon = safeSelect(inputPrependSpan, 'i', 'fa fa-search'), - input = safeSelect(inputGroup, 'input', 'form-control').attr('placeholder', 'all').attr('type', 'text'), - inputAppend = safeSelect(inputGroup, 'div', 'input-group-append'), - inputAppendButton = safeSelect(inputAppend, 'a', 'close-button').classed('btn btn-outline-secondary', true), - inputAppendButtonIcon = safeSelect(inputAppendButton, 'i', 'fa fa-close'); - - var keys = d3.keys(data), - options = select.selectAll('option'); - - options = options.data(d3.keys(data)); - options = options.merge(options.enter().append('option')).attr('value', function (d, i) { - return d; - }).text(function (d, i) { - return d; - }); - - var filterButton = selectAppendButton, - closeButton = inputAppendButton; - - filterButton.on('click', function (d, i) { - var currentStyle = inputGroup.classed('d-none'); - inputGroup.classed('d-none', !currentStyle); - }); - - closeButton.on('click', function (d, i) { - input.property('value', '').dispatch('input'); - }); - - input.on('input', function (d, i) { - var val = input.property('value'), - reg = new RegExp(val, 'gi'), - use; - - if (val == '') { - use = keys; - } else { - use = []; - d3.keys(data).map(function (option, j) { - var match = option.match(reg); - if (match == null || match.join('') == '') ; else { - use.push(option); - } - }); - } - - options = select.selectAll('option'); - options = options.data(use); - options.exit().remove(); - options = options.merge(options.enter().append('option')).attr('value', function (d, i) { - return d; - }).text(function (d, i) { - return d; - }); - - var current = currentOption(); - if (lastValue != current) { - lastValue = current; - select.dispatch('change'); - } - }); - } - - function currentOption() { - var val = selection.select("select").property('value'); - return val == undefined || val == '' ? defaultValue == undefined ? d3.keys(data)[0] : defaultValue : val; - } - - return selectFilter; - } - - /******************************************************************************* - ** ** - ** ** - ** DATATOGGLE ** - ** ** - ** ** - *******************************************************************************/ - /** - * Creates a datatoggle - * @constructor datatoggle - * @param {d3.selection} selection - * @namespace datatoggle - * @returns {function} datatoggle - */ - function datatoggle(selection) { - var - /** - * What to do when a different key is clicked - * (see {@link datatoggle#updateFunction}) - * @param {function} [updateFunction=function(){}] - * @memberof datatoggle# - * @property - */ - updateFunction = function updateFunction() {}, - - /** - * Namespace for all items made by this instance of datatoggle - * @param {string} [namespace="d3sm-databar"] - * @memberof datatoggle# - * @property - */ - namespace = 'd3sm-databar', - - xAxisSelectQ = false, - xAxisOptions, - yAxisSelectQ = false, - yAxisOptions, - data; - toggle.xAxisSelectQ = function (_) { - return arguments.length ? (xAxisSelectQ = _, toggle) : xAxisSelectQ; - }; - toggle.yAxisSelectQ = function (_) { - return arguments.length ? (yAxisSelectQ = _, toggle) : yAxisSelectQ; - }; - toggle.xAxisOptions = function (_) { - return arguments.length ? (xAxisOptions = _, toggle) : xAxisOptions; - }; - toggle.yAxisOptions = function (_) { - return arguments.length ? (yAxisOptions = _, toggle) : yAxisOptions; - }; - toggle.data = function (_) { - return arguments.length ? (data = _, toggle) : data; - }; - - /** - * Gets / sets the updateFunction - * (see {@link datatoggle#updateFunction}) - * @function datatoggle.updateFunction - * @param {function} [_=none] - * @returns {datatoggle | function} - * @memberof datatoggle - * @property - * by default updateFunction = function(){} - */ - toggle.updateFunction = function (_) { - return arguments.length ? (updateFunction = _, toggle) : updateFunction; - }; - /** - * Gets / sets the namespace - * (see {@link datatoggle#namespace}) - * @function datatoggle.namespace - * @param {string} [_=none] - * @returns {datatoggle | string} - * @memberof datatoggle - * @property - * by default namespace = 'd3sm-databar' - */ - toggle.namespace = function (_) { - return arguments.length ? (namespace = _, toggle) : namespace; - }; - /** - * Gets / sets the currentKey - * (see {@link datatoggle#currentKey}) - * @function datatoggle.currentKey - * @param {string} [_=none] - * @returns {datatoggle | string} - * @memberof datatoggle - * @property - * by default currentKey = undefined - */ - toggle.currentKeys = function () { - var vals = {}; - d3.keys(filterSelects).map(function (k, i) { - vals[k] = filterSelects[k].currentOption(); - }); - return vals; - }; - - var filterSelects = {}; - function toggle() { - // selection options - - // selection.classed('d-flex flex-row', true) - // var filterButton = safeSelect(selection, 'a', 'slider-buttons') - // var filterI = safeSelect(filterButton, 'i', 'fa fa-sliders') - - /*BUG: unexpected behavior. - - when using bootstrap-eque way for collapse, clicking button submits to - the same page in applications (but not in demo), so using anchor () - - when using anchor, cause page to jump to that location - - when using show, the first open works, but the close does not. - */ - // filterButton.attr('class', 'btn btn-secondary') - // .attr('data-toggle', 'collapse') - // .attr('href', '#'+hypenate(namespace, 'data-toggle')) - // .attr('target', '_blank') - // .html(filterButton.html()+' Filters') - // .on('click', function(d, i){ - // d3.event.preventDefault() - // d3.event.stopPropagation() - // var dt = d3.select("#"+hypenate(namespace, 'data-toggle')) - // dt.classed('show', !dt.classed('show')) - // dt.classed('d-inline-flex', dt.classed('show')) - // - // filterButton.classed('btn-primary', dt.classed('show')) - // filterButton.classed('btn-secondary', !dt.classed('show')) - // }) - // .classed('d-inline-flex', true) - // .style("margin-right", '10px') - - - // var datatoggleCollapse = safeSelect(selection, 'div', hypenate(namespace,'collapse')) - // .attr('id', hypenate(namespace, 'data-toggle')) - // .classed('collapse', true) - - // var flexRow = safeSelect(datatoggleCollapse, 'div', 'd-inline-flex flex-row flex-wrap') - var flexRow = safeSelect(selection, 'div', 'd-inline-flex flex-row flex-wrap'); - - var dataopts = flexRow.selectAll('div.' + hypenate(namespace, 'select-filter')); - // remove excess - dataopts.exit().remove(); - // bind data - dataopts = dataopts.data(d3.keys(data)); - //enter - var doEnter = dataopts.enter().append('div').attr('class', 'select-filter'); - - dataopts = dataopts.merge(doEnter).style('margin-right', "10px"); - - dataopts.each(function (d, i) { - var t = d3.select(this); - var sf = selectFilter(t).data(data[d]).namespace(hypenate(namespace, d)).selectionName(d); - sf(); - filterSelects[d] = sf; - }); - - selection.selectAll('select').on('change', function () { - updateFunction(); - }); - // bind update function - // d3.selectAll - return toggle; - } - - return toggle; - } - - /******************************************************************************* - ** ** - ** ** - ** SCATTER ** - ** ** - ** ** - *******************************************************************************/ - /** - * Creates a scatter - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/scatter/index.html Demo} - * @constructor scatter - * @param {d3.selection} selection - * @namespace scatter - * @returns {function} scatter - */ - function scatter(selection) { - - var - /** - * Data to plot. Assumed to be a object, where each key corresponds to a point - * (see {@link scatter#data}) - * @param {Object} [data=undefined] - * @memberof scatter# - * @property - */ - data, - - /** - * Amount of horizontal space (in pixels) avaible to render the scatter in - * (see {@link scatter#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof scatter# - * @property - */ - spaceX, - - /** - * Amount of vertical space (in pixels) avaible to render the scatter in - * (see {@link scatter.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof scatter# - * @property - */ - spaceY, - - - /** - * The scale for which scatter x values should be transformed by - * @param {d3.scale} [scaleX=d3.scaleLinear] - * @memberof scatter# - * @property - */ - scaleX = d3.scaleLinear(), - - /** - * The padding for the domain of the scaleX (see {@link scatter#scaleX}) - * @param {number} [domainPaddingX=0.5] - * @memberof scatter# - * @property - */ - domainPaddingX = 0.5, - - /** - * The function for getting the x value of the current point - * @param {function} [valueExtractorX=function(d, i){return data[d]['x']}] - * @memberof scatter# - * @property - */ - valueExtractorX = function valueExtractorX(d, i) { - return data[d]['x']; - }, - - - /** - * The scale for which scatter y values should be transformed by - * @param {d3.scale} [scaleY=d3.scaleLinear] - * @memberof scatter# - * @property - */ - scaleY = d3.scaleLinear(), - - /** - * The padding for the domain of the scaleY (see {@link scatter#scaleY}) - * @param {number} [domainPaddingY=0.5] - * @memberof scatter# - * @property - */ - domainPaddingY = 0.5, - - /** - * The function for getting the y value of the current point - * @param {function} [valueExtractorY=function(d, i){return data[d]['y']}] - * @memberof scatter# - * @property - */ - valueExtractorY = function valueExtractorY(d, i) { - return data[d]['y']; - }, - - - /** - * The scale for which scatter r values should be transformed by - * @param {d3.scale} [scaleR=d3.scaleLinear] - * @memberof scatter# - * @property - */ - scaleR = d3.scaleLinear(), - - /** - * The padding for the domain of the scaleR (see {@link scatter#scaleR}) - * @param {number} [domainPaddingR=0.5] - * @memberof scatter# - * @property - */ - domainPaddingR = 0.5, - - /** - * The function for getting the r value of the current point - * @param {function} [valueExtractorR=function(d, i){return 2}] - * @memberof scatter# - * @property - */ - valueExtractorR = function valueExtractorR(d, i) { - return 2; - }, - - /** - * The min radius a point can have - * @param {function} [minRadius=2] - * @memberof scatter# - * @property - */ - minRadius = 2, - - /** - * The min radius a point can have - * @param {function} [maxRadius=10] - * @memberof scatter# - * @property - */ - maxRadius = 10, - - - /** - * The stroke width of the points - * @param {number} [pointStrokeWidth=2] - * @memberof scatter# - * @property - */ - pointStrokeWidth = 2, - - /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof scatter# - * @property - */ - colorFunction$$1 = colorFunction(), - - /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof scatter# - * @property - */ - backgroundFill = 'transparent', - - /** - * Namespace for all items made by this instance of scatter - * @param {string} [namespace="d3sm-scatter"] - * @memberof scatter# - * @property - */ - namespace = 'd3sm-scatter', - - /** - * Class name for scatter container ( element) - * @param {string} [objectClass="scatter-point"] - * @memberof scatter# - * @property - */ - objectClass = 'scatter-point', - - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof scatter# - * @property - */ - transitionDuration = 1000, - - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof scatter# - * @property - */ - easeFunc = d3.easeExp, - - - // useful values to extract to prevent re-calculation - /** - * The keys of the points - * @param {string[]} [pointKeys=undefined] - * @memberof scatter# - * @property - */ - pointKeys, - - /** - * The x values of the points - * @param {number[]} [valuesX=undefined] - * @memberof scatter# - * @property - */ - valuesX, - - /** - * The y values of the points - * @param {number[]} [valuesY=undefined] - * @memberof scatter# - * @property - */ - valuesY, - - /** - * The r values of the points - * @param {number[]} [valuesR=undefined] - * @memberof scatter# - * @property - */ - valuesR, - - - /** - * Instance of Tooltip - * @param {function} [tooltip=tooltip()] - * @memberof scatter# - * @property - */ - tooltip$$1 = tooltip(); - - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {scatter | d3.selection} - * @memberof scatter - * @property - * by default selection = selection - */ - scatter.selection = function (_) { - return arguments.length ? (selection = _, scatter) : selection; - }; - /** - * Gets or sets the data - * (see {@link scatter#data}) - * @param {number} [_=none] - * @returns {scatter | object} - * @memberof scatter - * @property - */ - scatter.data = function (_) { - return arguments.length ? (data = _, scatter) : data; - }; - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link scatter#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {scatter | number} - * @memberof scatter - * @property - * by default spaceX = undefined - */ - scatter.spaceX = function (_) { - return arguments.length ? (spaceX = _, scatter) : spaceX; - }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link scatter#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {scatter | number} - * @memberof scatter - * @property - * by default spaceY = undefined - */ - scatter.spaceY = function (_) { - return arguments.length ? (spaceY = _, scatter) : spaceY; - }; - - /** - * Gets / sets the x scale for which the scatter x values should be transformed by - * (see {@link scatter#scaleX}) - * @param {d3.scale} [_=none] - * @returns {scatter | d3.scale} - * @memberof scatter - * @property - * by default scaleX = d3.scaleLinear() - */ - scatter.scaleX = function (_) { - return arguments.length ? (scaleX = _, scatter) : scaleX; - }; - /** - * Gets / sets the padding for the domain of the x scale - * (see {@link scatter#domainPaddingX}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter - * @property - * by default domainPaddingX = 0.5 - */ - scatter.domainPaddingX = function (_) { - return arguments.length ? (domainPaddingX = _, scatter) : domainPaddingX; - }; - /** - * Gets / sets the valueExtractorX - * (see {@link scatter#valueExtractorX}) - * @param {function} [_=none] - * @returns {scatter | function} - * @memberof scatter - * @property - * by default valueExtractorX = function(key, index) { return data[key]['x'] } - */ - scatter.valueExtractorX = function (_) { - return arguments.length ? (valueExtractorX = _, scatter) : valueExtractorX; - }; - - /** - * Gets / sets the y scale for which the scatter y values should be transformed by - * (see {@link scatter#scaleY}) - * @param {d3.scale} [_=none] - * @returns {scatter | d3.scale} - * @memberof scatter - * @property - * by default scaleY = d3.scaleLinear() - */ - scatter.scaleY = function (_) { - return arguments.length ? (scaleY = _, scatter) : scaleY; - }; - /** - * Gets / sets the padding for the domain of the y scale - * (see {@link scatter#domainPaddingY}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter - * @property - * by default domainPaddingY = 0.5 - */ - scatter.domainPaddingY = function (_) { - return arguments.length ? (domainPaddingY = _, scatter) : domainPaddingY; - }; - /** - * Gets / sets the valueExtractorY - * (see {@link scatter#valueExtractorY}) - * @param {function} [_=none] - * @returns {scatter | function} - * @memberof scatter - * @property - * by default valueExtractorY = function(key, index) { return data[key]['y'] } - */ - scatter.valueExtractorY = function (_) { - return arguments.length ? (valueExtractorY = _, scatter) : valueExtractorY; - }; - - /** - * Gets / sets the r scale for which the scatter r values should be transformed by - * (see {@link scatter#scaleR}) - * @param {d3.scale} [_=none] - * @returns {scatter | d3.scale} - * @memberof scatter - * @property - * by default scaleR = d3.scaleLinear() - */ - scatter.scaleR = function (_) { - return arguments.length ? (scaleR = _, scatter) : scaleR; - }; - /** - * Gets / sets the padding for the domain of the r scale - * (see {@link scatter#domainPaddingR}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter - * @property - * by default domainPaddingR = 0.5 - */ - scatter.domainPaddingR = function (_) { - return arguments.length ? (domainPaddingR = _, scatter) : domainPaddingR; - }; - /** - * Gets / sets the valueExtractorR - * (see {@link scatter#valueExtractorR}) - * @param {function} [_=none] - * @returns {scatter | function} - * @memberof scatter - * @property - * by default valueExtractorR = function(key, index) { return data[key]['r'] } - */ - scatter.valueExtractorR = function (_) { - return arguments.length ? (valueExtractorR = _, scatter) : valueExtractorR; - }; - /** - * Gets / sets the minRadius - * (see {@link scatter#minRadius}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter - * @property - * by default minRadius = 2 - */ - scatter.minRadius = function (_) { - return arguments.length ? (minRadius = _, scatter) : minRadius; - }; - /** - * Gets / sets the maxRadius - * (see {@link scatter#maxRadius}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter - * @property - * by default maxRadius = 10 - */ - scatter.maxRadius = function (_) { - return arguments.length ? (maxRadius = _, scatter) : maxRadius; - }; - - /** - * Gets / sets the pointStrokeWidth - * (see {@link scatter#pointStrokeWidth}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter - * @property - * by default pointStrokeWidth = 2 - */ - scatter.pointStrokeWidth = function (_) { - return arguments.length ? (pointStrokeWidth = _, scatter) : pointStrokeWidth; - }; - /** - * Gets / sets the colorFunction - * (see {@link scatter#colorFunction}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter - * @property - * by default colorFunction = colorFunction() - */ - scatter.colorFunction = function (_) { - return arguments.length ? (colorFunction$$1 = _, scatter) : colorFunction$$1; - }; - /** - * Gets / sets the backgroundFill - * (see {@link scatter#backgroundFill}) - * @param {string} [_=none] - * @returns {scatter | string} - * @memberof scatter - * @property - * by default backgroundFill = 'transparent' - */ - scatter.backgroundFill = function (_) { - return arguments.length ? (backgroundFill = _, scatter) : backgroundFill; - }; - /** - * Gets / sets the namespace - * (see {@link scatter#namespace}) - * @param {string} [_=none] - * @returns {scatter | string} - * @memberof scatter - * @property - * by default namespace = 'd3sm-scatter' - */ - scatter.namespace = function (_) { - return arguments.length ? (namespace = _, scatter) : namespace; - }; - /** - * Gets / sets the objectClass - * (see {@link scatter#objectClass}) - * @param {string} [_=none] - * @returns {scatter | string} - * @memberof scatter - * @property - * by default objectClass = 'tick-group' - */ - scatter.objectClass = function (_) { - return arguments.length ? (objectClass = _, scatter) : objectClass; - }; - /** - * Gets / sets the transitionDuration - * (see {@link scatter#transitionDuration}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter - * @property - * by default transitionDuration = 1000 - */ - scatter.transitionDuration = function (_) { - return arguments.length ? (transitionDuration = _, scatter) : transitionDuration; - }; - /** - * Gets / sets the easeFunc - * (see {@link scatter#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {scatter | d3.ease} - * @memberof scatter - * @property - * by default easeFunc = d3.easeExp - */ - scatter.easeFunc = function (_) { - return arguments.length ? (easeFunc = _, scatter) : easeFunc; - }; - - /** - * Gets / sets the pointKeys - * (see {@link scatter#pointKeys}) - * @param {string[]} [_=none] - * @returns {scatter | string[]} - * @memberof scatter - * @property - * by default pointKeys = undefined - */ - scatter.pointKeys = function (_) { - return arguments.length ? (pointKeys = _, scatter) : pointKeys; - }; - /** - * Gets / sets the valuesX - * (see {@link scatter#valuesX}) - * @param {number[]} [_=none] - * @returns {scatter | number[]} - * @memberof scatter - * @property - * by default valuesX = undefined - */ - scatter.valuesX = function (_) { - return arguments.length ? (valuesX = _, scatter) : valuesX; - }; - /** - * Gets / sets the valuesY - * (see {@link scatter#valuesY}) - * @param {number[]} [_=none] - * @returns {scatter | number[]} - * @memberof scatter - * @property - * by default valuesY = undefined - */ - scatter.valuesY = function (_) { - return arguments.length ? (valuesY = _, scatter) : valuesY; - }; - /** - * Gets / sets the valuesR - * (see {@link scatter#valuesR}) - * @param {number[]} [_=none] - * @returns {scatter | number[]} - * @memberof scatter - * @property - * by default valuesR = undefined - */ - scatter.valuesR = function (_) { - return arguments.length ? (valuesR = _, scatter) : valuesR; - }; - /** - * Gets / sets the tooltip - * (see {@link scatter#tooltip}) - * @param {tooltip} [_=none] - * @returns {scatter | tooltip} - * @memberof scatter - * @property - * by default tooltip = tooltip() - */ - - scatter.tooltip = function (_) { - return arguments.length ? (tooltip$$1 = _, scatter) : tooltip$$1; - }; - - function scatter() { - // background cliping rectangle - var bgcpRect = { x: 0, y: 0, width: spaceX, height: spaceY }; - var container = setupContainer(selection, namespace, bgcpRect, backgroundFill); - - pointKeys = d3.keys(data); - valuesX = pointKeys.map(valueExtractorX); - valuesY = pointKeys.map(valueExtractorY); - valuesR = pointKeys.map(valueExtractorR); - - var numberOfObjects = pointKeys.length; - var extentX = [Math.min.apply(Math, toConsumableArray(valuesX)) - domainPaddingX, Math.max.apply(Math, toConsumableArray(valuesX)) + domainPaddingX]; - var extentY = [Math.min.apply(Math, toConsumableArray(valuesY)) - domainPaddingY, Math.max.apply(Math, toConsumableArray(valuesY)) + domainPaddingY]; - var extentR = [Math.min.apply(Math, toConsumableArray(valuesR)) - domainPaddingR, Math.max.apply(Math, toConsumableArray(valuesR)) + domainPaddingR]; - - scaleX.domain(extentX).range([0, spaceX]); - scaleY.domain(extentY).range([spaceY, 0]); - scaleR.domain(extentR).range([minRadius, maxRadius]); - - var points = container.selectAll('.' + objectClass); - points = points.data(pointKeys); - var pEnter = points.enter().append('circle').attr('class', objectClass).attr('cx', 0).attr('cy', spaceY).attr('r', 0); - - var pExit = points.exit(); - - points = points.merge(pEnter); - - points.each(function (key, i) { - var t = d3.select(this), - currentData = data[key], - x = valuesX[i], - y = valuesY[i], - r = valuesR[i], - fillColor = colorFunction$$1(key, currentData, i, 'fill'), - strokeColor = colorFunction$$1(key, currentData, i, 'stroke'); - - t.transition().duration(transitionDuration).ease(easeFunc).attr('cx', scaleX(x)).attr('cy', scaleY(y)).attr('r', scaleR(r)).attr('fill', fillColor).attr('stroke', strokeColor).attr('stroke-width', pointStrokeWidth); - - t.on('mouseover', function (d, i) { - points.style('opacity', 0.2); - t.style('opacity', 1); - t.transition().duration(transitionDuration / 2).ease(easeFunc).attr('stroke-width', pointStrokeWidth * 2).attr('r', scaleR(r) * 1.5); - }); - t.node().addEventListener('mouseout', function () { - container.selectAll('.' + objectClass).style('opacity', 1); - t.transition().duration(transitionDuration / 2).ease(easeFunc).attr('stroke-width', pointStrokeWidth).attr('r', scaleR(r)); - }); - }); - - pExit.transition().duration(transitionDuration).ease(easeFunc).attr('cx', 0).attr('cy', spaceY).attr('r', 0).remove(); - - tooltip$$1.selection(points).data(data); - - tooltip$$1(); - } - - return scatter; - } - - /******************************************************************************* - - ** ** - ** ** - ** PLOTZOOM ** - ** ** - ** ** - *******************************************************************************/ - /** - * Creates an plotZoom instance, which can handle drag and scroll events - * @constructor plotZoom - * @param {function} chart a function instance of one of the d3sm plots (e.g. bar, boxwhisker, bubbleHeatmap, violin, etc) - * @param {axis} xAxis the axis instance responsible for the x axis - * @param {axis} yAxis the axis instance responsible for the y axis - * @namespace plotZoom - * @returns {function} zoom - */ - function plotZoom(chart, xAxis, yAxis) { - var - /** - * The event on which to fire - * (see {@link plotZoom#eventType}) - * @param {string} eventType which event it should handle. Currently supports scroll and wheel - * @memberof plotZoom# - * @property - */ - eventType, - - /** - * A scaling factor for the wheel "speed" - * (see {@link plotZoom#wheelSpeed}) - * @param {number} [wheelSpeed=20] scales the wheel translation by wheelSpeed - * @memberof plotZoom# - * @property - */ - wheelSpeed = 20, - - /** - * The orientation in which to allow scrolling: 'horizontal', 'vertical', or '2D' - * (see {@link plotZoom#orient}) - * @param {string} [orient=chart.orient() || 'horizontal'] - * @memberof plotZoom# - * @property - */ - orient = chart.orient == undefined ? 'horizontal' : chart.orient(), - - /** - * The max distance allowed to scroll in the x direction - * (see {@link plotZoom#xLock}) - * @param {number} [xLock=chart.spaceX()] ideally chart.overflowQ() == true and this value is the - * bounding rect across all elements in the chart minus the space in which to show. - * @memberof plotZoom# - * @property - */ - xLock = chart.spaceX(), - - /** - * The max distance allowed to scroll in the y direction - * (see {@link plotZoom#yLock}) - * @param {number} [yLock=chart.spaceY()] ideally chart.overflowQ() == true and this value is the - * bounding rect across all elements in the chart minus the space in which to show. - * @memberof plotZoom# - * @property - */ - yLock = chart.spaceY(), - chartSel = chart.selection(), - xAxisSel = xAxis.selection(), - yAxisSel = yAxis.selection(), - svg = d3.select(chartSel.thisSVG()); - - /** - * Gets or sets the event type in which to respond - * (see {@link plotZoom#eventType}) - * @param {string} [_=none] should be 'drag' or 'wheel' - * @returns {zoom | string} - * @memberof plotZoom - * @property - * by default plotZoom=undefined - */ - zoom.eventType = function (_) { - return arguments.length ? (eventType = _, zoom) : eventType; - }; - /** - * Gets or sets the wheel speed in which to scale the wheel scroll transform - * (see {@link plotZoom#wheelSpeed}) - * @param {number} [_=none] - * @returns {zoom | number} - * @memberof plotZoom - * @property - * by default wheelSpeed=20 - */ - zoom.wheelSpeed = function (_) { - return arguments.length ? (wheelSpeed = _, zoom) : wheelSpeed; - }; - /** - * Gets or sets the orientation in which items are manipulated - * (see {@link plotZoom#orient}) - * @param {string} [_=none] should be horizontal, vertical, or 2D - * @returns {zoom | string} - * @memberof plotZoom - * @property - * by default orient=chart.orient() || 'horizontal' - */ - zoom.orient = function (_) { - return arguments.length ? (orient = _, zoom) : orient; - }; - - /** - * Gets or sets the max distance in which to scroll X - * (see {@link plotZoom#xLock}) - * @param {number} [_=none] should be a positive value - * @returns {zoom | number} - * @memberof plotZoom - * @property - * by default xLock=chart.spaceX() - */ - zoom.xLock = function (_) { - return arguments.length ? (xLock = _, zoom) : xLock; - }; - /** - * Gets or sets the max distance in which to scroll Y - * (see {@link plotZoom#yLock}) - * @param {number} [_=none] should be a positive value - * @returns {zoom | number} - * @memberof plotZoom - * @property - * by default yLock=chart.spaceY() - */ - zoom.yLock = function (_) { - return arguments.length ? (yLock = _, zoom) : yLock; - }; - - function setLocks() { - var chartObjSel = chartSel.select('.' + hypenate(chart.namespace(), 'object-container')); - var chartObjTrans = getTranslation(chartObjSel.attr('transform')); - var cos = chartObjSel.attr('transform', 'translate(0,0)'); - xLock = chartSel.node().getBBox().width - chart.spaceX() * .9; - yLock = chartSel.node().getBBox().height - chart.spaceY() * .9; - cos.attr('transform', 'translate(' + chartObjTrans[0] + ',' + chartObjTrans[1] + ')'); - log('plotZoom', 'setLocks', { xLock: xLock, yLock: yLock }); - } - - /** - * Sets the x and y locks (how far one can scroll) - * (see {@link plotZoom#xLock} and {@link plotZoom#yLock}) - * @function plotZoom.setLocks - * @returns {undefined} - * @memberof plotZoom - * @property - */ - zoom.setLocks = setLocks; - - function zoom() { - setLocks(); - - var horizontalQ, verticalQ; - if (orient == '2D') { - horizontalQ = true;verticalQ = true; - } - if (orient == 'horizontal') { - horizontalQ = true;verticalQ = false; - } - if (orient == 'vertical') { - verticalQ = true;horizontalQ = false; - } - - // capture transform event - var transform = d3.event.transform; - - var chartBox = chartSel.node().getBBox(); - var xAxisBox = xAxisSel.node().getBBox(); - var yAxisBox = xAxisSel.node().getBBox(); - - var chartWidth = chartBox.width - chartBox.x; - var chartHeight = chartBox.height - chartBox.y; - var xAxisWidth = xAxisBox.width; // - xAxisBox.x - var xAxisHeight = xAxisBox.height; // - xAxisBox.y - var yAxisWidth = yAxisBox.width; // - yAxisBox.x - var yAxisHeight = yAxisBox.height; // -yAxisBox.y - - // enable wheel event - if (eventType == "wheel") { - var e = d3.event; - // prevent page scrolling - e.preventDefault(); - // event delta is very very slow, so speed it up with wheel speed - var w = d3.event.deltaY * wheelSpeed; - var shiftQ = d3.event.shiftKey; - - // d3 has no way to make custom transform, so make an object and add - // the necessary functions to make wheel event compatible with drag events - if (orient == '2D') { - transform = shiftQ ? { k: 1, x: w, y: 0 } : { k: 1, x: 0, y: w }; - } else { - transform = horizontalQ ? { k: 1, x: w, y: 0 } : { k: 1, x: 0, y: w }; - } - // the * -1 inverts the direction - transform.applyX = function (x) { - return x * this.k + this.x * -1; - }; - transform.applyY = function (y) { - return y * this.k + this.y * -1; - }; - } - - var chartObjSel = chartSel.select('.' + hypenate(chart.namespace(), 'object-container')); - var xAxisObjSel = xAxisSel.select('.' + hypenate(xAxis.namespace(), 'object-container')); - var yAxisObjSel = yAxisSel.select('.' + hypenate(yAxis.namespace(), 'object-container')); - - // xLock = chartSel.node().getBBox().width - chart.spaceX() - chartSel.node().getBBox().x - // yLock = chartSel.node().getBBox().height - chart.spaceY() - // console.table({'xLock':xLock, "yLock":yLock}) - // bhm.selection().node().getBBox().width - bhm.spaceX() - - - var chartObjTrans = getTranslation(chartObjSel.attr('transform')); - var xAxisObjTrans = getTranslation(xAxisObjSel.attr('transform')); - var yAxisObjTrans = getTranslation(yAxisObjSel.attr('transform')); - - var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0; - if (horizontalQ) { - x = x < -xLock ? (transform.x = 0, -xLock) : (transform.x = 0, Math.min(x, 0)); - } - - var y = verticalQ ? transform.applyY(chartObjTrans[1]) : 0; - if (verticalQ) { - y = y < -yLock ? (transform.y = 0, -yLock) : (transform.y = 0, Math.min(y, 0)); - } - - chartObjSel.attr('transform', 'translate(' + x + ',' + y + ')'); - if (horizontalQ) { - xAxisObjSel.attr('transform', 'translate(' + x + ',' + 0 + ')'); - } - if (verticalQ) { - yAxisObjSel.attr('transform', 'translate(' + 0 + ',' + y + ')'); - } - - // var lasso = svg.select(".lasso-container") - // if (!lasso.empty()) { - // lasso.attr('transform', 'translate('+x+','+y+')') - // } - } - - zoom.reset = function () { - - var chartObjSel = chartSel.select('.' + hypenate(chart.namespace(), 'object-container')); - var xAxisObjSel = xAxisSel.select('.' + hypenate(xAxis.namespace(), 'object-container')); - var yAxisObjSel = yAxisSel.select('.' + hypenate(yAxis.namespace(), 'object-container')); - chartObjSel.attr('transform', 'translate(' + 0 + ',' + 0 + ')'); - xAxisObjSel.attr('transform', 'translate(' + 0 + ',' + 0 + ')'); - yAxisObjSel.attr('transform', 'translate(' + 0 + ',' + 0 + ')'); - }; - - return zoom; - } - - /******************************************************************************* - - ** ** - ** ** - ** PLOTZOOM ** - ** ** - ** ** - *******************************************************************************/ - /** - * Creates an plotZoom instance, which can handle drag and scroll events - * @constructor plotZoom - * @param {function} chart a function instance of one of the d3sm plots (e.g. bar, boxwhisker, bubbleHeatmap, violin, etc) - * @param {axis} xAxis the axis instance responsible for the x axis - * @param {axis} yAxis the axis instance responsible for the y axis - * @namespace plotZoom - * @returns {function} zoom - */ - function multiPlotZoom(chart) { - var - /** - * The event on which to fire - * (see {@link plotZoom#eventType}) - * @param {string} eventType which event it should handle. Currently supports scroll and wheel - * @memberof plotZoom# - * @property - */ - eventType, - - /** - * A scaling factor for the wheel "speed" - * (see {@link plotZoom#wheelSpeed}) - * @param {number} [wheelSpeed=20] scales the wheel translation by wheelSpeed - * @memberof plotZoom# - * @property - */ - wheelSpeed = 20, - - /** - * The orientation in which to allow scrolling: 'horizontal', 'vertical', or '2D' - * (see {@link plotZoom#orient}) - * @param {string} [orient=chart.orient() || 'horizontal'] - * @memberof plotZoom# - * @property - */ - orient = chart.orient == undefined ? 'horizontal' : chart.orient(), - - /** - * The max distance allowed to scroll in the x direction - * (see {@link plotZoom#xLock}) - * @param {number} [xLock=chart.spaceX()] ideally chart.overflowQ() == true and this value is the - * bounding rect across all elements in the chart minus the space in which to show. - * @memberof plotZoom# - * @property - */ - xLock = chart.spaceX(), - - /** - * The max distance allowed to scroll in the y direction - * (see {@link plotZoom#yLock}) - * @param {number} [yLock=chart.spaceY()] ideally chart.overflowQ() == true and this value is the - * bounding rect across all elements in the chart minus the space in which to show. - * @memberof plotZoom# - * @property - */ - yLock = chart.spaceY(), - chartSel = chart.selection(), - svg = d3.select(chartSel.thisSVG()), - xComponents = [], - yComponents = []; - - /** - * Gets or sets the event type in which to respond - * (see {@link plotZoom#eventType}) - * @param {string} [_=none] should be 'drag' or 'wheel' - * @returns {zoom | string} - * @memberof plotZoom - * @property - * by default plotZoom=undefined - */ - zoom.eventType = function (_) { - return arguments.length ? (eventType = _, zoom) : eventType; - }; - /** - * Gets or sets the wheel speed in which to scale the wheel scroll transform - * (see {@link plotZoom#wheelSpeed}) - * @param {number} [_=none] - * @returns {zoom | number} - * @memberof plotZoom - * @property - * by default wheelSpeed=20 - */ - zoom.wheelSpeed = function (_) { - return arguments.length ? (wheelSpeed = _, zoom) : wheelSpeed; - }; - /** - * Gets or sets the orientation in which items are manipulated - * (see {@link plotZoom#orient}) - * @param {string} [_=none] should be horizontal, vertical, or 2D - * @returns {zoom | string} - * @memberof plotZoom - * @property - * by default orient=chart.orient() || 'horizontal' - */ - zoom.orient = function (_) { - return arguments.length ? (orient = _, zoom) : orient; - }; - - /** - * Gets or sets the max distance in which to scroll X - * (see {@link plotZoom#xLock}) - * @param {number} [_=none] should be a positive value - * @returns {zoom | number} - * @memberof plotZoom - * @property - * by default xLock=chart.spaceX() - */ - zoom.xLock = function (_) { - return arguments.length ? (xLock = _, zoom) : xLock; - }; - /** - * Gets or sets the max distance in which to scroll Y - * (see {@link plotZoom#yLock}) - * @param {number} [_=none] should be a positive value - * @returns {zoom | number} - * @memberof plotZoom - * @property - * by default yLock=chart.spaceY() - */ - zoom.yLock = function (_) { - return arguments.length ? (yLock = _, zoom) : yLock; - }; - - zoom.xComponents = function (_) { - return arguments.length ? (xComponents = _, zoom) : xComponents; - }; - zoom.yComponents = function (_) { - return arguments.length ? (yComponents = _, zoom) : yComponents; - }; - - function setLocks() { - var chartObjSel = chartSel.select('.' + hypenate(chart.namespace(), 'object-container')); - var chartObjTrans = getTranslation(chartObjSel.attr('transform')); - var cos = chartObjSel.attr('transform', 'translate(0,0)'); - - xLock = chartSel.node().getBBox().width - chart.spaceX(); // * .9 - yLock = chartSel.node().getBBox().height - chart.spaceY(); // * .9 - cos.attr('transform', 'translate(' + chartObjTrans[0] + ',' + chartObjTrans[1] + ')'); - log('plotZoom', 'setLocks', { xLock: xLock, yLock: yLock }); - } - - /** - * Sets the x and y locks (how far one can scroll) - * (see {@link plotZoom#xLock} and {@link plotZoom#yLock}) - * @function plotZoom.setLocks - * @returns {undefined} - * @memberof plotZoom - * @property - */ - zoom.setLocks = setLocks; - - function zoom() { - setLocks(); - - var xComponentsSel = xComponents.map(function (d, i) { - return d.selection(); - }), - yComponentsSel = yComponents.map(function (d, i) { - return d.selection(); - }); - - var horizontalQ, verticalQ; - if (orient == '2D') { - horizontalQ = true;verticalQ = true; - } - if (orient == 'horizontal') { - horizontalQ = true;verticalQ = false; - } - if (orient == 'vertical') { - verticalQ = true;horizontalQ = false; - } - - // capture transform event - var transform = d3.event.transform; - - var chartBox = chartSel.node().getBBox(); - var xComponentsBox = xComponentsSel.map(function (d, i) { - return d.node().getBBox(); - }); - var yComponentsBox = xComponentsSel.map(function (d, i) { - return d.node().getBBox(); - }); - - var chartWidth = chartBox.width - chartBox.x; - var chartHeight = chartBox.height - chartBox.y; - - // enable wheel event - if (eventType == "wheel") { - var e = d3.event; - // prevent page scrolling - e.preventDefault(); - // event delta is very very slow, so speed it up with wheel speed - var w = d3.event.deltaY * wheelSpeed; - var shiftQ = d3.event.shiftKey; - - // d3 has no way to make custom transform, so make an object and add - // the necessary functions to make wheel event compatible with drag events - if (orient == '2D') { - transform = shiftQ ? { k: 1, x: w, y: 0 } : { k: 1, x: 0, y: w }; - } else { - transform = horizontalQ ? { k: 1, x: w, y: 0 } : { k: 1, x: 0, y: w }; - } - // * -1 is invert - transform.applyX = function (x) { - return x * this.k + this.x * -1; - }; - transform.applyY = function (y) { - return y * this.k + this.y * -1; - }; - } - - var chartObjSel = chartSel.select('.' + hypenate(chart.namespace(), 'object-container')); - var xComponentObjSel = xComponentsSel.map(function (d, i) { - return d.select('.' + hypenate(xComponents[i].namespace(), 'object-container')); - }); - var yComponentObjSel = yComponentsSel.map(function (d, i) { - return d.select('.' + hypenate(yComponents[i].namespace(), 'object-container')); - }); - - var chartObjTrans = getTranslation(chartObjSel.attr('transform')); - var xComponentsObjTrans = xComponentObjSel.map(function (d, i) { - return getTranslation(d.attr('transform')); - }); - var yComponentsObjTrans = yComponentObjSel.map(function (d, i) { - return getTranslation(d.attr('transform')); - }); - - var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0; - if (horizontalQ) { - x = x < -xLock ? (transform.x = 0, -xLock) : (transform.x = 0, Math.min(x, 0)); - } - - var y = verticalQ ? transform.applyY(chartObjTrans[1]) : 0; - if (verticalQ) { - y = y < -yLock ? (transform.y = 0, -yLock) : (transform.y = 0, Math.min(y, 0)); - } - - chartObjSel.attr('transform', 'translate(' + x + ',' + y + ')'); - if (horizontalQ) { - // xAxisObjSel.attr('transform', 'translate('+x+','+0+')') - xComponentObjSel.map(function (d, i) { - d.attr('transform', 'translate(' + x + ',' + 0 + ')'); - }); - } - if (verticalQ) { - // yAxisObjSel.attr('transform', 'translate('+0+','+y+')') - yComponentObjSel.map(function (d, i) { - d.attr('transform', 'translate(' + 0 + ',' + y + ')'); - }); - } - } - - zoom.reset = function () { - - var chartObjSel = chartSel.select('.' + hypenate(chart.namespace(), 'object-container')); - var xAxisObjSel = xAxisSel.select('.' + hypenate(xAxis.namespace(), 'object-container')); - var yAxisObjSel = yAxisSel.select('.' + hypenate(yAxis.namespace(), 'object-container')); - chartObjSel.attr('transform', 'translate(' + 0 + ',' + 0 + ')'); - xAxisObjSel.attr('transform', 'translate(' + 0 + ',' + 0 + ')'); - yAxisObjSel.attr('transform', 'translate(' + 0 + ',' + 0 + ')'); - }; - - return zoom; - } - - /******************************************************************************* - ** ** - ** ** - ** VIOLIN ** - ** ** - ** ** - *******************************************************************************/ - - /** - * Creates a violin - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/basic-violins/index.html Demo} - * @constructor violin - * @param {d3.selection} selection - * @namespace violin - * @returns {function} violin - */ - function violin(selection) { - var - /** - * Data to plot. Assumed to be a object, where each key corresponds to a violin - * (see {@link violin#data}) - * @param {Object} [data=undefined] - * @memberof violin# - * @property - */ - data, - - /** - * Which direction to render the bars in - * (see {@link violin#orient}) - * @param {number} [orient='horizontal'] - * @memberof violin# - * @property - */ - orient = 'horizontal', - - /** - * Amount of horizontal space (in pixels) avaible to render the violin in - * (see {@link violin#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof violin# - * @property - */ - spaceX, - - /** - * Amount of vertical space (in pixels) avaible to render the violin in - * (see {@link violin.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof violin# - * @property - */ - spaceY, - - /** - * Whether or not to allow violin to render elements pass the main spatial dimension - * given the orientation (see {@link violin#orient}), where {@link violin#orient}="horizontal" - * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" - * the main dimension is {@link violin#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof violin# - * @property - */ - overflowQ = true, - - /** - * Whether or not to display points inside the points - * @param {boolean} [pointsQ=false] - * @memberof violin# - * @property - */ - pointsQ = true, - - /** - * An array - putatively of other arrays - depicting how bars should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof violin# - * @property - */ - grouping, - - /** - * How to get the value of the violin - * @param {function} [valueExtractor=function(key, index) { return data[key] }] - * @memberof violin# - * @property - */ - valueExtractor = function valueExtractor(key, index) { - return data[key]; - }, - - /** - * How to sort the bars - if {@link violin#grouping} is not provided. - * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] - * @memberof violin# - * @property - */ - sortingFunction = function sortingFunction(keyA, keyB) { - return d3.descending(data[keyA], data[keyB]); - }, - - - /** - * The scale for which violin values should be transformed by - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof violin# - * @property - */ - scale = d3.scaleLinear(), - - /** - * The padding for the domain of the scale (see {@link violin#scale}) - * @param {number} [domainPadding=0.5] - * @memberof violin# - * @property - */ - domainPadding = 0.5, - - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link violin#orient}), where {@link violin#orient}="horizontal" - * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" - * the main dimension is {@link violin#spaceY} between bars - * @param {number} [objectSpacer=0.05] - * @memberof violin# - * @property - */ - objectSpacer = 0.05, - - /** - * The minimum size that an object can be - * @param {number} [minObjectSize=50] - * @memberof violin# - * @property - */ - minObjectSize = 50, - - /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof violin# - * @property - */ - maxObjectSize = 100, - - - /** - * The stroke width of the bars - * @param {number} [barStrokeWidth=2] - * @memberof violin# - * @property - */ - objectStrokeWidth = 2, - - /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof violin# - * @property - */ - colorFunction$$1 = colorFunction(), - - /** - * Instance of ColorFunction modified by a scale for the points - * @param {function} [pointColorFunc = colorFunction()] - * @memberof violin# - * @property - */ - pointColorFunc = function pointColorFunc(d, type, base, min, max) { - var minMaxHexScale = d3.scaleLinear().domain([min, max]).range([-0.25, 0.05]); - var scaledColor = modifyHexidecimalColorLuminance(base.replace('#', ''), minMaxHexScale(d)); - var mod = type == "stroke" ? 0 : 0.25; - return modifyHexidecimalColorLuminance(scaledColor.replace('#', ''), mod); - }, - - - /** - * The radius of a point - * @param {number} [pointRadius=3] - * @memberof violin# - * @property - */ - pointRadius = 3, - - /** - * The stroke width of the oints - * @param {number} [pointStrokeWidth=2] - * @memberof violin# - * @property - */ - pointStrokeWidth = 2, - - - /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof violin# - * @property - */ - backgroundFill = 'transparent', - - /** - * Namespace for all items made by this instance of violin - * @param {string} [namespace="d3sm-violin"] - * @memberof violin# - * @property - */ - namespace = 'd3sm-violin', - - /** - * Class name for violin container ( element) - * @param {string} [objectClass="violin"] - * @memberof violin# - * @property - */ - objectClass = 'violin', - - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof violin# - * @property - */ - transitionDuration = 1000, - - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof violin# - * @property - */ - easeFunc = d3.easeExp, - - - /** - * The keys corresponding to each quartile - * @param {string[]} [quartileKeys=["Q0", "Q1", "Q2", "Q3", "Q4"]] - * @memberof violin# - * @property - */ - quartileKeys = ["Q0", "Q1", "Q2", "Q3", "Q4"], - - - /** - * The keys of the bars - * @param {string[]} [violinKeys=undefined] - * @memberof violin# - * @property - */ - violinKeys, - - /** - * The values of the bars - * @param {number[]} [violinValues=undefined] - * @memberof violin# - * @property - */ - violinValues, - - /** - * The objectSize (actual width) used by the bars - * @param {number} [objectSize=undefined] - * @memberof violin# - * @property - */ - objectSize, - - /** - * The spacerSize (actual width) used by the spacers between the bars - * @param {number} [spacerSize=undefined] - * @memberof violin# - * @property - */ - spacerSize, - - - /** - * Instance of Tooltip - * @param {function} [tooltip=tooltip()] - * @memberof violin# - * @property - */ - tooltip$$1 = tooltip().keys([quartileKeys[4], quartileKeys[3], quartileKeys[2], quartileKeys[1], quartileKeys[0]]), - pointsTooltip = tooltip(), - - // pointKeyExtractor = function(violinKey, violinData, violinValues) {return d3.keys(violinValues[violinKey].values)}, - // pointValueExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]}, - - - /** - * Function which given the key of the violin and that key's associated value - * returns the object consiting of the points of the violin - * (see {@link violin#violinPointsExtractor}) - * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] - * @memberof violin# - * @property - */ - violinPointsExtractor = function violinPointsExtractor(violinKey, violinData) { - return violinData.points; - }, - - /** - * Function which given the key of the current point and the object of points for the - * violin, returns the numerical value of the point - * (see {@link violin#violinPointValueExtractor}) - * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] - * @memberof violin# - * @property - */ - violinPointValueExtractor = function violinPointValueExtractor(violinPointKey, violinPointData) { - return violinPointData[violinPointKey].value; - }; - - /** - * Gets or sets the violinPointsExtractor - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } - */ - violin.violinPointsExtractor = function (_) { - return arguments.length ? (violinPointsExtractor = _, violin) : violinPointsExtractor; - }; - /** - * Gets or sets the violinPointValueExtractor - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - * by default violinPointsExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]} - */ - violin.violinPointValueExtractor = function (_) { - return arguments.length ? (violinPointValueExtractor = _, violin) : violinPointValueExtractor; - }; - - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {violin | d3.selection} - * @memberof violin - * @property - * by default selection = selection - */ - violin.selection = function (_) { - return arguments.length ? (selection = _, violin) : selection; - }; - /** - * Gets or sets the data - * (see {@link violin#data}) - * @param {number} [_=none] - * @returns {violin | object} - * @memberof violin - * @property - */ - violin.data = function (_) { - return arguments.length ? (data = _, violin) : data; - }; - /** - * Gets or sets the orient of the boxes - * (see {@link violin#orient}) - * @param {number} [_=none] - * @returns {violin | object} - * @memberof violin - * @property - */ - violin.orient = function (_) { - return arguments.length ? (orient = _, violin) : orient; - }; - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link violin#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {violin | number} - * @memberof violin - * @property - * by default spaceX = undefined - */ - violin.spaceX = function (_) { - return arguments.length ? (spaceX = _, violin) : spaceX; - }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link violin#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {violin | number} - * @memberof violin - * @property - * by default spaceY = undefined - */ - violin.spaceY = function (_) { - return arguments.length ? (spaceY = _, violin) : spaceY; - }; - - /** - * Gets / sets whether or not violin is allowed to go beyond specified dimensions - * (see {@link violin#overflowQ}) - * @param {boolean} [_=none] - * @returns {violin | boolean} - * @memberof violin - * @property - * by default overflowQ = false - */ - violin.overflowQ = function (_) { - return arguments.length ? (overflowQ = _, violin) : overflowQ; - }; - /** - * Gets / sets whether or not to plot points with the violins - * (see {@link violin#pointsQ}) - * @param {boolean} [_=none] - * @returns {violin | boolean} - * @memberof violin - * @property - * by default pointsQ = false - */ - violin.pointsQ = function (_) { - return arguments.length ? (pointsQ = _, violin) : pointsQ; - }; - - /** - * Gets / sets the grouping of the boxes - * (see {@link violin#grouping}) - * @param {Array[]} [_=none] - * @returns {violin | Array[]} - * @memberof violin - * @property - * by default grouping = undefined - */ - violin.grouping = function (_) { - return arguments.length ? (grouping = _, violin) : grouping; - }; - /** - * Gets / sets the valueExtractor - * (see {@link violin#valueExtractor}) - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - */ - violin.valueExtractor = function (_) { - return arguments.length ? (valueExtractor = _, violin) : valueExtractor; - }; - /** - * Gets / sets the sortingFunction - * (see {@link violin#sortingFunction}) - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - */ - violin.sortingFunction = function (_) { - return arguments.length ? (sortingFunction = _, violin) : sortingFunction; - }; - - /** - * Gets / sets the scale for which the violin values should be transformed by - * (see {@link violin#scale}) - * @param {d3.scale} [_=none] - * @returns {violin | d3.scale} - * @memberof violin - * @property - * by default scale = d3.scaleLinear() - */ - violin.scale = function (_) { - return arguments.length ? (scale = _, violin) : scale; - }; - /** - * Gets / sets the padding for the domain of the scale - * (see {@link violin#domainPadding}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default domainPadding = 0.5 - */ - violin.domainPadding = function (_) { - return arguments.length ? (domainPadding = _, violin) : domainPadding; - }; - - /** - * Gets / sets objectSpacer - * (see {@link violin#objectSpacer}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default objectSpacer = 0.05 - */ - violin.objectSpacer = function (_) { - return arguments.length ? (objectSpacer = _, violin) : objectSpacer; - }; - /** - * Gets / sets the minObjectSize - * (see {@link violin#minObjectSize}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default minObjectSize = 15 - */ - violin.minObjectSize = function (_) { - return arguments.length ? (minObjectSize = _, violin) : minObjectSize; - }; - /** - * Gets / sets the maxObjectSize - * (see {@link violin#maxObjectSize}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default maxObjectSize = 50 - */ - violin.maxObjectSize = function (_) { - return arguments.length ? (maxObjectSize = _, violin) : maxObjectSize; - }; - - /** - * Gets / sets the objectStrokeWidth - * (see {@link violin#objectStrokeWidth}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default objectStrokeWidth = 2 - */ - violin.objectStrokeWidth = function (_) { - return arguments.length ? (objectStrokeWidth = _, violin) : objectStrokeWidth; - }; - - /** - * Gets / sets the colorFunction - * (see {@link violin#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {violin | colorFunction} - * @memberof violin - * @property - * by default colorFunction = colorFunction() - */ - violin.colorFunction = function (_) { - return arguments.length ? (colorFunction$$1 = _, violin) : colorFunction$$1; - }; - /** - * Gets / sets the colorFunction - * (see {@link violin#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {violin | colorFunction} - * @memberof violin - * @property - * by default colorFunction = colorFunction() - */ - violin.pointColorFunc = function (_) { - return arguments.length ? (pointColorFunc = _, violin) : pointColorFunc; - }; - - /** - * Gets / sets the pointRadius - * (see {@link violin#pointRadius}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default pointRadius = 2 - */ - violin.pointRadius = function (_) { - return arguments.length ? (pointRadius = _, violin) : pointRadius; - }; - /** - * Gets / sets the pointStrokeWidth - * (see {@link violin#pointStrokeWidth}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default pointStrokeWidth = 2 - */ - violin.pointStrokeWidth = function (_) { - return arguments.length ? (pointStrokeWidth = _, violin) : pointStrokeWidth; - }; - - /** - * Gets / sets the backgroundFill - * (see {@link violin#backgroundFill}) - * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin - * @property - * by default backgroundFill = 'transparent' - */ - violin.backgroundFill = function (_) { - return arguments.length ? (backgroundFill = _, violin) : backgroundFill; - }; - /** - * Gets / sets the namespace - * (see {@link violin#namespace}) - * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin - * @property - * by default namespace = 'd3sm-violin' - */ - violin.namespace = function (_) { - return arguments.length ? (namespace = _, violin) : namespace; - }; - /** - * Gets / sets the objectClass - * (see {@link violin#objectClass}) - * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin - * @property - * by default objectClass = 'tick-group' - */ - violin.objectClass = function (_) { - return arguments.length ? (objectClass = _, violin) : objectClass; - }; - - /** - * Gets / sets the transitionDuration - * (see {@link violin#transitionDuration}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default transitionDuration = 1000 - */ - violin.transitionDuration = function (_) { - return arguments.length ? (transitionDuration = _, violin) : transitionDuration; - }; - /** - * Gets / sets the easeFunc - * (see {@link violin#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {violin | d3.ease} - * @memberof violin - * @property - * by default easeFunc = d3.easeExp - */ - violin.easeFunc = function (_) { - return arguments.length ? (easeFunc = _, violin) : easeFunc; - }; - - /** - * Gets / sets the quartileKey - * (see {@link violin#quartileKey}) - * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin - * @property - * by default quartileKey = "quartiles" - */ - violin.quartileKey = function (_) { - return arguments.length ? (quartileKey = _, violin) : quartileKey; - }; - /** - * Gets / sets the quartileKeys - * (see {@link violin#quartileKeys}) - * @param {string[]} [_=none] - * @returns {violin | string[]} - * @memberof violin - * @property - * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] - */ - violin.quartileKeys = function (_) { - return arguments.length ? (quartileKeys = _, violin) : quartileKeys; - }; - - /** - * Gets / sets the violinKeys - * (see {@link violin#violinKeys}) - * @param {string[]} [_=none] - * @returns {violin | string[]} - * @memberof violin - * @property - * by default violinKeys = undefined - */ - violin.violinKeys = function (_) { - return arguments.length ? (violinKeys = _, violin) : violinKeys; - }; - /** - * Gets / sets the violinValues - * (see {@link violin#violinValues}) - * @param {Object[]} [_=none] - * @returns {violin | Object[]} - * @memberof violin - * @property - * by default violinValues = undefined - */ - violin.violinValues = function (_) { - return arguments.length ? (violinValues = _, violin) : violinValues; - }; - - /** - * Gets / sets the objectSize - * (see {@link violin#objectSize}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default objectSize = undefined - */ - violin.objectSize = function (_) { - return arguments.length ? (objectSize = _, violin) : objectSize; - }; - /** - * Gets / sets the spacerSize - * (see {@link violin#spacerSize}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default spacerSize = undefined - */ - violin.spacerSize = function (_) { - return arguments.length ? (spacerSize = _, violin) : spacerSize; - }; - /** - * Gets / sets the tooltip - * (see {@link violin#tooltip}) - * @param {tooltip} [_=none] - * @returns {violin | tooltip} - * @memberof violin - * @property - * by default tooltip = tooltip() - */ - violin.tooltip = function (_) { - return arguments.length ? (tooltip$$1 = _, violin) : tooltip$$1; - }; - /** - * Gets / sets the pointsTooltip - * (see {@link violin#pointsTooltip}) - * @param {tooltip} [_=none] - * @returns {violin | tooltip} - * @memberof violin - * @property - * by default pointsTooltip = tooltip() - */ - violin.pointsTooltip = function (_) { - return arguments.length ? (pointsTooltip = _, violin) : pointsTooltip; - }; - - function violin() { - var _ref, _ref2, _ref3; - - // for convenience in handling orientation specific values - var horizontalQ = orient == 'horizontal' ? true : false; - - // background cliping rectangle - var bgcpRect = { x: 0, y: 0, width: spaceX, height: spaceY }; - var container = setupContainer(selection, namespace, bgcpRect, backgroundFill); - - // if grouping is undefined sort violinKeys by sortingFunction - var ordered = grouping == undefined ? d3.keys(data).sort(sortingFunction) : grouping; - - // console.log(ordered) - - violinKeys = flatten(ordered); - - var calcValues = neededViolinValues().horizontalQ(horizontalQ).quartileKeys(quartileKeys).violinPointsExtractor(violinPointsExtractor).violinPointValueExtractor(violinPointValueExtractor); - - // augment valus - violinKeys.map(function (vk, i) { - calcValues(vk, data); - }); - - var numberOfObjects = violinKeys.length; - - var min = (_ref = []).concat.apply(_ref, toConsumableArray(violinKeys.map(function (k, i) { - return data[k].quartiles[quartileKeys[0]]; - }))); - var max = (_ref2 = []).concat.apply(_ref2, toConsumableArray(violinKeys.map(function (k, i) { - return data[k].quartiles[quartileKeys[quartileKeys.length - 1]]; - }))); - var extent = [Math.min.apply(Math, toConsumableArray(min)) - domainPadding, Math.max.apply(Math, toConsumableArray(max)) + domainPadding]; - // console.log(extent, violinValues, ordered) - - // set the scale - scale.domain(extent).range(horizontalQ ? [0, spaceY] : [0, spaceX]); - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - objectSize = calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - // calculate spacer size if needed - spacerSize = calculateWidthOfSpacer(ordered, space, objectSize, numberOfObjects, objectSpacer, overflowQ); - // make the nested groups - var spacerFunction = groupingSpacer().horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects).objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize).transitionDuration(transitionDuration).easeFunc(easeFunc).namespace(namespace); - - // move stuff - spacerFunction(container, ordered, 0); - // console.log(violinKeys, ordered, container.selectAll('g:not(.to-remove).'+objectClass).nodes()) - - // for color function - var parentIndexArray = []; - container.selectAll('g:not(.to-remove).' + objectClass).each(function (d, i) { - if (hasQ(violinKeys, d)) { - parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); - } - }); - - // update color function - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' ? colorFunction$$1.dataExtent([0, Math.max.apply(Math, parentIndexArray)]) : colorFunction$$1.dataExtent(extent); - - /* violiin specific needs */ - - var frequencyMax = Math.max.apply(Math, toConsumableArray((_ref3 = []).concat.apply(_ref3, toConsumableArray(violinKeys.map(function (k, i) { - return d3.max(data[k].frequencies); - }))))); - var vScale = d3.scaleLinear().domain([0, frequencyMax]).range([0, objectSize / 2]); - - var lArea = d3.line().x(function (d, i) { - return horizontalQ ? -vScale(d.x) : scale(d.x); - }).y(function (d, i) { - return horizontalQ ? scale(extent[1]) - scale(d.y) : -vScale(d.y); - }).curve(d3.curveBasis); - var rArea = d3.line().x(function (d, i) { - return horizontalQ ? vScale(d.x) : scale(d.x); - }).y(function (d, i) { - return horizontalQ ? scale(extent[1]) - scale(d.y) : vScale(d.y); - }).curve(d3.curveBasis); - - container.selectAll('g:not(.to-remove).' + objectClass).each(function (key, i) { - var t = d3.select(this), - currentData = data[key]; - // needed because bug in selecting .to-remove - if (!hasQ(violinKeys, key)) { - return; - } - var i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, currentData, i, 'fill'), - // prevent duplicate computation - strokeColor = colorFunction$$1(key, currentData, i, 'stroke'), - area = safeSelect(t, 'g', 'area'), - la = safeSelect(area, 'path', 'left'), - ra = safeSelect(area, 'path', 'right'), - quarts = safeSelect(t, 'g', 'quarts'), - lq3 = safeSelect(quarts, 'line', 'q3'), - lq1 = safeSelect(quarts, 'line', 'q1'), - q3 = currentData.quartiles[quartileKeys[3]], - q2 = currentData.quartiles[quartileKeys[2]], - q1 = currentData.quartiles[quartileKeys[1]]; - - t.attr('transform', horizontalQ ? 'translate(' + objectSize / 2 + ',0)' : 'translate(0,' + objectSize / 2 + ')'); - // draw curve - la.transition().duration(transitionDuration).attr('d', function (dd, ii) { - return lArea(currentData.contour); - }).attr('fill', fillColor).attr('stroke', strokeColor).attr('stroke-width', objectStrokeWidth); - - ra.transition().duration(transitionDuration).attr('d', function (dd, ii) { - return rArea(currentData.contour); - }).attr('fill', fillColor).attr('stroke', strokeColor).attr('stroke-width', objectStrokeWidth); - - area.node().addEventListener('mouseover', function (dd, ii) { - container.selectAll('g.' + objectClass).style('opacity', 0.2); - t.style('opacity', 1); - la.attr('stroke-width', objectStrokeWidth * 2); - ra.attr('stroke-width', objectStrokeWidth * 2); - }); - area.node().addEventListener('mouseout', function (dd, ii) { - container.selectAll('g.' + objectClass).style('opacity', 1); - la.attr('stroke-width', objectStrokeWidth); - ra.attr('stroke-width', objectStrokeWidth); - }); - - if (pointsQ) { - var ptsContainer = safeSelect(t, 'g', 'points'); - var pts = ptsContainer.selectAll('.point').data(currentData.pointKeys); - pts.on('mouseover', null); - - var ptsExit = pts.exit().transition().ease(easeFunc).duration(transitionDuration).attr('r', 0).attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)).attr('cx', horizontalQ ? vScale(0) : scale(q2)).remove(); - - var ptsEnter = pts.enter().append('circle').attr('class', 'point').attr('r', 0).attr('cx', horizontalQ ? 0 : scale(q2)).attr('cy', horizontalQ ? scale(q2) : 0); - - pts = pts.merge(ptsEnter); - - // console.log(pointsTooltip.header()) - - var pTTips = tooltip().selection(pts).data(violinPointsExtractor(key, currentData)).header(pointsTooltip.header()).keys(pointsTooltip.keys()).values(pointsTooltip.values()); - - pTTips(); - - var pMin = d3.min(currentData.pointValues), - pMax = d3.max(currentData.pointValues); - - pts.transition().duration(transitionDuration).ease(easeFunc).attr('r', pointRadius).attr('cy', function (pointKey, ii) { - var dd = currentData.pointValues[ii]; - if (horizontalQ) { - return scale(extent[1]) - scale(dd); - } - var j = whichBin(currentData.binned, dd); - var r = Math.random(); - var n = vScale(r * currentData.frequencies[j] * 0.5); - var k = Math.random() > 0.5 ? n : -n; - return k; - }).attr('cx', function (pointKey, ii) { - var dd = currentData.pointValues[ii]; - if (horizontalQ) { - var j = whichBin(currentData.binned, dd); - var r = Math.random(); - var n = vScale(r * currentData.frequencies[j] * 0.5); - var k = Math.random() > 0.5 ? n : -n; - return k; - } - return scale(dd); - }).attr('stroke', function (dd, ii) { - var dd = currentData.pointValues[ii];return pointColorFunc(dd, 'stroke', strokeColor, pMin, pMax); - }).attr('fill', function (dd, ii) { - var dd = currentData.pointValues[ii];return pointColorFunc(dd, 'fill', strokeColor, pMin, pMax); - }).attr('stroke-width', pointStrokeWidth); - - ptsContainer.selectAll('circle.point').on('mouseover', function (dd, ii) { - container.selectAll('g.' + objectClass).style('opacity', 0.2); - t.style('opacity', 1); - la.attr('stroke-width', objectStrokeWidth * 2); - ra.attr('stroke-width', objectStrokeWidth * 2); - - container.selectAll('.point').style('opacity', 0.2); - d3.select(this).style('opacity', 1).attr('r', pointRadius * 2).attr('stroke-width', pointStrokeWidth * 2); - }); - ptsContainer.selectAll('circle.point').on('mouseout', function (dd, ii) { - var e = document.createEvent('SVGEvents'); - e.initEvent('mouseout', true, true); - area.node().dispatchEvent(e); - - container.selectAll('.point').style('opacity', 1); - d3.select(this).attr('stroke-width', pointStrokeWidth).attr('r', pointRadius); - }); - } else { - cV.selectAll('.point').transition().duration(transitionDuration).ease(easeFunc).attr('r', 0).attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)).attr('cx', horizontalQ ? vScale(0) : scale(q2)).remove(); - } - }); - - tooltip$$1.selection(container.selectAll('g:not(.to-remove).' + objectClass + ' .area')); - if (tooltip$$1.data() == undefined) { - tooltip$$1.data(data); - } - tooltip$$1(); - if (tooltip$$1.values() == undefined) { - tooltip$$1.values([function (currentData, tooltipKey) { - return currentData['quartiles'][tooltipKey]; - }, function (currentData, tooltipKey) { - return currentData['quartiles'][tooltipKey]; - }, function (currentData, tooltipKey) { - return currentData['quartiles'][tooltipKey]; - }, function (currentData, tooltipKey) { - return currentData['quartiles'][tooltipKey]; - }, function (currentData, tooltipKey) { - return currentData['quartiles'][tooltipKey]; - }]); - } - } - - return violin; - } - - /** - * Produces the function which manipulates the violin data to have values needed - * for rendering the violins as svg. - * @returns {function} calculateViolinValues - * @namespace neededViolinValues - */ - function neededViolinValues() { - var - /** - * Whether or not the orientation of the violins are horizontal - * (see {@link violin#orient}) - * @param {Object} [horizontalQ=true] - * @memberof neededViolinValues# - * @property - */ - horizontalQ = true, - - /** - * Keys to be put into the quartiles if they need to be calculated. - * (see {@link violin#quartileKeys}) - * @param {Object} [quartileKeys=['Q0', 'Q1', 'Q2', 'Q3', 'Q4']] - * @memberof neededViolinValues# - * @property - */ - quartileKeys = ['Q0', 'Q1', 'Q2', 'Q3', 'Q4'], - - /** - * Function which given the key of the violin and that key's associated value - * returns the object consiting of the points of the violin - * (see {@link violin#violinPointsExtractor}) - * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] - * @memberof neededViolinValues# - * @property - */ - violinPointsExtractor, - - /** - * Function which given the key of the current point and the object of points for the - * violin, returns the numerical value of the point - * (see {@link violin#violinPointValueExtractor}) - * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] - * @memberof neededViolinValues# - * @property - */ - violinPointValueExtractor; - - /** - * Gets / sets the horizontalQ - * (see {@link violin#orient}) - * @param {boolean} [_=none] - * @returns {calculateViolinValues | boolean} - * @memberof calculateViolinValues - * @property - * - * by default horizontalQ = true - */ - calculateViolinValues.horizontalQ = function (_) { - return arguments.length ? (horizontalQ = _, calculateViolinValues) : horizontalQ; - }; - /** - * Gets / sets the quartileKeys - * (see {@link violin#quartileKeys}) - * @param {string[]} [_=none] - * @returns {calculateViolinValues | string[]} - * @memberof calculateViolinValues - * @property - * - * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] - */ - calculateViolinValues.quartileKeys = function (_) { - return arguments.length ? (quartileKeys = _, calculateViolinValues) : quartileKeys; - }; - /** - * Gets / sets the violinPointsExtractor - * (see {@link violin#violinPointsExtractor}) - * @param {function} [_=none] - * @returns {calculateViolinValues | function} - * @memberof calculateViolinValues - * @property - * - * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } - */ - calculateViolinValues.violinPointsExtractor = function (_) { - return arguments.length ? (violinPointsExtractor = _, calculateViolinValues) : violinPointsExtractor; - }; - /** - * Gets / sets the violinPointValueExtractor - * (see {@link violin#violinPointValueExtractor}) - * @param {function} [_=none] - * @returns {calculateViolinValues | function} - * @memberof calculateViolinValues - * @property - * - * by default violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value } - */ - calculateViolinValues.violinPointValueExtractor = function (_) { - return arguments.length ? (violinPointValueExtractor = _, calculateViolinValues) : violinPointValueExtractor; - }; - - /** - * The function produced by neededViolinValues. - * - * Adds the data need to render the violin as an svg - * @param {string} violinKey the key of the current violin - * @param {object} data the object consisting of violinKey - values (violin data) pairs - * @returns {none} this function manipulates the passed data object adding the following keys - * - * data[violinKey].binned // the binned values of the points - * - * data[violinKey].frequencies // the list consisting of the length of each bin - * - * data[violinKey].contour // the points depicting the contour of 1/2 of the violin - * - * data[violinKey].quartiles // the quartiles of the points' values - * - * data[violinKey].pointKeys // the keys associated with the points - * - * data[violinKey].pointValues // the numerical values of the points - * - * @memberof neededViolinValues# - * @property - */ - function calculateViolinValues(violinKey, data) { - // data for the current violin - var violinData = data[violinKey]; - // the object of points - var violinPoints = violinPointsExtractor(violinKey, violinData); - // - var violinPointsKeys = d3.keys(violinPoints); - // the numerical values of those points - var violinPointsValues = violinPointsKeys.map(function (pk, i) { - return violinPointValueExtractor(pk, violinPoints); - }); - - // quartiles of those points - var pointQuartiles = quartiles(violinPointsValues, quartileKeys); - - // binned points - var binned = d3.histogram()(violinPointsValues); - // length of bins - var frequencies = binned.map(function (bin) { - return bin.length; - }); - // min and max countour points for nice drawings - var minContourPoint = horizontalQ ? { x: 0, y: d3.min(violinPointsValues) } : { x: d3.min(violinPointsValues), y: 0 }; - var maxContourPoint = horizontalQ ? { x: 0, y: d3.max(violinPointsValues) } : { x: d3.max(violinPointsValues), y: 0 }; - var violinContourPoints = binned.map(function (bin, i) { - return horizontalQ ? { y: bin.length ? d3.median(bin) : d3.median([bin.x0, bin.x1]), x: frequencies[i] } : { x: bin.length ? d3.median(bin) : d3.median([bin.x0, bin.x1]), y: frequencies[i] }; - }); - // points along which to draw the violin shpe - violinContourPoints = [minContourPoint].concat(violinContourPoints).concat([maxContourPoint]); - - // set data - violinData.binned = binned; - violinData.frequencies = frequencies; - violinData.contour = violinContourPoints; - violinData.quartiles = pointQuartiles; - violinData.pointKeys = violinPointsKeys; - violinData.pointValues = violinPointsValues; - } - - return calculateViolinValues; - } - - function numericLegend(selection) { - - var min = 0, - max = 1, - spaceX, - spaceY, - colorFunction$$1 = colorFunction(), - namespace = 'd3sm-linear-vertical-gradient', - fontSize = 12, - backgroundFill = 'transparent', - textColor = 'black', - roundTo = 2; - - legend.min = function (_) { - return arguments.length ? (min = _, legend) : min; - }; - legend.max = function (_) { - return arguments.length ? (max = _, legend) : max; - }; - legend.spaceX = function (_) { - return arguments.length ? (spaceX = _, legend) : spaceX; - }; - legend.spaceY = function (_) { - return arguments.length ? (spaceY = _, legend) : spaceY; - }; - legend.namespace = function (_) { - return arguments.length ? (namespace = _, legend) : namespace; - }; - legend.fontSize = function (_) { - return arguments.length ? (fontSize = _, legend) : fontSize; - }; - legend.backgroundFill = function (_) { - return arguments.length ? (backgroundFill = _, legend) : backgroundFill; - }; - legend.colorFunction = function (_) { - return arguments.length ? (colorFunction$$1 = _, legend) : colorFunction$$1; - }; - legend.textColor = function (_) { - return arguments.length ? (textColor = _, legend) : textColor; - }; - legend.roundTo = function (_) { - return arguments.length ? (roundTo = _, legend) : roundTo; - }; - - function legend() { - // background cliping rectangle - var bgcpRect = { x: 0, y: 0, width: spaceX, height: spaceY }; - var container = setupContainer(selection, namespace, bgcpRect, backgroundFill); - - var defs = safeSelect(selection, 'defs'); - var linearGradient = safeSelect(defs, 'linearGradient').attr("x1", "0%").attr("y1", "100%").attr("x2", "0%").attr("y2", "0%").attr('id', hypenate(namespace, 'numerical-legend-gradient')); - - colorFunction$$1.dataExtent([min, max]).colorBy('value').valueExtractor(function (k, v, i) { - return v; - }); - - linearGradient.selectAll('stop').data(colorFunction$$1.colors()).enter().append('stop').attr("offset", function (d, i) { - return i / (colorFunction$$1.colors().length - 1); - }).attr('stop-color', function (d) { - return d; - }); - - var rect = safeSelect(container, 'rect', 'legend').attr('transform', 'translate(0,' + fontSize + ')').style("fill", "url(#" + hypenate(namespace, 'numerical-legend-gradient') + ")").attr('x', 0).attr('y', 0).attr('width', spaceX).attr('height', spaceY - fontSize * 2).on('mousemove', function (d, i) { - legendMousemove(d, i, rect, d3.select(this)); - }).on('mouseout', function (d, i) { - d3.select("#" + hypenate(namespace, 'legend-tooltip')).remove(); - }); - - var minText = safeSelect(container, 'text', 'min').text(round(min, 2)).attr('text-anchor', 'middle').attr("font-size", fontSize + 'px').attr('transform', function (d, i) { - var x = spaceX / 2, - y = spaceY - fontSize / 4, - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - - var maxText = safeSelect(container, 'text', 'max').text(round(max, 2)).attr('text-anchor', 'middle').attr("font-size", fontSize + 'px').attr('transform', function (d, i) { - var x = spaceX / 2, - y = fontSize, - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - } - - function legendMousemove(d, i, rect, t) { - var s = d3.scaleLinear().domain([0, rect.attr('height')]).range([max, min]); - var m = d3.mouse(rect.node()); - var v = round(s(m[1]), roundTo); - - var strokeColor = colorFunction$$1(undefined, v, undefined, 'stroke'); - var fillColor = colorFunction$$1(undefined, v, undefined, 'fill'); - - var div = safeSelect(d3.select('body'), 'div', hypenate(namespace, 'legend-tooltip')).attr('id', hypenate(namespace, 'legend-tooltip')).style('position', 'absolute').style('left', d3.event.pageX + 15 + 'px').style('top', d3.event.pageY + 15 + 'px').style('background-color', fillColor).style('border-color', strokeColor).style('min-width', fontSize * (String(max).split('.')[0].length + 3) + 'px').style('min-height', fontSize * (String(max).split('.')[0].length + 3) + 'px').style('border-radius', '50%').style('border-radius', '5000px').style('display', 'flex').style('justify-content', 'center').style('text-align', 'middle').style('padding', 2 + "px").style('border-style', 'solid').style('border-width', 2); - - var text = safeSelect(div, 'div').text(v).style('color', textColor).style('align-self', 'center'); - } - - return legend; - } - - function categoricLegend(selection) { - var categories, - - - /** - * Data to plot. Assumed to be a object, where each key corresponds to a legend - * (see {@link legend#data}) - * @param {Object} [data=undefined] - * @memberof legend# - * @property - */ - data, - - /** - * Which direction to render the bars in - * (see {@link legend#orient}) - * @param {number} [orient='horizontal'] - * @memberof legend# - * @property - */ - orient = 'horizontal', - - /** - * Amount of horizontal space (in pixels) avaible to render the legend in - * (see {@link legend#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof legend# - * @property - */ - spaceX, - - /** - * Amount of vertical space (in pixels) avaible to render the legend in - * (see {@link legend.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof legend# - * @property - */ - spaceY, - - - /** - * Whether or not to allow legend to render elements pass the main spatial dimension - * given the orientation (see {@link legend#orient}), where {@link legend#orient}="horizontal" - * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" - * the main dimension is {@link legend#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof legend# - * @property - */ - overflowQ = false, - - - /** - * An array - putatively of other arrays - depicting how bars should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof legend# - * @property - */ - grouping, - - - /** - * How to get the value of the legend - * @param {function} [valueExtractor=function(key, index) { return data[key] }] - * @memberof legend# - * @property - */ - valueExtractor = function valueExtractor(key, index) { - return data[key]; - }, - - /** - * How to sort the bars - if {@link bar#grouping} is not provided. - * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] - * @memberof bar# - * @property - */ - sortingFunction = function sortingFunction(keyA, keyB) { - return d3.ascending(keyA, keyB); - }, - - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link legend#orient}), where {@link legend#orient}="horizontal" - * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" - * the main dimension is {@link legend#spaceY} between bars - * @param {number} [objectSpacer=0.05] - * @memberof legend# - * @property - */ - objectSpacer = 0.05, - - /** - * The minimum size that an object can be - * @param {number} [minObjectSize=50] - * @memberof legend# - * @property - */ - minObjectSize = 10, - - /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof legend# - * @property - */ - maxObjectSize = 100, - - - /** - * The stroke width of the bars - * @param {number} [barStrokeWidth=2] - * @memberof legend# - * @property - */ - bubbleStrokeWidth = 2, - - /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof legend# - * @property - */ - colorFunction$$1 = colorFunction(), - - - /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof legend# - * @property - */ - backgroundFill = 'transparent', - - /** - * Namespace for all items made by this instance of legend - * @param {string} [namespace="d3sm-legend"] - * @memberof legend# - * @property - */ - namespace = 'd3sm-legend', - - /** - * Class name for legend container ( element) - * @param {string} [objectClass="legend"] - * @memberof legend# - * @property - */ - objectClass = 'legend', - - - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof legend# - * @property - */ - transitionDuration = 1000, - - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof legend# - * @property - */ - easeFunc = d3.easeExp; - - legend.categories = function (_) { - return arguments.length ? (categories = _, legend) : categories; - }; - - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {legend | d3.selection} - * @memberof legend - * @property - * by default selection = selection - */ - legend.selection = function (_) { - return arguments.length ? (selection = _, legend) : selection; - }; - /** - * Gets or sets the data - * (see {@link legend#data}) - * @param {number} [_=none] - * @returns {legend | object} - * @memberof legend - * @property - */ - legend.data = function (_) { - return arguments.length ? (data = _, legend) : data; - }; - /** - * Gets or sets the orient of the bars - * (see {@link legend#orient}) - * @param {number} [_=none] - * @returns {legend | object} - * @memberof legend - * @property - */ - legend.orient = function (_) { - return arguments.length ? (orient = _, legend) : orient; - }; - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link legend#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {legend | number} - * @memberof legend - * @property - * by default spaceX = undefined - */ - legend.spaceX = function (_) { - return arguments.length ? (spaceX = _, legend) : spaceX; - }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link legend#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {legend | number} - * @memberof legend - * @property - * by default spaceY = undefined - */ - legend.spaceY = function (_) { - return arguments.length ? (spaceY = _, legend) : spaceY; - }; - - /** - * Gets / sets whether or not legend is allowed to go beyond specified dimensions - * (see {@link legend#spaceX}) - * @param {boolean} [_=none] - * @returns {legend | boolean} - * @memberof legend - * @property - * by default overflowQ = false - */ - legend.overflowQ = function (_) { - return arguments.length ? (overflowQ = _, legend) : overflowQ; - }; - /** - * Gets / sets the grouping of the bars - * (see {@link legend#grouping}) - * @param {Array[]} [_=none] - * @returns {legend | Array[]} - * @memberof legend - * @property - * by default grouping = undefined - */ - legend.grouping = function (_) { - return arguments.length ? (grouping = _, legend) : grouping; - }; - /** - * Gets / sets the valueExtractor - * (see {@link legend#valueExtractor}) - * @param {function} [_=none] - * @returns {legend | function} - * @memberof legend - * @property - * by default valueExtractor = function(key, index) { return data[key] }, - */ - legend.valueExtractor = function (_) { - return arguments.length ? (valueExtractor = _, legend) : valueExtractor; - }; - /** - * Gets / sets the sortingFunction - * (see {@link bar#sortingFunction}) - * @param {function} [_=none] - * @returns {bar | function} - * @memberof bar - * @property - * by default sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, - */ - legend.sortingFunction = function (_) { - return arguments.length ? (sortingFunction = _, legend) : sortingFunction; - }; - /** - * Gets / sets objectSpacer - * (see {@link legend#objectSpacer}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default objectSpacer = 0.05 - */ - legend.objectSpacer = function (_) { - return arguments.length ? (objectSpacer = _, legend) : objectSpacer; - }; - /** - * Gets / sets the minObjectSize - * (see {@link legend#minObjectSize}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default minObjectSize = 50 - */ - legend.minObjectSize = function (_) { - return arguments.length ? (minObjectSize = _, legend) : minObjectSize; - }; - /** - * Gets / sets the maxObjectSize - * (see {@link legend#maxObjectSize}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default maxObjectSize = 100 - */ - legend.maxObjectSize = function (_) { - return arguments.length ? (maxObjectSize = _, legend) : maxObjectSize; - }; - - /** - * Gets / sets the barStrokeWidth - * (see {@link legend#barStrokeWidth}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default barStrokeWidth = 2 - */ - legend.bubbleStrokeWidth = function (_) { - return arguments.length ? (bubbleStrokeWidth = _, legend) : bubbleStrokeWidth; - }; - /** - * Gets / sets the colorFunction - * (see {@link legend#colorFunction}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default colorFunction = colorFunction() - */ - legend.colorFunction = function (_) { - return arguments.length ? (colorFunction$$1 = _, legend) : colorFunction$$1; - }; - - /** - * Gets / sets the backgroundFill - * (see {@link legend#backgroundFill}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default backgroundFill = 'transparent' - */ - legend.backgroundFill = function (_) { - return arguments.length ? (backgroundFill = _, legend) : backgroundFill; - }; - /** - * Gets / sets the namespace - * (see {@link legend#namespace}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default namespace = 'd3sm-legend' - */ - legend.namespace = function (_) { - return arguments.length ? (namespace = _, legend) : namespace; - }; - /** - * Gets / sets the objectClass - * (see {@link legend#objectClass}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default objectClass = 'tick-group' - */ - legend.objectClass = function (_) { - return arguments.length ? (objectClass = _, legend) : objectClass; - }; - /** - * Gets / sets the transitionDuration - * (see {@link legend#transitionDuration}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default transitionDuration = 1000 - */ - legend.transitionDuration = function (_) { - return arguments.length ? (transitionDuration = _, legend) : transitionDuration; - }; - /** - * Gets / sets the easeFunc - * (see {@link legend#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {legend | d3.ease} - * @memberof legend - * @property - * by default easeFunc = d3.easeExp - */ - legend.easeFunc = function (_) { - return arguments.length ? (easeFunc = _, legend) : easeFunc; - }; - - function legend() { - var horizontalQ = orient == 'horizontal' ? true : false; - var verticalQ = !horizontalQ; - // background cliping rectangle - var bgcpRect = { x: 0, y: 0, width: spaceX, height: spaceY }; - var container = setupContainer(selection, namespace, bgcpRect, backgroundFill); - - colorFunction$$1.dataExtent([0, categories.length - 1]).colorBy('categories').categoryExtractor(function (k, v, i) { - return v; - }); - - var r = Math.min(spaceX, spaceY) / 2; - var numberOfObjects = categories.length; - - // if grouping is undefined sort barKeys by sortingFunction - var ordered = grouping == undefined ? categories.sort(sortingFunction) : grouping; - // ordered might be nested depending on grouping - var catKeys = flatten(ordered); - - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - var objectSize = calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - // calculate spacer size if needed - var spacerSize = calculateWidthOfSpacer(catKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); - // make the nested groups - var spacerFunction = groupingSpacer().horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects).objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize).transitionDuration(transitionDuration).easeFunc(easeFunc).namespace(namespace); - - spacerFunction(container, ordered, 0); - var r = Math.min(objectSize, spaceX, spaceY) / 2 - bubbleStrokeWidth; - - container.selectAll('g:not(.to-remove).' + objectClass).each(function (cat, i) { - var t = d3.select(this); - var c = safeSelect(t, 'circle'); - var fillColor = colorFunction$$1(undefined, cat, i, 'fill'), - // prevent duplicate computation - strokeColor = colorFunction$$1(undefined, cat, i, 'stroke'); - - var cx = horizontalQ ? r + bubbleStrokeWidth : (spaceX - r * 2) / 2 + r; - var cy = verticalQ ? r + bubbleStrokeWidth : (spaceX - r * 2) / 2 + r; - - c.attr("r", r).attr('cx', cx).attr('cy', cy).attr('fill', fillColor).attr('stroke', strokeColor).attr('stroke-width', bubbleStrokeWidth); - - var text = safeSelect(t, 'text'); - text.text(cat).attr('text-anchor', 'middle').attr("transform", function (d, i) { - var x = cx, - y = cy + text.node().getBoundingClientRect().height / 4, - t = 'translate(' + x + ',' + y + ')'; - return t; - }); - }); - } - - return legend; - } - - /******************************************************************************* - ** ** - ** ** - ** D3 EXTENSIONS ** - ** ** - ** ** - *******************************************************************************/ - /** - * Recursively ascends parents of selection until it finds an svg tag - * @function d3.selection.thisSVG - * @augments d3.selection - * @returns {Element} which is the svg tag, not the d3 selection of that tag - */ - d3.selection.prototype.thisSVG = function () { - return getContainingSVG(this.node()); - }; - - /** - * Helper for getting absolute position of the mouse - * @function d3.mouse.absolute - * @augments d3.mouse - * @returns {number[]} [x, y] as they relate to `html` not to local scope. - */ - d3.mouse.absolute = function () { - var html = d3.select('html').node(); - - var _ref = this(html), - _ref2 = slicedToArray(_ref, 2), - x = _ref2[0], - y = _ref2[1]; - - return [x, y]; - }; - - /** - * Gets position of the selection in relation to the containing svg - * @see{@link getContainingSVG} - * @function d3.selection.absolutePosition - * @augments d3.selection - * @returns {Object} with structure similar to getBoundingClientRect, e.g. - * top, left, bottom, right, height, width - */ - d3.selection.prototype.absolutePosition = function () { - var element = this.node(); - var elementPosition = element.getBoundingClientRect(); - var containerSVG = getContainingSVG(element); - var svgPosition = containerSVG.getBoundingClientRect(); - - return { - top: elementPosition.top - svgPosition.top, - left: elementPosition.left - svgPosition.left, - bottom: elementPosition.bottom - svgPosition.top, - right: elementPosition.right - svgPosition.left, - height: elementPosition.height, - width: elementPosition.width - }; - }; - - d3.selection.prototype.relativePositionTo = function (container) { - var element = this.node(); - var elementPosition = element.getBoundingClientRect(); - var containerSVG = container; - var svgPosition = containerSVG.getBoundingClientRect(); - - return { - top: elementPosition.top - svgPosition.top, - left: elementPosition.left - svgPosition.left, - bottom: elementPosition.bottom - svgPosition.top, - right: elementPosition.right - svgPosition.left, - height: elementPosition.height, - width: elementPosition.width - }; - }; - - function getTranslation$1(selection) { - var transform = selection.attr('transform'); - - var _transform$split = transform.split('translate('), - _transform$split2 = slicedToArray(_transform$split, 2), - junk = _transform$split2[0], - xy = _transform$split2[1]; - - var _xy$split = xy.split(','), - _xy$split2 = slicedToArray(_xy$split, 2), - x = _xy$split2[0], - y = _xy$split2[1]; - - junk = y.split(')'); - return [parseFloat(x), parseFloat(y)]; - } - - function lasso(selection) { - var svg, - // svg that is target of events - objectContainer, - // container which houses objects we are selecting (allows for transform to be applied to lasso) - objectClass, - // class of object we are selecting - namespace = "d3sm-lasso", - chartContainer, - chartOffset, - objectsOffset, - eventCatcher, - xScale, - // optional scale for the lasso currentPoints - yScale, - // optional scale for the lasso currentPoints - - activeQ = false, - // whether or not lasso is active - - currentPoints = [], - // mouse points for current lasso - allPoints = [], - // list of lists for all points of lassos - - line = d3.line().x(function (d, i) { - var x; - if (xScale != undefined) { - x = xScale(d[0]); - } else { - x = d[0]; - } - return x; //- chartOffset[0]// - objectsOffset[0] - }).y(function (d, i) { - var y; - if (yScale != undefined) { - y = yScale(d[1]); - } else { - y = d[1]; - } - return y; // - chartOffset[1]// - objectsOffset[1] - }).curve(d3.curveLinearClosed), - instance = 0, - // an indentifier for which instance this lasso is under the current svg - - tickDistance = 10, - - - // styles for lasso path - color = '#17a2b8', - animationRate = '10s', - opacity = 0.3, - dashArray = '5, 10', - stroke = 'black', - strokeWidth = 2, - - - // styles for lassoed objects - lassoedFill = "white", - lassoedStroke = 'black', - lassoedStrokeWidth = 3, - transitionDuration = 1000, - easeFunc = d3.easeExp; - - var path; - - lasso.svg = function (_) { - return arguments.length ? (svg = _, lasso) : svg; - }; - lasso.chartContainer = function (_) { - return arguments.length ? (chartContainer = _, lasso) : chartContainer; - }; - lasso.objectContainer = function (_) { - return arguments.length ? (objectContainer = _, lasso) : objectContainer; - }; - lasso.objectClass = function (_) { - return arguments.length ? (objectClass = _, lasso) : objectClass; - }; - lasso.namespace = function (_) { - return arguments.length ? (namespace = _, lasso) : namespace; - }; - lasso.xScale = function (_) { - return arguments.length ? (xScale = _, lasso) : xScale; - }; - lasso.yScale = function (_) { - return arguments.length ? (yScale = _, lasso) : yScale; - }; - lasso.activeQ = function (_) { - return arguments.length ? (activeQ = _, lasso) : activeQ; - }; - lasso.currentPoints = function (_) { - return arguments.length ? (currentPoints = _, lasso) : currentPoints; - }; - lasso.allPoints = function (_) { - return arguments.length ? (allPoints = _, lasso) : allPoints; - }; - lasso.instance = function (_) { - return arguments.length ? (instance = _, lasso) : instance; - }; - lasso.tickDistance = function (_) { - return arguments.length ? (tickDistance = _, lasso) : tickDistance; - }; - lasso.color = function (_) { - return arguments.length ? (color = _, lasso) : color; - }; - lasso.animationRate = function (_) { - return arguments.length ? (animationRate = _, lasso) : animationRate; - }; - lasso.opacity = function (_) { - return arguments.length ? (opacity = _, lasso) : opacity; - }; - lasso.dashArray = function (_) { - return arguments.length ? (dashArray = _, lasso) : dashArray; - }; - lasso.stroke = function (_) { - return arguments.length ? (stroke = _, lasso) : stroke; - }; - lasso.lassoedFill = function (_) { - return arguments.length ? (lassoedFill = _, lasso) : lassoedFill; - }; - lasso.lassoedStroke = function (_) { - return arguments.length ? (lassoedStroke = _, lasso) : lassoedStroke; - }; - lasso.lassoedStrokeWidth = function (_) { - return arguments.length ? (lassoedStrokeWidth = _, lasso) : lassoedStrokeWidth; - }; - lasso.eventCatcher = function (_) { - return arguments.length ? (eventCatcher = _, lasso) : eventCatcher; - }; - - lasso.drag = drag; - lasso.draw = draw; - lasso.tick = tick; - lasso.detect = detect; - lasso.toggle = toggle; - lasso.remove = remove; - lasso.render = render; - lasso.keyFrames = keyFrames; - lasso.updateObjects = updateObjects; - lasso.applyPathAttributes = applyPathAttributes; - lasso.applyObjectAttributes = applyObjectAttributes; - - keyFrames(); - - function lasso() { - // add a dash animation if needed - if (activeQ) { - transitionDraw(); - } - } - - function toggle(state) { - // use optional param to set state, otherwise toggle state - activeQ = state != undefined ? state : !activeQ; - chartOffset = getTranslation$1(chartContainer); - objectsOffset = getTranslation$1(objectContainer); - - if (activeQ) { - svg.node().addEventListener('mousedown', render, true); - } else { - svg.node().removeEventListener('mousedown', render, true); - remove(); - } - } - - function draw() { - chartOffset = getTranslation$1(chartContainer); - objectsOffset = getTranslation$1(objectContainer); - - var container = safeSelect(objectContainer, 'g', 'lasso-container'); - var paths = container.selectAll('path[instance="' + instance + '"]'); - - // update - paths = paths.data(allPoints); - - // remove excess - var pExit = paths.exit().remove(); - // add needed paths - var pEnter = paths.enter().append('path'); - - // merge - paths = paths.merge(pEnter); - - // apply - applyPathAttributes(paths); - } - - function remove() { - var container = safeSelect(objectContainer, 'g', 'lasso-container'); - var paths = container.selectAll('path[instance="' + instance + '"]').remove(); - container.remove(); - objectContainer.selectAll(objectClass).classed("in-lasso", false); - updateObjects(); - } - - function render(event) { - // nothing can interefer with drawing the lasso - event.preventDefault();event.stopPropagation(); - - var container = safeSelect(objectContainer, 'g', 'lasso-container'); - - /* - each time the user presses down, while the state is active, the lasso - the lasso should make a seperate segment. - */ - currentPoints = []; - - svg.node().addEventListener('mousemove', drag); - svg.node().addEventListener('mouseup', function (event) { - svg.node().removeEventListener('mousemove', drag); - allPoints.push(currentPoints); - // BUG: somehow this is pushing currentPoints n times where n is the nth lasso path for the current instance - // NOTE: allPoints = unique(allPoints) is a temporary and inefficient fix - allPoints = unique(allPoints); - }); - - path = container.append('path').data([currentPoints]); - applyPathAttributes(path); - } - - function transitionDraw() { - var container = safeSelect(objectContainer, 'g', 'lasso-container'); - var paths = container.selectAll('path[instance="' + instance + '"]'); - - // update - paths = paths.data(allPoints); - - // remove excess - var pExit = paths.exit().remove(); - // add needed paths - var pEnter = paths.enter().append('path'); - - // merge - paths = paths.merge(pEnter).transition().duration(transitionDuration).ease(easeFunc); - applyPathAttributes(paths); - } - - function applyPathAttributes(path) { - path.attr("class", hypenate(namespace, "lasso-path")).style('opacity', opacity).attr('fill', color).attr("d", line).attr('instance', instance).style("stroke-dasharray", dashArray).attr("stroke", stroke).attr("stroke-width", strokeWidth).style('animation', 'lassoDash ' + animationRate + ' linear').style("animation-iteration-count", "infinite"); - } - - function drag(event) { - /* - effectively create a mouse down and move event (which normally is inteperated - as 'drag' by the browser) by dynamically adding / removing this event on - mouse down / mouse up. - */ - - if (eventCatcher != undefined) { - eventCatcher.dispatch(hypenate(namespace, "drag")); - } - // d3.dispatch(hypenate(namespace,"drag")) - - if (event.which != 1) { - return; - } // ensures left mouse button set - d3.event = event; - var pt = d3.mouse(objectContainer.node()); - var pt = d3.mouse(svg.node()); - - if (xScale != undefined) { - pt[0] = xScale.invert(pt[0]); - } - if (yScale != undefined) { - pt[1] = yScale.invert(pt[1]); - } - pt[0] = pt[0] - chartOffset[0] - objectsOffset[0]; - pt[1] = pt[1] - chartOffset[1] - objectsOffset[1]; - - /* if we have a point already, test if it passes a minimum distance to prevent overwhelming with too many tick functions */ - if (currentPoints.length) { - var lastPt = currentPoints[currentPoints.length - 1]; - var a = [pt[0], pt[1]], - b = [lastPt[0], lastPt[1]]; - - if (xScale) { - b[0] = xScale(b[0]);a[0] = xScale(a[0]); - } - if (yScale) { - b[1] = yScale(b[1]);a[1] = yScale(a[1]); - } - - var dist = euclideanDistance(b, a); - if (dist > tickDistance) { - tick(pt); - } - } else { - tick(pt); - } - } - - function tick(pt) { - /* - If a point is provided update data and objects. - Otherwise just call on data we already have. - Why like this?: - 1. currentPoints is current points to allow disjunct lassos, currentPoints is only pushed to - allPoints after mouseup. - 2. to allow render of objects in the lasso class / updating the data list - just by toggling the button - */ - - if (pt != undefined) { - currentPoints.push(pt); - path.attr("d", line); - if (currentPoints.length < 3) { - return; - } // need at least 3 points to detect anything. - detect(allPoints.concat([currentPoints])); - } else { - detect(allPoints); - } - } - - function detect(lassos) { - if (lassos == undefined) { - lassos = allPoints; - } - objectContainer.selectAll(objectClass).each(function (d, i) { - var current = d3.select(this), - box = current.absolutePosition(), - - // box = current.relativePositionTo(objectContainer.node()), - - boxPts = [[box.left - chartOffset[0] - objectsOffset[0], box.top - chartOffset[1] - objectsOffset[1]], [box.right - chartOffset[0] - objectsOffset[0], box.top - chartOffset[1] - objectsOffset[1]], [box.left - chartOffset[0] - objectsOffset[0], box.bottom - chartOffset[1] - objectsOffset[1]], [box.right - chartOffset[0] - objectsOffset[0], box.bottom - chartOffset[1] - objectsOffset[1]]]; - - if (xScale != undefined) { - boxPts[0][0] = xScale.invert(boxPts[0][0]); - boxPts[1][0] = xScale.invert(boxPts[1][0]); - boxPts[2][0] = xScale.invert(boxPts[2][0]); - boxPts[3][0] = xScale.invert(boxPts[3][0]); - } - if (yScale != undefined) { - boxPts[0][1] = yScale.invert(boxPts[0][1]); - boxPts[1][1] = yScale.invert(boxPts[1][1]); - boxPts[2][1] = yScale.invert(boxPts[2][1]); - boxPts[3][1] = yScale.invert(boxPts[3][1]); - } - - /* - flag needed as we have to test multiple lasso segments, and if the point - is not in one segment, it does not mean it is not in any - */ - var inAnyLassoQ = false; - for (var i = 0; i < lassos.length; i++) { - var lassoPoints = lassos[i]; - // .map(function(pt){ - // var x, y = pt - // if (xScale!=undefined) {x = xScale(x)} - // if (yScale!=undefined) {y = yScale(y)} - // return [x, y] - // }) - var boxInLassoQ = boxPts.every(function (coord) { - return d3.polygonContains(lassoPoints, coord); - }); - - if (boxInLassoQ) { - inAnyLassoQ = true; - } // only update flag in the positive case. - } - - current.classed('in-lasso', inAnyLassoQ); - current.classed('in-lasso-' + instance, inAnyLassoQ); - }); - - updateObjects(); - return objectContainer.selectAll('.in-lasso-' + instance); - } - - function updateObjects() { - objectContainer.selectAll(objectClass).each(function (d, i) { - var t = d3.select(this); - applyObjectAttributes(t, t.classed('in-lasso')); - }); - } - - function applyObjectAttributes(obj, setQ) { - var preLassoFill = obj.attr('_pre_lasso_fill'), - preLassoStroke = obj.attr('_pre_lasso_stroke'), - preLassoStrokeWidth = obj.attr('_pre_lasso_stroke-width'); - - if (setQ) { - obj.classed("in-lasso", true); - obj.classed('in-lasso-' + instance, true); - if (preLassoFill == undefined) { - obj.attr('_pre_lasso_fill', obj.attr('fill')); - } - if (preLassoStroke == undefined) { - obj.attr('_pre_lasso_stroke', obj.attr('stroke')); - } - if (preLassoStrokeWidth == undefined) { - obj.attr('_pre_lasso_stroke-width', obj.attr('stroke-width')); - } - - obj - //BUG: when .raise() - .attr('fill', lassoedFill).attr('stroke', lassoedStroke).attr('stoke-width', lassoedStrokeWidth); - } else { - obj.classed("in-lasso", false); - obj.classed('in-lasso-' + instance, false); - if (preLassoFill != undefined) { - obj.attr('fill', preLassoFill); - } - if (preLassoStroke != undefined) { - obj.attr('stroke', preLassoStroke); - } - if (preLassoStrokeWidth != undefined) { - obj.attr('stroke-width', preLassoStrokeWidth); - } - } - } - - function keyFrames() { - var style = d3.select("html").select('style.' + hypenate(namespace, "lasso-dash")); - if (style.empty()) { - d3.select("html").append('style').classed(hypenate(namespace, "lasso-dash"), true).html("@keyframes lassoDash {to { stroke-dashoffset: 1000;}}"); - } - } - return lasso; - } - - function lassoWidget(selection) { - var namespace = 'd3sm-lasso', - selection = selection, - svg, - chartContainer, - objectContainer, - objectClass, - lassoLine = d3.line().x(function (d, i) { - return d[0]; - }).y(function (d, i) { - return d[1]; - }).curve(d3.curveLinearClosed), - colorFunction$$1 = colorFunction(), - maxNumberOfGroups, - dataExtractor, - xScale, - yScale; - - function onError(msg, style) { - console.log(msg, style); - } - // setup the container - setupDataselectContainer(); - - lassoWidget.objectClass = function (_) { - return arguments.length ? (objectClass = '.' + _.replace('.', ''), lassoWidget) : objectClass; - }; - lassoWidget.svg = function (_) { - return arguments.length ? (svg = _, lassoWidget) : svg; - }; - lassoWidget.submit = function (_) { - return arguments.length ? (submit = _, lassoWidget) : submit; - }; - lassoWidget.maxNumberOfGroups = function (_) { - return arguments.length ? (maxNumberOfGroups = _, lassoWidget) : maxNumberOfGroups; - }; - lassoWidget.onError = function (_) { - return arguments.length ? (onError = _, lassoWidget) : onError; - }; - lassoWidget.objectContainer = function (_) { - return arguments.length ? (objectContainer = _, lassoWidget) : objectContainer; - }; - lassoWidget.chartContainer = function (_) { - return arguments.length ? (chartContainer = _, lassoWidget) : chartContainer; - }; - lassoWidget.dataExtractor = function (_) { - return arguments.length ? (dataExtractor = _, lassoWidget) : dataExtractor; - }; - lassoWidget.xScale = function (_) { - return arguments.length ? (xScale = _, lassoWidget) : xScale; - }; - lassoWidget.yScale = function (_) { - return arguments.length ? (yScale = _, lassoWidget) : yScale; - }; - - function styles() { - var s = d3.select("html").select("style." + namespace + 'lasso-widget'); - if (s.empty()) { - d3.select("html").append("style").classed(namespace + 'lasso-widget', true).html("." + hypenate(namespace, "data-table") + "{\ - height:100px;\ - overflow:auto;\ - }"); - } - } - - function lassoWidget() { - styles(); - } - - function submit(data) { - console.log(data); - } - - function plusTab(tabList) { - var tab = safeSelect(tabList, 'li', hypenate(namespace, 'plus-tab')).classed('ml-auto', true).classed('nav-item', true).on('click', makeNewGroup), - anchor = safeSelect(tab, 'a', 'nav-link'), - icon = safeSelect(anchor, 'i', 'fa').classed('fa-plus fa-2x text-success', true); - } - - function sendTab(tabList) { - var tab = safeSelect(tabList, 'li', hypenate(namespace, 'send-tab')).classed('ml-auto', true).classed('nav-item', true).on('click', clickSend), - anchor = safeSelect(tab, 'a', 'nav-link'), - icon = safeSelect(anchor, 'i', 'fa').classed('fa-paper-plane-o fa-2x text-primary', true); - } - - function clickSend() { - var data = gatherDataLists(); - submit(data); - } - - function closeTab(tabList) { - var tab = safeSelect(tabList, 'li', hypenate(namespace, 'close-tab')).classed('ml-auto', true).classed('nav-item', true).on('click', function () { - var m = d3.mouse(d3.select("html").node()); - }), - anchor = safeSelect(tab, 'a', 'nav-link'), - icon = safeSelect(anchor, 'i', 'fa').classed('fa-window-close-o fa-2x text-danger', true); - } - - function setupDataselectContainer() { - var card = safeSelect(selection, 'div', 'card'), - cardHeader = safeSelect(card, 'div', 'card-header'), - tabList = safeSelect(cardHeader, 'ul', 'nav').classed('nav-tabs card-header-tabs', true).classed(hypenate(namespace, 'tab-list'), true).attr('role', 'tablist'), - cardBody = safeSelect(card, 'div', 'card-body'), - tabContent = safeSelect(cardBody, 'div', 'tab-content').classed(hypenate(namespace, 'tab-content'), true), - - - // leftTabs = safeSelect(tabList, 'div', 'nav mr-auto left-aligned-tabs') - rightTabs = safeSelect(tabList, 'div', 'right-aligned-tabs').classed('nav ml-auto ', true); - - plusTab(rightTabs); - sendTab(rightTabs); - closeTab(rightTabs); - var defaultTab = safeSelect(tabContent, 'div', 'tab-pane').classed(hypenate(namespace, 'default-tab'), true).classed("active", true).classed('text-left', true), - p = safeSelect(defaultTab, 'div').html("Click to add a new group.
" + "Click to submit for re-analysis.
" + "Click to close the dataselect widget."); - // .text('Click the green plus to add a new group.') - } - - function makeRemoveButton(paneBtnList) { - var btn = safeSelect(paneBtnList, 'button', 'remove-btn').classed('btn btn-danger', true).on('click', removeBtnClickToRemove), - i = safeSelect(btn, 'i', 'fa').classed('fa-trash-o', true), - s = safeSelect(btn, 'span').text('Remove group'); - return btn; - } - - function removeBtnClickToRemove(d, i) { - var tab = selection.select('#' + hypenate(namespace, 'tab', d)), - pane = selection.select('#' + hypenate(namespace, 'tab', 'pane', d)); - - showRemainingPanes(); - updateTabAndPaneNumbers(); - } - - function togglePaneById(id) { - var panes = selection.select('.' + hypenate(namespace, 'tab-content')); - } - - function insertTab(tabs, n) { - - // var li = tabs.insert("li", ':nth-child('+(n)+')') - var li = tabs.insert("li", '.right-aligned-tabs').classed('nav-item', true).classed('alert', true).classed('alert-secondary', true).classed('alert-dismissable', true).classed('fade', true).classed('show', true) - // .classed('mr-auto', true) - .attr("role", 'alert').attr("id", hypenate(namespace, 'tab', n)).classed(hypenate(namespace, 'tab'), true); - - var a = safeSelect(li, 'a').attr('data-toggle', 'tab').text('Group ' + n).attr('href', '#' + hypenate(namespace, 'tab', 'pane', n)).on('dblclick', function (d, i) { - var t = d3.select(this); - t.attr('contenteditable', true); - d3.select(t.node().parentNode).classed('alert-secondary', false).classed('alert-warning', true); - - // d3.select("html").on("click", dispatchBlur) - // - // function dispatchBlur(d, i) { - // if (d3.event.target != d3.select(this)) { - // d3.select(this).dispatch("blur") - // } - // } - - }).on('blur', function (d, i) { - - var t = d3.select(this); - t.attr('contenteditable', false); - d3.select(t.node().parentNode).classed('alert-secondary', true).classed('alert-warning', false); - }).on('input', function (d, i) { - var t = d3.select(this); - var txt = t.text(); - d3.select(t.attr('href')).select("p.lead").text(txt); - }); - - return li; - } - - function makeTabPane(panes, n) { - var pane = panes.append('div').datum(n).attr('role', 'tabpanel').classed('tab-pane', true).classed('text-left', true).classed(hypenate(namespace, 'tab', 'pane'), true).attr('id', hypenate(namespace, 'tab', 'pane', n)); - return pane; - } - - function populatePane(pane, n) { - var lead = safeSelect(pane, 'p', 'lead').text('Group ' + n), - tableContainer = safeSelect(pane, 'div', 'table-responsive').attr("class", hypenate(namespace, "data-table")), - table = safeSelect(tableContainer, "table", "table").classed("table-sm", true).classed("table-hover", true), - caption = safeSelect(table, "caption", "caption").html("List of selected"), - btns = safeSelect(pane, 'div', 'text-right'), - cN = n % colorFunction$$1.colors().length; - if (n % 2 != 0) { - cN = colorFunction$$1.colors().length - 1 - cN; - } - - var lassoBtn = makeLassoButton(btns); - var currentLasso = makeLassoFunction(n, colorFunction$$1.colors()[cN]); - var clearBtn = makeClearButton(btns); - - bindButtons(lassoBtn, clearBtn, currentLasso, table); - - var removeBtn = makeRemoveButton(btns); - - lead.on("mouseover", function () { - currentLasso.draw(); - }); - lead.on("mouseout", function () { - currentLasso.remove(); - }); - } - - function makeLassoButton(btns) { - var btn = safeSelect(btns, 'button', 'lasso-btn').classed('btn btn-info', true).classed(namespace, true); - // .datum([lasso]) - var i = safeSelect(btn, 'i', 'fa').classed('fa-hand-pointer-o', true); - var s = safeSelect(btn, 'span').text('Lasso select'); - return btn; - } - - function makeClearButton(btns) { - var btn = safeSelect(btns, 'button', 'clear-btn').classed('btn btn-light', true).classed(namespace, true); - var i = safeSelect(btn, 'i', 'fa').classed('fa-eraser', true); - var s = safeSelect(btn, 'span').text('Clear selection'); - return btn; - } - - function makeLassoFunction(instance, color) { - var currentLasso = lasso().namespace(namespace).svg(svg).objectClass(objectClass).chartContainer(chartContainer).objectContainer(objectContainer).instance(instance).color(color).yScale(yScale).xScale(xScale); - - return currentLasso; - } - - function bindButtons(lassoBtn, clearBtn, currentLasso, table) { - currentLasso.eventCatcher(lassoBtn); - lassoBtn.node().addEventListener("click", lassoBtnToggle); - - lassoBtn.datum([currentLasso]).on(hypenate(namespace, 'render'), function () { - d3.select(this).datum()[0].draw(); - }).on(hypenate(namespace, "drag"), function (las) { - var nodes = chartContainer.selectAll(".in-lasso-" + las[0].instance()).nodes(); - var tableData = nodes.map(function (d, i) { - var extracted = dataExtractor(d3.select(d).datum()); - extracted["__node"] = d; - return extracted; - }); - - makeTable(table, tableData, currentLasso); - }); - - clearBtn.on('click', function () { - currentLasso.allPoints([]); - currentLasso.currentPoints([]); - lassoBtn.dispatch(hypenate(namespace, "drag")); - }); - } - - function lassoBtnToggle() { - var that = d3.select(this); - var las = that.datum()[0]; - - las.toggle(); - var activeQ = las.activeQ(); - - if (las.activeQ()) { - that.classed('btn-info', !activeQ); - that.classed('btn-warning', activeQ); - that.select("span").text("Lasso select (active)"); - selection.selectAll("." + namespace + ".lasso-btn").dispatch(hypenate(namespace, 'render')); - d3.select("html").node().addEventListener('mousedown', monitorLassoButtonState); - } else { - that.classed('btn-info', !activeQ); - that.classed('btn-warning', activeQ); - that.select("span").text("Lasso select"); - d3.select("html").node().removeEventListener('mousedown', monitorLassoButtonState); - } - - function monitorLassoButtonState(event) { - /* - activeLasso stops event stopPropagation, so this event will not register in - that case. Thus we only need to ensure that the usre is clicking on the lasso button - otherwise, de-spawn lasso. - */ - if (event.target != that.node() && event.target != that.select("span").node() && event.target != that.select("i").node()) { - // event.preventDefault() - // event.stopPropagation() - las.toggle(false); - that.classed('btn-info', true); - that.classed('btn-warning', false); - that.select("span").text("Lasso select"); - // console.log(that, that.node()) - // that.dispatch("focusout") - } - } - } - - function updateTableHeaderColumns(headR, tableData) { - var headerKeys = d3.keys(tableData[0]).filter(function (k) { - return k != "__node"; - }); - if (headerKeys.length > 0) { - // headerKeys = ["remove"].concat(headerKeys) - headerKeys.push("remove"); - } - - var headerCols = headR.selectAll("th"); - - headerCols = headerCols.data(headerKeys); - headerCols.exit().remove(); - headerCols = headerCols.merge(headerCols.enter().append("th").attr("scope", "col")).text(function (d, i) { - return d; - }); - - return headerKeys; - } - - function updateTableRows(body, tableData) { - var bodyRows = body.selectAll("tr"); - - bodyRows = bodyRows.data(tableData); - bodyRows.exit().remove(); - bodyRows = bodyRows.merge(bodyRows.enter().append("tr")); - - return bodyRows; - } - - function updateTableRowColumns(cols, headerKeys, rowData, tableData, lasso$$1, table) { - cols = cols.data(headerKeys); - cols.exit().remove(); - cols = cols.merge(cols.enter().append("td")); - - cols.html(function (d, i) { - if (d != "remove") { - return rowData[d]; - } - return ""; - }).classed("text-left", function (d, i) { - if (d != "remove") { - return false; - } - return true; - }); - // .attr("scope",function(d, i){ - // if (d != "remove") { return false } - // return true - // }) - - cols.select("i.fa-close").on("click", function (d, i) { - var node = rowData["__node"], - n = d3.select(node); - n.classed("in-lasso", false); - n.classed("in-lasso-" + lasso$$1.instance(), false); - - tableData.map(function (dd, j) { - if (dd["__node"] == node) { - tableData.splice(j, 1); - makeTable(table, tableData, lasso$$1); - } - }); - }); - } - - function makeTable(table, tableData, lasso$$1) { - var head = safeSelect(table, "thead"), - headR = safeSelect(head, "tr"), - body = safeSelect(table, "tbody"), - headerKeys = updateTableHeaderColumns(headR, tableData), - bodyRows = updateTableRows(body, tableData); - - bodyRows.each(function (rowData, i) { - var t = d3.select(this); - var cols = t.selectAll("td"); - - updateTableRowColumns(cols, headerKeys, rowData, tableData, lasso$$1, table); - - t.on("mouseover", function (d, i) { - lasso$$1.applyObjectAttributes(d3.select(d["__node"]), true); - }).on("mouseout", function (d, i) { - lasso$$1.applyObjectAttributes(d3.select(d["__node"]), false); - }); - }); - } - - function makeNewGroup(d, i) { - - var tabs = selection.select('.' + hypenate(namespace, 'tab-list')), - panes = selection.select('.' + hypenate(namespace, 'tab-content')), - n = newGroupNumber(); - - if (tabs.selectAll('.' + hypenate(namespace, 'tab')).size() == maxNumberOfGroups) { - onError('only ' + maxNumberOfGroups + ' allowed.', 'warning'); - return; - } - - var tab = insertTab(tabs, n), - pane = makeTabPane(panes, n); - - populatePane(pane, n); - togglePaneById(pane.attr('id')); - } - - function newGroupNumber() { - var tabs = selection.select('.' + hypenate(namespace, 'tab-list')); //, - var n = tabs.selectAll('li').size() - 3, - // minus 1 for plus tab - s = 'Group ' + n, - gs = []; - while (gs.includes(s)) { - n += 1;s = 'Group ' + n; - } - return n; - } - - function updateTabAndPaneNumbers() { - var tabs = selection.select('.' + hypenate(namespace, 'tab-list')), - panes = selection.select('.' + hypenate(namespace, 'tab-content')); - - tabs = tabs.selectAll('.' + hypenate(namespace, 'tab')); - panes = panes.selectAll('.' + hypenate(namespace, 'tab', 'pane')); - - tabs.each(function (d, i) { - d3.select(this).datum(i).attr("id", hypenate(namespace, 'tab', i)).select('a').attr('href', '#' + hypenate(namespace, 'tab', 'pane', i)).text(function (dd, ii) { - var curText = d3.select(this).text(); - if (curText.split(' ')[0] == 'Group') { - return 'Group ' + i; - } - return curText; - }); - }); - - panes.each(function (d, i) { - d3.select(this).datum(i).attr('id', hypenate(namespace, 'tab', 'pane', i)); - safeSelect(d3.select(this), 'p', 'lead').text(function (dd, ii) { - var curText = d3.select(this).text(); - if (curText.split(' ')[0] == 'Group') { - return 'Group ' + i; - } - return curText; - }); - safeSelect(d3.select(this), 'button', 'remove-btn').on('click', removeBtnClickToRemove); - }); - } - - function gatherDataLists() { - var tabs = selection.select('.' + hypenate(namespace, 'tab-list')); - var panes = selection.select('.' + hypenate(namespace, 'tab-content')); - var tables = panes.selectAll('.' + hypenate(namespace, "data-table")); - var data = {}; - - return data; - } - - function showRemainingPanes() { - var tabs = selection.select('.' + hypenate(namespace, 'tab-list')), - panes = selection.select('.' + hypenate(namespace, 'tab-content')), - remainingTabs = tabs.selectAll('.' + hypenate(namespace, 'tab')); - - if (remainingTabs.size() == 0) ; else { - var lastTab = remainingTabs.nodes()[remainingTabs.size() - 1], - lastPaneId = d3.select(lastTab).select('a').attr('href'); - } - } - - return lassoWidget; - } - - function upset(selection) { - var data, - orient = 'horizontal', - spaceX, - spaceY, - overflowQ = false, - minObjectSize = 20, - maxObjectSize = 50, - circleStrokeWidth = 2, - - // colorFunction= - backgroundFill = 'transparent', - namespace = 'd3sm-upset', - objectClass = 'upset', - transitionDuration = 1000, - easeFunc = d3.easeExp, - setKey = "set", - intersectionKey = "intersection", - elementsKey = "elements", - setExtractor = function setExtractor(key, i) { - return data[key][setKey]; - }, - intersectionExtractor = function intersectionExtractor(key, i) { - return data[key][intersectionKey]; - }, - elementExtractor = function elementExtractor(key, i) { - return data[key][elementsKey]; - }, - cellKeys, - setValues, - intersectionValues, - xObjectSpacer = 0.05, - yObjectSpacer = 0.05, - radius, - - - // listDelim = ';' - - yObjectSize, - ySpacerSize, - xObjectSize, - xSpacerSize, - setKeySortingFunction = function setKeySortingFunction(a, b) { - return setValues.indexOf(setExtractor(a)) - setValues.indexOf(setExtractor(b)); - }, - intersectionKeySortingFunction = function intersectionKeySortingFunction(a, b) { - return intersectionValues.indexOf(intersectionExtractor(a)) - intersectionValues.indexOf(intersectionExtractor(b)); - }; - - upset.selection = function (_) { - return arguments.length ? (selection = _, upset) : selection; - }; - upset.data = function (_) { - return arguments.length ? (data = _, upset) : data; - }; - upset.orient = function (_) { - return arguments.length ? (orient = _, upset) : orient; - }; - upset.spaceX = function (_) { - return arguments.length ? (spaceX = _, upset) : spaceX; - }; - upset.spaceY = function (_) { - return arguments.length ? (spaceY = _, upset) : spaceY; - }; - upset.overflowQ = function (_) { - return arguments.length ? (overflowQ = _, upset) : overflowQ; - }; - upset.minObjectSize = function (_) { - return arguments.length ? (minObjectSize = _, upset) : minObjectSize; - }; - upset.maxObjectSize = function (_) { - return arguments.length ? (maxObjectSize = _, upset) : maxObjectSize; - }; - upset.circleStrokeWidth = function (_) { - return arguments.length ? (circleStrokeWidth = _, upset) : circleStrokeWidth; - }; - upset.backgroundFill = function (_) { - return arguments.length ? (backgroundFill = _, upset) : backgroundFill; - }; - upset.namespace = function (_) { - return arguments.length ? (namespace = _, upset) : namespace; - }; - upset.objectClass = function (_) { - return arguments.length ? (objectClass = _, upset) : objectClass; - }; - upset.transitionDuration = function (_) { - return arguments.length ? (transitionDuration = _, upset) : transitionDuration; - }; - upset.easeFunc = function (_) { - return arguments.length ? (easeFunc = _, upset) : easeFunc; - }; - upset.cellKeys = function (_) { - return arguments.length ? (cellKeys = _, upset) : cellKeys; - }; - upset.setValues = function (_) { - return arguments.length ? (setValues = _, upset) : setValues; - }; - upset.intersectionValues = function (_) { - return arguments.length ? (intersectionValues = _, upset) : intersectionValues; - }; - upset.xObjectSpacer = function (_) { - return arguments.length ? (xObjectSpacer = _, upset) : xObjectSpacer; - }; - upset.yObjectSpacer = function (_) { - return arguments.length ? (yObjectSpacer = _, upset) : yObjectSpacer; - }; - upset.radius = function (_) { - return arguments.length ? (radius = _, upset) : radius; - }; - upset.setExtractor = function (_) { - return arguments.length ? (setExtractor = _, upset) : setExtractor; - }; - upset.intersectionExtractor = function (_) { - return arguments.length ? (intersectionExtractor = _, upset) : intersectionExtractor; - }; - upset.elementExtractor = function (_) { - return arguments.length ? (elementExtractor = _, upset) : elementExtractor; - }; - upset.setKeySortingFunction = function (_) { - return arguments.length ? (setKeySortingFunction = _, upset) : setKeySortingFunction; - }; - upset.intersectionKeySortingFunction = function (_) { - return arguments.length ? (intersectionKeySortingFunction = _, upset) : intersectionKeySortingFunction; - }; - - upset.yObjectSize = function (_) { - return arguments.length ? (yObjectSize = _, upset) : yObjectSize; - }; - upset.ySpacerSize = function (_) { - return arguments.length ? (ySpacerSize = _, upset) : ySpacerSize; - }; - upset.xObjectSize = function (_) { - return arguments.length ? (xObjectSize = _, upset) : xObjectSize; - }; - upset.xSpacerSize = function (_) { - return arguments.length ? (xSpacerSize = _, upset) : xSpacerSize; - }; - - function upset() { - // for convenience in handling orientation specific values - var horizontalQ = orient == 'horizontal' ? true : false; - var verticalQ = !horizontalQ; - - // background cliping rectangle - var bgcpRect = { x: 0, y: 0, width: spaceX, height: spaceY }; - var container = setupContainer(selection, namespace, bgcpRect, backgroundFill); - - cellKeys = d3.keys(data); - setValues = unique(cellKeys.map(setExtractor)).sort(); - intersectionValues = unique(cellKeys.map(intersectionExtractor)).sort().sort(function (a, b) { - return a.split(';').length - b.split(';').length; - }); - - if (!horizontalQ) { - cellKeys.sort(function (a, b) { - return setKeySortingFunction(a, b) || intersectionKeySortingFunction(a, b); - }); - } else { - cellKeys.sort(function (a, b) { - return intersectionKeySortingFunction(a, b) || setKeySortingFunction(a, b); - }); - } - - var xValues = horizontalQ ? intersectionValues : setValues, - yValues = horizontalQ ? setValues : intersectionValues, - xDim = horizontalQ ? xValues.length : yValues.length, - yDim = horizontalQ ? yValues.length : xValues.length; - - // console.log(xValues, yValues) - - - xObjectSize = calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, xObjectSpacer, overflowQ); - yObjectSize = calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, yObjectSpacer, overflowQ); - xSpacerSize = calculateWidthOfSpacer(xValues, spaceX, xObjectSize, xDim, xObjectSpacer, overflowQ); - ySpacerSize = calculateWidthOfSpacer(yValues, spaceY, yObjectSize, yDim, yObjectSpacer, overflowQ); - - var ySpacer = groupingSpacer().horizontalQ(false).moveby('category').numberOfObjects(yDim).objectSize(yObjectSize).spacerSize(ySpacerSize).transitionDuration(transitionDuration).easeFunc(easeFunc); - - var xSpacer = groupingSpacer().horizontalQ(true).moveby('category').numberOfObjects(xDim).objectClass(objectClass).objectSize(xObjectSize).spacerSize(xSpacerSize).transitionDuration(transitionDuration).easeFunc(easeFunc); - - if (verticalQ) { - xSpacer.objectClass(objectClass); - ySpacer.namespace('across').objectClass(hypenate(objectClass, 'across')); - - ySpacer(container, yValues, 0); - container.selectAll('g.' + hypenate(objectClass, 'across')).each(function (d, i) { - xSpacer(d3.select(this), xValues, 0); - }); - } else { - xSpacer.namespace('across').objectClass(hypenate(objectClass, 'across')); - ySpacer.objectClass(objectClass); - - xSpacer(container, xValues, 0); - container.selectAll('g.' + hypenate(objectClass, 'across')).each(function (d, i) { - ySpacer(d3.select(this), yValues, 0); - }); - } - - var cells = container.selectAll('g:not(.to-remove).' + objectClass); - var lookup = {}; - cellKeys.map(function (k, i) { - lookup[setExtractor(k) + '::' + intersectionExtractor(k)] = k; - }); - - // var positionedCellKeys = [] - // for (var i = 0; i < setValues.length; i++) { - // for (var j = 0; j < intersectionValues.length; j++) { - // var lookupValue = lookup[setValues[j]+"::"+intersectionValues[i]] - // if (lookupValue == undefined) { - // positionedCellKeys.push(undefined) - // } else { - // positionedCellKeys.push(lookupValue) - // } - // console.log(i, j, lookupValue) - // } - // } - // - // // console.log(positionedCellKeys) - // - // cells.data(positionedCellKeys); - - - cells.data(cellKeys); - - cells.each(function (key, i) { - var t = d3.select(this); - if (key == undefined) { - return; - } - var currentData = data[key]; - // console.log(key, currentData) - var set = setExtractor(key, i), - intersection = intersectionExtractor(key, i); - - // console.log(set, intersection) - - t.classed(intersection, true); - t.classed(set, true); - - var c = safeSelect(t, 'circle', hypenate(objectClass, 'circle')); - c.attr('cx', xObjectSize / 2).attr('cy', yObjectSize / 2).attr('r', radius == undefined ? Math.min(xObjectSize, yObjectSize) / 2 : radius).attr('fill', intersection.includes(set) ? "black" : 'rgb(233,233,233)').attr('stroke', "black").attr("in-intersection", intersection.includes(set)); - }); - } - - function intersectionTotals() { - var totals = {}; - // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) - - intersectionValues.map(function (k, i) { - totals[k] = { 'total': 0 }; - }); - cellKeys.map(function (k, i) { - var e = elementExtractor(k, i); - if (totals[intersectionExtractor(k, i)]['total'] == 0) { - if (Array.isArray(e)) { - totals[intersectionExtractor(k, i)]['total'] += e.length; - totals[intersectionExtractor(k, i)]['values'] = e; - } else { - totals[intersectionExtractor(k, i)]['total'] += e; - } - } - }); - return totals; - } - - function setTotals() { - var totals = {}; - // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) - - setValues.map(function (k, i) { - totals[k] = { 'total': 0 }; - }); - - cellKeys.map(function (k, i) { - var e = elementExtractor(k, i); - if (Array.isArray(e)) { - totals[setExtractor(k, i)]['total'] += e.length; - } else { - totals[setExtractor(k, i)]['total'] += e; - } - }); - return totals; - } - - upset.intersectionTotals = intersectionTotals; - upset.setTotals = setTotals; - - return upset; - } - - function filterTable(selection) { - var data, - namespace = 'd3sm-filter-table', - fieldFunction = function fieldFunction(record, columnKey, recordValue) { - return recordValue; - }; - - filterTable.data = function (_) { - return arguments.length ? (data = _, filterTable) : data; - }; - filterTable.namespace = function (_) { - return arguments.length ? (namespace = _, filterTable) : namespace; - }; - filterTable.fieldFunction = function (_) { - return arguments.length ? (fieldFunction = _, filterTable) : fieldFunction; - }; - - function filterTable() { - - var container = safeSelect(selection, 'div', 'filter-table').classed(hypenate(namespace, 'container'), true), - inputGroup = safeSelect(container, 'div', 'filter-input-group').classed('input-group', true), - inputPrepend = safeSelect(inputGroup, 'div', 'input-group-prepend'), - inputPrependSpan = safeSelect(inputPrepend, 'span', 'input-group-text').classed('search-button', true), - inputPrependSpanIcon = safeSelect(inputPrependSpan, 'i', 'fa fa-search'), - input = safeSelect(inputGroup, 'input', 'form-control').attr('placeholder', 'filter').attr('type', 'text'), - inputAppend = safeSelect(inputGroup, 'div', 'input-group-append'), - inputAppendButton = safeSelect(inputAppend, 'a', 'close-button').classed('btn btn-outline-secondary', true), - inputAppendButtonIcon = safeSelect(inputAppendButton, 'i', 'fa fa-close'), - closeButton = inputAppendButton, - rTable = safeSelect(container, 'div', 'table-responsive'), - table = safeSelect(rTable, 'table', 'table').classed('table-hover', true).classed('table-bordered', true).classed("table-striped", true), - tHead = safeSelect(table, 'thead').classed("thead-dark", true), - header = safeSelect(tHead, 'tr'), - tBody = safeSelect(table, 'tbody'); - - var rows = d3.keys(data), - cols = d3.keys(data[rows[0]]); - - var th = makeColumns(header, cols); - var tr = makeRows(tBody, rows); - var tr = populateRecords(tr, cols); - - input.on('input', function (d, i) { - var val = input.property('value'), - reg = new RegExp(val, 'gi'), - use; - if (val == '') { - use = rows; - } else { - use = []; - rows.map(function (r, i) { - var row = data[r]; - var fieldJoin = cols.map(function (c, j) { - return fieldFunction(row, c, row[c]); - // return row[c] - }).join(''); - var match = fieldJoin.match(reg); - if (match == null || match.join('') == '') ; else { - use.push(r); - } - }); - } - - tr = makeRows(tBody, use); - tr = populateRecords(tr, cols); - }); - - closeButton.on('click', function (d, i) { - input.property('value', '').dispatch('input'); - }); - } - - function makeColumns(header, cols) { - var th = header.selectAll('th'); - th = th.data(cols); - th.exit().remove(); - th = th.merge(th.enter().append('th')); - th.attr('scope', 'col').text(function (d, i) { - return d; - }); - return th; - } - - function makeRows(body, rows) { - var tr = body.selectAll('tr'); - tr = tr.data(rows); - tr.exit().remove(); - tr = tr.merge(tr.enter().append('tr')); - return tr; - } - - function populateRecords(rows, cols) { - rows.each(function (r, i) { - var record = data[r], - t = d3.select(this); - - var fields = t.selectAll('td'); - fields = fields.data(cols); - fields.exit().remove(); - fields = fields.merge(fields.enter().append('td')); - - fields.attr('scope', function (f, j) { - return j == 0; - }).html(function (f, j) { - return fieldFunction(record, f, record[f]); - }); - }); - return rows; - } - - return filterTable; - } - - // Import styles (automatically inject into ). - - // /** @module d3sm */ - var d3sm$1 = {}; - d3sm$1.axis = axis; - d3sm$1.bar = bar; - d3sm$1.bubbleHeatmap = bubbleHeatmap; - d3sm$1.heatmap = heatmap; - d3sm$1.boxwhisker = boxwhisker; - d3sm$1.colorFunction = colorFunction; - d3sm$1.datatoggle = datatoggle; - d3sm$1.groupingSpacer = groupingSpacer; - d3sm$1.tooltip = tooltip; - d3sm$1.scatter = scatter; - d3sm$1.plotZoom = plotZoom; - d3sm$1.multiPlotZoom = multiPlotZoom; - d3sm$1.violin = violin; - d3sm$1.numericLegend = numericLegend; - d3sm$1.categoricLegend = categoricLegend; - d3sm$1.lasso = lasso; - d3sm$1.lassoWidget = lassoWidget; - d3sm$1.selectFilter = selectFilter; - d3sm$1.upset = upset; - d3sm$1.filterTable = filterTable; - - d3sm$1.uniqueElements = uniqueElements; - d3sm$1.getTranslation = getTranslation; - d3sm$1.modifyHexidecimalColorLuminance = modifyHexidecimalColorLuminance; - d3sm$1.tickRange = tickRange; - d3sm$1.quartiles = quartiles; - d3sm$1.extractViolinValues = extractViolinValues; - d3sm$1.hypenate = hypenate; - d3sm$1.round = round; - d3sm$1.getContainingSVG = getContainingSVG; - d3sm$1.interpolateColors = interpolateColors; - d3sm$1.truncateText = truncateText; - d3sm$1.safeSelect = safeSelect; - - d3sm$1.whichBin = whichBin; - d3sm$1.unique = unique; - d3sm$1.flatten = flatten; - - d3sm$1.setupStandardChartContainers = setupStandardChartContainers; - d3sm$1.log = log; - d3sm$1.warn = warn; - d3sm$1.info = info; - d3sm$1.error = error; - d3sm$1.consoleGroup = consoleGroup; - d3sm$1.consoleGroupEnd = consoleGroupEnd; - d3sm$1.resizeDebounce = resizeDebounce; - - d3sm$1.debugQ = false; - - // Import a logger for easier debugging - // import debug from 'debug'; - // const log = debug('app:log'); - - // The logger should only be disabled if we're not in production. - // if ("development" !== 'production') { - // // Enable the logger. - // debug.enable('*'); - // log('Logging is enabled!'); - // - // // Enable LiveReload - // document.write( - // ' - - - - - - - - - - - - - - - - - - - - -
- - - -
-

By Value

-
- -
-

By Category

-
- -
-

By Value Guidelines

-
-
- - - - - diff --git a/demos/axes/js/v001.js b/demos/axes/js/v001.js deleted file mode 100644 index ab67ca4..0000000 --- a/demos/axes/js/v001.js +++ /dev/null @@ -1,196 +0,0 @@ -var data = d3smDemoData['basicViolins'] - -var -selection = d3.select("#axes-by-value") -namespace = 'axes', -container = d3sm.safeSelect(selection, 'svg', namespace).style('width', '1000px'), -lAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'left-axis')).attr('transform', "translate(100,100)"), -rAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'right-axis')).attr('transform', "translate(900,100)") -uAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'up-axis')).attr('transform', "translate(100,100)") -dAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'down-axis')).attr('transform', "translate(100,400)") - - -tickValues = [1,2,3,4,5] - -var uA = d3sm.axis(uAxis) -.spaceX(800) -.spaceY(100) -.orient("top") -.overflowQ(false) -.tickValues(tickValues) -.numberOfTicks(5) -.namespace('u') -// .guideLinesQ(true) -.guidelineSpace(300) - -var dA = d3sm.axis(dAxis) -.spaceX(800) -.spaceY(100) -.orient("bottom") -.overflowQ(false) -.tickValues(tickValues) -.numberOfTicks(5) -.namespace('d') -// .guideLinesQ(true) -.guidelineSpace(300) - - -var lA = d3sm.axis(lAxis) -.spaceX(100) -.spaceY(300) -.orient("left") -.overflowQ(false) -.tickValues(tickValues) -.numberOfTicks(5) -.namespace('l') -// .guideLinesQ(true) -.guidelineSpace(800) - -var rA = d3sm.axis(rAxis) -.spaceX(100) -.spaceY(300) -.orient("right") -.overflowQ(false) -.tickValues(tickValues) -.numberOfTicks(5) -.namespace('r') -// .guideLinesQ(true) -.guidelineSpace(800) - -uA() -dA() -lA() -rA() - - - -var -selection = d3.select("#axes-by-cat") -namespace = 'axes', -container = d3sm.safeSelect(selection, 'svg', namespace).style('width', '1000px'), -lAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'left-axis')).attr('transform', "translate(100,100)"), -rAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'right-axis')).attr('transform', "translate(900,100)") -uAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'up-axis')).attr('transform', "translate(100,100)") -dAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'down-axis')).attr('transform', "translate(100,400)") - - -tickValues = [1,2,3,4,5] -tickLabels = ["this", "this-text", "this-text-gets", "this-text-gets-longer", "this-text-gets-longer-as-we-go-on"] - -var uA = d3sm.axis(uAxis) -.spaceX(800) -.spaceY(100) -.orient("top") -.overflowQ(false) -.categoricalQ(true) -.tickLabels(tickLabels) -.numberOfTicks(5) -.namespace('u-c') -// .guideLinesQ(true) -.guidelineSpace(300) - -var dA = d3sm.axis(dAxis) -.spaceX(800) -.spaceY(100) -.orient("bottom") -.overflowQ(false) -.categoricalQ(true) -.tickLabels(tickLabels) -.numberOfTicks(5) -.namespace('d-c') -// .guideLinesQ(true) -.guidelineSpace(300) - - -var lA = d3sm.axis(lAxis) -.spaceX(100) -.spaceY(300) -.orient("left") -.overflowQ(false) -.categoricalQ(true) -.tickLabels(tickLabels) -.numberOfTicks(5) -.namespace('l-c') -// .guideLinesQ(true) -.guidelineSpace(800) - -var rA = d3sm.axis(rAxis) -.spaceX(100) -.spaceY(300) -.orient("right") -.overflowQ(false) -.categoricalQ(true) -.tickLabels(tickLabels) -.numberOfTicks(5) -.namespace('r-c') -// .guideLinesQ(true) -.guidelineSpace(800) - -uA() -dA() -lA() -rA() - - - -var -selection = d3.select("#axes-by-val-g") -namespace = 'axes', -container = d3sm.safeSelect(selection, 'svg', namespace).style('width', '1000px'), -lAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'left-axis')).attr('transform', "translate(100,100)"), -rAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'right-axis')).attr('transform', "translate(900,100)") -uAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'up-axis')).attr('transform', "translate(100,100)") -dAxis = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'down-axis')).attr('transform', "translate(100,400)") - - -tickValues = [1,2,3,4,5] - -var uA = d3sm.axis(uAxis) -.spaceX(800) -.spaceY(100) -.orient("top") -.overflowQ(false) -.tickValues(tickValues) -.numberOfTicks(5) -.namespace('u-g') -.guideLinesQ(true) -.guidelineSpace(300) - -var dA = d3sm.axis(dAxis) -.spaceX(800) -.spaceY(100) -.orient("bottom") -.overflowQ(false) -.tickValues(tickValues) -.numberOfTicks(5) -.namespace('d-g') -.guideLinesQ(true) -.guidelineSpace(300) - - -var lA = d3sm.axis(lAxis) -.spaceX(100) -.spaceY(300) -.orient("left") -.overflowQ(false) -.tickValues(tickValues) -.numberOfTicks(5) -.namespace('l-g') -.guideLinesQ(true) -.guidelineSpace(800) - -var rA = d3sm.axis(rAxis) -.spaceX(100) -.spaceY(300) -.orient("right") -.overflowQ(false) -.tickValues(tickValues) -.numberOfTicks(5) -.namespace('r-g') -.guideLinesQ(true) -.guidelineSpace(800) - -uA() -dA() -lA() -rA() diff --git a/demos/axes/js/v003.js b/demos/axes/js/v003.js deleted file mode 100644 index e69de29..0000000 diff --git a/demos/bar-chart-same-data-complex-grouping/index.html b/demos/bar-chart-same-data-complex-grouping/index.html deleted file mode 100644 index 84a766e..0000000 --- a/demos/bar-chart-same-data-complex-grouping/index.html +++ /dev/null @@ -1,195 +0,0 @@ - - - - - Complex Grouping - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/bar-chart-same-data-complex-grouping/js/v001.js b/demos/bar-chart-same-data-complex-grouping/js/v001.js deleted file mode 100644 index 50a6405..0000000 --- a/demos/bar-chart-same-data-complex-grouping/js/v001.js +++ /dev/null @@ -1,125 +0,0 @@ -function barPlot( selection ) { - var data, - namespace - plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; - plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; - - - function plot() { - var - databar = d3sm.safeSelect(selection, 'form', d3sm.hypenate(namespace,'databar')), - dataPrint = d3sm.safeSelect(selection, 'div', d3sm.hypenate(namespace,'data-print')), - - selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:500}, undefined, {axes:{x:0.1, y:0.1},space:{w:1,h:1}}) - container = selections.svg.selection, - bars = selections.plot.selection, - xAxis = selections.xAxis.selection, - yAxis = selections.yAxis.selection - - - // make data selection - var dataSelect = d3sm.datatoggle(databar) - .keys(d3.keys(data.groupings)) - .updateFunction(function(){plot(); zoom.reset()}) - .namespace(namespace)() - - // grab current selected key - var currentKey = dataSelect.currentKey() - // // extract subdata - // var currentData = data[currentKey] - // - - - dataPrint.text(JSON.stringify(data.groupings[currentKey])) - // make bars - var b = d3sm.bar(bars) - .data(data.data) - .grouping(data.groupings[currentKey]) - .spaceX(selections.plot.rect.w) - .spaceY(selections.plot.rect.h) - .overflowQ(true) - .orient('horizontal') - .maxObjectSize(20) - .minObjectSize(10) - .valueExtractor(function(d, i){return data.data[d]['value'] }) - - - // b.tooltip().keys(['value']) - // - // b.colorFunction().colorBy('index') - b() - - // extract keys and values to safe re-computation - var bk = b.barKeys(), bv = b.barValues() - - // left d3sm.axis - var yA = d3sm.axis(yAxis) - .spaceX(selections.yAxis.rect.w) - .spaceY(selections.yAxis.rect.h) - .orient("left") - .overflowQ(false) - .tickValues(bv) - .guideLinesQ(true) - .guidelineSpace(selections.xAxis.rect.w) - .namespace('axis-left') - .numberOfTicks(5) - yA() - - // bottom d3sm.axis - var xA = d3sm.axis(xAxis) - .spaceX(selections.xAxis.rect.w) - .spaceY(selections.xAxis.rect.h) - .orient("bottom") - .overflowQ(true) - .categoricalQ(true) - .tickLabels(bk) - .namespace('axis-bottom') - .minObjectSize(b.minObjectSize()) - .maxObjectSize(b.maxObjectSize()) - .grouping(data.groupings[currentKey]) - .objectSpacer(b.objectSpacer()) - xA() - - b.selection().selectAll("rect").nodes().map(function(d, i){ - console.log(d.getBoundingClientRect(), i) - return this - }), - console.log( - b.selection().node(), - b.selection().selectAll("rect").nodes(), - b.selection().selectAll("rect").nodes(), - 'BCR:',b.selection().node().getBoundingClientRect(), - 'BBox:',b.selection().node().getBBox() - ) - - - - - - // // set-up zoom, whell and drag - var zoom = d3sm.plotZoom(b, xA, yA).eventType('drag') - .orient('horizontal') - // .xLock( - // objs.getBBox().width - // ) - - console.log(zoom) - - // zoom.setLocks() - - var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ - // zoom.setLocks() - zoom.eventType('drag')() - }) - container.call(pan).on('wheel', function(){ - // zoom.setLocks() - - zoom.eventType('wheel')() - }) - } - - - - - return plot -} diff --git a/demos/basic-violins/index.html b/demos/basic-violins/index.html deleted file mode 100644 index e0cbea7..0000000 --- a/demos/basic-violins/index.html +++ /dev/null @@ -1,172 +0,0 @@ - - - - - Basic Violins - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/box-whiskers-points/index.html b/demos/box-whiskers-points/index.html deleted file mode 100644 index 165e1ac..0000000 --- a/demos/box-whiskers-points/index.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/box-whiskers/index.html b/demos/box-whiskers/index.html deleted file mode 100644 index 69abf28..0000000 --- a/demos/box-whiskers/index.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/bubble-heatmap/index.html b/demos/bubble-heatmap/index.html deleted file mode 100644 index 19fc41e..0000000 --- a/demos/bubble-heatmap/index.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - Basic Bubble Heatmap - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/bubble-heatmap/js/v000.js b/demos/bubble-heatmap/js/v000.js deleted file mode 100644 index 03adb85..0000000 --- a/demos/bubble-heatmap/js/v000.js +++ /dev/null @@ -1,96 +0,0 @@ -function heatmap( selection ) { - var data, - namespace - plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; - plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; - - function plot(){ - var - selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:750}, undefined, {axes:{x:0.2, y:0.2},space:{w:1,h:1}}) - container = selections.svg.selection, - bubbles = selections.plot.selection, - xAxis = selections.xAxis.selection, - yAxis = selections.yAxis.selection - - // d3sm.debugQ = true - - - var bhm = d3sm.bubbleHeatmap(bubbles) - .spaceX(selections.plot.rect.w) - .spaceY(selections.plot.rect.h) - .xKey('xKey') - .yKey('yKey') - .vKey('val') - .rKey('val') - .overflowQ(true) - // .minObjectSize(100) - .maxObjectSize(100) - - - // .yKeySortingFunction() - // .xKeySortingFunction(function(a,b){return data[a]['xKey']}) - - .data(data) - - bhm.tooltip().keys(['tooltip']) - bhm() - // - // - // - // // extract keys and values to safe re-computation - var xk = bhm.xValues(), yk = bhm.yValues() - // var pd = [] - // vk.map(function(k, i){ pd.push(...d3.values(vv[k].quartiles)) }) - // pd = d3sm.unique(pd.sort(function (a, b) {return d3.ascending(a, b)})) - // - // console.log(pd) - // console.log(yk, bhm.minObjectSize(), bhm.maxObjectSize(), bhm.ySpacerSize()) - - var yA = d3sm.axis(yAxis) - .spaceX(selections.yAxis.rect.w) - .spaceY(selections.yAxis.rect.h) - .orient("left") - .overflowQ(true) - .categoricalQ(true) - .tickLabels(yk) - .namespace('axis-left') - .minObjectSize(bhm.minObjectSize()) - .maxObjectSize(bhm.maxObjectSize()) - .objectSpacer(bhm.ySpacerSize()) - .objectSize(bhm.ySize()) - - .guideLinesQ(true) - .guidelineSpace(selections.xAxis.rect.w) - yA() - - var xA = d3sm.axis(xAxis) - .spaceX(selections.xAxis.rect.w) - .spaceY(selections.xAxis.rect.h) - .orient("bottom") - .overflowQ(true) - .categoricalQ(true) - .tickLabels(xk) - .namespace('axis-bottom') - .minObjectSize(bhm.minObjectSize()) - .maxObjectSize(bhm.maxObjectSize()) - .objectSpacer(bhm.xSpacerSize()) - .objectSize(bhm.xSize()) - .guideLinesQ(true) - .guidelineSpace(selections.yAxis.rect.h) - - xA() - - console.log(bhm.selection().node(), bhm.selection().node().getBBox(), bhm.selection().node().getBoundingClientRect()) - - - var zoom = d3sm.plotZoom(bhm, xA, yA).eventType('drag') - .orient('2D') - - var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){zoom.eventType('drag')()}) - container.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) - - } - - - return plot -} diff --git a/demos/bundle/index.html b/demos/bundle/index.html deleted file mode 100644 index 3c70c73..0000000 --- a/demos/bundle/index.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/filter-select/index.html b/demos/filter-select/index.html deleted file mode 100644 index b289735..0000000 --- a/demos/filter-select/index.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - Filter Select - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
-
-
- - - - - - - - - - -
-
- - -
-
Rendered with v0.0.2
- -
-
- -
-
-
-
- - - - - - - - - diff --git a/demos/filter-select/js/v002.txt b/demos/filter-select/js/v002.txt deleted file mode 100644 index e69de29..0000000 diff --git a/demos/filter-table/index.html b/demos/filter-table/index.html deleted file mode 100644 index 1dfaca4..0000000 --- a/demos/filter-table/index.html +++ /dev/null @@ -1,120 +0,0 @@ - - - - - Filter Table - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
-
Rendered with v0.0.3
- -
-
- -
-
-
-
- - - - - - - - - - diff --git a/demos/filter-table/js/v003.js b/demos/filter-table/js/v003.js deleted file mode 100644 index a32e9c3..0000000 --- a/demos/filter-table/js/v003.js +++ /dev/null @@ -1,222 +0,0 @@ -//VERSION: 0.0.3 -function upset( selection ) { - var - data, - namespace='upset', - barYAxis, - upsetYAxis, - ups, - bar, barR, barRXAxis - - var - upset, - upsetAxis, - setTotalsBarChart, - setTotalsAxis, - intersectionTotalsBarChart, - intersectionTotalsAxis - - plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; - plot.upset = function(_){return arguments.length ? (upset = _, plot) : upset; }; - plot.upsetAxis = function(_){return arguments.length ? (upsetAxis = _, plot) : upsetAxis; }; - plot.setTotalsBarChart = function(_){return arguments.length ? (setTotalsBarChart = _, plot) : setTotalsBarChart; }; - plot.setTotalsAxis = function(_){return arguments.length ? (setTotalsAxis = _, plot) : setTotalsAxis; }; - plot.intersectionTotalsBarChart = function(_){return arguments.length ? (intersectionTotalsBarChart = _, plot) : intersectionTotalsBarChart; }; - plot.intersectionTotalsAxis = function(_){return arguments.length ? (intersectionTotalsAxis = _, plot) : intersectionTotalsAxis; }; - - - - function plot() { - var - svg = d3sm.safeSelect(selection, 'svg'), - - upsetAxisSelection = d3sm.safeSelect(svg, 'g', 'upset-chart-axis'), - setTotalsAxisSelection = d3sm.safeSelect(svg, 'g', 'set-bar-chart-axis'), - intersectionTotalsAxisSelection = d3sm.safeSelect(svg, 'g', 'intersection-bar-chart-axis'), - - upsetSelection = d3sm.safeSelect(svg, 'g', 'upset-chart'), - setTotalsBarChartSelection = d3sm.safeSelect(svg, 'g', 'set-bar-chart'), - intersectionTotalsBarChartSelection = d3sm.safeSelect(svg, 'g', 'intersection-bar-chart') - - - - if (upset == undefined) { upset = d3sm.upset(upsetSelection) } - if (upsetAxis == undefined) { upsetAxis = d3sm.axis(upsetAxisSelection) } - if (setTotalsBarChart == undefined) { setTotalsBarChart = d3sm.bar(setTotalsBarChartSelection) } - if (setTotalsAxis == undefined) { setTotalsAxis = d3sm.axis(setTotalsAxisSelection) } - if (intersectionTotalsBarChart == undefined) { intersectionTotalsBarChart = d3sm.bar(intersectionTotalsBarChartSelection) } - if (intersectionTotalsAxis == undefined) { intersectionTotalsAxis = d3sm.axis(intersectionTotalsAxisSelection) } - - - var - setChartSpacer = 10, - intersectionChartSpacer = 10, - - percentages = { - inChart: {w: 0.75, h: 0.7 }, - usChart: {w: 0.75, h: 0.2 }, - stChart: {w: 0.10, h: 0.2 }, - - inAxis : {w: 0.15, h: 0.7 }, - usAxis : {w: 0.15, h: 0.2 }, - stAxis : {w: 0.10, h: 0.2 }, - - svg: {w: 0.8, h: 0.7} - } - - var - page = {w: window.innerWidth, h: window.innerHeight}, - margins = {left: page.w *0.01, right: page.w *0.01, top: page.h*0.01, bottom: page.h*0.01}, - svgRect = { - _w: page.w * percentages.svg.w, - _h: page.h * percentages.svg.h, - w: page.w * percentages.svg.w - margins.left - margins.right, - h: page.h * percentages.svg.h - margins.top - margins.bottom - }, - chartRect = { - w: svgRect.w - setChartSpacer*2, - h: svgRect.h - intersectionChartSpacer*2 - }, - upsetRect = { - x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer*2, - y: margins.top + chartRect.h * percentages.inChart.h + intersectionChartSpacer, - w: chartRect.w * percentages.usChart.w, - h: chartRect.h * percentages.usChart.h - }, - upsetAxisRect = { - x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer, - y: margins.top + chartRect.h * percentages.inChart.h + intersectionChartSpacer, - w: chartRect.w * percentages.usAxis.w, - h: chartRect.h * percentages.usAxis.h - }, - setTotalsRect = { - x: margins.left, - y: margins.top + chartRect.h * percentages.inChart.h + intersectionChartSpacer, - w: chartRect.w * percentages.stChart.w, - h: chartRect.h * percentages.stChart.h - }, - setTotalsAxisRect = { - x: margins.left, - y: margins.top + (chartRect.h * percentages.inChart.h) + (chartRect.h * percentages.stChart.h) + intersectionChartSpacer*2, - w: chartRect.w * percentages.stAxis.w, - h: chartRect.h * percentages.stAxis.h - }, - intersectionTotalsRect = { - x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer*2, - y: margins.top, - w: chartRect.w * percentages.inChart.w, - h: chartRect.h * percentages.inChart.h - }, - intersectionTotalsAxisRect = { - x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer, - y: margins.top, - w: chartRect.w * percentages.inAxis.w, - h: chartRect.h * percentages.inAxis.h - } - - svg.attr('width', svgRect._w).attr('height', svgRect._h) - upsetAxisSelection.attr('transform', 'translate('+upsetAxisRect.x+','+upsetAxisRect.y+')') - setTotalsAxisSelection.attr('transform', 'translate('+setTotalsAxisRect.x+','+setTotalsAxisRect.y+')') - intersectionTotalsAxisSelection.attr('transform', 'translate('+intersectionTotalsAxisRect.x+','+intersectionTotalsAxisRect.y+')') - - upsetSelection.attr('transform', 'translate('+upsetRect.x+','+upsetRect.y+')') - setTotalsBarChartSelection.attr('transform', 'translate('+setTotalsRect.x+','+setTotalsRect.y+')') - intersectionTotalsBarChartSelection.attr('transform', 'translate('+intersectionTotalsRect.x+','+intersectionTotalsRect.y+')') - - - var overflowQ = false - - upset - .data(data) - .spaceX(upsetRect.w) - .spaceY(upsetRect.h) - .xObjectSpacer(0.01) - .yObjectSpacer(0.01) - .maxObjectSize(15) - .minObjectSize(15) - .overflowQ(overflowQ) - // .radius(1) - upset() - - upsetAxis - .spaceX(upsetAxisRect.w) - .spaceY(upsetAxisRect.h) - .orient('left') - .categoricalQ(true) - .tickLabels(upset.setValues()) - .overflowQ(overflowQ) - .minObjectSize(upset.minObjectSize()) - .maxObjectSize(upset.maxObjectSize()) - .objectSpacer(upset.yObjectSpacer()) - - upsetAxis() - - var intersectionTotals = upset.intersectionTotals() - var setTotals = upset.setTotals() - - - intersectionTotalsBarChart.data(intersectionTotals) - .grouping(upset.intersectionValues()) - .spaceX(intersectionTotalsRect.w) - .spaceY(intersectionTotalsRect.h) - .namespace('intersections') - .orient('horizontal') - .valueExtractor(function(k, i){return intersectionTotals[k]['total']}) - .overflowQ(overflowQ) - .minObjectSize(upset.minObjectSize()) - .maxObjectSize(upset.maxObjectSize()) - .objectSpacer(upset.xObjectSpacer()) - - - intersectionTotalsBarChart() - - intersectionTotalsAxis - .spaceX(intersectionTotalsAxisRect.w) - .spaceY(intersectionTotalsAxisRect.h) - .orient('left') - .namespace('intersections-axis-left') - .tickValues(intersectionTotalsBarChart.barValues()) - .guideLinesQ(true) - .guidelineSpace(intersectionTotalsRect.w + intersectionChartSpacer) - .overflowQ(overflowQ) - - intersectionTotalsAxis() - - - - setTotalsBarChart.data(setTotals) - .grouping(upset.setValues()) - .spaceX(setTotalsRect.w) - .spaceY(setTotalsRect.h) - .orient('right') - .valueExtractor(function(k, i){return setTotals[k]['total']}) - .namespace('set') - .overflowQ(overflowQ) - .minObjectSize(upset.minObjectSize()) - .maxObjectSize(upset.maxObjectSize()) - .objectSpacer(upset.yObjectSpacer()) - - - setTotalsBarChart() - - setTotalsAxis - .spaceX(setTotalsAxisRect.w) - .spaceY(setTotalsAxisRect.h) - .namespace('set-axis-bottom') - .orient('bottom') - .tickValues(setTotalsBarChart.barValues()) - .reverseScaleQ(true) - // .tickLabelRotation(0) - .overflowQ(overflowQ) - - setTotalsAxis() - - // var zoom = d3sm.plotZoom(upset, upsetAxis, function(){}).eventType('drag').orient('2D') - // var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) - // upsetSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) - - - } - - return plot -} diff --git a/demos/heatmap/index.html b/demos/heatmap/index.html deleted file mode 100644 index e5c7a19..0000000 --- a/demos/heatmap/index.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - Basic Heatmap - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demos/heatmap/js/v000.js b/demos/heatmap/js/v000.js deleted file mode 100644 index 2dff8e0..0000000 --- a/demos/heatmap/js/v000.js +++ /dev/null @@ -1,98 +0,0 @@ -function heatmap( selection ) { - var data, - namespace - plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; - plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; - - function plot(){ - var - selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:750}, undefined, {axes:{x:0.2, y:0.2},space:{w:1,h:1}}) - container = selections.svg.selection, - bubbles = selections.plot.selection, - xAxis = selections.xAxis.selection, - yAxis = selections.yAxis.selection - - - d3sm.debugQ = true - - var overflow = true - // var overflow = false - var bhm = d3sm.heatmap(bubbles) - .spaceX(selections.plot.rect.w) - .spaceY(selections.plot.rect.h) - .xKey('xKey') - .yKey('yKey') - .vKey('val') - .overflowQ(overflow) - .objectStrokeWidth(2) - // .minObjectSize(100) - .maxObjectSize(100) - - - // .yKeySortingFunction() - // .xKeySortingFunction(function(a,b){return data[a]['xKey']}) - - .data(data) - - bhm.tooltip().keys(['tooltip']) - bhm() - // - // - // - // // extract keys and values to safe re-computation - var xk = bhm.xValues(), yk = bhm.yValues() - // var pd = [] - // vk.map(function(k, i){ pd.push(...d3.values(vv[k].quartiles)) }) - // pd = d3sm.unique(pd.sort(function (a, b) {return d3.ascending(a, b)})) - // - // console.log(pd) - // console.log(yk, bhm.minObjectSize(), bhm.maxObjectSize(), bhm.ySpacerSize()) - - var yA = d3sm.axis(yAxis) - .spaceX(selections.yAxis.rect.w) - .spaceY(selections.yAxis.rect.h) - .orient("left") - .overflowQ(overflow) - .categoricalQ(true) - .tickLabels(yk) - .namespace('axis-left') - .minObjectSize(bhm.minObjectSize()) - .maxObjectSize(bhm.maxObjectSize()) - .objectSpacer(bhm.ySpacerSize()) - .objectSize(bhm.ySize()) - - .guideLinesQ(true) - .guidelineSpace(selections.xAxis.rect.w) - yA() - - var xA = d3sm.axis(xAxis) - .spaceX(selections.xAxis.rect.w) - .spaceY(selections.xAxis.rect.h) - .orient("bottom") - .overflowQ(overflow) - .categoricalQ(true) - .tickLabels(xk) - .namespace('axis-bottom') - .minObjectSize(bhm.minObjectSize()) - .maxObjectSize(bhm.maxObjectSize()) - .objectSpacer(bhm.xSpacerSize()) - .objectSize(bhm.xSize()) - .guideLinesQ(true) - .guidelineSpace(selections.yAxis.rect.h) - - xA() - - console.log(bhm.selection().node(), bhm.selection().node().getBBox(), bhm.selection().node().getBoundingClientRect()) - - - var zoom = d3sm.plotZoom(bhm, xA, yA).eventType('drag') - .orient('2D') - - var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){zoom.eventType('drag')()}) - container.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) - - } - - - return plot -} diff --git a/demos/heatmap/js/v003.js b/demos/heatmap/js/v003.js deleted file mode 100644 index d8cafa7..0000000 --- a/demos/heatmap/js/v003.js +++ /dev/null @@ -1,132 +0,0 @@ -function heatmap ( selection ) { - var - chart, - xAxis, - yAxis, - legend, - - data, - namespace = 'heatmap', - title = 'Heatmap' - - plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; - plot.title = function(_){return arguments.length ? (title = _, plot) : title; }; - plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; - - plot.chart = function(_){return arguments.length ? (chart = _, plot) : chart; }; - plot.xAxis = function(_){return arguments.length ? (xAxis = _, plot) : xAxis; }; - plot.yAxis = function(_){return arguments.length ? (yAxis = _, plot) : yAxis; }; - plot.legend = function(_){return arguments.length ? (legend = _, plot) : legend; }; - - function plot(){ - var - orient = '2D', - - titleContainer = d3sm.safeSelect(selection, 'h5', d3sm.hypenate(namespace,'title')).text(title), - - selections = d3sm.setupStandardChartContainers( - selection, - namespace, - {w:Math.min(window.innerWidth,1200), h:Math.min(window.innerHeight,700)}, - {top:0.01, bottom:0.01, left:0.01, right:0.01}, - {w:1, h:1}, // percent of container space for svg - {y:0.1,x:0.2}, // percent of container space for axes, - {x:25, margin:05} // absolute width of legendSelect and space on either size - ), - - container = selections.svg.selection, - chartSelection = selections.plot.selection, - xAxisSelection = selections.xAxis.selection, - yAxisSelection = selections.yAxis.selection, - legendSelect = selections.legend.selection - - - if (chart == undefined){ chart = d3sm.heatmap(chartSelection) } - if (yAxis == undefined) { yAxis = d3sm.axis(yAxisSelection) } - if (xAxis == undefined) { xAxis = d3sm.axis(xAxisSelection) } - if (legend == undefined) { - legend = d3sm.numericLegend(legendSelect) - .spaceX(selections.legend.rect.w).spaceY(selections.plot.rect.h) - - } - - // d3sm.debugQ = true - var overflowQ = true - - chart - .spaceX(selections.plot.rect.w) - .spaceY(selections.plot.rect.h) - .xKey('ykey') - .yKey('xkey') - .vKey('padj') - // .minObjectSize(25) - // .maxObjectSize(100) - // .domainPadding(0.01) - .overflowQ(overflowQ) - .data(data) - - chart.tooltip() - .header(function(k, cD){return cD.experimentId}) - .keys(['padj', 'log2FoldChange', 'xkey', 'ykey']) - - chart.colorFunction().colorBy('value') - .valueExtractor(function(k, v, i){ return data[k].padj }) - - chart() - - legend - .min(d3.min(chart.vValues())) - .max(d3.max(chart.vValues())) - .colorFunction(chart.colorFunction()) - .roundTo(5) - legend() - - var xk = chart.xValues(), yk = chart.yValues() - - // console.log(xk, yk) - yAxis - .spaceX(selections.yAxis.rect.w) - .spaceY(selections.yAxis.rect.h) - .orient("left") - .overflowQ(overflowQ) - .categoricalQ(true) - .tickLabels(yk) - .namespace(d3sm.hypenate(namespace,'axis-left')) - .minObjectSize(chart.yMinObjectSize()) - .maxObjectSize(chart.yMaxObjectSize()) - .objectSpacer(chart.objectSpacer()) - .spacerSize(0) - .objectSize(chart.ySize() + chart.ySpacerSize()) - .guideLinesQ(false) - .guidelineSpace(selections.xAxis.rect.w) - - yAxis() - - xAxis = d3sm.axis(xAxisSelection) - .spaceX(selections.xAxis.rect.w) - .spaceY(selections.xAxis.rect.h) - .orient("bottom") - .overflowQ(overflowQ) - .categoricalQ(true) - .tickLabels(xk) - .namespace(d3sm.hypenate(namespace,'axis-bottom')) - .minObjectSize(chart.xMinObjectSize()) - .maxObjectSize(chart.xMaxObjectSize()) - .objectSpacer(chart.objectSpacer()) - .spacerSize(0) - .objectSize(chart.xSize() + chart.xSpacerSize()) - .guideLinesQ(false) - .guidelineSpace(selections.yAxis.rect.h) - xAxis() - - var zoom = d3sm.plotZoom(chart, xAxis, yAxis).eventType('drag') - .orient("vertical") - - var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){zoom.eventType('drag')()}) - chartSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) - - } - - -return plot -} diff --git a/demos/index.html b/demos/index.html index cbe8679..92a645f 100644 --- a/demos/index.html +++ b/demos/index.html @@ -8,43 +8,32 @@ + + + + + + + diff --git a/demos/scatter/index.html b/demos/scatter/index.html deleted file mode 100644 index 539f489..0000000 --- a/demos/scatter/index.html +++ /dev/null @@ -1,141 +0,0 @@ - - - - - Scatter Plot - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
-
-
- - -
-
Rendered with v0.0.3
- - -
- - - - - - - - - diff --git a/demos/scatter/js/v001.js b/demos/scatter/js/v001.js deleted file mode 100644 index 6d0bea9..0000000 --- a/demos/scatter/js/v001.js +++ /dev/null @@ -1,103 +0,0 @@ -//VERSION: 0.0.1 -function scatterPlot( selection ) { - var data, - namespace - plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; - plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; - - - function plot() { - var - databar = d3sm.safeSelect(selection, 'form', d3sm.hypenate(namespace,'databar')), - - selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:500}, undefined, {axes:{x:0.1, y:0.1},space:{w:1,h:1}}) - container = selections.svg.selection, - chart = selections.plot.selection, - xAxis = selections.xAxis.selection, - yAxis = selections.yAxis.selection - - leg = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace,'legend')) - .attr('transform','translate(10, 24)') - - var dataSelect = d3sm.datatoggle(databar) - .keys(d3.keys(data.groupings)) - .namespace(namespace) - .updateFunction(function(){ plot(); zoom.reset(); }) - () - - var currentKey = dataSelect.currentKey() - - var currentData = d3.keys(data.data).reduce( (acc, k) => { - if (data.groupings[currentKey].includes(k)) { acc[k] = data.data[k] } - return acc - }, {}) - - - var scale = currentKey == "linearScale" ? d3.scaleLinear() : d3.scaleSqrt() - var ticks = currentKey == "linearScale" ? 5 : 10 - - var s = d3sm.scatter(chart) - .data(currentData) - .spaceX(selections.plot.rect.w) - .spaceY(selections.plot.rect.h) - .domainPaddingX(0.1) - .domainPaddingY(0.1) - .valueExtractorR(function(d, i){return currentData[d]['r']}) - .maxRadius(20) - .minRadius(1) - .scaleY(scale) - .scaleR(d3.scalePow()) - - var rs = d3.keys(currentData).map(function(d, i){return currentData[d].r}) - - s.colorFunction().colorBy('value').valueExtractor(function(k, v, i){ return v.r }) - .dataExtent( [d3.min(rs), d3.max(rs)]) - - s() - - var xV = s.valuesX(), yV = s.valuesY() - - var yA = d3sm.axis(yAxis) - .spaceX(selections.yAxis.rect.w) - .spaceY(selections.yAxis.rect.h) - .orient("left") - .overflowQ(false) - .tickValues(yV) - .guideLinesQ(true) - .guidelineSpace(selections.xAxis.rect.w) - .namespace('axis-left') - .numberOfTicks(ticks) - .domainPadding(s.domainPaddingY()) - .scale(scale) - yA() - - var xA = d3sm.axis(xAxis) - .spaceX(selections.xAxis.rect.w) - .spaceY(selections.xAxis.rect.h) - .orient("bottom") - .overflowQ(false) - .tickValues(xV) - .guideLinesQ(true) - .guidelineSpace(selections.yAxis.rect.h) - .namespace('axis-bottom') - .numberOfTicks(ticks) - .domainPadding(s.domainPaddingX()) - - xA() - - // set-up zoom, whell and drag - var zoom = d3sm.plotZoom(s, xA, yA).eventType('drag').orient('2D') - var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) - container.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) - - l = d3sm.numericLegend(leg) - .min(d3.min(s.valuesR())) - .max(d3.max(s.valuesR())) - .spaceX(25) - .spaceY(300) - - l() - - } - return plot -} diff --git a/demos/scatter/js/v002.js b/demos/scatter/js/v002.js deleted file mode 100644 index eb27c94..0000000 --- a/demos/scatter/js/v002.js +++ /dev/null @@ -1,117 +0,0 @@ -//VERSION: 0.0.2 -function scatterPlot( selection ) { - var data, - namespace - plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; - plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; - - - function plot() { - var - databar = d3sm.safeSelect(selection, 'form', d3sm.hypenate(namespace,'databar')), - - selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:500}, undefined, {axes:{x:0.1, y:0.1},space:{w:1,h:1}}) - container = selections.svg.selection, - chart = selections.plot.selection, - xAxis = selections.xAxis.selection, - yAxis = selections.yAxis.selection - - leg = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace,'legend')) - .attr('transform','translate(10, 24)') - - var dataSelect = d3sm.datatoggle(databar) - .keys(d3.keys(data.groupings)) - .namespace(namespace) - .updateFunction(function(){ plot(); zoom.reset(); }) - () - - var currentKey = dataSelect.currentKey() - - var currentData = d3.keys(data.data).reduce( (acc, k) => { - if (data.groupings[currentKey].includes(k)) { acc[k] = data.data[k] } - return acc - }, {}) - - - var scale = currentKey == "linearScale" ? d3.scaleLinear() : d3.scaleSqrt() - var ticks = currentKey == "linearScale" ? 5 : 10 - - var s = d3sm.scatter(chart) - .data(currentData) - .spaceX(selections.plot.rect.w) - .spaceY(selections.plot.rect.h) - .domainPaddingX(0.1) - .domainPaddingY(0.1) - .valueExtractorR(function(d, i){return currentData[d]['r']}) - .maxRadius(20) - .minRadius(1) - .scaleY(scale) - .scaleR(d3.scalePow()) - - var rs = d3.keys(currentData).map(function(d, i){return currentData[d].r}) - - s.colorFunction().colorBy('value').valueExtractor(function(k, v, i){ return v.r }) - .dataExtent( [d3.min(rs), d3.max(rs)]) - - s() - - var xV = s.valuesX(), yV = s.valuesY() - - var yA = d3sm.axis(yAxis) - .spaceX(selections.yAxis.rect.w) - .spaceY(selections.yAxis.rect.h) - .orient("left") - .overflowQ(false) - .tickValues(yV) - .guideLinesQ(true) - .guidelineSpace(selections.xAxis.rect.w) - .namespace('axis-left') - .numberOfTicks(ticks) - .domainPadding(s.domainPaddingY()) - .scale(scale) - yA() - - var xA = d3sm.axis(xAxis) - .spaceX(selections.xAxis.rect.w) - .spaceY(selections.xAxis.rect.h) - .orient("bottom") - .overflowQ(false) - .tickValues(xV) - .guideLinesQ(true) - .guidelineSpace(selections.yAxis.rect.h) - .namespace('axis-bottom') - .numberOfTicks(ticks) - .domainPadding(s.domainPaddingX()) - - xA() - // - // - // set-up zoom, whell and drag - var zoom = d3sm.plotZoom(s, xA, yA).eventType('drag').orient('2D') - var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) - container.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) - - l = d3sm.numericLegend(leg) - .min(d3.min(s.valuesR())) - .max(d3.max(s.valuesR())) - .spaceX(25) - .spaceY(300) - - l() - - // l = d3sm.categoricLegend(leg) - // .categories(['a','b','c']) - // .spaceX(100) - // .spaceY(300) - // .overflowQ(false) - // .orient('vertical') - // - // l() - - } - - - - - return plot -} diff --git a/demos/scatter/js/v003.js b/demos/scatter/js/v003.js deleted file mode 100644 index b111659..0000000 --- a/demos/scatter/js/v003.js +++ /dev/null @@ -1,187 +0,0 @@ -//VERSION: 0.0.3 -function scatterPlot( selection ) { - var - data, - namespace, - chart, - xAxis, - yAxis, - dataSelect, - legend, - lasso, - lassoWidget - - plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; - plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; - plot.chart = function(_){return arguments.length ? (chart = _, plot) : chart; }; - plot.lasso = function(_){return arguments.length ? (lasso = _, plot) : lasso; }; - - function plot() { - var objectClass= 'item' - - var - lassoDiv = d3sm.safeSelect(selection, 'div', d3sm.hypenate(namespace,'lassoWidget')), - databar = d3sm.safeSelect(selection, 'form', d3sm.hypenate(namespace,'databar')), - selections = d3sm.setupStandardChartContainers( - selection, - namespace, - {w:window.innerWidth, h:window.innerHeight}, - {top:0.01, bottom:0.01, left:0.01, right:0.01}, - {w: 0.8, h:0.8}, // percent of container space for svg - {y:0.1,x:0.1}, // percent of container space for axes, - {x:25, margin:05} // absolute width of legendSelect and space on either size - ), - - // selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:500}, undefined, {axes:{x:0.1, y:0.1},space:{w:1,h:1}}) - container = selections.svg.selection, - chartSelection = selections.plot.selection, - xAxisSelection = selections.xAxis.selection, - yAxisSelection = selections.yAxis.selection, - legendSelect = selections.legend.selection - - - var - xAxisToggle = { 'linear': d3.scaleLinear(), 'sqrt': d3.scaleSqrt() }, - yAxisToggle = { 'linear': d3.scaleLinear(), 'sqrt': d3.scaleSqrt() }, - rAxisToggle = { 'linear': d3.scaleLinear(), 'sqrt': d3.scaleSqrt(), 'pow': d3.scalePow() } - - - if (dataSelect == undefined) { dataSelect = d3sm.datatoggle(databar) - .data({ - 'dataset': data.groupings, - 'x-axis': xAxisToggle, - 'y-axis': yAxisToggle, - 'radius scale': rAxisToggle - }) - .namespace(namespace) - .updateFunction(function(){ plot(); zoom.reset(); }) - dataSelect() - } - - if (chart == undefined) { chart = d3sm.scatter(chartSelection) } - if (yAxis == undefined) { yAxis = d3sm.axis(yAxisSelection) } - if (xAxis == undefined) { xAxis = d3sm.axis(xAxisSelection) } - if (legend == undefined) {legend = d3sm.numericLegend(legendSelect) } - - - var currentKeys = dataSelect.currentKeys() - var xScale = xAxisToggle[currentKeys['x-axis']] - var yScale = yAxisToggle[currentKeys['y-axis']] - var rScale = rAxisToggle[currentKeys['radius scale']] - var currentDataKey = currentKeys['dataset'] - - - var currentKey = dataSelect.currentKeys() - var currentData = d3.keys(data.data).reduce( (acc, k) => { - if (data.groupings[currentDataKey].includes(k)) { acc[k] = data.data[k] } - return acc - }, {}) - - - chart - .data(currentData) - .spaceX(selections.plot.rect.w) - .spaceY(selections.plot.rect.h) - .domainPaddingX(0.1) - .domainPaddingY(0.1) - .valueExtractorR(function(d, i){return currentData[d]['r']}) - .maxRadius(20) - .minRadius(1) - .scaleY(yScale) - .scaleX(xScale) - .scaleR(rScale) - .namespace(namespace) - .objectClass(objectClass) - - var rs = d3.keys(currentData).map(function(d, i){return currentData[d].r}) - - chart.colorFunction().colorBy('value').valueExtractor(function(k, v, i){ return v.r }) - .dataExtent( [d3.min(rs), d3.max(rs)]) - chart() - - var xV = chart.valuesX(), yV = chart.valuesY() - - yAxis - .spaceX(selections.yAxis.rect.w) - .spaceY(selections.yAxis.rect.h) - .orient("left") - .overflowQ(false) - .tickValues(yV) - .guideLinesQ(true) - .guidelineSpace(selections.xAxis.rect.w) - .namespace('axis-left') - .numberOfTicks(5) - .domainPadding(chart.domainPaddingY()) - .scale(yScale) - yAxis() - - xAxis = d3sm.axis(xAxisSelection) - .spaceX(selections.xAxis.rect.w) - .spaceY(selections.xAxis.rect.h) - .orient("bottom") - .overflowQ(false) - .tickValues(xV) - .guideLinesQ(true) - .guidelineSpace(selections.yAxis.rect.h) - .namespace('axis-bottom') - .numberOfTicks(5) - .domainPadding(chart.domainPaddingX()) - .scale(xScale) - - xAxis() - - - legend - .min(d3.min(chart.valuesR())) - .max(d3.max(chart.valuesR())) - .spaceX(selections.legend.rect.w) - .spaceY(selections.legend.rect.h) - - legend() - - - - // set-up zoom, whell and drag - var zoom = d3sm.plotZoom(chart, xAxis, yAxis).eventType('drag').orient('2D') - var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) - chartSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) - - - // var svg = selection.select('svg') - // var objs = svg.select('g.'+namespace+'-object-container') - // var objC = '.chart-point' - - // if (lasso == undefined) { lasso = d3sm.lasso( ) - // .svg(svg) - // .objectClass(objC) - // .objectContainer(objs) - // .namespace('test') - // .chartContainer(chartSelection) - // } - // - // lasso - // .xScale(chart.scaleX()) - // .yScale(chart.scaleY()) - // - // lasso() - - - if (lassoWidget == undefined) {lassoWidget = d3sm.lassoWidget(lassoDiv) - .svg(selections.svg.selection) - .objectClass("."+objectClass) - .chartContainer(chartSelection) - .objectContainer(chartSelection.select('g.'+namespace+'-object-container')) - .maxNumberOfGroups(2) - .dataExtractor(function(nodeData){ - return {"name": nodeData} - }) - lassoWidget() - } - - } - - - - - return plot -} diff --git a/demos/tooltip-design/index.html b/demos/tooltip-design/index.html deleted file mode 100644 index 1449187..0000000 --- a/demos/tooltip-design/index.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- Tooltip Title -
- - - - - - - - - - - - - - - - - - - -
NameTooltip
Number of Keys4
Color of valuesCoral
Thiskey has a long value
-
-
- -
- - - - diff --git a/demos/upset/index.html b/demos/upset/index.html deleted file mode 100644 index a9d1c80..0000000 --- a/demos/upset/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - UpSet Plot - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
-
-
- - -
-
Rendered with v0.0.3
- -
-
- -
-
-
-
- - - - - - - - - - diff --git a/demos/upset/js/v003.js b/demos/upset/js/v003.js deleted file mode 100644 index f08937c..0000000 --- a/demos/upset/js/v003.js +++ /dev/null @@ -1,300 +0,0 @@ -function upset ( selection ) { - var - data, - namespace='upset' - - - var - upset, - upsetAxis, - setTotalsBarChart, - setTotalsAxis, - intersectionTotalsBarChart, - intersectionTotalsAxis, - intersectionSetsAxis - - plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; - plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; - plot.upset = function(_){return arguments.length ? (upset = _, plot) : upset; }; - plot.upsetAxis = function(_){return arguments.length ? (upsetAxis = _, plot) : upsetAxis; }; - plot.setTotalsBarChart = function(_){return arguments.length ? (setTotalsBarChart = _, plot) : setTotalsBarChart; }; - plot.setTotalsAxis = function(_){return arguments.length ? (setTotalsAxis = _, plot) : setTotalsAxis; }; - plot.intersectionTotalsBarChart = function(_){return arguments.length ? (intersectionTotalsBarChart = _, plot) : intersectionTotalsBarChart; }; - plot.intersectionTotalsAxis = function(_){return arguments.length ? (intersectionTotalsAxis = _, plot) : intersectionTotalsAxis; }; - - - - function plot() { - var - svg = d3sm.safeSelect(selection, 'svg'), - - upsetAxisSelection = d3sm.safeSelect(svg, 'g', namespace+'-upset-chart-axis'), - setTotalsAxisSelection = d3sm.safeSelect(svg, 'g', namespace+'-set-bar-chart-axis'), - intersectionTotalsAxisSelection = d3sm.safeSelect(svg, 'g', namespace+'-intersection-bar-chart-axis'), - intersectionSetsAxisSelection = d3sm.safeSelect(svg, 'g', namespace+'-intersection-bar-set-axis'), - - upsetSelection = d3sm.safeSelect(svg, 'g', namespace+'-upset-chart'), - setTotalsBarChartSelection = d3sm.safeSelect(svg, 'g', namespace+'-set-bar-chart'), - intersectionTotalsBarChartSelection = d3sm.safeSelect(svg, 'g', namespace+'-intersection-bar-chart') - - - - if (upset == undefined) { upset = d3sm.upset(upsetSelection) } - if (upsetAxis == undefined) { upsetAxis = d3sm.axis(upsetAxisSelection) } - if (setTotalsBarChart == undefined) { setTotalsBarChart = d3sm.bar(setTotalsBarChartSelection) } - if (setTotalsAxis == undefined) { setTotalsAxis = d3sm.axis(setTotalsAxisSelection) } - if (intersectionTotalsBarChart == undefined) { intersectionTotalsBarChart = d3sm.bar(intersectionTotalsBarChartSelection) } - if (intersectionTotalsAxis == undefined) { intersectionTotalsAxis = d3sm.axis(intersectionTotalsAxisSelection) } - if (intersectionSetsAxis == undefined) { intersectionSetsAxis = d3sm.axis(intersectionSetsAxisSelection) } - - - - var - setChartSpacer = 10, - intersectionChartSpacer = 10, - - percentages = { - inChart: {w: 0.75, h: 0.5 }, - usChart: {w: 0.75, h: 0.2 }, - stChart: {w: 0.10, h: 0.2 }, - - isAxis : {w: 0.75, h: 0.2 }, - inAxis : {w: 0.15, h: 0.5 }, - usAxis : {w: 0.15, h: 0.2 }, - stAxis : {w: 0.10, h: 0.1 }, - - svg: {w: 1, h: 0.7} - } - - var - page = {w: selection.node().getBoundingClientRect().width, h: window.innerHeight}, - margins = {left: page.w *0.01, right: page.w *0.01, top: page.h*0.01, bottom: page.h*0.01}, - svgRect = { - _w: page.w * percentages.svg.w, - _h: page.h * percentages.svg.h, - w: page.w * percentages.svg.w - margins.left - margins.right, - h: page.h * percentages.svg.h - margins.top - margins.bottom - }, - chartRect = { - w: svgRect.w - setChartSpacer*2, - h: svgRect.h - intersectionChartSpacer*2 - }, - upsetRect = { - x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer*2, - y: margins.top + chartRect.h * percentages.inChart.h + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer, - w: chartRect.w * percentages.usChart.w, - h: chartRect.h * percentages.usChart.h - }, - upsetAxisRect = { - x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer, - y: margins.top + chartRect.h * percentages.inChart.h + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer, - w: chartRect.w * percentages.usAxis.w, - h: chartRect.h * percentages.usAxis.h - }, - setTotalsRect = { - x: margins.left, - y: margins.top + chartRect.h * percentages.inChart.h + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer, - w: chartRect.w * percentages.stChart.w, - h: chartRect.h * percentages.stChart.h - }, - setTotalsAxisRect = { - x: margins.left, - y: margins.top + (chartRect.h * percentages.inChart.h) + (chartRect.h * percentages.stChart.h) + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer*2, - w: chartRect.w * percentages.stAxis.w, - h: chartRect.h * percentages.stAxis.h - }, - intersectionTotalsRect = { - x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer*2, - y: margins.top + (chartRect.h * percentages.isAxis.h), - w: chartRect.w * percentages.inChart.w, - h: chartRect.h * percentages.inChart.h - }, - intersectionTotalsAxisRect = { - x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer, - y: margins.top + (chartRect.h * percentages.isAxis.h), - w: chartRect.w * percentages.inAxis.w, - h: chartRect.h * percentages.inAxis.h - }, - intersectionSetsAxisRect = { - x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer + intersectionChartSpacer, - y: margins.top + (chartRect.h * percentages.isAxis.h), - w: chartRect.w * percentages.isAxis.w, - h: chartRect.h * percentages.isAxis.h - } - - - svg.attr('width', svgRect._w).attr('height', svgRect._h) - upsetAxisSelection.attr('transform', 'translate('+upsetAxisRect.x+','+upsetAxisRect.y+')') - setTotalsAxisSelection.attr('transform', 'translate('+setTotalsAxisRect.x+','+setTotalsAxisRect.y+')') - intersectionTotalsAxisSelection.attr('transform', 'translate('+intersectionTotalsAxisRect.x+','+intersectionTotalsAxisRect.y+')') - intersectionSetsAxisSelection.attr('transform', 'translate('+intersectionSetsAxisRect.x+','+intersectionSetsAxisRect.y+')') - - upsetSelection.attr('transform', 'translate('+upsetRect.x+','+upsetRect.y+')') - setTotalsBarChartSelection.attr('transform', 'translate('+setTotalsRect.x+','+setTotalsRect.y+')') - intersectionTotalsBarChartSelection.attr('transform', 'translate('+intersectionTotalsRect.x+','+intersectionTotalsRect.y+')') - - var overflowQ = true - - upset - .data(data) - .spaceX(upsetRect.w) - .spaceY(upsetRect.h) - .xObjectSpacer(0.01) - .yObjectSpacer(0.01) - .maxObjectSize(5) - .minObjectSize(20) - .overflowQ(overflowQ) - .intersectionExtractor(function(k, i){ return data[k].intersection.join(';') }) - .intersectionKeySortingFunction(function(a, b){ - return ( - data[a].intersection.length - data[b].intersection.length - || - upset.intersectionValues().indexOf(data[a].intersection.join(';')) - -upset.intersectionValues().indexOf(data[b].intersection.join(';')) - ) - }) - // .radius(1) - upset() - - upsetAxis - .spaceX(upsetAxisRect.w) - .spaceY(upsetAxisRect.h) - .orient('left') - .categoricalQ(true) - .tickLabels(upset.setValues()) - .overflowQ(overflowQ) - .minObjectSize(upset.minObjectSize()) - .maxObjectSize(upset.maxObjectSize()) - .objectSpacer(upset.yObjectSpacer()) - .tickLabelMargin(30) - - upsetAxis() - - var intersectionTotals = upset.intersectionTotals() - var setTotals = upset.setTotals() - - - intersectionSetsAxis - .tickLabels(upset.intersectionValues()) - .categoricalQ(true) - .spaceX(intersectionSetsAxisRect.w) - .spaceY(intersectionSetsAxisRect.h) - .orient('top') - .namespace('intersection-sets-name') - .overflowQ(overflowQ) - .minObjectSize(upset.minObjectSize()) - .maxObjectSize(upset.maxObjectSize()) - .objectSpacer(upset.xObjectSpacer()) - .objectSize(upset.xObjectSize()) - .spacerSize(upset.xSpacerSize()) - // .tickLabelRotation(-45) - .tickTickLabelSpacer(0) - - intersectionSetsAxis() - - intersectionTotalsBarChart.data(intersectionTotals) - .grouping(upset.intersectionValues()) - .spaceX(intersectionTotalsRect.w) - .spaceY(intersectionTotalsRect.h) - .namespace('intersections') - .orient('horizontal') - .valueExtractor(function(k, i){return intersectionTotals[k]['total']}) - .overflowQ(overflowQ) - .minObjectSize(upset.minObjectSize()) - .maxObjectSize(upset.maxObjectSize()) - .objectSpacer(upset.xObjectSpacer()) - .objectSize(upset.xObjectSize()) - .spacerSize(upset.xSpacerSize()) - - - intersectionTotalsBarChart.tooltip().keys(['Amount','Values']) - .values([ - function(currentData, d){ - return currentData.total - }, - function(currentData, d){ - var l = currentData.values.length - return currentData.values.join(', ').slice(0,100)+ ((l > 100) ? '...': '') - } - ]) - - // intersectionTotalsBarChart.colorFunction().colors(['cyan', 'blue']).colorBy('value') - - // intersectionSetsAxis() - intersectionTotalsBarChart() - - intersectionTotalsAxis - .spaceX(intersectionTotalsAxisRect.w) - .spaceY(intersectionTotalsAxisRect.h) - .orient('left') - .namespace('intersections-axis-left') - .tickValues(intersectionTotalsBarChart.barValues()) - .guideLinesQ(true) - .guidelineSpace(intersectionTotalsRect.w + intersectionChartSpacer) - .overflowQ(overflowQ) - - intersectionTotalsAxis() - - - - setTotalsBarChart.data(setTotals) - .grouping(upset.setValues()) - .spaceX(setTotalsRect.w) - .spaceY(setTotalsRect.h) - .orient('right') - .valueExtractor(function(k, i){return setTotals[k]['total']}) - .namespace('set') - .overflowQ(overflowQ) - .minObjectSize(upset.minObjectSize()) - .maxObjectSize(upset.maxObjectSize()) - .objectSpacer(upset.yObjectSpacer()) - .objectSize(upset.yObjectSize()) - .spacerSize(upset.ySpacerSize()) - .barPercent(0.75) - - setTotalsBarChart() - - - - setTotalsAxis - .spaceX(setTotalsAxisRect.w) - .spaceY(setTotalsAxisRect.h) - .namespace('set-axis-bottom') - .orient('bottom') - .tickValues(setTotalsBarChart.barValues()) - .reverseScaleQ(true) - // .tickLabelRotation(0) - .overflowQ(overflowQ) - - setTotalsAxis() - - - - - - - var zoom = d3sm.multiPlotZoom(upset).eventType('drag').orient('2D') - .xComponents([ - intersectionTotalsBarChart, - intersectionSetsAxis - ]) - .yComponents([ - upsetAxis, - setTotalsBarChart - ]) - var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) - upsetSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) - intersectionTotalsBarChartSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) - - - - intersectionTotalsBarChartSelection.selectAll('.bar.intersections > rect.bar-rect').on('click', function(d, i){ - console.log( d, i) - }) - // selection.selectAll('text').attr('pointer-events', 'none') - // .attr('pointer-events', 'none') - - } - - return plot -} diff --git a/demos/vertical-violins/index.html b/demos/vertical-violins/index.html deleted file mode 100644 index 4020970..0000000 --- a/demos/vertical-violins/index.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - Basic Violins - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dist/d3sm.esm.js b/dist/d3sm.esm.js index b944a52..b9ee62a 100644 --- a/dist/d3sm.esm.js +++ b/dist/d3sm.esm.js @@ -1,11 +1,3 @@ -// import {hasQ} from './array-functions'; -/******************************************************************************* -** ** -** ** -** HELPERS ** -** ** -** ** -*******************************************************************************/ /** * Helper function for Array.filter to get unique elements of the array * @param {*} value current value as mapping over array (self) @@ -16,270 +8,175 @@ function uniqueElements(value, index, self) { return self.indexOf(value) === index; } /** -* Extracts x and y of translate from transform property -* @param {string} transform transform property of svg element -* @returns {number[]} x, y of translate(x, y) +* This function tests to see if all elements of the passed array are true. +* @param {Array} array of values +* @param {Function} [func function(value){return value == true;}] is applied to each value of the array and should return a boolean. +* @returns {boolean} if all values are true by function */ -function getTranslation(transform) { - // Create a dummy g for calculation purposes only. This will never - // be appended to the DOM and will be discarded once this function - // returns. - var g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - // Set the transform attribute to the provided string value. - transform = transform == undefined ? 'translate(0,0)' : transform; - g.setAttributeNS(null, 'transform', transform); - // consolidate the SVGTransformList containing all transformations - // to a single SVGTransform of type SVG_TRANSFORM_MATRIX and get - // its SVGMatrix. - var matrix = g.transform.baseVal.consolidate().matrix; - // As per definition values e and f are the ones for the translation. - return [matrix.e, matrix.f]; +function all( array, func ) { + if (func == undefined) { return array.every( function(value) { return value === true; }); } + return array.every( function(value) { return func(value); } ); } - /** -* Modifies luminance of hexidecimal number -* @param {string} hex should be hexidecimal value with or without the proceeding octotrope -* @param {number} lum value to increase or decrease luminosity by -* @returns {string} updated hexidecimal value without the proceeding octotrope +* Counts the number of occurances of each element in the given array. +* @param {Array} array of elements +* @returns {Object} of key: value pairs where key is an element in the array and value is the number of times it occurs. */ -function modifyHexidecimalColorLuminance(hex, lum) { - // validate hex string - var hex = String(hex).replace(/[^0-9a-f]/gi, ''); - - if (hex.length < 6) { - hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; - } - lum = lum || 0; - - // convert to decimal and change luminosity - var rgb = '#', c, i; - for (i = 0; i < 3; i++) { - c = parseInt(hex.substr(i*2,2), 16); - c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); - rgb += ('00'+c).substr(c.length); - } - - return rgb; +function tally( array ) { + var tallies = {}; + array.map( function ( element ) { + if ( hasQ(Object.keys(tallies), element) ) { tallies[element] = 1; } + else { tallies[element] += 1; } + }); + return tallies; } - - /** -* Calculated the quartiles of the passed data and stores them with qKeys -* @param {number[]} data list of numerical values -* @param {string[]} [qKeys=['q0', 'q1', 'q2', 'q3', 'q4']] how returned object with quartiles should be stored -* @returns {Object} with keys qKeys giving only the numerical values for the quartiles +* Short-hand for array.includes(item); +* @param {Array} array +* @param {*} item to test if contained in {array} +* @returns {boolean} */ -function quartiles(data, qKeys) { - var - q2 = d3.median(data), - lower = data.filter(x => x < q2), - upper = data.filter(x => x > q2), - - q1 = d3.median(lower), - q1 = q1 == undefined ? q2 : q1, - - q0 = d3.min(lower), - q0 = q0 == undefined ? q1 : q0, - - q3 = d3.median(upper), - q3 = q3 == undefined ? q2 : q3, - - q4 = d3.max(upper), - q4 = q4 == undefined ? q3 : q4, - - k0 = 'q0', k1 = 'q1', k2 = 'q2', k3 = 'q3', k4 = 'q4', - obj = {}; - if (qKeys!=undefined && qKeys.length == 5) { k0 = qKeys[0]; k1 = qKeys[1]; k2 = qKeys[2]; k3 = qKeys[3]; k4 = qKeys[4]; } - obj[k0] = q0; obj[k1] = q1; obj[k2] = q2; obj[k3] = q3; obj[k4] = q4; - - return obj -} - +function hasQ( array, item ) { return array.includes(item); } /** -* Helper function to get all values needed in making violin plots -* @param {string[]} violinKeys -* @param {number[]} data -* @param {Function} valueExtractorFunction how to get values from data[violinKeys[i]] -* @param {boolean} horizontalQ whether or not violins will be rendered horizontally or vertically -* @param {string} qKey how the object containing the quartiles should be labeled as -* @param {string[]} qKeys how each quartile should be labeled as -* @returns {Object} required for @see{@link violin} containing keys values, binnned, frequencies, points, and quartiles -* @see{@link quartiles} +* Returns first item in array +* @param {Array} array of items +* @returns {*} array[0] */ -function extractViolinValues( - violinKeys, - data, - valueExtractorFunction, - horizontalQ, - qKey, - qKeys -){ - var obj = {}; - violinKeys.map(function(k, i){ - var d = valueExtractorFunction(k, i, data), - binned = d3.histogram()(d), - frequencies = binned.map(x=>x.length), - minPoint = horizontalQ ? {y: d3.min(d), x: 0} : {x: d3.min(d), y: 0}, - maxPoint = horizontalQ ? {y: d3.max(d), x: 0} : {x: d3.max(d), y: 0}, - points = binned.map(function(bin, i) { - return horizontalQ - ? {y: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), x: frequencies[i]} - : {x: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), y: frequencies[i]} - }), - quarts = quartiles(d, qKeys), - o = { - values: d, - binned: binned, - frequencies: frequencies, - points: [minPoint].concat(points).concat([maxPoint]) - }; - o[qKey] = quarts; - obj[k] = o; - }); - return obj; -} +function first( array ) { return array[0]; } /** -* Hypenates all strings together -* @param {string[]} arguments -* @returns {string} "arg1-arg2-...-argn" +* Returns last item in array +* @param {Array} array of items +* @returns {*} array[array.length-1] */ -function hypenate(){ return Array.prototype.slice.call(arguments).join('-') } - +function last( array ) { return array[array.length-1]; } /** -* Rounds decimals of number to precision -* @param {number} number -* @param {number} precision -* @returns {number} rounded to precision +* Calculates the total value of numbers in passed array +* @param {number[]} array of numerical values +* @returns {number} sum over elements in array */ -function round(number, precision) { - var shift = function (number, precision, reverseShift) { - if (reverseShift) { - precision = -precision; - } - var numArray = ('' + number).split('e'); - return +(numArray[0] + 'e' + (numArray[1] ? (+numArray[1] + precision) : precision)); - }; - return shift(Math.round(shift(number, precision, false)), precision, true); -} - +function total$1( array ) { return array.reduce((a, b) => a + b, 0) } /** -* recursively ascends element.parentElement to find a svg tag -* @param {Element} element -* @returns {Element | undefined} +* Removes duplicates in array +* @param {Array} array of items +* @returns {Array} of items such that item_i != item_j for all i < j +* @see{@link uniqueElements} for the filtering function */ -function getContainingSVG(element) { - var parent = element.parentElement; - var tag = parent.tagName.toLowerCase(); - if (tag === 'svg') { return parent; } - if (tag === 'html') { return undefined; } - return getContainingSVG(parent); -} +function unique( array ) { return array.filter( uniqueElements ); } /** -* Maps arguments in to d3.interpolateRgbBasis -* @param arguments -* @returns {Function} +* Filters passed array for specified indicies +* @param {Array} array of items +* @param {number[]} positions of integers such that i < array.length +* @returns {Array} of items such that for any item_i, positions.includes(i) === true */ -function interpolateColors(){return d3.interpolateRgbBasis(arguments)} - +function get( array, positions ) { + return array.filter( function( value, index ) { return hasQ(positions, index); } ); +} /** -* Trys to reduce text to fit in specified area, made for tick labels as called by -* @see{@link axis} -* @param {d3.selection} t container for specific axis tick -* @param {string} text to be the label of the passed axis tick -* @param {boolean} orient of the axis, true is horizontal, false is vertical -* @param {number} tickLength is the length of the text -* @param {number} space is the amount of availble space for the text and the tick to fit in -* @param {boolean} overflowQ whether or not allowed to go over the alloted space -* @returns {none} +* Determines if all elements in passed array are arrays themselves. +* @param {Array} array of items +* @returns {boolean} true if Array.isArray(e) is true for all e in array +* @see{@link all} */ -function truncateText(t, text, orient, tickLength, space, overflowQ) { - var rect = t.node().getBoundingClientRect(); - t.text(text); - while (Math.max(rect.width, rect.height) > space - tickLength) { - text = String(text); - text = text.slice(0, text.length - 1); - t.text(text + '...'); - rect = t.node().getBoundingClientRect(); - if (text.length == 0) break - } -} - -function truncateString(string, space, font) { - var chars = space / font; - if (chars < string.length) { - return string.slice(0, Math.round(chars-5)) + '...' - } else { - return string - } +function listOfListsQ( array ) { + return all( array.map( function( element, index ) { return Array.isArray(element) } ) ) } - /** -* Trys to use d3.selection to get element, if it doesnt exist, makes one -* @param {d3.selection} sel selection in which to try and find object -* @param {string} tag tag of which to try and select -* @param {string} [cls=''] class of tag to try and grab -* @returns {d3.selection} of either append or selected tag.cls within sel +* Built on top of @see{@link get}, mapping if positions is a list of lists (@see{@link listOfListsQ}) +* @param {Array} array of items +* @param {number[] | []number[] } positions of integers or list of positions of integers +* @returns {boolean} returns specified positions from array. If nested positions passed, returns requested items in same structure. */ -function safeSelect(sel, tag, cls) { - var clsStr = cls == undefined ? '' : '.'+cls; - var sSel = sel.select(tag+clsStr).empty() - ? sel.append(tag) - : sel.select(tag+clsStr); - return sSel - .classed(clsStr.replace('.', ''), true) - .attr('transform', sSel.attr('transform') == undefined ? 'translate(0,0)' : sSel.attr('transform')) +function cut( array, positions ) { + if ( listOfListsQ(array) ) { return positions.map(function(pos, i) { return array.get(pos); }); } + return get( array, positions ); } /** -* evenly partitions the range [min, max] into n parts -* @param {number} min -* @param {number} max -* @param {number} n -* @returns {number[]} array of length n evenly partitioned between min and max +* Given an array of objects, constructs new objects where each value is a list +* based on the corresonding key, which is extracted by the parameter by +* @param {Objects[]} array of objects +* @param {string} by key within all objects of passed array +* @param {string[]} [groups] saves some computation if all known values extracted by mapping over the parameter by are passed +* @returns {Object} of key value pairs, where keys are all values of the key by from an object in the passed array and the value are those corresponding objects. */ -function tickRange(min, max, n) { - var a = [min]; - var d = max-min; - var s = d / (n-1); - for (var i = 0; i < n-2; i++) { a.push(min + s * (i+1)); } - a.push(max); - return a -} - +function groupBy (array, by, groups) { + if (groups == undefined) { + groups = unique(array.map(function(elements, index){ return element[by]; })); + groups.map(function(value, index){groupped[value] = [];}); + } -function euclideanDistance(p1, p2){ - var a = p1[0] - p2[0], b = p1[1] - p2[1]; - return Math.sqrt(a*a + b*b) + var groupped = {}; + array.map(function(element, index){groupped[element[by]].push(element);}); + return groupped } /** -* Short-hand for array.includes(item); +* Tests if two arrays are equivalent * @param {Array} array -* @param {*} item to test if contained in {array} -* @returns {boolean} +* @param {Array} other +* @returns {boolean} if every element of array matches that of other */ -function hasQ( array, item ) { return array.includes(item); } +function arrayEquals(array, other) { + if (!other) + return false; + // compare lengths - can save a lot of time + if (array.length != other.length) + return false; + + for (var i = 0, l=array.length; i < l; i++) { + // Check if we have nested arrays + if (array[i] instanceof Array && other[i] instanceof Array) { + // recurse into the nested arrays + if (!arrayEquals(array[i],other[i])) + return false; + } + else if (array[i] != other[i]) { + // Warning - two different object instances will never be equal: {x:20} != {x:20} + return false; + } + } + return true; +} + + /** -* Calculates the total value of numbers in passed array -* @param {number[]} array of numerical values -* @returns {number} sum over elements in array +* Recursively tallies the number of elements at each level of the passed, putatively nested array +* @param {Array} array of items which may include nested arrays +* @param {number} [level=0] current depth in the recursion +* @param {Array} [levelData=[]] keeps track of items seen so far at each depth +* @returns {Array} stating the number of elements (array inclusive) found at each level of the array */ -function total( array ) { return array.reduce((a, b) => a + b, 0) } +function elementsAtLevels(array, level, levelData) { + level = level == undefined ? 0 : level + 1; + levelData = levelData == undefined ? [] : levelData; + if ( level >= levelData.length ) { levelData.push(array.length);} else {levelData[level] += array.length; } + array.map(function(e, i) {if (Array.isArray(e)){ elementsAtLevels(e, level, levelData); }}); + return levelData +} + + /** -* Removes duplicates in array -* @param {Array} array of items -* @returns {Array} of items such that item_i != item_j for all i < j -* @see{@link uniqueElements} for the filtering function +* Recursively tallies the number of elements of the passed, putatively nested array +* @param {Array} array of items which may include nested arrays +* @param {number} [elements=0] current number of elements seen so far +* @returns {number} number of elements (array inclusive) found in passed array */ -function unique( array ) { return array.filter( uniqueElements ); } +function numberOfElements( array, elements ) { + elements = elements == undefined ? 0 : elements; + array.map(function(e, i) { + if ( Array.isArray(e) ) { elements = numberOfElements(e, elements); } + else { elements += 1; } + }); + return elements +} /** * Concats all nested arrays in passed array to form a single array @@ -308,258 +205,255 @@ function whichBin(bins, value) { return i } +var arr = /*#__PURE__*/Object.freeze({ + uniqueElements: uniqueElements, + all: all, + tally: tally, + hasQ: hasQ, + first: first, + last: last, + total: total$1, + unique: unique, + get: get, + listOfListsQ: listOfListsQ, + cut: cut, + groupBy: groupBy, + arrayEquals: arrayEquals, + elementsAtLevels: elementsAtLevels, + numberOfElements: numberOfElements, + flatten: flatten, + whichBin: whichBin +}); + /** - * calls console.group if d3sm.debugQ == true - * @param {string} name of the group - * @returns {undefined} - */ -function consoleGroup(name) { - if (window.d3sm.debugQ === true){ - console.group(name); - } +* Modifies luminance of hexidecimal number +* @param {string} hex should be hexidecimal value with or without the proceeding octotrope +* @param {number} lum value to increase or decrease luminosity by +* @returns {string} updated hexidecimal value without the proceeding octotrope +*/ +function modifyHexidecimalColorLuminance(hex, lum) { + // validate hex string + var hex = String(hex).replace(/[^0-9a-f]/gi, ''); + + if (hex.length < 6) { + hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; + } + lum = lum || 0; + + // convert to decimal and change luminosity + var rgb = '#', c, i; + for (i = 0; i < 3; i++) { + c = parseInt(hex.substr(i*2,2), 16); + c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); + rgb += ('00'+c).substr(c.length); + } + + return rgb; } /** - * calls console.groupEnd if d3sm.debugQ == true - * @returns {undefined} - */ -function consoleGroupEnd() { - if (window.d3sm.debugQ === true){ - console.groupEnd(); - } -} +* Maps arguments in to d3.interpolateRgbBasis +* @param arguments +* @returns {Function} +*/ +function interpolateColors(){return d3.interpolateRgbBasis(arguments)} + +var color = /*#__PURE__*/Object.freeze({ + modifyHexidecimalColorLuminance: modifyHexidecimalColorLuminance, + interpolateColors: interpolateColors +}); /** - * Calls console.log if d3sm.debugQ == true - * @param {string} func name of the function logging - * @param {string} msg to log - * @param {Object} data to be logged along side the message - * @returns {undefined} - */ -function log(func, msg, data) { - if (window.d3sm.debugQ === true){ - console.log( - `%c[d3sm::${func}]:\t${msg}`, - [ - 'background: #6cd1ef', - 'border-radius: 5000px', - 'padding: 0px 2px', - 'font-size: 14px' - ].join(';') - ); - console.table(data); - // console.trace() - } +* Rounds decimals of number to precision +* @param {number} number +* @param {number} precision +* @returns {number} rounded to precision +*/ +function round(number, precision) { + var shift = function (number, precision, reverseShift) { + if (reverseShift) { + precision = -precision; + } + var numArray = ('' + number).split('e'); + return +(numArray[0] + 'e' + (numArray[1] ? (+numArray[1] + precision) : precision)); + }; + return shift(Math.round(shift(number, precision, false)), precision, true); } /** - * Calls console.warn if d3sm.debugQ == true - * @param {string} func name of the function warning - * @param {string} msg to display - * @param {Object} data to be displayed along side the message - * @returns {undefined} - */ -function warn(func, msg, data) { - if (window.d3sm.debugQ === true) - console.warn( - `%c[d3sm::${func}]:\t${msg}`, - [ - 'background: #ffd53e', - 'border-radius: 5000px', - 'padding: 0px 2px', - 'font-size: 14px' - ].join(';') - ); - console.table(data); +* evenly partitions the range [min, max] into n parts +* @param {number} min +* @param {number} max +* @param {number} n +* @returns {number[]} array of length n evenly partitioned between min and max +*/ +function tickRange(min, max, n) { + var a = [min]; + var d = max-min; + var s = d / (n-1); + for (var i = 0; i < n-2; i++) { a.push(min + s * (i+1)); } + a.push(max); + return a } + /** - * Calls the console.info if d3sm.debugQ == true - * @param {string} func name of the function providing info - * @param {string} msg to display - * @param {Object} data to be displayed along side the message - * @returns {undefined} - */ -function info(func, msg, data) { - if (window.d3sm.debugQ) - console.info( - `%c[d3sm::${func}]:\t${msg}`, - [ - 'background: #009ccd', - 'border-radius: 5000px', - 'padding: 0px 2px', - 'font-size: 14px' - ].join(';') - ); - console.table(data); +* @deprecated @see{@link tickRange} +* @param {number} min +* @param {number} max +* @param {number} parts +* @returns {number[]} array of length parts evenly partitioned between min and max +*/ +function partitionRangeInto(min, max, parts) { + var diff = max - min; + return Array(parts).map(function (e, i) { return min + diff / parts * i }) } +function euclideanDistance(p1, p2){ + var a = p1[0] - p2[0], b = p1[1] - p2[1]; + return Math.sqrt(a*a + b*b) +} + /** - * Calls console.error if d3sm.debugQ == true - * @param {string} func name of the function which sends the error - * @param {string} msg to display - * @param {Object} data to be displayed along side the message - * @returns {undefined} - */ -function error(func, msg, data) { - if (window.d3sm.debugQ) - console.error(`[d3sm::${func}]:\t${msg}\t%o`,data); +* Calculated the quartiles of the passed data and stores them with qKeys +* @param {number[]} data list of numerical values +* @param {string[]} [qKeys=['q0', 'q1', 'q2', 'q3', 'q4']] how returned object with quartiles should be stored +* @returns {Object} with keys qKeys giving only the numerical values for the quartiles +*/ +function quartiles(data, qKeys) { + var + q2 = d3.median(data), + lower = data.filter(x => x < q2), + upper = data.filter(x => x > q2), + + q1 = d3.median(lower), + q1 = q1 == undefined ? q2 : q1, + + q0 = d3.min(lower), + q0 = q0 == undefined ? q1 : q0, + + q3 = d3.median(upper), + q3 = q3 == undefined ? q2 : q3, + + q4 = d3.max(upper), + q4 = q4 == undefined ? q3 : q4, + + k0 = 'q0', k1 = 'q1', k2 = 'q2', k3 = 'q3', k4 = 'q4', + obj = {}; + if (qKeys!=undefined && qKeys.length == 5) { k0 = qKeys[0]; k1 = qKeys[1]; k2 = qKeys[2]; k3 = qKeys[3]; k4 = qKeys[4]; } + obj[k0] = q0; obj[k1] = q1; obj[k2] = q2; obj[k3] = q3; obj[k4] = q4; + + return obj } +/** +* determines the width of an object for the calling plotting function +* @param {number} freeSpace how much space is avalible +* @param {number} numberOfObjects how many object do we need +* @param {number} minObjectWidth how small are these objects allowed to be +* @param {number} maxObjectWidth how large are these object allowed to be +* @param {number} sizeOfSpacer percent of freeSpace that a single spacer should take up (need numberOfObjects - 1 spacers) +* @param {boolean} overflowQ can we go beyond alloted space +* @returns {number} how large object should be +* function tries to keep object within min / max width, but wil default to +* 5e-10 (smallest consistenly visible by svg size of element) if overflowQ is false +*/ +function calculateWidthOfObject(freeSpace, numberOfObjects, minObjectWidth, maxObjectWidth, sizeOfSpacer, overflowQ) { + var sizeOfSpacer = + sizeOfSpacer == 0 || sizeOfSpacer > 1 + ? sizeOfSpacer + : freeSpace * sizeOfSpacer; + var numberOfSpacers = numberOfObjects - 1; + var spaceTakenBySpacers = numberOfSpacers * sizeOfSpacer; + var remainingSpace = freeSpace - spaceTakenBySpacers; + remainingSpace = remainingSpace < 0 ? 0 : remainingSpace; + var objectWidth = remainingSpace / numberOfObjects; + if ( overflowQ && minObjectWidth != undefined && objectWidth < minObjectWidth ) { objectWidth = minObjectWidth; } + // if ( maxObjectWidth != undefined && objectWidth > maxObjectWidth ) { objectWidth = maxObjectWidth } + if ( overflowQ && maxObjectWidth != undefined && objectWidth < maxObjectWidth ) { objectWidth = maxObjectWidth; } + return Math.max(objectWidth, 5e-10) +} /** -* Function for setting up containers for most plots with the y axis container -* positioned on the left and the x axis container positioned on the bottom -* @param {d3.selection} selection selection of container in which the svg is or should be made -* @param {string} namespace namespace of the chart -* @param {Object} [space={w:window.innerWidth, h:window.innerHeight}] the width (w) and height (h) availble -* @param {number} [space.w=window.innerWidth] the available width in which to render the chart -* @param {number} [space.h=window.innerHeight] the available height in which to render the chart - -* @param {Object} [margins={top: 0.01, bottom: 0.01, left: 0.01, right: 0.01}] the margins for the chart -* @param {number} [margins.top=0.01] the top margin of the chart -* @param {number} [margins.bottom=0.01] the bottom margin of the chart -* @param {number} [margins.left=0.01] the left margin of the chart -* @param {number} [margins.right=0.01] the right margin of the chart - - -* @param {Object} [percentages = {axes:{x:0.1,y:0.1},space:{w:0.8,h:0.6}}] percentages of the paramater space of which to make the x and y axes as well as the percent of the availble space in which to render the plot -* @param {Object} [percentages.axes={x:0.1,y:0.1}] the percentages of the paramater space, of which the x and y axes will take up -* @param {number} [percentages.axes.xAxisPercent=0.1] the percentages of the paramater space, of which the x axis will take up -* @param {number} [percentages.axes.yAxisPercent=0.1] the percentages of the paramater space, of which the y axis will take up - -* @param {Object} [percentages.space={w:0.8,h:0.6}] the percentages of the paramater space, of which the SVG's width and height will be set -* @param {number} [percentages.space.percentOfSpaceForWidth=0.1] the percentages of the paramater space, of which the SVG's width will be set -* @param {number} [percentages.space.percentOfSpaceForHeight=0.1] the percentages of the paramater space, of which the SVG's height will be set - -* @returns {Object} returns the selection and "boundingRects" of the plot container, x-axis container and y-axis container -* as -* -* { -* -* plot: {selection: plotSelection, rect: plotRect}, -* -* xAxis:{selection:xAxisSelection, rect:xAxisRect}, -* -* yAxis: {selection:yAxisSelection, rect:yAxisRect} -* -* } -* -* where each rect has form: -* -* {x: #, y: #, h: #, w: #} -* -* depicting the starting x and y coordinate of the coresponding container (also their default transform values) as well their height (h) ans width (w) +* @param {Array[]} data list data (can be nested). If nested will create more complex spacer size +* @param {number} freeSpace how much space is avalible +* @param {number} objectWidth @see{@link calculateWidthOfObject} +* @param {number} numberOfObjects how many object do we need +* @param {number} baseSpacerSize percent of freeSpace that a single spacer should take up (need numberOfObjects - 1 spacers) +* @param {boolean} overflowQ can we go beyond alloted space +* @returns {number} returns size that spacer should be at level=0 */ -// export function setupStandardChartContainers( selection, namespace, space, margins, percentages) { -// export function setupStandardChartContainers( -// selection, -// namespace, -// space={w:availableWidth=window.innerWidth, h:availableHeight=window.innerHeight}, -// margins={top:0.01, bottom:0.01, left:0.01, right:0.01}, -// percentages={axes: {x: xAxisPercent=0.1, y: yAxisPercent=0.1}, space: {w: percentOfSpaceForWidth, h: percentOfSpaceForHeight}} -// ) { -// if (space == undefined) { space = {w: window.innerWidth, h: window.innerHeight} } -// if (margins == undefined) { margins = {top: 0.01, bottom: 0.01, left: 0.01, right: 0.01} } -// if (percentages == undefined) { percentages = {}; } -// if (percentages.axes == undefined) { percentages.axes = { x:0.1, y:0.1 } } -// if (percentages.space == undefined) { percentages.space = { w: 0.8, h: 0.6 } } -// -// // SVG width and height -// var svgSpace = { -// w: space.w * percentages.space.w, -// h: space.h * percentages.space.h -// }, -// -// // Space after removing margins -// chartSpace = { -// w: svgSpace.w - (margins.left * space.w) - (margins.right * space.w), -// h: svgSpace.h - (margins.top * space.h) - (margins.bottom * space.h) -// }, -// -// // main dimension of x and y axies -// // e.g. defines how tall x axis is as length is determined by plotRect.w -// axesSpace = { -// x: chartSpace.h * percentages.axes.x, -// y: chartSpace.w * percentages.axes.y -// }, -// -// // space left for drawing the chart properly (e.g. bars, violins, etc) -// drawingSpace = { -// x: chartSpace.w - axesSpace.y, -// y: chartSpace.h - axesSpace.x -// }, -// -// -// yAxisRect = { -// x: axesSpace.y + (margins.left * space.w), -// y: (margins.top * space.h), -// w: axesSpace.y, -// h: drawingSpace.y -// }, -// -// plotRect = { -// x: axesSpace.y + (margins.left * space.w), -// y: (margins.top * space.h), -// w: drawingSpace.x, -// h: drawingSpace.y -// }, -// -// xAxisRect = { -// x: axesSpace.y + (margins.left * space.w), -// y: (margins.top * space.h + plotRect.h), -// w: drawingSpace.x, -// h: axesSpace.x -// } -// -// -// var container = safeSelect(selection, 'svg', namespace) -// .style('width', svgSpace.w+'px') -// .style('height', svgSpace.h+'px') -// -// var axes = safeSelect(container, 'g', hypenate(namespace, 'axes')) -// -// // .attr('transform', "translate("+plotRect.x+","+plotRect.y+")"), -// -// var plot = safeSelect(container, 'g', hypenate(namespace, 'plot')) -// .attr('transform', "translate("+plotRect.x+","+plotRect.y+")") -// -// var xAxis = safeSelect(axes, 'g', hypenate(namespace, 'x-axis')) -// .attr('transform', "translate("+xAxisRect.x+","+xAxisRect.y+")") -// -// var yAxis = safeSelect(axes, 'g', hypenate(namespace, 'y-axis')) -// .attr('transform', "translate("+yAxisRect.x+","+yAxisRect.y+")") -// -// return { -// svg: { -// selection: container, -// rect: svgSpace -// }, -// plot: { -// selection: plot, -// rect: plotRect -// }, -// xAxis: { -// selection: xAxis, -// rect: xAxisRect -// }, -// yAxis: { -// selection: yAxis, -// rect: yAxisRect -// } -// } -// -// // return [plot, xAxis, yAxis] -// } -// -// +function calculateWidthOfSpacer(data, freeSpace, objectWidth, numberOfObjects, baseSpacerSize, overflowQ) { + if (overflowQ) { + // var limitedNumberOfObjects = numberOfObjects > 6 ? 6 : numberOfObjects + // var spaceLeft = freeSpace - limitedNumberOfObjects * objectWidth + // return spaceLeft / (limitedNumberOfObjects - 1) + return freeSpace * baseSpacerSize + } + var spacersAtEachLevel = spacersNeededAtEachLevel(data); + var totalSpacerPercent = total(spacersAtEachLevel.map(function(e, i) {return e * 1 / (i+1)})); + var baseSpacerSize = (freeSpace - (objectWidth * numberOfObjects)) / totalSpacerPercent; + // console.log(freeSpace, objectWidth, numberOfObjects, totalSpacerPercent) + // console.log(totalSpacerPercent, baseSpacerSize, totalSpacerPercent * baseSpacerSize) + return isNaN(baseSpacerSize) ? 0 : baseSpacerSize +} +/** +* Calculates number of spacers needed to seperate elements at each level. +* @param {Array[]} array list data (can be nested). If nested will create more complex spacer size +* @param {number} [level=0] current level, used in recusrion +* @param {Array} [levelData=[]] how many spacers needed at a given level +* @returns {Array} levelData +* +* @example +* array = [[1,2], [3,4]] +* // returns [1, 2] +* as at level=0 the only spacer needed is between [1,2] and [3,4] +* and at level=1 the only two spacers needed is between 1 and 2 as well as +* 3 and 4 since the spacer between 2 and 3 is handled at level=0 +*/ +function spacersNeededAtEachLevel (array, level, levelData ) { + if ( level == undefined ) { level = 0; } else { level += 1; } + if ( levelData == undefined ) { levelData = []; } + if ( level >= levelData.length ) { levelData.push(array.length - 1); } + else { levelData[level] += array.length - 1; } + array.map(function(e, i) { if (Array.isArray(e)) { spacersNeededAtEachLevel(e, level, levelData); } } ); + return levelData +} +var math = /*#__PURE__*/Object.freeze({ + round: round, + tickRange: tickRange, + partitionRangeInto: partitionRangeInto, + euclideanDistance: euclideanDistance, + quartiles: quartiles, + calculateWidthOfObject: calculateWidthOfObject, + calculateWidthOfSpacer: calculateWidthOfSpacer, + spacersNeededAtEachLevel: spacersNeededAtEachLevel +}); +function resizeDebounce(f, wait) { + var resize = debounce(function(){f();},wait); + window.addEventListener('resize', resize); +} +function debounce(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +} function setupStandardChartContainers( selection, @@ -680,28 +574,132 @@ function setupStandardChartContainers( } } - +var misc = /*#__PURE__*/Object.freeze({ + resizeDebounce: resizeDebounce, + setupStandardChartContainers: setupStandardChartContainers +}); /** -* Adds a clip-path rect and binds it to container -* @param {d3.selection} container in which to add the clip-path and to which to bind the cliping path to -* @param {Object} rect the coordinates (x, y, width, height) of the clip-path -* @param {string} namespace -* @returns {d3.selection} of the clip-path rect +* Hypenates all strings together +* @param {string[]} arguments +* @returns {string} "arg1-arg2-...-argn" */ -function cpRect(container, rect, namespace) { - var defs = safeSelect(container, 'defs', hypenate(namespace, 'definitions')); - var cp = safeSelect(defs, 'clipPath', hypenate(namespace, 'clip-path')) - .attr('id', hypenate(namespace, 'clip-path')); +function hypenate(){ return Array.prototype.slice.call(arguments).join('-') } - var cpRect = safeSelect(cp, 'rect') - .attr('x', rect.x) - .attr('y', rect.y) - .attr('width', rect.width) - .attr('height', rect.height); - defs.raise(); - // set clipping path to container +/** +* Trys to reduce text to fit in specified area, made for tick labels as called by +* @see{@link axis} +* @param {d3.selection} t container for specific axis tick +* @param {string} text to be the label of the passed axis tick +* @param {boolean} orient of the axis, true is horizontal, false is vertical +* @param {number} tickLength is the length of the text +* @param {number} space is the amount of availble space for the text and the tick to fit in +* @param {boolean} overflowQ whether or not allowed to go over the alloted space +* @returns {none} +*/ +function truncateText(t, text, orient, tickLength, space, overflowQ) { + var rect = t.node().getBoundingClientRect(); + t.text(text); + while (Math.max(rect.width, rect.height) > space - tickLength) { + text = String(text); + text = text.slice(0, text.length - 1); + t.text(text + '...'); + rect = t.node().getBoundingClientRect(); + if (text.length == 0) break + } +} + + +function truncateString(string, space, font) { + var chars = space / font; + if (chars < string.length) { + return string.slice(0, Math.round(chars-5)) + '...' + } else { + return string + } +} + +var str = /*#__PURE__*/Object.freeze({ + hypenate: hypenate, + truncateText: truncateText, + truncateString: truncateString +}); + +/** +* Extracts x and y of translate from transform property +* @param {string} transform transform property of svg element +* @returns {number[]} x, y of translate(x, y) +*/ +function getTranslation(transform) { + // Create a dummy g for calculation purposes only. This will never + // be appended to the DOM and will be discarded once this function + // returns. + var g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + // Set the transform attribute to the provided string value. + transform = transform == undefined ? 'translate(0,0)' : transform; + g.setAttributeNS(null, 'transform', transform); + // consolidate the SVGTransformList containing all transformations + // to a single SVGTransform of type SVG_TRANSFORM_MATRIX and get + // its SVGMatrix. + var matrix = g.transform.baseVal.consolidate().matrix; + // As per definition values e and f are the ones for the translation. + return [matrix.e, matrix.f]; +} + +/** +* recursively ascends element.parentElement to find a svg tag +* @param {Element} element +* @returns {Element | undefined} +*/ +function getContainingSVG$1(element) { + var parent = element.parentElement; + var tag = parent.tagName.toLowerCase(); + if (tag === 'svg') { return parent; } + if (tag === 'html') { return undefined; } + return getContainingSVG$1(parent); +} + + +/** +* Trys to use d3.selection to get element, if it doesnt exist, makes one +* @param {d3.selection} sel selection in which to try and find object +* @param {string} tag tag of which to try and select +* @param {string} [cls=''] class of tag to try and grab +* @returns {d3.selection} of either append or selected tag.cls within sel +*/ +function safeSelect(sel, tag, cls) { + var clsStr = cls == undefined ? '' : '.'+cls; + var sSel = sel.select(tag+clsStr).empty() + ? sel.append(tag) + : sel.select(tag+clsStr); + return sSel + .classed(clsStr.replace('.', ''), true) + .attr('transform', sSel.attr('transform') == undefined ? 'translate(0,0)' : sSel.attr('transform')) +} + + + +/** +* Adds a clip-path rect and binds it to container +* @param {d3.selection} container in which to add the clip-path and to which to bind the cliping path to +* @param {Object} rect the coordinates (x, y, width, height) of the clip-path +* @param {string} namespace +* @returns {d3.selection} of the clip-path rect +*/ +function cpRect(container, rect, namespace) { + var defs = safeSelect(container, 'defs', hypenate(namespace, 'definitions')); + var cp = safeSelect(defs, 'clipPath', hypenate(namespace, 'clip-path')) + .attr('id', hypenate(namespace, 'clip-path')); + + var cpRect = safeSelect(cp, 'rect') + .attr('x', rect.x) + .attr('y', rect.y) + .attr('width', rect.width) + .attr('height', rect.height); + + defs.raise(); + // set clipping path to container container.attr('clip-path', 'url(#'+ hypenate(namespace, 'clip-path')+')'); return cpRect @@ -748,96 +746,130 @@ function setupContainer(selection, namespace, rect, fill) { return objectContainer } +var sel = /*#__PURE__*/Object.freeze({ + getTranslation: getTranslation, + getContainingSVG: getContainingSVG$1, + safeSelect: safeSelect, + cpRect: cpRect, + bgRect: bgRect, + setupContainer: setupContainer +}); /** -* determines the width of an object for the calling plotting function -* @param {number} freeSpace how much space is avalible -* @param {number} numberOfObjects how many object do we need -* @param {number} minObjectWidth how small are these objects allowed to be -* @param {number} maxObjectWidth how large are these object allowed to be -* @param {number} sizeOfSpacer percent of freeSpace that a single spacer should take up (need numberOfObjects - 1 spacers) -* @param {boolean} overflowQ can we go beyond alloted space -* @returns {number} how large object should be -* function tries to keep object within min / max width, but wil default to -* 5e-10 (smallest consistenly visible by svg size of element) if overflowQ is false -*/ -function calculateWidthOfObject(freeSpace, numberOfObjects, minObjectWidth, maxObjectWidth, sizeOfSpacer, overflowQ) { - var sizeOfSpacer = - sizeOfSpacer == 0 || sizeOfSpacer > 1 - ? sizeOfSpacer - : freeSpace * sizeOfSpacer; - - var numberOfSpacers = numberOfObjects - 1; - var spaceTakenBySpacers = numberOfSpacers * sizeOfSpacer; - var remainingSpace = freeSpace - spaceTakenBySpacers; - remainingSpace = remainingSpace < 0 ? 0 : remainingSpace; - var objectWidth = remainingSpace / numberOfObjects; - - if ( overflowQ && minObjectWidth != undefined && objectWidth < minObjectWidth ) { objectWidth = minObjectWidth; } - // if ( maxObjectWidth != undefined && objectWidth > maxObjectWidth ) { objectWidth = maxObjectWidth } - if ( overflowQ && maxObjectWidth != undefined && objectWidth < maxObjectWidth ) { objectWidth = maxObjectWidth; } - return Math.max(objectWidth, 5e-10) + * calls console.group if d3sm.debugQ == true + * @param {string} name of the group + * @returns {undefined} + */ +function consoleGroup(name) { + if (window.d3sm.debugQ === true){ + console.group(name); + } } /** -* @param {Array[]} data list data (can be nested). If nested will create more complex spacer size -* @param {number} freeSpace how much space is avalible -* @param {number} objectWidth @see{@link calculateWidthOfObject} -* @param {number} numberOfObjects how many object do we need -* @param {number} baseSpacerSize percent of freeSpace that a single spacer should take up (need numberOfObjects - 1 spacers) -* @param {boolean} overflowQ can we go beyond alloted space -* @returns {number} returns size that spacer should be at level=0 -*/ -function calculateWidthOfSpacer(data, freeSpace, objectWidth, numberOfObjects, baseSpacerSize, overflowQ) { - if (overflowQ) { - // var limitedNumberOfObjects = numberOfObjects > 6 ? 6 : numberOfObjects - // var spaceLeft = freeSpace - limitedNumberOfObjects * objectWidth - // return spaceLeft / (limitedNumberOfObjects - 1) - return freeSpace * baseSpacerSize + * calls console.groupEnd if d3sm.debugQ == true + * @returns {undefined} + */ +function consoleGroupEnd() { + if (window.d3sm.debugQ === true){ + console.groupEnd(); } - var spacersAtEachLevel = spacersNeededAtEachLevel(data); - var totalSpacerPercent = total(spacersAtEachLevel.map(function(e, i) {return e * 1 / (i+1)})); - var baseSpacerSize = (freeSpace - (objectWidth * numberOfObjects)) / totalSpacerPercent; - // console.log(freeSpace, objectWidth, numberOfObjects, totalSpacerPercent) - // console.log(totalSpacerPercent, baseSpacerSize, totalSpacerPercent * baseSpacerSize) - return isNaN(baseSpacerSize) ? 0 : baseSpacerSize } - /** -* Calculates number of spacers needed to seperate elements at each level. -* @param {Array[]} array list data (can be nested). If nested will create more complex spacer size -* @param {number} [level=0] current level, used in recusrion -* @param {Array} [levelData=[]] how many spacers needed at a given level -* @returns {Array} levelData -* -* @example -* array = [[1,2], [3,4]] -* // returns [1, 2] -* as at level=0 the only spacer needed is between [1,2] and [3,4] -* and at level=1 the only two spacers needed is between 1 and 2 as well as -* 3 and 4 since the spacer between 2 and 3 is handled at level=0 -*/ -function spacersNeededAtEachLevel (array, level, levelData ) { - if ( level == undefined ) { level = 0; } else { level += 1; } - if ( levelData == undefined ) { levelData = []; } - if ( level >= levelData.length ) { levelData.push(array.length - 1); } - else { levelData[level] += array.length - 1; } - array.map(function(e, i) { if (Array.isArray(e)) { spacersNeededAtEachLevel(e, level, levelData); } } ); - return levelData + * Calls console.log if d3sm.debugQ == true + * @param {string} func name of the function logging + * @param {string} msg to log + * @param {Object} data to be logged along side the message + * @returns {undefined} + */ +function log(func, msg, data) { + if (window.d3sm.debugQ === true){ + console.log( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #6cd1ef', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ); + console.table(data); + // console.trace() + } } - +/** + * Calls console.warn if d3sm.debugQ == true + * @param {string} func name of the function warning + * @param {string} msg to display + * @param {Object} data to be displayed along side the message + * @returns {undefined} + */ +function warn(func, msg, data) { + if (window.d3sm.debugQ === true) + console.warn( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #ffd53e', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ); + console.table(data); +} +/** + * Calls the console.info if d3sm.debugQ == true + * @param {string} func name of the function providing info + * @param {string} msg to display + * @param {Object} data to be displayed along side the message + * @returns {undefined} + */ +function info(func, msg, data) { + if (window.d3sm.debugQ) + console.info( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #009ccd', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ); + console.table(data); +} /** -* Draws a whisker for @see{@link boxwhisker} -* @param {boolean} dir direction to draw whisker, should be either true (up, top) or false (down or bottom) -* @param {number} x starting x coordinate in which to draw whisker -* @param {number} y starting y coordinate in which to draw whisker -* @param {number} w width of space in which to draw whisker -* @param {number} h height of space in which to draw whisker -* @param {number} per percentage of w or h (depends on o) to make whisker + * Calls console.error if d3sm.debugQ == true + * @param {string} func name of the function which sends the error + * @param {string} msg to display + * @param {Object} data to be displayed along side the message + * @returns {undefined} + */ +function error(func, msg, data) { + if (window.d3sm.debugQ) + console.error(`[d3sm::${func}]:\t${msg}\t%o`,data); +} + +var con = /*#__PURE__*/Object.freeze({ + consoleGroup: consoleGroup, + consoleGroupEnd: consoleGroupEnd, + log: log, + warn: warn, + info: info, + error: error +}); + +/** +* Draws a whisker for @see{@link boxwhisker} +* @param {boolean} dir direction to draw whisker, should be either true (up, top) or false (down or bottom) +* @param {number} x starting x coordinate in which to draw whisker +* @param {number} y starting y coordinate in which to draw whisker +* @param {number} w width of space in which to draw whisker +* @param {number} h height of space in which to draw whisker +* @param {number} per percentage of w or h (depends on o) to make whisker * @param {boolean} o orientation, true is horizontal and false is vertical * @returns {string} representing the svg path (i.e. the d attribute for a path tag) */ @@ -870,39 +902,13 @@ function whiskerPath(dir, x, y, w, h, per, o) { return p } +var paths = /*#__PURE__*/Object.freeze({ + whiskerPath: whiskerPath +}); - - - - - - - - - - - -function resizeDebounce(f, wait) { - var resize = debounce(function(){f();},wait); - window.addEventListener('resize', resize); -} - - - -function debounce(func, wait, immediate) { - var timeout; - return function() { - var context = this, args = arguments; - var later = function() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; -} +let utils$1 = { + arr, color, math, misc, sel, str, con, paths +}; /******************************************************************************* ** ** @@ -1030,7 +1036,7 @@ function groupingSpacer() { x = horizontalQ ? window.outerWidth : 0, y = !horizontalQ ? window.outerWidth : 0, t = 'translate('+x+','+y+')'; - // if(y == undefined) {console.log(cur.node(), y, d)} + // if(y == undefined) {console.con.log(cur.node(), y, d)} return t }); }, @@ -1063,7 +1069,7 @@ function groupingSpacer() { x = horizontalQ ? window.outerWidth : 0, y = !horizontalQ ? window.outerWidth : 0, t = 'translate('+x+','+y+')'; - // if(y == undefined) {console.log(cur.node(), y, d)} + // if(y == undefined) {console.con.log(cur.node(), y, d)} return t }).remove(); }; @@ -2010,7 +2016,7 @@ function axis ( selection ) { function axis () { // for convenience in handling orientation specific values - var horizontalQ = hasQ(['top', 'bottom', 'horizontal'], orient) ? true : false; + var horizontalQ = utils$1.arr.hasQ(['top', 'bottom', 'horizontal'], orient) ? true : false; var verticalQ = !horizontalQ; // background cliping rectangle @@ -2041,7 +2047,7 @@ function axis ( selection ) { } - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); // defaults for text-anchor and text rotation if (orient == 'top') { @@ -2079,12 +2085,12 @@ function axis ( selection ) { : (grouping == undefined) ? (numberOfTicks != undefined) // ? (tickValues.length < numberOfTicks) - ? (tickRange(...d3.extent(tickValues), numberOfTicks)) + ? (utils$1.math.tickRange(...d3.extent(tickValues), numberOfTicks)) : tickValues : grouping; - var flatTickData = flatten(tickData); + var flatTickData = utils$1.arr.flatten(tickData); var numberOfObjects = flatTickData.length; var space = horizontalQ ? spaceX : spaceY; var extent = d3.extent(flatTickData); @@ -2108,16 +2114,16 @@ function axis ( selection ) { // calculate object size if needed objectSize = (objectSize == undefined) - ? calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + ? utils$1.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) : objectSize; // calculate spacer size if needed spacerSize = (spacerSize == undefined) - ? calculateWidthOfSpacer(flatTickData, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + ? utils$1.math.calculateWidthOfSpacer(flatTickData, space, objectSize, numberOfObjects, objectSpacer, overflowQ) : spacerSize; - var objClass = hypenate(namespace, categoricalQ ? objectClass+'-categorical' : objectClass); + var objClass = utils$1.str.hypenate(namespace, categoricalQ ? objectClass+'-categorical' : objectClass); var spacerFunction = groupingSpacer() .horizontalQ(horizontalQ).scale(scale).moveby((categoricalQ?'category':'scale')).numberOfObjects(numberOfObjects) @@ -2125,12 +2131,12 @@ function axis ( selection ) { .transitionDuration(transitionDuration).easeFunc(easeFunc) .namespace(namespace); - var tickEnterAnimation = function(sel){ - var mt = scale(sel.datum()), + var tickEnterAnimation = function(sel$$1){ + var mt = scale(sel$$1.datum()), dist = scale(extent[1]) * 2, k = (mt < extent[1] / 2) ? 1 : -1; k = horizontalQ ? k * -1 : k; - sel.attr('transform', function (d, i) { + sel$$1.attr('transform', function (d, i) { var x = horizontalQ ? dist * k : 0, y = !horizontalQ ? dist * k : 0, @@ -2138,12 +2144,12 @@ function axis ( selection ) { return t }); }; - var tickExitAnimation = function(sel) { - var mt = scale(sel.datum()), + var tickExitAnimation = function(sel$$1) { + var mt = scale(sel$$1.datum()), dist = scale(extent[1]) * 2, k = (mt < extent[1] / 2) ? 1 : -1; k = horizontalQ ? k * -1 : k; - sel.transition().duration(transitionDuration).ease(easeFunc) + sel$$1.transition().duration(transitionDuration).ease(easeFunc) .style('opacity', 0) .attr('transform', function (d, i) { var @@ -2182,11 +2188,11 @@ function axis ( selection ) { - var labelNameGroup = safeSelect(selection, 'g', hypenate(namespace,'axis-name')); + var labelNameGroup = utils$1.sel.safeSelect(selection, 'g', utils$1.str.hypenate(namespace,'axis-name')); - var labelElement = safeSelect(labelNameGroup, 'text', hypenate(namespace, 'name')); + var labelElement = utils$1.sel.safeSelect(labelNameGroup, 'text', utils$1.str.hypenate(namespace, 'name')); if (labelElement != undefined) { labelElement.text(label); @@ -2232,8 +2238,8 @@ function axis ( selection ) { to use clip path to make things fit in fixed size. Have yet got this to work nicely. */ // var defs = d3.select(container.node().parentNode).select('defs') - // var tickLabelClipPath = safeSelect(defs, 'clipPath', hypenate(namespace,'tick-label-clip-path')).attr('id', hypenate(namespace,'tick-label-clip-path')) - // var tickLabelClipPathRect = safeSelect(tickLabelClipPath, 'rect', hypenate(namespace,'tick-label-clip-path-rect')) + // var tickLabelClipPath = utils.sel.safeSelect(defs, 'clipPath', utils.str.hypenate(namespace,'tick-label-clip-path')).attr('id', utils.str.hypenate(namespace,'tick-label-clip-path')) + // var tickLabelClipPathRect = utils.sel.safeSelect(tickLabelClipPath, 'rect', utils.str.hypenate(namespace,'tick-label-clip-path-rect')) // .attr('x', 0) // .attr('y', 0) // .attr('width', function(d, i){ @@ -2251,7 +2257,7 @@ function axis ( selection ) { var that = d3.select(this).style('opacity', 1); // make and move tick - var tick = safeSelect(that, 'line', hypenate(namespace,'tick')) + var tick = utils$1.sel.safeSelect(that, 'line', utils$1.str.hypenate(namespace,'tick')) .attr("x1", 0) .attr("x2", horizontalQ ? 0 : orient == "left" ? -tickLength : tickLength) .attr("y1", 0) @@ -2267,15 +2273,15 @@ function axis ( selection ) { }); // make and move label - var label = safeSelect(that, 'text', hypenate(namespace,'label')) + var label = utils$1.sel.safeSelect(that, 'text', utils$1.str.hypenate(namespace,'label')) .text(function(d, i){ - var s = typeof d == 'number' ? round(d, roundTo) : d; - s = truncateString(String(s), (horizontalQ ? spaceY : spaceX) - tickLength-tickLabelMargin-tickTickLabelSpacer, tickLabelFontSize * 0.45); + var s = typeof d == 'number' ? utils$1.math.round(d, roundTo) : d; + s = utils$1.str.truncateString(String(s), (horizontalQ ? spaceY : spaceX) - tickLength-tickLabelMargin-tickTickLabelSpacer, tickLabelFontSize * 0.45); return s }) .attr('font-size', tickLabelFontSize) .attr('text-anchor', tickLabelTextAnchor); - // truncateText(label, label.text(), orient, tickLength, horizontalQ ? spaceY : spaceX, overflowQ) + // utils.str.truncateText(label, label.text(), orient, tickLength, horizontalQ ? spaceY : spaceX, overflowQ) label.attr('transform', function(d, i) { var @@ -2318,11 +2324,11 @@ function axis ( selection ) { .on('mousemove', labelHover) .on('mouseout', labelHoverOff) .on('click', tickLabelOnClick); - // .attr('clip-path', 'url(#'+hypenate(namespace,'tick-label-clip-path')+')') + // .attr('clip-path', 'url(#'+utils.str.hypenate(namespace,'tick-label-clip-path')+')') // add guidlines as needed if (guideLinesQ) { - var gline = safeSelect(that, 'line', hypenate(namespace, 'guideline')) + var gline = utils$1.sel.safeSelect(that, 'line', utils$1.str.hypenate(namespace, 'guideline')) .transition().duration(transitionDuration).ease(easeFunc) .attr("x1", 0) .attr("x2", horizontalQ ? 0 : orient == "left" ? guidelineSpace : -guidelineSpace) @@ -2335,15 +2341,15 @@ function axis ( selection ) { t = 'translate('+x+','+y+')'; return t }); - } else { that.select('line.'+hypenate(namespace, 'guideline')).remove(); } + } else { that.select('line.'+utils$1.str.hypenate(namespace, 'guideline')).remove(); } }); // apply alternating guidline thickness if (guideLinesQ) { - container.selectAll('.'+hypenate(namespace,'guideline')) + container.selectAll('.'+utils$1.str.hypenate(namespace,'guideline')) .attr('stroke', function(d, i){ - if (i % 2 == 0) { return modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } + if (i % 2 == 0) { return utils$1.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } return guideLineStroke }) .attr('stroke-width', function(d, i){ @@ -2357,7 +2363,7 @@ function axis ( selection ) { /*************************************************************************** ** Make the line of the axis ***************************************************************************/ - var line = safeSelect(selection, 'path', hypenate(namespace,'line')) + var line = utils$1.sel.safeSelect(selection, 'path', utils$1.str.hypenate(namespace,'line')) // .attr('x1', 0) // .attr('x2', horizontalQ ? spaceX : 0) // .attr('y1', 0) @@ -2377,21 +2383,21 @@ function axis ( selection ) { // hover of label show full text label in case it is truncated function labelHover(d, i){ var t = d3.select(this).style('fill', 'red'); - d3.select(t.node().parentNode).select("line."+hypenate(namespace,'tick')) + d3.select(t.node().parentNode).select("line."+utils$1.str.hypenate(namespace,'tick')) .attr("stroke", 'red') .attr("stroke-width", tickStrokeWidth*2); if (guideLinesQ) { - d3.select(t.node().parentNode).select('line.'+hypenate(namespace, 'guideline')) + d3.select(t.node().parentNode).select('line.'+utils$1.str.hypenate(namespace, 'guideline')) .attr('stroke', 'red') .attr('stroke-width', guideLineStrokeWidth*2); } - var s = typeof d == 'number' ? round(d, roundTo) : d; + var s = typeof d == 'number' ? utils$1.math.round(d, roundTo) : d; var m = d3.mouse(d3.select('html').node()); - var div = safeSelect(d3.select('body'), 'div', hypenate(namespace,'guideline-tooltip')) - .attr('id', hypenate(namespace,'guideline-tooltip')) + var div = utils$1.sel.safeSelect(d3.select('body'), 'div', utils$1.str.hypenate(namespace,'guideline-tooltip')) + .attr('id', utils$1.str.hypenate(namespace,'guideline-tooltip')) .style('position', 'absolute') .style('left', (d3.event.pageX+15)+'px') .style('top', (d3.event.pageY+15)+'px') @@ -2408,7 +2414,7 @@ function axis ( selection ) { .style('border-style', 'solid') .style('border-width', 2); - var text = safeSelect(div, 'div') + var text = utils$1.sel.safeSelect(div, 'div') .text(tickLabelOnHoverFunc(s, i)) .style('color', 'black') .style('align-self', 'center'); @@ -2421,15 +2427,15 @@ function axis ( selection ) { function labelHoverOff(d, i){ var t = d3.select(this).style('fill', 'black'); - d3.select(t.node().parentNode).select("line."+hypenate(namespace,'tick')) + d3.select(t.node().parentNode).select("line."+utils$1.str.hypenate(namespace,'tick')) .attr("stroke", tickStroke) .attr("stroke-width", tickStrokeWidth); if (guideLinesQ) { - var gline = d3.select(t.node().parentNode).select('line.'+hypenate(namespace, 'guideline')); + var gline = d3.select(t.node().parentNode).select('line.'+utils$1.str.hypenate(namespace, 'guideline')); var minorQ = gline.attr('minor'); gline.attr('stroke', function(d, ii){ - if (minorQ == 'true') { return modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } + if (minorQ == 'true') { return utils$1.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } return guideLineStroke }) .attr('stroke-width', function(d, ii){ @@ -2437,7 +2443,7 @@ function axis ( selection ) { return guideLineStrokeWidth }); } - d3.select("#"+hypenate(namespace,'guideline-tooltip')).remove(); + d3.select("#"+utils$1.str.hypenate(namespace,'guideline-tooltip')).remove(); } @@ -2445,6 +2451,164 @@ function axis ( selection ) { return axis } +/******************************************************************************* +** ** +** ** +** TOOLTIP ** +** ** +** ** +*******************************************************************************/ +/** + * Produces a function for handling the tooltip + * + * {@link https://sumneuron.gitlab.io/d3sm/demos/tooltip-design/index.html Demo} + * @param {d3.selection} selection + * @returns {tooltip} + * @namespace tooltip + */ +function tooltip( selection ) { + + var + keys, + values, + header, + data, + selection; + + /** + * Gets / sets the keys to be displayed in the tooltip. + * If not set, uses d3.keys(data[key]) + * @param {string[]} [_=none] + * @returns {tooltip | string[]} + * @memberof tooltip + */ + tooltip.keys = function(_){return arguments.length ? (keys = _, tooltip) : keys}; + /** + * Gets / sets the values to be displayed next to the keys. + * If not set, uses data[key][keys[i]]. + * If a function, gets passed currentData (data[key]) and keys[i]. + * @param {*[]} [_=none] + * @returns {tooltip | *[]} + * @memberof tooltip + */ + tooltip.values = function(_){return arguments.length ? (values = _, tooltip) : values}; + /** + * Gets / sets the header to be displayed in the tooltip. + * If not set, uses key + * @param {string} [_=none] + * @returns {tooltip | string} + * @memberof tooltip + */ + tooltip.header = function(_){return arguments.length ? (header = _, tooltip) : header}; + /** + * Gets / sets the data (over the selection) to be used for the tooltip + * @param {Object} [_=none] + * @returns {tooltip | Object} + * @memberof tooltip + */ + tooltip.data = function(_){return arguments.length ? (data = _, tooltip) : data}; + /** + * Gets / sets the selection for the tooltip to be applied on + * @param {d3.selection} [_=none] + * @returns {tooltip | d3.selection} + * @memberof tooltip + */ + tooltip.selection = function(_){return arguments.length ? (selection = _, tooltip) : selection}; + + /** + * Bind, via selection.on(), the mousemove and mouseout events + * @returns undefined + */ + function tooltip( ) { + selection.on('mouseover', mousemove); + selection.on('mousemove', mousemove); + selection.on('mouseout', function(){ d3.selectAll(".d3sm-tooltip").remove();}); + } + + + /** + * Produces the tooltip on mousemove + * @param {string} key of the object targeted by the mousemove + * @param {number} i (index) of the object targeted by mousemove + * @memberof tooltip + * @private + */ + function mousemove(key, i) { + consoleGroup('d3sm-tooltip'); + var currentData = data[key]; + + var [x, y] = d3.mouse(d3.select("html").node()); + log('tooltip', 'mousemove detected',{key: key, index: i, x:x, y:y}); + log('tooltip', 'current data', currentData); + + + + var div = safeSelect(d3.select('html'), 'tooltip', 'd3sm-tooltip') + .classed('card', true) + .style('max-width', '300px') + .style('background-color', "#212529") + .style('color', 'white'); + + + + var cardBody = safeSelect(div, 'div', 'card-body'); + var cardTitle = safeSelect(cardBody, 'h5', 'card-title') + .text(header == undefined ? key : typeof header == 'function' ? header(key, currentData, i) : header) + .style('color', 'cyan'); + + + var table = safeSelect(cardBody, 'table', 'table').classed('table-dark', true); + var tBody = safeSelect(table, 'tbody'); + + tBody = tBody.selectAll('tr'); + tBody= tBody.data(keys == undefined ? d3.keys(currentData): keys); + tBody.exit().remove(); + + + var tr = tBody.enter().append('tr').style('max-width', '300px'); + tr.append('td').attr('class', function(d, i){return 'tooltip-key'}); + tr.append('td').attr('class', function(d, i, j){return 'tooltip-value'}) + .attr('tooltip-row-index', function(d, i){return i}); + + // tBody = tBody.merge(tr) + consoleGroup('tooltip-rows'); + tBody.selectAll('.tooltip-key').text(function(d, i){return d}); + tBody.selectAll('tr .tooltip-value') + .text(function(d, i){ + log('tooltip', 'trying to set value', {rowKey: d, rowIndex: i}); + var i = d3.select(this).attr('tooltip-row-index'); + var v = currentData[d]; + + + if (values != undefined) {v = values[i]; if(typeof v == "function") {v = v(currentData, d);}} + return typeof v == 'number' ? round(v, 5) : v + }); + consoleGroupEnd(); + consoleGroupEnd(); + + x += 15; + // x += 15 + var bbox = div.node().getBoundingClientRect(); + if (x + bbox.width > window.innerWidth - window.scrollX) { x = d3.event.pageX - bbox.width - 15; } + if (y + bbox.height > window.innerHeight - window.scrollY) { y = d3.event.pageY - bbox.height - 15; } + div.style('position') == "relative" + ? div.style('position', 'absolute').style('left', x+'px').style('top', y+'px') + : div.style('left', x+'px').style('top', y+'px'); + // .transition().duration(200).ease(d3.easeSin) + + // if (bbox.x + bbox.width > window.innerWidth) { + // div.style('left', (d3.event.pageX-15-bbox.width)+'px') + // } + // if (bbox.y + bbox.height > window.innerHeight) { + // div.style('top', (d3.event.pageY-15-bbox.height)+'px') + // } + + div.attr('z-index', 10000); + } + + return tooltip +} + /** * Creates a colorFunction * @constructor colorFunction @@ -2733,1857 +2897,1539 @@ function colorFunction() { /******************************************************************************* ** ** ** ** -** TOOLTIP ** +** SCATTER ** ** ** ** ** *******************************************************************************/ /** - * Produces a function for handling the tooltip + * Creates a scatter * - * {@link https://sumneuron.gitlab.io/d3sm/demos/tooltip-design/index.html Demo} + * {@link https://sumneuron.gitlab.io/d3sm/demos/scatter/index.html Demo} + * @constructor scatter * @param {d3.selection} selection - * @returns {tooltip} - * @namespace tooltip + * @namespace scatter + * @returns {function} scatter */ -function tooltip( selection ) { +function scatter ( selection ) { var - keys, - values, - header, + /** + * Data to plot. Assumed to be a object, where each key corresponds to a point + * (see {@link scatter#data}) + * @param {Object} [data=undefined] + * @memberof scatter# + * @property + */ data, - selection; - /** - * Gets / sets the keys to be displayed in the tooltip. - * If not set, uses d3.keys(data[key]) - * @param {string[]} [_=none] - * @returns {tooltip | string[]} - * @memberof tooltip - */ - tooltip.keys = function(_){return arguments.length ? (keys = _, tooltip) : keys}; + * Amount of horizontal space (in pixels) avaible to render the scatter in + * (see {@link scatter#spaceX}) + * @param {number} [spaceX=undefined] + * @memberof scatter# + * @property + */ + spaceX, /** - * Gets / sets the values to be displayed next to the keys. - * If not set, uses data[key][keys[i]]. - * If a function, gets passed currentData (data[key]) and keys[i]. - * @param {*[]} [_=none] - * @returns {tooltip | *[]} - * @memberof tooltip - */ - tooltip.values = function(_){return arguments.length ? (values = _, tooltip) : values}; - /** - * Gets / sets the header to be displayed in the tooltip. - * If not set, uses key - * @param {string} [_=none] - * @returns {tooltip | string} - * @memberof tooltip - */ - tooltip.header = function(_){return arguments.length ? (header = _, tooltip) : header}; - /** - * Gets / sets the data (over the selection) to be used for the tooltip - * @param {Object} [_=none] - * @returns {tooltip | Object} - * @memberof tooltip - */ - tooltip.data = function(_){return arguments.length ? (data = _, tooltip) : data}; - /** - * Gets / sets the selection for the tooltip to be applied on - * @param {d3.selection} [_=none] - * @returns {tooltip | d3.selection} - * @memberof tooltip - */ - tooltip.selection = function(_){return arguments.length ? (selection = _, tooltip) : selection}; - - /** - * Bind, via selection.on(), the mousemove and mouseout events - * @returns undefined - */ - function tooltip( ) { - selection.on('mouseover', mousemove); - selection.on('mousemove', mousemove); - selection.on('mouseout', function(){ d3.selectAll(".d3sm-tooltip").remove();}); - } - + * Amount of vertical space (in pixels) avaible to render the scatter in + * (see {@link scatter.spaceY}) + * @param {number} [spaceY=undefined] + * @memberof scatter# + * @property + */ + spaceY, /** - * Produces the tooltip on mousemove - * @param {string} key of the object targeted by the mousemove - * @param {number} i (index) of the object targeted by mousemove - * @memberof tooltip - * @private - */ - function mousemove(key, i) { - consoleGroup('d3sm-tooltip'); - var currentData = data[key]; - - var [x, y] = d3.mouse(d3.select("html").node()); - log('tooltip', 'mousemove detected',{key: key, index: i, x:x, y:y}); - log('tooltip', 'current data', currentData); - - - - var div = safeSelect(d3.select('html'), 'tooltip', 'd3sm-tooltip') - .classed('card', true) - .style('max-width', '300px') - .style('background-color', "#212529") - .style('color', 'white'); - - - - var cardBody = safeSelect(div, 'div', 'card-body'); - var cardTitle = safeSelect(cardBody, 'h5', 'card-title') - .text(header == undefined ? key : typeof header == 'function' ? header(key, currentData, i) : header) - .style('color', 'cyan'); - - - var table = safeSelect(cardBody, 'table', 'table').classed('table-dark', true); - var tBody = safeSelect(table, 'tbody'); - - tBody = tBody.selectAll('tr'); - tBody= tBody.data(keys == undefined ? d3.keys(currentData): keys); - tBody.exit().remove(); - - - var tr = tBody.enter().append('tr').style('max-width', '300px'); - tr.append('td').attr('class', function(d, i){return 'tooltip-key'}); - tr.append('td').attr('class', function(d, i, j){return 'tooltip-value'}) - .attr('tooltip-row-index', function(d, i){return i}); - - // tBody = tBody.merge(tr) - consoleGroup('tooltip-rows'); - tBody.selectAll('.tooltip-key').text(function(d, i){return d}); - tBody.selectAll('tr .tooltip-value') - .text(function(d, i){ - log('tooltip', 'trying to set value', {rowKey: d, rowIndex: i}); - var i = d3.select(this).attr('tooltip-row-index'); - var v = currentData[d]; - - - if (values != undefined) {v = values[i]; if(typeof v == "function") {v = v(currentData, d);}} - return typeof v == 'number' ? round(v, 5) : v - }); - consoleGroupEnd(); - consoleGroupEnd(); - - x += 15; - // x += 15 - var bbox = div.node().getBoundingClientRect(); - if (x + bbox.width > window.innerWidth - window.scrollX) { x = d3.event.pageX - bbox.width - 15; } - if (y + bbox.height > window.innerHeight - window.scrollY) { y = d3.event.pageY - bbox.height - 15; } - div.style('position') == "relative" - ? div.style('position', 'absolute').style('left', x+'px').style('top', y+'px') - : div.style('left', x+'px').style('top', y+'px'); - // .transition().duration(200).ease(d3.easeSin) - - // if (bbox.x + bbox.width > window.innerWidth) { - // div.style('left', (d3.event.pageX-15-bbox.width)+'px') - // } - // if (bbox.y + bbox.height > window.innerHeight) { - // div.style('top', (d3.event.pageY-15-bbox.height)+'px') - // } - - div.attr('z-index', 10000); - } - - return tooltip -} - -/******************************************************************************* -** ** -** ** -** BAR ** -** ** -** ** -*******************************************************************************/ - -/** - * Creates a bar - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/bar-chart-same-data-complex-grouping/index.html Demo} - * @constructor bar - * @param {d3.selection} selection - * @namespace bar - * @returns {function} bar - */ -function bar ( selection ) { - /* - Assumes that data is list an object. - - The keys of data will be bound to the bars. - The valueExtractor function will extract the value from data[key]. - - Grouping can be used if desired. It should be an arbitrary complex list where - the values are strings matching keys in data. + * The scale for which scatter x values should be transformed by + * @param {d3.scale} [scaleX=d3.scaleLinear] + * @memberof scatter# + * @property */ - var + scaleX = d3.scaleLinear(), /** - * Data to plot. Assumed to be a object, where each key corresponds to a bar - * (see {@link bar#data}) - * @param {Object} [data=undefined] - * @memberof bar# + * The padding for the domain of the scaleX (see {@link scatter#scaleX}) + * @param {number} [domainPaddingX=0.5] + * @memberof scatter# * @property */ - data, + domainPaddingX = 0.5, /** - * Which direction to render the bars in - * (see {@link bar#orient}) - * @param {number} [orient='horizontal'] - * @memberof bar# + * The function for getting the x value of the current point + * @param {function} [valueExtractorX=function(d, i){return data[d]['x']}] + * @memberof scatter# * @property */ - orient='horizontal', + valueExtractorX = function(d, i) {return data[d]['x']}, + /** - * Amount of horizontal space (in pixels) avaible to render the bar in - * (see {@link bar#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof bar# + * The scale for which scatter y values should be transformed by + * @param {d3.scale} [scaleY=d3.scaleLinear] + * @memberof scatter# * @property */ - spaceX, + scaleY = d3.scaleLinear(), /** - * Amount of vertical space (in pixels) avaible to render the bar in - * (see {@link bar.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof bar# + * The padding for the domain of the scaleY (see {@link scatter#scaleY}) + * @param {number} [domainPaddingY=0.5] + * @memberof scatter# * @property */ - spaceY, - + domainPaddingY = 0.5, /** - * Whether or not to allow bar to render elements pass the main spatial dimension - * given the orientation (see {@link bar#orient}), where {@link bar#orient}="horizontal" - * the main dimension is {@link bar#spaceX} and where {@link bar#orient}="vertical" - * the main dimension is {@link bar#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof bar# + * The function for getting the y value of the current point + * @param {function} [valueExtractorY=function(d, i){return data[d]['y']}] + * @memberof scatter# * @property */ - overflowQ = false, + valueExtractorY = function(d, i) {return data[d]['y']}, + /** - * An array - putatively of other arrays - depicting how bars should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof bar# + * The scale for which scatter r values should be transformed by + * @param {d3.scale} [scaleR=d3.scaleLinear] + * @memberof scatter# * @property */ - grouping, - + scaleR = d3.scaleLinear(), /** - * How to get the value of the bar - * @param {function} [valueExtractor=function(key, index) { return data[key] }] - * @memberof bar# + * The padding for the domain of the scaleR (see {@link scatter#scaleR}) + * @param {number} [domainPaddingR=0.5] + * @memberof scatter# * @property */ - valueExtractor = function(key, index) { return data[key] }, + domainPaddingR = 0.5, /** - * How to sort the bars - if {@link bar#grouping} is not provided. - * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] - * @memberof bar# + * The function for getting the r value of the current point + * @param {function} [valueExtractorR=function(d, i){return 2}] + * @memberof scatter# * @property */ - sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, - + valueExtractorR = function(d, i) {return 2}, /** - * The scale for which bar values should be transformed by - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof bar# + * The min radius a point can have + * @param {function} [minRadius=2] + * @memberof scatter# * @property */ - scale = d3.scaleLinear(), + minRadius = 2, /** - * The padding for the domain of the scale (see {@link bar#scale}) - * @param {number} [domainPadding=0.5] - * @memberof bar# + * The min radius a point can have + * @param {function} [maxRadius=10] + * @memberof scatter# * @property */ - domainPadding = 0.5, + maxRadius = 10, /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link bar#orient}), where {@link bar#orient}="horizontal" - * the main dimension is {@link bar#spaceX} and where {@link bar#orient}="vertical" - * the main dimension is {@link bar#spaceY} between bars - * @param {number} [objectSpacer=0.05] - * @memberof bar# - * @property - */ - objectSpacer = 0.05, - /** - * The minimum size that an object can be - * @param {number} [minObjectSize=50] - * @memberof bar# - * @property - */ - minObjectSize = 50, - /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof bar# - * @property - */ - maxObjectSize = 100, - - /** - * The stroke width of the bars - * @param {number} [barStrokeWidth=2] - * @memberof bar# + * The stroke width of the points + * @param {number} [pointStrokeWidth=2] + * @memberof scatter# * @property */ - barStrokeWidth = 2, + pointStrokeWidth = 2, /** * Instance of ColorFunction * @param {function} [colorFunction = colorFunction()] - * @memberof bar# + * @memberof scatter# * @property */ colorFunction$$1 = colorFunction(), - - /** * Color of the background * @param {string} [backgroundFill="transparent"] - * @memberof bar# + * @memberof scatter# * @property */ backgroundFill = 'transparent', /** - * Namespace for all items made by this instance of bar - * @param {string} [namespace="d3sm-bar"] - * @memberof bar# + * Namespace for all items made by this instance of scatter + * @param {string} [namespace="d3sm-scatter"] + * @memberof scatter# * @property */ - namespace = 'd3sm-bar', + namespace = 'd3sm-scatter', /** - * Class name for bar container ( element) - * @param {string} [objectClass="bar"] - * @memberof bar# + * Class name for scatter container ( element) + * @param {string} [objectClass="scatter-point"] + * @memberof scatter# * @property */ - objectClass = 'bar', - + objectClass = 'scatter-point', /** * Duration of all transitions of this element * @param {number} [transitionDuration=1000] - * @memberof bar# + * @memberof scatter# * @property */ transitionDuration = 1000, /** * Easing function for transitions * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof bar# + * @memberof scatter# * @property */ easeFunc = d3.easeExp, // useful values to extract to prevent re-calculation /** - * The keys of the bars - * @param {string[]} [barKeys=undefined] - * @memberof bar# + * The keys of the points + * @param {string[]} [pointKeys=undefined] + * @memberof scatter# * @property */ - barKeys, + pointKeys, /** - * The values of the bars - * @param {number[]} [barValues=undefined] - * @memberof bar# + * The x values of the points + * @param {number[]} [valuesX=undefined] + * @memberof scatter# * @property */ - barValues, + valuesX, /** - * The objectSize (actual width) used by the bars - * @param {number} [objectSize=undefined] - * @memberof bar# + * The y values of the points + * @param {number[]} [valuesY=undefined] + * @memberof scatter# * @property */ - objectSize, + valuesY, /** - * The spacerSize (actual width) used by the spacers between the bars - * @param {number} [spacerSize=undefined] - * @memberof bar# + * The r values of the points + * @param {number[]} [valuesR=undefined] + * @memberof scatter# * @property */ - spacerSize, + valuesR, + /** * Instance of Tooltip * @param {function} [tooltip=tooltip()] - * @memberof bar# + * @memberof scatter# * @property */ - tooltip$$1 = tooltip(), - barPercent = 1; + tooltip$$1 = tooltip(); /** * Gets or sets the selection in which items are manipulated * @param {d3.selection} [_=none] - * @returns {bar | d3.selection} - * @memberof bar + * @returns {scatter | d3.selection} + * @memberof scatter * @property * by default selection = selection */ - bar.selection = function(_) { return arguments.length ? (selection = _, bar) : selection; }; + scatter.selection = function(_) { return arguments.length ? (selection =_, scatter) : selection}; /** * Gets or sets the data - * (see {@link bar#data}) - * @param {number} [_=none] - * @returns {bar | object} - * @memberof bar - * @property - */ - bar.data = function(_) { return arguments.length ? (data = _, bar) : data; }; - /** - * Gets or sets the orient of the bars - * (see {@link bar#orient}) + * (see {@link scatter#data}) * @param {number} [_=none] - * @returns {bar | object} - * @memberof bar + * @returns {scatter | object} + * @memberof scatter * @property */ - bar.orient = function(_) { return arguments.length ? (orient = _, bar) : orient; }; + scatter.data = function(_) { return arguments.length ? (data =_, scatter) : data}; /** * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link bar#spaceX}) + * (see {@link scatter#spaceX}) * @param {number} [_=none] should be a number > 0 - * @returns {bar | number} - * @memberof bar + * @returns {scatter | number} + * @memberof scatter * @property * by default spaceX = undefined */ - bar.spaceX = function(_) { return arguments.length ? (spaceX = _, bar) : spaceX; }; + scatter.spaceX = function(_) { return arguments.length ? (spaceX =_, scatter) : spaceX}; /** * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link bar#spaceY}) + * (see {@link scatter#spaceY}) * @param {number} [_=none] should be a number > 0 - * @returns {bar | number} - * @memberof bar + * @returns {scatter | number} + * @memberof scatter * @property * by default spaceY = undefined */ - bar.spaceY = function(_) { return arguments.length ? (spaceY = _, bar) : spaceY; }; + scatter.spaceY = function(_) { return arguments.length ? (spaceY =_, scatter) : spaceY}; + + /** - * Gets / sets whether or not bar is allowed to go beyond specified dimensions - * (see {@link bar#spaceX}) - * @param {boolean} [_=none] - * @returns {bar | boolean} - * @memberof bar - * @property - * by default overflowQ = false - */ - bar.overflowQ = function(_) { return arguments.length ? (overflowQ = _, bar) : overflowQ; }; - /** - * Gets / sets the grouping of the bars - * (see {@link bar#grouping}) - * @param {Array[]} [_=none] - * @returns {bar | Array[]} - * @memberof bar + * Gets / sets the x scale for which the scatter x values should be transformed by + * (see {@link scatter#scaleX}) + * @param {d3.scale} [_=none] + * @returns {scatter | d3.scale} + * @memberof scatter * @property - * by default grouping = undefined + * by default scaleX = d3.scaleLinear() */ - bar.grouping = function(_) { return arguments.length ? (grouping = _, bar) : grouping; }; + scatter.scaleX = function(_) { return arguments.length ? (scaleX =_, scatter) : scaleX}; /** - * Gets / sets the valueExtractor - * (see {@link bar#valueExtractor}) - * @param {function} [_=none] - * @returns {bar | function} - * @memberof bar + * Gets / sets the padding for the domain of the x scale + * (see {@link scatter#domainPaddingX}) + * @param {number} [_=none] + * @returns {scatter | number} + * @memberof scatter * @property - * by default valueExtractor = function(key, index) { return data[key] }, + * by default domainPaddingX = 0.5 */ - bar.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, bar) : valueExtractor; }; + scatter.domainPaddingX = function(_) { return arguments.length ? (domainPaddingX =_, scatter) : domainPaddingX}; /** - * Gets / sets the sortingFunction - * (see {@link bar#sortingFunction}) + * Gets / sets the valueExtractorX + * (see {@link scatter#valueExtractorX}) * @param {function} [_=none] - * @returns {bar | function} - * @memberof bar + * @returns {scatter | function} + * @memberof scatter * @property - * by default sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, + * by default valueExtractorX = function(key, index) { return data[key]['x'] } */ - bar.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, bar) : sortingFunction; }; + scatter.valueExtractorX = function(_) { return arguments.length ? (valueExtractorX =_, scatter) : valueExtractorX}; + + /** - * Gets / sets the scale for which the bar values should be transformed by - * (see {@link bar#scale}) + * Gets / sets the y scale for which the scatter y values should be transformed by + * (see {@link scatter#scaleY}) * @param {d3.scale} [_=none] - * @returns {bar | d3.scale} - * @memberof bar + * @returns {scatter | d3.scale} + * @memberof scatter * @property - * by default scale = d3.scaleLinear() + * by default scaleY = d3.scaleLinear() */ - bar.scale = function(_) { return arguments.length ? (scale = _, bar) : scale; }; + scatter.scaleY = function(_) { return arguments.length ? (scaleY =_, scatter) : scaleY}; /** - * Gets / sets the padding for the domain of the scale - * (see {@link bar#domainPadding}) + * Gets / sets the padding for the domain of the y scale + * (see {@link scatter#domainPaddingY}) * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar + * @returns {scatter | number} + * @memberof scatter * @property - * by default domainPadding = 0.5 + * by default domainPaddingY = 0.5 */ - bar.domainPadding = function(_) { return arguments.length ? (domainPadding = _, bar) : domainPadding; }; + scatter.domainPaddingY = function(_) { return arguments.length ? (domainPaddingY =_, scatter) : domainPaddingY}; /** - * Gets / sets objectSpacer - * (see {@link bar#objectSpacer}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar + * Gets / sets the valueExtractorY + * (see {@link scatter#valueExtractorY}) + * @param {function} [_=none] + * @returns {scatter | function} + * @memberof scatter * @property - * by default objectSpacer = 0.05 + * by default valueExtractorY = function(key, index) { return data[key]['y'] } */ - bar.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, bar) : objectSpacer; }; + scatter.valueExtractorY = function(_) { return arguments.length ? (valueExtractorY =_, scatter) : valueExtractorY}; + + /** - * Gets / sets the minObjectSize - * (see {@link bar#minObjectSize}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar + * Gets / sets the r scale for which the scatter r values should be transformed by + * (see {@link scatter#scaleR}) + * @param {d3.scale} [_=none] + * @returns {scatter | d3.scale} + * @memberof scatter * @property - * by default minObjectSize = 50 + * by default scaleR = d3.scaleLinear() */ - bar.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, bar) : minObjectSize; }; + scatter.scaleR = function(_) { return arguments.length ? (scaleR =_, scatter) : scaleR}; /** - * Gets / sets the maxObjectSize - * (see {@link bar#maxObjectSize}) + * Gets / sets the padding for the domain of the r scale + * (see {@link scatter#domainPaddingR}) * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar + * @returns {scatter | number} + * @memberof scatter * @property - * by default maxObjectSize = 100 + * by default domainPaddingR = 0.5 */ - bar.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, bar) : maxObjectSize; }; - + scatter.domainPaddingR = function(_) { return arguments.length ? (domainPaddingR =_, scatter) : domainPaddingR}; /** - * Gets / sets the barStrokeWidth - * (see {@link bar#barStrokeWidth}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar - * @property - * by default barStrokeWidth = 2 + * Gets / sets the valueExtractorR + * (see {@link scatter#valueExtractorR}) + * @param {function} [_=none] + * @returns {scatter | function} + * @memberof scatter + * @property + * by default valueExtractorR = function(key, index) { return data[key]['r'] } */ - bar.barStrokeWidth = function(_) { return arguments.length ? (barStrokeWidth = _, bar) : barStrokeWidth; }; + scatter.valueExtractorR = function(_) { return arguments.length ? (valueExtractorR =_, scatter) : valueExtractorR}; + /** + * Gets / sets the minRadius + * (see {@link scatter#minRadius}) + * @param {number} [_=none] + * @returns {scatter | number} + * @memberof scatter + * @property + * by default minRadius = 2 + */ + scatter.minRadius = function(_) { return arguments.length ? (minRadius =_, scatter) : minRadius}; + /** + * Gets / sets the maxRadius + * (see {@link scatter#maxRadius}) + * @param {number} [_=none] + * @returns {scatter | number} + * @memberof scatter + * @property + * by default maxRadius = 10 + */ + scatter.maxRadius = function(_) { return arguments.length ? (maxRadius =_, scatter) : maxRadius}; + + /** + * Gets / sets the pointStrokeWidth + * (see {@link scatter#pointStrokeWidth}) + * @param {number} [_=none] + * @returns {scatter | number} + * @memberof scatter + * @property + * by default pointStrokeWidth = 2 + */ + scatter.pointStrokeWidth = function(_) { return arguments.length ? (pointStrokeWidth =_, scatter) : pointStrokeWidth}; /** * Gets / sets the colorFunction - * (see {@link bar#colorFunction}) + * (see {@link scatter#colorFunction}) * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar + * @returns {scatter | number} + * @memberof scatter * @property * by default colorFunction = colorFunction() */ - bar.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, bar) : colorFunction$$1; }; - + scatter.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 =_, scatter) : colorFunction$$1}; /** * Gets / sets the backgroundFill - * (see {@link bar#backgroundFill}) + * (see {@link scatter#backgroundFill}) * @param {string} [_=none] - * @returns {bar | string} - * @memberof bar + * @returns {scatter | string} + * @memberof scatter * @property * by default backgroundFill = 'transparent' */ - bar.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, bar) : backgroundFill; }; + scatter.backgroundFill = function(_) { return arguments.length ? (backgroundFill =_, scatter) : backgroundFill}; /** * Gets / sets the namespace - * (see {@link bar#namespace}) + * (see {@link scatter#namespace}) * @param {string} [_=none] - * @returns {bar | string} - * @memberof bar + * @returns {scatter | string} + * @memberof scatter * @property - * by default namespace = 'd3sm-bar' + * by default namespace = 'd3sm-scatter' */ - bar.namespace = function(_) { return arguments.length ? (namespace = _, bar) : namespace; }; + scatter.namespace = function(_) { return arguments.length ? (namespace =_, scatter) : namespace}; /** * Gets / sets the objectClass - * (see {@link bar#objectClass}) + * (see {@link scatter#objectClass}) * @param {string} [_=none] - * @returns {bar | string} - * @memberof bar + * @returns {scatter | string} + * @memberof scatter * @property * by default objectClass = 'tick-group' */ - bar.objectClass = function(_) { return arguments.length ? (objectClass = _, bar) : objectClass; }; + scatter.objectClass = function(_) { return arguments.length ? (objectClass =_, scatter) : objectClass}; /** * Gets / sets the transitionDuration - * (see {@link bar#transitionDuration}) + * (see {@link scatter#transitionDuration}) * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar + * @returns {scatter | number} + * @memberof scatter * @property * by default transitionDuration = 1000 */ - bar.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, bar) : transitionDuration; }; + scatter.transitionDuration = function(_) { return arguments.length ? (transitionDuration =_, scatter) : transitionDuration}; /** * Gets / sets the easeFunc - * (see {@link bar#easeFunc}) + * (see {@link scatter#easeFunc}) * @param {d3.ease} [_=none] - * @returns {bar | d3.ease} - * @memberof bar + * @returns {scatter | d3.ease} + * @memberof scatter * @property * by default easeFunc = d3.easeExp */ - bar.easeFunc = function(_) { return arguments.length ? (easeFunc = _, bar) : easeFunc; }; - + scatter.easeFunc = function(_) { return arguments.length ? (easeFunc =_, scatter) : easeFunc}; /** - * Gets / sets the barKeys - * (see {@link bar#barKeys}) + * Gets / sets the pointKeys + * (see {@link scatter#pointKeys}) * @param {string[]} [_=none] - * @returns {bar | string[]} - * @memberof bar + * @returns {scatter | string[]} + * @memberof scatter * @property - * by default barKeys = undefined + * by default pointKeys = undefined */ - bar.barKeys = function(_) { return arguments.length ? (barKeys = _, bar) : barKeys; }; + scatter.pointKeys = function(_) { return arguments.length ? (pointKeys =_, scatter) : pointKeys}; /** - * Gets / sets the barValues - * (see {@link bar#barValues}) + * Gets / sets the valuesX + * (see {@link scatter#valuesX}) * @param {number[]} [_=none] - * @returns {bar | number[]} - * @memberof bar + * @returns {scatter | number[]} + * @memberof scatter * @property - * by default barValues = undefined + * by default valuesX = undefined */ - bar.barValues = function(_) { return arguments.length ? (barValues = _, bar) : barValues; }; + scatter.valuesX = function(_) { return arguments.length ? (valuesX =_, scatter) : valuesX}; /** - * Gets / sets the objectSize - * (see {@link bar#objectSize}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar + * Gets / sets the valuesY + * (see {@link scatter#valuesY}) + * @param {number[]} [_=none] + * @returns {scatter | number[]} + * @memberof scatter * @property - * by default objectSize = undefined + * by default valuesY = undefined */ - bar.objectSize = function(_) { return arguments.length ? (objectSize = _, bar) : objectSize; }; + scatter.valuesY = function(_) { return arguments.length ? (valuesY =_, scatter) : valuesY}; /** - * Gets / sets the spacerSize - * (see {@link bar#spacerSize}) - * @param {number} [_=none] - * @returns {bar | number} - * @memberof bar + * Gets / sets the valuesR + * (see {@link scatter#valuesR}) + * @param {number[]} [_=none] + * @returns {scatter | number[]} + * @memberof scatter * @property - * by default spacerSize = undefined + * by default valuesR = undefined */ - bar.spacerSize = function(_) { return arguments.length ? (spacerSize = _, bar) : spacerSize; }; - + scatter.valuesR = function(_) { return arguments.length ? (valuesR =_, scatter) : valuesR}; /** * Gets / sets the tooltip - * (see {@link bar#tooltip}) + * (see {@link scatter#tooltip}) * @param {tooltip} [_=none] - * @returns {bar | tooltip} - * @memberof bar + * @returns {scatter | tooltip} + * @memberof scatter * @property * by default tooltip = tooltip() */ - bar.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, bar) : tooltip$$1; }; - bar.barPercent = function(_) { return arguments.length ? (barPercent = _, bar) : barPercent; }; + scatter.tooltip = function(_) { return arguments.length ? (tooltip$$1 =_, scatter) : tooltip$$1}; - function bar() { - // for convenience in handling orientation specific values - var horizontalQ = (orient == 'horizontal' || orient == 'bottom' || orient == 'top') ? true : false; - var verticalQ = !horizontalQ; + function scatter() { // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); - - // to prevent re-calculation and getters to be passed to axes - barKeys = d3.keys(data); - barValues = barKeys.map(valueExtractor); - - // if grouping is undefined sort barKeys by sortingFunction - var ordered = (grouping == undefined) ? barKeys.sort(sortingFunction) : grouping; - // ordered might be nested depending on grouping - barKeys = flatten(ordered); - - var numberOfObjects = barKeys.length; - var extent = [Math.min(...barValues) - domainPadding,Math.max(...barValues) + domainPadding]; + var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + pointKeys = d3.keys(data); + valuesX = pointKeys.map(valueExtractorX); + valuesY = pointKeys.map(valueExtractorY); + valuesR = pointKeys.map(valueExtractorR); - // set the scale + var numberOfObjects = pointKeys.length; + var extentX = [Math.min(...valuesX) - domainPaddingX, Math.max(...valuesX) + domainPaddingX]; + var extentY = [Math.min(...valuesY) - domainPaddingY, Math.max(...valuesY) + domainPaddingY]; + var extentR = [Math.min(...valuesR) - domainPaddingR, Math.max(...valuesR) + domainPaddingR]; - scale.domain(extent).range(horizontalQ - ? [0,spaceY] - : orient == 'right' - ? [0, spaceX] - : [spaceX, 0] - ); - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - objectSize = (objectSize == undefined) - ? calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) - : objectSize; + scaleX.domain(extentX).range([0, spaceX]); + scaleY.domain(extentY).range([spaceY, 0]); + scaleR.domain(extentR).range([minRadius, maxRadius]); - // calculate spacer size if needed - spacerSize = (spacerSize == undefined) - ? calculateWidthOfSpacer(barKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) - : spacerSize; - // make the nested groups - var spacerFunction = groupingSpacer() - .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) - .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc) - .namespace(namespace); - // safe default function - var defaultExit = spacerFunction.exitFunction(); + var points = container.selectAll('.'+objectClass); + points = points.data(pointKeys); + var pEnter = points.enter().append('circle') + .attr('class', objectClass) + .attr('cx', 0).attr('cy', spaceY).attr('r', 0); - spacerFunction.exitFunction(function(sel){ - // use default to move objects off screen - // console.log("EXIT", sel.nodes(), objectSize, scale(extent[1])) - if (objectSize == undefined) {console.log(sel.nodes(), objectSize);} - defaultExit(sel); - sel.selectAll('g').classed("to-remove", true); - // shrink rectangles in addition - sel.selectAll('* > rect') - .transition().duration(transitionDuration) - .attr('transform', function(d, i) { - var - x = horizontalQ - ? 0 - : 0 - , - y = verticalQ - ? 0 - : scale(extent[1]) - , - t = 'translate('+x+','+y+')'; - return t - }) - .attr('width', horizontalQ ? objectSize : 0) - .attr('height', verticalQ ? objectSize : 0) - .remove(); - }); + var pExit = points.exit(); + points = points.merge(pEnter); - // move stuff - spacerFunction(container, ordered, 0); + points.each(function(key, i){ + var t = d3.select(this), + currentData = data[key], + x = valuesX[i], + y = valuesY[i], + r = valuesR[i], + fillColor = colorFunction$$1(key, currentData, i, 'fill'), + strokeColor = colorFunction$$1(key, currentData, i, 'stroke'); + t.transition().duration(transitionDuration).ease(easeFunc) + .attr('cx', scaleX(x)) + .attr('cy', scaleY(y)) + .attr('r', scaleR(r)) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', pointStrokeWidth); + t.on('mouseover', function(d, i){ + points.style('opacity', 0.2); + t.style('opacity', 1); + t.transition().duration(transitionDuration/2).ease(easeFunc) + .attr('stroke-width', pointStrokeWidth*2) + .attr('r', scaleR(r) * 1.5); - var parentIndexArray = []; - container.selectAll('g:not(.to-remove).'+objectClass) - .each(function(d, i){parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}); - - - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' - ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) - : colorFunction$$1.dataExtent(extent); - - - - container.selectAll('g.'+objectClass+':not(.to-remove)').each(function(key, i) { - // console.log(key, scale(extent[1]) - scale(valueExtractor(key, i))) - var t = d3.select(this), - currentData = data[key], - value = valueExtractor(key, i), - i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, value, i, 'fill'), // prevent duplicate computation - strokeColor = colorFunction$$1(key, value, i, 'stroke'); - - - var bar = safeSelect(t, 'rect', 'bar-rect'); - - if (bar.attr('transform') == undefined) { - bar.attr('transform', function(d, i) { - var - x = horizontalQ - ? 0 - : 0 - , - y = verticalQ - ? 0 - : scale(extent[1]) - , - t = 'translate('+x+','+y+')'; - return t - }) - .attr('width', horizontalQ ? objectSize : 0) - .attr('height', verticalQ ? objectSize : 0); - - } - + }); + t.node().addEventListener('mouseout', function(){ + container.selectAll('.'+objectClass).style('opacity', 1); + t.transition().duration(transitionDuration/2).ease(easeFunc) + .attr('stroke-width', pointStrokeWidth) + .attr('r', scaleR(r)); - bar.transition().duration(transitionDuration).ease(easeFunc) - .attr('transform', function(d, i) { - var - x = horizontalQ - ? objectSize - objectSize * barPercent - : orient == 'right' - ? scale(extent[1]) - scale(value) - : objectSize - objectSize * barPercent - , - y = verticalQ - ? objectSize - objectSize * barPercent - : scale(extent[1]) - scale(value) - , - t = 'translate('+x+','+y+')'; - return t - }) - .attr('width', horizontalQ ? objectSize * barPercent : scale(value)) - .attr('height', verticalQ ? objectSize * barPercent: scale(value)) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', barStrokeWidth); + }); + }); - t.on('mouseover', function(d, i){ - container.selectAll('g.'+objectClass).style('opacity', 0.2); - t.style('opacity', 1); - bar.attr('stroke-width',barStrokeWidth*2); - }); - t.on('mouseout', function(){ - container.selectAll('g.'+objectClass).style('opacity', 1); - bar.attr('stroke-width', barStrokeWidth); - }); - }); + pExit.transition().duration(transitionDuration).ease(easeFunc) + .attr('cx', 0).attr('cy', spaceY).attr('r', 0) + .remove(); - tooltip$$1.selection(container.selectAll('.bar-rect')) + tooltip$$1.selection(points) .data(data); tooltip$$1(); - } - return bar + + + return scatter } +/******************************************************************************* +** ** +** ** +** BAR ** +** ** +** ** +*******************************************************************************/ + /** - * Creates a bubbleHeatmap + * Creates a bar * - * {@link https://sumneuron.gitlab.io/d3sm/demos/bubble-heatmap/index.html Demo} - * @constructor bubbleHeatmap + * {@link https://sumneuron.gitlab.io/d3sm/demos/bar-chart-same-data-complex-grouping/index.html Demo} + * @constructor bar * @param {d3.selection} selection - * @namespace bubbleHeatmap - * @returns {function} bubbleHeatmap + * @namespace bar + * @returns {function} bar */ -function bubbleHeatmap( selection ) { - var +function bar ( selection ) { + /* + Assumes that data is list an object. + + The keys of data will be bound to the bars. + The valueExtractor function will extract the value from data[key]. + + Grouping can be used if desired. It should be an arbitrary complex list where + the values are strings matching keys in data. + */ + var /** - * Data to plot. Assumed to be a object, where each key corresponds to a cell - * (see {@link bubbleHeatmap#data}) + * Data to plot. Assumed to be a object, where each key corresponds to a bar + * (see {@link bar#data}) * @param {Object} [data=undefined] - * @memberof bubbleHeatmap# + * @memberof bar# * @property */ data, - /** - * Amount of horizontal space (in pixels) avaible to render the bubbleHeatmap in - * (see {@link bubbleHeatmap#spaceX}) + * Which direction to render the bars in + * (see {@link bar#orient}) + * @param {number} [orient='horizontal'] + * @memberof bar# + * @property + */ + orient='horizontal', + /** + * Amount of horizontal space (in pixels) avaible to render the bar in + * (see {@link bar#spaceX}) * @param {number} [spaceX=undefined] - * @memberof bubbleHeatmap# + * @memberof bar# * @property */ spaceX, /** - * Amount of vertical space (in pixels) avaible to render the bubbleHeatmap in - * (see {@link bubbleHeatmap.spaceY}) + * Amount of vertical space (in pixels) avaible to render the bar in + * (see {@link bar.spaceY}) * @param {number} [spaceY=undefined] - * @memberof bubbleHeatmap# + * @memberof bar# * @property */ spaceY, /** - * The internal key of the cell specifiying to which x axis key it belongs - * (see {@link bubbleHeatmap.xKey}) - * @param {string} [xKey='x'] - * @memberof bubbleHeatmap# + * Whether or not to allow bar to render elements pass the main spatial dimension + * given the orientation (see {@link bar#orient}), where {@link bar#orient}="horizontal" + * the main dimension is {@link bar#spaceX} and where {@link bar#orient}="vertical" + * the main dimension is {@link bar#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof bar# * @property */ - xKey = 'x', + overflowQ = false, + /** - * The internal key of the cell specifiying to which y axis key it belongs - * (see {@link bubbleHeatmap.yKey}) - * @param {string} [yKey='y'] - * @memberof bubbleHeatmap# + * An array - putatively of other arrays - depicting how bars should be arranged + * @param {Array[]} [grouping=undefined] + * @memberof bar# * @property */ - yKey = 'y', + grouping, + /** - * The internal key of the cell specifiying what value to use to determine the radius - * (see {@link bubbleHeatmap.rKey}) - * @param {string} [rKey='r'] - * @memberof bubbleHeatmap# + * How to get the value of the bar + * @param {function} [valueExtractor=function(key, index) { return data[key] }] + * @memberof bar# * @property */ - rKey = 'r', + valueExtractor = function(key, index) { return data[key] }, /** - * The internal key of the cell specifiying what value to use to determine the color - * (see {@link bubbleHeatmap.vKey}) - * @param {string} [vKey='v'] - * @memberof bubbleHeatmap# + * How to sort the bars - if {@link bar#grouping} is not provided. + * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] + * @memberof bar# * @property */ - vKey = 'v', + sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, /** - * Function for extracting the the value from xKey. - * (see {@link bubbleHeatmap.xExtractor}) - * @param {function} [xExtractor=function(key, i) { return data[key][xKey] }] - * @returns {string} - * @memberof bubbleHeatmap# + * The scale for which bar values should be transformed by + * @param {d3.scale} [scale=d3.scaleLinear] + * @memberof bar# * @property */ - xExtractor = function(key, i) {return data[key][xKey] }, + scale = d3.scaleLinear(), /** - * Function for extracting the the value from yKey. - * (see {@link bubbleHeatmap.yExtractor}) - * @param {function} [yExtractor=function(key, i) { return data[key][yKey] }] - * @returns {string} - * @memberof bubbleHeatmap# + * The padding for the domain of the scale (see {@link bar#scale}) + * @param {number} [domainPadding=0.5] + * @memberof bar# * @property */ - yExtractor = function(key, i) { return data[key][yKey] }, + domainPadding = 0.5, + /** - * Function for extracting the the value from rKey. - * (see {@link bubbleHeatmap.rExtractor}) - * @param {function} [rExtractor=function(key, i) { return data[key][rKey] }] - * @returns {number} - * @memberof bubbleHeatmap# + * Default space for the spacer (percentage) of main dimension given the orientation + * (see {@link bar#orient}), where {@link bar#orient}="horizontal" + * the main dimension is {@link bar#spaceX} and where {@link bar#orient}="vertical" + * the main dimension is {@link bar#spaceY} between bars + * @param {number} [objectSpacer=0.05] + * @memberof bar# * @property */ - rExtractor = function(key, i) { return data[key][rKey] }, + objectSpacer = 0.05, /** - * Function for extracting the the value from vKey. - * (see {@link bubbleHeatmap.vExtractor}) - * @param {function} [vExtractor=function(key, i) { return data[key][vKey] }] - * @returns {number} - * @memberof bubbleHeatmap# + * The minimum size that an object can be + * @param {number} [minObjectSize=50] + * @memberof bar# * @property */ - vExtractor = function(key, i) { return data[key][vKey] }, - - + minObjectSize = 50, /** - * Whether or not to allow bubbleHeatmap to render elements pass the main spatial dimension - * given the orientation (see {@link bubbleHeatmap#orient}), where {@link bubbleHeatmap#orient}="bottom" or {@link bubbleHeatmap#orient}="top" - * the main dimension is {@link bubbleHeatmap#spaceX} and where {@link bubbleHeatmap#orient}="left" or {@link bubbleHeatmap#orient}="right" - * the main dimension is {@link bubbleHeatmap#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof bubbleHeatmap# + * The maximum size that an object can be + * @param {number} [maxObjectSize=100] + * @memberof bar# * @property */ - overflowQ = false, + maxObjectSize = 100, /** - * The scale for which the radius values should be transformed by - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof bubbleHeatmap# + * The stroke width of the bars + * @param {number} [barStrokeWidth=2] + * @memberof bar# * @property */ - scale = d3.scaleLinear(), + barStrokeWidth = 2, /** - * The padding for the domain of the scale (see {@link bubbleHeatmap#scale}) - * @param {number} [domainPadding=0.5] - * @memberof bubbleHeatmap# - * @property - */ - domainPadding = 0.5, - - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link bubbleHeatmap#orient}), where {@link bubbleHeatmap#orient}="horizontal" - * the main dimension is {@link bubbleHeatmap#spaceX} and where {@link bubbleHeatmap#orient}="vertical" - * the main dimension is {@link bubbleHeatmap#spaceY} between bubbles - * @param {number} [objectSpacer=0.0] - * @memberof bubbleHeatmap# - * @property - */ - objectSpacer = 0.0, - /** - * The minimum size that an object can be - * @param {number} [minObjectSize=50] - * @memberof bubbleHeatmap# - * @property - */ - minObjectSize = 50, - /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof bubbleHeatmap# + * Instance of ColorFunction + * @param {function} [colorFunction = colorFunction()] + * @memberof bar# * @property */ - maxObjectSize = 100, - + colorFunction$$1 = colorFunction(), - /** - * The stroke width of the bubbles - * @param {number} [bubbleStrokeWidth=2] - * @memberof bubbleHeatmap# - * @property - */ - bubbleStrokeWidth = 2, - // colorFunc = colorFunction(), /** * Color of the background * @param {string} [backgroundFill="transparent"] - * @memberof bubbleHeatmap# + * @memberof bar# * @property */ backgroundFill = 'transparent', /** - * Namespace for all items made by this instance of bubbleHeatmap - * @param {string} [namespace="d3sm-bubble"] - * @memberof bubbleHeatmap# + * Namespace for all items made by this instance of bar + * @param {string} [namespace="d3sm-bar"] + * @memberof bar# * @property */ - namespace = 'd3sm-bubble', + namespace = 'd3sm-bar', /** - * Class name for bubble container ( element) - * @param {string} [objectClass="bubble"] - * @memberof bubbleHeatmap# + * Class name for bar container ( element) + * @param {string} [objectClass="bar"] + * @memberof bar# * @property */ - objectClass = 'bubble', + objectClass = 'bar', + /** * Duration of all transitions of this element * @param {number} [transitionDuration=1000] - * @memberof bubbleHeatmap# + * @memberof bar# * @property */ transitionDuration = 1000, /** * Easing function for transitions * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof bubbleHeatmap# + * @memberof bar# * @property */ easeFunc = d3.easeExp, + // useful values to extract to prevent re-calculation /** - * Stores the keys of all the cells - * Calculated after bubbleHeatmap called. - * @param {string[]} [cellKeys=undefined] - * @memberof bubbleHeatmap# - * @property - */ - cellKeys, - /** - * Stores the list of unique xValues - * Calculated after bubbleHeatmap called. - * @param {string[]} [xValues=undefined] - * @memberof bubbleHeatmap# - * @property - */ - xValues, - /** - * Stores the list of unique yValues - * Calculated after bubbleHeatmap called. - * @param {string[]} [yValues=undefined] - * @memberof bubbleHeatmap# + * The keys of the bars + * @param {string[]} [barKeys=undefined] + * @memberof bar# * @property */ - yValues, + barKeys, /** - * Stores the list of unique rValues - * Calculated after bubbleHeatmap called. - * @param {string[]} [rValues=undefined] - * @memberof bubbleHeatmap# + * The values of the bars + * @param {number[]} [barValues=undefined] + * @memberof bar# * @property */ - rValues, + barValues, /** - * Stores the list of unique vValues - * Calculated after bubbleHeatmap called. - * @param {string[]} [vValues=undefined] - * @memberof bubbleHeatmap# + * The objectSize (actual width) used by the bars + * @param {number} [objectSize=undefined] + * @memberof bar# * @property */ - vValues, - - xKeySortingFunction = function(a, b) { return xExtractor(a) - xExtractor(b) }, - yKeySortingFunction = function(a, b) { return yExtractor(a) - yExtractor(b) }, + objectSize, /** - * Instance of ColorFunction with .colorBy set to 'category' - * @function colorFunction - * @memberof bubbleHeatmap# + * The spacerSize (actual width) used by the spacers between the bars + * @param {number} [spacerSize=undefined] + * @memberof bar# * @property */ - colorFunction$$1 = colorFunction().colorBy('value'), + spacerSize, /** * Instance of Tooltip - * @function tooltip - * @memberof bubbleHeatmap# + * @param {function} [tooltip=tooltip()] + * @memberof bar# * @property */ tooltip$$1 = tooltip(), - - /** - * store the size the bubble could be in the x dimension - * the actuall size of the bubble will be the min of xSize and {@link bubbleHeatmap#ySize} - * Calculated after bubbleHeatmap called. - * @param {string[]} [xSize=undefined] - * @memberof bubbleHeatmap# - * @property - */ - xSize, - /** - * store the size of the spacer in the x dimension - * Calculated after bubbleHeatmap called. - * @param {string[]} [xSpacerSize=undefined] - * @memberof bubbleHeatmap# - * @property - */ - xSpacerSize, - - /** - * store the size the bubble could be in the y dimension - * the actuall size of the bubble will be the min of xSize and {@link bubbleHeatmap#xSize} - * Calculated after bubbleHeatmap called. - * @param {string[]} [ySize=undefined] - * @memberof bubbleHeatmap# - * @property - */ - ySize, - /** - * store the size of the spacer in the y dimension. - * Calculated after bubbleHeatmap called. - * @param {string[]} [xSpacerSize=undefined] - * @memberof bubbleHeatmap# - * @property - */ - ySpacerSize; + barPercent = 1; /** * Gets or sets the selection in which items are manipulated * @param {d3.selection} [_=none] - * @returns {bubbleHeatmap | d3.selection} - * @memberof bubbleHeatmap + * @returns {bar | d3.selection} + * @memberof bar * @property * by default selection = selection */ - bhm.selection = function(_) { return arguments.length ? (selection = _, bhm) : selection; }; + bar.selection = function(_) { return arguments.length ? (selection = _, bar) : selection; }; /** * Gets or sets the data - * (see {@link bubbleHeatmap#data}) + * (see {@link bar#data}) * @param {number} [_=none] - * @returns {bubbleHeatmap | object} - * @memberof bubbleHeatmap + * @returns {bar | object} + * @memberof bar * @property */ - bhm.data = function(_) { return arguments.length ? (data = _, bhm) : data; }; - // bhm.orient = function(_) { return arguments.length ? (orient = _, bhm) : orient; } + bar.data = function(_) { return arguments.length ? (data = _, bar) : data; }; + /** + * Gets or sets the orient of the bars + * (see {@link bar#orient}) + * @param {number} [_=none] + * @returns {bar | object} + * @memberof bar + * @property + */ + bar.orient = function(_) { return arguments.length ? (orient = _, bar) : orient; }; /** * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link bubbleHeatmap#spaceX}) + * (see {@link bar#spaceX}) * @param {number} [_=none] should be a number > 0 - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * @returns {bar | number} + * @memberof bar * @property * by default spaceX = undefined */ - bhm.spaceX = function(_) { return arguments.length ? (spaceX = _, bhm) : spaceX; }; + bar.spaceX = function(_) { return arguments.length ? (spaceX = _, bar) : spaceX; }; /** * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link bubbleHeatmap#spaceY}) + * (see {@link bar#spaceY}) * @param {number} [_=none] should be a number > 0 - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * @returns {bar | number} + * @memberof bar * @property * by default spaceY = undefined */ - bhm.spaceY = function(_) { return arguments.length ? (spaceY = _, bhm) : spaceY; }; + bar.spaceY = function(_) { return arguments.length ? (spaceY = _, bar) : spaceY; }; /** - * Gets or sets the xKey - * (see {@link bubbleHeatmap#xKey}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap + * Gets / sets whether or not bar is allowed to go beyond specified dimensions + * (see {@link bar#spaceX}) + * @param {boolean} [_=none] + * @returns {bar | boolean} + * @memberof bar * @property - * by default xKey = 'x' + * by default overflowQ = false */ - bhm.xKey = function(_) { return arguments.length ? (xKey = _, bhm) : xKey; }; + bar.overflowQ = function(_) { return arguments.length ? (overflowQ = _, bar) : overflowQ; }; /** - * Gets or sets the yKey - * (see {@link bubbleHeatmap#yKey}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap + * Gets / sets the grouping of the bars + * (see {@link bar#grouping}) + * @param {Array[]} [_=none] + * @returns {bar | Array[]} + * @memberof bar * @property - * by default yKey = 'y' + * by default grouping = undefined */ - bhm.yKey = function(_) { return arguments.length ? (yKey = _, bhm) : yKey; }; + bar.grouping = function(_) { return arguments.length ? (grouping = _, bar) : grouping; }; /** - * Gets or sets the rKey - * (see {@link bubbleHeatmap#rKey}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap + * Gets / sets the valueExtractor + * (see {@link bar#valueExtractor}) + * @param {function} [_=none] + * @returns {bar | function} + * @memberof bar * @property - * by default rKey = 'r' + * by default valueExtractor = function(key, index) { return data[key] }, */ - bhm.rKey = function(_) { return arguments.length ? (rKey = _, bhm) : rKey; }; + bar.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, bar) : valueExtractor; }; /** - * Gets or sets the vKey - * (see {@link bubbleHeatmap#vKey}) - * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap + * Gets / sets the sortingFunction + * (see {@link bar#sortingFunction}) + * @param {function} [_=none] + * @returns {bar | function} + * @memberof bar * @property - * by default vKey = 'y' + * by default sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, */ - bhm.vKey = function(_) { return arguments.length ? (vKey = _, bhm) : vKey; }; - + bar.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, bar) : sortingFunction; }; /** - * Gets or sets the cellKeys - * (see {@link bubbleHeatmap#cellKeys}) - * @param {string[]} [_=none] - * @returns {bubbleHeatmap | string[]} - * @memberof bubbleHeatmap - * @property - * by default cellKeys = undefined - */ - bhm.cellKeys = function(_) { return arguments.length ? (cellKeys = _, bhm) : cellKeys; }; - /** - * Gets or sets the xValues - * (see {@link bubbleHeatmap#xValues}) - * @param {string[]} [_=none] - * @returns {bubbleHeatmap | string[]} - * @memberof bubbleHeatmap - * @property - * by default xValues = undefined - */ - bhm.xValues = function(_) { return arguments.length ? (xValues = _, bhm) : xValues; }; - /** - * Gets or sets the yValues - * (see {@link bubbleHeatmap#yValues}) - * @param {string[]} [_=none] - * @returns {bubbleHeatmap | string[]} - * @memberof bubbleHeatmap - * @property - * by default yValues = undefined - */ - bhm.yValues = function(_) { return arguments.length ? (yValues = _, bhm) : yValues; }; - /** - * Gets or sets the rValues - * (see {@link bubbleHeatmap#rValues}) - * @param {number[]} [_=none] - * @returns {bubbleHeatmap | number[]} - * @memberof bubbleHeatmap - * @property - * by default rValues = undefined - */ - bhm.rValues = function(_) { return arguments.length ? (rValues = _, bhm) : rValues; }; - /** - * Gets or sets the vValues - * (see {@link bubbleHeatmap#vValues}) - * @param {number[]} [_=none] - * @returns {bubbleHeatmap | number[]} - * @memberof bubbleHeatmap - * @property - * by default vValues = undefined - */ - bhm.vValues = function(_) { return arguments.length ? (vValues = _, bhm) : vValues; }; - - - /** - * Gets or sets the xExtractor - * (see {@link bubbleHeatmap#xExtractor}) - * @param {function} [_=none] - * @returns {bubbleHeatmap | function} - * @memberof bubbleHeatmap - * @property - * by default xExtractor = undefined - */ - bhm.xExtractor = function(_) { return arguments.length ? (xExtractor = _, bhm) : xExtractor; }; - /** - * Gets or sets the yExtractor - * (see {@link bubbleHeatmap#yExtractor}) - * @param {function} [_=none] - * @returns {bubbleHeatmap | function} - * @memberof bubbleHeatmap - * @property - * by default yExtractor = undefined - */ - bhm.yExtractor = function(_) { return arguments.length ? (yExtractor = _, bhm) : yExtractor; }; - /** - * Gets or sets the rExtractor - * (see {@link bubbleHeatmap#rExtractor}) - * @param {function} [_=none] - * @returns {bubbleHeatmap | function} - * @memberof bubbleHeatmap - * @property - * by default rExtractor = undefined - */ - bhm.rExtractor = function(_) { return arguments.length ? (rExtractor = _, bhm) : rExtractor; }; - /** - * Gets or sets the vExtractor - * (see {@link bubbleHeatmap#vExtractor}) - * @param {function} [_=none] - * @returns {bubbleHeatmap | function} - * @memberof bubbleHeatmap - * @property - * by default vExtractor = undefined - */ - bhm.vExtractor = function(_) { return arguments.length ? (vExtractor = _, bhm) : vExtractor; }; - - /** - * Gets / sets whether or not bubbleHeatmap is allowed to go beyond specified dimensions - * (see {@link bubbleHeatmap#spaceX}) - * @param {boolean} [_=none] - * @returns {bubbleHeatmap | boolean} - * @memberof bubbleHeatmap - * @property - * by default overflowQ = false - */ - bhm.overflowQ = function(_) { return arguments.length ? (overflowQ = _, bhm) : overflowQ; }; - /** - * Gets / sets the scale for which the radius of bubbles should be transformed by - * (see {@link bubbleHeatmap#scale}) + * Gets / sets the scale for which the bar values should be transformed by + * (see {@link bar#scale}) * @param {d3.scale} [_=none] - * @returns {bubbleHeatmap | d3.scale} - * @memberof bubbleHeatmap + * @returns {bar | d3.scale} + * @memberof bar * @property * by default scale = d3.scaleLinear() */ - bhm.scale = function(_) { return arguments.length ? (scale = _, bhm) : scale; }; + bar.scale = function(_) { return arguments.length ? (scale = _, bar) : scale; }; /** * Gets / sets the padding for the domain of the scale - * (see {@link bubbleHeatmap#domainPadding}) + * (see {@link bar#domainPadding}) * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * @returns {bar | number} + * @memberof bar * @property * by default domainPadding = 0.5 */ - bhm.domainPadding = function(_) { return arguments.length ? (domainPadding = _, bhm) : domainPadding; }; + bar.domainPadding = function(_) { return arguments.length ? (domainPadding = _, bar) : domainPadding; }; /** * Gets / sets objectSpacer - * (see {@link bubbleHeatmap#objectSpacer}) + * (see {@link bar#objectSpacer}) * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * @returns {bar | number} + * @memberof bar * @property - * by default objectSpacer = 0.0 + * by default objectSpacer = 0.05 */ - bhm.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, objectSpacer) : data; }; + bar.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, bar) : objectSpacer; }; /** * Gets / sets the minObjectSize - * (see {@link bubbleHeatmap#minObjectSize}) + * (see {@link bar#minObjectSize}) * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * @returns {bar | number} + * @memberof bar * @property * by default minObjectSize = 50 */ - bhm.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, bhm) : minObjectSize; }; + bar.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, bar) : minObjectSize; }; /** * Gets / sets the maxObjectSize - * (see {@link bubbleHeatmap#maxObjectSize}) + * (see {@link bar#maxObjectSize}) * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * @returns {bar | number} + * @memberof bar * @property * by default maxObjectSize = 100 */ - bhm.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, bhm) : maxObjectSize; }; + bar.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, bar) : maxObjectSize; }; + /** - * Gets / sets the bubbleStrokeWidth - * (see {@link bubbleHeatmap#bubbleStrokeWidth}) + * Gets / sets the barStrokeWidth + * (see {@link bar#barStrokeWidth}) * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * @returns {bar | number} + * @memberof bar * @property - * by default bubbleStrokeWidth = 2 + * by default barStrokeWidth = 2 */ - bhm.bubbleStrokeWidth = function(_) { return arguments.length ? (bubbleStrokeWidth = _, bhm) : bubbleStrokeWidth; }; + bar.barStrokeWidth = function(_) { return arguments.length ? (barStrokeWidth = _, bar) : barStrokeWidth; }; + /** + * Gets / sets the colorFunction + * (see {@link bar#colorFunction}) + * @param {number} [_=none] + * @returns {bar | number} + * @memberof bar + * @property + * by default colorFunction = colorFunction() + */ + bar.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, bar) : colorFunction$$1; }; + /** * Gets / sets the backgroundFill - * (see {@link bubbleHeatmap#backgroundFill}) + * (see {@link bar#backgroundFill}) * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap + * @returns {bar | string} + * @memberof bar * @property * by default backgroundFill = 'transparent' */ - bhm.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, bhm) : backgroundFill; }; + bar.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, bar) : backgroundFill; }; /** * Gets / sets the namespace - * (see {@link bubbleHeatmap#namespace}) + * (see {@link bar#namespace}) * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap + * @returns {bar | string} + * @memberof bar * @property - * by default namespace = 'd3sm-bubbleHeatmap' + * by default namespace = 'd3sm-bar' */ - bhm.namespace = function(_) { return arguments.length ? (namespace = _, bhm) : namespace; }; + bar.namespace = function(_) { return arguments.length ? (namespace = _, bar) : namespace; }; /** * Gets / sets the objectClass - * (see {@link bubbleHeatmap#objectClass}) + * (see {@link bar#objectClass}) * @param {string} [_=none] - * @returns {bubbleHeatmap | string} - * @memberof bubbleHeatmap + * @returns {bar | string} + * @memberof bar * @property * by default objectClass = 'tick-group' */ - bhm.objectClass = function(_) { return arguments.length ? (objectClass = _, bhm) : objectClass; }; + bar.objectClass = function(_) { return arguments.length ? (objectClass = _, bar) : objectClass; }; /** * Gets / sets the transitionDuration - * (see {@link bubbleHeatmap#transitionDuration}) + * (see {@link bar#transitionDuration}) * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * @returns {bar | number} + * @memberof bar * @property * by default transitionDuration = 1000 */ - bhm.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, bhm) : transitionDuration; }; + bar.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, bar) : transitionDuration; }; /** * Gets / sets the easeFunc - * (see {@link bubbleHeatmap#easeFunc}) + * (see {@link bar#easeFunc}) * @param {d3.ease} [_=none] - * @returns {bubbleHeatmap | d3.ease} - * @memberof bubbleHeatmap + * @returns {bar | d3.ease} + * @memberof bar * @property * by default easeFunc = d3.easeExp */ - bhm.easeFunc = function(_) { return arguments.length ? (easeFunc = _, bhm) : easeFunc; }; + bar.easeFunc = function(_) { return arguments.length ? (easeFunc = _, bar) : easeFunc; }; + /** - * Gets / sets the tooltip - * (see {@link bubbleHeatmap#tooltip}) - * @param {tooltip} [_=none] - * @returns {bubbleHeatmap | tooltip} - * @memberof bubbleHeatmap + * Gets / sets the barKeys + * (see {@link bar#barKeys}) + * @param {string[]} [_=none] + * @returns {bar | string[]} + * @memberof bar * @property - * by default tooltip = tooltip() + * by default barKeys = undefined */ - bhm.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, bhm) : tooltip$$1; }; - + bar.barKeys = function(_) { return arguments.length ? (barKeys = _, bar) : barKeys; }; /** - * Gets / sets the colorFunction - * (see {@link bubbleHeatmap#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {bubbleHeatmap | colorFunction} - * @memberof bubbleHeatmap + * Gets / sets the barValues + * (see {@link bar#barValues}) + * @param {number[]} [_=none] + * @returns {bar | number[]} + * @memberof bar * @property - * by default colorFunction = colorFunction() + * by default barValues = undefined */ - bhm.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, bhm) : colorFunction$$1; }; - + bar.barValues = function(_) { return arguments.length ? (barValues = _, bar) : barValues; }; /** - * Gets / sets the xSize - * (see {@link bubbleHeatmap#xSize}) + * Gets / sets the objectSize + * (see {@link bar#objectSize}) * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * @returns {bar | number} + * @memberof bar * @property - * by default xSize = undefined + * by default objectSize = undefined */ - bhm.xSize = function(_) { return arguments.length ? (xSize = _, bhm) : xSize; }; + bar.objectSize = function(_) { return arguments.length ? (objectSize = _, bar) : objectSize; }; /** - * Gets / sets the xSpacerSize - * (see {@link bubbleHeatmap#xSpacerSize}) + * Gets / sets the spacerSize + * (see {@link bar#spacerSize}) * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap - * @property - * by default xSpacerSize = undefined - */ - bhm.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, bhm) : xSpacerSize; }; - /** - * Gets / sets the ySize - * (see {@link bubbleHeatmap#ySize}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * @returns {bar | number} + * @memberof bar * @property - * by default ySize = undefined + * by default spacerSize = undefined */ - bhm.ySize = function(_) { return arguments.length ? (ySize = _, bhm) : ySize; }; + bar.spacerSize = function(_) { return arguments.length ? (spacerSize = _, bar) : spacerSize; }; + /** - * Gets / sets the ySpacerSize - * (see {@link bubbleHeatmap#ySpacerSize}) - * @param {number} [_=none] - * @returns {bubbleHeatmap | number} - * @memberof bubbleHeatmap + * Gets / sets the tooltip + * (see {@link bar#tooltip}) + * @param {tooltip} [_=none] + * @returns {bar | tooltip} + * @memberof bar * @property - * by default ySpacerSize = undefined + * by default tooltip = tooltip() */ - bhm.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, bhm) : ySpacerSize; }; - // bhm.yKeySortingFunction = function(_) { return arguments.length ? (yKeySortingFunction = _, bhm) : yKeySortingFunction; } - // bhm.xKeySortingFunction = function(_) { return arguments.length ? (xKeySortingFunction = _, bhm) : xKeySortingFunction; } + bar.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, bar) : tooltip$$1; }; + bar.barPercent = function(_) { return arguments.length ? (barPercent = _, bar) : barPercent; }; + function bar() { + // for convenience in handling orientation specific values + var horizontalQ = (orient == 'horizontal' || orient == 'bottom' || orient == 'top') ? true : false; + var verticalQ = !horizontalQ; - function bhm() { + // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); - - cellKeys = d3.keys(data); - cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }); - log('bubbleHeatmap', 'cells are sorted by', cellKeys); + var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + // to prevent re-calculation and getters to be passed to axes + barKeys = d3.keys(data); + barValues = barKeys.map(valueExtractor); + // if grouping is undefined sort barKeys by sortingFunction + var ordered = (grouping == undefined) ? barKeys.sort(sortingFunction) : grouping; + // ordered might be nested depending on grouping + barKeys = utils$1.arr.flatten(ordered); - xValues = unique(cellKeys.map(xExtractor)); - yValues = unique(cellKeys.map(yExtractor)); - rValues = unique(cellKeys.map(rExtractor)); - vValues = unique(cellKeys.map(vExtractor)); - log('bubbleHeatmap', 'x and y keys are', {x: xValues, y:yValues}); + var numberOfObjects = barKeys.length; + var extent = [Math.min(...barValues) - domainPadding,Math.max(...barValues) + domainPadding]; - var xDim = xValues.length, yDim = yValues.length; + // set the scale - var extent = [Math.min(...rValues) - domainPadding,Math.max(...rValues) + domainPadding]; + scale.domain(extent).range(horizontalQ + ? [0,spaceY] + : orient == 'right' + ? [0, spaceX] + : [spaceX, 0] + ); + var space = horizontalQ ? spaceX : spaceY; + // calculate object size + objectSize = (objectSize == undefined) + ? utils$1.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + : objectSize; + // calculate spacer size if needed + spacerSize = (spacerSize == undefined) + ? utils$1.math.calculateWidthOfSpacer(barKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + : spacerSize; + // make the nested groups + var spacerFunction = groupingSpacer() + .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) + .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc) + .namespace(namespace); + // safe default function + var defaultExit = spacerFunction.exitFunction(); - ySize = calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - xSize = calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - ySpacerSize = calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); - xSpacerSize = calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); - log('bubbleHeatmap', 'size of', {x: xSize, y: ySize}); + spacerFunction.exitFunction(function(sel$$1){ + // use default to move objects off screen + // console.log("EXIT", sel.nodes(), objectSize, scale(extent[1])) + if (objectSize == undefined) {console.log(sel$$1.nodes(), objectSize);} + defaultExit(sel$$1); + sel$$1.selectAll('g').classed("to-remove", true); + // shrink rectangles in addition + sel$$1.selectAll('* > rect') + .transition().duration(transitionDuration) + .attr('transform', function(d, i) { + var + x = horizontalQ + ? 0 + : 0 + , + y = verticalQ + ? 0 + : scale(extent[1]) + , + t = 'translate('+x+','+y+')'; + return t + }) + .attr('width', horizontalQ ? objectSize : 0) + .attr('height', verticalQ ? objectSize : 0) + .remove(); + }); - scale.domain(extent).range([Math.min(minObjectSize/2, Math.min(ySize, xSize)/2), Math.min(ySize, xSize)/2]); + // move stuff + spacerFunction(container, ordered, 0); - var ySpacer = groupingSpacer() - .horizontalQ(false) - .moveby('category').numberOfObjects(yDim) - .objectClass(hypenate(objectClass, 'row')) - .objectSize(ySize).spacerSize(ySpacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc) - .namespace('row'); - var xSpacer = groupingSpacer() - .horizontalQ(true) - .moveby('category').numberOfObjects(xDim) - .objectClass(objectClass) - .objectSize(xSize).spacerSize(xSpacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc); - ySpacer(container, yValues, 0); - container.selectAll('g.'+hypenate(objectClass, 'row')) - .each(function(d, i){ - xSpacer(d3.select(this), xValues, 0); - }); - var cells = container.selectAll('g:not(.to-remove).'+objectClass).data(cellKeys); var parentIndexArray = []; - cells.each(function(d, i){ parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); }); + container.selectAll('g:not(.to-remove).'+objectClass) + .each(function(d, i){parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}); colorFunction$$1 = colorFunction$$1.colorBy() == 'index' ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) - : colorFunction$$1.dataExtent([0, Math.max(...vValues)]); + : colorFunction$$1.dataExtent(extent); + - cells.each(function(key, i) { - log('bubbleHeatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); + container.selectAll('g.'+objectClass+':not(.to-remove)').each(function(key, i) { + // console.log(key, scale(extent[1]) - scale(valueExtractor(key, i))) var t = d3.select(this), currentData = data[key], - value = vExtractor(key, i), - radius= rExtractor(key, i), + value = valueExtractor(key, i), i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), fillColor = colorFunction$$1(key, value, i, 'fill'), // prevent duplicate computation strokeColor = colorFunction$$1(key, value, i, 'stroke'); - log('bubbleHeatmap', 'radius',{radius: radius, scaled: scale(radius), extent: extent, range:scale.range()}); - var c = safeSelect(t, 'circle', hypenate(objectClass,'circle')); - c.attr('cx', xSize / 2) - .attr('cy', ySize / 2 ) - .attr('r', scale(radius)) + var bar = utils$1.sel.safeSelect(t, 'rect', 'bar-rect'); + + if (bar.attr('transform') == undefined) { + bar.attr('transform', function(d, i) { + var + x = horizontalQ + ? 0 + : 0 + , + y = verticalQ + ? 0 + : scale(extent[1]) + , + t = 'translate('+x+','+y+')'; + return t + }) + .attr('width', horizontalQ ? objectSize : 0) + .attr('height', verticalQ ? objectSize : 0); + + } + + + bar.transition().duration(transitionDuration).ease(easeFunc) + .attr('transform', function(d, i) { + var + x = horizontalQ + ? objectSize - objectSize * barPercent + : orient == 'right' + ? scale(extent[1]) - scale(value) + : objectSize - objectSize * barPercent + , + y = verticalQ + ? objectSize - objectSize * barPercent + : scale(extent[1]) - scale(value) + , + t = 'translate('+x+','+y+')'; + return t + }) + .attr('width', horizontalQ ? objectSize * barPercent : scale(value)) + .attr('height', verticalQ ? objectSize * barPercent: scale(value)) .attr('fill', fillColor) .attr('stroke', strokeColor) - .attr('stroke-width', bubbleStrokeWidth); + .attr('stroke-width', barStrokeWidth); + + + + t.on('mouseover', function(d, i){ + container.selectAll('g.'+objectClass).style('opacity', 0.2); + t.style('opacity', 1); + bar.attr('stroke-width',barStrokeWidth*2); + }); + t.on('mouseout', function(){ + container.selectAll('g.'+objectClass).style('opacity', 1); + bar.attr('stroke-width', barStrokeWidth); + }); }); - tooltip$$1.selection(cells.selectAll('circle.'+hypenate(objectClass, 'circle'))) + tooltip$$1.selection(container.selectAll('.bar-rect')) .data(data); - // .keys(['r', 'v']) - // .header(function(d, i){return hypenate(data[d][xKey], data[d][yKey]) }) tooltip$$1(); - } - - return bhm; + return bar } /** - * Creates a heatmap + * Creates a bubbleHeatmap * - * {@link https://sumneuron.gitlab.io/d3sm/demos/heatmap-heatmap/index.html Demo} - * @constructor heatmap + * {@link https://sumneuron.gitlab.io/d3sm/demos/bubble-heatmap/index.html Demo} + * @constructor bubbleHeatmap * @param {d3.selection} selection - * @namespace heatmap - * @returns {function} heatmap + * @namespace bubbleHeatmap + * @returns {function} bubbleHeatmap */ -function heatmap( selection ) { +function bubbleHeatmap( selection ) { var /** * Data to plot. Assumed to be a object, where each key corresponds to a cell - * (see {@link heatmap#data}) + * (see {@link bubbleHeatmap#data}) * @param {Object} [data=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ data, /** - * Amount of horizontal space (in pixels) avaible to render the heatmap in - * (see {@link heatmap#spaceX}) + * Amount of horizontal space (in pixels) avaible to render the bubbleHeatmap in + * (see {@link bubbleHeatmap#spaceX}) * @param {number} [spaceX=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ spaceX, /** - * Amount of vertical space (in pixels) avaible to render the heatmap in - * (see {@link heatmap.spaceY}) + * Amount of vertical space (in pixels) avaible to render the bubbleHeatmap in + * (see {@link bubbleHeatmap.spaceY}) * @param {number} [spaceY=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ spaceY, /** * The internal key of the cell specifiying to which x axis key it belongs - * (see {@link heatmap.xKey}) + * (see {@link bubbleHeatmap.xKey}) * @param {string} [xKey='x'] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ xKey = 'x', /** * The internal key of the cell specifiying to which y axis key it belongs - * (see {@link heatmap.yKey}) + * (see {@link bubbleHeatmap.yKey}) * @param {string} [yKey='y'] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ yKey = 'y', - + /** + * The internal key of the cell specifiying what value to use to determine the radius + * (see {@link bubbleHeatmap.rKey}) + * @param {string} [rKey='r'] + * @memberof bubbleHeatmap# + * @property + */ + rKey = 'r', /** * The internal key of the cell specifiying what value to use to determine the color - * (see {@link heatmap.vKey}) + * (see {@link bubbleHeatmap.vKey}) * @param {string} [vKey='v'] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ vKey = 'v', /** * Function for extracting the the value from xKey. - * (see {@link heatmap.xExtractor}) + * (see {@link bubbleHeatmap.xExtractor}) * @param {function} [xExtractor=function(key, i) { return data[key][xKey] }] * @returns {string} - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ xExtractor = function(key, i) {return data[key][xKey] }, /** * Function for extracting the the value from yKey. - * (see {@link heatmap.yExtractor}) + * (see {@link bubbleHeatmap.yExtractor}) * @param {function} [yExtractor=function(key, i) { return data[key][yKey] }] * @returns {string} - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ yExtractor = function(key, i) { return data[key][yKey] }, - /** - * Function for extracting the the value from vKey. - * (see {@link heatmap.vExtractor}) - * @param {function} [vExtractor=function(key, i) { return data[key][vKey] }] + * Function for extracting the the value from rKey. + * (see {@link bubbleHeatmap.rExtractor}) + * @param {function} [rExtractor=function(key, i) { return data[key][rKey] }] * @returns {number} - * @memberof heatmap# + * @memberof bubbleHeatmap# + * @property + */ + rExtractor = function(key, i) { return data[key][rKey] }, + /** + * Function for extracting the the value from vKey. + * (see {@link bubbleHeatmap.vExtractor}) + * @param {function} [vExtractor=function(key, i) { return data[key][vKey] }] + * @returns {number} + * @memberof bubbleHeatmap# * @property */ vExtractor = function(key, i) { return data[key][vKey] }, /** - * Whether or not to allow heatmap to render elements pass the main spatial dimension - * given the orientation (see {@link heatmap#orient}), where {@link heatmap#orient}="bottom" or {@link heatmap#orient}="top" - * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="left" or {@link heatmap#orient}="right" - * the main dimension is {@link heatmap#spaceY} + * Whether or not to allow bubbleHeatmap to render elements pass the main spatial dimension + * given the orientation (see {@link bubbleHeatmap#orient}), where {@link bubbleHeatmap#orient}="bottom" or {@link bubbleHeatmap#orient}="top" + * the main dimension is {@link bubbleHeatmap#spaceX} and where {@link bubbleHeatmap#orient}="left" or {@link bubbleHeatmap#orient}="right" + * the main dimension is {@link bubbleHeatmap#spaceY} * @param {boolean} [overflowQ=false] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ overflowQ = false, /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link heatmap#orient}), where {@link heatmap#orient}="horizontal" - * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="vertical" - * the main dimension is {@link heatmap#spaceY} between bubbles - * @param {number} [objectSpacer=0.0] - * @memberof heatmap# + * The scale for which the radius values should be transformed by + * @param {d3.scale} [scale=d3.scaleLinear] + * @memberof bubbleHeatmap# * @property */ - objectSpacer = 0.0, + scale = d3.scaleLinear(), /** - * The minimum size that an object can be in the y dimension - * @param {number} [minObjectSize=50] - * @memberof heatmap# + * The padding for the domain of the scale (see {@link bubbleHeatmap#scale}) + * @param {number} [domainPadding=0.5] + * @memberof bubbleHeatmap# * @property */ - yMinObjectSize = 50, + domainPadding = 0.5, + /** - * The minimum size that an object can be in the x dimension - * @param {number} [minObjectSize=50] - * @memberof heatmap# + * Default space for the spacer (percentage) of main dimension given the orientation + * (see {@link bubbleHeatmap#orient}), where {@link bubbleHeatmap#orient}="horizontal" + * the main dimension is {@link bubbleHeatmap#spaceX} and where {@link bubbleHeatmap#orient}="vertical" + * the main dimension is {@link bubbleHeatmap#spaceY} between bubbles + * @param {number} [objectSpacer=0.0] + * @memberof bubbleHeatmap# * @property */ - xMinObjectSize = 50, + objectSpacer = 0.0, /** - * The maximum size that an object can be in the x dimension - * @param {number} [maxObjectSize=100] - * @memberof heatmap# + * The minimum size that an object can be + * @param {number} [minObjectSize=50] + * @memberof bubbleHeatmap# * @property */ - xMaxObjectSize = 100, + minObjectSize = 50, /** - * The maximum size that an object can be in the y dimension + * The maximum size that an object can be * @param {number} [maxObjectSize=100] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ - yMaxObjectSize = 100, + maxObjectSize = 100, /** * The stroke width of the bubbles - * @param {number} [objectStrokeWidth=2] - * @memberof heatmap# + * @param {number} [bubbleStrokeWidth=2] + * @memberof bubbleHeatmap# * @property */ - objectStrokeWidth = 2, + bubbleStrokeWidth = 2, // colorFunc = colorFunction(), /** * Color of the background * @param {string} [backgroundFill="transparent"] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ backgroundFill = 'transparent', /** - * Namespace for all items made by this instance of heatmap - * @param {string} [namespace="d3sm-heatmap"] - * @memberof heatmap# + * Namespace for all items made by this instance of bubbleHeatmap + * @param {string} [namespace="d3sm-bubble"] + * @memberof bubbleHeatmap# * @property */ - namespace = 'd3sm-heatmap', + namespace = 'd3sm-bubble', /** - * Class name for heatmap container ( element) - * @param {string} [objectClass="heatmap"] - * @memberof heatmap# + * Class name for bubble container ( element) + * @param {string} [objectClass="bubble"] + * @memberof bubbleHeatmap# * @property */ - objectClass = 'heatmap', + objectClass = 'bubble', /** * Duration of all transitions of this element * @param {number} [transitionDuration=1000] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ transitionDuration = 1000, /** * Easing function for transitions * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ easeFunc = d3.easeExp, /** * Stores the keys of all the cells - * Calculated after heatmap called. + * Calculated after bubbleHeatmap called. * @param {string[]} [cellKeys=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ cellKeys, /** - * Stores the list of unique xValues - * Calculated after heatmap called. + * Stores the list of utils.arr.unique xValues + * Calculated after bubbleHeatmap called. * @param {string[]} [xValues=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ xValues, /** - * Stores the list of unique yValues - * Calculated after heatmap called. + * Stores the list of utils.arr.unique yValues + * Calculated after bubbleHeatmap called. * @param {string[]} [yValues=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ yValues, /** - * Stores the list of unique vValues - * Calculated after heatmap called. + * Stores the list of utils.arr.unique rValues + * Calculated after bubbleHeatmap called. + * @param {string[]} [rValues=undefined] + * @memberof bubbleHeatmap# + * @property + */ + rValues, + /** + * Stores the list of utils.arr.unique vValues + * Calculated after bubbleHeatmap called. * @param {string[]} [vValues=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ vValues, - xKeySortingFunction = function(a, b) { return xValues.indexOf(xExtractor(a)) - xValues.indexOf(xExtractor(b)) }, - yKeySortingFunction = function(a, b) { return yValues.indexOf(yExtractor(a)) - yValues.indexOf(yExtractor(b)) }, + xKeySortingFunction = function(a, b) { return xExtractor(a) - xExtractor(b) }, + yKeySortingFunction = function(a, b) { return yExtractor(a) - yExtractor(b) }, /** * Instance of ColorFunction with .colorBy set to 'category' * @function colorFunction - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ - colorFunction$$1 = colorFunction().colorBy('category'), + colorFunction$$1 = colorFunction().colorBy('value'), /** * Instance of Tooltip * @function tooltip - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ tooltip$$1 = tooltip(), /** - * store the size the heatmap could be in the x dimension - * the actuall size of the heatmap will be the min of xSize and {@link heatmap#ySize} - * Calculated after heatmap called. + * store the size the bubble could be in the x dimension + * the actuall size of the bubble will be the min of xSize and {@link bubbleHeatmap#ySize} + * Calculated after bubbleHeatmap called. * @param {string[]} [xSize=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ xSize, /** * store the size of the spacer in the x dimension - * Calculated after heatmap called. + * Calculated after bubbleHeatmap called. * @param {string[]} [xSpacerSize=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ xSpacerSize, /** - * store the size the heatmap could be in the y dimension - * the actuall size of the heatmap will be the min of xSize and {@link heatmap#xSize} - * Calculated after heatmap called. + * store the size the bubble could be in the y dimension + * the actuall size of the bubble will be the min of xSize and {@link bubbleHeatmap#xSize} + * Calculated after bubbleHeatmap called. * @param {string[]} [ySize=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ ySize, /** * store the size of the spacer in the y dimension. - * Calculated after heatmap called. + * Calculated after bubbleHeatmap called. * @param {string[]} [xSpacerSize=undefined] - * @memberof heatmap# + * @memberof bubbleHeatmap# * @property */ ySpacerSize; @@ -4591,2660 +4437,2586 @@ function heatmap( selection ) { /** * Gets or sets the selection in which items are manipulated * @param {d3.selection} [_=none] - * @returns {heatmap | d3.selection} - * @memberof heatmap + * @returns {bubbleHeatmap | d3.selection} + * @memberof bubbleHeatmap * @property * by default selection = selection */ - hm.selection = function(_) { return arguments.length ? (selection = _, hm) : selection; }; + bhm.selection = function(_) { return arguments.length ? (selection = _, bhm) : selection; }; /** * Gets or sets the data - * (see {@link heatmap#data}) + * (see {@link bubbleHeatmap#data}) * @param {number} [_=none] - * @returns {heatmap | object} - * @memberof heatmap + * @returns {bubbleHeatmap | object} + * @memberof bubbleHeatmap * @property */ - hm.data = function(_) { return arguments.length ? (data = _, hm) : data; }; - // hm.orient = function(_) { return arguments.length ? (orient = _, hm) : orient; } + bhm.data = function(_) { return arguments.length ? (data = _, bhm) : data; }; + // bhm.orient = function(_) { return arguments.length ? (orient = _, bhm) : orient; } /** * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link heatmap#spaceX}) + * (see {@link bubbleHeatmap#spaceX}) * @param {number} [_=none] should be a number > 0 - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property * by default spaceX = undefined */ - hm.spaceX = function(_) { return arguments.length ? (spaceX = _, hm) : spaceX; }; + bhm.spaceX = function(_) { return arguments.length ? (spaceX = _, bhm) : spaceX; }; /** * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link heatmap#spaceY}) + * (see {@link bubbleHeatmap#spaceY}) * @param {number} [_=none] should be a number > 0 - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property * by default spaceY = undefined */ - hm.spaceY = function(_) { return arguments.length ? (spaceY = _, hm) : spaceY; }; + bhm.spaceY = function(_) { return arguments.length ? (spaceY = _, bhm) : spaceY; }; /** * Gets or sets the xKey - * (see {@link heatmap#xKey}) + * (see {@link bubbleHeatmap#xKey}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {bubbleHeatmap | string} + * @memberof bubbleHeatmap * @property * by default xKey = 'x' */ - hm.xKey = function(_) { return arguments.length ? (xKey = _, hm) : xKey; }; + bhm.xKey = function(_) { return arguments.length ? (xKey = _, bhm) : xKey; }; /** * Gets or sets the yKey - * (see {@link heatmap#yKey}) + * (see {@link bubbleHeatmap#yKey}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {bubbleHeatmap | string} + * @memberof bubbleHeatmap * @property * by default yKey = 'y' */ - hm.yKey = function(_) { return arguments.length ? (yKey = _, hm) : yKey; }; - + bhm.yKey = function(_) { return arguments.length ? (yKey = _, bhm) : yKey; }; + /** + * Gets or sets the rKey + * (see {@link bubbleHeatmap#rKey}) + * @param {string} [_=none] + * @returns {bubbleHeatmap | string} + * @memberof bubbleHeatmap + * @property + * by default rKey = 'r' + */ + bhm.rKey = function(_) { return arguments.length ? (rKey = _, bhm) : rKey; }; /** * Gets or sets the vKey - * (see {@link heatmap#vKey}) + * (see {@link bubbleHeatmap#vKey}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {bubbleHeatmap | string} + * @memberof bubbleHeatmap * @property * by default vKey = 'y' */ - hm.vKey = function(_) { return arguments.length ? (vKey = _, hm) : vKey; }; + bhm.vKey = function(_) { return arguments.length ? (vKey = _, bhm) : vKey; }; /** * Gets or sets the cellKeys - * (see {@link heatmap#cellKeys}) + * (see {@link bubbleHeatmap#cellKeys}) * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap + * @returns {bubbleHeatmap | string[]} + * @memberof bubbleHeatmap * @property * by default cellKeys = undefined */ - hm.cellKeys = function(_) { return arguments.length ? (cellKeys = _, hm) : cellKeys; }; + bhm.cellKeys = function(_) { return arguments.length ? (cellKeys = _, bhm) : cellKeys; }; /** * Gets or sets the xValues - * (see {@link heatmap#xValues}) + * (see {@link bubbleHeatmap#xValues}) * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap + * @returns {bubbleHeatmap | string[]} + * @memberof bubbleHeatmap * @property * by default xValues = undefined */ - hm.xValues = function(_) { return arguments.length ? (xValues = _, hm) : xValues; }; + bhm.xValues = function(_) { return arguments.length ? (xValues = _, bhm) : xValues; }; /** * Gets or sets the yValues - * (see {@link heatmap#yValues}) + * (see {@link bubbleHeatmap#yValues}) * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap + * @returns {bubbleHeatmap | string[]} + * @memberof bubbleHeatmap * @property * by default yValues = undefined */ - hm.yValues = function(_) { return arguments.length ? (yValues = _, hm) : yValues; }; + bhm.yValues = function(_) { return arguments.length ? (yValues = _, bhm) : yValues; }; /** - * Gets or sets the vValues - * (see {@link heatmap#vValues}) + * Gets or sets the rValues + * (see {@link bubbleHeatmap#rValues}) * @param {number[]} [_=none] - * @returns {heatmap | number[]} - * @memberof heatmap + * @returns {bubbleHeatmap | number[]} + * @memberof bubbleHeatmap * @property - * by default vValues = undefined + * by default rValues = undefined */ - hm.vValues = function(_) { return arguments.length ? (vValues = _, hm) : vValues; }; - - + bhm.rValues = function(_) { return arguments.length ? (rValues = _, bhm) : rValues; }; /** - * Gets or sets the xExtractor - * (see {@link heatmap#xExtractor}) + * Gets or sets the vValues + * (see {@link bubbleHeatmap#vValues}) + * @param {number[]} [_=none] + * @returns {bubbleHeatmap | number[]} + * @memberof bubbleHeatmap + * @property + * by default vValues = undefined + */ + bhm.vValues = function(_) { return arguments.length ? (vValues = _, bhm) : vValues; }; + + + /** + * Gets or sets the xExtractor + * (see {@link bubbleHeatmap#xExtractor}) * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap + * @returns {bubbleHeatmap | function} + * @memberof bubbleHeatmap * @property * by default xExtractor = undefined */ - hm.xExtractor = function(_) { return arguments.length ? (xExtractor = _, hm) : xExtractor; }; + bhm.xExtractor = function(_) { return arguments.length ? (xExtractor = _, bhm) : xExtractor; }; /** * Gets or sets the yExtractor - * (see {@link heatmap#yExtractor}) + * (see {@link bubbleHeatmap#yExtractor}) * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap + * @returns {bubbleHeatmap | function} + * @memberof bubbleHeatmap * @property * by default yExtractor = undefined */ - hm.yExtractor = function(_) { return arguments.length ? (yExtractor = _, hm) : yExtractor; }; + bhm.yExtractor = function(_) { return arguments.length ? (yExtractor = _, bhm) : yExtractor; }; + /** + * Gets or sets the rExtractor + * (see {@link bubbleHeatmap#rExtractor}) + * @param {function} [_=none] + * @returns {bubbleHeatmap | function} + * @memberof bubbleHeatmap + * @property + * by default rExtractor = undefined + */ + bhm.rExtractor = function(_) { return arguments.length ? (rExtractor = _, bhm) : rExtractor; }; /** * Gets or sets the vExtractor - * (see {@link heatmap#vExtractor}) + * (see {@link bubbleHeatmap#vExtractor}) * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap + * @returns {bubbleHeatmap | function} + * @memberof bubbleHeatmap * @property * by default vExtractor = undefined */ - hm.vExtractor = function(_) { return arguments.length ? (vExtractor = _, hm) : vExtractor; }; + bhm.vExtractor = function(_) { return arguments.length ? (vExtractor = _, bhm) : vExtractor; }; /** - * Gets / sets whether or not heatmap is allowed to go beyond specified dimensions - * (see {@link heatmap#spaceX}) + * Gets / sets whether or not bubbleHeatmap is allowed to go beyond specified dimensions + * (see {@link bubbleHeatmap#spaceX}) * @param {boolean} [_=none] - * @returns {heatmap | boolean} - * @memberof heatmap + * @returns {bubbleHeatmap | boolean} + * @memberof bubbleHeatmap * @property * by default overflowQ = false */ - hm.overflowQ = function(_) { return arguments.length ? (overflowQ = _, hm) : overflowQ; }; + bhm.overflowQ = function(_) { return arguments.length ? (overflowQ = _, bhm) : overflowQ; }; /** - * Gets / sets objectSpacer - * (see {@link heatmap#objectSpacer}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * Gets / sets the scale for which the radius of bubbles should be transformed by + * (see {@link bubbleHeatmap#scale}) + * @param {d3.scale} [_=none] + * @returns {bubbleHeatmap | d3.scale} + * @memberof bubbleHeatmap * @property - * by default objectSpacer = 0.0 + * by default scale = d3.scaleLinear() */ - hm.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, objectSpacer) : data; }; + bhm.scale = function(_) { return arguments.length ? (scale = _, bhm) : scale; }; /** - * Gets / sets the minObjectSize - * (see {@link heatmap#minObjectSize}) + * Gets / sets the padding for the domain of the scale + * (see {@link bubbleHeatmap#domainPadding}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property - * by default minObjectSize = 50 + * by default domainPadding = 0.5 */ - hm.yMinObjectSize = function(_) { return arguments.length ? (yMinObjectSize = _, hm) : yMinObjectSize; }; + bhm.domainPadding = function(_) { return arguments.length ? (domainPadding = _, bhm) : domainPadding; }; /** - * Gets / sets the maxObjectSize - * (see {@link heatmap#maxObjectSize}) + * Gets / sets objectSpacer + * (see {@link bubbleHeatmap#objectSpacer}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property - * by default maxObjectSize = 100 + * by default objectSpacer = 0.0 */ - hm.yMaxObjectSize = function(_) { return arguments.length ? (yMaxObjectSize = _, hm) : yMaxObjectSize; }; + bhm.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, objectSpacer) : data; }; /** * Gets / sets the minObjectSize - * (see {@link heatmap#minObjectSize}) + * (see {@link bubbleHeatmap#minObjectSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property * by default minObjectSize = 50 */ - hm.xMinObjectSize = function(_) { return arguments.length ? (xMinObjectSize = _, hm) : xMinObjectSize; }; + bhm.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, bhm) : minObjectSize; }; /** * Gets / sets the maxObjectSize - * (see {@link heatmap#maxObjectSize}) + * (see {@link bubbleHeatmap#maxObjectSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property * by default maxObjectSize = 100 */ - hm.xMaxObjectSize = function(_) { return arguments.length ? (xMaxObjectSize = _, hm) : xMaxObjectSize; }; + bhm.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, bhm) : maxObjectSize; }; /** - * Gets / sets the objectStrokeWidth - * (see {@link heatmap#objectStrokeWidth}) + * Gets / sets the bubbleStrokeWidth + * (see {@link bubbleHeatmap#bubbleStrokeWidth}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property - * by default objectStrokeWidth = 2 + * by default bubbleStrokeWidth = 2 */ - hm.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, hm) : objectStrokeWidth; }; + bhm.bubbleStrokeWidth = function(_) { return arguments.length ? (bubbleStrokeWidth = _, bhm) : bubbleStrokeWidth; }; /** * Gets / sets the backgroundFill - * (see {@link heatmap#backgroundFill}) + * (see {@link bubbleHeatmap#backgroundFill}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {bubbleHeatmap | string} + * @memberof bubbleHeatmap * @property * by default backgroundFill = 'transparent' */ - hm.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, hm) : backgroundFill; }; + bhm.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, bhm) : backgroundFill; }; /** * Gets / sets the namespace - * (see {@link heatmap#namespace}) + * (see {@link bubbleHeatmap#namespace}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {bubbleHeatmap | string} + * @memberof bubbleHeatmap * @property - * by default namespace = 'd3sm-heatmap' + * by default namespace = 'd3sm-bubbleHeatmap' */ - hm.namespace = function(_) { return arguments.length ? (namespace = _, hm) : namespace; }; + bhm.namespace = function(_) { return arguments.length ? (namespace = _, bhm) : namespace; }; /** * Gets / sets the objectClass - * (see {@link heatmap#objectClass}) + * (see {@link bubbleHeatmap#objectClass}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {bubbleHeatmap | string} + * @memberof bubbleHeatmap * @property * by default objectClass = 'tick-group' */ - hm.objectClass = function(_) { return arguments.length ? (objectClass = _, hm) : objectClass; }; + bhm.objectClass = function(_) { return arguments.length ? (objectClass = _, bhm) : objectClass; }; /** * Gets / sets the transitionDuration - * (see {@link heatmap#transitionDuration}) + * (see {@link bubbleHeatmap#transitionDuration}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property * by default transitionDuration = 1000 */ - hm.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, hm) : transitionDuration; }; + bhm.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, bhm) : transitionDuration; }; /** * Gets / sets the easeFunc - * (see {@link heatmap#easeFunc}) + * (see {@link bubbleHeatmap#easeFunc}) * @param {d3.ease} [_=none] - * @returns {heatmap | d3.ease} - * @memberof heatmap + * @returns {bubbleHeatmap | d3.ease} + * @memberof bubbleHeatmap * @property * by default easeFunc = d3.easeExp */ - hm.easeFunc = function(_) { return arguments.length ? (easeFunc = _, hm) : easeFunc; }; + bhm.easeFunc = function(_) { return arguments.length ? (easeFunc = _, bhm) : easeFunc; }; /** * Gets / sets the tooltip - * (see {@link heatmap#tooltip}) + * (see {@link bubbleHeatmap#tooltip}) * @param {tooltip} [_=none] - * @returns {heatmap | tooltip} - * @memberof heatmap + * @returns {bubbleHeatmap | tooltip} + * @memberof bubbleHeatmap * @property * by default tooltip = tooltip() */ - hm.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, hm) : tooltip$$1; }; + bhm.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, bhm) : tooltip$$1; }; /** * Gets / sets the colorFunction - * (see {@link heatmap#colorFunction}) + * (see {@link bubbleHeatmap#colorFunction}) * @param {colorFunction} [_=none] - * @returns {heatmap | colorFunction} - * @memberof heatmap + * @returns {bubbleHeatmap | colorFunction} + * @memberof bubbleHeatmap * @property * by default colorFunction = colorFunction() */ - hm.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, hm) : colorFunction$$1; }; + bhm.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, bhm) : colorFunction$$1; }; /** * Gets / sets the xSize - * (see {@link heatmap#xSize}) + * (see {@link bubbleHeatmap#xSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property * by default xSize = undefined */ - hm.xSize = function(_) { return arguments.length ? (xSize = _, hm) : xSize; }; + bhm.xSize = function(_) { return arguments.length ? (xSize = _, bhm) : xSize; }; /** * Gets / sets the xSpacerSize - * (see {@link heatmap#xSpacerSize}) + * (see {@link bubbleHeatmap#xSpacerSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property * by default xSpacerSize = undefined */ - hm.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, hm) : xSpacerSize; }; + bhm.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, bhm) : xSpacerSize; }; /** * Gets / sets the ySize - * (see {@link heatmap#ySize}) + * (see {@link bubbleHeatmap#ySize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property * by default ySize = undefined */ - hm.ySize = function(_) { return arguments.length ? (ySize = _, hm) : ySize; }; + bhm.ySize = function(_) { return arguments.length ? (ySize = _, bhm) : ySize; }; /** * Gets / sets the ySpacerSize - * (see {@link heatmap#ySpacerSize}) + * (see {@link bubbleHeatmap#ySpacerSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {bubbleHeatmap | number} + * @memberof bubbleHeatmap * @property * by default ySpacerSize = undefined */ - hm.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, hm) : ySpacerSize; }; - // hm.yKeySortingFunction = function(_) { return arguments.length ? (yKeySortingFunction = _, hm) : yKeySortingFunction; } - // hm.xKeySortingFunction = function(_) { return arguments.length ? (xKeySortingFunction = _, hm) : xKeySortingFunction; } + bhm.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, bhm) : ySpacerSize; }; + // bhm.yKeySortingFunction = function(_) { return arguments.length ? (yKeySortingFunction = _, bhm) : yKeySortingFunction; } + // bhm.xKeySortingFunction = function(_) { return arguments.length ? (xKeySortingFunction = _, bhm) : xKeySortingFunction; } - function hm() { + function bhm() { var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); cellKeys = d3.keys(data); - - xValues = unique(cellKeys.map(xExtractor)); - yValues = unique(cellKeys.map(yExtractor)); - vValues = unique(cellKeys.map(vExtractor)); - cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }); - log('heatmap', 'cells are sorted by', cellKeys); + utils$1.con.log('bubbleHeatmap', 'cells are sorted by', cellKeys); - log('heatmap', 'x and y keys are', {x: xValues, y:yValues}); + xValues = utils$1.arr.unique(cellKeys.map(xExtractor)); + yValues = utils$1.arr.unique(cellKeys.map(yExtractor)); + rValues = utils$1.arr.unique(cellKeys.map(rExtractor)); + vValues = utils$1.arr.unique(cellKeys.map(vExtractor)); + utils$1.con.log('bubbleHeatmap', 'x and y keys are', {x: xValues, y:yValues}); var xDim = xValues.length, yDim = yValues.length; - ySize = calculateWidthOfObject(spaceY, yDim, yMinObjectSize, yMaxObjectSize, objectSpacer, overflowQ); - xSize = calculateWidthOfObject(spaceX, xDim, xMinObjectSize, xMaxObjectSize, objectSpacer, overflowQ); - ySpacerSize = calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); - xSpacerSize = calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); - // console.table({ - // x:{ - // object: xSize, - // spacer: xSpacerSize, - // dim: xDim - // }, - // y:{ - // object: ySize, - // spacer: ySpacerSize, - // dim: yDim - // } - // - // }) - log('heatmap', 'size of', {x: xSize, y: ySize}); + var extent = [Math.min(...rValues) - domainPadding,Math.max(...rValues) + domainPadding]; + + ySize = utils$1.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + xSize = utils$1.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + ySpacerSize = utils$1.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); + xSpacerSize = utils$1.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); + utils$1.con.log('bubbleHeatmap', 'size of', {x: xSize, y: ySize}); + scale.domain(extent).range([Math.min(minObjectSize/2, Math.min(ySize, xSize)/2), Math.min(ySize, xSize)/2]); + var ySpacer = groupingSpacer() .horizontalQ(false) - .moveby('category') - .numberOfObjects(yDim) - .objectClass(hypenate(objectClass, 'row')) - .objectSize(ySize + ySpacerSize) - .spacerSize(0) - .transitionDuration(transitionDuration) - .easeFunc(easeFunc) + .moveby('category').numberOfObjects(yDim) + .objectClass(utils$1.str.hypenate(objectClass, 'row')) + .objectSize(ySize).spacerSize(ySpacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc) .namespace('row'); var xSpacer = groupingSpacer() .horizontalQ(true) - .moveby('category') - .numberOfObjects(xDim) + .moveby('category').numberOfObjects(xDim) .objectClass(objectClass) - .objectSize(xSize + xSpacerSize) - .spacerSize(0) - .transitionDuration(transitionDuration) - .easeFunc(easeFunc); + .objectSize(xSize).spacerSize(xSpacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc); ySpacer(container, yValues, 0); - container.selectAll('g.'+hypenate(objectClass, 'row')) - .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); - - var cells = container.selectAll('g:not(.to-remove).'+objectClass); - - - if (cellKeys.length != yValues.length * xValues.length) { - var lookup = {}; - cellKeys.map(function(k, i){ - lookup[xExtractor(k)+'::'+yExtractor(k)] = k; - }); - - var positionedCellKeys = []; - for (var i = 0; i < yValues.length; i++) { - for (var j = 0; j < xValues.length; j++) { - var lookupValue = lookup[xValues[j]+"::"+yValues[i]]; - if (lookupValue == undefined) { - positionedCellKeys.push(undefined); - } else { - positionedCellKeys.push(lookupValue); - } - } - } - - cells.data(positionedCellKeys); - - // maybe breaks this - // !!!!!! IMPORTANT NOTE TODO LOOK HERE BUG - cellKeys = positionedCellKeys; - } else { - cells.data(cellKeys); - } - - + container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'row')) + .each(function(d, i){ + xSpacer(d3.select(this), xValues, 0); + }); + var cells = container.selectAll('g:not(.to-remove).'+objectClass).data(cellKeys); var parentIndexArray = []; cells.each(function(d, i){ parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); }); + colorFunction$$1 = colorFunction$$1.colorBy() == 'index' ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) : colorFunction$$1.dataExtent([0, Math.max(...vValues)]); cells.each(function(key, i) { - log('heatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); + utils$1.con.log('bubbleHeatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); - var t = d3.select(this); - if (key == undefined) {return} - var currentData = data[key], + var t = d3.select(this), + currentData = data[key], value = vExtractor(key, i), + radius= rExtractor(key, i), i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), fillColor = colorFunction$$1(key, value, i, 'fill'), // prevent duplicate computation strokeColor = colorFunction$$1(key, value, i, 'stroke'); - var c = safeSelect(t, 'rect', hypenate(objectClass,'rect')); - c.attr('width', xSize + xSpacerSize - objectStrokeWidth) - .attr('height', ySize + ySpacerSize - objectStrokeWidth) + utils$1.con.log('bubbleHeatmap', 'radius',{radius: radius, scaled: scale(radius), extent: extent, range:scale.range()}); + + var c = utils$1.sel.safeSelect(t, 'circle', utils$1.str.hypenate(objectClass,'circle')); + c.attr('cx', xSize / 2) + .attr('cy', ySize / 2 ) + .attr('r', scale(radius)) .attr('fill', fillColor) - .attr('x', objectStrokeWidth/2) - .attr('y', objectStrokeWidth/2) - .attr('stroke', "#000") - .attr('stroke-width', objectStrokeWidth); + .attr('stroke', strokeColor) + .attr('stroke-width', bubbleStrokeWidth); }); - tooltip$$1.selection(cells.selectAll('rect.'+hypenate(objectClass, 'rect'))) + tooltip$$1.selection(cells.selectAll('circle.'+utils$1.str.hypenate(objectClass, 'circle'))) .data(data); // .keys(['r', 'v']) - // .header(function(d, i){return hypenate(data[d][xKey], data[d][yKey]) }) + // .header(function(d, i){return utils.str.hypenate(data[d][xKey], data[d][yKey]) }) tooltip$$1(); } - return hm; + return bhm; } -/******************************************************************************* -** ** -** ** -** BOX AND WHISKER ** -** ** -** ** -*******************************************************************************/ /** - * Creates a boxwhisker + * Creates a heatmap * - * {@link https://sumneuron.gitlab.io/d3sm/demos/box-whiskers/index.html Demo} - * @constructor boxwhisker + * {@link https://sumneuron.gitlab.io/d3sm/demos/heatmap-heatmap/index.html Demo} + * @constructor heatmap * @param {d3.selection} selection - * @namespace boxwhisker - * @returns {function} boxwhisker + * @namespace heatmap + * @returns {function} heatmap */ -function boxwhisker( selection ) { - var +function heatmap( selection ) { + var /** - * Data to plot. Assumed to be a object, where each key corresponds to a box - * (see {@link boxwhisker#data}) + * Data to plot. Assumed to be a object, where each key corresponds to a cell + * (see {@link heatmap#data}) * @param {Object} [data=undefined] - * @memberof boxwhisker# + * @memberof heatmap# * @property */ data, + /** - * Which direction to render the boxes in - * (see {@link boxwhisker#orient}) - * @param {number} [orient='horizontal'] - * @memberof boxwhisker# - * @property - */ - orient = 'horizontal', - /** - * Amount of horizontal space (in pixels) avaible to render the boxwhisker in - * (see {@link boxwhisker#spaceX}) + * Amount of horizontal space (in pixels) avaible to render the heatmap in + * (see {@link heatmap#spaceX}) * @param {number} [spaceX=undefined] - * @memberof boxwhisker# + * @memberof heatmap# * @property */ spaceX, /** - * Amount of vertical space (in pixels) avaible to render the boxwhisker in - * (see {@link boxwhisker.spaceY}) + * Amount of vertical space (in pixels) avaible to render the heatmap in + * (see {@link heatmap.spaceY}) * @param {number} [spaceY=undefined] - * @memberof boxwhisker# + * @memberof heatmap# * @property */ spaceY, + /** - * Whether or not to allow boxwhisker to render elements pass the main spatial dimension - * given the orientation (see {@link boxwhisker#orient}), where {@link boxwhisker#orient}="horizontal" - * the main dimension is {@link boxwhisker#spaceX} and where {@link boxwhisker#orient}="vertical" - * the main dimension is {@link boxwhisker#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof boxwhisker# + * The internal key of the cell specifiying to which x axis key it belongs + * (see {@link heatmap.xKey}) + * @param {string} [xKey='x'] + * @memberof heatmap# * @property */ - overflowQ = true, - + xKey = 'x', /** - * An array - putatively of other arrays - depicting how boxes should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof boxwhisker# + * The internal key of the cell specifiying to which y axis key it belongs + * (see {@link heatmap.yKey}) + * @param {string} [yKey='y'] + * @memberof heatmap# * @property */ - grouping, - quartilesKey = 'quartiles', // key in object where quartiles are stored - quartilesKeys = ["0.00", "0.25", "0.50", "0.75", "1.00"], // keys in quartiles object mapping values to min, q1, q2, q3 and max - + yKey = 'y', /** - * How to get the value of the boxwhisker - * @param {function} [valueExtractor=function(key, index) { return data[key][quartilesKey] }] - * @memberof boxwhisker# + * The internal key of the cell specifiying what value to use to determine the color + * (see {@link heatmap.vKey}) + * @param {string} [vKey='v'] + * @memberof heatmap# * @property */ - valueExtractor = function(key, index) { return data[key][quartilesKey] }, + vKey = 'v', + /** - * How to sort the boxes - if {@link boxwhisker#grouping} is not provided. - * @param {function} [sortingFunction=descending] - * @memberof boxwhisker# + * Function for extracting the the value from xKey. + * (see {@link heatmap.xExtractor}) + * @param {function} [xExtractor=function(key, i) { return data[key][xKey] }] + * @returns {string} + * @memberof heatmap# * @property - * default - * function(keyA, keyB) {return d3.descending( - * valueExtractor(keyA)[quartilesKeys[4]], - * valueExtractor(keyB)[quartilesKeys[4]] - * )} */ - sortingFunction = function(keyA, keyB) {return d3.descending( - valueExtractor(keyA)[quartilesKeys[4]], - valueExtractor(keyB)[quartilesKeys[4]] - )}, + xExtractor = function(key, i) {return data[key][xKey] }, /** - * The scale for which boxwhisker values should be transformed by - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof boxwhisker# + * Function for extracting the the value from yKey. + * (see {@link heatmap.yExtractor}) + * @param {function} [yExtractor=function(key, i) { return data[key][yKey] }] + * @returns {string} + * @memberof heatmap# * @property */ - scale = d3.scaleLinear(), + yExtractor = function(key, i) { return data[key][yKey] }, + /** - * The padding for the domain of the scale (see {@link boxwhisker#scale}) - * @param {number} [domainPadding=0.5] - * @memberof boxwhisker# + * Function for extracting the the value from vKey. + * (see {@link heatmap.vExtractor}) + * @param {function} [vExtractor=function(key, i) { return data[key][vKey] }] + * @returns {number} + * @memberof heatmap# * @property */ - domainPadding = 0.5, + vExtractor = function(key, i) { return data[key][vKey] }, + + /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link boxwhisker#orient}), where {@link boxwhisker#orient}="horizontal" - * the main dimension is {@link boxwhisker#spaceX} and where {@link boxwhisker#orient}="vertical" - * the main dimension is {@link boxwhisker#spaceY} between boxes - * @param {number} [objectSpacer=0.05] - * @memberof boxwhisker# + * Whether or not to allow heatmap to render elements pass the main spatial dimension + * given the orientation (see {@link heatmap#orient}), where {@link heatmap#orient}="bottom" or {@link heatmap#orient}="top" + * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="left" or {@link heatmap#orient}="right" + * the main dimension is {@link heatmap#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof heatmap# * @property */ - objectSpacer = 0.05, + overflowQ = false, + /** - * The minimum size that an object can be - * @param {number} [minObjectSize=15] - * @memberof boxwhisker# + * Default space for the spacer (percentage) of main dimension given the orientation + * (see {@link heatmap#orient}), where {@link heatmap#orient}="horizontal" + * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="vertical" + * the main dimension is {@link heatmap#spaceY} between bubbles + * @param {number} [objectSpacer=0.0] + * @memberof heatmap# * @property */ - minObjectSize = 15, + objectSpacer = 0.0, /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=50] - * @memberof boxwhisker# + * The minimum size that an object can be in the y dimension + * @param {number} [minObjectSize=50] + * @memberof heatmap# * @property */ - maxObjectSize = 50, + yMinObjectSize = 50, /** - * Percent of box and whisker size that whiskers will be rendered - * see {@link boxwhisker#objectSize} - * @param {number} [whiskerWidthPercent=0.6] - * @memberof boxwhisker# + * The minimum size that an object can be in the x dimension + * @param {number} [minObjectSize=50] + * @memberof heatmap# * @property */ - whiskerWidthPercent = .6, + xMinObjectSize = 50, /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof boxwhisker# + * The maximum size that an object can be in the x dimension + * @param {number} [maxObjectSize=100] + * @memberof heatmap# * @property */ - colorFunction$$1 = colorFunction(), + xMaxObjectSize = 100, /** - * The stroke width of the boxes - * @param {number} [boxStrokeWidth=2] - * @memberof boxwhisker# + * The maximum size that an object can be in the y dimension + * @param {number} [maxObjectSize=100] + * @memberof heatmap# * @property */ - boxStrokeWidth = 2, + yMaxObjectSize = 100, + + /** - * The stroke width of the whiskers - * @param {number} [whiskerStrokeWidth=2] - * @memberof boxwhisker# + * The stroke width of the bubbles + * @param {number} [objectStrokeWidth=2] + * @memberof heatmap# * @property */ - whiskerStrokeWidth = 2, + objectStrokeWidth = 2, + // colorFunc = colorFunction(), /** * Color of the background * @param {string} [backgroundFill="transparent"] - * @memberof boxwhisker# + * @memberof heatmap# * @property */ backgroundFill = 'transparent', /** - * Namespace for all items made by this instance of boxwhisker - * @param {string} [namespace="d3sm-box-whisker"] - * @memberof boxwhisker# + * Namespace for all items made by this instance of heatmap + * @param {string} [namespace="d3sm-heatmap"] + * @memberof heatmap# * @property */ - namespace = 'd3sm-box-whisker', + namespace = 'd3sm-heatmap', /** - * Class name for boxwhisker container ( element) - * @param {string} [objectClass="box-whisk"] - * @memberof boxwhisker# + * Class name for heatmap container ( element) + * @param {string} [objectClass="heatmap"] + * @memberof heatmap# * @property */ - objectClass = 'box-whisk', - + objectClass = 'heatmap', /** * Duration of all transitions of this element * @param {number} [transitionDuration=1000] - * @memberof boxwhisker# + * @memberof heatmap# * @property */ transitionDuration = 1000, /** * Easing function for transitions * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof boxwhisker# + * @memberof heatmap# * @property */ easeFunc = d3.easeExp, /** - * The keys of the boxes - * @param {string[]} [boxKeys=undefined] - * @memberof boxwhisker# + * Stores the keys of all the cells + * Calculated after heatmap called. + * @param {string[]} [cellKeys=undefined] + * @memberof heatmap# * @property */ - boxKeys, + cellKeys, /** - * The values of the boxes - * @param {string[]} [boxValues=undefined] - * @memberof boxwhisker# + * Stores the list of utils.arr.unique xValues + * Calculated after heatmap called. + * @param {string[]} [xValues=undefined] + * @memberof heatmap# * @property */ - boxValues, + xValues, /** - * The objectSize (actual width) used by the boxes - * @param {number} [objectSize=undefined] - * @memberof boxwhisker# + * Stores the list of utils.arr.unique yValues + * Calculated after heatmap called. + * @param {string[]} [yValues=undefined] + * @memberof heatmap# * @property */ - objectSize, + yValues, /** - * The spacerSize (actual width) used by the spacers between the boxes - * @param {number} [spacerSize=undefined] - * @memberof boxwhisker# + * Stores the list of utils.arr.unique vValues + * Calculated after heatmap called. + * @param {string[]} [vValues=undefined] + * @memberof heatmap# * @property */ - spacerSize, + vValues, + + xKeySortingFunction = function(a, b) { return xValues.indexOf(xExtractor(a)) - xValues.indexOf(xExtractor(b)) }, + yKeySortingFunction = function(a, b) { return yValues.indexOf(yExtractor(a)) - yValues.indexOf(yExtractor(b)) }, /** - * Instance of Tooltip - * @param {function} [tooltip=tooltip()] - * @memberof boxwhisker# + * Instance of ColorFunction with .colorBy set to 'category' + * @function colorFunction + * @memberof heatmap# * @property */ - tooltip$$1 = tooltip(); - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {boxwhisker | d3.selection} - * @memberof boxwhisker - * @property - * by default selection = selection - */ - boxwhisker.selection = function(_) { return arguments.length ? (selection = _, boxwhisker) : selection; }; + colorFunction$$1 = colorFunction().colorBy('category'), /** - * Gets or sets the data - * (see {@link boxwhisker#data}) - * @param {number} [_=none] - * @returns {boxwhisker | object} - * @memberof boxwhisker - * @property - */ - boxwhisker.data = function(_) { return arguments.length ? (data = _, boxwhisker) : data; }; + * Instance of Tooltip + * @function tooltip + * @memberof heatmap# + * @property + */ + tooltip$$1 = tooltip(), + /** - * Gets or sets the orient of the boxes - * (see {@link boxwhisker#orient}) - * @param {number} [_=none] - * @returns {boxwhisker | object} - * @memberof boxwhisker - * @property - */ - boxwhisker.orient = function(_) { return arguments.length ? (orient = _, boxwhisker) : orient; }; + * store the size the heatmap could be in the x dimension + * the actuall size of the heatmap will be the min of xSize and {@link heatmap#ySize} + * Calculated after heatmap called. + * @param {string[]} [xSize=undefined] + * @memberof heatmap# + * @property + */ + xSize, /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link boxwhisker#spaceX}) + * store the size of the spacer in the x dimension + * Calculated after heatmap called. + * @param {string[]} [xSpacerSize=undefined] + * @memberof heatmap# + * @property + */ + xSpacerSize, + + /** + * store the size the heatmap could be in the y dimension + * the actuall size of the heatmap will be the min of xSize and {@link heatmap#xSize} + * Calculated after heatmap called. + * @param {string[]} [ySize=undefined] + * @memberof heatmap# + * @property + */ + ySize, + /** + * store the size of the spacer in the y dimension. + * Calculated after heatmap called. + * @param {string[]} [xSpacerSize=undefined] + * @memberof heatmap# + * @property + */ + ySpacerSize; + + /** + * Gets or sets the selection in which items are manipulated + * @param {d3.selection} [_=none] + * @returns {heatmap | d3.selection} + * @memberof heatmap + * @property + * by default selection = selection + */ + hm.selection = function(_) { return arguments.length ? (selection = _, hm) : selection; }; + /** + * Gets or sets the data + * (see {@link heatmap#data}) + * @param {number} [_=none] + * @returns {heatmap | object} + * @memberof heatmap + * @property + */ + hm.data = function(_) { return arguments.length ? (data = _, hm) : data; }; + // hm.orient = function(_) { return arguments.length ? (orient = _, hm) : orient; } + /** + * Gets or sets the amount of horizontal space in which items are manipulated + * (see {@link heatmap#spaceX}) * @param {number} [_=none] should be a number > 0 - * @returns {boxwhisker | number} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property * by default spaceX = undefined */ - boxwhisker.spaceX = function(_) { return arguments.length ? (spaceX = _, boxwhisker) : spaceX; }; + hm.spaceX = function(_) { return arguments.length ? (spaceX = _, hm) : spaceX; }; /** * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link boxwhisker#spaceY}) + * (see {@link heatmap#spaceY}) * @param {number} [_=none] should be a number > 0 - * @returns {boxwhisker | number} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property * by default spaceY = undefined */ - boxwhisker.spaceY = function(_) { return arguments.length ? (spaceY = _, boxwhisker) : spaceY; }; + hm.spaceY = function(_) { return arguments.length ? (spaceY = _, hm) : spaceY; }; + /** - * Gets / sets whether or not boxwhisker is allowed to go beyond specified dimensions - * (see {@link boxwhisker#overflowQ}) - * @param {boolean} [_=none] - * @returns {boxwhisker | boolean} - * @memberof boxwhisker + * Gets or sets the xKey + * (see {@link heatmap#xKey}) + * @param {string} [_=none] + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default overflowQ = false + * by default xKey = 'x' */ - boxwhisker.overflowQ = function(_) { return arguments.length ? (overflowQ = _, boxwhisker) : overflowQ; }; + hm.xKey = function(_) { return arguments.length ? (xKey = _, hm) : xKey; }; /** - * Gets / sets the grouping of the boxes - * (see {@link boxwhisker#grouping}) - * @param {Array[]} [_=none] - * @returns {boxwhisker | Array[]} - * @memberof boxwhisker + * Gets or sets the yKey + * (see {@link heatmap#yKey}) + * @param {string} [_=none] + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default grouping = undefined + * by default yKey = 'y' */ - boxwhisker.grouping = function(_) { return arguments.length ? (grouping = _, boxwhisker) : grouping; }; + hm.yKey = function(_) { return arguments.length ? (yKey = _, hm) : yKey; }; + /** - * Gets / sets the quartilesKey - * (see {@link boxwhisker#quartilesKey}) + * Gets or sets the vKey + * (see {@link heatmap#vKey}) * @param {string} [_=none] - * @returns {boxwhisker | string} - * @memberof boxwhisker + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default quartilesKey = quartiles + * by default vKey = 'y' */ - boxwhisker.quartilesKey = function(_) { return arguments.length ? (quartilesKey = _, boxwhisker) : quartilesKey; }; + hm.vKey = function(_) { return arguments.length ? (vKey = _, hm) : vKey; }; + /** - * Gets / sets the quartilesKeys - * (see {@link boxwhisker#quartilesKeys}) + * Gets or sets the cellKeys + * (see {@link heatmap#cellKeys}) * @param {string[]} [_=none] - * @returns {boxwhisker | string[]} - * @memberof boxwhisker + * @returns {heatmap | string[]} + * @memberof heatmap * @property - * by default quartilesKeys = [Q0, Q1, Q2, Q3, Q4] + * by default cellKeys = undefined */ - boxwhisker.quartilesKeys = function(_) { return arguments.length ? (quartilesKeys = _, boxwhisker) : quartilesKeys; }; + hm.cellKeys = function(_) { return arguments.length ? (cellKeys = _, hm) : cellKeys; }; /** - * Gets / sets the valueExtractor - * (see {@link boxwhisker#valueExtractor}) - * @param {function} [_=none] - * @returns {boxwhisker | function} - * @memberof boxwhisker + * Gets or sets the xValues + * (see {@link heatmap#xValues}) + * @param {string[]} [_=none] + * @returns {heatmap | string[]} + * @memberof heatmap * @property - * by default valueExtractor = function(key, index) { return data[key][quartilesKey] }, + * by default xValues = undefined + */ + hm.xValues = function(_) { return arguments.length ? (xValues = _, hm) : xValues; }; + /** + * Gets or sets the yValues + * (see {@link heatmap#yValues}) + * @param {string[]} [_=none] + * @returns {heatmap | string[]} + * @memberof heatmap + * @property + * by default yValues = undefined + */ + hm.yValues = function(_) { return arguments.length ? (yValues = _, hm) : yValues; }; + /** + * Gets or sets the vValues + * (see {@link heatmap#vValues}) + * @param {number[]} [_=none] + * @returns {heatmap | number[]} + * @memberof heatmap + * @property + * by default vValues = undefined */ + hm.vValues = function(_) { return arguments.length ? (vValues = _, hm) : vValues; }; + - boxwhisker.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, boxwhisker) : valueExtractor; }; /** - * Gets / sets the sortingFunction - * (see {@link boxwhisker#sortingFunction}) + * Gets or sets the xExtractor + * (see {@link heatmap#xExtractor}) * @param {function} [_=none] - * @returns {boxwhisker | function} - * @memberof boxwhisker + * @returns {heatmap | function} + * @memberof heatmap * @property - * by default sortingFunction = function(keyA, keyB) {return d3.descending( - * valueExtractor(keyA)[quartilesKeys[4]], - * valueExtractor(keyB)[quartilesKeys[4]] - * )}, + * by default xExtractor = undefined */ - boxwhisker.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, boxwhisker) : sortingFunction; }; + hm.xExtractor = function(_) { return arguments.length ? (xExtractor = _, hm) : xExtractor; }; /** - * Gets / sets the scale for which the boxwhisker values should be transformed by - * (see {@link boxwhisker#scale}) - * @param {d3.scale} [_=none] - * @returns {boxwhisker | d3.scale} - * @memberof boxwhisker + * Gets or sets the yExtractor + * (see {@link heatmap#yExtractor}) + * @param {function} [_=none] + * @returns {heatmap | function} + * @memberof heatmap * @property - * by default scale = d3.scaleLinear() + * by default yExtractor = undefined */ - boxwhisker.scale = function(_) { return arguments.length ? (scale = _, boxwhisker) : scale; }; + hm.yExtractor = function(_) { return arguments.length ? (yExtractor = _, hm) : yExtractor; }; /** - * Gets / sets the padding for the domain of the scale - * (see {@link boxwhisker#domainPadding}) - * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker + * Gets or sets the vExtractor + * (see {@link heatmap#vExtractor}) + * @param {function} [_=none] + * @returns {heatmap | function} + * @memberof heatmap * @property - * by default domainPadding = 0.5 + * by default vExtractor = undefined */ - boxwhisker.domainPadding = function(_) { return arguments.length ? (domainPadding = _, boxwhisker) : domainPadding; }; + hm.vExtractor = function(_) { return arguments.length ? (vExtractor = _, hm) : vExtractor; }; + + /** + * Gets / sets whether or not heatmap is allowed to go beyond specified dimensions + * (see {@link heatmap#spaceX}) + * @param {boolean} [_=none] + * @returns {heatmap | boolean} + * @memberof heatmap + * @property + * by default overflowQ = false + */ + hm.overflowQ = function(_) { return arguments.length ? (overflowQ = _, hm) : overflowQ; }; /** * Gets / sets objectSpacer - * (see {@link boxwhisker#objectSpacer}) + * (see {@link heatmap#objectSpacer}) * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default objectSpacer = 0.05 + * by default objectSpacer = 0.0 */ - boxwhisker.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, boxwhisker) : objectSpacer; }; + hm.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, objectSpacer) : data; }; /** * Gets / sets the minObjectSize - * (see {@link boxwhisker#minObjectSize}) + * (see {@link heatmap#minObjectSize}) * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default minObjectSize = 15 + * by default minObjectSize = 50 */ - boxwhisker.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, boxwhisker) : minObjectSize; }; + hm.yMinObjectSize = function(_) { return arguments.length ? (yMinObjectSize = _, hm) : yMinObjectSize; }; /** * Gets / sets the maxObjectSize - * (see {@link boxwhisker#maxObjectSize}) + * (see {@link heatmap#maxObjectSize}) * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default maxObjectSize = 50 + * by default maxObjectSize = 100 */ - boxwhisker.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, boxwhisker) : maxObjectSize; }; + hm.yMaxObjectSize = function(_) { return arguments.length ? (yMaxObjectSize = _, hm) : yMaxObjectSize; }; /** - * Gets / sets the whiskerWidthPercent - * (see {@link boxwhisker#whiskerWidthPercent}) + * Gets / sets the minObjectSize + * (see {@link heatmap#minObjectSize}) * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker - * @property - * by default maxObjectSize = 0.6 - */ - boxwhisker.whiskerWidthPercent = function(_) { return arguments.length ? (whiskerWidthPercent = _, boxwhisker) : whiskerWidthPercent; }; - /** - * Gets / sets the colorFunction - * (see {@link boxwhisker#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {boxwhisker | colorFunction} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default colorFunction = colorFunction() + * by default minObjectSize = 50 */ - boxwhisker.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, boxwhisker) : colorFunction$$1; }; + hm.xMinObjectSize = function(_) { return arguments.length ? (xMinObjectSize = _, hm) : xMinObjectSize; }; /** - * Gets / sets the boxStrokeWidth - * (see {@link boxwhisker#boxStrokeWidth}) + * Gets / sets the maxObjectSize + * (see {@link heatmap#maxObjectSize}) * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default boxStrokeWidth = 2 + * by default maxObjectSize = 100 */ - boxwhisker.boxStrokeWidth = function(_) { return arguments.length ? (boxStrokeWidth = _, boxwhisker) : boxStrokeWidth; }; + hm.xMaxObjectSize = function(_) { return arguments.length ? (xMaxObjectSize = _, hm) : xMaxObjectSize; }; /** - * Gets / sets the whiskerStrokeWidth - * (see {@link boxwhisker#whiskerStrokeWidth}) + * Gets / sets the objectStrokeWidth + * (see {@link heatmap#objectStrokeWidth}) * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default whiskerStrokeWidth = 2 + * by default objectStrokeWidth = 2 */ - boxwhisker.whiskerStrokeWidth = function(_) { return arguments.length ? (whiskerStrokeWidth = _, boxwhisker) : whiskerStrokeWidth; }; - + hm.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, hm) : objectStrokeWidth; }; /** * Gets / sets the backgroundFill - * (see {@link boxwhisker#backgroundFill}) + * (see {@link heatmap#backgroundFill}) * @param {string} [_=none] - * @returns {boxwhisker | string} - * @memberof boxwhisker + * @returns {heatmap | string} + * @memberof heatmap * @property * by default backgroundFill = 'transparent' */ - boxwhisker.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, boxwhisker) : backgroundFill; }; + hm.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, hm) : backgroundFill; }; /** * Gets / sets the namespace - * (see {@link boxwhisker#namespace}) + * (see {@link heatmap#namespace}) * @param {string} [_=none] - * @returns {boxwhisker | string} - * @memberof boxwhisker + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default namespace = 'd3sm-boxwhisker' + * by default namespace = 'd3sm-heatmap' */ - boxwhisker.namespace = function(_) { return arguments.length ? (namespace = _, boxwhisker) : namespace; }; + hm.namespace = function(_) { return arguments.length ? (namespace = _, hm) : namespace; }; /** * Gets / sets the objectClass - * (see {@link boxwhisker#objectClass}) + * (see {@link heatmap#objectClass}) * @param {string} [_=none] - * @returns {boxwhisker | string} - * @memberof boxwhisker + * @returns {heatmap | string} + * @memberof heatmap * @property * by default objectClass = 'tick-group' */ - boxwhisker.objectClass = function(_) { return arguments.length ? (objectClass = _, boxwhisker) : objectClass; }; + hm.objectClass = function(_) { return arguments.length ? (objectClass = _, hm) : objectClass; }; /** * Gets / sets the transitionDuration - * (see {@link boxwhisker#transitionDuration}) + * (see {@link heatmap#transitionDuration}) * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property * by default transitionDuration = 1000 */ - boxwhisker.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, boxwhisker) : transitionDuration; }; + hm.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, hm) : transitionDuration; }; /** * Gets / sets the easeFunc - * (see {@link boxwhisker#easeFunc}) + * (see {@link heatmap#easeFunc}) * @param {d3.ease} [_=none] - * @returns {boxwhisker | d3.ease} - * @memberof boxwhisker + * @returns {heatmap | d3.ease} + * @memberof heatmap * @property * by default easeFunc = d3.easeExp */ - boxwhisker.easeFunc = function(_) { return arguments.length ? (easeFunc = _, boxwhisker) : easeFunc; }; + hm.easeFunc = function(_) { return arguments.length ? (easeFunc = _, hm) : easeFunc; }; /** - * Gets / sets the barKeys - * (see {@link boxwhisker#boxKeys}) - * @param {string[]} [_=none] - * @returns {boxwhisker | string[]} - * @memberof boxwhisker + * Gets / sets the tooltip + * (see {@link heatmap#tooltip}) + * @param {tooltip} [_=none] + * @returns {heatmap | tooltip} + * @memberof heatmap * @property - * by default boxKeys = undefined + * by default tooltip = tooltip() */ - boxwhisker.boxKeys = function(_) { return arguments.length ? (boxKeys = _, boxwhisker) : boxKeys; }; + hm.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, hm) : tooltip$$1; }; + /** - * Gets / sets the boxValues - * (see {@link boxwhisker#boxValues}) - * @param {number[]} [_=none] - * @returns {boxwhisker | number[]} - * @memberof boxwhisker + * Gets / sets the colorFunction + * (see {@link heatmap#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {heatmap | colorFunction} + * @memberof heatmap * @property - * by default boxValues = undefined + * by default colorFunction = colorFunction() */ - boxwhisker.boxValues = function(_) { return arguments.length ? (boxValues = _, boxwhisker) : boxValues; }; + hm.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, hm) : colorFunction$$1; }; + /** - * Gets / sets the objectSize - * (see {@link boxwhisker#objectSize}) + * Gets / sets the xSize + * (see {@link heatmap#xSize}) * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default objectSize = undefined + * by default xSize = undefined */ - boxwhisker.objectSize = function(_) { return arguments.length ? (objectSize = _, boxwhisker) : objectSize; }; + hm.xSize = function(_) { return arguments.length ? (xSize = _, hm) : xSize; }; /** - * Gets / sets the spacerSize - * (see {@link boxwhisker#spacerSize}) + * Gets / sets the xSpacerSize + * (see {@link heatmap#xSpacerSize}) * @param {number} [_=none] - * @returns {boxwhisker | number} - * @memberof boxwhisker + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default spacerSize = undefined + * by default xSpacerSize = undefined */ - boxwhisker.spacerSize = function(_) { return arguments.length ? (spacerSize = _, boxwhisker) : spacerSize; }; + hm.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, hm) : xSpacerSize; }; /** - * Gets / sets the tooltip - * (see {@link boxwhisker#tooltip}) - * @param {tooltip} [_=none] - * @returns {boxwhisker | tooltip} - * @memberof boxwhisker + * Gets / sets the ySize + * (see {@link heatmap#ySize}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default tooltip = tooltip() + * by default ySize = undefined + */ + hm.ySize = function(_) { return arguments.length ? (ySize = _, hm) : ySize; }; + /** + * Gets / sets the ySpacerSize + * (see {@link heatmap#ySpacerSize}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap + * @property + * by default ySpacerSize = undefined */ - boxwhisker.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, boxwhisker) : tooltip$$1; }; + hm.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, hm) : ySpacerSize; }; + // hm.yKeySortingFunction = function(_) { return arguments.length ? (yKeySortingFunction = _, hm) : yKeySortingFunction; } + // hm.xKeySortingFunction = function(_) { return arguments.length ? (xKeySortingFunction = _, hm) : xKeySortingFunction; } - function boxwhisker() { - // for convenience in handling orientation specific values - var horizontalQ = (orient == 'horizontal') ? true : false; - var verticalQ = !horizontalQ; - // background cliping rectangle + function hm() { var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); - - // if grouping is undefined sort keys by sorting funct - var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping; - // to prevent re-calculation and getters to be passed to axes - boxKeys = flatten(ordered); - boxValues = boxKeys.map(valueExtractor); - - - var numberOfObjects = boxKeys.length; - var extent = [ - Math.min(...boxValues.map(function(d,i){return d[quartilesKeys[0]]})) - domainPadding, - Math.max(...boxValues.map(function(d,i){return d[quartilesKeys[4]]})) + domainPadding - ]; - - // set the scale - scale.domain(extent).range(horizontalQ ? [0,spaceY] : [spaceX, 0]); - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - objectSize = calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - // calculate spacer size if needed - spacerSize = calculateWidthOfSpacer(boxKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); - // make the nested groups - var spacerFunction = groupingSpacer() - .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) - .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc) - .namespace(namespace); - - // move stuff - spacerFunction(container, ordered, 0); - - var parentIndexArray = []; - container.selectAll('g:not(.to-remove).'+objectClass) - .each(function(d, i){if (hasQ(boxKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}}); + var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + cellKeys = d3.keys(data); + xValues = utils$1.arr.unique(cellKeys.map(xExtractor)); + yValues = utils$1.arr.unique(cellKeys.map(yExtractor)); + vValues = utils$1.arr.unique(cellKeys.map(vExtractor)); - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' - ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) - : colorFunction$$1.dataExtent(extent); - - - // set attributes for box and whiskers - container.selectAll('g:not(.to-remove).'+objectClass).each(function(key, i) { - var t = d3.select(this), - currentData = data[key], - - quartiles$$1 = valueExtractor(key, i), - q0 = quartiles$$1[quartilesKeys[0]], - q1 = quartiles$$1[quartilesKeys[1]], - q2 = quartiles$$1[quartilesKeys[2]], - q3 = quartiles$$1[quartilesKeys[3]], - q4 = quartiles$$1[quartilesKeys[4]]; - - var i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, q2, i, 'fill'), // prevent duplicate computation - strokeColor = colorFunction$$1(key, q2, i, 'stroke'); - - - var - whisk = safeSelect(t, 'g', 'whisker'), - uWhisk = safeSelect(whisk, 'path', 'upper'), - lWhisk = safeSelect(whisk, 'path', 'lower'), - quart = safeSelect(t, 'g', 'quartile'), - uQuart = safeSelect(quart, 'rect', 'upper'), - lQuart = safeSelect(quart, 'rect', 'lower'), - mQuart = safeSelect(quart, 'circle', 'median'); - - - // set upper quartile (q3) - uQuart.transition().duration(transitionDuration).ease(easeFunc) - .attr('width', horizontalQ ? objectSize : scale(q3) - scale(q2)) - .attr('height', verticalQ ? objectSize : scale(q3) - scale(q2)) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', boxStrokeWidth) - .attr('transform', function(d, i){ - var - x = horizontalQ ? 0 : scale(q2), - y = verticalQ ? 0 : scale(extent[1]) - scale(q3), - t = 'translate('+x+','+y+')'; - return t - }); - - // set lower quartile (q1) - lQuart.transition().duration(transitionDuration).ease(easeFunc) - .attr('width', horizontalQ ? objectSize : scale(q2) - scale(q1)) - .attr('height', verticalQ ? objectSize : scale(q2) - scale(q1)) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', boxStrokeWidth) - .attr('transform', function(d, i){ - var - x = horizontalQ ? 0 : scale(q1), - y = verticalQ ? 0 : scale(extent[1]) - scale(q2), - t = 'translate('+x+','+y+')'; - return t - }); - + cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }); + utils$1.con.log('heatmap', 'cells are sorted by', cellKeys); - // set median (q2) - mQuart.transition().duration(transitionDuration).ease(easeFunc) - .attr('r', function(d, i){ - var r = objectSize / 2; - var dif = (scale(q3) - scale(q1)) / 2; - return (r > dif) ? dif : r - }) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', boxStrokeWidth) - .attr('transform', function(d, i){ - var - x = horizontalQ ? objectSize / 2 : scale(q2), - y = verticalQ ? objectSize / 2 : scale(extent[1]) - scale(q2), - t = 'translate('+x+','+y+')'; - return t - }); - // set lower whisker (min) - lWhisk.transition().duration(transitionDuration).ease(easeFunc) - .attr('d', function(dd, ii){ - var - dir = false, - x = 0, - y = 0, - h = horizontalQ ? scale(q1) - scale(q0) : objectSize, - w = verticalQ ? scale(q1) - scale(q0) : objectSize; - return whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient) - }) - .attr('transform', function(d, i){ - var - x = horizontalQ ? 0 : scale(q1), - y = verticalQ ? 0 : scale(extent[1]) - scale(q1), - t = 'translate('+x+','+y+')'; - return t - }) - .attr('stroke', 'black').attr('stroke-width', whiskerStrokeWidth) - .attr('fill', 'none'); - // set upper whisker (max) - uWhisk.transition().duration(transitionDuration).ease(easeFunc) - .attr('d', function(dd, ii){ - var - dir = true, - x = 0, - y = 0, - h = horizontalQ ? scale(q4) - scale(q3) : objectSize, - w = verticalQ ? scale(q4) - scale(q3) : objectSize; - return whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient) - }) - .attr('transform', function(d, i){ - var - x = horizontalQ ? 0 : scale(q3), - y = verticalQ ? 0 : scale(extent[1]) - scale(q4), - t = 'translate('+x+','+y+')'; - return t - }) - .attr('stroke', 'black') - .attr('stroke-width', whiskerStrokeWidth) - .attr('fill', 'none'); + utils$1.con.log('heatmap', 'x and y keys are', {x: xValues, y:yValues}); - }); - tooltip$$1.selection(container.selectAll('g:not(.to-remove).'+objectClass)) - .data(data); - tooltip$$1(); + var xDim = xValues.length, yDim = yValues.length; - } + ySize = utils$1.math.calculateWidthOfObject(spaceY, yDim, yMinObjectSize, yMaxObjectSize, objectSpacer, overflowQ); + xSize = utils$1.math.calculateWidthOfObject(spaceX, xDim, xMinObjectSize, xMaxObjectSize, objectSpacer, overflowQ); + ySpacerSize = utils$1.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); + xSpacerSize = utils$1.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); + // console.table({ + // x:{ + // object: xSize, + // spacer: xSpacerSize, + // dim: xDim + // }, + // y:{ + // object: ySize, + // spacer: ySpacerSize, + // dim: yDim + // } + // + // }) + utils$1.con.log('heatmap', 'size of', {x: xSize, y: ySize}); - return boxwhisker -} -function selectFilter(selection) { - var - data, - namespace = 'd3sm-select-filter', - selectionName = 'Select options:', - defaultValue = undefined; + var ySpacer = groupingSpacer() + .horizontalQ(false) + .moveby('category') + .numberOfObjects(yDim) + .objectClass(utils$1.str.hypenate(objectClass, 'row')) + .objectSize(ySize + ySpacerSize) + .spacerSize(0) + .transitionDuration(transitionDuration) + .easeFunc(easeFunc) + .namespace('row'); + var xSpacer = groupingSpacer() + .horizontalQ(true) + .moveby('category') + .numberOfObjects(xDim) + .objectClass(objectClass) + .objectSize(xSize + xSpacerSize) + .spacerSize(0) + .transitionDuration(transitionDuration) + .easeFunc(easeFunc); + ySpacer(container, yValues, 0); + container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'row')) + .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); - var lastValue = undefined; + var cells = container.selectAll('g:not(.to-remove).'+objectClass); - selectFilter.data = function(_) { return arguments.length ? (data = _, selectFilter) : data}; - selectFilter.namespace = function(_) { return arguments.length ? (namespace = _, selectFilter) : namespace}; - selectFilter.selectionName = function(_) { return arguments.length ? (selectionName = _, selectFilter) : selectionName}; - selectFilter.defaultValue = function(_) { return arguments.length ? (defaultValue = _, selectFilter) : defaultValue}; - selectFilter.currentOption = currentOption; - function selectFilter() { - var - container = safeSelect(selection, 'div', 'input-group').classed(hypenate(namespace,'container'),true), + if (cellKeys.length != yValues.length * xValues.length) { + var lookup = {}; + cellKeys.map(function(k, i){ + lookup[xExtractor(k)+'::'+yExtractor(k)] = k; + }); - selectPrepend = safeSelect(container, 'div', 'select-prepend').classed('input-group-prepend', true), - selectPrependSpan = safeSelect(selectPrepend, 'span', 'input-group-text').text(selectionName), + var positionedCellKeys = []; + for (var i = 0; i < yValues.length; i++) { + for (var j = 0; j < xValues.length; j++) { + var lookupValue = lookup[xValues[j]+"::"+yValues[i]]; + if (lookupValue == undefined) { + positionedCellKeys.push(undefined); + } else { + positionedCellKeys.push(lookupValue); + } + } + } - select = safeSelect(container, 'select', 'custom-select').classed(hypenate(namespace,'select'),true), + cells.data(positionedCellKeys); - selectAppend = safeSelect(container, 'div', 'select-append').classed('input-group-prepend', true), - selectAppendButton = safeSelect(selectAppend, 'a', 'filter-button').classed('btn btn-outline-secondary', true), - filterButtonIcon = safeSelect(selectAppendButton, 'i', 'fa fa-filter'), + // maybe breaks this + // !!!!!! IMPORTANT NOTE TODO LOOK HERE BUG + cellKeys = positionedCellKeys; + } else { + cells.data(cellKeys); + } - inputGroup = safeSelect(container, 'div', 'filter-input-group').classed('input-group',true).classed('d-none', true), - inputPrepend = safeSelect(inputGroup, 'div', 'input-group-prepend'), - inputPrependSpan = safeSelect(inputPrepend, 'span', 'input-group-text').classed('search-button', true), - inputPrependSpanIcon = safeSelect(inputPrependSpan,'i','fa fa-search'), - input = safeSelect(inputGroup, 'input', 'form-control').attr('placeholder', 'all').attr('type', 'text'), - inputAppend = safeSelect(inputGroup, 'div', 'input-group-append'), - inputAppendButton = safeSelect(inputAppend, 'a', 'close-button').classed('btn btn-outline-secondary', true), - inputAppendButtonIcon = safeSelect(inputAppendButton, 'i', 'fa fa-close'); + var parentIndexArray = []; + cells.each(function(d, i){ parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); }); - var keys = d3.keys(data), - options = select.selectAll('option'); + colorFunction$$1 = colorFunction$$1.colorBy() == 'index' + ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) + : colorFunction$$1.dataExtent([0, Math.max(...vValues)]); - options = options.data(d3.keys(data)); - options = options.merge(options.enter().append('option')) - .attr('value', function(d, i){return d}) - .text(function(d, i){return d}); + cells.each(function(key, i) { + utils$1.con.log('heatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); - var - filterButton = selectAppendButton, - closeButton = inputAppendButton; + var t = d3.select(this); + if (key == undefined) {return} + var currentData = data[key], + value = vExtractor(key, i), + i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), + fillColor = colorFunction$$1(key, value, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(key, value, i, 'stroke'); - filterButton.on('click', function(d, i){ - var currentStyle = inputGroup.classed('d-none'); - inputGroup.classed('d-none', !currentStyle); - }); + var c = utils$1.sel.safeSelect(t, 'rect', utils$1.str.hypenate(objectClass,'rect')); + c.attr('width', xSize + xSpacerSize - objectStrokeWidth) + .attr('height', ySize + ySpacerSize - objectStrokeWidth) + .attr('fill', fillColor) + .attr('x', objectStrokeWidth/2) + .attr('y', objectStrokeWidth/2) + .attr('stroke', "#000") + .attr('stroke-width', objectStrokeWidth); - closeButton.on('click', function(d, i){ - input.property('value', '').dispatch('input'); }); - input.on('input', function(d, i){ - var - val = input.property('value'), - reg = new RegExp(val, 'gi'), - use; - - if (val == '') {use = keys;} - else { - use = []; - d3.keys(data).map(function(option, j){ - var match = option.match(reg); - if (match == null || match.join('') == '') ; - else { use.push(option); } - }); - } - - options = select.selectAll('option'); - options = options.data(use); - options.exit().remove(); - options = options.merge(options.enter().append('option')) - .attr('value', function(d, i){return d}) - .text(function(d, i){return d}); - - var current = currentOption(); - if (lastValue != current) { - lastValue = current; - select.dispatch('change'); - } - }); + tooltip$$1.selection(cells.selectAll('rect.'+utils$1.str.hypenate(objectClass, 'rect'))) + .data(data); + // .keys(['r', 'v']) + // .header(function(d, i){return utils.str.hypenate(data[d][xKey], data[d][yKey]) }) + tooltip$$1(); - } - function currentOption() { - var val = selection.select("select").property('value'); - return val == undefined || val == '' - ? defaultValue == undefined - ? d3.keys(data)[0] - : defaultValue - : val } - return selectFilter + return hm; } /******************************************************************************* ** ** ** ** -** DATATOGGLE ** +** VIOLIN ** ** ** ** ** *******************************************************************************/ + /** - * Creates a datatoggle - * @constructor datatoggle + * Creates a violin + * + * {@link https://sumneuron.gitlab.io/d3sm/demos/basic-violins/index.html Demo} + * @constructor violin * @param {d3.selection} selection - * @namespace datatoggle - * @returns {function} datatoggle + * @namespace violin + * @returns {function} violin */ -function datatoggle( selection ) { - var +function violin( selection ) { + var /** - * Keys to make toggle-able options - * (see {@link datatoggle#keys}) - * @param {string[]} [keys=undefined] - * @memberof datatoggle# + * Data to plot. Assumed to be a object, where each key corresponds to a violin + * (see {@link violin#data}) + * @param {Object} [data=undefined] + * @memberof violin# * @property */ - keys, - + data, /** - * What to do when a different key is clicked - * (see {@link datatoggle#updateFunction}) - * @param {function} [updateFunction=function(){}] - * @memberof datatoggle# + * Which direction to render the bars in + * (see {@link violin#orient}) + * @param {number} [orient='horizontal'] + * @memberof violin# * @property */ - updateFunction = function(){}, + orient='horizontal', /** - * Namespace for all items made by this instance of datatoggle - * @param {string} [namespace="d3sm-databar"] - * @memberof datatoggle# + * Amount of horizontal space (in pixels) avaible to render the violin in + * (see {@link violin#spaceX}) + * @param {number} [spaceX=undefined] + * @memberof violin# * @property */ - namespace='d3sm-databar', + spaceX, /** - * Currently toggled key - * @param {string} [currentKey=undefined] - * @memberof datatoggle# + * Amount of vertical space (in pixels) avaible to render the violin in + * (see {@link violin.spaceY}) + * @param {number} [spaceY=undefined] + * @memberof violin# * @property */ - currentKey, - - - - xAxisSelectQ = false, - xAxisOptions, - yAxisSelectQ = false, - yAxisOptions, - data; - toggle.xAxisSelectQ = function(_){return arguments.length ? (xAxisSelectQ = _, toggle) : xAxisSelectQ}; - toggle.yAxisSelectQ = function(_){return arguments.length ? (yAxisSelectQ = _, toggle) : yAxisSelectQ}; - toggle.xAxisOptions = function(_){return arguments.length ? (xAxisOptions = _, toggle) : xAxisOptions}; - toggle.yAxisOptions = function(_){return arguments.length ? (yAxisOptions = _, toggle) : yAxisOptions}; - toggle.data = function(_){return arguments.length ? (data = _, toggle) : data}; - toggle.keys = function(_){return arguments.length ? (keys = _, toggle) : keys}; - toggle.currentKey = function(_){return arguments.length ? (currentKey = _, toggle) : currentKey}; - - /** - * Gets / sets the updateFunction - * (see {@link datatoggle#updateFunction}) - * @function datatoggle.updateFunction - * @param {function} [_=none] - * @returns {datatoggle | function} - * @memberof datatoggle - * @property - * by default updateFunction = function(){} - */ - toggle.updateFunction = function(_){return arguments.length ? (updateFunction = _, toggle) : updateFunction}; + spaceY, /** - * Gets / sets the namespace - * (see {@link datatoggle#namespace}) - * @function datatoggle.namespace - * @param {string} [_=none] - * @returns {datatoggle | string} - * @memberof datatoggle - * @property - * by default namespace = 'd3sm-databar' - */ - toggle.namespace = function(_){return arguments.length ? (namespace = _, toggle) : namespace}; + * Whether or not to allow violin to render elements pass the main spatial dimension + * given the orientation (see {@link violin#orient}), where {@link violin#orient}="horizontal" + * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" + * the main dimension is {@link violin#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof violin# + * @property + */ + overflowQ = true, /** - * Gets / sets the currentKey - * (see {@link datatoggle#currentKey}) - * @function datatoggle.currentKey - * @param {string} [_=none] - * @returns {datatoggle | string} - * @memberof datatoggle - * @property - * by default currentKey = undefined - */ - toggle.currentKeys = - function () { - var vals = {}; - d3.keys(filterSelects).map(function(k, i){ - vals[k]= filterSelects[k].currentOption(); - }); - return vals - }; - - var filterSelects = {}; - function toggle() { - // selection options - - // selection.classed('d-flex flex-row', true) - // var filterButton = safeSelect(selection, 'a', 'slider-buttons') - // var filterI = safeSelect(filterButton, 'i', 'fa fa-sliders') - - /*BUG: unexpected behavior. - - when using bootstrap-eque way for collapse, clicking button submits to - the same page in applications (but not in demo), so using anchor () - - when using anchor, cause page to jump to that location - - when using show, the first open works, but the close does not. - */ - // filterButton.attr('class', 'btn btn-secondary') - // .attr('data-toggle', 'collapse') - // .attr('href', '#'+hypenate(namespace, 'data-toggle')) - // .attr('target', '_blank') - // .html(filterButton.html()+' Filters') - // .on('click', function(d, i){ - // d3.event.preventDefault() - // d3.event.stopPropagation() - // var dt = d3.select("#"+hypenate(namespace, 'data-toggle')) - // dt.classed('show', !dt.classed('show')) - // dt.classed('d-inline-flex', dt.classed('show')) - // - // filterButton.classed('btn-primary', dt.classed('show')) - // filterButton.classed('btn-secondary', !dt.classed('show')) - // }) - // .classed('d-inline-flex', true) - // .style("margin-right", '10px') - - - - // var datatoggleCollapse = safeSelect(selection, 'div', hypenate(namespace,'collapse')) - // .attr('id', hypenate(namespace, 'data-toggle')) - // .classed('collapse', true) - - // var flexRow = safeSelect(datatoggleCollapse, 'div', 'd-inline-flex flex-row flex-wrap') - var flexRow = safeSelect(selection, 'div', 'd-inline-flex flex-row flex-wrap'); - - var dataopts = flexRow.selectAll('div.'+hypenate(namespace,'select-filter')); - // remove excess - dataopts.exit().remove(); - // bind data - dataopts = dataopts.data(d3.keys(data)); - //enter - var doEnter = dataopts.enter().append('div') - .attr('class', 'select-filter'); - - dataopts = dataopts.merge(doEnter).style('margin-right', "10px"); - - dataopts.each(function(d, i){ - var t = d3.select(this); - var sf = selectFilter(t) - .data(data[d]) - .namespace(hypenate(namespace, d)) - .selectionName(d); - sf(); - filterSelects[d] = sf; - }); - - - selection.selectAll('select') - .on('change', function(){updateFunction();}); - // bind update function - // d3.selectAll - return toggle - } - - return toggle -} - -/******************************************************************************* -** ** -** ** -** SCATTER ** -** ** -** ** -*******************************************************************************/ -/** - * Creates a scatter - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/scatter/index.html Demo} - * @constructor scatter - * @param {d3.selection} selection - * @namespace scatter - * @returns {function} scatter - */ -function scatter ( selection ) { - - var - /** - * Data to plot. Assumed to be a object, where each key corresponds to a point - * (see {@link scatter#data}) - * @param {Object} [data=undefined] - * @memberof scatter# + * Whether or not to display points inside the points + * @param {boolean} [pointsQ=false] + * @memberof violin# * @property */ - data, + pointsQ = true, /** - * Amount of horizontal space (in pixels) avaible to render the scatter in - * (see {@link scatter#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof scatter# + * An array - putatively of other arrays - depicting how bars should be arranged + * @param {Array[]} [grouping=undefined] + * @memberof violin# * @property */ - spaceX, + grouping, /** - * Amount of vertical space (in pixels) avaible to render the scatter in - * (see {@link scatter.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof scatter# + * How to get the value of the violin + * @param {function} [valueExtractor=function(key, index) { return data[key] }] + * @memberof violin# * @property */ - spaceY, - + valueExtractor = function(key, index) {return data[key] }, /** - * The scale for which scatter x values should be transformed by - * @param {d3.scale} [scaleX=d3.scaleLinear] - * @memberof scatter# + * How to sort the bars - if {@link violin#grouping} is not provided. + * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] + * @memberof violin# * @property */ - scaleX = d3.scaleLinear(), + sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, + /** - * The padding for the domain of the scaleX (see {@link scatter#scaleX}) - * @param {number} [domainPaddingX=0.5] - * @memberof scatter# + * The scale for which violin values should be transformed by + * @param {d3.scale} [scale=d3.scaleLinear] + * @memberof violin# * @property */ - domainPaddingX = 0.5, + scale = d3.scaleLinear(), /** - * The function for getting the x value of the current point - * @param {function} [valueExtractorX=function(d, i){return data[d]['x']}] - * @memberof scatter# + * The padding for the domain of the scale (see {@link violin#scale}) + * @param {number} [domainPadding=0.5] + * @memberof violin# * @property */ - valueExtractorX = function(d, i) {return data[d]['x']}, - + domainPadding = 0.5, /** - * The scale for which scatter y values should be transformed by - * @param {d3.scale} [scaleY=d3.scaleLinear] - * @memberof scatter# + * Default space for the spacer (percentage) of main dimension given the orientation + * (see {@link violin#orient}), where {@link violin#orient}="horizontal" + * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" + * the main dimension is {@link violin#spaceY} between bars + * @param {number} [objectSpacer=0.05] + * @memberof violin# * @property */ - scaleY = d3.scaleLinear(), + objectSpacer = 0.05, /** - * The padding for the domain of the scaleY (see {@link scatter#scaleY}) - * @param {number} [domainPaddingY=0.5] - * @memberof scatter# + * The minimum size that an object can be + * @param {number} [minObjectSize=50] + * @memberof violin# * @property */ - domainPaddingY = 0.5, + minObjectSize = 50, /** - * The function for getting the y value of the current point - * @param {function} [valueExtractorY=function(d, i){return data[d]['y']}] - * @memberof scatter# + * The maximum size that an object can be + * @param {number} [maxObjectSize=100] + * @memberof violin# * @property */ - valueExtractorY = function(d, i) {return data[d]['y']}, - + maxObjectSize = 100, /** - * The scale for which scatter r values should be transformed by - * @param {d3.scale} [scaleR=d3.scaleLinear] - * @memberof scatter# - * @property - */ - scaleR = d3.scaleLinear(), - /** - * The padding for the domain of the scaleR (see {@link scatter#scaleR}) - * @param {number} [domainPaddingR=0.5] - * @memberof scatter# + * The stroke width of the bars + * @param {number} [barStrokeWidth=2] + * @memberof violin# * @property */ - domainPaddingR = 0.5, + objectStrokeWidth = 2, /** - * The function for getting the r value of the current point - * @param {function} [valueExtractorR=function(d, i){return 2}] - * @memberof scatter# + * Instance of ColorFunction + * @param {function} [colorFunction = colorFunction()] + * @memberof violin# * @property */ - valueExtractorR = function(d, i) {return 2}, + colorFunction$$1 = colorFunction(), /** - * The min radius a point can have - * @param {function} [minRadius=2] - * @memberof scatter# + * Instance of ColorFunction modified by a scale for the points + * @param {function} [pointColorFunc = colorFunction()] + * @memberof violin# * @property */ - minRadius = 2, + pointColorFunc = function (d, type, base, min, max) { + var minMaxHexScale = d3.scaleLinear().domain([min, max]).range([-0.25, 0.05]); + var scaledColor = utils.color.modifyHexidecimalColorLuminance(base.replace('#', ''), minMaxHexScale(d)); + var mod = type == "stroke" ? 0 : 0.25; + return utils.color.modifyHexidecimalColorLuminance(scaledColor.replace('#', ''), mod) + }, + /** - * The min radius a point can have - * @param {function} [maxRadius=10] - * @memberof scatter# + * The radius of a point + * @param {number} [pointRadius=3] + * @memberof violin# * @property */ - maxRadius = 10, - + pointRadius = 3, /** - * The stroke width of the points + * The stroke width of the oints * @param {number} [pointStrokeWidth=2] - * @memberof scatter# + * @memberof violin# * @property */ pointStrokeWidth = 2, - /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof scatter# - * @property - */ - colorFunction$$1 = colorFunction(), + /** * Color of the background * @param {string} [backgroundFill="transparent"] - * @memberof scatter# + * @memberof violin# * @property */ backgroundFill = 'transparent', /** - * Namespace for all items made by this instance of scatter - * @param {string} [namespace="d3sm-scatter"] - * @memberof scatter# + * Namespace for all items made by this instance of violin + * @param {string} [namespace="d3sm-violin"] + * @memberof violin# * @property */ - namespace = 'd3sm-scatter', + namespace = 'd3sm-violin', /** - * Class name for scatter container ( element) - * @param {string} [objectClass="scatter-point"] - * @memberof scatter# + * Class name for violin container ( element) + * @param {string} [objectClass="violin"] + * @memberof violin# * @property */ - objectClass = 'scatter-point', + objectClass = 'violin', /** * Duration of all transitions of this element * @param {number} [transitionDuration=1000] - * @memberof scatter# + * @memberof violin# * @property */ transitionDuration = 1000, /** * Easing function for transitions * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof scatter# + * @memberof violin# * @property */ easeFunc = d3.easeExp, - // useful values to extract to prevent re-calculation /** - * The keys of the points - * @param {string[]} [pointKeys=undefined] - * @memberof scatter# + * The keys corresponding to each quartile + * @param {string[]} [quartileKeys=["Q0", "Q1", "Q2", "Q3", "Q4"]] + * @memberof violin# * @property */ - pointKeys, + quartileKeys = ["Q0", "Q1", "Q2", "Q3", "Q4"], + /** - * The x values of the points - * @param {number[]} [valuesX=undefined] - * @memberof scatter# + * The keys of the bars + * @param {string[]} [violinKeys=undefined] + * @memberof violin# * @property */ - valuesX, + violinKeys, /** - * The y values of the points - * @param {number[]} [valuesY=undefined] - * @memberof scatter# + * The values of the bars + * @param {number[]} [violinValues=undefined] + * @memberof violin# * @property */ - valuesY, + violinValues, /** - * The r values of the points - * @param {number[]} [valuesR=undefined] - * @memberof scatter# + * The objectSize (actual width) used by the bars + * @param {number} [objectSize=undefined] + * @memberof violin# * @property */ - valuesR, + objectSize, + /** + * The spacerSize (actual width) used by the spacers between the bars + * @param {number} [spacerSize=undefined] + * @memberof violin# + * @property + */ + spacerSize, /** * Instance of Tooltip * @param {function} [tooltip=tooltip()] - * @memberof scatter# + * @memberof violin# * @property */ - tooltip$$1 = tooltip(); + tooltip$$1 = tooltip().keys([quartileKeys[4], quartileKeys[3], quartileKeys[2], quartileKeys[1], quartileKeys[0]]), + pointsTooltip = tooltip(), + // pointKeyExtractor = function(violinKey, violinData, violinValues) {return d3.keys(violinValues[violinKey].values)}, + // pointValueExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]}, + /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {scatter | d3.selection} - * @memberof scatter + * Function which given the key of the violin and that key's associated value + * returns the object consiting of the points of the violin + * (see {@link violin#violinPointsExtractor}) + * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] + * @memberof violin# + * @property + */ + violinPointsExtractor = function (violinKey, violinData) {return violinData.points }, + /** + * Function which given the key of the current point and the object of points for the + * violin, returns the numerical value of the point + * (see {@link violin#violinPointValueExtractor}) + * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] + * @memberof violin# + * @property + */ + violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }; + + + /** + * Gets or sets the violinPointsExtractor + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin + * @property + * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } + */ + violin.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor = _, violin) : violinPointsExtractor; }; + /** + * Gets or sets the violinPointValueExtractor + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin + * @property + * by default violinPointsExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]} + */ + violin.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor = _, violin) : violinPointValueExtractor; }; + + + /** + * Gets or sets the selection in which items are manipulated + * @param {d3.selection} [_=none] + * @returns {violin | d3.selection} + * @memberof violin * @property * by default selection = selection */ - scatter.selection = function(_) { return arguments.length ? (selection =_, scatter) : selection}; + violin.selection = function(_) { return arguments.length ? (selection = _, violin) : selection; }; /** * Gets or sets the data - * (see {@link scatter#data}) + * (see {@link violin#data}) * @param {number} [_=none] - * @returns {scatter | object} - * @memberof scatter + * @returns {violin | object} + * @memberof violin * @property */ - scatter.data = function(_) { return arguments.length ? (data =_, scatter) : data}; + violin.data = function(_) { return arguments.length ? (data = _, violin) : data; }; + /** + * Gets or sets the orient of the boxes + * (see {@link violin#orient}) + * @param {number} [_=none] + * @returns {violin | object} + * @memberof violin + * @property + */ + violin.orient = function(_) { return arguments.length ? (orient = _, violin) : orient; }; /** * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link scatter#spaceX}) + * (see {@link violin#spaceX}) * @param {number} [_=none] should be a number > 0 - * @returns {scatter | number} - * @memberof scatter + * @returns {violin | number} + * @memberof violin * @property * by default spaceX = undefined */ - scatter.spaceX = function(_) { return arguments.length ? (spaceX =_, scatter) : spaceX}; + violin.spaceX = function(_) { return arguments.length ? (spaceX = _, violin) : spaceX; }; /** * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link scatter#spaceY}) + * (see {@link violin#spaceY}) * @param {number} [_=none] should be a number > 0 - * @returns {scatter | number} - * @memberof scatter + * @returns {violin | number} + * @memberof violin * @property * by default spaceY = undefined */ - scatter.spaceY = function(_) { return arguments.length ? (spaceY =_, scatter) : spaceY}; - + violin.spaceY = function(_) { return arguments.length ? (spaceY = _, violin) : spaceY; }; /** - * Gets / sets the x scale for which the scatter x values should be transformed by - * (see {@link scatter#scaleX}) - * @param {d3.scale} [_=none] - * @returns {scatter | d3.scale} - * @memberof scatter - * @property - * by default scaleX = d3.scaleLinear() - */ - scatter.scaleX = function(_) { return arguments.length ? (scaleX =_, scatter) : scaleX}; - /** - * Gets / sets the padding for the domain of the x scale - * (see {@link scatter#domainPaddingX}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter + * Gets / sets whether or not violin is allowed to go beyond specified dimensions + * (see {@link violin#overflowQ}) + * @param {boolean} [_=none] + * @returns {violin | boolean} + * @memberof violin * @property - * by default domainPaddingX = 0.5 + * by default overflowQ = false */ - scatter.domainPaddingX = function(_) { return arguments.length ? (domainPaddingX =_, scatter) : domainPaddingX}; + violin.overflowQ = function(_) { return arguments.length ? (overflowQ = _, violin) : overflowQ; }; /** - * Gets / sets the valueExtractorX - * (see {@link scatter#valueExtractorX}) - * @param {function} [_=none] - * @returns {scatter | function} - * @memberof scatter + * Gets / sets whether or not to plot points with the violins + * (see {@link violin#pointsQ}) + * @param {boolean} [_=none] + * @returns {violin | boolean} + * @memberof violin * @property - * by default valueExtractorX = function(key, index) { return data[key]['x'] } + * by default pointsQ = false */ - scatter.valueExtractorX = function(_) { return arguments.length ? (valueExtractorX =_, scatter) : valueExtractorX}; + violin.pointsQ = function(_) { return arguments.length ? (pointsQ = _, violin) : pointsQ; }; /** - * Gets / sets the y scale for which the scatter y values should be transformed by - * (see {@link scatter#scaleY}) - * @param {d3.scale} [_=none] - * @returns {scatter | d3.scale} - * @memberof scatter + * Gets / sets the grouping of the boxes + * (see {@link violin#grouping}) + * @param {Array[]} [_=none] + * @returns {violin | Array[]} + * @memberof violin * @property - * by default scaleY = d3.scaleLinear() + * by default grouping = undefined */ - scatter.scaleY = function(_) { return arguments.length ? (scaleY =_, scatter) : scaleY}; + violin.grouping = function(_) { return arguments.length ? (grouping = _, violin) : grouping; }; /** - * Gets / sets the padding for the domain of the y scale - * (see {@link scatter#domainPaddingY}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter + * Gets / sets the valueExtractor + * (see {@link violin#valueExtractor}) + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin * @property - * by default domainPaddingY = 0.5 */ - scatter.domainPaddingY = function(_) { return arguments.length ? (domainPaddingY =_, scatter) : domainPaddingY}; + violin.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, violin) : valueExtractor; }; /** - * Gets / sets the valueExtractorY - * (see {@link scatter#valueExtractorY}) + * Gets / sets the sortingFunction + * (see {@link violin#sortingFunction}) * @param {function} [_=none] - * @returns {scatter | function} - * @memberof scatter + * @returns {violin | function} + * @memberof violin * @property - * by default valueExtractorY = function(key, index) { return data[key]['y'] } */ - scatter.valueExtractorY = function(_) { return arguments.length ? (valueExtractorY =_, scatter) : valueExtractorY}; - + violin.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, violin) : sortingFunction; }; /** - * Gets / sets the r scale for which the scatter r values should be transformed by - * (see {@link scatter#scaleR}) + * Gets / sets the scale for which the violin values should be transformed by + * (see {@link violin#scale}) * @param {d3.scale} [_=none] - * @returns {scatter | d3.scale} - * @memberof scatter + * @returns {violin | d3.scale} + * @memberof violin * @property - * by default scaleR = d3.scaleLinear() + * by default scale = d3.scaleLinear() */ - scatter.scaleR = function(_) { return arguments.length ? (scaleR =_, scatter) : scaleR}; + violin.scale = function(_) { return arguments.length ? (scale = _, violin) : scale; }; /** - * Gets / sets the padding for the domain of the r scale - * (see {@link scatter#domainPaddingR}) + * Gets / sets the padding for the domain of the scale + * (see {@link violin#domainPadding}) * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter + * @returns {violin | number} + * @memberof violin * @property - * by default domainPaddingR = 0.5 + * by default domainPadding = 0.5 */ - scatter.domainPaddingR = function(_) { return arguments.length ? (domainPaddingR =_, scatter) : domainPaddingR}; + violin.domainPadding = function(_) { return arguments.length ? (domainPadding = _, violin) : domainPadding; }; + + /** - * Gets / sets the valueExtractorR - * (see {@link scatter#valueExtractorR}) - * @param {function} [_=none] - * @returns {scatter | function} - * @memberof scatter + * Gets / sets objectSpacer + * (see {@link violin#objectSpacer}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin * @property - * by default valueExtractorR = function(key, index) { return data[key]['r'] } + * by default objectSpacer = 0.05 */ - scatter.valueExtractorR = function(_) { return arguments.length ? (valueExtractorR =_, scatter) : valueExtractorR}; + violin.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, violin) : objectSpacer; }; /** - * Gets / sets the minRadius - * (see {@link scatter#minRadius}) + * Gets / sets the minObjectSize + * (see {@link violin#minObjectSize}) * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter + * @returns {violin | number} + * @memberof violin * @property - * by default minRadius = 2 + * by default minObjectSize = 15 */ - scatter.minRadius = function(_) { return arguments.length ? (minRadius =_, scatter) : minRadius}; + violin.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, violin) : minObjectSize; }; /** - * Gets / sets the maxRadius - * (see {@link scatter#maxRadius}) + * Gets / sets the maxObjectSize + * (see {@link violin#maxObjectSize}) * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter + * @returns {violin | number} + * @memberof violin * @property - * by default maxRadius = 10 + * by default maxObjectSize = 50 */ - scatter.maxRadius = function(_) { return arguments.length ? (maxRadius =_, scatter) : maxRadius}; + violin.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, violin) : maxObjectSize; }; /** - * Gets / sets the pointStrokeWidth - * (see {@link scatter#pointStrokeWidth}) + * Gets / sets the objectStrokeWidth + * (see {@link violin#objectStrokeWidth}) * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter + * @returns {violin | number} + * @memberof violin * @property - * by default pointStrokeWidth = 2 + * by default objectStrokeWidth = 2 */ - scatter.pointStrokeWidth = function(_) { return arguments.length ? (pointStrokeWidth =_, scatter) : pointStrokeWidth}; + violin.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, violin) : objectStrokeWidth; }; + + /** * Gets / sets the colorFunction - * (see {@link scatter#colorFunction}) - * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter + * (see {@link violin#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {violin | colorFunction} + * @memberof violin * @property * by default colorFunction = colorFunction() */ - scatter.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 =_, scatter) : colorFunction$$1}; + violin.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, violin) : colorFunction$$1; }; /** - * Gets / sets the backgroundFill - * (see {@link scatter#backgroundFill}) - * @param {string} [_=none] - * @returns {scatter | string} - * @memberof scatter - * @property - * by default backgroundFill = 'transparent' - */ - scatter.backgroundFill = function(_) { return arguments.length ? (backgroundFill =_, scatter) : backgroundFill}; + * Gets / sets the colorFunction + * (see {@link violin#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {violin | colorFunction} + * @memberof violin + * @property + * by default colorFunction = colorFunction() + */ + violin.pointColorFunc = function(_) { return arguments.length ? (pointColorFunc = _, violin) : pointColorFunc; }; + + + /** + * Gets / sets the pointRadius + * (see {@link violin#pointRadius}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default pointRadius = 2 + */ + violin.pointRadius = function(_) { return arguments.length ? (pointRadius = _, violin) : pointRadius; }; + /** + * Gets / sets the pointStrokeWidth + * (see {@link violin#pointStrokeWidth}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default pointStrokeWidth = 2 + */ + violin.pointStrokeWidth = function(_) { return arguments.length ? (pointStrokeWidth = _, violin) : pointStrokeWidth; }; + + + /** + * Gets / sets the backgroundFill + * (see {@link violin#backgroundFill}) + * @param {string} [_=none] + * @returns {violin | string} + * @memberof violin + * @property + * by default backgroundFill = 'transparent' + */ + violin.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, violin) : backgroundFill; }; /** * Gets / sets the namespace - * (see {@link scatter#namespace}) + * (see {@link violin#namespace}) * @param {string} [_=none] - * @returns {scatter | string} - * @memberof scatter + * @returns {violin | string} + * @memberof violin * @property - * by default namespace = 'd3sm-scatter' + * by default namespace = 'd3sm-violin' */ - scatter.namespace = function(_) { return arguments.length ? (namespace =_, scatter) : namespace}; + violin.namespace = function(_) { return arguments.length ? (namespace = _, violin) : namespace; }; /** * Gets / sets the objectClass - * (see {@link scatter#objectClass}) + * (see {@link violin#objectClass}) * @param {string} [_=none] - * @returns {scatter | string} - * @memberof scatter + * @returns {violin | string} + * @memberof violin * @property * by default objectClass = 'tick-group' */ - scatter.objectClass = function(_) { return arguments.length ? (objectClass =_, scatter) : objectClass}; + violin.objectClass = function(_) { return arguments.length ? (objectClass = _, violin) : objectClass; }; + + /** * Gets / sets the transitionDuration - * (see {@link scatter#transitionDuration}) + * (see {@link violin#transitionDuration}) * @param {number} [_=none] - * @returns {scatter | number} - * @memberof scatter + * @returns {violin | number} + * @memberof violin * @property * by default transitionDuration = 1000 */ - scatter.transitionDuration = function(_) { return arguments.length ? (transitionDuration =_, scatter) : transitionDuration}; + violin.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, violin) : transitionDuration; }; /** * Gets / sets the easeFunc - * (see {@link scatter#easeFunc}) + * (see {@link violin#easeFunc}) * @param {d3.ease} [_=none] - * @returns {scatter | d3.ease} - * @memberof scatter + * @returns {violin | d3.ease} + * @memberof violin * @property * by default easeFunc = d3.easeExp */ - scatter.easeFunc = function(_) { return arguments.length ? (easeFunc =_, scatter) : easeFunc}; + violin.easeFunc = function(_) { return arguments.length ? (easeFunc = _, violin) : easeFunc; }; + /** - * Gets / sets the pointKeys - * (see {@link scatter#pointKeys}) + * Gets / sets the quartileKey + * (see {@link violin#quartileKey}) + * @param {string} [_=none] + * @returns {violin | string} + * @memberof violin + * @property + * by default quartileKey = "utils.math.quartiles" + */ + violin.quartileKey = function(_) { return arguments.length ? (quartileKey = _, violin) : quartileKey; }; + /** + * Gets / sets the quartileKeys + * (see {@link violin#quartileKeys}) * @param {string[]} [_=none] - * @returns {scatter | string[]} - * @memberof scatter + * @returns {violin | string[]} + * @memberof violin * @property - * by default pointKeys = undefined + * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] */ - scatter.pointKeys = function(_) { return arguments.length ? (pointKeys =_, scatter) : pointKeys}; + violin.quartileKeys = function(_) { return arguments.length ? (quartileKeys = _, violin) : quartileKeys; }; + + /** - * Gets / sets the valuesX - * (see {@link scatter#valuesX}) - * @param {number[]} [_=none] - * @returns {scatter | number[]} - * @memberof scatter + * Gets / sets the violinKeys + * (see {@link violin#violinKeys}) + * @param {string[]} [_=none] + * @returns {violin | string[]} + * @memberof violin * @property - * by default valuesX = undefined + * by default violinKeys = undefined */ - scatter.valuesX = function(_) { return arguments.length ? (valuesX =_, scatter) : valuesX}; + violin.violinKeys = function(_) { return arguments.length ? (violinKeys = _, violin) : violinKeys; }; /** - * Gets / sets the valuesY - * (see {@link scatter#valuesY}) - * @param {number[]} [_=none] - * @returns {scatter | number[]} - * @memberof scatter + * Gets / sets the violinValues + * (see {@link violin#violinValues}) + * @param {Object[]} [_=none] + * @returns {violin | Object[]} + * @memberof violin * @property - * by default valuesY = undefined + * by default violinValues = undefined */ - scatter.valuesY = function(_) { return arguments.length ? (valuesY =_, scatter) : valuesY}; + violin.violinValues = function(_) { return arguments.length ? (violinValues = _, violin) : violinValues; }; + /** - * Gets / sets the valuesR - * (see {@link scatter#valuesR}) - * @param {number[]} [_=none] - * @returns {scatter | number[]} - * @memberof scatter + * Gets / sets the objectSize + * (see {@link violin#objectSize}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin * @property - * by default valuesR = undefined + * by default objectSize = undefined */ - scatter.valuesR = function(_) { return arguments.length ? (valuesR =_, scatter) : valuesR}; + violin.objectSize = function(_) { return arguments.length ? (objectSize = _, violin) : objectSize; }; + /** + * Gets / sets the spacerSize + * (see {@link violin#spacerSize}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default spacerSize = undefined + */ + violin.spacerSize = function(_) { return arguments.length ? (spacerSize = _, violin) : spacerSize; }; /** * Gets / sets the tooltip - * (see {@link scatter#tooltip}) + * (see {@link violin#tooltip}) * @param {tooltip} [_=none] - * @returns {scatter | tooltip} - * @memberof scatter + * @returns {violin | tooltip} + * @memberof violin * @property * by default tooltip = tooltip() */ + violin.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, violin) : tooltip$$1; }; + /** + * Gets / sets the pointsTooltip + * (see {@link violin#pointsTooltip}) + * @param {tooltip} [_=none] + * @returns {violin | tooltip} + * @memberof violin + * @property + * by default pointsTooltip = tooltip() + */ + violin.pointsTooltip = function(_) { return arguments.length ? (pointsTooltip = _, violin) : pointsTooltip; }; - scatter.tooltip = function(_) { return arguments.length ? (tooltip$$1 =_, scatter) : tooltip$$1}; - function scatter() { - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); - pointKeys = d3.keys(data); - valuesX = pointKeys.map(valueExtractorX); - valuesY = pointKeys.map(valueExtractorY); - valuesR = pointKeys.map(valueExtractorR); + function violin () { + // for convenience in handling orientation specific values + var horizontalQ = (orient == 'horizontal') ? true : false; - var numberOfObjects = pointKeys.length; - var extentX = [Math.min(...valuesX) - domainPaddingX, Math.max(...valuesX) + domainPaddingX]; - var extentY = [Math.min(...valuesY) - domainPaddingY, Math.max(...valuesY) + domainPaddingY]; - var extentR = [Math.min(...valuesR) - domainPaddingR, Math.max(...valuesR) + domainPaddingR]; + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - scaleX.domain(extentX).range([0, spaceX]); - scaleY.domain(extentY).range([spaceY, 0]); - scaleR.domain(extentR).range([minRadius, maxRadius]); + // if grouping is undefined sort violinKeys by sortingFunction + var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping; - var points = container.selectAll('.'+objectClass); - points = points.data(pointKeys); - var pEnter = points.enter().append('circle') - .attr('class', objectClass) - .attr('cx', 0).attr('cy', spaceY).attr('r', 0); + // console.log(ordered) - var pExit = points.exit(); + violinKeys = utils.arr.flatten(ordered); - points = points.merge(pEnter); - points.each(function(key, i){ - var t = d3.select(this), - currentData = data[key], - x = valuesX[i], - y = valuesY[i], - r = valuesR[i], - fillColor = colorFunction$$1(key, currentData, i, 'fill'), - strokeColor = colorFunction$$1(key, currentData, i, 'stroke'); + var calcValues = neededViolinValues() + .horizontalQ(horizontalQ) + .quartileKeys(quartileKeys) + .violinPointsExtractor(violinPointsExtractor) + .violinPointValueExtractor(violinPointValueExtractor); - t.transition().duration(transitionDuration).ease(easeFunc) - .attr('cx', scaleX(x)) - .attr('cy', scaleY(y)) - .attr('r', scaleR(r)) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', pointStrokeWidth); + // augment valus + violinKeys.map(function(vk, i){ calcValues(vk, data); }); - t.on('mouseover', function(d, i){ - points.style('opacity', 0.2); - t.style('opacity', 1); - t.transition().duration(transitionDuration/2).ease(easeFunc) - .attr('stroke-width', pointStrokeWidth*2) - .attr('r', scaleR(r) * 1.5); + var numberOfObjects = violinKeys.length; - }); - t.node().addEventListener('mouseout', function(){ - container.selectAll('.'+objectClass).style('opacity', 1); - t.transition().duration(transitionDuration/2).ease(easeFunc) - .attr('stroke-width', pointStrokeWidth) - .attr('r', scaleR(r)); + var min = [].concat(...violinKeys.map(function(k, i){return data[k].utils.math.quartiles[quartileKeys[0]]})); + var max = [].concat(...violinKeys.map(function(k, i){return data[k].utils.math.quartiles[quartileKeys[quartileKeys.length - 1]]})); + var extent = [Math.min(...min) - domainPadding, Math.max(...max) + domainPadding]; + // console.log(extent, violinValues, ordered) - }); + // set the scale + scale.domain(extent).range(horizontalQ ? [0,spaceY] : [0, spaceX]); + var space = horizontalQ ? spaceX : spaceY; + // calculate object size + objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + // calculate spacer size if needed + spacerSize = utils.math.calculateWidthOfSpacer(ordered, space, objectSize, numberOfObjects, objectSpacer, overflowQ); + // make the nested groups + var spacerFunction = groupingSpacer() + .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) + .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc) + .namespace(namespace); - }); + // move stuff + spacerFunction(container, ordered, 0); + // console.log(violinKeys, ordered, container.selectAll('g:not(.to-remove).'+objectClass).nodes()) + // for color function + var parentIndexArray = []; + container.selectAll('g:not(.to-remove).'+objectClass) + .each(function(d, i){if (utils.arr.hasQ(violinKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}}); + // update color function + colorFunction$$1 = colorFunction$$1.colorBy() == 'index' + ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) + : colorFunction$$1.dataExtent(extent); - pExit.transition().duration(transitionDuration).ease(easeFunc) - .attr('cx', 0).attr('cy', spaceY).attr('r', 0) - .remove(); + /* violiin specific needs */ + + + var frequencyMax = Math.max(...[].concat(...violinKeys.map(function(k, i){return d3.max(data[k].frequencies)}))); + var vScale = d3.scaleLinear().domain([0, frequencyMax]).range([0, objectSize / 2]); + + var lArea = d3.line() + .x(function(d, i){ return horizontalQ ? -vScale(d.x) : scale(d.x)}) + .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : -vScale(d.y)}) + .curve(d3.curveBasis); + var rArea = d3.line() + .x(function(d, i){ return horizontalQ ? vScale(d.x) : scale(d.x)}) + .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : vScale(d.y)}) + .curve(d3.curveBasis); + + + + + + + container.selectAll('g:not(.to-remove).'+objectClass).each(function(key, i){ + var t = d3.select(this), + currentData = data[key]; + // needed because bug in selecting .to-remove + if (!utils.arr.hasQ(violinKeys, key)) {return} + var + i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), + fillColor = colorFunction$$1(key, currentData, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(key, currentData, i, 'stroke'), + area = utils.sel.safeSelect(t, 'g', 'area'), + la = utils.sel.safeSelect(area, 'path', 'left'), + ra = utils.sel.safeSelect(area, 'path', 'right'), + quarts = utils.sel.safeSelect(t, 'g', 'quarts'), + lq3 = utils.sel.safeSelect(quarts, 'line', 'q3'), + lq1 = utils.sel.safeSelect(quarts, 'line', 'q1'), + q3 = currentData.utils.math.quartiles[quartileKeys[3]], + q2 = currentData.utils.math.quartiles[quartileKeys[2]], + q1 = currentData.utils.math.quartiles[quartileKeys[1]]; + + t.attr('transform', horizontalQ ? 'translate('+objectSize / 2+',0)' : 'translate(0,'+objectSize / 2+')' ); + // draw curve + la.transition().duration(transitionDuration).attr('d', function(dd, ii){ return lArea(currentData.contour)}) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', objectStrokeWidth); + + ra.transition().duration(transitionDuration).attr('d', function(dd, ii){ return rArea(currentData.contour)}) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', objectStrokeWidth); + + area.node().addEventListener('mouseover', function(dd, ii){ + container.selectAll('g.'+objectClass).style('opacity', 0.2); + t.style('opacity', 1); + la.attr('stroke-width',objectStrokeWidth*2); + ra.attr('stroke-width',objectStrokeWidth*2); + }); + area.node().addEventListener('mouseout', function(dd, ii){ + container.selectAll('g.'+objectClass).style('opacity', 1); + la.attr('stroke-width',objectStrokeWidth); + ra.attr('stroke-width',objectStrokeWidth); + }); + + if (pointsQ) { + var ptsContainer = utils.sel.safeSelect(t, 'g', 'points'); + var pts = ptsContainer.selectAll('.point').data(currentData.pointKeys); + pts.on('mouseover', null); + + + var ptsExit = pts.exit().transition().ease(easeFunc).duration(transitionDuration) + .attr('r', 0) + .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) + .attr('cx', horizontalQ ? vScale(0) : scale(q2)).remove(); + + var ptsEnter = pts.enter().append('circle').attr('class', 'point').attr('r', 0) + .attr('cx', horizontalQ ? 0 : scale(q2)) + .attr('cy', horizontalQ ? scale(q2) : 0); + + pts = pts.merge(ptsEnter); + + // console.log(pointsTooltip.header()) + + var pTTips = tooltip().selection(pts).data(violinPointsExtractor(key, currentData)) + .header(pointsTooltip.header()) + .keys(pointsTooltip.keys()) + .values(pointsTooltip.values()); + + pTTips(); + + var pMin = d3.min(currentData.pointValues), pMax = d3.max(currentData.pointValues); + + + + pts.transition().duration(transitionDuration).ease(easeFunc).attr('r', pointRadius) + .attr('cy', function(pointKey, ii){ + var dd = currentData.pointValues[ii]; + if (horizontalQ) { return scale(extent[1]) - scale(dd) } + var j = utils.arr.whichBin(currentData.binned, dd); + var r = Math.random(); + var n = vScale(r * currentData.frequencies[j] * 0.5); + var k = Math.random() > 0.5 ? n : -n; + return k + }) + .attr('cx', function(pointKey, ii){ + var dd = currentData.pointValues[ii]; + if (horizontalQ) { + var j = utils.arr.whichBin(currentData.binned, dd); + var r = Math.random(); + var n = vScale(r * currentData.frequencies[j] * 0.5); + var k = Math.random() > 0.5 ? n : -n; + return k + } + return scale(dd) + }) + .attr('stroke', function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'stroke', strokeColor, pMin, pMax) }) + .attr('fill' , function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'fill' , strokeColor, pMin, pMax) }) + .attr('stroke-width', pointStrokeWidth); + + ptsContainer.selectAll('circle.point').on('mouseover', function(dd, ii){ + container.selectAll('g.'+objectClass).style('opacity', 0.2); + t.style('opacity', 1); + la.attr('stroke-width',objectStrokeWidth*2); + ra.attr('stroke-width',objectStrokeWidth*2); + + container.selectAll('.point').style('opacity', 0.2); + d3.select(this).style('opacity', 1).attr('r', pointRadius * 2).attr('stroke-width',pointStrokeWidth*2); + }); + ptsContainer.selectAll('circle.point').on('mouseout', function(dd, ii){ + var e = document.createEvent('SVGEvents'); + e.initEvent('mouseout',true,true); + area.node().dispatchEvent(e); + + container.selectAll('.point').style('opacity', 1); + d3.select(this).attr('stroke-width', pointStrokeWidth).attr('r', pointRadius); + }); + } + else { + cV.selectAll('.point') + .transition().duration(transitionDuration).ease(easeFunc) + .attr('r', 0) + .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) + .attr('cx', horizontalQ ? vScale(0) : scale(q2)) + .remove(); + } + + + }); - tooltip$$1.selection(points) - .data(data); + tooltip$$1.selection(container.selectAll('g:not(.to-remove).'+objectClass + ' .area')); + if (tooltip$$1.data() == undefined) {tooltip$$1.data(data);} tooltip$$1(); - } + if (tooltip$$1.values() == undefined) { + tooltip$$1.values([ + function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] } + ]); + } - return scatter + } + + return violin } -/******************************************************************************* -** ** -** ** -** PLOTZOOM ** -** ** -** ** -*******************************************************************************/ + + + /** - * Creates an plotZoom instance, which can handle drag and scroll events - * @constructor plotZoom - * @param {function} chart a function instance of one of the d3sm plots (e.g. bar, boxwhisker, bubbleHeatmap, violin, etc) - * @param {axis} xAxis the axis instance responsible for the x axis - * @param {axis} yAxis the axis instance responsible for the y axis - * @namespace plotZoom - * @returns {function} zoom - */ -function plotZoom( chart, xAxis, yAxis ) { +* Produces the function which manipulates the violin data to have values needed +* for rendering the violins as svg. +* @returns {function} calculateViolinValues +* @namespace neededViolinValues +*/ +function neededViolinValues() { var /** - * The event on which to fire - * (see {@link plotZoom#eventType}) - * @param {string} eventType which event it should handle. Currently supports scroll and wheel - * @memberof plotZoom# - * @property - */ - eventType, - /** - * A scaling factor for the wheel "speed" - * (see {@link plotZoom#wheelSpeed}) - * @param {number} [wheelSpeed=20] scales the wheel translation by wheelSpeed - * @memberof plotZoom# + * Whether or not the orientation of the violins are horizontal + * (see {@link violin#orient}) + * @param {Object} [horizontalQ=true] + * @memberof neededViolinValues# * @property */ - wheelSpeed = 20, + horizontalQ = true, /** - * The orientation in which to allow scrolling: 'horizontal', 'vertical', or '2D' - * (see {@link plotZoom#orient}) - * @param {string} [orient=chart.orient() || 'horizontal'] - * @memberof plotZoom# + * Keys to be put into the utils.math.quartiles if they need to be calculated. + * (see {@link violin#quartileKeys}) + * @param {Object} [quartileKeys=['Q0', 'Q1', 'Q2', 'Q3', 'Q4']] + * @memberof neededViolinValues# * @property */ - orient = (chart.orient == undefined) ? 'horizontal' : chart.orient(), + quartileKeys = ['Q0', 'Q1', 'Q2', 'Q3', 'Q4'], /** - * The max distance allowed to scroll in the x direction - * (see {@link plotZoom#xLock}) - * @param {number} [xLock=chart.spaceX()] ideally chart.overflowQ() == true and this value is the - * bounding rect across all elements in the chart minus the space in which to show. - * @memberof plotZoom# + * Function which given the key of the violin and that key's associated value + * returns the object consiting of the points of the violin + * (see {@link violin#violinPointsExtractor}) + * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] + * @memberof neededViolinValues# * @property */ - xLock=chart.spaceX(), + violinPointsExtractor, /** - * The max distance allowed to scroll in the y direction - * (see {@link plotZoom#yLock}) - * @param {number} [yLock=chart.spaceY()] ideally chart.overflowQ() == true and this value is the - * bounding rect across all elements in the chart minus the space in which to show. - * @memberof plotZoom# + * Function which given the key of the current point and the object of points for the + * violin, returns the numerical value of the point + * (see {@link violin#violinPointValueExtractor}) + * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] + * @memberof neededViolinValues# * @property */ - yLock=chart.spaceY(), - - chartSel = chart.selection(), - xAxisSel = xAxis.selection(), - yAxisSel = yAxis.selection(), - svg = d3.select(chartSel.thisSVG()); + violinPointValueExtractor; /** - * Gets or sets the event type in which to respond - * (see {@link plotZoom#eventType}) - * @param {string} [_=none] should be 'drag' or 'wheel' - * @returns {zoom | string} - * @memberof plotZoom + * Gets / sets the horizontalQ + * (see {@link violin#orient}) + * @param {boolean} [_=none] + * @returns {calculateViolinValues | boolean} + * @memberof calculateViolinValues * @property - * by default plotZoom=undefined + * + * by default horizontalQ = true */ - zoom.eventType = function(_) { return arguments.length ? (eventType = _, zoom) : eventType; }; + calculateViolinValues.horizontalQ = function(_) { return arguments.length ? (horizontalQ=_, calculateViolinValues) : horizontalQ }; /** - * Gets or sets the wheel speed in which to scale the wheel scroll transform - * (see {@link plotZoom#wheelSpeed}) - * @param {number} [_=none] - * @returns {zoom | number} - * @memberof plotZoom + * Gets / sets the quartileKeys + * (see {@link violin#quartileKeys}) + * @param {string[]} [_=none] + * @returns {calculateViolinValues | string[]} + * @memberof calculateViolinValues * @property - * by default wheelSpeed=20 + * + * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] */ - zoom.wheelSpeed = function(_) { return arguments.length ? (wheelSpeed = _, zoom) : wheelSpeed; }; + calculateViolinValues.quartileKeys = function(_) { return arguments.length ? (quartileKeys=_, calculateViolinValues) : quartileKeys }; /** - * Gets or sets the orientation in which items are manipulated - * (see {@link plotZoom#orient}) - * @param {string} [_=none] should be horizontal, vertical, or 2D - * @returns {zoom | string} - * @memberof plotZoom + * Gets / sets the violinPointsExtractor + * (see {@link violin#violinPointsExtractor}) + * @param {function} [_=none] + * @returns {calculateViolinValues | function} + * @memberof calculateViolinValues * @property - * by default orient=chart.orient() || 'horizontal' + * + * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } */ - zoom.orient = function(_) { return arguments.length ? (orient = _, zoom) : orient; }; - + calculateViolinValues.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor=_, calculateViolinValues) : violinPointsExtractor }; /** - * Gets or sets the max distance in which to scroll X - * (see {@link plotZoom#xLock}) - * @param {number} [_=none] should be a positive value - * @returns {zoom | number} - * @memberof plotZoom + * Gets / sets the violinPointValueExtractor + * (see {@link violin#violinPointValueExtractor}) + * @param {function} [_=none] + * @returns {calculateViolinValues | function} + * @memberof calculateViolinValues * @property - * by default xLock=chart.spaceX() + * + * by default violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value } */ - zoom.xLock = function(_) { return arguments.length ? (xLock = _, zoom) : xLock; }; + calculateViolinValues.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor=_, calculateViolinValues) : violinPointValueExtractor }; + + /** - * Gets or sets the max distance in which to scroll Y - * (see {@link plotZoom#yLock}) - * @param {number} [_=none] should be a positive value - * @returns {zoom | number} - * @memberof plotZoom - * @property - * by default yLock=chart.spaceY() - */ - zoom.yLock = function(_) { return arguments.length ? (yLock = _, zoom) : yLock; }; + * The function produced by neededViolinValues. + * + * Adds the data need to render the violin as an svg + * @param {string} violinKey the key of the current violin + * @param {object} data the object consisting of violinKey - values (violin data) pairs + * @returns {none} this function manipulates the passed data object adding the following keys + * + * data[violinKey].binned // the binned values of the points + * + * data[violinKey].frequencies // the list consisting of the length of each bin + * + * data[violinKey].contour // the points depicting the contour of 1/2 of the violin + * + * data[violinKey].utils.math.quartiles // the utils.math.quartiles of the points' values + * + * data[violinKey].pointKeys // the keys associated with the points + * + * data[violinKey].pointValues // the numerical values of the points + * + * @memberof neededViolinValues# + * @property + */ + function calculateViolinValues(violinKey, data) { + // data for the current violin + var violinData = data[violinKey]; + // the object of points + var violinPoints = violinPointsExtractor(violinKey, violinData); + // + var violinPointsKeys = d3.keys(violinPoints); + // the numerical values of those points + var violinPointsValues = violinPointsKeys.map(function(pk, i){return violinPointValueExtractor(pk, violinPoints)}); + // utils.math.quartiles of those points + var pointQuartiles = utils.math.quartiles(violinPointsValues, quartileKeys); - function setLocks() { - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')); - var chartObjTrans = getTranslation(chartObjSel.attr('transform')); - var cos = chartObjSel.attr('transform', 'translate(0,0)'); - xLock = chartSel.node().getBBox().width - chart.spaceX() * .9; - yLock = chartSel.node().getBBox().height - chart.spaceY() * .9; - cos.attr('transform', 'translate('+chartObjTrans[0]+','+chartObjTrans[1]+')'); - log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); + // binned points + var binned = d3.histogram()(violinPointsValues); + // length of bins + var frequencies = binned.map(bin=>bin.length); + // min and max countour points for nice drawings + var minContourPoint = horizontalQ ? {x: 0, y: d3.min(violinPointsValues)} : {x: d3.min(violinPointsValues), y: 0}; + var maxContourPoint = horizontalQ ? {x: 0, y: d3.max(violinPointsValues)} : {x: d3.max(violinPointsValues), y: 0}; + var violinContourPoints = binned.map(function(bin, i) { + return horizontalQ + ? {y: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), x: frequencies[i]} + : {x: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), y: frequencies[i]} + }); + // points along which to draw the violin shpe + violinContourPoints = [minContourPoint].concat(violinContourPoints).concat([maxContourPoint]); + + // set data + violinData.binned = binned; + violinData.frequencies = frequencies; + violinData.contour = violinContourPoints; + violinData.utils.math.quartiles = pointQuartiles; + violinData.pointKeys = violinPointsKeys; + violinData.pointValues = violinPointsValues; } + return calculateViolinValues +} - /** - * Sets the x and y locks (how far one can scroll) - * (see {@link plotZoom#xLock} and {@link plotZoom#yLock}) - * @function plotZoom.setLocks - * @returns {undefined} - * @memberof plotZoom - * @property - */ - zoom.setLocks = setLocks; +function upset ( selection ) { + var + data, + orient='horizontal', + spaceX, + spaceY, + overflowQ=false, + minObjectSize=20, + maxObjectSize=50, + circleStrokeWidth=2, + // colorFunction= + backgroundFill = 'transparent', + namespace='d3sm-upset', + objectClass = 'upset', - function zoom() { - setLocks(); + transitionDuration = 1000, + easeFunc = d3.easeExp, - var horizontalQ, verticalQ; - if (orient == '2D') {horizontalQ = true; verticalQ = true;} - if (orient == 'horizontal') {horizontalQ = true; verticalQ = false;} - if (orient == 'vertical') {verticalQ = true; horizontalQ = false;} + setKey = "set", + intersectionKey = "intersection", + elementsKey = "elements", - // capture transform event - var transform = d3.event.transform; + setExtractor = function(key, i) {return data[key][setKey]}, + intersectionExtractor = function(key, i) {return data[key][intersectionKey]}, + elementExtractor = function(key, i) {return data[key][elementsKey]}, - var chartBox = chartSel.node().getBBox(); - var xAxisBox = xAxisSel.node().getBBox(); - var yAxisBox = xAxisSel.node().getBBox(); + cellKeys, + setValues, + intersectionValues, + xObjectSpacer = 0.05, + yObjectSpacer = 0.05, + radius, - var chartWidth = chartBox.width - chartBox.x; - var chartHeight = chartBox.height - chartBox.y; - var xAxisWidth = xAxisBox.width;// - xAxisBox.x - var xAxisHeight = xAxisBox.height;// - xAxisBox.y - var yAxisWidth = yAxisBox.width;// - yAxisBox.x - var yAxisHeight = yAxisBox.height;// -yAxisBox.y + // listDelim = ';' - // enable wheel event - if (eventType == "wheel") { - var e = d3.event; - // prevent page scrolling - e.preventDefault(); - // event delta is very very slow, so speed it up with wheel speed - var w = d3.event.deltaY * wheelSpeed; - var shiftQ = d3.event.shiftKey; + yObjectSize, + ySpacerSize, + xObjectSize, + xSpacerSize, - // d3 has no way to make custom transform, so make an object and add - // the necessary functions to make wheel event compatible with drag events - if (orient == '2D') { - transform = shiftQ ? {k: 1, x: w, y: 0} : {k: 1, x: 0, y: w}; - } else { - transform = horizontalQ ? {k: 1, x: w, y: 0} : {k: 1, x: 0, y: w}; - } - // the * -1 inverts the direction - transform.applyX = function(x) { return x * this.k + this.x * -1; }; - transform.applyY = function(y) { return y * this.k + this.y * -1; }; - } + setKeySortingFunction = function(a, b) { return setValues.indexOf(setExtractor(a)) - setValues.indexOf(setExtractor(b)) }, + intersectionKeySortingFunction = function(a, b) { return intersectionValues.indexOf(intersectionExtractor(a)) - intersectionValues.indexOf(intersectionExtractor(b)) }; + upset.selection = function(_) { return arguments.length ? (selection = _, upset) : selection; }; + upset.data = function(_) { return arguments.length ? (data = _, upset) : data; }; + upset.orient = function(_) { return arguments.length ? (orient = _, upset) : orient; }; + upset.spaceX = function(_) { return arguments.length ? (spaceX = _, upset) : spaceX; }; + upset.spaceY = function(_) { return arguments.length ? (spaceY = _, upset) : spaceY; }; + upset.overflowQ = function(_) { return arguments.length ? (overflowQ = _, upset) : overflowQ; }; + upset.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, upset) : minObjectSize; }; + upset.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, upset) : maxObjectSize; }; + upset.circleStrokeWidth = function(_) { return arguments.length ? (circleStrokeWidth = _, upset) : circleStrokeWidth; }; + upset.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, upset) : backgroundFill; }; + upset.namespace = function(_) { return arguments.length ? (namespace = _, upset) : namespace; }; + upset.objectClass = function(_) { return arguments.length ? (objectClass = _, upset) : objectClass; }; + upset.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, upset) : transitionDuration; }; + upset.easeFunc = function(_) { return arguments.length ? (easeFunc = _, upset) : easeFunc; }; + upset.cellKeys = function(_) { return arguments.length ? (cellKeys = _, upset) : cellKeys; }; + upset.setValues = function(_) { return arguments.length ? (setValues = _, upset) : setValues; }; + upset.intersectionValues = function(_) { return arguments.length ? (intersectionValues = _, upset) : intersectionValues; }; + upset.xObjectSpacer = function(_) { return arguments.length ? (xObjectSpacer = _, upset) : xObjectSpacer; }; + upset.yObjectSpacer = function(_) { return arguments.length ? (yObjectSpacer = _, upset) : yObjectSpacer; }; + upset.radius = function(_) { return arguments.length ? (radius = _, upset) : radius; }; + upset.setExtractor = function(_) { return arguments.length ? (setExtractor = _, upset) : setExtractor; }; + upset.intersectionExtractor = function(_) { return arguments.length ? (intersectionExtractor = _, upset) : intersectionExtractor; }; + upset.elementExtractor = function(_) { return arguments.length ? (elementExtractor = _, upset) : elementExtractor; }; + upset.setKeySortingFunction = function(_) { return arguments.length ? (setKeySortingFunction = _, upset) : setKeySortingFunction; }; + upset.intersectionKeySortingFunction = function(_) { return arguments.length ? (intersectionKeySortingFunction = _, upset) : intersectionKeySortingFunction; }; - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')); - var xAxisObjSel = xAxisSel.select('.'+hypenate(xAxis.namespace(),'object-container')); - var yAxisObjSel = yAxisSel.select('.'+hypenate(yAxis.namespace(),'object-container')); + upset.yObjectSize = function(_) { return arguments.length ? (yObjectSize = _, upset) : yObjectSize; }; + upset.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, upset) : ySpacerSize; }; + upset.xObjectSize = function(_) { return arguments.length ? (xObjectSize = _, upset) : xObjectSize; }; + upset.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, upset) : xSpacerSize; }; - // xLock = chartSel.node().getBBox().width - chart.spaceX() - chartSel.node().getBBox().x - // yLock = chartSel.node().getBBox().height - chart.spaceY() - // console.table({'xLock':xLock, "yLock":yLock}) - // bhm.selection().node().getBBox().width - bhm.spaceX() + function upset() { + // for convenience in handling orientation specific values + var horizontalQ = (orient == 'horizontal') ? true : false; + var verticalQ = !horizontalQ; + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - var chartObjTrans = getTranslation(chartObjSel.attr('transform')); - var xAxisObjTrans = getTranslation(xAxisObjSel.attr('transform')); - var yAxisObjTrans = getTranslation(yAxisObjSel.attr('transform')); + cellKeys = d3.keys(data); + setValues = utils$1.arr.unique(cellKeys.map(setExtractor)).sort(); + intersectionValues = utils$1.arr.unique(cellKeys.map(intersectionExtractor)).sort().sort(function(a, b){ + return a.split(';').length - b.split(';').length + }); - var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0; - if (horizontalQ) {x = x < -xLock ? (transform.x = 0, -xLock) : (transform.x = 0, Math.min(x, 0)); } + if (!horizontalQ) { + cellKeys.sort(function(a, b){ return setKeySortingFunction(a, b) || intersectionKeySortingFunction(a, b) }); + } else { + cellKeys.sort(function(a, b){ return intersectionKeySortingFunction(a, b) || setKeySortingFunction(a, b) }); + } - var y = verticalQ ? transform.applyY(chartObjTrans[1]) : 0; - if (verticalQ) {y = y < -yLock ? (transform.y = 0, -yLock): (transform.y = 0, Math.min(y, 0));} - chartObjSel.attr('transform', 'translate('+x+','+y+')'); - if (horizontalQ) { xAxisObjSel.attr('transform', 'translate('+x+','+0+')'); } - if (verticalQ) { yAxisObjSel.attr('transform', 'translate('+0+','+y+')'); } - // var lasso = svg.select(".lasso-container") - // if (!lasso.empty()) { - // lasso.attr('transform', 'translate('+x+','+y+')') - // } - } + var + xValues = horizontalQ ? intersectionValues : setValues, + yValues = horizontalQ ? setValues : intersectionValues, + xDim = horizontalQ ? xValues.length : yValues.length, + yDim = horizontalQ ? yValues.length : xValues.length; - zoom.reset = function() { - - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')); - var xAxisObjSel = xAxisSel.select('.'+hypenate(xAxis.namespace(),'object-container')); - var yAxisObjSel = yAxisSel.select('.'+hypenate(yAxis.namespace(),'object-container')); - chartObjSel.attr('transform', 'translate('+0+','+0+')'); - xAxisObjSel.attr('transform', 'translate('+0+','+0+')'); - yAxisObjSel.attr('transform', 'translate('+0+','+0+')'); - }; + // console.utils.con.log(xValues, yValues) - return zoom -} -/******************************************************************************* + xObjectSize = utils$1.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, xObjectSpacer, overflowQ); + yObjectSize = utils$1.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, yObjectSpacer, overflowQ); + xSpacerSize = utils$1.math.calculateWidthOfSpacer(xValues, spaceX, xObjectSize, xDim, xObjectSpacer, overflowQ); + ySpacerSize = utils$1.math.calculateWidthOfSpacer(yValues, spaceY, yObjectSize, yDim, yObjectSpacer, overflowQ); -** ** -** ** -** PLOTZOOM ** -** ** -** ** -*******************************************************************************/ -/** - * Creates an plotZoom instance, which can handle drag and scroll events - * @constructor plotZoom - * @param {function} chart a function instance of one of the d3sm plots (e.g. bar, boxwhisker, bubbleHeatmap, violin, etc) - * @param {axis} xAxis the axis instance responsible for the x axis - * @param {axis} yAxis the axis instance responsible for the y axis - * @namespace plotZoom - * @returns {function} zoom - */ -function multiPlotZoom( chart ) { - var - /** - * The event on which to fire - * (see {@link plotZoom#eventType}) - * @param {string} eventType which event it should handle. Currently supports scroll and wheel - * @memberof plotZoom# - * @property - */ - eventType, - /** - * A scaling factor for the wheel "speed" - * (see {@link plotZoom#wheelSpeed}) - * @param {number} [wheelSpeed=20] scales the wheel translation by wheelSpeed - * @memberof plotZoom# - * @property - */ - wheelSpeed = 20, - /** - * The orientation in which to allow scrolling: 'horizontal', 'vertical', or '2D' - * (see {@link plotZoom#orient}) - * @param {string} [orient=chart.orient() || 'horizontal'] - * @memberof plotZoom# - * @property - */ - orient = (chart.orient == undefined) ? 'horizontal' : chart.orient(), - /** - * The max distance allowed to scroll in the x direction - * (see {@link plotZoom#xLock}) - * @param {number} [xLock=chart.spaceX()] ideally chart.overflowQ() == true and this value is the - * bounding rect across all elements in the chart minus the space in which to show. - * @memberof plotZoom# - * @property - */ - xLock=chart.spaceX(), - /** - * The max distance allowed to scroll in the y direction - * (see {@link plotZoom#yLock}) - * @param {number} [yLock=chart.spaceY()] ideally chart.overflowQ() == true and this value is the - * bounding rect across all elements in the chart minus the space in which to show. - * @memberof plotZoom# - * @property - */ - yLock=chart.spaceY(), + var ySpacer = groupingSpacer() + .horizontalQ(false) + .moveby('category').numberOfObjects(yDim) + .objectSize(yObjectSize).spacerSize(ySpacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc); - chartSel = chart.selection(), + var xSpacer = groupingSpacer() + .horizontalQ(true) + .moveby('category').numberOfObjects(xDim) + .objectClass(objectClass) + .objectSize(xObjectSize).spacerSize(xSpacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc); - svg = d3.select(chartSel.thisSVG()), - xComponents = [], - yComponents = []; + if (verticalQ) { + xSpacer.objectClass(objectClass); + ySpacer.namespace('across').objectClass(utils$1.str.hypenate(objectClass, 'across')); - /** - * Gets or sets the event type in which to respond - * (see {@link plotZoom#eventType}) - * @param {string} [_=none] should be 'drag' or 'wheel' - * @returns {zoom | string} - * @memberof plotZoom - * @property - * by default plotZoom=undefined - */ - zoom.eventType = function(_) { return arguments.length ? (eventType = _, zoom) : eventType; }; - /** - * Gets or sets the wheel speed in which to scale the wheel scroll transform - * (see {@link plotZoom#wheelSpeed}) - * @param {number} [_=none] - * @returns {zoom | number} - * @memberof plotZoom - * @property - * by default wheelSpeed=20 - */ - zoom.wheelSpeed = function(_) { return arguments.length ? (wheelSpeed = _, zoom) : wheelSpeed; }; - /** - * Gets or sets the orientation in which items are manipulated - * (see {@link plotZoom#orient}) - * @param {string} [_=none] should be horizontal, vertical, or 2D - * @returns {zoom | string} - * @memberof plotZoom - * @property - * by default orient=chart.orient() || 'horizontal' - */ - zoom.orient = function(_) { return arguments.length ? (orient = _, zoom) : orient; }; + ySpacer(container, yValues, 0); + container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'across')) + .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); + } else { + xSpacer.namespace('across').objectClass(utils$1.str.hypenate(objectClass, 'across')); + ySpacer.objectClass(objectClass); - /** - * Gets or sets the max distance in which to scroll X - * (see {@link plotZoom#xLock}) - * @param {number} [_=none] should be a positive value - * @returns {zoom | number} - * @memberof plotZoom - * @property - * by default xLock=chart.spaceX() - */ - zoom.xLock = function(_) { return arguments.length ? (xLock = _, zoom) : xLock; }; - /** - * Gets or sets the max distance in which to scroll Y - * (see {@link plotZoom#yLock}) - * @param {number} [_=none] should be a positive value - * @returns {zoom | number} - * @memberof plotZoom - * @property - * by default yLock=chart.spaceY() - */ - zoom.yLock = function(_) { return arguments.length ? (yLock = _, zoom) : yLock; }; + xSpacer(container, xValues, 0); + container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'across')) + .each(function(d, i){ ySpacer(d3.select(this), yValues, 0); }); + } - zoom.xComponents = function(_) { return arguments.length ? (xComponents = _, zoom) : xComponents; }; - zoom.yComponents = function(_) { return arguments.length ? (yComponents = _, zoom) : yComponents; }; + var cells = container.selectAll('g:not(.to-remove).'+objectClass); + var lookup = {}; + cellKeys.map(function(k, i){ + lookup[setExtractor(k)+'::'+intersectionExtractor(k)] = k; + }); - function setLocks() { - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')); - var chartObjTrans = getTranslation(chartObjSel.attr('transform')); - var cos = chartObjSel.attr('transform', 'translate(0,0)'); + // var positionedCellKeys = [] + // for (var i = 0; i < setValues.length; i++) { + // for (var j = 0; j < intersectionValues.length; j++) { + // var lookupValue = lookup[setValues[j]+"::"+intersectionValues[i]] + // if (lookupValue == undefined) { + // positionedCellKeys.push(undefined) + // } else { + // positionedCellKeys.push(lookupValue) + // } + // console.utils.con.log(i, j, lookupValue) + // } + // } + // + // // console.utils.con.log(positionedCellKeys) + // + // cells.data(positionedCellKeys); - xLock = chartSel.node().getBBox().width - chart.spaceX();// * .9 - yLock = chartSel.node().getBBox().height - chart.spaceY();// * .9 - cos.attr('transform', 'translate('+chartObjTrans[0]+','+chartObjTrans[1]+')'); - log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); - } + cells.data(cellKeys); - /** - * Sets the x and y locks (how far one can scroll) - * (see {@link plotZoom#xLock} and {@link plotZoom#yLock}) - * @function plotZoom.setLocks - * @returns {undefined} - * @memberof plotZoom - * @property - */ - zoom.setLocks = setLocks; + cells.each(function(key, i) { + var t = d3.select(this); + if (key == undefined) {return } + var + currentData = data[key]; + // console.utils.con.log(key, currentData) + var + set = setExtractor(key, i), + intersection = intersectionExtractor(key, i); - function zoom() { - setLocks(); + // console.utils.con.log(set, intersection) - var - xComponentsSel = xComponents.map(function(d, i){return d.selection()}), - yComponentsSel = yComponents.map(function(d, i){return d.selection()}); + t.classed(intersection, true); + t.classed(set, true); - var horizontalQ, verticalQ; - if (orient == '2D') {horizontalQ = true; verticalQ = true;} - if (orient == 'horizontal') {horizontalQ = true; verticalQ = false;} - if (orient == 'vertical') {verticalQ = true; horizontalQ = false;} + var c = utils$1.sel.safeSelect(t, 'circle', utils$1.str.hypenate(objectClass,'circle')); + c.attr('cx', xObjectSize / 2) + .attr('cy', yObjectSize / 2 ) + .attr('r', radius == undefined ? Math.min(xObjectSize, yObjectSize) / 2 : radius) + .attr('fill', intersection.includes(set) ? "black": 'rgb(233,233,233)') + .attr('stroke', "black") + .attr("in-intersection", intersection.includes(set)); + }); - // capture transform event - var transform = d3.event.transform; - var chartBox = chartSel.node().getBBox(); - var xComponentsBox = xComponentsSel.map(function(d, i){return d.node().getBBox()}); - var yComponentsBox = xComponentsSel.map(function(d, i){return d.node().getBBox()}); + } - var chartWidth = chartBox.width - chartBox.x; - var chartHeight = chartBox.height - chartBox.y; + function intersectionTotals() { + var totals = {}; + // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) - // enable wheel event - if (eventType == "wheel") { - var e = d3.event; - // prevent page scrolling - e.preventDefault(); - // event delta is very very slow, so speed it up with wheel speed - var w = d3.event.deltaY * wheelSpeed; - var shiftQ = d3.event.shiftKey; + intersectionValues.map(function(k, i){ totals[k] = {'total': 0}; }); + cellKeys.map(function(k, i){ + var e = elementExtractor(k, i); + if (totals[intersectionExtractor(k, i)]['total'] == 0) { + if (Array.isArray(e)) { + totals[intersectionExtractor(k, i)]['total']+= e.length; + totals[intersectionExtractor(k, i)]['values'] = e; + } else { + totals[intersectionExtractor(k, i)]['total']+= e; + } - // d3 has no way to make custom transform, so make an object and add - // the necessary functions to make wheel event compatible with drag events - if (orient == '2D') { - transform = shiftQ ? {k: 1, x: w, y: 0} : {k: 1, x: 0, y: w}; - } else { - transform = horizontalQ ? {k: 1, x: w, y: 0} : {k: 1, x: 0, y: w}; } - // * -1 is invert - transform.applyX = function(x) { return x * this.k + this.x *-1; }; - transform.applyY = function(y) { return y * this.k + this.y *-1; }; - } - + }); + return totals + } + function setTotals(){ + var totals = {}; + // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')); - var xComponentObjSel = xComponentsSel.map(function(d, i){ - return d.select('.'+hypenate(xComponents[i].namespace(),'object-container')) - }); - var yComponentObjSel = yComponentsSel.map(function(d, i){ - return d.select('.'+hypenate(yComponents[i].namespace(),'object-container')) - }); + setValues.map(function(k, i){ totals[k] = {'total': 0}; }); - var chartObjTrans = getTranslation(chartObjSel.attr('transform')); - var xComponentsObjTrans = xComponentObjSel.map(function(d, i){ - return getTranslation(d.attr('transform')) - }); - var yComponentsObjTrans = yComponentObjSel.map(function(d, i){ - return getTranslation(d.attr('transform')) + cellKeys.map(function(k, i){ + var e = elementExtractor(k, i); + if (Array.isArray(e)) { + totals[setExtractor(k, i)]['total']+= e.length; + } else { + totals[setExtractor(k, i)]['total']+= e; + } }); - - var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0; - if (horizontalQ) {x = x < -xLock ? (transform.x = 0, -xLock) : (transform.x = 0, Math.min(x, 0)); } - - var y = verticalQ ? transform.applyY(chartObjTrans[1]) : 0; - if (verticalQ) {y = y < -yLock ? (transform.y = 0, -yLock): (transform.y = 0, Math.min(y, 0));} - - chartObjSel.attr('transform', 'translate('+x+','+y+')'); - if (horizontalQ) { - // xAxisObjSel.attr('transform', 'translate('+x+','+0+')') - xComponentObjSel.map(function(d, i){ d.attr('transform', 'translate('+x+','+0+')'); }); - } - if (verticalQ) { - // yAxisObjSel.attr('transform', 'translate('+0+','+y+')') - yComponentObjSel.map(function(d, i){ d.attr('transform', 'translate('+0+','+y+')'); }); - - } - - + return totals } - zoom.reset = function() { - - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')); - var xAxisObjSel = xAxisSel.select('.'+hypenate(xAxis.namespace(),'object-container')); - var yAxisObjSel = yAxisSel.select('.'+hypenate(yAxis.namespace(),'object-container')); - chartObjSel.attr('transform', 'translate('+0+','+0+')'); - xAxisObjSel.attr('transform', 'translate('+0+','+0+')'); - yAxisObjSel.attr('transform', 'translate('+0+','+0+')'); - }; + upset.intersectionTotals = intersectionTotals; + upset.setTotals = setTotals; - return zoom + return upset } -/******************************************************************************* -** ** -** ** -** VIOLIN ** -** ** -** ** -*******************************************************************************/ +let charts = { + scatter, bar, bubble: bubbleHeatmap, heatmap, violin, neededViolinValues, upset +}; + +function categoricLegend( selection ) { + var + categories, -/** - * Creates a violin - * - * {@link https://sumneuron.gitlab.io/d3sm/demos/basic-violins/index.html Demo} - * @constructor violin - * @param {d3.selection} selection - * @namespace violin - * @returns {function} violin - */ -function violin( selection ) { - var /** - * Data to plot. Assumed to be a object, where each key corresponds to a violin - * (see {@link violin#data}) + * Data to plot. Assumed to be a object, where each key corresponds to a legend + * (see {@link legend#data}) * @param {Object} [data=undefined] - * @memberof violin# + * @memberof legend# * @property */ data, /** * Which direction to render the bars in - * (see {@link violin#orient}) + * (see {@link legend#orient}) * @param {number} [orient='horizontal'] - * @memberof violin# + * @memberof legend# * @property */ orient='horizontal', /** - * Amount of horizontal space (in pixels) avaible to render the violin in - * (see {@link violin#spaceX}) + * Amount of horizontal space (in pixels) avaible to render the legend in + * (see {@link legend#spaceX}) * @param {number} [spaceX=undefined] - * @memberof violin# + * @memberof legend# * @property */ spaceX, /** - * Amount of vertical space (in pixels) avaible to render the violin in - * (see {@link violin.spaceY}) + * Amount of vertical space (in pixels) avaible to render the legend in + * (see {@link legend.spaceY}) * @param {number} [spaceY=undefined] - * @memberof violin# + * @memberof legend# * @property */ spaceY, + /** - * Whether or not to allow violin to render elements pass the main spatial dimension - * given the orientation (see {@link violin#orient}), where {@link violin#orient}="horizontal" - * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" - * the main dimension is {@link violin#spaceY} + * Whether or not to allow legend to render elements pass the main spatial dimension + * given the orientation (see {@link legend#orient}), where {@link legend#orient}="horizontal" + * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" + * the main dimension is {@link legend#spaceY} * @param {boolean} [overflowQ=false] - * @memberof violin# - * @property - */ - overflowQ = true, - /** - * Whether or not to display points inside the points - * @param {boolean} [pointsQ=false] - * @memberof violin# + * @memberof legend# * @property */ - pointsQ = true, + overflowQ = false, + /** * An array - putatively of other arrays - depicting how bars should be arranged * @param {Array[]} [grouping=undefined] - * @memberof violin# + * @memberof legend# * @property */ grouping, + /** - * How to get the value of the violin + * How to get the value of the legend * @param {function} [valueExtractor=function(key, index) { return data[key] }] - * @memberof violin# + * @memberof legend# * @property */ - valueExtractor = function(key, index) {return data[key] }, + valueExtractor = function(key, index) { return data[key] }, /** - * How to sort the bars - if {@link violin#grouping} is not provided. + * How to sort the bars - if {@link bar#grouping} is not provided. * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] - * @memberof violin# - * @property - */ - sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, - - /** - * The scale for which violin values should be transformed by - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof violin# - * @property - */ - scale = d3.scaleLinear(), - /** - * The padding for the domain of the scale (see {@link violin#scale}) - * @param {number} [domainPadding=0.5] - * @memberof violin# + * @memberof bar# * @property */ - domainPadding = 0.5, + sortingFunction = function(keyA, keyB) {return d3.ascending(keyA, keyB)}, /** * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link violin#orient}), where {@link violin#orient}="horizontal" - * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" - * the main dimension is {@link violin#spaceY} between bars + * (see {@link legend#orient}), where {@link legend#orient}="horizontal" + * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" + * the main dimension is {@link legend#spaceY} between bars * @param {number} [objectSpacer=0.05] - * @memberof violin# + * @memberof legend# * @property */ objectSpacer = 0.05, /** * The minimum size that an object can be * @param {number} [minObjectSize=50] - * @memberof violin# + * @memberof legend# * @property */ - minObjectSize = 50, + minObjectSize = 10, /** * The maximum size that an object can be * @param {number} [maxObjectSize=100] - * @memberof violin# + * @memberof legend# * @property */ maxObjectSize = 100, @@ -7252,2947 +7024,1410 @@ function violin( selection ) { /** * The stroke width of the bars * @param {number} [barStrokeWidth=2] - * @memberof violin# + * @memberof legend# * @property */ - objectStrokeWidth = 2, + bubbleStrokeWidth = 2, /** * Instance of ColorFunction * @param {function} [colorFunction = colorFunction()] - * @memberof violin# + * @memberof legend# * @property */ colorFunction$$1 = colorFunction(), - /** - * Instance of ColorFunction modified by a scale for the points - * @param {function} [pointColorFunc = colorFunction()] - * @memberof violin# - * @property - */ - pointColorFunc = function (d, type, base, min, max) { - var minMaxHexScale = d3.scaleLinear().domain([min, max]).range([-0.25, 0.05]); - var scaledColor = modifyHexidecimalColorLuminance(base.replace('#', ''), minMaxHexScale(d)); - var mod = type == "stroke" ? 0 : 0.25; - return modifyHexidecimalColorLuminance(scaledColor.replace('#', ''), mod) - }, - - /** - * The radius of a point - * @param {number} [pointRadius=3] - * @memberof violin# - * @property - */ - pointRadius = 3, - /** - * The stroke width of the oints - * @param {number} [pointStrokeWidth=2] - * @memberof violin# - * @property - */ - pointStrokeWidth = 2, /** * Color of the background * @param {string} [backgroundFill="transparent"] - * @memberof violin# + * @memberof legend# * @property */ backgroundFill = 'transparent', /** - * Namespace for all items made by this instance of violin - * @param {string} [namespace="d3sm-violin"] - * @memberof violin# + * Namespace for all items made by this instance of legend + * @param {string} [namespace="d3sm-legend"] + * @memberof legend# * @property */ - namespace = 'd3sm-violin', + namespace = 'd3sm-legend', /** - * Class name for violin container ( element) - * @param {string} [objectClass="violin"] - * @memberof violin# + * Class name for legend container ( element) + * @param {string} [objectClass="legend"] + * @memberof legend# * @property */ - objectClass = 'violin', + objectClass = 'legend', + /** * Duration of all transitions of this element * @param {number} [transitionDuration=1000] - * @memberof violin# + * @memberof legend# * @property */ transitionDuration = 1000, /** * Easing function for transitions * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof violin# + * @memberof legend# * @property */ - easeFunc = d3.easeExp, + easeFunc = d3.easeExp; - /** - * The keys corresponding to each quartile - * @param {string[]} [quartileKeys=["Q0", "Q1", "Q2", "Q3", "Q4"]] - * @memberof violin# - * @property - */ - quartileKeys = ["Q0", "Q1", "Q2", "Q3", "Q4"], + + legend.categories = function(_) { return arguments.length ? (categories=_, legend) : categories }; /** - * The keys of the bars - * @param {string[]} [violinKeys=undefined] - * @memberof violin# - * @property - */ - violinKeys, - /** - * The values of the bars - * @param {number[]} [violinValues=undefined] - * @memberof violin# - * @property - */ - violinValues, + * Gets or sets the selection in which items are manipulated + * @param {d3.selection} [_=none] + * @returns {legend | d3.selection} + * @memberof legend + * @property + * by default selection = selection + */ + legend.selection = function(_) { return arguments.length ? (selection = _, legend) : selection; }; /** - * The objectSize (actual width) used by the bars - * @param {number} [objectSize=undefined] - * @memberof violin# - * @property - */ - objectSize, + * Gets or sets the data + * (see {@link legend#data}) + * @param {number} [_=none] + * @returns {legend | object} + * @memberof legend + * @property + */ + legend.data = function(_) { return arguments.length ? (data = _, legend) : data; }; /** - * The spacerSize (actual width) used by the spacers between the bars - * @param {number} [spacerSize=undefined] - * @memberof violin# - * @property - */ - spacerSize, - - /** - * Instance of Tooltip - * @param {function} [tooltip=tooltip()] - * @memberof violin# - * @property - */ - tooltip$$1 = tooltip().keys([quartileKeys[4], quartileKeys[3], quartileKeys[2], quartileKeys[1], quartileKeys[0]]), - pointsTooltip = tooltip(), - // pointKeyExtractor = function(violinKey, violinData, violinValues) {return d3.keys(violinValues[violinKey].values)}, - // pointValueExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]}, - - - /** - * Function which given the key of the violin and that key's associated value - * returns the object consiting of the points of the violin - * (see {@link violin#violinPointsExtractor}) - * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] - * @memberof violin# - * @property - */ - violinPointsExtractor = function (violinKey, violinData) {return violinData.points }, - /** - * Function which given the key of the current point and the object of points for the - * violin, returns the numerical value of the point - * (see {@link violin#violinPointValueExtractor}) - * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] - * @memberof violin# - * @property - */ - violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }; - - - /** - * Gets or sets the violinPointsExtractor - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } - */ - violin.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor = _, violin) : violinPointsExtractor; }; - /** - * Gets or sets the violinPointValueExtractor - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - * by default violinPointsExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]} - */ - violin.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor = _, violin) : violinPointValueExtractor; }; - - - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {violin | d3.selection} - * @memberof violin - * @property - * by default selection = selection - */ - violin.selection = function(_) { return arguments.length ? (selection = _, violin) : selection; }; - /** - * Gets or sets the data - * (see {@link violin#data}) - * @param {number} [_=none] - * @returns {violin | object} - * @memberof violin - * @property - */ - violin.data = function(_) { return arguments.length ? (data = _, violin) : data; }; - /** - * Gets or sets the orient of the boxes - * (see {@link violin#orient}) + * Gets or sets the orient of the bars + * (see {@link legend#orient}) * @param {number} [_=none] - * @returns {violin | object} - * @memberof violin + * @returns {legend | object} + * @memberof legend * @property */ - violin.orient = function(_) { return arguments.length ? (orient = _, violin) : orient; }; + legend.orient = function(_) { return arguments.length ? (orient = _, legend) : orient; }; /** * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link violin#spaceX}) + * (see {@link legend#spaceX}) * @param {number} [_=none] should be a number > 0 - * @returns {violin | number} - * @memberof violin + * @returns {legend | number} + * @memberof legend * @property * by default spaceX = undefined */ - violin.spaceX = function(_) { return arguments.length ? (spaceX = _, violin) : spaceX; }; + legend.spaceX = function(_) { return arguments.length ? (spaceX = _, legend) : spaceX; }; /** * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link violin#spaceY}) + * (see {@link legend#spaceY}) * @param {number} [_=none] should be a number > 0 - * @returns {violin | number} - * @memberof violin + * @returns {legend | number} + * @memberof legend * @property * by default spaceY = undefined */ - violin.spaceY = function(_) { return arguments.length ? (spaceY = _, violin) : spaceY; }; - + legend.spaceY = function(_) { return arguments.length ? (spaceY = _, legend) : spaceY; }; /** - * Gets / sets whether or not violin is allowed to go beyond specified dimensions - * (see {@link violin#overflowQ}) + * Gets / sets whether or not legend is allowed to go beyond specified dimensions + * (see {@link legend#spaceX}) * @param {boolean} [_=none] - * @returns {violin | boolean} - * @memberof violin + * @returns {legend | boolean} + * @memberof legend * @property * by default overflowQ = false */ - violin.overflowQ = function(_) { return arguments.length ? (overflowQ = _, violin) : overflowQ; }; - /** - * Gets / sets whether or not to plot points with the violins - * (see {@link violin#pointsQ}) - * @param {boolean} [_=none] - * @returns {violin | boolean} - * @memberof violin - * @property - * by default pointsQ = false - */ - violin.pointsQ = function(_) { return arguments.length ? (pointsQ = _, violin) : pointsQ; }; - - + legend.overflowQ = function(_) { return arguments.length ? (overflowQ = _, legend) : overflowQ; }; /** - * Gets / sets the grouping of the boxes - * (see {@link violin#grouping}) + * Gets / sets the grouping of the bars + * (see {@link legend#grouping}) * @param {Array[]} [_=none] - * @returns {violin | Array[]} - * @memberof violin + * @returns {legend | Array[]} + * @memberof legend * @property * by default grouping = undefined */ - violin.grouping = function(_) { return arguments.length ? (grouping = _, violin) : grouping; }; + legend.grouping = function(_) { return arguments.length ? (grouping = _, legend) : grouping; }; /** * Gets / sets the valueExtractor - * (see {@link violin#valueExtractor}) + * (see {@link legend#valueExtractor}) * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin + * @returns {legend | function} + * @memberof legend * @property + * by default valueExtractor = function(key, index) { return data[key] }, */ - violin.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, violin) : valueExtractor; }; + legend.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, legend) : valueExtractor; }; /** * Gets / sets the sortingFunction - * (see {@link violin#sortingFunction}) + * (see {@link bar#sortingFunction}) * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - */ - violin.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, violin) : sortingFunction; }; - - /** - * Gets / sets the scale for which the violin values should be transformed by - * (see {@link violin#scale}) - * @param {d3.scale} [_=none] - * @returns {violin | d3.scale} - * @memberof violin - * @property - * by default scale = d3.scaleLinear() - */ - violin.scale = function(_) { return arguments.length ? (scale = _, violin) : scale; }; - /** - * Gets / sets the padding for the domain of the scale - * (see {@link violin#domainPadding}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {bar | function} + * @memberof bar * @property - * by default domainPadding = 0.5 + * by default sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, */ - violin.domainPadding = function(_) { return arguments.length ? (domainPadding = _, violin) : domainPadding; }; - - + legend.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, legend) : sortingFunction; }; /** * Gets / sets objectSpacer - * (see {@link violin#objectSpacer}) + * (see {@link legend#objectSpacer}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {legend | number} + * @memberof legend * @property * by default objectSpacer = 0.05 */ - violin.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, violin) : objectSpacer; }; + legend.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, legend) : objectSpacer; }; /** * Gets / sets the minObjectSize - * (see {@link violin#minObjectSize}) + * (see {@link legend#minObjectSize}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {legend | number} + * @memberof legend * @property - * by default minObjectSize = 15 + * by default minObjectSize = 50 */ - violin.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, violin) : minObjectSize; }; + legend.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, legend) : minObjectSize; }; /** * Gets / sets the maxObjectSize - * (see {@link violin#maxObjectSize}) + * (see {@link legend#maxObjectSize}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {legend | number} + * @memberof legend * @property - * by default maxObjectSize = 50 + * by default maxObjectSize = 100 */ - violin.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, violin) : maxObjectSize; }; + legend.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, legend) : maxObjectSize; }; /** - * Gets / sets the objectStrokeWidth - * (see {@link violin#objectStrokeWidth}) + * Gets / sets the barStrokeWidth + * (see {@link legend#barStrokeWidth}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {legend | number} + * @memberof legend * @property - * by default objectStrokeWidth = 2 + * by default barStrokeWidth = 2 */ - violin.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, violin) : objectStrokeWidth; }; - - + legend.bubbleStrokeWidth = function(_) { return arguments.length ? (bubbleStrokeWidth = _, legend) : bubbleStrokeWidth; }; /** * Gets / sets the colorFunction - * (see {@link violin#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {violin | colorFunction} - * @memberof violin + * (see {@link legend#colorFunction}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend * @property * by default colorFunction = colorFunction() */ - violin.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, violin) : colorFunction$$1; }; + legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, legend) : colorFunction$$1; }; + /** - * Gets / sets the colorFunction - * (see {@link violin#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {violin | colorFunction} - * @memberof violin + * Gets / sets the backgroundFill + * (see {@link legend#backgroundFill}) + * @param {string} [_=none] + * @returns {legend | string} + * @memberof legend * @property - * by default colorFunction = colorFunction() + * by default backgroundFill = 'transparent' */ - violin.pointColorFunc = function(_) { return arguments.length ? (pointColorFunc = _, violin) : pointColorFunc; }; - - + legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, legend) : backgroundFill; }; /** - * Gets / sets the pointRadius - * (see {@link violin#pointRadius}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * Gets / sets the namespace + * (see {@link legend#namespace}) + * @param {string} [_=none] + * @returns {legend | string} + * @memberof legend * @property - * by default pointRadius = 2 + * by default namespace = 'd3sm-legend' */ - violin.pointRadius = function(_) { return arguments.length ? (pointRadius = _, violin) : pointRadius; }; - /** - * Gets / sets the pointStrokeWidth - * (see {@link violin#pointStrokeWidth}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default pointStrokeWidth = 2 - */ - violin.pointStrokeWidth = function(_) { return arguments.length ? (pointStrokeWidth = _, violin) : pointStrokeWidth; }; - - - /** - * Gets / sets the backgroundFill - * (see {@link violin#backgroundFill}) - * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin - * @property - * by default backgroundFill = 'transparent' - */ - violin.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, violin) : backgroundFill; }; - /** - * Gets / sets the namespace - * (see {@link violin#namespace}) - * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin - * @property - * by default namespace = 'd3sm-violin' - */ - violin.namespace = function(_) { return arguments.length ? (namespace = _, violin) : namespace; }; - /** - * Gets / sets the objectClass - * (see {@link violin#objectClass}) - * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin - * @property - * by default objectClass = 'tick-group' - */ - violin.objectClass = function(_) { return arguments.length ? (objectClass = _, violin) : objectClass; }; - - - /** - * Gets / sets the transitionDuration - * (see {@link violin#transitionDuration}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default transitionDuration = 1000 - */ - violin.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, violin) : transitionDuration; }; - /** - * Gets / sets the easeFunc - * (see {@link violin#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {violin | d3.ease} - * @memberof violin - * @property - * by default easeFunc = d3.easeExp - */ - violin.easeFunc = function(_) { return arguments.length ? (easeFunc = _, violin) : easeFunc; }; - - - /** - * Gets / sets the quartileKey - * (see {@link violin#quartileKey}) - * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin - * @property - * by default quartileKey = "quartiles" - */ - violin.quartileKey = function(_) { return arguments.length ? (quartileKey = _, violin) : quartileKey; }; - /** - * Gets / sets the quartileKeys - * (see {@link violin#quartileKeys}) - * @param {string[]} [_=none] - * @returns {violin | string[]} - * @memberof violin - * @property - * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] - */ - violin.quartileKeys = function(_) { return arguments.length ? (quartileKeys = _, violin) : quartileKeys; }; - - - /** - * Gets / sets the violinKeys - * (see {@link violin#violinKeys}) - * @param {string[]} [_=none] - * @returns {violin | string[]} - * @memberof violin - * @property - * by default violinKeys = undefined - */ - violin.violinKeys = function(_) { return arguments.length ? (violinKeys = _, violin) : violinKeys; }; - /** - * Gets / sets the violinValues - * (see {@link violin#violinValues}) - * @param {Object[]} [_=none] - * @returns {violin | Object[]} - * @memberof violin - * @property - * by default violinValues = undefined - */ - violin.violinValues = function(_) { return arguments.length ? (violinValues = _, violin) : violinValues; }; - - /** - * Gets / sets the objectSize - * (see {@link violin#objectSize}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default objectSize = undefined - */ - violin.objectSize = function(_) { return arguments.length ? (objectSize = _, violin) : objectSize; }; - /** - * Gets / sets the spacerSize - * (see {@link violin#spacerSize}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin - * @property - * by default spacerSize = undefined - */ - violin.spacerSize = function(_) { return arguments.length ? (spacerSize = _, violin) : spacerSize; }; - /** - * Gets / sets the tooltip - * (see {@link violin#tooltip}) - * @param {tooltip} [_=none] - * @returns {violin | tooltip} - * @memberof violin - * @property - * by default tooltip = tooltip() - */ - violin.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, violin) : tooltip$$1; }; - /** - * Gets / sets the pointsTooltip - * (see {@link violin#pointsTooltip}) - * @param {tooltip} [_=none] - * @returns {violin | tooltip} - * @memberof violin - * @property - * by default pointsTooltip = tooltip() - */ - violin.pointsTooltip = function(_) { return arguments.length ? (pointsTooltip = _, violin) : pointsTooltip; }; - - - - - - function violin () { - // for convenience in handling orientation specific values - var horizontalQ = (orient == 'horizontal') ? true : false; - - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); - - // if grouping is undefined sort violinKeys by sortingFunction - var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping; - - // console.log(ordered) - - violinKeys = flatten(ordered); - - - var calcValues = neededViolinValues() - .horizontalQ(horizontalQ) - .quartileKeys(quartileKeys) - .violinPointsExtractor(violinPointsExtractor) - .violinPointValueExtractor(violinPointValueExtractor); - - - - // augment valus - violinKeys.map(function(vk, i){ calcValues(vk, data); }); - - var numberOfObjects = violinKeys.length; - - var min = [].concat(...violinKeys.map(function(k, i){return data[k].quartiles[quartileKeys[0]]})); - var max = [].concat(...violinKeys.map(function(k, i){return data[k].quartiles[quartileKeys[quartileKeys.length - 1]]})); - var extent = [Math.min(...min) - domainPadding, Math.max(...max) + domainPadding]; - // console.log(extent, violinValues, ordered) - - // set the scale - scale.domain(extent).range(horizontalQ ? [0,spaceY] : [0, spaceX]); - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - objectSize = calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - // calculate spacer size if needed - spacerSize = calculateWidthOfSpacer(ordered, space, objectSize, numberOfObjects, objectSpacer, overflowQ); - // make the nested groups - var spacerFunction = groupingSpacer() - .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) - .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc) - .namespace(namespace); - - // move stuff - spacerFunction(container, ordered, 0); - // console.log(violinKeys, ordered, container.selectAll('g:not(.to-remove).'+objectClass).nodes()) - - // for color function - var parentIndexArray = []; - container.selectAll('g:not(.to-remove).'+objectClass) - .each(function(d, i){if (hasQ(violinKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}}); - - // update color function - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' - ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) - : colorFunction$$1.dataExtent(extent); - - /* violiin specific needs */ - - - var frequencyMax = Math.max(...[].concat(...violinKeys.map(function(k, i){return d3.max(data[k].frequencies)}))); - var vScale = d3.scaleLinear().domain([0, frequencyMax]).range([0, objectSize / 2]); - - var lArea = d3.line() - .x(function(d, i){ return horizontalQ ? -vScale(d.x) : scale(d.x)}) - .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : -vScale(d.y)}) - .curve(d3.curveBasis); - var rArea = d3.line() - .x(function(d, i){ return horizontalQ ? vScale(d.x) : scale(d.x)}) - .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : vScale(d.y)}) - .curve(d3.curveBasis); - - - - - - - container.selectAll('g:not(.to-remove).'+objectClass).each(function(key, i){ - var t = d3.select(this), - currentData = data[key]; - // needed because bug in selecting .to-remove - if (!hasQ(violinKeys, key)) {return} - var - i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, currentData, i, 'fill'), // prevent duplicate computation - strokeColor = colorFunction$$1(key, currentData, i, 'stroke'), - area = safeSelect(t, 'g', 'area'), - la = safeSelect(area, 'path', 'left'), - ra = safeSelect(area, 'path', 'right'), - quarts = safeSelect(t, 'g', 'quarts'), - lq3 = safeSelect(quarts, 'line', 'q3'), - lq1 = safeSelect(quarts, 'line', 'q1'), - q3 = currentData.quartiles[quartileKeys[3]], - q2 = currentData.quartiles[quartileKeys[2]], - q1 = currentData.quartiles[quartileKeys[1]]; - - t.attr('transform', horizontalQ ? 'translate('+objectSize / 2+',0)' : 'translate(0,'+objectSize / 2+')' ); - // draw curve - la.transition().duration(transitionDuration).attr('d', function(dd, ii){ return lArea(currentData.contour)}) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', objectStrokeWidth); - - ra.transition().duration(transitionDuration).attr('d', function(dd, ii){ return rArea(currentData.contour)}) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', objectStrokeWidth); - - area.node().addEventListener('mouseover', function(dd, ii){ - container.selectAll('g.'+objectClass).style('opacity', 0.2); - t.style('opacity', 1); - la.attr('stroke-width',objectStrokeWidth*2); - ra.attr('stroke-width',objectStrokeWidth*2); - }); - area.node().addEventListener('mouseout', function(dd, ii){ - container.selectAll('g.'+objectClass).style('opacity', 1); - la.attr('stroke-width',objectStrokeWidth); - ra.attr('stroke-width',objectStrokeWidth); - }); - - if (pointsQ) { - var ptsContainer = safeSelect(t, 'g', 'points'); - var pts = ptsContainer.selectAll('.point').data(currentData.pointKeys); - pts.on('mouseover', null); - - - var ptsExit = pts.exit().transition().ease(easeFunc).duration(transitionDuration) - .attr('r', 0) - .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) - .attr('cx', horizontalQ ? vScale(0) : scale(q2)).remove(); - - var ptsEnter = pts.enter().append('circle').attr('class', 'point').attr('r', 0) - .attr('cx', horizontalQ ? 0 : scale(q2)) - .attr('cy', horizontalQ ? scale(q2) : 0); - - pts = pts.merge(ptsEnter); - - // console.log(pointsTooltip.header()) - - var pTTips = tooltip().selection(pts).data(violinPointsExtractor(key, currentData)) - .header(pointsTooltip.header()) - .keys(pointsTooltip.keys()) - .values(pointsTooltip.values()); - - pTTips(); - - var pMin = d3.min(currentData.pointValues), pMax = d3.max(currentData.pointValues); - - - - pts.transition().duration(transitionDuration).ease(easeFunc).attr('r', pointRadius) - .attr('cy', function(pointKey, ii){ - var dd = currentData.pointValues[ii]; - if (horizontalQ) { return scale(extent[1]) - scale(dd) } - var j = whichBin(currentData.binned, dd); - var r = Math.random(); - var n = vScale(r * currentData.frequencies[j] * 0.5); - var k = Math.random() > 0.5 ? n : -n; - return k - }) - .attr('cx', function(pointKey, ii){ - var dd = currentData.pointValues[ii]; - if (horizontalQ) { - var j = whichBin(currentData.binned, dd); - var r = Math.random(); - var n = vScale(r * currentData.frequencies[j] * 0.5); - var k = Math.random() > 0.5 ? n : -n; - return k - } - return scale(dd) - }) - .attr('stroke', function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'stroke', strokeColor, pMin, pMax) }) - .attr('fill' , function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'fill' , strokeColor, pMin, pMax) }) - .attr('stroke-width', pointStrokeWidth); - - ptsContainer.selectAll('circle.point').on('mouseover', function(dd, ii){ - container.selectAll('g.'+objectClass).style('opacity', 0.2); - t.style('opacity', 1); - la.attr('stroke-width',objectStrokeWidth*2); - ra.attr('stroke-width',objectStrokeWidth*2); - - container.selectAll('.point').style('opacity', 0.2); - d3.select(this).style('opacity', 1).attr('r', pointRadius * 2).attr('stroke-width',pointStrokeWidth*2); - }); - ptsContainer.selectAll('circle.point').on('mouseout', function(dd, ii){ - var e = document.createEvent('SVGEvents'); - e.initEvent('mouseout',true,true); - area.node().dispatchEvent(e); - - container.selectAll('.point').style('opacity', 1); - d3.select(this).attr('stroke-width', pointStrokeWidth).attr('r', pointRadius); - }); - } - else { - cV.selectAll('.point') - .transition().duration(transitionDuration).ease(easeFunc) - .attr('r', 0) - .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) - .attr('cx', horizontalQ ? vScale(0) : scale(q2)) - .remove(); - } - - - }); - - - tooltip$$1.selection(container.selectAll('g:not(.to-remove).'+objectClass + ' .area')); - if (tooltip$$1.data() == undefined) {tooltip$$1.data(data);} - tooltip$$1(); - if (tooltip$$1.values() == undefined) { - tooltip$$1.values([ - function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] } - ]); - - } - - } - - return violin -} - - - - - -/** -* Produces the function which manipulates the violin data to have values needed -* for rendering the violins as svg. -* @returns {function} calculateViolinValues -* @namespace neededViolinValues -*/ -function neededViolinValues() { - var - /** - * Whether or not the orientation of the violins are horizontal - * (see {@link violin#orient}) - * @param {Object} [horizontalQ=true] - * @memberof neededViolinValues# - * @property - */ - horizontalQ = true, - /** - * Keys to be put into the quartiles if they need to be calculated. - * (see {@link violin#quartileKeys}) - * @param {Object} [quartileKeys=['Q0', 'Q1', 'Q2', 'Q3', 'Q4']] - * @memberof neededViolinValues# - * @property - */ - quartileKeys = ['Q0', 'Q1', 'Q2', 'Q3', 'Q4'], - /** - * Function which given the key of the violin and that key's associated value - * returns the object consiting of the points of the violin - * (see {@link violin#violinPointsExtractor}) - * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] - * @memberof neededViolinValues# - * @property - */ - violinPointsExtractor, - /** - * Function which given the key of the current point and the object of points for the - * violin, returns the numerical value of the point - * (see {@link violin#violinPointValueExtractor}) - * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] - * @memberof neededViolinValues# - * @property - */ - violinPointValueExtractor; - - - /** - * Gets / sets the horizontalQ - * (see {@link violin#orient}) - * @param {boolean} [_=none] - * @returns {calculateViolinValues | boolean} - * @memberof calculateViolinValues - * @property - * - * by default horizontalQ = true - */ - calculateViolinValues.horizontalQ = function(_) { return arguments.length ? (horizontalQ=_, calculateViolinValues) : horizontalQ }; - /** - * Gets / sets the quartileKeys - * (see {@link violin#quartileKeys}) - * @param {string[]} [_=none] - * @returns {calculateViolinValues | string[]} - * @memberof calculateViolinValues - * @property - * - * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] - */ - calculateViolinValues.quartileKeys = function(_) { return arguments.length ? (quartileKeys=_, calculateViolinValues) : quartileKeys }; - /** - * Gets / sets the violinPointsExtractor - * (see {@link violin#violinPointsExtractor}) - * @param {function} [_=none] - * @returns {calculateViolinValues | function} - * @memberof calculateViolinValues - * @property - * - * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } - */ - calculateViolinValues.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor=_, calculateViolinValues) : violinPointsExtractor }; - /** - * Gets / sets the violinPointValueExtractor - * (see {@link violin#violinPointValueExtractor}) - * @param {function} [_=none] - * @returns {calculateViolinValues | function} - * @memberof calculateViolinValues - * @property - * - * by default violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value } - */ - calculateViolinValues.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor=_, calculateViolinValues) : violinPointValueExtractor }; - - - /** - * The function produced by neededViolinValues. - * - * Adds the data need to render the violin as an svg - * @param {string} violinKey the key of the current violin - * @param {object} data the object consisting of violinKey - values (violin data) pairs - * @returns {none} this function manipulates the passed data object adding the following keys - * - * data[violinKey].binned // the binned values of the points - * - * data[violinKey].frequencies // the list consisting of the length of each bin - * - * data[violinKey].contour // the points depicting the contour of 1/2 of the violin - * - * data[violinKey].quartiles // the quartiles of the points' values - * - * data[violinKey].pointKeys // the keys associated with the points - * - * data[violinKey].pointValues // the numerical values of the points - * - * @memberof neededViolinValues# - * @property - */ - function calculateViolinValues(violinKey, data) { - // data for the current violin - var violinData = data[violinKey]; - // the object of points - var violinPoints = violinPointsExtractor(violinKey, violinData); - // - var violinPointsKeys = d3.keys(violinPoints); - // the numerical values of those points - var violinPointsValues = violinPointsKeys.map(function(pk, i){return violinPointValueExtractor(pk, violinPoints)}); - - // quartiles of those points - var pointQuartiles = quartiles(violinPointsValues, quartileKeys); - - // binned points - var binned = d3.histogram()(violinPointsValues); - // length of bins - var frequencies = binned.map(bin=>bin.length); - // min and max countour points for nice drawings - var minContourPoint = horizontalQ ? {x: 0, y: d3.min(violinPointsValues)} : {x: d3.min(violinPointsValues), y: 0}; - var maxContourPoint = horizontalQ ? {x: 0, y: d3.max(violinPointsValues)} : {x: d3.max(violinPointsValues), y: 0}; - var violinContourPoints = binned.map(function(bin, i) { - return horizontalQ - ? {y: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), x: frequencies[i]} - : {x: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), y: frequencies[i]} - }); - // points along which to draw the violin shpe - violinContourPoints = [minContourPoint].concat(violinContourPoints).concat([maxContourPoint]); - - // set data - violinData.binned = binned; - violinData.frequencies = frequencies; - violinData.contour = violinContourPoints; - violinData.quartiles = pointQuartiles; - violinData.pointKeys = violinPointsKeys; - violinData.pointValues = violinPointsValues; - } - - return calculateViolinValues -} - -function numericLegend( selection ) { - - var - min=0, - max=1, - spaceX, - spaceY, - colorFunction$$1 = colorFunction(), - namespace='d3sm-linear-vertical-gradient', - fontSize = 12, - backgroundFill = 'transparent', - textColor = 'black', - roundTo = 2; - - - legend.min = function(_) { return arguments.length ? (min=_, legend) : min }; - legend.max = function(_) { return arguments.length ? (max=_, legend) : max }; - legend.spaceX = function(_) { return arguments.length ? (spaceX=_, legend) : spaceX }; - legend.spaceY = function(_) { return arguments.length ? (spaceY=_, legend) : spaceY }; - legend.namespace = function(_) { return arguments.length ? (namespace=_, legend) : namespace }; - legend.fontSize = function(_) { return arguments.length ? (fontSize=_, legend) : fontSize }; - legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill=_, legend) : backgroundFill }; - legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1=_, legend) : colorFunction$$1 }; - legend.textColor = function(_) { return arguments.length ? (textColor=_, legend) : textColor }; - legend.roundTo = function(_) { return arguments.length ? (roundTo=_, legend) : roundTo }; - - function legend() { - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); - - var defs = safeSelect(selection, 'defs'); - var linearGradient = safeSelect(defs, 'linearGradient') - .attr("x1", "0%") - .attr("y1", "100%") - .attr("x2", "0%") - .attr("y2", "0%") - .attr('id', hypenate(namespace,'numerical-legend-gradient')); - - - colorFunction$$1.dataExtent([min, max]) - .colorBy('value') - .valueExtractor(function(k, v, i){return v}); - - - linearGradient.selectAll('stop') - .data( colorFunction$$1.colors() ) - .enter() - .append('stop') - .attr("offset", function(d, i){ return i / (colorFunction$$1.colors().length - 1) }) - .attr('stop-color', function(d) {return d}); - - - - - var rect = safeSelect(container, 'rect', 'legend') - .attr('transform', 'translate(0,'+fontSize+')') - .style("fill", "url(#"+hypenate(namespace,'numerical-legend-gradient')+")") - .attr('x', 0) - .attr('y', 0) - .attr('width', spaceX) - .attr('height', spaceY - fontSize*2) - .on('mousemove', function(d, i){legendMousemove(d, i, rect, d3.select(this));}) - .on('mouseout', function(d, i){ d3.select("#"+hypenate(namespace,'legend-tooltip')).remove(); }); - - var minText = safeSelect(container, 'text', 'min') - .text(round(min, 2)) - .attr('text-anchor', 'middle') - .attr("font-size", fontSize+'px') - .attr('transform', function(d, i){ - var - x = spaceX / 2, - y = spaceY - fontSize / 4, - t = 'translate('+x+','+y+')'; - return t - }); - - var maxText = safeSelect(container, 'text', 'max') - .text(round(max, 2)) - .attr('text-anchor', 'middle') - .attr("font-size", fontSize+'px') - .attr('transform', function(d, i){ - var - x = spaceX / 2, - y = fontSize, - t = 'translate('+x+','+y+')'; - return t - }); - - - - - } - - function legendMousemove(d, i, rect, t) { - var s = d3.scaleLinear() - .domain([0, rect.attr('height')]) - .range([max, min]); - var m = d3.mouse(rect.node()); - var v = round(s(m[1]),roundTo); - - var strokeColor = colorFunction$$1(undefined, v, undefined, 'stroke'); - var fillColor = colorFunction$$1(undefined, v, undefined, 'fill'); - - var div = safeSelect(d3.select('body'), 'div', hypenate(namespace,'legend-tooltip')) - .attr('id', hypenate(namespace,'legend-tooltip')) - .style('position', 'absolute') - .style('left', (d3.event.pageX+15)+'px') - .style('top', (d3.event.pageY+15)+'px') - .style('background-color', fillColor) - .style('border-color', strokeColor) - - .style('min-width', (fontSize * (String(max).split('.')[0].length+3))+'px') - .style('min-height', (fontSize * (String(max).split('.')[0].length+3))+'px') - .style('border-radius', '50%') - .style('border-radius', '5000px') - - .style('display', 'flex') - .style('justify-content', 'center') - .style('text-align', 'middle') - .style('padding', 2+"px") - - .style('border-style', 'solid') - .style('border-width', 2); - - var text = safeSelect(div, 'div') - .text(v) - .style('color', textColor) - .style('align-self', 'center'); - } - - return legend -} - -function categoricLegend( selection ) { - var - categories, - - /** - * Data to plot. Assumed to be a object, where each key corresponds to a legend - * (see {@link legend#data}) - * @param {Object} [data=undefined] - * @memberof legend# - * @property - */ - data, - /** - * Which direction to render the bars in - * (see {@link legend#orient}) - * @param {number} [orient='horizontal'] - * @memberof legend# - * @property - */ - orient='horizontal', - /** - * Amount of horizontal space (in pixels) avaible to render the legend in - * (see {@link legend#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof legend# - * @property - */ - spaceX, - /** - * Amount of vertical space (in pixels) avaible to render the legend in - * (see {@link legend.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof legend# - * @property - */ - spaceY, - - /** - * Whether or not to allow legend to render elements pass the main spatial dimension - * given the orientation (see {@link legend#orient}), where {@link legend#orient}="horizontal" - * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" - * the main dimension is {@link legend#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof legend# - * @property - */ - overflowQ = false, - - /** - * An array - putatively of other arrays - depicting how bars should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof legend# - * @property - */ - grouping, - - /** - * How to get the value of the legend - * @param {function} [valueExtractor=function(key, index) { return data[key] }] - * @memberof legend# - * @property - */ - valueExtractor = function(key, index) { return data[key] }, - /** - * How to sort the bars - if {@link bar#grouping} is not provided. - * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] - * @memberof bar# - * @property - */ - sortingFunction = function(keyA, keyB) {return d3.ascending(keyA, keyB)}, - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link legend#orient}), where {@link legend#orient}="horizontal" - * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" - * the main dimension is {@link legend#spaceY} between bars - * @param {number} [objectSpacer=0.05] - * @memberof legend# - * @property - */ - objectSpacer = 0.05, - /** - * The minimum size that an object can be - * @param {number} [minObjectSize=50] - * @memberof legend# - * @property - */ - minObjectSize = 10, - /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof legend# - * @property - */ - maxObjectSize = 100, - - /** - * The stroke width of the bars - * @param {number} [barStrokeWidth=2] - * @memberof legend# - * @property - */ - bubbleStrokeWidth = 2, - /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof legend# - * @property - */ - colorFunction$$1 = colorFunction(), - - /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof legend# - * @property - */ - backgroundFill = 'transparent', - /** - * Namespace for all items made by this instance of legend - * @param {string} [namespace="d3sm-legend"] - * @memberof legend# - * @property - */ - namespace = 'd3sm-legend', - /** - * Class name for legend container ( element) - * @param {string} [objectClass="legend"] - * @memberof legend# - * @property - */ - objectClass = 'legend', - - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof legend# - * @property - */ - transitionDuration = 1000, - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof legend# - * @property - */ - easeFunc = d3.easeExp; - - - legend.categories = function(_) { return arguments.length ? (categories=_, legend) : categories }; - - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {legend | d3.selection} - * @memberof legend - * @property - * by default selection = selection - */ - legend.selection = function(_) { return arguments.length ? (selection = _, legend) : selection; }; - /** - * Gets or sets the data - * (see {@link legend#data}) - * @param {number} [_=none] - * @returns {legend | object} - * @memberof legend - * @property - */ - legend.data = function(_) { return arguments.length ? (data = _, legend) : data; }; - /** - * Gets or sets the orient of the bars - * (see {@link legend#orient}) - * @param {number} [_=none] - * @returns {legend | object} - * @memberof legend - * @property - */ - legend.orient = function(_) { return arguments.length ? (orient = _, legend) : orient; }; - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link legend#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {legend | number} - * @memberof legend - * @property - * by default spaceX = undefined - */ - legend.spaceX = function(_) { return arguments.length ? (spaceX = _, legend) : spaceX; }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link legend#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {legend | number} - * @memberof legend - * @property - * by default spaceY = undefined - */ - legend.spaceY = function(_) { return arguments.length ? (spaceY = _, legend) : spaceY; }; - - /** - * Gets / sets whether or not legend is allowed to go beyond specified dimensions - * (see {@link legend#spaceX}) - * @param {boolean} [_=none] - * @returns {legend | boolean} - * @memberof legend - * @property - * by default overflowQ = false - */ - legend.overflowQ = function(_) { return arguments.length ? (overflowQ = _, legend) : overflowQ; }; - /** - * Gets / sets the grouping of the bars - * (see {@link legend#grouping}) - * @param {Array[]} [_=none] - * @returns {legend | Array[]} - * @memberof legend - * @property - * by default grouping = undefined - */ - legend.grouping = function(_) { return arguments.length ? (grouping = _, legend) : grouping; }; - /** - * Gets / sets the valueExtractor - * (see {@link legend#valueExtractor}) - * @param {function} [_=none] - * @returns {legend | function} - * @memberof legend - * @property - * by default valueExtractor = function(key, index) { return data[key] }, - */ - legend.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, legend) : valueExtractor; }; - /** - * Gets / sets the sortingFunction - * (see {@link bar#sortingFunction}) - * @param {function} [_=none] - * @returns {bar | function} - * @memberof bar - * @property - * by default sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, - */ - legend.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, legend) : sortingFunction; }; - /** - * Gets / sets objectSpacer - * (see {@link legend#objectSpacer}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default objectSpacer = 0.05 - */ - legend.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, legend) : objectSpacer; }; - /** - * Gets / sets the minObjectSize - * (see {@link legend#minObjectSize}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default minObjectSize = 50 - */ - legend.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, legend) : minObjectSize; }; - /** - * Gets / sets the maxObjectSize - * (see {@link legend#maxObjectSize}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default maxObjectSize = 100 - */ - legend.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, legend) : maxObjectSize; }; - - /** - * Gets / sets the barStrokeWidth - * (see {@link legend#barStrokeWidth}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default barStrokeWidth = 2 - */ - legend.bubbleStrokeWidth = function(_) { return arguments.length ? (bubbleStrokeWidth = _, legend) : bubbleStrokeWidth; }; - /** - * Gets / sets the colorFunction - * (see {@link legend#colorFunction}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default colorFunction = colorFunction() - */ - legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, legend) : colorFunction$$1; }; - - /** - * Gets / sets the backgroundFill - * (see {@link legend#backgroundFill}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default backgroundFill = 'transparent' - */ - legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, legend) : backgroundFill; }; - /** - * Gets / sets the namespace - * (see {@link legend#namespace}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default namespace = 'd3sm-legend' - */ - legend.namespace = function(_) { return arguments.length ? (namespace = _, legend) : namespace; }; - /** - * Gets / sets the objectClass - * (see {@link legend#objectClass}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default objectClass = 'tick-group' - */ - legend.objectClass = function(_) { return arguments.length ? (objectClass = _, legend) : objectClass; }; - /** - * Gets / sets the transitionDuration - * (see {@link legend#transitionDuration}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default transitionDuration = 1000 - */ - legend.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, legend) : transitionDuration; }; - /** - * Gets / sets the easeFunc - * (see {@link legend#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {legend | d3.ease} - * @memberof legend - * @property - * by default easeFunc = d3.easeExp - */ - legend.easeFunc = function(_) { return arguments.length ? (easeFunc = _, legend) : easeFunc; }; - - - function legend() { - var horizontalQ = (orient == 'horizontal') ? true : false; - var verticalQ = !horizontalQ; - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); - - - colorFunction$$1.dataExtent([0, categories.length - 1]) - .colorBy('categories') - .categoryExtractor(function(k, v, i){return v}); - - var r = Math.min(spaceX, spaceY) / 2; - var numberOfObjects = categories.length; - - // if grouping is undefined sort barKeys by sortingFunction - var ordered = (grouping == undefined) ? categories.sort(sortingFunction) : grouping; - // ordered might be nested depending on grouping - var catKeys = flatten(ordered); - - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - var objectSize = calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - // calculate spacer size if needed - var spacerSize = calculateWidthOfSpacer(catKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); - // make the nested groups - var spacerFunction = groupingSpacer() - .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) - .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc) - .namespace(namespace); - - spacerFunction(container, ordered, 0); - var r = Math.min(objectSize, spaceX, spaceY) / 2 - bubbleStrokeWidth; - - container.selectAll('g:not(.to-remove).'+objectClass).each(function(cat, i) { - var t = d3.select(this); - var c = safeSelect(t, 'circle'); - var fillColor = colorFunction$$1(undefined, cat, i, 'fill'), // prevent duplicate computation - strokeColor = colorFunction$$1(undefined, cat, i, 'stroke'); - - var cx = horizontalQ - ? r+bubbleStrokeWidth - : (spaceX - r*2) / 2 + r; - var cy = verticalQ - ? r+bubbleStrokeWidth - : (spaceX - r*2) / 2 + r; - - c.attr("r", r) - .attr('cx', cx) - .attr('cy', cy) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', bubbleStrokeWidth); - - var text = safeSelect(t, 'text'); - text.text(cat) - .attr('text-anchor', 'middle') - .attr("transform", function(d, i){ - var - x = cx, - y = cy + text.node().getBoundingClientRect().height / 4, - t = 'translate('+x+','+y+')'; - return t - }); - - }); - - - } - - return legend -} - -// import * as d3 from "d3"; -/******************************************************************************* -** ** -** ** -** D3 EXTENSIONS ** -** ** -** ** -*******************************************************************************/ -/** -* Recursively ascends parents of selection until it finds an svg tag -* @function d3.selection.thisSVG -* @augments d3.selection -* @returns {Element} which is the svg tag, not the d3 selection of that tag -*/ -d3.selection.prototype.thisSVG = function() { return getContainingSVG(this.node()); }; - - -/** -* Helper for getting absolute position of the mouse -* @function d3.mouse.absolute -* @augments d3.mouse -* @returns {number[]} [x, y] as they relate to `html` not to local scope. -*/ -d3.mouse.absolute = function() { - var html = d3.select('html').node(); - var [x, y] = this(html); - return [x, y] -}; - - -/** -* Gets position of the selection in relation to the containing svg -* @see{@link getContainingSVG} -* @function d3.selection.absolutePosition -* @augments d3.selection -* @returns {Object} with structure similar to getBoundingClientRect, e.g. -* top, left, bottom, right, height, width -*/ -d3.selection.prototype.absolutePosition = function() { - var element = this.node(); - var elementPosition = element.getBoundingClientRect(); - var containerSVG = getContainingSVG(element); - var svgPosition = containerSVG.getBoundingClientRect(); - - return { - top: elementPosition.top - svgPosition.top, - left: elementPosition.left - svgPosition.left, - bottom: elementPosition.bottom - svgPosition.top, - right: elementPosition.right - svgPosition.left, - height: elementPosition.height, - width: elementPosition.width - }; - -}; - - -d3.selection.prototype.relativePositionTo = function(container) { - var element = this.node(); - var elementPosition = element.getBoundingClientRect(); - var containerSVG = container; - var svgPosition = containerSVG.getBoundingClientRect(); - - return { - top: elementPosition.top - svgPosition.top, - left: elementPosition.left - svgPosition.left, - bottom: elementPosition.bottom - svgPosition.top, - right: elementPosition.right - svgPosition.left, - height: elementPosition.height, - width: elementPosition.width - }; - -}; - -function getTranslation$1(selection){ - var transform = selection.attr('transform'); - var [junk, xy] =transform.split('translate('); - var [x, y] = xy.split(','); - junk = y.split(')'); - return [parseFloat(x), parseFloat(y)] -} - -function lasso( selection ) { - var - svg, // svg that is target of events - objectContainer, // container which houses objects we are selecting (allows for transform to be applied to lasso) - objectClass, // class of object we are selecting - namespace="d3sm-lasso", - chartContainer, - chartOffset, - objectsOffset, - eventCatcher, - - xScale, // optional scale for the lasso currentPoints - yScale, // optional scale for the lasso currentPoints - - activeQ = false, // whether or not lasso is active - - currentPoints=[], // mouse points for current lasso - allPoints=[], // list of lists for all points of lassos - - line = d3.line() - .x(function(d, i){ - var x; - if (xScale != undefined) { x = xScale(d[0]); } - else {x = d[0];} - return x //- chartOffset[0]// - objectsOffset[0] - }) - .y(function(d, i){ - var y; - if (yScale != undefined) { y= yScale(d[1]); } - else {y = d[1];} - return y// - chartOffset[1]// - objectsOffset[1] - }) - .curve(d3.curveLinearClosed), - - instance=0, // an indentifier for which instance this lasso is under the current svg - - tickDistance = 10, - - // styles for lasso path - color = '#17a2b8', - animationRate = '10s', - opacity=0.3, - dashArray = '5, 10', - stroke = 'black', - strokeWidth=2, - - // styles for lassoed objects - lassoedFill = "white", - lassoedStroke = 'black', - lassoedStrokeWidth = 3, - - transitionDuration = 1000, - easeFunc = d3.easeExp; - - var path; - - lasso.svg = function(_) { return arguments.length ? (svg = _, lasso) : svg; }; - lasso.chartContainer = function(_) { return arguments.length ? (chartContainer = _, lasso) : chartContainer; }; - lasso.objectContainer = function(_) { return arguments.length ? (objectContainer = _, lasso) : objectContainer; }; - lasso.objectClass = function(_) { return arguments.length ? (objectClass = _, lasso) : objectClass; }; - lasso.namespace = function(_) { return arguments.length ? (namespace = _, lasso) : namespace; }; - lasso.xScale = function(_) { return arguments.length ? (xScale = _, lasso) : xScale; }; - lasso.yScale = function(_) { return arguments.length ? (yScale = _, lasso) : yScale; }; - lasso.activeQ = function(_) { return arguments.length ? (activeQ = _, lasso) : activeQ; }; - lasso.currentPoints = function(_) { return arguments.length ? (currentPoints = _, lasso) : currentPoints; }; - lasso.allPoints = function(_) { return arguments.length ? (allPoints = _, lasso) : allPoints; }; - lasso.instance = function(_) { return arguments.length ? (instance = _, lasso) : instance; }; - lasso.tickDistance = function(_) { return arguments.length ? (tickDistance = _, lasso) : tickDistance; }; - lasso.color = function(_) { return arguments.length ? (color = _, lasso) : color; }; - lasso.animationRate = function(_) { return arguments.length ? (animationRate = _, lasso) : animationRate; }; - lasso.opacity = function(_) { return arguments.length ? (opacity = _, lasso) : opacity; }; - lasso.dashArray = function(_) { return arguments.length ? (dashArray = _, lasso) : dashArray; }; - lasso.stroke = function(_) { return arguments.length ? (stroke = _, lasso) : stroke; }; - lasso.lassoedFill = function(_) { return arguments.length ? (lassoedFill = _, lasso) : lassoedFill; }; - lasso.lassoedStroke = function(_) { return arguments.length ? (lassoedStroke = _, lasso) : lassoedStroke; }; - lasso.lassoedStrokeWidth = function(_) { return arguments.length ? (lassoedStrokeWidth = _, lasso) : lassoedStrokeWidth; }; - lasso.eventCatcher = function(_) { return arguments.length ? (eventCatcher = _, lasso) : eventCatcher; }; - - lasso.drag = drag; - lasso.draw = draw; - lasso.tick = tick; - lasso.detect = detect; - lasso.toggle = toggle; - lasso.remove = remove; - lasso.render = render; - lasso.keyFrames = keyFrames; - lasso.updateObjects = updateObjects; - lasso.applyPathAttributes = applyPathAttributes; - lasso.applyObjectAttributes = applyObjectAttributes; - - keyFrames(); - - function lasso() { - // add a dash animation if needed - if (activeQ) { transitionDraw(); } - } - - function toggle(state) { - // use optional param to set state, otherwise toggle state - activeQ = (state!=undefined) ? state : !activeQ; - chartOffset = getTranslation$1(chartContainer); - objectsOffset = getTranslation$1(objectContainer); - - if (activeQ) { - svg.node().addEventListener('mousedown', render, true); - } else { - svg.node().removeEventListener('mousedown', render, true); - remove(); - } - - } - - function draw() { - chartOffset = getTranslation$1(chartContainer); - objectsOffset = getTranslation$1(objectContainer); - - var container = safeSelect(objectContainer, 'g', 'lasso-container'); - var paths = container.selectAll('path[instance="'+instance+'"]'); - - // update - paths = paths.data(allPoints); - - // remove excess - var pExit = paths.exit().remove(); - // add needed paths - var pEnter = paths.enter().append('path'); - - // merge - paths = paths.merge(pEnter); - - // apply - applyPathAttributes(paths); - } - - function remove() { - var container = safeSelect(objectContainer, 'g', 'lasso-container'); - var paths = container.selectAll('path[instance="'+instance+'"]').remove(); - container.remove(); - objectContainer.selectAll(objectClass).classed("in-lasso", false); - updateObjects(); - } - - function render( event ) { - // nothing can interefer with drawing the lasso - event.preventDefault(); event.stopPropagation(); - - var container = safeSelect(objectContainer, 'g', 'lasso-container'); - - /* - each time the user presses down, while the state is active, the lasso - the lasso should make a seperate segment. - */ - currentPoints = []; - - svg.node().addEventListener('mousemove', drag); - svg.node().addEventListener('mouseup', function(event) { - svg.node().removeEventListener('mousemove', drag); - allPoints.push(currentPoints); - // BUG: somehow this is pushing currentPoints n times where n is the nth lasso path for the current instance - // NOTE: allPoints = unique(allPoints) is a temporary and inefficient fix - allPoints = unique(allPoints); - }); - - path = container.append('path').data([currentPoints]); - applyPathAttributes(path); - } - - function transitionDraw() { - var container = safeSelect(objectContainer, 'g', 'lasso-container'); - var paths = container.selectAll('path[instance="'+instance+'"]'); - - // update - paths = paths.data(allPoints); - - // remove excess - var pExit = paths.exit().remove(); - // add needed paths - var pEnter = paths.enter().append('path'); - - // merge - paths = paths.merge(pEnter) - .transition().duration(transitionDuration) - .ease(easeFunc); - applyPathAttributes(paths); - - } - - function applyPathAttributes(path) { - path - .attr("class", hypenate(namespace, "lasso-path")) - .style('opacity', opacity) - .attr('fill', color) - .attr("d", line) - .attr('instance', instance) - .style("stroke-dasharray", dashArray) - .attr("stroke", stroke) - .attr("stroke-width", strokeWidth) - .style('animation', 'lassoDash '+animationRate+' linear') - .style("animation-iteration-count", "infinite"); - } - - function drag(event) { - /* - effectively create a mouse down and move event (which normally is inteperated - as 'drag' by the browser) by dynamically adding / removing this event on - mouse down / mouse up. - */ - - if (eventCatcher != undefined) {eventCatcher.dispatch(hypenate(namespace,"drag"));} - // d3.dispatch(hypenate(namespace,"drag")) - - if (event.which != 1) {return} // ensures left mouse button set - d3.event = event; - var pt = d3.mouse(objectContainer.node()); - var pt = d3.mouse(svg.node()); - - if (xScale != undefined) {pt[0] = xScale.invert(pt[0]);} - if (yScale != undefined) {pt[1] = yScale.invert(pt[1]);} - pt[0] = pt[0] - chartOffset[0] - objectsOffset[0]; - pt[1] = pt[1] - chartOffset[1] - objectsOffset[1]; - - /* if we have a point already, test if it passes a minimum distance to prevent overwhelming with too many tick functions */ - if (currentPoints.length) { - var lastPt = currentPoints[currentPoints.length - 1]; - var a = [pt[0], pt[1]], b = [lastPt[0], lastPt[1]]; - - if (xScale) {b[0] = xScale(b[0]); a[0] = xScale(a[0]);} - if (yScale) {b[1] = yScale(b[1]); a[1] = yScale(a[1]);} - - var dist = euclideanDistance(b, a); - if (dist > tickDistance) { tick(pt); } - } - else { tick(pt); } - } + legend.namespace = function(_) { return arguments.length ? (namespace = _, legend) : namespace; }; + /** + * Gets / sets the objectClass + * (see {@link legend#objectClass}) + * @param {string} [_=none] + * @returns {legend | string} + * @memberof legend + * @property + * by default objectClass = 'tick-group' + */ + legend.objectClass = function(_) { return arguments.length ? (objectClass = _, legend) : objectClass; }; + /** + * Gets / sets the transitionDuration + * (see {@link legend#transitionDuration}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default transitionDuration = 1000 + */ + legend.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, legend) : transitionDuration; }; + /** + * Gets / sets the easeFunc + * (see {@link legend#easeFunc}) + * @param {d3.ease} [_=none] + * @returns {legend | d3.ease} + * @memberof legend + * @property + * by default easeFunc = d3.easeExp + */ + legend.easeFunc = function(_) { return arguments.length ? (easeFunc = _, legend) : easeFunc; }; - function tick (pt) { - /* - If a point is provided update data and objects. - Otherwise just call on data we already have. + function legend() { + var horizontalQ = (orient == 'horizontal') ? true : false; + var verticalQ = !horizontalQ; + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - Why like this?: - 1. currentPoints is current points to allow disjunct lassos, currentPoints is only pushed to - allPoints after mouseup. - 2. to allow render of objects in the lasso class / updating the data list - just by toggling the button - */ - if (pt != undefined) { - currentPoints.push(pt); - path.attr("d", line); - if (currentPoints.length < 3) {return} // need at least 3 points to detect anything. - detect(allPoints.concat([currentPoints])); - } else { - detect(allPoints); - } - } + colorFunction$$1.dataExtent([0, categories.length - 1]) + .colorBy('categories') + .categoryExtractor(function(k, v, i){return v}); + var r = Math.min(spaceX, spaceY) / 2; + var numberOfObjects = categories.length; - function detect(lassos) { - if (lassos == undefined) {lassos = allPoints;} - objectContainer.selectAll(objectClass).each(function(d, i){ - var current = d3.select(this), + // if grouping is undefined sort barKeys by sortingFunction + var ordered = (grouping == undefined) ? categories.sort(sortingFunction) : grouping; + // ordered might be nested depending on grouping + var catKeys = utils$1.arr.flatten(ordered); - box = current.absolutePosition(), - // box = current.relativePositionTo(objectContainer.node()), + var space = horizontalQ ? spaceX : spaceY; + // calculate object size + var objectSize = utils$1.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + // calculate spacer size if needed + var spacerSize = utils$1.math.calculateWidthOfSpacer(catKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); + // make the nested groups + var spacerFunction = groupingSpacer() + .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) + .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc) + .namespace(namespace); - boxPts = [ - [ - box.left - chartOffset[0] - objectsOffset[0], - box.top - chartOffset[1] - objectsOffset[1] - ], - [ - box.right - chartOffset[0] - objectsOffset[0], - box.top - chartOffset[1] - objectsOffset[1] - ], - [ - box.left - chartOffset[0] - objectsOffset[0], - box.bottom - chartOffset[1] - objectsOffset[1] - ], - [ - box.right - chartOffset[0] - objectsOffset[0], - box.bottom - chartOffset[1] - objectsOffset[1] - ] - ]; + spacerFunction(container, ordered, 0); + var r = Math.min(objectSize, spaceX, spaceY) / 2 - bubbleStrokeWidth; - if (xScale != undefined) { - boxPts[0][0] = xScale.invert(boxPts[0][0]); - boxPts[1][0] = xScale.invert(boxPts[1][0]); - boxPts[2][0] = xScale.invert(boxPts[2][0]); - boxPts[3][0] = xScale.invert(boxPts[3][0]); - } - if (yScale != undefined) { - boxPts[0][1] = yScale.invert(boxPts[0][1]); - boxPts[1][1] = yScale.invert(boxPts[1][1]); - boxPts[2][1] = yScale.invert(boxPts[2][1]); - boxPts[3][1] = yScale.invert(boxPts[3][1]); - } + container.selectAll('g:not(.to-remove).'+objectClass).each(function(cat, i) { + var t = d3.select(this); + var c = utils$1.sel.safeSelect(t, 'circle'); + var fillColor = colorFunction$$1(undefined, cat, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(undefined, cat, i, 'stroke'); + var cx = horizontalQ + ? r+bubbleStrokeWidth + : (spaceX - r*2) / 2 + r; + var cy = verticalQ + ? r+bubbleStrokeWidth + : (spaceX - r*2) / 2 + r; - /* - flag needed as we have to test multiple lasso segments, and if the point - is not in one segment, it does not mean it is not in any - */ - var inAnyLassoQ = false; - for (var i = 0; i < lassos.length; i++) { - var lassoPoints = lassos[i]; - // .map(function(pt){ - // var x, y = pt - // if (xScale!=undefined) {x = xScale(x)} - // if (yScale!=undefined) {y = yScale(y)} - // return [x, y] - // }) - var boxInLassoQ = boxPts.every(coord => d3.polygonContains(lassoPoints, coord)); + c.attr("r", r) + .attr('cx', cx) + .attr('cy', cy) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', bubbleStrokeWidth); - if (boxInLassoQ) { inAnyLassoQ = true; } // only update flag in the positive case. - } + var text = utils$1.sel.safeSelect(t, 'text'); + text.text(cat) + .attr('text-anchor', 'middle') + .attr("transform", function(d, i){ + var + x = cx, + y = cy + text.node().getBoundingClientRect().height / 4, + t = 'translate('+x+','+y+')'; + return t + }); - current.classed('in-lasso', inAnyLassoQ); - current.classed('in-lasso-'+instance, inAnyLassoQ); }); - updateObjects(); - return objectContainer.selectAll('.in-lasso-'+instance) - } + } + return legend +} - function updateObjects() { - objectContainer.selectAll(objectClass).each(function(d, i) { - var t = d3.select(this); - applyObjectAttributes(t, t.classed('in-lasso')); - }); - } +function numericLegend( selection ) { - function applyObjectAttributes(obj, setQ) { - var - preLassoFill = obj.attr('_pre_lasso_fill'), - preLassoStroke = obj.attr('_pre_lasso_stroke'), - preLassoStrokeWidth = obj.attr('_pre_lasso_stroke-width'); + var + min=0, + max=1, + spaceX, + spaceY, + colorFunction$$1 = colorFunction(), + namespace='d3sm-linear-vertical-gradient', + fontSize = 12, + backgroundFill = 'transparent', + textColor = 'black', + roundTo = 2; - if (setQ) { - obj.classed("in-lasso", true); - obj.classed('in-lasso-'+instance, true); - if (preLassoFill == undefined) { obj.attr('_pre_lasso_fill', obj.attr('fill')); } - if (preLassoStroke == undefined) { obj.attr('_pre_lasso_stroke', obj.attr('stroke')); } - if (preLassoStrokeWidth == undefined) { obj.attr('_pre_lasso_stroke-width', obj.attr('stroke-width')); } - obj - //BUG: when .raise() - .attr('fill', lassoedFill) - .attr('stroke', lassoedStroke) - .attr('stoke-width', lassoedStrokeWidth); + legend.min = function(_) { return arguments.length ? (min=_, legend) : min }; + legend.max = function(_) { return arguments.length ? (max=_, legend) : max }; + legend.spaceX = function(_) { return arguments.length ? (spaceX=_, legend) : spaceX }; + legend.spaceY = function(_) { return arguments.length ? (spaceY=_, legend) : spaceY }; + legend.namespace = function(_) { return arguments.length ? (namespace=_, legend) : namespace }; + legend.fontSize = function(_) { return arguments.length ? (fontSize=_, legend) : fontSize }; + legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill=_, legend) : backgroundFill }; + legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1=_, legend) : colorFunction$$1 }; + legend.textColor = function(_) { return arguments.length ? (textColor=_, legend) : textColor }; + legend.roundTo = function(_) { return arguments.length ? (roundTo=_, legend) : roundTo }; - } else { - obj.classed("in-lasso", false); - obj.classed('in-lasso-'+instance, false); - if (preLassoFill != undefined) { obj.attr('fill', preLassoFill); } - if (preLassoStroke != undefined) { obj.attr('stroke', preLassoStroke); } - if (preLassoStrokeWidth != undefined) { obj.attr('stroke-width', preLassoStrokeWidth); } - } - } + function legend() { + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - function keyFrames() { - var style = - d3.select("html").select('style.'+hypenate(namespace,"lasso-dash")); - if (style.empty()) { - d3.select("html").append('style') - .classed(hypenate(namespace,"lasso-dash"), true) - .html("@keyframes lassoDash {to { stroke-dashoffset: 1000;}}"); - } + var defs = utils$1.sel.safeSelect(selection, 'defs'); + var linearGradient = utils$1.sel.safeSelect(defs, 'linearGradient') + .attr("x1", "0%") + .attr("y1", "100%") + .attr("x2", "0%") + .attr("y2", "0%") + .attr('id', utils$1.str.hypenate(namespace,'numerical-legend-gradient')); - } - return lasso -} -function lassoWidget( selection ) { - var - namespace = 'd3sm-lasso', - selection = selection, - svg, - chartContainer, - objectContainer, - objectClass, - lassoLine = d3.line() - .x(function(d, i){return d[0]}) - .y(function(d, i){return d[1]}) - .curve(d3.curveLinearClosed), - colorFunction$$1 = colorFunction(), - maxNumberOfGroups, - dataExtractor, - xScale, - yScale; + colorFunction$$1.dataExtent([min, max]) + .colorBy('value') + .valueExtractor(function(k, v, i){return v}); - function onError(msg, style){ - console.log(msg, style); - } - // setup the container - setupDataselectContainer(); - - lassoWidget.objectClass = function(_){return arguments.length ? (objectClass='.'+_.replace('.',''), lassoWidget) : objectClass}; - lassoWidget.svg = function(_){return arguments.length ? (svg=_, lassoWidget) : svg}; - lassoWidget.submit = function(_){return arguments.length ? (submit=_, lassoWidget) : submit}; - lassoWidget.maxNumberOfGroups = function(_){return arguments.length ? (maxNumberOfGroups=_, lassoWidget) : maxNumberOfGroups}; - lassoWidget.onError = function(_){return arguments.length ? (onError=_, lassoWidget) : onError}; - lassoWidget.objectContainer = function(_){return arguments.length ? (objectContainer=_, lassoWidget) : objectContainer}; - lassoWidget.chartContainer = function(_){return arguments.length ? (chartContainer=_, lassoWidget) : chartContainer}; - lassoWidget.dataExtractor = function(_){return arguments.length ? (dataExtractor=_, lassoWidget) : dataExtractor}; - lassoWidget.xScale = function(_){return arguments.length ? (xScale=_, lassoWidget) : xScale}; - lassoWidget.yScale = function(_){return arguments.length ? (yScale=_, lassoWidget) : yScale}; - - function styles() { - var s = d3.select("html").select("style."+namespace+'lasso-widget'); - if (s.empty()) { - d3.select("html").append("style") - .classed(namespace+'lasso-widget', true) - .html( - "."+hypenate(namespace, "data-table") + "{\ - height:100px;\ - overflow:auto;\ - }" - ); - } - } + linearGradient.selectAll('stop') + .data( colorFunction$$1.colors() ) + .enter() + .append('stop') + .attr("offset", function(d, i){ return i / (colorFunction$$1.colors().length - 1) }) + .attr('stop-color', function(d) {return d}); - function lassoWidget() { - styles(); - } - function submit(data) { - console.log(data); - } - function plusTab(tabList) { - var tab = safeSelect(tabList, 'li', hypenate(namespace, 'plus-tab')) - .classed('ml-auto', true) - .classed('nav-item', true) - .on('click', makeNewGroup), + var rect = utils$1.sel.safeSelect(container, 'rect', 'legend') + .attr('transform', 'translate(0,'+fontSize+')') + .style("fill", "url(#"+utils$1.str.hypenate(namespace,'numerical-legend-gradient')+")") + .attr('x', 0) + .attr('y', 0) + .attr('width', spaceX) + .attr('height', spaceY - fontSize*2) + .on('mousemove', function(d, i){legendMousemove(d, i, rect, d3.select(this));}) + .on('mouseout', function(d, i){ d3.select("#"+utils$1.str.hypenate(namespace,'legend-tooltip')).remove(); }); - anchor = safeSelect(tab, 'a', 'nav-link'), - icon = safeSelect(anchor, 'i', 'fa') - .classed('fa-plus fa-2x text-success', true); - } + var minText = utils$1.sel.safeSelect(container, 'text', 'min') + .text(utils$1.math.round(min, 2)) + .attr('text-anchor', 'middle') + .attr("font-size", fontSize+'px') + .attr('transform', function(d, i){ + var + x = spaceX / 2, + y = spaceY - fontSize / 4, + t = 'translate('+x+','+y+')'; + return t + }); - function sendTab(tabList) { - var tab = safeSelect(tabList, 'li', hypenate(namespace, 'send-tab')) - .classed('ml-auto', true) - .classed('nav-item', true) - .on('click', clickSend) - , + var maxText = utils$1.sel.safeSelect(container, 'text', 'max') + .text(utils$1.math.round(max, 2)) + .attr('text-anchor', 'middle') + .attr("font-size", fontSize+'px') + .attr('transform', function(d, i){ + var + x = spaceX / 2, + y = fontSize, + t = 'translate('+x+','+y+')'; + return t + }); - anchor = safeSelect(tab, 'a', 'nav-link'), - icon = safeSelect(anchor, 'i', 'fa') - .classed('fa-paper-plane-o fa-2x text-primary', true); - } - function clickSend() { - var data = gatherDataLists(); - submit(data); - } - function closeTab(tabList) { - var tab = safeSelect(tabList, 'li', hypenate(namespace, 'close-tab')) - .classed('ml-auto', true) - .classed('nav-item', true) - .on('click', function(){ - var m = d3.mouse(d3.select("html").node()); - selection.select('div.card').style("position", 'relative') - .transition().duration(1000) - .ease(d3.easeBack) - .style('left', window.outerWidth +'px') - .remove(); - }) - , - anchor = safeSelect(tab, 'a', 'nav-link'), - icon = safeSelect(anchor, 'i', 'fa') - .classed('fa-window-close-o fa-2x text-danger', true); } + function legendMousemove(d, i, rect, t) { + var s = d3.scaleLinear() + .domain([0, rect.attr('height')]) + .range([max, min]); + var m = d3.mouse(rect.node()); + var v = utils$1.math.round(s(m[1]),roundTo); + var strokeColor = colorFunction$$1(undefined, v, undefined, 'stroke'); + var fillColor = colorFunction$$1(undefined, v, undefined, 'fill'); - function setupDataselectContainer() { - var - card = safeSelect(selection, 'div', 'card'), - cardHeader = safeSelect(card, 'div', 'card-header'), - - tabList = safeSelect(cardHeader, 'ul', 'nav') - .classed('nav-tabs card-header-tabs', true) - .classed(hypenate(namespace, 'tab-list'), true) - .attr('role', 'tablist'), - - cardBody = safeSelect(card, 'div', 'card-body'), - tabContent = safeSelect(cardBody, 'div', 'tab-content') - .classed(hypenate(namespace, 'tab-content'), true), + var div = utils$1.sel.safeSelect(d3.select('body'), 'div', utils$1.str.hypenate(namespace,'legend-tooltip')) + .attr('id', utils$1.str.hypenate(namespace,'legend-tooltip')) + .style('position', 'absolute') + .style('left', (d3.event.pageX+15)+'px') + .style('top', (d3.event.pageY+15)+'px') + .style('background-color', fillColor) + .style('border-color', strokeColor) - // leftTabs = safeSelect(tabList, 'div', 'nav mr-auto left-aligned-tabs') - rightTabs = safeSelect(tabList, 'div', 'right-aligned-tabs').classed('nav ml-auto ', true); + .style('min-width', (fontSize * (String(max).split('.')[0].length+3))+'px') + .style('min-height', (fontSize * (String(max).split('.')[0].length+3))+'px') + .style('border-radius', '50%') + .style('border-radius', '5000px') - plusTab(rightTabs); - sendTab(rightTabs); - closeTab(rightTabs); - var - defaultTab = safeSelect(tabContent, 'div', 'tab-pane') - .classed(hypenate(namespace,'default-tab'), true) - .classed("active", true) - .classed('text-left', true), - - p = safeSelect(defaultTab, 'div') - .html( - "Click to add a new group.
"+ - "Click to submit for re-analysis.
"+ - "Click to close the dataselect widget." - ); - // .text('Click the green plus to add a new group.') - } + .style('display', 'flex') + .style('justify-content', 'center') + .style('text-align', 'middle') + .style('padding', 2+"px") + .style('border-style', 'solid') + .style('border-width', 2); - function makeRemoveButton(paneBtnList) { - var - btn = safeSelect(paneBtnList, 'button', 'remove-btn') - .classed('btn btn-danger', true) - .on('click', removeBtnClickToRemove), - i = safeSelect(btn, 'i', 'fa').classed('fa-trash-o', true), - s = safeSelect(btn, 'span').text('Remove group'); - return btn + var text = utils$1.sel.safeSelect(div, 'div') + .text(v) + .style('color', textColor) + .style('align-self', 'center'); } - function removeBtnClickToRemove(d, i) { - var - tab = selection.select('#'+hypenate(namespace,'tab',d)), - pane = selection.select('#'+hypenate(namespace,'tab','pane',d)); + return legend +} - tab.remove(); - pane.remove(); +let legends = { + categorical: categoricLegend, numeric: numericLegend +}; - showRemainingPanes(); - updateTabAndPaneNumbers(); - } +// import * as d3 from "d3"; +/******************************************************************************* +** ** +** ** +** D3 EXTENSIONS ** +** ** +** ** +*******************************************************************************/ +/** +* Recursively ascends parents of selection until it finds an svg tag +* @function d3.selection.thisSVG +* @augments d3.selection +* @returns {Element} which is the svg tag, not the d3 selection of that tag +*/ +d3.selection.prototype.thisSVG = function() { return utils$1.sel.getContainingSVG(this.node()); }; - function togglePaneById(id) { - var panes = selection.select('.'+hypenate(namespace, 'tab-content')); - panes.selectAll('div.tab-pane') - .each(function(d, i){ - d3.select(this).classed('active show', d3.select(this).attr('id') == id); - }); - } +/** +* Helper for getting absolute position of the mouse +* @function d3.mouse.absolute +* @augments d3.mouse +* @returns {number[]} [x, y] as they relate to `html` not to local scope. +*/ +d3.mouse.absolute = function() { + var html = d3.select('html').node(); + var [x, y] = this(html); + return [x, y] +}; - function insertTab(tabs, n) { - - // var li = tabs.insert("li", ':nth-child('+(n)+')') - var li = tabs.insert("li", '.right-aligned-tabs') - .classed('nav-item', true) - .classed('alert', true) - .classed('alert-secondary', true) - .classed('alert-dismissable', true) - .classed('fade', true) - .classed('show', true) - // .classed('mr-auto', true) - .attr("role", 'alert') - .attr("id", hypenate(namespace,'tab',n)) - .classed(hypenate(namespace,'tab'), true); - - var a = safeSelect(li, 'a') - .attr('data-toggle', 'tab') - .text('Group ' + n) - .attr('href', '#'+hypenate(namespace,'tab','pane',n)) - .on('dblclick', function(d, i){ - var t = d3.select(this); - t.attr('contenteditable', true); - d3.select(t.node().parentNode) - .classed('alert-secondary', false) - .classed('alert-warning', true); - // d3.select("html").on("click", dispatchBlur) - // - // function dispatchBlur(d, i) { - // if (d3.event.target != d3.select(this)) { - // d3.select(this).dispatch("blur") - // } - // } +/** +* Gets position of the selection in relation to the containing svg +* @see{@link getContainingSVG} +* @function d3.selection.absolutePosition +* @augments d3.selection +* @returns {Object} with structure similar to getBoundingClientRect, e.g. +* top, left, bottom, right, height, width +*/ +d3.selection.prototype.absolutePosition = function() { + var element = this.node(); + var elementPosition = element.getBoundingClientRect(); + var containerSVG = getContainingSVG(element); + var svgPosition = containerSVG.getBoundingClientRect(); + return { + top: elementPosition.top - svgPosition.top, + left: elementPosition.left - svgPosition.left, + bottom: elementPosition.bottom - svgPosition.top, + right: elementPosition.right - svgPosition.left, + height: elementPosition.height, + width: elementPosition.width + }; - }) - .on('blur', function(d, i){ +}; - var t = d3.select(this); - t.attr('contenteditable', false); - d3.select(t.node().parentNode) - .classed('alert-secondary', true) - .classed('alert-warning', false); - }) - .on('input', function(d, i){ - var t = d3.select(this); - var txt = t.text(); - d3.select(t.attr('href')).select("p.lead").text(txt); - }); +d3.selection.prototype.relativePositionTo = function(container) { + var element = this.node(); + var elementPosition = element.getBoundingClientRect(); + var containerSVG = container; + var svgPosition = containerSVG.getBoundingClientRect(); + return { + top: elementPosition.top - svgPosition.top, + left: elementPosition.left - svgPosition.left, + bottom: elementPosition.bottom - svgPosition.top, + right: elementPosition.right - svgPosition.left, + height: elementPosition.height, + width: elementPosition.width + }; - return li - } +}; - function makeTabPane(panes, n) { - var pane = panes.append('div') - .datum(n) - .attr('role', 'tabpanel') - .classed('tab-pane', true) - .classed('text-left', true) - .classed(hypenate(namespace,'tab','pane'), true) - .attr('id', hypenate(namespace,'tab','pane',n)); - return pane - } +// function getTranslation(selection){ +// var transform = selection.attr('transform') +// var [junk, xy] =transform.split('translate(') +// var [x, y] = xy.split(',') +// y, junk = y.split(')') +// return [parseFloat(x), parseFloat(y)] +// } - function populatePane(pane, n) { - var - lead = safeSelect(pane, 'p', 'lead').text('Group '+n), +function lasso( selection ) { + var + svg, // svg that is target of events + objectContainer, // container which houses objects we are selecting (allows for transform to be applied to lasso) + objectClass, // class of object we are selecting + namespace="d3sm-lasso", + chartContainer, + chartOffset, + objectsOffset, + eventCatcher, - tableContainer = safeSelect(pane, 'div', 'table-responsive') - .attr("class", hypenate(namespace, "data-table")), + xScale, // optional scale for the lasso currentPoints + yScale, // optional scale for the lasso currentPoints - table = safeSelect(tableContainer, "table", "table") - .classed("table-sm", true).classed("table-hover", true), + activeQ = false, // whether or not lasso is active - caption = safeSelect(table, "caption", "caption").html("List of selected"), + currentPoints=[], // mouse points for current lasso + allPoints=[], // list of lists for all points of lassos - btns = safeSelect(pane, 'div', 'text-right'), + line = d3.line() + .x(function(d, i){ + var x; + if (xScale != undefined) { x = xScale(d[0]); } + else {x = d[0];} + return x //- chartOffset[0]// - objectsOffset[0] + }) + .y(function(d, i){ + var y; + if (yScale != undefined) { y= yScale(d[1]); } + else {y = d[1];} + return y// - chartOffset[1]// - objectsOffset[1] + }) + .curve(d3.curveLinearClosed), - cN = n % colorFunction$$1.colors().length; - if (n % 2 != 0) { cN = (colorFunction$$1.colors().length - 1) - cN; } + instance=0, // an indentifier for which instance this lasso is under the current svg + tickDistance = 10, + // styles for lasso path + color$$1 = '#17a2b8', + animationRate = '10s', + opacity=0.3, + dashArray = '5, 10', + stroke = 'black', + strokeWidth=2, + // styles for lassoed objects + lassoedFill = "white", + lassoedStroke = 'black', + lassoedStrokeWidth = 3, + transitionDuration = 1000, + easeFunc = d3.easeExp; - var lassoBtn = makeLassoButton(btns); - var currentLasso = makeLassoFunction(n, colorFunction$$1.colors()[cN]); - var clearBtn = makeClearButton(btns); + var path; - bindButtons(lassoBtn, clearBtn, currentLasso, table); + lasso.svg = function(_) { return arguments.length ? (svg = _, lasso) : svg; }; + lasso.chartContainer = function(_) { return arguments.length ? (chartContainer = _, lasso) : chartContainer; }; + lasso.objectContainer = function(_) { return arguments.length ? (objectContainer = _, lasso) : objectContainer; }; + lasso.objectClass = function(_) { return arguments.length ? (objectClass = _, lasso) : objectClass; }; + lasso.namespace = function(_) { return arguments.length ? (namespace = _, lasso) : namespace; }; + lasso.xScale = function(_) { return arguments.length ? (xScale = _, lasso) : xScale; }; + lasso.yScale = function(_) { return arguments.length ? (yScale = _, lasso) : yScale; }; + lasso.activeQ = function(_) { return arguments.length ? (activeQ = _, lasso) : activeQ; }; + lasso.currentPoints = function(_) { return arguments.length ? (currentPoints = _, lasso) : currentPoints; }; + lasso.allPoints = function(_) { return arguments.length ? (allPoints = _, lasso) : allPoints; }; + lasso.instance = function(_) { return arguments.length ? (instance = _, lasso) : instance; }; + lasso.tickDistance = function(_) { return arguments.length ? (tickDistance = _, lasso) : tickDistance; }; + lasso.color = function(_) { return arguments.length ? (color$$1 = _, lasso) : color$$1; }; + lasso.animationRate = function(_) { return arguments.length ? (animationRate = _, lasso) : animationRate; }; + lasso.opacity = function(_) { return arguments.length ? (opacity = _, lasso) : opacity; }; + lasso.dashArray = function(_) { return arguments.length ? (dashArray = _, lasso) : dashArray; }; + lasso.stroke = function(_) { return arguments.length ? (stroke = _, lasso) : stroke; }; + lasso.lassoedFill = function(_) { return arguments.length ? (lassoedFill = _, lasso) : lassoedFill; }; + lasso.lassoedStroke = function(_) { return arguments.length ? (lassoedStroke = _, lasso) : lassoedStroke; }; + lasso.lassoedStrokeWidth = function(_) { return arguments.length ? (lassoedStrokeWidth = _, lasso) : lassoedStrokeWidth; }; + lasso.eventCatcher = function(_) { return arguments.length ? (eventCatcher = _, lasso) : eventCatcher; }; - var removeBtn = makeRemoveButton(btns); + lasso.drag = drag; + lasso.draw = draw; + lasso.tick = tick; + lasso.detect = detect; + lasso.toggle = toggle; + lasso.remove = remove; + lasso.render = render; + lasso.keyFrames = keyFrames; + lasso.updateObjects = updateObjects; + lasso.applyPathAttributes = applyPathAttributes; + lasso.applyObjectAttributes = applyObjectAttributes; - lead.on("mouseover", function(){ - currentLasso.draw(); - }); - lead.on("mouseout", function(){ - currentLasso.remove(); - }); + keyFrames(); + function lasso() { + // add a dash animation if needed + if (activeQ) { transitionDraw(); } } - function makeLassoButton(btns) { - var btn = safeSelect(btns, 'button', 'lasso-btn') - .classed('btn btn-info', true) - .classed(namespace, true); - // .datum([lasso]) - var i = safeSelect(btn, 'i', 'fa').classed('fa-hand-pointer-o', true); - var s = safeSelect(btn, 'span').text('Lasso select'); - return btn - - } + function toggle(state) { + // use optional param to set state, otherwise toggle state + activeQ = (state!=undefined) ? state : !activeQ; + chartOffset = utils$1.sel.getTranslation(chartContainer); + objectsOffset = utils$1.sel.getTranslation(objectContainer); - function makeClearButton(btns) { - var btn = safeSelect(btns, 'button', 'clear-btn') - .classed('btn btn-light', true) - .classed(namespace, true); - var i = safeSelect(btn, 'i', 'fa').classed('fa-eraser', true); - var s = safeSelect(btn, 'span').text('Clear selection'); - return btn - } + if (activeQ) { + svg.node().addEventListener('mousedown', render, true); + } else { + svg.node().removeEventListener('mousedown', render, true); + remove(); + } - function makeLassoFunction(instance, color) { - var currentLasso = lasso() - .namespace(namespace) - .svg(svg) - .objectClass(objectClass) - .chartContainer(chartContainer) - .objectContainer(objectContainer) - .instance(instance) - .color(color) - .yScale(yScale) - .xScale(xScale); - - return currentLasso } - function bindButtons(lassoBtn, clearBtn, currentLasso, table) { - currentLasso.eventCatcher(lassoBtn); - lassoBtn.node().addEventListener("click", lassoBtnToggle); - - lassoBtn.datum([currentLasso]) - .on(hypenate(namespace,'render'), function(){ - d3.select(this).datum()[0].draw(); - }) - .on(hypenate(namespace,"drag"), function(las){ - var nodes = chartContainer.selectAll(".in-lasso-"+las[0].instance()).nodes(); - var tableData = nodes.map(function(d, i){ - var extracted = dataExtractor(d3.select(d).datum()); - extracted["__node"] = d; - return extracted - }); + function draw() { + chartOffset = utils$1.sel.getTranslation(chartContainer); + objectsOffset = utils$1.sel.getTranslation(objectContainer); + var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + var paths$$1 = container.selectAll('path[instance="'+instance+'"]'); - makeTable(table, tableData, currentLasso); - }); + // update + paths$$1 = paths$$1.data(allPoints); - clearBtn.on('click', function(){ - currentLasso.allPoints([]); - currentLasso.currentPoints([]); - lassoBtn.dispatch(hypenate(namespace,"drag")); + // remove excess + var pExit = paths$$1.exit().remove(); + // add needed paths + var pEnter = paths$$1.enter().append('path'); - }); + // merge + paths$$1 = paths$$1.merge(pEnter); + // apply + applyPathAttributes(paths$$1); } - function lassoBtnToggle() { - var that = d3.select(this); - var las = that.datum()[0]; - - las.toggle(); - var activeQ = las.activeQ(); - - if (las.activeQ()) { - that.classed('btn-info', !activeQ); - that.classed('btn-warning', activeQ); - that.select("span").text("Lasso select (active)"); - selection.selectAll("."+namespace+".lasso-btn").dispatch(hypenate(namespace,'render')); - d3.select("html").node().addEventListener('mousedown', monitorLassoButtonState); - } else { - that.classed('btn-info', !activeQ); - that.classed('btn-warning', activeQ); - that.select("span").text("Lasso select"); - d3.select("html").node().removeEventListener('mousedown', monitorLassoButtonState); - } - - function monitorLassoButtonState(event) { - /* - activeLasso stops event stopPropagation, so this event will not register in - that case. Thus we only need to ensure that the usre is clicking on the lasso button - otherwise, de-spawn lasso. - */ - if ( - event.target != that.node() && - event.target != that.select("span").node() && - event.target != that.select("i").node() - ) { - // event.preventDefault() - // event.stopPropagation() - las.toggle(false); - that.classed('btn-info', true); - that.classed('btn-warning', false); - that.select("span").text("Lasso select"); - // console.log(that, that.node()) - // that.dispatch("focusout") - } - } - + function remove() { + var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + var paths$$1 = container.selectAll('path[instance="'+instance+'"]').remove(); + container.remove(); + objectContainer.selectAll(objectClass).classed("in-lasso", false); + updateObjects(); } + function render( event ) { + // nothing can interefer with drawing the lasso + event.preventDefault(); event.stopPropagation(); - function updateTableHeaderColumns(headR, tableData) { - var headerKeys = d3.keys(tableData[0]).filter(k=>k!="__node"); - if (headerKeys.length > 0) { - // headerKeys = ["remove"].concat(headerKeys) - headerKeys.push("remove"); - } - - var headerCols = headR.selectAll("th"); - - headerCols = headerCols.data(headerKeys); - headerCols.exit().remove(); - headerCols = headerCols.merge(headerCols.enter().append("th").attr("scope","col")) - .text(function(d, i){return d}); - - return headerKeys - } + var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); - function updateTableRows(body, tableData) { - var bodyRows = body.selectAll("tr"); + /* + each time the user presses down, while the state is active, the lasso + the lasso should make a seperate segment. + */ + currentPoints = []; - bodyRows = bodyRows.data(tableData); - bodyRows.exit().remove(); - bodyRows = bodyRows.merge(bodyRows.enter().append("tr")); + svg.node().addEventListener('mousemove', drag); + svg.node().addEventListener('mouseup', function(event) { + svg.node().removeEventListener('mousemove', drag); + allPoints.push(currentPoints); + // BUG: somehow this is pushing currentPoints n times where n is the nth lasso path for the current instance + // NOTE: allPoints = utils.arr.unique(allPoints) is a temporary and inefficient fix + allPoints = utils$1.arr.unique(allPoints); + }); - return bodyRows + path = container.append('path').data([currentPoints]); + applyPathAttributes(path); } - function updateTableRowColumns(cols, headerKeys, rowData, tableData, lasso$$1, table) { - cols = cols.data(headerKeys); - cols.exit().remove(); - cols = cols.merge(cols.enter().append("td")); - - cols.html(function(d, i){ - if (d != "remove") { return rowData[d] } - return "" - }).classed("text-left", function(d, i){ - if (d != "remove") { return false } - return true - }); - // .attr("scope",function(d, i){ - // if (d != "remove") { return false } - // return true - // }) + function transitionDraw() { + var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + var paths$$1 = container.selectAll('path[instance="'+instance+'"]'); - cols.select("i.fa-close").on("click", function(d, i){ - var node = rowData["__node"], - n = d3.select(node); - n.classed("in-lasso", false); - n.classed("in-lasso-"+lasso$$1.instance(), false); + // update + paths$$1 = paths$$1.data(allPoints); - tableData.map(function(dd, j){ - if (dd["__node"] == node) { - tableData.splice(j, 1); - makeTable(table, tableData, lasso$$1); - } - }); + // remove excess + var pExit = paths$$1.exit().remove(); + // add needed paths + var pEnter = paths$$1.enter().append('path'); + + // merge + paths$$1 = paths$$1.merge(pEnter) + .transition().duration(transitionDuration) + .ease(easeFunc); + applyPathAttributes(paths$$1); - }); } - function makeTable(table, tableData, lasso$$1) { - var - head = safeSelect(table, "thead"), - headR = safeSelect(head, "tr"), - body = safeSelect(table, "tbody"), + function applyPathAttributes(path) { + path + .attr("class", utils$1.str.hypenate(namespace, "lasso-path")) + .style('opacity', opacity) + .attr('fill', color$$1) + .attr("d", line) + .attr('instance', instance) + .style("stroke-dasharray", dashArray) + .attr("stroke", stroke) + .attr("stroke-width", strokeWidth) + .style('animation', 'lassoDash '+animationRate+' linear') + .style("animation-iteration-count", "infinite"); + } - headerKeys = updateTableHeaderColumns(headR, tableData), + function drag(event) { + /* + effectively create a mouse down and move event (which normally is inteperated + as 'drag' by the browser) by dynamically adding / removing this event on + mouse down / mouse up. + */ - bodyRows = updateTableRows(body, tableData); + if (eventCatcher != undefined) {eventCatcher.dispatch(utils$1.str.hypenate(namespace,"drag"));} + // d3.dispatch(utils.str.hypenate(namespace,"drag")) - bodyRows.each(function(rowData, i){ - var t = d3.select(this); - var cols = t.selectAll("td"); + if (event.which != 1) {return} // ensures left mouse button set + d3.event = event; + var pt = d3.mouse(objectContainer.node()); + var pt = d3.mouse(svg.node()); - updateTableRowColumns(cols, headerKeys, rowData, tableData, lasso$$1, table); + if (xScale != undefined) {pt[0] = xScale.invert(pt[0]);} + if (yScale != undefined) {pt[1] = yScale.invert(pt[1]);} + pt[0] = pt[0] - chartOffset[0] - objectsOffset[0]; + pt[1] = pt[1] - chartOffset[1] - objectsOffset[1]; - t.on("mouseover", function(d, i) { - lasso$$1.applyObjectAttributes(d3.select(d["__node"]),true); - }) - .on("mouseout", function(d, i) { - lasso$$1.applyObjectAttributes(d3.select(d["__node"]),false); - }); - }); + /* if we have a point already, test if it passes a minimum distance to prevent overwhelming with too many tick functions */ + if (currentPoints.length) { + var lastPt = currentPoints[currentPoints.length - 1]; + var a = [pt[0], pt[1]], b = [lastPt[0], lastPt[1]]; + if (xScale) {b[0] = xScale(b[0]); a[0] = xScale(a[0]);} + if (yScale) {b[1] = yScale(b[1]); a[1] = yScale(a[1]);} + var dist = utils$1.math.euclideanDistance(b, a); + if (dist > tickDistance) { tick(pt); } + } + else { tick(pt); } } + function tick (pt) { + /* + If a point is provided update data and objects. + Otherwise just call on data we already have. + Why like this?: + 1. currentPoints is current points to allow disjunct lassos, currentPoints is only pushed to + allPoints after mouseup. + 2. to allow render of objects in the lasso class / updating the data list + just by toggling the button + */ + if (pt != undefined) { + currentPoints.push(pt); + path.attr("d", line); + if (currentPoints.length < 3) {return} // need at least 3 points to detect anything. + detect(allPoints.concat([currentPoints])); + } else { + detect(allPoints); + } + } + function detect(lassos) { + if (lassos == undefined) {lassos = allPoints;} + objectContainer.selectAll(objectClass).each(function(d, i){ + var current = d3.select(this), - function makeNewGroup(d, i) { - - var tabs = selection.select('.'+hypenate(namespace, 'tab-list')), - panes = selection.select('.'+hypenate(namespace, 'tab-content')), - n = newGroupNumber(); - - if (tabs.selectAll('.'+hypenate(namespace,'tab')).size() == maxNumberOfGroups) { - onError('only '+maxNumberOfGroups+' allowed.', 'warning'); - return - } + box = current.absolutePosition(), + // box = current.relativePositionTo(objectContainer.node()), - var - tab = insertTab(tabs, n), - pane = makeTabPane(panes, n); + boxPts = [ + [ + box.left - chartOffset[0] - objectsOffset[0], + box.top - chartOffset[1] - objectsOffset[1] + ], + [ + box.right - chartOffset[0] - objectsOffset[0], + box.top - chartOffset[1] - objectsOffset[1] + ], + [ + box.left - chartOffset[0] - objectsOffset[0], + box.bottom - chartOffset[1] - objectsOffset[1] + ], + [ + box.right - chartOffset[0] - objectsOffset[0], + box.bottom - chartOffset[1] - objectsOffset[1] + ] + ]; - populatePane(pane, n); - togglePaneById(pane.attr('id')); + if (xScale != undefined) { + boxPts[0][0] = xScale.invert(boxPts[0][0]); + boxPts[1][0] = xScale.invert(boxPts[1][0]); + boxPts[2][0] = xScale.invert(boxPts[2][0]); + boxPts[3][0] = xScale.invert(boxPts[3][0]); + } + if (yScale != undefined) { + boxPts[0][1] = yScale.invert(boxPts[0][1]); + boxPts[1][1] = yScale.invert(boxPts[1][1]); + boxPts[2][1] = yScale.invert(boxPts[2][1]); + boxPts[3][1] = yScale.invert(boxPts[3][1]); + } - } - function newGroupNumber() { - var tabs = selection.select('.'+hypenate(namespace, 'tab-list'));//, - var - n = tabs.selectAll('li').size() - 3, // minus 1 for plus tab - s = 'Group ' + n, - gs = []; - tabs.each(function(d, i){ return gs.push(d3.select(this).select("a").text()) }); - while (gs.includes(s)) { n+=1; s = 'Group ' + n; } - return n - } + /* + flag needed as we have to test multiple lasso segments, and if the point + is not in one segment, it does not mean it is not in any + */ + var inAnyLassoQ = false; + for (var i = 0; i < lassos.length; i++) { + var lassoPoints = lassos[i]; + // .map(function(pt){ + // var x, y = pt + // if (xScale!=undefined) {x = xScale(x)} + // if (yScale!=undefined) {y = yScale(y)} + // return [x, y] + // }) + var boxInLassoQ = boxPts.every(coord => d3.polygonContains(lassoPoints, coord)); - function updateTabAndPaneNumbers() { - var - tabs = selection.select('.'+hypenate(namespace, 'tab-list')), - panes = selection.select('.'+hypenate(namespace, 'tab-content')); - - tabs = tabs.selectAll('.'+hypenate(namespace,'tab')); - panes = panes.selectAll('.'+hypenate(namespace,'tab', 'pane')); - - tabs.each(function(d, i){ - d3.select(this).datum(i) - .attr("id", hypenate(namespace,'tab',i)) - .select('a') - .attr('href', '#'+hypenate(namespace,'tab','pane',i)) - .text(function(dd, ii){ - var curText = d3.select(this).text(); - if (curText.split(' ')[0] == 'Group') { - return 'Group ' + i - } - return curText - }); - }); + if (boxInLassoQ) { inAnyLassoQ = true; } // only update flag in the positive case. + } - panes.each(function(d, i){ - d3.select(this).datum(i) - .attr('id', hypenate(namespace,'tab','pane',i)); - safeSelect(d3.select(this), 'p', 'lead') - .text(function(dd, ii){ - var curText = d3.select(this).text(); - if (curText.split(' ')[0] == 'Group') { - return 'Group ' + i - } - return curText - }); - safeSelect(d3.select(this), 'button', 'remove-btn') - .on('click', removeBtnClickToRemove); + current.classed('in-lasso', inAnyLassoQ); + current.classed('in-lasso-'+instance, inAnyLassoQ); }); + updateObjects(); + return objectContainer.selectAll('.in-lasso-'+instance) } - function gatherDataLists() { - var tabs = selection.select('.'+hypenate(namespace, 'tab-list')); - var panes = selection.select('.'+hypenate(namespace, 'tab-content')); - var tables = panes.selectAll('.'+hypenate(namespace, "data-table")); - var data = {}; - var textGroups = tabs.selectAll('li.'+hypenate(namespace,'tab') + ' > a') - .nodes().map(function(d, i){return d3.select(d).text()}); - textGroups.map(function(e, i){ - data[e] = []; + function updateObjects() { + objectContainer.selectAll(objectClass).each(function(d, i) { + var t = d3.select(this); + applyObjectAttributes(t, t.classed('in-lasso')); }); + } + function applyObjectAttributes(obj, setQ) { + var + preLassoFill = obj.attr('_pre_lasso_fill'), + preLassoStroke = obj.attr('_pre_lasso_stroke'), + preLassoStrokeWidth = obj.attr('_pre_lasso_stroke-width'); - tables.each(function(d, i){ - var table = d3.select(this).select("tbody"); - data[textGroups[i]] = table.selectAll('tr').data(); - }); + if (setQ) { + obj.classed("in-lasso", true); + obj.classed('in-lasso-'+instance, true); + if (preLassoFill == undefined) { obj.attr('_pre_lasso_fill', obj.attr('fill')); } + if (preLassoStroke == undefined) { obj.attr('_pre_lasso_stroke', obj.attr('stroke')); } + if (preLassoStrokeWidth == undefined) { obj.attr('_pre_lasso_stroke-width', obj.attr('stroke-width')); } - return data - } + obj + //BUG: when .raise() + .attr('fill', lassoedFill) + .attr('stroke', lassoedStroke) + .attr('stoke-width', lassoedStrokeWidth); - function showRemainingPanes() { - var - tabs = selection.select('.'+hypenate(namespace, 'tab-list')), - panes = selection.select('.'+hypenate(namespace, 'tab-content')), - remainingTabs = tabs.selectAll('.'+hypenate(namespace,'tab')); - - if (remainingTabs.size() == 0) { - panes.select('.'+hypenate(namespace,'default-tab')) - .classed("active", true) - .classed('text-left', true); - } - else { - var lastTab = remainingTabs.nodes()[remainingTabs.size()-1], - lastPaneId = d3.select(lastTab).select('a').attr('href'); - panes.select(lastPaneId) - .classed("active", true) - .classed('text-left', true); + } else { + obj.classed("in-lasso", false); + obj.classed('in-lasso-'+instance, false); + if (preLassoFill != undefined) { obj.attr('fill', preLassoFill); } + if (preLassoStroke != undefined) { obj.attr('stroke', preLassoStroke); } + if (preLassoStrokeWidth != undefined) { obj.attr('stroke-width', preLassoStrokeWidth); } } } + function keyFrames() { + var style = + d3.select("html").select('style.'+utils$1.str.hypenate(namespace,"lasso-dash")); + if (style.empty()) { + d3.select("html").append('style') + .classed(utils$1.str.hypenate(namespace,"lasso-dash"), true) + .html("@keyframes lassoDash {to { stroke-dashoffset: 1000;}}"); + } - return lassoWidget + } + return lasso } -function upset ( selection ) { - var - data, - orient='horizontal', - spaceX, - spaceY, - overflowQ=false, - minObjectSize=20, - maxObjectSize=50, - circleStrokeWidth=2, - // colorFunction= - backgroundFill = 'transparent', - namespace='d3sm-upset', - objectClass = 'upset', - - transitionDuration = 1000, - easeFunc = d3.easeExp, - - setKey = "set", - intersectionKey = "intersection", - elementsKey = "elements", - - setExtractor = function(key, i) {return data[key][setKey]}, - intersectionExtractor = function(key, i) {return data[key][intersectionKey]}, - elementExtractor = function(key, i) {return data[key][elementsKey]}, - - cellKeys, - setValues, - intersectionValues, - xObjectSpacer = 0.05, - yObjectSpacer = 0.05, - radius, +/******************************************************************************* - // listDelim = ';' +** ** +** ** +** PLOTZOOM ** +** ** +** ** +*******************************************************************************/ +/** + * Creates an plotZoom instance, which can handle drag and scroll events + * @constructor plotZoom + * @param {function} chart a function instance of one of the d3sm plots (e.g. bar, boxwhisker, bubbleHeatmap, violin, etc) + * @param {axis} xAxis the axis instance responsible for the x axis + * @param {axis} yAxis the axis instance responsible for the y axis + * @namespace plotZoom + * @returns {function} zoom + */ +function multiPlotZoom( chart ) { + var + /** + * The event on which to fire + * (see {@link plotZoom#eventType}) + * @param {string} eventType which event it should handle. Currently supports scroll and wheel + * @memberof plotZoom# + * @property + */ + eventType, + /** + * A scaling factor for the wheel "speed" + * (see {@link plotZoom#wheelSpeed}) + * @param {number} [wheelSpeed=20] scales the wheel translation by wheelSpeed + * @memberof plotZoom# + * @property + */ + wheelSpeed = 20, + /** + * The orientation in which to allow scrolling: 'horizontal', 'vertical', or '2D' + * (see {@link plotZoom#orient}) + * @param {string} [orient=chart.orient() || 'horizontal'] + * @memberof plotZoom# + * @property + */ + orient = (chart.orient == undefined) ? 'horizontal' : chart.orient(), + /** + * The max distance allowed to scroll in the x direction + * (see {@link plotZoom#xLock}) + * @param {number} [xLock=chart.spaceX()] ideally chart.overflowQ() == true and this value is the + * bounding rect across all elements in the chart minus the space in which to show. + * @memberof plotZoom# + * @property + */ + xLock=chart.spaceX(), + /** + * The max distance allowed to scroll in the y direction + * (see {@link plotZoom#yLock}) + * @param {number} [yLock=chart.spaceY()] ideally chart.overflowQ() == true and this value is the + * bounding rect across all elements in the chart minus the space in which to show. + * @memberof plotZoom# + * @property + */ + yLock=chart.spaceY(), - yObjectSize, - ySpacerSize, - xObjectSize, - xSpacerSize, + chartSel = chart.selection(), - setKeySortingFunction = function(a, b) { return setValues.indexOf(setExtractor(a)) - setValues.indexOf(setExtractor(b)) }, - intersectionKeySortingFunction = function(a, b) { return intersectionValues.indexOf(intersectionExtractor(a)) - intersectionValues.indexOf(intersectionExtractor(b)) }; + svg = d3.select(chartSel.thisSVG()), + xComponents = [], + yComponents = []; - upset.selection = function(_) { return arguments.length ? (selection = _, upset) : selection; }; - upset.data = function(_) { return arguments.length ? (data = _, upset) : data; }; - upset.orient = function(_) { return arguments.length ? (orient = _, upset) : orient; }; - upset.spaceX = function(_) { return arguments.length ? (spaceX = _, upset) : spaceX; }; - upset.spaceY = function(_) { return arguments.length ? (spaceY = _, upset) : spaceY; }; - upset.overflowQ = function(_) { return arguments.length ? (overflowQ = _, upset) : overflowQ; }; - upset.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, upset) : minObjectSize; }; - upset.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, upset) : maxObjectSize; }; - upset.circleStrokeWidth = function(_) { return arguments.length ? (circleStrokeWidth = _, upset) : circleStrokeWidth; }; - upset.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, upset) : backgroundFill; }; - upset.namespace = function(_) { return arguments.length ? (namespace = _, upset) : namespace; }; - upset.objectClass = function(_) { return arguments.length ? (objectClass = _, upset) : objectClass; }; - upset.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, upset) : transitionDuration; }; - upset.easeFunc = function(_) { return arguments.length ? (easeFunc = _, upset) : easeFunc; }; - upset.cellKeys = function(_) { return arguments.length ? (cellKeys = _, upset) : cellKeys; }; - upset.setValues = function(_) { return arguments.length ? (setValues = _, upset) : setValues; }; - upset.intersectionValues = function(_) { return arguments.length ? (intersectionValues = _, upset) : intersectionValues; }; - upset.xObjectSpacer = function(_) { return arguments.length ? (xObjectSpacer = _, upset) : xObjectSpacer; }; - upset.yObjectSpacer = function(_) { return arguments.length ? (yObjectSpacer = _, upset) : yObjectSpacer; }; - upset.radius = function(_) { return arguments.length ? (radius = _, upset) : radius; }; - upset.setExtractor = function(_) { return arguments.length ? (setExtractor = _, upset) : setExtractor; }; - upset.intersectionExtractor = function(_) { return arguments.length ? (intersectionExtractor = _, upset) : intersectionExtractor; }; - upset.elementExtractor = function(_) { return arguments.length ? (elementExtractor = _, upset) : elementExtractor; }; - upset.setKeySortingFunction = function(_) { return arguments.length ? (setKeySortingFunction = _, upset) : setKeySortingFunction; }; - upset.intersectionKeySortingFunction = function(_) { return arguments.length ? (intersectionKeySortingFunction = _, upset) : intersectionKeySortingFunction; }; - upset.yObjectSize = function(_) { return arguments.length ? (yObjectSize = _, upset) : yObjectSize; }; - upset.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, upset) : ySpacerSize; }; - upset.xObjectSize = function(_) { return arguments.length ? (xObjectSize = _, upset) : xObjectSize; }; - upset.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, upset) : xSpacerSize; }; + /** + * Gets or sets the event type in which to respond + * (see {@link plotZoom#eventType}) + * @param {string} [_=none] should be 'drag' or 'wheel' + * @returns {zoom | string} + * @memberof plotZoom + * @property + * by default plotZoom=undefined + */ + zoom.eventType = function(_) { return arguments.length ? (eventType = _, zoom) : eventType; }; + /** + * Gets or sets the wheel speed in which to scale the wheel scroll transform + * (see {@link plotZoom#wheelSpeed}) + * @param {number} [_=none] + * @returns {zoom | number} + * @memberof plotZoom + * @property + * by default wheelSpeed=20 + */ + zoom.wheelSpeed = function(_) { return arguments.length ? (wheelSpeed = _, zoom) : wheelSpeed; }; + /** + * Gets or sets the orientation in which items are manipulated + * (see {@link plotZoom#orient}) + * @param {string} [_=none] should be horizontal, vertical, or 2D + * @returns {zoom | string} + * @memberof plotZoom + * @property + * by default orient=chart.orient() || 'horizontal' + */ + zoom.orient = function(_) { return arguments.length ? (orient = _, zoom) : orient; }; - function upset() { - // for convenience in handling orientation specific values - var horizontalQ = (orient == 'horizontal') ? true : false; - var verticalQ = !horizontalQ; + /** + * Gets or sets the max distance in which to scroll X + * (see {@link plotZoom#xLock}) + * @param {number} [_=none] should be a positive value + * @returns {zoom | number} + * @memberof plotZoom + * @property + * by default xLock=chart.spaceX() + */ + zoom.xLock = function(_) { return arguments.length ? (xLock = _, zoom) : xLock; }; + /** + * Gets or sets the max distance in which to scroll Y + * (see {@link plotZoom#yLock}) + * @param {number} [_=none] should be a positive value + * @returns {zoom | number} + * @memberof plotZoom + * @property + * by default yLock=chart.spaceY() + */ + zoom.yLock = function(_) { return arguments.length ? (yLock = _, zoom) : yLock; }; - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + zoom.xComponents = function(_) { return arguments.length ? (xComponents = _, zoom) : xComponents; }; + zoom.yComponents = function(_) { return arguments.length ? (yComponents = _, zoom) : yComponents; }; - cellKeys = d3.keys(data); - setValues = unique(cellKeys.map(setExtractor)).sort(); - intersectionValues = unique(cellKeys.map(intersectionExtractor)).sort().sort(function(a, b){ - return a.split(';').length - b.split(';').length - }); + function setLocks() { + var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); + var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); + var cos = chartObjSel.attr('transform', 'translate(0,0)'); - if (!horizontalQ) { - cellKeys.sort(function(a, b){ return setKeySortingFunction(a, b) || intersectionKeySortingFunction(a, b) }); - } else { - cellKeys.sort(function(a, b){ return intersectionKeySortingFunction(a, b) || setKeySortingFunction(a, b) }); - } + xLock = chartSel.node().getBBox().width - chart.spaceX();// * .9 + yLock = chartSel.node().getBBox().height - chart.spaceY();// * .9 + cos.attr('transform', 'translate('+chartObjTrans[0]+','+chartObjTrans[1]+')'); + utils$1.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); + } + /** + * Sets the x and y locks (how far one can scroll) + * (see {@link plotZoom#xLock} and {@link plotZoom#yLock}) + * @function plotZoom.setLocks + * @returns {undefined} + * @memberof plotZoom + * @property + */ + zoom.setLocks = setLocks; + function zoom() { + setLocks(); var - xValues = horizontalQ ? intersectionValues : setValues, - yValues = horizontalQ ? setValues : intersectionValues, - xDim = horizontalQ ? xValues.length : yValues.length, - yDim = horizontalQ ? yValues.length : xValues.length; - - // console.log(xValues, yValues) - - - xObjectSize = calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, xObjectSpacer, overflowQ); - yObjectSize = calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, yObjectSpacer, overflowQ); - xSpacerSize = calculateWidthOfSpacer(xValues, spaceX, xObjectSize, xDim, xObjectSpacer, overflowQ); - ySpacerSize = calculateWidthOfSpacer(yValues, spaceY, yObjectSize, yDim, yObjectSpacer, overflowQ); + xComponentsSel = xComponents.map(function(d, i){return d.selection()}), + yComponentsSel = yComponents.map(function(d, i){return d.selection()}); - var ySpacer = groupingSpacer() - .horizontalQ(false) - .moveby('category').numberOfObjects(yDim) - .objectSize(yObjectSize).spacerSize(ySpacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc); + var horizontalQ, verticalQ; + if (orient == '2D') {horizontalQ = true; verticalQ = true;} + if (orient == 'horizontal') {horizontalQ = true; verticalQ = false;} + if (orient == 'vertical') {verticalQ = true; horizontalQ = false;} - var xSpacer = groupingSpacer() - .horizontalQ(true) - .moveby('category').numberOfObjects(xDim) - .objectClass(objectClass) - .objectSize(xObjectSize).spacerSize(xSpacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc); + // capture transform event + var transform = d3.event.transform; + var chartBox = chartSel.node().getBBox(); + var xComponentsBox = xComponentsSel.map(function(d, i){return d.node().getBBox()}); + var yComponentsBox = xComponentsSel.map(function(d, i){return d.node().getBBox()}); - if (verticalQ) { - xSpacer.objectClass(objectClass); - ySpacer.namespace('across').objectClass(hypenate(objectClass, 'across')); + var chartWidth = chartBox.width - chartBox.x; + var chartHeight = chartBox.height - chartBox.y; - ySpacer(container, yValues, 0); - container.selectAll('g.'+hypenate(objectClass, 'across')) - .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); - } else { - xSpacer.namespace('across').objectClass(hypenate(objectClass, 'across')); - ySpacer.objectClass(objectClass); + // enable wheel event + if (eventType == "wheel") { + var e = d3.event; + // prevent page scrolling + e.preventDefault(); + // event delta is very very slow, so speed it up with wheel speed + var w = d3.event.deltaY * wheelSpeed; + var shiftQ = d3.event.shiftKey; - xSpacer(container, xValues, 0); - container.selectAll('g.'+hypenate(objectClass, 'across')) - .each(function(d, i){ ySpacer(d3.select(this), yValues, 0); }); + // d3 has no way to make custom transform, so make an object and add + // the necessary functions to make wheel event compatible with drag events + if (orient == '2D') { + transform = shiftQ ? {k: 1, x: w, y: 0} : {k: 1, x: 0, y: w}; + } else { + transform = horizontalQ ? {k: 1, x: w, y: 0} : {k: 1, x: 0, y: w}; + } + // * -1 is invert + transform.applyX = function(x) { return x * this.k + this.x *-1; }; + transform.applyY = function(y) { return y * this.k + this.y *-1; }; } - var cells = container.selectAll('g:not(.to-remove).'+objectClass); - var lookup = {}; - cellKeys.map(function(k, i){ - lookup[setExtractor(k)+'::'+intersectionExtractor(k)] = k; - }); - - // var positionedCellKeys = [] - // for (var i = 0; i < setValues.length; i++) { - // for (var j = 0; j < intersectionValues.length; j++) { - // var lookupValue = lookup[setValues[j]+"::"+intersectionValues[i]] - // if (lookupValue == undefined) { - // positionedCellKeys.push(undefined) - // } else { - // positionedCellKeys.push(lookupValue) - // } - // console.log(i, j, lookupValue) - // } - // } - // - // // console.log(positionedCellKeys) - // - // cells.data(positionedCellKeys); - - - cells.data(cellKeys); - cells.each(function(key, i) { - var t = d3.select(this); - if (key == undefined) {return } - var - currentData = data[key]; - // console.log(key, currentData) - var - set = setExtractor(key, i), - intersection = intersectionExtractor(key, i); + var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); + var xComponentObjSel = xComponentsSel.map(function(d, i){ + return d.select('.'+utils$1.str.hypenate(xComponents[i].namespace(),'object-container')) + }); + var yComponentObjSel = yComponentsSel.map(function(d, i){ + return d.select('.'+utils$1.str.hypenate(yComponents[i].namespace(),'object-container')) + }); - // console.log(set, intersection) + var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); + var xComponentsObjTrans = xComponentObjSel.map(function(d, i){ + return utils$1.sel.getTranslation(d.attr('transform')) + }); + var yComponentsObjTrans = yComponentObjSel.map(function(d, i){ + return utils$1.sel.getTranslation(d.attr('transform')) + }); - t.classed(intersection, true); - t.classed(set, true); + var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0; + if (horizontalQ) {x = x < -xLock ? (transform.x = 0, -xLock) : (transform.x = 0, Math.min(x, 0)); } - var c = safeSelect(t, 'circle', hypenate(objectClass,'circle')); - c.attr('cx', xObjectSize / 2) - .attr('cy', yObjectSize / 2 ) - .attr('r', radius == undefined ? Math.min(xObjectSize, yObjectSize) / 2 : radius) - .attr('fill', intersection.includes(set) ? "black": 'rgb(233,233,233)') - .attr('stroke', "black") - .attr("in-intersection", intersection.includes(set)); - }); + var y = verticalQ ? transform.applyY(chartObjTrans[1]) : 0; + if (verticalQ) {y = y < -yLock ? (transform.y = 0, -yLock): (transform.y = 0, Math.min(y, 0));} + chartObjSel.attr('transform', 'translate('+x+','+y+')'); + if (horizontalQ) { + // xAxisObjSel.attr('transform', 'translate('+x+','+0+')') + xComponentObjSel.map(function(d, i){ d.attr('transform', 'translate('+x+','+0+')'); }); + } + if (verticalQ) { + // yAxisObjSel.attr('transform', 'translate('+0+','+y+')') + yComponentObjSel.map(function(d, i){ d.attr('transform', 'translate('+0+','+y+')'); }); + + } } - function intersectionTotals() { - var totals = {}; - // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) + zoom.reset = function() { - intersectionValues.map(function(k, i){ totals[k] = {'total': 0}; }); - cellKeys.map(function(k, i){ - var e = elementExtractor(k, i); - if (totals[intersectionExtractor(k, i)]['total'] == 0) { - if (Array.isArray(e)) { - totals[intersectionExtractor(k, i)]['total']+= e.length; - totals[intersectionExtractor(k, i)]['values'] = e; - } else { - totals[intersectionExtractor(k, i)]['total']+= e; - } + var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); + var xAxisObjSel = xAxisSel.select('.'+utils$1.str.hypenate(xAxis.namespace(),'object-container')); + var yAxisObjSel = yAxisSel.select('.'+utils$1.str.hypenate(yAxis.namespace(),'object-container')); + chartObjSel.attr('transform', 'translate('+0+','+0+')'); + xAxisObjSel.attr('transform', 'translate('+0+','+0+')'); + yAxisObjSel.attr('transform', 'translate('+0+','+0+')'); + }; - } - }); - return totals - } + return zoom +} - function setTotals(){ - var totals = {}; - // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) +/******************************************************************************* - setValues.map(function(k, i){ totals[k] = {'total': 0}; }); +** ** +** ** +** PLOTZOOM ** +** ** +** ** +*******************************************************************************/ +/** + * Creates an plotZoom instance, which can handle drag and scroll events + * @constructor plotZoom + * @param {function} chart a function instance of one of the d3sm plots (e.g. bar, boxwhisker, bubbleHeatmap, violin, etc) + * @param {axis} xAxis the axis instance responsible for the x axis + * @param {axis} yAxis the axis instance responsible for the y axis + * @namespace plotZoom + * @returns {function} zoom + */ +function plotZoom( chart, xAxis, yAxis ) { + var + /** + * The event on which to fire + * (see {@link plotZoom#eventType}) + * @param {string} eventType which event it should handle. Currently supports scroll and wheel + * @memberof plotZoom# + * @property + */ + eventType, + /** + * A scaling factor for the wheel "speed" + * (see {@link plotZoom#wheelSpeed}) + * @param {number} [wheelSpeed=20] scales the wheel translation by wheelSpeed + * @memberof plotZoom# + * @property + */ + wheelSpeed = 20, + /** + * The orientation in which to allow scrolling: 'horizontal', 'vertical', or '2D' + * (see {@link plotZoom#orient}) + * @param {string} [orient=chart.orient() || 'horizontal'] + * @memberof plotZoom# + * @property + */ + orient = (chart.orient == undefined) ? 'horizontal' : chart.orient(), + /** + * The max distance allowed to scroll in the x direction + * (see {@link plotZoom#xLock}) + * @param {number} [xLock=chart.spaceX()] ideally chart.overflowQ() == true and this value is the + * bounding rect across all elements in the chart minus the space in which to show. + * @memberof plotZoom# + * @property + */ + xLock=chart.spaceX(), + /** + * The max distance allowed to scroll in the y direction + * (see {@link plotZoom#yLock}) + * @param {number} [yLock=chart.spaceY()] ideally chart.overflowQ() == true and this value is the + * bounding rect across all elements in the chart minus the space in which to show. + * @memberof plotZoom# + * @property + */ + yLock=chart.spaceY(), - cellKeys.map(function(k, i){ - var e = elementExtractor(k, i); - if (Array.isArray(e)) { - totals[setExtractor(k, i)]['total']+= e.length; - } else { - totals[setExtractor(k, i)]['total']+= e; - } - }); - return totals - } + chartSel = chart.selection(), + xAxisSel = xAxis.selection(), + yAxisSel = yAxis.selection(), + svg = d3.select(chartSel.thisSVG()); - upset.intersectionTotals = intersectionTotals; - upset.setTotals = setTotals; - return upset -} + /** + * Gets or sets the event type in which to respond + * (see {@link plotZoom#eventType}) + * @param {string} [_=none] should be 'drag' or 'wheel' + * @returns {zoom | string} + * @memberof plotZoom + * @property + * by default plotZoom=undefined + */ + zoom.eventType = function(_) { return arguments.length ? (eventType = _, zoom) : eventType; }; + /** + * Gets or sets the wheel speed in which to scale the wheel scroll transform + * (see {@link plotZoom#wheelSpeed}) + * @param {number} [_=none] + * @returns {zoom | number} + * @memberof plotZoom + * @property + * by default wheelSpeed=20 + */ + zoom.wheelSpeed = function(_) { return arguments.length ? (wheelSpeed = _, zoom) : wheelSpeed; }; + /** + * Gets or sets the orientation in which items are manipulated + * (see {@link plotZoom#orient}) + * @param {string} [_=none] should be horizontal, vertical, or 2D + * @returns {zoom | string} + * @memberof plotZoom + * @property + * by default orient=chart.orient() || 'horizontal' + */ + zoom.orient = function(_) { return arguments.length ? (orient = _, zoom) : orient; }; -function filterTable(selection) { - var - data, - namespace = 'd3sm-filter-table', - fieldFunction = function(record, columnKey, recordValue) {return recordValue}; + /** + * Gets or sets the max distance in which to scroll X + * (see {@link plotZoom#xLock}) + * @param {number} [_=none] should be a positive value + * @returns {zoom | number} + * @memberof plotZoom + * @property + * by default xLock=chart.spaceX() + */ + zoom.xLock = function(_) { return arguments.length ? (xLock = _, zoom) : xLock; }; + /** + * Gets or sets the max distance in which to scroll Y + * (see {@link plotZoom#yLock}) + * @param {number} [_=none] should be a positive value + * @returns {zoom | number} + * @memberof plotZoom + * @property + * by default yLock=chart.spaceY() + */ + zoom.yLock = function(_) { return arguments.length ? (yLock = _, zoom) : yLock; }; - filterTable.data = function(_) { return arguments.length ? (data = _, filterTable) : data}; - filterTable.namespace = function(_) { return arguments.length ? (namespace = _, filterTable) : namespace}; - filterTable.fieldFunction = function(_) { return arguments.length ? (fieldFunction = _, filterTable) : fieldFunction}; - function filterTable () { + function setLocks() { + var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); + var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); + var cos = chartObjSel.attr('transform', 'translate(0,0)'); + xLock = chartSel.node().getBBox().width - chart.spaceX() * .9; + yLock = chartSel.node().getBBox().height - chart.spaceY() * .9; + cos.attr('transform', 'translate('+chartObjTrans[0]+','+chartObjTrans[1]+')'); + utils$1.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); + } - var - container = safeSelect(selection, 'div', 'filter-table').classed(hypenate(namespace,'container'),true), - inputGroup = safeSelect(container, 'div', 'filter-input-group').classed('input-group',true), - inputPrepend = safeSelect(inputGroup, 'div', 'input-group-prepend'), - inputPrependSpan = safeSelect(inputPrepend, 'span', 'input-group-text').classed('search-button', true), - inputPrependSpanIcon = safeSelect(inputPrependSpan,'i','fa fa-search'), + /** + * Sets the x and y locks (how far one can scroll) + * (see {@link plotZoom#xLock} and {@link plotZoom#yLock}) + * @function plotZoom.setLocks + * @returns {undefined} + * @memberof plotZoom + * @property + */ + zoom.setLocks = setLocks; - input = safeSelect(inputGroup, 'input', 'form-control').attr('placeholder', 'filter').attr('type', 'text'), - inputAppend = safeSelect(inputGroup, 'div', 'input-group-append'), - inputAppendButton = safeSelect(inputAppend, 'a', 'close-button').classed('btn btn-outline-secondary', true), - inputAppendButtonIcon = safeSelect(inputAppendButton, 'i', 'fa fa-close'), + function zoom() { + setLocks(); + var horizontalQ, verticalQ; + if (orient == '2D') {horizontalQ = true; verticalQ = true;} + if (orient == 'horizontal') {horizontalQ = true; verticalQ = false;} + if (orient == 'vertical') {verticalQ = true; horizontalQ = false;} - closeButton = inputAppendButton, + // capture transform event + var transform = d3.event.transform; - rTable = safeSelect(container, 'div', 'table-responsive'), - table = safeSelect(rTable, 'table', 'table') - .classed('table-hover', true) - .classed('table-bordered', true) - .classed("table-striped", true), - tHead = safeSelect(table, 'thead') - .classed("thead-dark", true), - header = safeSelect(tHead, 'tr'), - tBody = safeSelect(table, 'tbody'); + var chartBox = chartSel.node().getBBox(); + var xAxisBox = xAxisSel.node().getBBox(); + var yAxisBox = xAxisSel.node().getBBox(); + var chartWidth = chartBox.width - chartBox.x; + var chartHeight = chartBox.height - chartBox.y; + var xAxisWidth = xAxisBox.width;// - xAxisBox.x + var xAxisHeight = xAxisBox.height;// - xAxisBox.y + var yAxisWidth = yAxisBox.width;// - yAxisBox.x + var yAxisHeight = yAxisBox.height;// -yAxisBox.y - var - rows = d3.keys(data), - cols = d3.keys(data[rows[0]]); + // enable wheel event + if (eventType == "wheel") { + var e = d3.event; + // prevent page scrolling + e.preventDefault(); + // event delta is very very slow, so speed it up with wheel speed + var w = d3.event.deltaY * wheelSpeed; + var shiftQ = d3.event.shiftKey; - var th = makeColumns(header, cols); - var tr = makeRows(tBody, rows); - var tr = populateRecords(tr, cols); + // d3 has no way to make custom transform, so make an object and add + // the necessary functions to make wheel event compatible with drag events + if (orient == '2D') { + transform = shiftQ ? {k: 1, x: w, y: 0} : {k: 1, x: 0, y: w}; + } else { + transform = horizontalQ ? {k: 1, x: w, y: 0} : {k: 1, x: 0, y: w}; + } + // the * -1 inverts the direction + transform.applyX = function(x) { return x * this.k + this.x * -1; }; + transform.applyY = function(y) { return y * this.k + this.y * -1; }; + } - input.on('input', function(d, i){ - var - val = input.property('value'), - reg = new RegExp(val, 'gi'), - use; - if (val == '') {use = rows;} else { - use = []; - rows.map(function(r, i){ - var row = data[r]; - var fieldJoin = cols.map(function(c, j){ - return fieldFunction(row, c, row[c]) - // return row[c] - }).join(''); - var match = fieldJoin.match(reg); - if (match == null || match.join('') == '') ; - else { use.push(r); } - }); - } + var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); + var xAxisObjSel = xAxisSel.select('.'+utils$1.str.hypenate(xAxis.namespace(),'object-container')); + var yAxisObjSel = yAxisSel.select('.'+utils$1.str.hypenate(yAxis.namespace(),'object-container')); - tr = makeRows(tBody, use); - tr = populateRecords(tr, cols); - }); + // xLock = chartSel.node().getBBox().width - chart.spaceX() - chartSel.node().getBBox().x + // yLock = chartSel.node().getBBox().height - chart.spaceY() + // console.table({'xLock':xLock, "yLock":yLock}) + // bhm.selection().node().getBBox().width - bhm.spaceX() - closeButton.on('click', function(d, i){ - input.property('value', '').dispatch('input'); - }); - } + var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); + var xAxisObjTrans = utils$1.sel.getTranslation(xAxisObjSel.attr('transform')); + var yAxisObjTrans = utils$1.sel.getTranslation(yAxisObjSel.attr('transform')); - function makeColumns(header, cols) { - var th = header.selectAll('th'); - th = th.data(cols); - th.exit().remove(); - th = th.merge(th.enter().append('th')); - th.attr('scope', 'col').text(function(d, i){return d}); - return th - } - function makeRows(body, rows) { - var tr = body.selectAll('tr'); - tr = tr.data(rows); - tr.exit().remove(); - tr = tr.merge(tr.enter().append('tr')); - return tr - } + var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0; + if (horizontalQ) {x = x < -xLock ? (transform.x = 0, -xLock) : (transform.x = 0, Math.min(x, 0)); } - function populateRecords(rows, cols) { - rows.each(function(r, i){ - var record = data[r], - t = d3.select(this); + var y = verticalQ ? transform.applyY(chartObjTrans[1]) : 0; + if (verticalQ) {y = y < -yLock ? (transform.y = 0, -yLock): (transform.y = 0, Math.min(y, 0));} - var fields = t.selectAll('td'); - fields = fields.data(cols); - fields.exit().remove(); - fields = fields.merge(fields.enter().append('td')); + chartObjSel.attr('transform', 'translate('+x+','+y+')'); + if (horizontalQ) { xAxisObjSel.attr('transform', 'translate('+x+','+0+')'); } + if (verticalQ) { yAxisObjSel.attr('transform', 'translate('+0+','+y+')'); } - fields.attr('scope', function(f, j){ return j == 0 }) - .html(function(f, j){ return fieldFunction(record, f, record[f]) }); + // var lasso = svg.select(".lasso-container") + // if (!lasso.empty()) { + // lasso.attr('transform', 'translate('+x+','+y+')') + // } - }); - return rows } + zoom.reset = function() { + var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); + var xAxisObjSel = xAxisSel.select('.'+utils$1.str.hypenate(xAxis.namespace(),'object-container')); + var yAxisObjSel = yAxisSel.select('.'+utils$1.str.hypenate(yAxis.namespace(),'object-container')); + chartObjSel.attr('transform', 'translate('+0+','+0+')'); + xAxisObjSel.attr('transform', 'translate('+0+','+0+')'); + yAxisObjSel.attr('transform', 'translate('+0+','+0+')'); + }; - return filterTable + return zoom } -// Import styles (automatically inject into ). - -// /** @module d3sm */ -var d3sm$1 = {}; -d3sm$1.axis = axis; -d3sm$1.bar = bar; -d3sm$1.bubbleHeatmap = bubbleHeatmap; -d3sm$1.heatmap = heatmap; -d3sm$1.boxwhisker = boxwhisker; -d3sm$1.colorFunction = colorFunction; -d3sm$1.datatoggle = datatoggle; -d3sm$1.groupingSpacer = groupingSpacer; -d3sm$1.tooltip = tooltip; -d3sm$1.scatter = scatter; -d3sm$1.plotZoom = plotZoom; -d3sm$1.multiPlotZoom = multiPlotZoom; -d3sm$1.violin = violin; -d3sm$1.numericLegend = numericLegend; -d3sm$1.categoricLegend = categoricLegend; -d3sm$1.lasso = lasso; -d3sm$1.lassoWidget = lassoWidget; -d3sm$1.selectFilter = selectFilter; -d3sm$1.upset = upset; -d3sm$1.filterTable = filterTable; - -d3sm$1.uniqueElements = uniqueElements; -d3sm$1.getTranslation = getTranslation; -d3sm$1.modifyHexidecimalColorLuminance = modifyHexidecimalColorLuminance; -d3sm$1.tickRange = tickRange; -d3sm$1.quartiles = quartiles; -d3sm$1.extractViolinValues = extractViolinValues; -d3sm$1.hypenate = hypenate; -d3sm$1.round = round; -d3sm$1.getContainingSVG = getContainingSVG; -d3sm$1.interpolateColors = interpolateColors; -d3sm$1.truncateText = truncateText; -d3sm$1.safeSelect = safeSelect; - -d3sm$1.whichBin = whichBin; -d3sm$1.unique = unique; -d3sm$1.flatten = flatten; - -d3sm$1.setupStandardChartContainers = setupStandardChartContainers; -d3sm$1.log = log; -d3sm$1.warn = warn; -d3sm$1.info = info; -d3sm$1.error = error; -d3sm$1.consoleGroup = consoleGroup; -d3sm$1.consoleGroupEnd = consoleGroupEnd; -d3sm$1.resizeDebounce = resizeDebounce; - -d3sm$1.debugQ = false; - - - -// Import a logger for easier debugging -// import debug from 'debug'; -// const log = debug('app:log'); - -// The logger should only be disabled if we're not in production. -// if (ENV !== 'production') { -// // Enable the logger. -// debug.enable('*'); -// log('Logging is enabled!'); -// -// // Enable LiveReload -// document.write( -// ' + - - + + diff --git a/needs-to-be-bundled/distribution.js b/needs-to-be-bundled/distribution.js deleted file mode 100644 index 781b50d..0000000 --- a/needs-to-be-bundled/distribution.js +++ /dev/null @@ -1,241 +0,0 @@ -function distribution() { - // variables that need to be set externally - var container, - drawingSpace, - colors = ["#2c7bb6", "#00a6ca", "#00ccbc", "#90eb9d", "#ffff8c", "#f9d057", "#f29e2e", "#e76818", "#d7191c"], - data, - vScale = d3.scaleLinear(), // scale for values - nScale = d3.scaleLinear(), // number of elements - vKey, - cKey, - cOpacity = 0.2, - strokeWidth = 2, - cScale = d3.scaleSequential(interpolateColors.apply(this, colors)), - histogram, - curve = d3.curveBasis, - distributionContainer, - distributionPath, - rotation='up', - rangePad = 2 - - // variables set internally - var pointsNeeded, currentNumberOfPoints, - values, yValues, rValues, cValues, - dataExtent, fatal, - verbose = true, - animationTime = 1000, - svg - - var hexademicalOpacity = { - 1.0: "FF", 0.9: 'E6', 0.8: 'CC', - 0.7: "B3", 0.6: "99", 0.5: "80", - 0.4: "66", 0.3: "4D", 0.2: "33", - 0.1: "1A", 0.0: "00" - } - - - - - function plot(){ - fatal = warn(container, 'container', undefined, true, verbose) - if ( fatal ) {return} - fatal = warn(data, 'data', undefined, true, verbose) - if ( fatal ) {return} - fatal = warn(vKey, 'vKey', undefined, true, verbose) - if ( fatal ) {return} - fatal = warn(drawingSpace, 'drawingSpace', undefined, true, verbose) - if ( fatal ) {return} - - - svg = d3.select(container.thisSVG()) - - vValues = data.map( function(d, i) { return parseFloat( d[vKey] ) } ) - histogram = d3.histogram().value( function( d ){ return d[vKey] } )(data) - - nValues = histogram.map( function(bin, i) { return bin.length } ) - - - dataExtent = { - v: { - min: d3.min( vValues ), - max: d3.max( vValues ) - }, - n: { - min: d3.min( nValues ), - max: d3.max( nValues ) - } - } - - vScale.domain([ dataExtent.v.min -rangePad, dataExtent.v.max+rangePad ]) - .range([ 0, drawingSpace.x ]) - - nScale.domain([ 0, dataExtent.n.max ]) - .range([ drawingSpace.y, 0 ]) - - - if (cKey != undefined) { - cValues = data.map( function(d, i) { return parseFloat( d[cKey] ) } ) - cScale.domain([ d3.min(cValues), d3.max(cValues) ]) - } - - - - - histoMin = [] - histoMin.x0 = dataExtent.v.min - histoMin.x1 = dataExtent.v.min - - histoMax = [] - histoMax.x0 = dataExtent.v.max - histoMax.x1 = dataExtent.v.max - - - - line = d3.line() - .x( function(bin, i) { return vScale( d3.median( [bin.x0, bin.x1] ) ) }) - .y( function(bin, i) { return nScale( bin.length ) } ) - .curve(curve) - - - - - distributionContainer = container.select('g.distribution-container') - if (distributionContainer.empty()) { - distributionContainer = container.append('g') - .attr('class','distribution-container') - .data([histogram]) - - distributionContainer.append('path') - .attr('class', 'distribution-path') - } - - distributionPath = distributionContainer.select('path.distribution-path') - distributionPath.attr('d', function(d, i) { - var hist = [histoMin].concat(d).concat([histoMax]) - return line(hist) - }) - .attr('fill', function(d, i){ - if (cKey == undefined) { - var colorI = i % colors.length - var c = colors[colorI] - if (c.includes("#") && cOpacity != undefined) { - c += hexademicalOpacity[cOpacity] - } - return c - } else { - var c = cScale(d[cKey]) - if (c.includes("#") && cOpacity != undefined) { - c += hexademicalOpacity[cOpacity] - } else { - [r, g, b] = c.slice(3).split(',') - b = b.split(')').join('') - r = r.split('(').join('') - return 'rgb('+r+','+g+','+b+','+cOpacity+')' - } - return c - } - }) - .attr('stroke-width', strokeWidth) - .attr('stroke', function(d, i){ - if (cKey == undefined) { - var colorI = i % colors.length - var c = colors[colorI] - return c - } else { - return cScale(d[cKey]) - } - }) - - distributionContainer.attr('transform', function(d, i){ - var box = container.node().getBBox() - - var rx, ry, rr, tx, ty - - if (rotation == 'right') { - rr = 90 - rx = 0 - ry = 0 - tx = drawingSpace.y - ty = drawingSpace.x - } else if (rotation == 'left') { - rr = -90 - rx = 0 - ry = 0 - tx = 0 - ty = drawingSpace.x - } else if (rotation == 'down') { - rr = 180 - rx = drawingSpace.x / 2 - ry = drawingSpace.y / 2 - tx = - drawingSpace.x - ty = 0 - } else { - rr = 0 - rx = drawingSpace.x / 2 - ry = drawingSpace.y / 2 - tx = 0 - ty = 0 - } - - - var scale = 'scale(-1, 1)' - var rot = 'rotate('+rr+','+rx+','+ry+')' - var trans = 'translate('+tx+','+ty+')' - - if (rotation == 'down') { - return trans + rot + scale - } - if (rotation == 'right') { - return trans + rot + scale - } - return trans+rot - // return Math.abs(rotation / 180) >= 1 ? - // 'rotate('+rotation+',' + box.width / 2 + ',' + box.height + ')' - // : - // 'rotate('+rotation+',' + box.width + ',' + box.height / 2 + ')translate(0,'+-box.width+') ' - }) - - - - } - - - function setupSettersAndGetters() { - var closuredVariables = [ - 'container', - 'drawingSpace', - 'data', - 'colors', - 'vKey', 'cKey', - 'vScale', 'nScale', - 'vValues', 'nValues', - 'values', - 'cScale', - 'cOpacity', - 'strokeWidth', - 'xValues','yValues','cValues', - 'dataExtent', 'fatal', - 'verbose', 'animationTime', - 'svg', 'histogram', - 'rotation', - 'rangePad' - ] - - - for (var i = 0; i < closuredVariables.length; i++) { - var currentClosureVariable = closuredVariables[i] - var toEvaluateString = " \ - plot." + currentClosureVariable + " = function(value){ \ - if(!arguments.length) return " + currentClosureVariable + "; \ - " + currentClosureVariable + " = value; \ - return plot; \ - }; \ - " - eval(toEvaluateString) - } - } - - - setupSettersAndGetters() - return plot; -} diff --git a/needs-to-be-bundled/graph.js b/needs-to-be-bundled/graph.js deleted file mode 100644 index 4bbe410..0000000 --- a/needs-to-be-bundled/graph.js +++ /dev/null @@ -1,806 +0,0 @@ -function plot() { - var id, // svg ID - to='html', // scale svg to - x, // svg width either in pixels or percent - y, // svg height either in pixels or percetn - plot // plot type - - // internal variables - var svg - - - function configureSVG() { - if (to == undefined) { - svg.style("width", x + 'px') - svg.style("height", y + 'px') - return - } - - var el = d3.select(to).node() // element to scale to - var rect = el.getBoundingClientRect() // boundaries of the element - var css = getComputedStyle(el) // rendered css styles - - - var w = rect.width - parseFloat(css.paddingLeft) - parseFloat(css.paddingRight) // remove padding - var h = rect.height - parseFloat(css.paddingTop) - parseFloat(css.paddingBottom) - - var width, height - - if (String(x).includes('%')) { width = w * parseFloat(x) / 100 } - else if (x <= 1) { width = w * x} - else { width = x} - - if (String(y).includes('%')) { height = h * parseFloat(y) / 100 } - else if (x <= 1) { height = h * y} - else { height = y} - - - svg.style("width", width + 'px') - svg.style("height", height + 'px') - return - } - - function make() { - svg = d3.select('[id='+id+']') - configureSVG() - - plot.width(parseFloat(svg.style('width'))) - plot.height(parseFloat(svg.style('height'))) - - plot() - } - - function setupSettersAndGetters() { - var closuredVariables = [ - 'id', - 'to', - 'x', - 'y', - 'plot' - ] - - for (var i = 0; i < closuredVariables.length; i++) { - var currentClosureVariable = closuredVariables[i] - var toEvaluateString = " \ - make." + currentClosureVariable + " = function(value){ \ - if(!arguments.length) return " + currentClosureVariable + "; \ - " + currentClosureVariable + " = value; \ - return make; \ - }; \ - " - eval(toEvaluateString) - } - } - setupSettersAndGetters() - return make; - -} - - -function sugiyama() { - /* GRAPH DATA VARIABLES*/ - var vertexSet, - edgeSet, - dummySet, - realSet, - layering, - vertexKeys, - edgeKeys, - vertexMeta, - edgeMeta, - pathwayMeta, - pubmedMeta, - datasourceMeta - - - - /* GRAPH DRAWING VARIABLES*/ - var container, - vertexContainer, - vertexGroup, - edgeContainer, - edgeGroup, - width, - height, - - layout, - - xScale = d3.scaleLinear(), - yScale = d3.scaleLinear(), - - rangePad = 2, - - xCoordinates, - yCoordinates, - - dataExtent, - - zoom = d3.zoom() - .scaleExtent([0.25, 1.75]) - .on("zoom", containerZoom), - panRatio = 0.1 - - function containerZoom(){ - container.attr('transform', d3.event.transform) - } - - function containerZoomReset(){ - svg.call(zoom.transform, d3.zoomIdentity) - } - - /* GRAPH STYLE VARIABLES */ - var vertexRadius = 15, - vertexFill = '#333333', // vertex color - vertexStroke = '#333333', // vertex stroke - vertexStrokeWidth = 1, // vertex stroke - - textSize=7.5, // font size for vertex labels - textWeight = "900" // font weight for vertex labels - textStroke = '#333333', // stroke of vertex font (should match vertex color) - textStrokeWidth = 0.1, // stroke size of vertex font - textFill = 'white', // color of vertex font - - edgeLine = d3.line() - .x(function(d) { return d.x; }) - .y(function(d) { return d.y; }) - .curve(d3.curveMonotoneX), - // .curve(d3.curveBasis), - // .curve(d3.curveLinear), - - edgeStroke = "#333333", // color of edges - edgeStrokeMin = 2, - edgeStrokeMax = 10, - edgeStrokeScale = d3.scaleLinear().range([edgeStrokeMin, edgeStrokeMax]), - edgeStrokeWidth = edgeWidth, - - hoverOpacityDecrease = 0.2, - vertexIncreasedExpression = 'red', - vertexDecreasedExpression = 'blue' - - - function edgeWidth(edgeKey) { - var e = edgeSet[edgeKey] - if (edgeSet[edgeKey].meta) { - return edgeSet[edgeKey].meta.pubmed_ids.length - } else { - return edgeStrokeMin - } - } - - function vertexFillColor(vertexKey) { - var v = vertexSet[vertexKey] - - if (v.class.includes('dummy')) { return vertexFill } - - - if (v.expression) { - return (v.expression > 0) ? vertexIncreasedExpression : vertexDecreasedExpression - } else { - return vertexFill - } - } - - - function predraw() { - vertexKeys = d3.keys( vertexSet ) - edgeKeys = d3.keys( edgeSet ) - - xCoordinates = vertexKeys.map( function(d, i) { return vertexSet[d].coordinates.x }) - yCoordinates = vertexKeys.map( function(d, i) { return vertexSet[d].coordinates.y }) - - numPubs = edgeKeys.map( function (d, i) {if ( edgeSet[d].meta == undefined) {return edgeStrokeMin } return edgeSet[d].meta.pubmed_ids.length } ) - - dataExtent = { - x: { - min: d3.min( xCoordinates ), - max: d3.max( xCoordinates ) - }, - y: { - min: d3.min( yCoordinates ), - max: d3.max( yCoordinates ) - }, - pubs: { - min: d3.min( numPubs ), - max: d3.max( numPubs ) - } - - } - - - xScale.domain([ dataExtent.x.min - rangePad, dataExtent.x.max + rangePad ]) - .range([ 0 + vertexRadius, width - vertexRadius ]) - - yScale.domain([ dataExtent.y.min - rangePad, dataExtent.y.max + rangePad ]) - .range([ 0 + vertexRadius, height - vertexRadius ]) - - edgeStrokeScale.domain([ dataExtent.pubs.min, dataExtent.pubs.max ]) - - edgeLine.x(function(d) { return xScale(d.x); }) - .y(function(d) { return yScale(d.y); }) - - zoom.translateExtent([[width * panRatio * -1, height * panRatio * -1], [width * (1+panRatio), height * (1+panRatio)]]) - d3.select(container.thisSVG()).call(zoom) - - vertexContainer = container.select(".vertex-container") - vertexGroup = container.selectAll(".vertex-group") - - edgeContainer = container.select(".edge-container") - edgeGroup = container.selectAll(".edge-group") - - realSet = new Set([].concat.apply([],d3.keys(edgeSet).map(function(e,i){return [edgeSet[e].source, edgeSet[e].target]}))) - dummySet = new Set( d3.keys(vertexSet).map(function(v,i) {if (!realSet.has(v)) {return v}})) - } - - - function _vertex_setup() { - - if (vertexContainer.empty()) { - vertexContainer = container.append("g").attr("class", "vertex-container") - } else { - vertexContainer = container.select('.vertex-container') - } - - var neededVertices = vertexKeys.length - var currentVertices = vertexGroup.size() - if (currentVertices < neededVertices) { - vertexGroup = vertexContainer.selectAll(".vertex-group") - .data(vertexKeys).enter() - .append("g").attr("class", "vertex-group") - .attr("id", function(d, i){return d}) - - vertexGroup.each(function(d, i){ - var thisV = d3.select(this) - if (!dummySet.has(d)) { - thisV.append("circle").attr('class', 'vertex-shape') - thisV.append("text").attr('class', 'vertex-label') - } else { - thisV.classed("dummy", true) - thisV.append("rect").attr('class', 'vertex-shape') - } - }) - } else if (currentVertices > neededVertices) { - vertexGroup = vertexContainer.selectAll(".vertex-group") - .data(vertexKeys).exit().remove() - } else { - vertexGroup = vertexContainer.selectAll(".vertex-group").data(vertexKeys) - vertexGroup.each(function(d, i){ - var thisV = d3.select(this) - thisV.attr("id", function(d, i){return d}) - // console.log(d, dummySet.has(d)) - if (!dummySet.has(d)) { // real vertex - var circ, rect, text - circ = thisV.select('circle') - rect = thisV.select('rect') - text = thisV.select('text') - - if ( !rect.empty() ) { rect.remove() } - if ( circ.empty() ) { thisV.append('circle').attr('class', 'vertex-shape') } - text.attr('class', 'vertex-label') - - thisV.classed("dummy", false) - } else { // dummy vertex - var circ, rect, text - circ = thisV.select('circle') - rect = thisV.select('rect') - text = thisV.select('text') - - if ( rect.empty() ) { thisV.append('rect').attr('class', 'vertex-shape') } - if ( !circ.empty() ) { circ.remove() } - - thisV.classed("dummy", true) - } - }) - } - - vertexGroup = vertexContainer.selectAll(".vertex-group").data(vertexKeys) - - - - vertexGroup.on("mouseover", vertexMouseover) - vertexGroup.on("mouseout", vertexMouseout) - vertexGroup.call(d3.drag() - .on("start", vertexDragStart) - .on("drag", vertexDrag) - .on("end", vertexDragStop)); - - } - - - - function _vertex_style() { - - vertexGroup.attr("transform", function(d, i) { - var x = xScale(vertexSet[d].coordinates.x) - var y = yScale(vertexSet[d].coordinates.y) - var t = "translate("+x+","+y+")" - return t - }).each( function(d, i) { - var thisV = d3.select(this) - if (!dummySet.has(d)) { - thisV.select("text") - .text(d) - .attr("text-anchor", "middle") - .style("font-size",textSize) - .style("stroke", textStroke) - .style("stroke-width", textStrokeWidth) - .style("fill", textFill) - .style("font-weight", textWeight) - .style("cursor", "default") - .style("-webkit-user-select", "none") - .style("-moz-user-select", "none") - .style("-ms-user-select", "none") - .style("user-select", "none") - .attr("transform", function(d, i) { - var x = 0 - var y = textSize / 4 + textSize / 8 - var t = "translate("+x+","+y+")" - return t - }) - - thisV.select("circle") - .attr("stroke", vertexFillColor(d)) - .attr("fill", vertexFillColor(d)) - .attr("r", vertexRadius) - - } else { - thisV.select("rect") - .attr("stroke", vertexFillColor(d)) - .attr("fill", vertexFillColor(d)) - .attr("width", vertexRadius * 2) - .attr("height", vertexRadius * 2) - .attr("transform", function(d, i) { - var x = -vertexRadius - var y = -vertexRadius - var t = "translate("+x+","+y+")" - return t - }) - .style("opacity", 0) - } - }) - } - - function drawVertices() { - _vertex_setup() - _vertex_style() - } - - - function _edge_setup() { - - if (edgeContainer.empty()) { edgeContainer = container.append("g").attr("class", "edge-container") } - else { edgeContainer = container.select("g.edge-container")} - - var neededEdges = edgeKeys.length - var currentEdges = edgeContainer.size() - - if (currentEdges < neededEdges) { - edgeGroup = edgeContainer.selectAll(".edge-group") - .data(edgeKeys).enter() - .append("g").attr("class", "edge-group") - .attr("id", function(d, i){return d}) - - edgeGroup.each(function(d, i){ - thisE = d3.select(this) - thisE.append("path") - }) - } else if (neededEdges < currentEdges) { - edgeGroup = edgeContainer.selectAll(".edge-group") - .data(edgeKeys) - .exit().remove() - } else { - edgeGroup = edgeContainer.selectAll(".edge-group") - .data(edgeKeys) - } - - edgeGroup = edgeContainer.selectAll(".edge-group").data(edgeKeys) - - } - - function _edge_style() { - edgeGroup.each(function(d, i) { - var thisE = d3.select(this) - var coords = edgeSet[d].path.map(function(e, j) { return vertexSet[e].coordinates }) - thisE.select("path") - .attr("fill", "none") - .attr("stroke-width", edgeStrokeScale(edgeStrokeWidth(d))) - .attr("stroke", edgeStroke) - .attr("fill", "none") - .attr("d", edgeLine(coords)) - .on('mouseover', edgeMouseover) - .on('mouseout', edgeMouseout) - // .attr("stroke-dasharray", function(){ - // // console.log(d, d3.select(this)) - // return d3.select(this).node().getTotalLength() * 0.8 - // }) - - }) - edgeContainer.lower() - } - - function drawEdges() { - _edge_setup() - _edge_style() - } - - function draw() { - predraw() - drawVertices() - drawEdges() - } - - - function vertexMouseover(d, i) { - - var thisV = d3.select(this) - - if ( thisV.classed("dummy") ) { - thisV.select("rect").style("opacity","0.5") - return - } - - var adj = vertexSet[d].predecessors.concat(vertexSet[d].successors).concat([d]) - // console.log(d, adj) - - vertexKeys.map(function(dd, j){ - var cV = d3.select("[id='"+dd+"']") - if (adj.indexOf(dd) == -1) { - cV.style("opacity", hoverOpacityDecrease) - } - }) - - edgeKeys.map(function(dd, j){ - var cE = d3.select("[id='"+dd+"']") - if (edgeSet[dd].source != d && edgeSet[dd].target != d) { - cE.style("opacity", hoverOpacityDecrease) - } - }) - - - var ttip = d3.select('#graph-tooltip') - if (ttip.empty()) { ttip = d3.select("html").append('div').attr('id', 'graph-tooltip') } - var [div, title, body, close, txt, move] = setupTooltipCard( ttip ) - - if (!body.selectAll('*').empty()) {body.selectAll('*').remove()} - body.html('GeneCards: ') - - var bodyA = body.select('a') - if (bodyA.empty()) {bodyA = body.append('a'); bodyA.append('strong')} - bodyA.attr('href', 'http://www.genecards.org/cgi-bin/carddisp.pl?gene='+d) - .html(d).attr('class', 'gene-card').attr("target", '_blank') - - txt.html(d) - } - - function vertexMouseout(d, i) { - - var thisV = d3.select(this) - - d3.keys(vertexSet).map(function(dd, j){ - var cV = d3.select("[id='"+dd+"']") - cV.style("opacity", 1) - }) - - if ( thisV.classed("dummy") ) { - thisV.select("rect").style("opacity","0") - } - - d3.keys(edgeSet).map(function(dd, j){ - var cE = d3.select("[id='"+dd+"']") - cE.style("opacity", 1) - }) - - } - function vertexDragStart(){} - function vertexDrag(d, i){ - var curV = d3.select(this) - var m = d3.mouse(container.node()) - - vertexSet[d].coordinates.x = xScale.invert(m[0]) - vertexSet[d].coordinates.y = yScale.invert(m[1]) - - - curV.raise() - .attr("transform",function(d, i) { - x = m[0] - y = m[1] - trans = "translate("+x+","+y+")" - return trans - }) - - drawEdges() - } - function vertexDragStop(){} - - function edgeMouseout(d, i) { - var ttip = d3.select('#graph-tooltip') - // if (!ttip.empty()) { ttip.remove() } - } - - - function makeCloseButton(closeSelection, containerSelection) { - if (closeSelection.empty()) {closeSelection = containerSelection.append('button') - .attr('type', 'button') - .attr('class', 'close align-middle tt-close') - .attr('data-dismiss', 'alert') - .on('click', function(){d3.select("#graph-tooltip").transition().duration(300).style('opacity', 0).remove()}) - .append('i').attr('class', "fa fa-times fa-lg") - .style('color', '#000') - } - } - function makeMoveButton(moveSelection, containerSelection, toMoveSelection) { - if (moveSelection.empty()) { - moveSelection = containerSelection.append('button') - .attr('class', 'move close') - .call(d3.drag().on('drag', function(){ - var m = d3.mouse(moveSelection.node()) - toMoveSelection.style('left', (parseFloat(toMoveSelection.style('left'))+m[0])+'px') - .style('top', (parseFloat(toMoveSelection.style('top'))+m[1])+'px') - })) - .style('float','right') - .style('padding','none') - .append('i').attr('class','fa fa-arrows-alt fa-sm') - .style('color', '#000') - .style('margin-right', '3px') - } - } - function movePercentInsideHtml(div, mouse, percent) { - var html = d3.select('html').node().getBoundingClientRect() - var left = Math.min( mouse[0], html.width - (html.width * percent) ) - div.transition().duration(200).style('left', left+'px').style('top', mouse[1]+'px') - } - - function setupTooltipCard( tooltip ) { - var div = tooltip.select('div.card') - var m = d3.mouse.absolute() - - if (div.empty()) { div = tooltip.append('div').attr('class', 'card col-3 px-0').style('position', 'absolute').style('left', m[0]+'px').style('top', m[1]+'px') } - movePercentInsideHtml(div, m, 0.25) - - var title, body, footer, datasources, interactions, pubmedids, pathways, close, txt, move - title = div.select('div.card-header') - body = div.select('div.card-body') - close = title.select('button.tt-close') - txt = title.select('h6') - move = title.select('button.move') - - if (title.empty()) {title = div.append('div').attr('class','card-header card-title')} - if (txt.empty()) {txt = title.append('h6').style('display', 'inline-block')} - - makeCloseButton(close, title) - makeMoveButton(move, title, div) - - if (body.empty()) {body = div.append('div').attr('class','card-body')} - return [div, title, body, close, txt, move] - } - - function edgeMouseover(d, i) { - var thisE = d3.select(this) - if (edgeMeta == undefined) { return } - - var ttip = d3.select('#graph-tooltip') - if (ttip.empty()) { ttip = d3.select("html").append('div').attr('id', 'graph-tooltip') } - - var [div, title, body, close, txt, move] = setupTooltipCard( ttip ) - - var bodyA = body.select('a.gene-card') - if (!bodyA.empty()) {bodyA.remove()} - - var bodyUL = body.select('ul.list-group') - if (bodyUL.empty()) {bodyUL = body.append('ul').attr('class', 'list-group well')} - - var metakeys = [] - var raw_metakeys = ['datasources', "interactions", 'pathways', 'pubmed_ids'] - raw_metakeys.map(function(k, i){ - if((edgeMeta[d][k].length > 0) && (d3.keys(edgeMeta[d][k]).length > 0)) {metakeys.push(k)} - }) - - var bodyUlLis = setupTooltipCardBodyUl( bodyUL, metakeys ) - fillTooltipCardBodyUlList ( d, bodyUlLis, edgeMeta ) - - txt.html(d.split('_')[0] + '' + d.split('_')[1]) - } - - function setupTooltipCardBodyUl( bodyUL, metakeys ) { - var bodyUlLis = bodyUL.selectAll('li.outer-item') - if (metakeys.length > bodyUlLis.size()) { - bodyUlLis = bodyUL.selectAll('li.outer-item') - .data(metakeys).enter() - .append('li') - .attr('class', 'list-group-item list-group-item-action outer-item toolbar-list-group-item') - .attr('href', function(d){return'#'+'graph-tooltip-'+d}).attr('data-toggle', 'collapse') - - bodyUlLis.append('i').attr('class', 'fa fa-caret-right') - bodyUlLis.append('h6').style('display', 'inline') - - bodyUlLis.on('click', function() { - var i = d3.select(this).select('i') - if (i.classed('fa-caret-right')) {i.classed('fa-caret-down', true).classed('fa-caret-right', false)} - else {i.classed('fa-caret-down', false).classed('fa-caret-right', true)} - }) - - } else if (metakeys.length < bodyUlLis.size()) { - bodyUlLis = bodyUL.selectAll('li.outer-item').data(metakeys).exit().remove() - } else { - bodyUlLis = bodyUL.selectAll('li.outer-item').data(metakeys) - } - bodyUlLis = bodyUL.selectAll('li.outer-item').data(metakeys) - - return bodyUlLis - } - - function fillTooltipCardBodyUlList ( edgeKey, bodyUlLis, edgeMeta ) { - bodyUlLis.selectAll('h6').html(function(d, i){return d}) - bodyUlLis.each(function (dd, ii) { - var li = d3.select(this) - var liUl = li.select('ul') - if (liUl.empty()) { - liUl = li.append('ul') - .attr('class', 'list-group collapse indent-1') - .attr('id',function(d){return 'graph-tooltip-'+d}) - .style('max-height', '200px') - .style('overflow-y', 'scroll') - } - var dataToUse = edgeMeta[edgeKey][dd] - dataToUse = (dd == 'pathways') ? d3.keys(dataToUse) : dataToUse - var liUlLis = liUl.selectAll('li') - - if (liUlLis.size() < dataToUse.length) { liUlLis = liUl.selectAll('li').data(dataToUse).enter().append('li').attr('class', 'list-group-item list-group-item-action toolbar-list-group-item') } - else if (liUlLis.size() > dataToUse.length) { liUlLis = liUl.selectAll('li').data(dataToUse).exit().remove() } - else { liUlLis = liUl.selectAll('li').data(dataToUse) } - - if (dd == 'datasources') { _makeSubListFromMeta(liUlLis, datasourceMeta) } - else if (dd == 'pathways') { _makeSubListFromMeta(liUlLis, pathwayMeta) } - else if (dd == 'pubmed_ids') { _makeSubListFromMeta(liUlLis, pubmedMeta, 'uid', 'http://ncbi.nlm.nih.gov/pubmed/', 'title') } - else {_makeSubList(liUlLis)} - }) - } - - function _makeSubList(liSelection) { - liSelection.each(function(d, i) { - var li = d3.select(this) - li.html(d) - }) - } - - function _makeSubListFromMeta(liSelection, meta, urlKey, urlPrefix, urlTextKey) { - liSelection.each(function(d, i) { - var li = d3.select(this) - var a = li.select('a') - var urlText - if (meta != undefined && meta[d] != undefined) { - if (urlKey == undefined) {urlKey = 'url'} - if (urlPrefix == undefined) {urlPrefix == ''} - if (urlTextKey == undefined) {urlText = d} else {urlText = meta[d][urlTextKey]} - if (a.empty()) {li.html(''); a = li.append('a')} - a.attr('href', urlPrefix + meta[d][urlKey]).attr('target', '_blank').html(urlText) - } else { - if (!a.empty()) {a.remove()} - li.html(d) - } - }) - } - - - - - function setupSettersAndGetters() { - var closuredVariables = [ - 'vertexSet', - 'edgeSet', - 'layering', - /* GRAPH DRAWING VARIABLES*/ - 'container', - 'width', - 'height', - 'layout', - 'xScale', - 'yScale', - 'rangePad', - 'xCoordinates', - 'yCoordinates', - 'dataExtent', - /* GRAPH STYLE VARIABLES */ - 'vertexRadius', - 'vertexContainer', - 'edgeContainer', - - 'vertexGroup', - 'edgeGroup', - 'vertexMeta', - 'edgeMeta', - 'pathwayMeta', - 'pubmedMeta', - 'datasourceMeta' - ] - - for (var i = 0; i < closuredVariables.length; i++) { - var currentClosureVariable = closuredVariables[i] - var toEvaluateString = " \ - draw." + currentClosureVariable + " = function(value){ \ - if(!arguments.length) return " + currentClosureVariable + "; \ - " + currentClosureVariable + " = value; \ - return draw; \ - }; \ - " - eval(toEvaluateString) - } - } - setupSettersAndGetters() - return draw; -} - - -function marker() { - var id, - markerWidth=10, - markerHeight=10, - refX=0, - refY=3, - orient="auto", - markerUnits="strokeWidth", - viewBox="0 0 20 20", - path="M0,0 L0,6 L9,3 z", - fill="#333333", - type="triangle" - - - function make() { - var defs = d3.select(svg).select("defs") - if (defs.empty()) { - defs = d3.select(svg).append("defs") - } - defs.append("marker") - .attr("id", id) - .attr("markerWidth", markerWidth) - .attr("markerHeight", markerHeight) - .attr("refX", refX) - .attr("refY", refY) - .attr("orient", orient) - .attr("markerUnits", markerUnits) - .attr("viewBox", viewBox) - .append("path") - .attr("d", path) - } - - make.triangle = function (x, y, yOffset) { - refX = yOffset + y - refY = x / 2 - path = "M0,0 L0,"+x+" L"+y+","+(x/2)+" z" - } - - function setupSettersAndGetters() { - var closuredVariables = [ - 'id', 'markerWidth', 'markerHeight', 'refX', 'refY', - 'orient', 'markerUnits', 'viewBox', 'path', "svg", "fill" - ] - - - - for (var i = 0; i < closuredVariables.length; i++) { - var currentClosureVariable = closuredVariables[i] - var toEvaluateString = " \ - make." + currentClosureVariable + " = function(value){ \ - if(!arguments.length) return " + currentClosureVariable + "; \ - " + currentClosureVariable + " = value; \ - return make; \ - }; \ - " - eval(toEvaluateString) - } - } - setupSettersAndGetters() - return make; -} - - - -// marker().id("arrow-head") -// .markerWidth("10") -// .markerHeight("10") -// .refX("0") -// .refY("3") -// .orient("auto") -// .markerUnits("strokeWidth") -// .viewBox("0 0 20 20") -// .path("M0,0 L0,6 L9,3 z") -// .fill("#FFFFFF") diff --git a/needs-to-be-bundled/scatter.js b/needs-to-be-bundled/scatter.js deleted file mode 100644 index 3c97ada..0000000 --- a/needs-to-be-bundled/scatter.js +++ /dev/null @@ -1,265 +0,0 @@ -function scatter() { - // variables that need to be set externally - var plotContainer, - drawingSpace, - colors = ["#2c7bb6", "#00a6ca", "#00ccbc", "#90eb9d", "#ffff8c", "#f9d057", "#f29e2e", "#e76818", "#d7191c"], - tooltipKeys, - rKey, // r key - r=2, - stroke, - data, - xScale = d3.scaleLinear(), - yScale = d3.scaleLinear(), - rScale = d3.scaleLinear().range([ 2, 10]), - xKey, yKey, // x and y keys - points, - categorical = true, - cKey, // color key - cOpacity = 0.2, - strokeWidth = 2, - cScale = d3.scaleSequential(interpolateColors.apply(this, colors)) - - - - // variables set internally - var pointsNeeded, currentNumberOfPoints, - xValues, yValues, rValues, cValues, - dataExtent, fatal, - verbose = true, - animationTime = 1000, - svg, - rScaleMin = 0.002, - rScaleMax = 0.02, - xRangePad = 2, - yRangePad = 2 - hexademicalOpacity = { - 1: "FF", - 0.9: 'E6', - 0.8: 'CC', - 0.7: "B3", - 0.6: "99", - 0.5: "80", - 0.4: "66", - 0.3: "4D", - 0.2: "33", - 0.1: "1A", - 0.0: "00" - } - - - - - function plot(){ - fatal = warn(plotContainer, 'plotContainer', undefined, true, verbose) - if ( fatal ) {return} - fatal = warn(data, 'data', undefined, true, verbose) - if ( fatal ) {return} - fatal = warn(xKey, 'xKey', undefined, true, verbose) - if ( fatal ) {return} - fatal = warn(yKey, 'yKey', undefined, true, verbose) - if ( fatal ) {return} - fatal = warn(drawingSpace, 'drawingSpace', undefined, true, verbose) - if ( fatal ) {return} - - - svg = d3.select(plotContainer.thisSVG()) - - pointsNeeded = data.length - currentNumberOfPoints = plotContainer.selectAll('.point-container').size() - - - xValues = data.map( function(d, i) { return parseFloat( d[xKey] ) } ) - yValues = data.map( function(d, i) { return parseFloat( d[yKey] ) } ) - - - - dataExtent = { - x: { - min: d3.min( xValues ), - max: d3.max( xValues ) - }, - y: { - min: d3.min( yValues ), - max: d3.max( yValues ) - } - } - - xScale.domain([ dataExtent.x.min - xRangePad, dataExtent.x.max + xRangePad]) - .range([ 0, drawingSpace.x ]) - - yScale.domain([ dataExtent.y.min - yRangePad, dataExtent.y.max + yRangePad]) - .range([ drawingSpace.y, 0 ]) - - - // set r scale if rKey is defined - if (rKey != undefined) { - rValues = data.map( function(d, i) { return parseFloat( d[rKey] ) } ) - rScale.domain([ d3.min(rValues), d3.max(rValues) ]) - .range([ - parseFloat(svg.style('width')) * rScaleMin, - parseFloat(svg.style('width')) * rScaleMax] - ) - } - - if (cKey != undefined) { - cValues = data.map( function(d, i) { return parseFloat( d[cKey] ) } ) - cScale.domain([ d3.min(cValues), d3.max(cValues) ]) - } - - - // not enough pionts add more - if (pointsNeeded > currentNumberOfPoints) { - plotContainer.selectAll('.point-container') - .data(data) - .enter() - .append('g') - .attr('class', 'point-container') - .attr('transform', function(d, i) { - x = 0 - y = parseFloat(plotContainer.thisSVG().style.height) - trans = 'translate('+x+','+y+')' - return trans - }) // for entering animation - .append('circle') - .attr('class', 'point') - } else if (pointsNeeded == currentNumberOfPoints) { - plotContainer.selectAll('.point-container').data(data) - } else { // too many points, remove - plotContainer.selectAll('.point-container').data(data) - .exit() - .transition() - .duration(function(d, i) { - return (animationTime * 0.1) + (animationTime * .9) / data.length * i - }) - .attr('transform', function(d, i) { - x = 0 - y = parseFloat(svg.style('height')) - trans = 'translate('+x+','+y+')' - return trans - }) // for exiting animation - .remove() - } - - // rebind dat for safety - points = plotContainer.selectAll('.point-container').data(data) - - points.transition().duration(function(d, i){ - return (animationTime * 0.1) + (animationTime * .9) / data.length * i - }) // animate movement - .attr('transform', function(d, i) { - x = xScale(d[xKey]) - y = yScale(d[yKey]) - trans = "translate("+(x)+","+(y)+")" - return trans - }) // move points to correct location - .select('circle') - .attr('r', function(d, i){ - if (rKey != undefined) { return rScale(d[rKey]) } - else { return r } - }) // set size to r - .attr('fill', function(d, i){ - if (cKey == undefined) { - var colorI = i % colors.length - var c = colors[colorI] - if (c.includes("#") && cOpacity != undefined) { - c += hexademicalOpacity[cOpacity] - } - return c - } else { - var c = cScale(d[cKey]) - if (c.includes("#") && cOpacity != undefined) { - c += hexademicalOpacity[cOpacity] - } else { - [r, g, b] = c.slice(3).split(',') - b = b.split(')').join('') - r = r.split('(').join('') - return 'rgba('+r+','+g+','+b+','+cOpacity+')' - } - return c - } - }) - .attr('stroke-width', strokeWidth) - .attr('stroke', function(d, i){ - if (cKey == undefined) { - var colorI = i % colors.length - var c = colors[colorI] - return c - } else { - return cScale(d[cKey]) - } - }) - - - - points.on('mouseout', mouseoutPoint) - .on('mouseover', mouseoverPoint) - - function mouseoutPoint(d, i){ - var pnt = d3.select(this).select('circle') - d3.selectAll('div.tooltip').style('display', 'none') - pnt.transition().duration(animationTime * 0.5) - .attr('r', function(d, i){ - if (rKey != undefined) { return rScale(d[rKey]) } - else { return r } - }) - } - - function mouseoverPoint(d, i){ - var pnt = d3.select(this).select('circle') - pnt.transition().duration(animationTime * 0.5) - .attr('r', function(d, i){ - return rScale.range()[1] * 1.5 - }) - - ttip = tooltip().tooltipKeys(tooltipKeys).data(d) - ttip() - } - - } - - - function setupSettersAndGetters() { - var closuredVariables = [ - 'plotContainer', - 'drawingSpace', - 'data', - 'colors', - 'tooltipKeys', - 'rKey','xKey', 'yKey', 'cKey', - 'r', - 'xScale', - 'yScale', - 'cScale', - 'rScale', - 'xRangePad', - 'yRangePad', - 'points', - 'categorical', - 'cOpacity', - 'strokeWidth', - 'pointsNeeded', - 'currentNumberOfPoints', - 'xValues','yValues','rValues','cValues', - 'dataExtent', 'fatal', - 'verbose', 'animationTime', - 'svg', 'rScaleMax', 'rScaleMin' - ] - - - for (var i = 0; i < closuredVariables.length; i++) { - var currentClosureVariable = closuredVariables[i] - var toEvaluateString = " \ - plot." + currentClosureVariable + " = function(value){ \ - if(!arguments.length) return " + currentClosureVariable + "; \ - " + currentClosureVariable + " = value; \ - return plot; \ - }; \ - " - eval(toEvaluateString) - } - } - - - setupSettersAndGetters() - return plot; -} diff --git a/needs-to-be-bundled/tooltip.js b/needs-to-be-bundled/tooltip.js deleted file mode 100644 index c4ba690..0000000 --- a/needs-to-be-bundled/tooltip.js +++ /dev/null @@ -1,79 +0,0 @@ -function tooltip() { - var - ttip, - tooltipKeys, - tooltipContent, - divs, data - - - function make() { - ttip = d3.select('body').select('div.tooltip') - .style('display', 'flex') - - if (ttip.empty()) { - ttip = d3.select('body').append('div').attr('class','tooltip') - .style('display', 'flex').attr('id', 'tooltip') - } - - tooltipContent = ttip.select('div.tooltip-vertical-flex') - if (tooltipContent.empty()) { - tooltipContent = ttip.append('div').attr('class','tooltip-vertical-flex') - } - - divs = tooltipContent.selectAll('.tooltip-element-flex') - if (divs.empty() || divs.size() < tooltipKeys.length) { - divs = tooltipContent.selectAll('.tooltip-element-flex') - .data(tooltipKeys) - .enter() - .append('div').attr('class', 'tooltip-element-flex') - - divs.append('div').attr('class', 'tooltip-element-key') - divs.append('div').attr('class', 'tooltip-element-value') - } else { - tooltipContent.selectAll('.tooltip-element-flex') - .data(tooltipKeys) - .exit() - .remove() - } - divs = tooltipContent.selectAll('.tooltip-element-flex').data(tooltipKeys) - - divs.each(function(curKey){ - var k = d3.select(this).select('.tooltip-element-key') - var v = d3.select(this).select('.tooltip-element-value') - - k.html(curKey) - v.html(data[curKey]) - }) - - var mouse = d3.mouse.smart("tooltip") - ttip.style('left', mouse[0]+'px') - .style('top', mouse[1]+'px') - - } - - function setupSettersAndGetters() { - var closuredVariables = [ - 'ttipConatiner', - 'ttip', - 'tooltipKeys', - 'tooltipContent', - 'divs', 'data' - ] - - - for (var i = 0; i < closuredVariables.length; i++) { - var currentClosureVariable = closuredVariables[i] - var toEvaluateString = " \ - make." + currentClosureVariable + " = function(value){ \ - if(!arguments.length) return " + currentClosureVariable + "; \ - " + currentClosureVariable + " = value; \ - return make; \ - }; \ - " - eval(toEvaluateString) - } - } - - setupSettersAndGetters() - return make -} diff --git a/rollup.config.js b/rollup.config.js index f638afd..186a4da 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,8 +1,8 @@ -import babel from 'rollup-plugin-babel'; +// import babel from 'rollup-plugin-babel'; import uglify from 'rollup-plugin-uglify-es'; import minimist from 'minimist'; import commonjs from 'rollup-plugin-commonjs'; -import resolve from 'rollup-plugin-node-resolve'; +import nodeResolve from 'rollup-plugin-node-resolve'; import pkg from './package.json'; @@ -10,39 +10,25 @@ import pkg from './package.json'; const argv = minimist(process.argv.slice(2)); const config = { - input: 'src/scripts/main.js', + input: 'src/entry.js', output: { name: pkg.name, exports: 'named', extend: true, - // external: ['d3'], - // globals: { - // d3: 'd3' - // }, + external: ['d3'], + globals: { + d3: 'd3' + }, }, plugins: [ - // babel({ // exclude: 'node_modules/**', // externalHelpers: true, - // runtimeHelpers: true, - // plugins: [ - // [ - // 'wildcard', - // { - // exts: ['vue'], - // nostrip: true, - // }, - // ], - // '@babel/plugin-external-helpers', - // - // ], + // runtimeHelpers: true, // }), - resolve({ - jsnext: true, - main: true - + nodeResolve({ + mainFields:['jsnext', 'main'], }), commonjs({ // non-CommonJS modules will be ignored, but you can also diff --git a/src/entry.js b/src/entry.js new file mode 100644 index 0000000..2ac3867 --- /dev/null +++ b/src/entry.js @@ -0,0 +1,27 @@ +import utils from './modules/utils/index.js' +import axis from './modules/axis' +import groupingSpacer from './modules/grouping-spacer' +import tooltip from './modules/tooltip' +import colorFunction from './modules/color-function' +import charts from './modules/charts/index.js' +import legends from './modules/legends/index.js' +import aux from './modules/aux/index.js' + + + +let d3sm = { + aux, + axis, + charts, + colorFunction, + debugQ: false, + groupingSpacer, + legends, + tooltip, + utils, +} + +if (typeof window !== 'undefined') { + window.d3sm = d3sm; +} +export default d3sm diff --git a/src/modules/aux/index.js b/src/modules/aux/index.js new file mode 100644 index 0000000..c52b890 --- /dev/null +++ b/src/modules/aux/index.js @@ -0,0 +1,9 @@ +import lasso from './lasso' +import multiPlotZoom from './multi-plot-zoom' +import plotZoom from './plot-zoom' +let aux = { + lasso, plotZoom, multiPlotZoom +} + +export {lasso, plotZoom, multiPlotZoom} +export default aux diff --git a/src/scripts/modules/lasso.js b/src/modules/aux/lasso.js similarity index 87% rename from src/scripts/modules/lasso.js rename to src/modules/aux/lasso.js index 0ccf0e9..d3bbb24 100644 --- a/src/scripts/modules/lasso.js +++ b/src/modules/aux/lasso.js @@ -1,20 +1,18 @@ -import {hypenate, safeSelect, euclideanDistance} from './helpers'; -import {setupContainer, calculateWidthOfObject, calculateWidthOfSpacer} from './utils'; -import {unique, hasQ, flatten, whichBin} from './array-functions'; -import {groupingSpacer} from './grouping-spacer'; -import {colorFunction as CF} from './color-function'; -import {tooltip as TTip} from './tooltip'; -import './d3-prototypes'; - -function getTranslation(selection){ - var transform = selection.attr('transform') - var [junk, xy] =transform.split('translate(') - var [x, y] = xy.split(',') - y, junk = y.split(')') - return [parseFloat(x), parseFloat(y)] -} - -export function lasso( selection ) { +import utils from '../utils/index.js'; +import groupingSpacer from '../grouping-spacer'; +import CF from '../color-function'; +import TTip from '../tooltip'; +import '../d3-prototypes'; + +// function getTranslation(selection){ +// var transform = selection.attr('transform') +// var [junk, xy] =transform.split('translate(') +// var [x, y] = xy.split(',') +// y, junk = y.split(')') +// return [parseFloat(x), parseFloat(y)] +// } + +export default function lasso( selection ) { var svg, // svg that is target of events objectContainer, // container which houses objects we are selecting (allows for transform to be applied to lasso) @@ -114,8 +112,8 @@ export function lasso( selection ) { function toggle(state) { // use optional param to set state, otherwise toggle state activeQ = (state!=undefined) ? state : !activeQ - chartOffset = getTranslation(chartContainer) - objectsOffset = getTranslation(objectContainer) + chartOffset = utils.sel.getTranslation(chartContainer) + objectsOffset = utils.sel.getTranslation(objectContainer) if (activeQ) { svg.node().addEventListener('mousedown', render, true) @@ -127,10 +125,10 @@ export function lasso( selection ) { } function draw() { - chartOffset = getTranslation(chartContainer) - objectsOffset = getTranslation(objectContainer) + chartOffset = utils.sel.getTranslation(chartContainer) + objectsOffset = utils.sel.getTranslation(objectContainer) - var container = safeSelect(objectContainer, 'g', 'lasso-container') + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container') var paths = container.selectAll('path[instance="'+instance+'"]') // update @@ -149,7 +147,7 @@ export function lasso( selection ) { } function remove() { - var container = safeSelect(objectContainer, 'g', 'lasso-container') + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container') var paths = container.selectAll('path[instance="'+instance+'"]').remove() container.remove() objectContainer.selectAll(objectClass).classed("in-lasso", false) @@ -160,7 +158,7 @@ export function lasso( selection ) { // nothing can interefer with drawing the lasso event.preventDefault(); event.stopPropagation(); - var container = safeSelect(objectContainer, 'g', 'lasso-container') + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container') /* each time the user presses down, while the state is active, the lasso @@ -173,8 +171,8 @@ export function lasso( selection ) { svg.node().removeEventListener('mousemove', drag) allPoints.push(currentPoints) // BUG: somehow this is pushing currentPoints n times where n is the nth lasso path for the current instance - // NOTE: allPoints = unique(allPoints) is a temporary and inefficient fix - allPoints = unique(allPoints) + // NOTE: allPoints = utils.arr.unique(allPoints) is a temporary and inefficient fix + allPoints = utils.arr.unique(allPoints) }) path = container.append('path').data([currentPoints]) @@ -182,7 +180,7 @@ export function lasso( selection ) { } function transitionDraw() { - var container = safeSelect(objectContainer, 'g', 'lasso-container') + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container') var paths = container.selectAll('path[instance="'+instance+'"]') // update @@ -203,7 +201,7 @@ export function lasso( selection ) { function applyPathAttributes(path) { path - .attr("class", hypenate(namespace, "lasso-path")) + .attr("class", utils.str.hypenate(namespace, "lasso-path")) .style('opacity', opacity) .attr('fill', color) .attr("d", line) @@ -222,8 +220,8 @@ export function lasso( selection ) { mouse down / mouse up. */ - if (eventCatcher != undefined) {eventCatcher.dispatch(hypenate(namespace,"drag"))} - // d3.dispatch(hypenate(namespace,"drag")) + if (eventCatcher != undefined) {eventCatcher.dispatch(utils.str.hypenate(namespace,"drag"))} + // d3.dispatch(utils.str.hypenate(namespace,"drag")) if (event.which != 1) {return} // ensures left mouse button set d3.event = event @@ -243,7 +241,7 @@ export function lasso( selection ) { if (xScale) {b[0] = xScale(b[0]); a[0] = xScale(a[0])} if (yScale) {b[1] = yScale(b[1]); a[1] = yScale(a[1])} - var dist = euclideanDistance(b, a) + var dist = utils.math.euclideanDistance(b, a) if (dist > tickDistance) { tick(pt) } } else { tick(pt) } @@ -379,10 +377,10 @@ export function lasso( selection ) { function keyFrames() { var style = - d3.select("html").select('style.'+hypenate(namespace,"lasso-dash")) + d3.select("html").select('style.'+utils.str.hypenate(namespace,"lasso-dash")) if (style.empty()) { d3.select("html").append('style') - .classed(hypenate(namespace,"lasso-dash"), true) + .classed(utils.str.hypenate(namespace,"lasso-dash"), true) .html("@keyframes lassoDash {to { stroke-dashoffset: 1000;}}") } diff --git a/src/scripts/modules/multi-plot-zoom.js b/src/modules/aux/multi-plot-zoom.js similarity index 88% rename from src/scripts/modules/multi-plot-zoom.js rename to src/modules/aux/multi-plot-zoom.js index 1565e4a..18db8b3 100644 --- a/src/scripts/modules/multi-plot-zoom.js +++ b/src/modules/aux/multi-plot-zoom.js @@ -1,5 +1,4 @@ -import {hypenate, safeSelect, getTranslation} from './helpers'; -import {log, warn, error, info} from './utils'; +import utils from '../utils/index.js' /******************************************************************************* ** ** @@ -17,7 +16,7 @@ import {log, warn, error, info} from './utils'; * @namespace plotZoom * @returns {function} zoom */ -export function multiPlotZoom( chart ) { +export default function multiPlotZoom( chart ) { var /** * The event on which to fire @@ -127,14 +126,14 @@ export function multiPlotZoom( chart ) { function setLocks() { - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')) - var chartObjTrans = getTranslation(chartObjSel.attr('transform')) + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')) + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')) var cos = chartObjSel.attr('transform', 'translate(0,0)') xLock = chartSel.node().getBBox().width - chart.spaceX()// * .9 yLock = chartSel.node().getBBox().height - chart.spaceY()// * .9 cos.attr('transform', 'translate('+chartObjTrans[0]+','+chartObjTrans[1]+')') - log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}) + utils.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}) } @@ -194,20 +193,20 @@ export function multiPlotZoom( chart ) { - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')) + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')) var xComponentObjSel = xComponentsSel.map(function(d, i){ - return d.select('.'+hypenate(xComponents[i].namespace(),'object-container')) + return d.select('.'+utils.str.hypenate(xComponents[i].namespace(),'object-container')) }) var yComponentObjSel = yComponentsSel.map(function(d, i){ - return d.select('.'+hypenate(yComponents[i].namespace(),'object-container')) + return d.select('.'+utils.str.hypenate(yComponents[i].namespace(),'object-container')) }) - var chartObjTrans = getTranslation(chartObjSel.attr('transform')) + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')) var xComponentsObjTrans = xComponentObjSel.map(function(d, i){ - return getTranslation(d.attr('transform')) + return utils.sel.getTranslation(d.attr('transform')) }) var yComponentsObjTrans = yComponentObjSel.map(function(d, i){ - return getTranslation(d.attr('transform')) + return utils.sel.getTranslation(d.attr('transform')) }) var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0 @@ -236,9 +235,9 @@ export function multiPlotZoom( chart ) { if (orient == 'horizontal') {horizontalQ = true; verticalQ = false;} if (orient == 'vertical') {verticalQ = true; horizontalQ = false;} - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')) - var xAxisObjSel = xAxisSel.select('.'+hypenate(xAxis.namespace(),'object-container')) - var yAxisObjSel = yAxisSel.select('.'+hypenate(yAxis.namespace(),'object-container')) + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')) + var xAxisObjSel = xAxisSel.select('.'+utils.str.hypenate(xAxis.namespace(),'object-container')) + var yAxisObjSel = yAxisSel.select('.'+utils.str.hypenate(yAxis.namespace(),'object-container')) chartObjSel.attr('transform', 'translate('+0+','+0+')') xAxisObjSel.attr('transform', 'translate('+0+','+0+')') yAxisObjSel.attr('transform', 'translate('+0+','+0+')') diff --git a/src/scripts/modules/plot-zoom.js b/src/modules/aux/plot-zoom.js similarity index 87% rename from src/scripts/modules/plot-zoom.js rename to src/modules/aux/plot-zoom.js index 6cca5f6..6aef3f4 100644 --- a/src/scripts/modules/plot-zoom.js +++ b/src/modules/aux/plot-zoom.js @@ -1,5 +1,4 @@ -import {hypenate, safeSelect, getTranslation} from './helpers'; -import {log, warn, error, info} from './utils'; +import utils from '../utils/index.js'; /******************************************************************************* ** ** @@ -17,7 +16,7 @@ import {log, warn, error, info} from './utils'; * @namespace plotZoom * @returns {function} zoom */ -export function plotZoom( chart, xAxis, yAxis ) { +export default function plotZoom( chart, xAxis, yAxis ) { var /** * The event on which to fire @@ -122,13 +121,13 @@ export function plotZoom( chart, xAxis, yAxis ) { function setLocks() { - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')) - var chartObjTrans = getTranslation(chartObjSel.attr('transform')) + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')) + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')) var cos = chartObjSel.attr('transform', 'translate(0,0)') xLock = chartSel.node().getBBox().width - chart.spaceX() * .9 yLock = chartSel.node().getBBox().height - chart.spaceY() * .9 cos.attr('transform', 'translate('+chartObjTrans[0]+','+chartObjTrans[1]+')') - log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}) + utils.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}) } @@ -187,9 +186,9 @@ export function plotZoom( chart, xAxis, yAxis ) { - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')) - var xAxisObjSel = xAxisSel.select('.'+hypenate(xAxis.namespace(),'object-container')) - var yAxisObjSel = yAxisSel.select('.'+hypenate(yAxis.namespace(),'object-container')) + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')) + var xAxisObjSel = xAxisSel.select('.'+utils.str.hypenate(xAxis.namespace(),'object-container')) + var yAxisObjSel = yAxisSel.select('.'+utils.str.hypenate(yAxis.namespace(),'object-container')) // xLock = chartSel.node().getBBox().width - chart.spaceX() - chartSel.node().getBBox().x // yLock = chartSel.node().getBBox().height - chart.spaceY() @@ -197,9 +196,9 @@ export function plotZoom( chart, xAxis, yAxis ) { // bhm.selection().node().getBBox().width - bhm.spaceX() - var chartObjTrans = getTranslation(chartObjSel.attr('transform')) - var xAxisObjTrans = getTranslation(xAxisObjSel.attr('transform')) - var yAxisObjTrans = getTranslation(yAxisObjSel.attr('transform')) + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')) + var xAxisObjTrans = utils.sel.getTranslation(xAxisObjSel.attr('transform')) + var yAxisObjTrans = utils.sel.getTranslation(yAxisObjSel.attr('transform')) var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0 @@ -225,9 +224,9 @@ export function plotZoom( chart, xAxis, yAxis ) { if (orient == 'horizontal') {horizontalQ = true; verticalQ = false;} if (orient == 'vertical') {verticalQ = true; horizontalQ = false;} - var chartObjSel = chartSel.select('.'+hypenate(chart.namespace(),'object-container')) - var xAxisObjSel = xAxisSel.select('.'+hypenate(xAxis.namespace(),'object-container')) - var yAxisObjSel = yAxisSel.select('.'+hypenate(yAxis.namespace(),'object-container')) + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')) + var xAxisObjSel = xAxisSel.select('.'+utils.str.hypenate(xAxis.namespace(),'object-container')) + var yAxisObjSel = yAxisSel.select('.'+utils.str.hypenate(yAxis.namespace(),'object-container')) chartObjSel.attr('transform', 'translate('+0+','+0+')') xAxisObjSel.attr('transform', 'translate('+0+','+0+')') yAxisObjSel.attr('transform', 'translate('+0+','+0+')') diff --git a/src/scripts/modules/axis.js b/src/modules/axis.js similarity index 91% rename from src/scripts/modules/axis.js rename to src/modules/axis.js index e2dae61..c3f3d1f 100644 --- a/src/scripts/modules/axis.js +++ b/src/modules/axis.js @@ -1,12 +1,5 @@ -import { - hypenate, safeSelect, extractViolinValues, - tickRange, modifyHexidecimalColorLuminance, truncateText, - truncateString, - round -} from './helpers'; -import {setupContainer, calculateWidthOfObject, calculateWidthOfSpacer} from './utils'; -import {unique, hasQ, flatten} from './array-functions'; -import {groupingSpacer} from './grouping-spacer'; +import utils from './utils/index.js' +import groupingSpacer from './grouping-spacer'; /******************************************************************************* ** ** ** ** @@ -24,7 +17,7 @@ import {groupingSpacer} from './grouping-spacer'; * @namespace axis * @returns {function} axis */ -export function axis ( selection ) { +export default function axis ( selection ) { var /** * The orientation of the axis @@ -780,7 +773,7 @@ export function axis ( selection ) { function axis () { // for convenience in handling orientation specific values - var horizontalQ = hasQ(['top', 'bottom', 'horizontal'], orient) ? true : false + var horizontalQ = utils.arr.hasQ(['top', 'bottom', 'horizontal'], orient) ? true : false var verticalQ = !horizontalQ // background cliping rectangle @@ -815,7 +808,7 @@ export function axis ( selection ) { } - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); // defaults for text-anchor and text rotation if (orient == 'top') { @@ -853,12 +846,12 @@ export function axis ( selection ) { : (grouping == undefined) ? (numberOfTicks != undefined) // ? (tickValues.length < numberOfTicks) - ? (tickRange(...d3.extent(tickValues), numberOfTicks)) + ? (utils.math.tickRange(...d3.extent(tickValues), numberOfTicks)) : tickValues : grouping - var flatTickData = flatten(tickData) + var flatTickData = utils.arr.flatten(tickData) var numberOfObjects = flatTickData.length var space = horizontalQ ? spaceX : spaceY var extent = d3.extent(flatTickData) @@ -882,16 +875,16 @@ export function axis ( selection ) { // calculate object size if needed objectSize = (objectSize == undefined) - ? calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) : objectSize // calculate spacer size if needed spacerSize = (spacerSize == undefined) - ? calculateWidthOfSpacer(flatTickData, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfSpacer(flatTickData, space, objectSize, numberOfObjects, objectSpacer, overflowQ) : spacerSize - var objClass = hypenate(namespace, categoricalQ ? objectClass+'-categorical' : objectClass) + var objClass = utils.str.hypenate(namespace, categoricalQ ? objectClass+'-categorical' : objectClass) var spacerFunction = groupingSpacer() .horizontalQ(horizontalQ).scale(scale).moveby((categoricalQ?'category':'scale')).numberOfObjects(numberOfObjects) @@ -956,11 +949,11 @@ export function axis ( selection ) { - var labelNameGroup = safeSelect(selection, 'g', hypenate(namespace,'axis-name')) + var labelNameGroup = utils.sel.safeSelect(selection, 'g', utils.str.hypenate(namespace,'axis-name')) - var labelElement = safeSelect(labelNameGroup, 'text', hypenate(namespace, 'name')) + var labelElement = utils.sel.safeSelect(labelNameGroup, 'text', utils.str.hypenate(namespace, 'name')) if (labelElement != undefined) { labelElement.text(label) @@ -1008,8 +1001,8 @@ export function axis ( selection ) { to use clip path to make things fit in fixed size. Have yet got this to work nicely. */ // var defs = d3.select(container.node().parentNode).select('defs') - // var tickLabelClipPath = safeSelect(defs, 'clipPath', hypenate(namespace,'tick-label-clip-path')).attr('id', hypenate(namespace,'tick-label-clip-path')) - // var tickLabelClipPathRect = safeSelect(tickLabelClipPath, 'rect', hypenate(namespace,'tick-label-clip-path-rect')) + // var tickLabelClipPath = utils.sel.safeSelect(defs, 'clipPath', utils.str.hypenate(namespace,'tick-label-clip-path')).attr('id', utils.str.hypenate(namespace,'tick-label-clip-path')) + // var tickLabelClipPathRect = utils.sel.safeSelect(tickLabelClipPath, 'rect', utils.str.hypenate(namespace,'tick-label-clip-path-rect')) // .attr('x', 0) // .attr('y', 0) // .attr('width', function(d, i){ @@ -1027,7 +1020,7 @@ export function axis ( selection ) { var that = d3.select(this).style('opacity', 1) // make and move tick - var tick = safeSelect(that, 'line', hypenate(namespace,'tick')) + var tick = utils.sel.safeSelect(that, 'line', utils.str.hypenate(namespace,'tick')) .attr("x1", 0) .attr("x2", horizontalQ ? 0 : orient == "left" ? -tickLength : tickLength) .attr("y1", 0) @@ -1043,15 +1036,15 @@ export function axis ( selection ) { }) // make and move label - var label = safeSelect(that, 'text', hypenate(namespace,'label')) + var label = utils.sel.safeSelect(that, 'text', utils.str.hypenate(namespace,'label')) .text(function(d, i){ - var s = typeof d == 'number' ? round(d, roundTo) : d - s = truncateString(String(s), (horizontalQ ? spaceY : spaceX) - tickLength-tickLabelMargin-tickTickLabelSpacer, tickLabelFontSize * 0.45) + var s = typeof d == 'number' ? utils.math.round(d, roundTo) : d + s = utils.str.truncateString(String(s), (horizontalQ ? spaceY : spaceX) - tickLength-tickLabelMargin-tickTickLabelSpacer, tickLabelFontSize * 0.45) return s }) .attr('font-size', tickLabelFontSize) .attr('text-anchor', tickLabelTextAnchor) - // truncateText(label, label.text(), orient, tickLength, horizontalQ ? spaceY : spaceX, overflowQ) + // utils.str.truncateText(label, label.text(), orient, tickLength, horizontalQ ? spaceY : spaceX, overflowQ) label.attr('transform', function(d, i) { var @@ -1097,11 +1090,11 @@ export function axis ( selection ) { .on('mousemove', labelHover) .on('mouseout', labelHoverOff) .on('click', tickLabelOnClick) - // .attr('clip-path', 'url(#'+hypenate(namespace,'tick-label-clip-path')+')') + // .attr('clip-path', 'url(#'+utils.str.hypenate(namespace,'tick-label-clip-path')+')') // add guidlines as needed if (guideLinesQ) { - var gline = safeSelect(that, 'line', hypenate(namespace, 'guideline')) + var gline = utils.sel.safeSelect(that, 'line', utils.str.hypenate(namespace, 'guideline')) .transition().duration(transitionDuration).ease(easeFunc) .attr("x1", 0) .attr("x2", horizontalQ ? 0 : orient == "left" ? guidelineSpace : -guidelineSpace) @@ -1114,15 +1107,15 @@ export function axis ( selection ) { t = 'translate('+x+','+y+')' return t }) - } else { that.select('line.'+hypenate(namespace, 'guideline')).remove() } + } else { that.select('line.'+utils.str.hypenate(namespace, 'guideline')).remove() } }) // apply alternating guidline thickness if (guideLinesQ) { - container.selectAll('.'+hypenate(namespace,'guideline')) + container.selectAll('.'+utils.str.hypenate(namespace,'guideline')) .attr('stroke', function(d, i){ - if (i % 2 == 0) { return modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } + if (i % 2 == 0) { return utils.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } return guideLineStroke }) .attr('stroke-width', function(d, i){ @@ -1136,7 +1129,7 @@ export function axis ( selection ) { /*************************************************************************** ** Make the line of the axis ***************************************************************************/ - var line = safeSelect(selection, 'path', hypenate(namespace,'line')) + var line = utils.sel.safeSelect(selection, 'path', utils.str.hypenate(namespace,'line')) // .attr('x1', 0) // .attr('x2', horizontalQ ? spaceX : 0) // .attr('y1', 0) @@ -1156,21 +1149,21 @@ export function axis ( selection ) { // hover of label show full text label in case it is truncated function labelHover(d, i){ var t = d3.select(this).style('fill', 'red') - d3.select(t.node().parentNode).select("line."+hypenate(namespace,'tick')) + d3.select(t.node().parentNode).select("line."+utils.str.hypenate(namespace,'tick')) .attr("stroke", 'red') .attr("stroke-width", tickStrokeWidth*2) if (guideLinesQ) { - d3.select(t.node().parentNode).select('line.'+hypenate(namespace, 'guideline')) + d3.select(t.node().parentNode).select('line.'+utils.str.hypenate(namespace, 'guideline')) .attr('stroke', 'red') .attr('stroke-width', guideLineStrokeWidth*2) } - var s = typeof d == 'number' ? round(d, roundTo) : d + var s = typeof d == 'number' ? utils.math.round(d, roundTo) : d var m = d3.mouse(d3.select('html').node()) - var div = safeSelect(d3.select('body'), 'div', hypenate(namespace,'guideline-tooltip')) - .attr('id', hypenate(namespace,'guideline-tooltip')) + var div = utils.sel.safeSelect(d3.select('body'), 'div', utils.str.hypenate(namespace,'guideline-tooltip')) + .attr('id', utils.str.hypenate(namespace,'guideline-tooltip')) .style('position', 'absolute') .style('left', (d3.event.pageX+15)+'px') .style('top', (d3.event.pageY+15)+'px') @@ -1187,7 +1180,7 @@ export function axis ( selection ) { .style('border-style', 'solid') .style('border-width', 2) - var text = safeSelect(div, 'div') + var text = utils.sel.safeSelect(div, 'div') .text(tickLabelOnHoverFunc(s, i)) .style('color', 'black') .style('align-self', 'center') @@ -1200,15 +1193,15 @@ export function axis ( selection ) { function labelHoverOff(d, i){ var t = d3.select(this).style('fill', 'black') - d3.select(t.node().parentNode).select("line."+hypenate(namespace,'tick')) + d3.select(t.node().parentNode).select("line."+utils.str.hypenate(namespace,'tick')) .attr("stroke", tickStroke) .attr("stroke-width", tickStrokeWidth) if (guideLinesQ) { - var gline = d3.select(t.node().parentNode).select('line.'+hypenate(namespace, 'guideline')) + var gline = d3.select(t.node().parentNode).select('line.'+utils.str.hypenate(namespace, 'guideline')) var minorQ = gline.attr('minor') gline.attr('stroke', function(d, ii){ - if (minorQ == 'true') { return modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } + if (minorQ == 'true') { return utils.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } return guideLineStroke }) .attr('stroke-width', function(d, ii){ @@ -1216,7 +1209,7 @@ export function axis ( selection ) { return guideLineStrokeWidth }) } - d3.select("#"+hypenate(namespace,'guideline-tooltip')).remove() + d3.select("#"+utils.str.hypenate(namespace,'guideline-tooltip')).remove() } diff --git a/src/scripts/modules/bar.js b/src/modules/charts/bar.js similarity index 96% rename from src/scripts/modules/bar.js rename to src/modules/charts/bar.js index 6e75c75..82318b9 100644 --- a/src/scripts/modules/bar.js +++ b/src/modules/charts/bar.js @@ -1,9 +1,7 @@ -import {hypenate, safeSelect} from './helpers'; -import {setupContainer, calculateWidthOfObject, calculateWidthOfSpacer} from './utils'; -import {unique, flatten} from './array-functions'; -import {groupingSpacer} from './grouping-spacer'; -import {colorFunction as CF} from './color-function'; -import {tooltip as TTip} from './tooltip'; +import utils from '../utils/index.js' +import groupingSpacer from '../grouping-spacer'; +import CF from '../color-function'; +import TTip from '../tooltip'; /******************************************************************************* ** ** ** ** @@ -21,7 +19,7 @@ import {tooltip as TTip} from './tooltip'; * @namespace bar * @returns {function} bar */ -export function bar ( selection ) { +export default function bar ( selection ) { /* Assumes that data is list an object. @@ -503,7 +501,7 @@ export function bar ( selection ) { // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY} - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); // to prevent re-calculation and getters to be passed to axes barKeys = d3.keys(data) @@ -512,7 +510,7 @@ export function bar ( selection ) { // if grouping is undefined sort barKeys by sortingFunction var ordered = (grouping == undefined) ? barKeys.sort(sortingFunction) : grouping // ordered might be nested depending on grouping - barKeys = flatten(ordered) + barKeys = utils.arr.flatten(ordered) var numberOfObjects = barKeys.length var extent = [Math.min(...barValues) - domainPadding,Math.max(...barValues) + domainPadding]; @@ -530,12 +528,12 @@ export function bar ( selection ) { var space = horizontalQ ? spaceX : spaceY // calculate object size objectSize = (objectSize == undefined) - ? calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) : objectSize // calculate spacer size if needed spacerSize = (spacerSize == undefined) - ? calculateWidthOfSpacer(barKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfSpacer(barKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) : spacerSize // make the nested groups var spacerFunction = groupingSpacer() @@ -602,7 +600,7 @@ export function bar ( selection ) { strokeColor = colorFunction(key, value, i, 'stroke') - var bar = safeSelect(t, 'rect', 'bar-rect') + var bar = utils.sel.safeSelect(t, 'rect', 'bar-rect') if (bar.attr('transform') == undefined) { bar.attr('transform', function(d, i) { diff --git a/src/scripts/modules/box-whisker.js b/src/modules/charts/box-whisker.js similarity index 94% rename from src/scripts/modules/box-whisker.js rename to src/modules/charts/box-whisker.js index 03a0842..55e0638 100644 --- a/src/scripts/modules/box-whisker.js +++ b/src/modules/charts/box-whisker.js @@ -1,9 +1,7 @@ -import {hypenate, safeSelect} from './helpers'; -import {setupContainer, calculateWidthOfObject, calculateWidthOfSpacer, whiskerPath} from './utils'; -import {unique, hasQ, flatten} from './array-functions'; -import {groupingSpacer} from './grouping-spacer'; -import {colorFunction as CF} from './color-function'; -import {tooltip as TTip} from './tooltip'; +import utils from '../utils/index.js'; +import groupingSpacer from '../grouping-spacer'; +import CF from '../color-function'; +import TTip from '../tooltip'; /******************************************************************************* ** ** ** ** @@ -550,12 +548,12 @@ export function boxwhisker( selection ) { // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY} - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); // if grouping is undefined sort keys by sorting funct var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping // to prevent re-calculation and getters to be passed to axes - boxKeys = flatten(ordered) + boxKeys = utils.arr.flatten(ordered) boxValues = boxKeys.map(valueExtractor) @@ -569,9 +567,9 @@ export function boxwhisker( selection ) { scale.domain(extent).range(horizontalQ ? [0,spaceY] : [spaceX, 0]) var space = horizontalQ ? spaceX : spaceY // calculate object size - objectSize = calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) // calculate spacer size if needed - spacerSize = calculateWidthOfSpacer(boxKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + spacerSize = utils.math.calculateWidthOfSpacer(boxKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) // make the nested groups var spacerFunction = groupingSpacer() .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) @@ -584,7 +582,7 @@ export function boxwhisker( selection ) { var parentIndexArray = [] container.selectAll('g:not(.to-remove).'+objectClass) - .each(function(d, i){if (hasQ(boxKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')))}}) + .each(function(d, i){if (utils.arr.hasQ(boxKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')))}}) @@ -611,13 +609,13 @@ export function boxwhisker( selection ) { var - whisk = safeSelect(t, 'g', 'whisker'), - uWhisk = safeSelect(whisk, 'path', 'upper'), - lWhisk = safeSelect(whisk, 'path', 'lower'), - quart = safeSelect(t, 'g', 'quartile'), - uQuart = safeSelect(quart, 'rect', 'upper'), - lQuart = safeSelect(quart, 'rect', 'lower'), - mQuart = safeSelect(quart, 'circle', 'median') + whisk = utils.sel.safeSelect(t, 'g', 'whisker'), + uWhisk = utils.sel.safeSelect(whisk, 'path', 'upper'), + lWhisk = utils.sel.safeSelect(whisk, 'path', 'lower'), + quart = utils.sel.safeSelect(t, 'g', 'quartile'), + uQuart = utils.sel.safeSelect(quart, 'rect', 'upper'), + lQuart = utils.sel.safeSelect(quart, 'rect', 'lower'), + mQuart = utils.sel.safeSelect(quart, 'circle', 'median') // set upper quartile (q3) @@ -678,7 +676,7 @@ export function boxwhisker( selection ) { y = 0, h = horizontalQ ? scale(q1) - scale(q0) : objectSize, w = verticalQ ? scale(q1) - scale(q0) : objectSize - return whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient) + return utils.paths.whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient) }) .attr('transform', function(d, i){ var @@ -699,7 +697,7 @@ export function boxwhisker( selection ) { y = 0, h = horizontalQ ? scale(q4) - scale(q3) : objectSize, w = verticalQ ? scale(q4) - scale(q3) : objectSize - return whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient) + return utils.paths.whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient) }) .attr('transform', function(d, i){ var diff --git a/src/scripts/modules/bubble-heatmap.js b/src/modules/charts/bubble-heatmap.js similarity index 91% rename from src/scripts/modules/bubble-heatmap.js rename to src/modules/charts/bubble-heatmap.js index 8d4707e..906e05c 100644 --- a/src/scripts/modules/bubble-heatmap.js +++ b/src/modules/charts/bubble-heatmap.js @@ -1,9 +1,7 @@ -import {hypenate, safeSelect} from './helpers'; -import {setupContainer, calculateWidthOfObject, calculateWidthOfSpacer, log} from './utils'; -import {unique} from './array-functions'; -import {groupingSpacer} from './grouping-spacer'; -import {colorFunction as CF} from './color-function'; -import {tooltip as TTip} from './tooltip'; +import utils from '../utils/index.js' +import groupingSpacer from '../grouping-spacer'; +import CF from '../color-function'; +import TTip from '../tooltip'; /** @@ -15,7 +13,7 @@ import {tooltip as TTip} from './tooltip'; * @namespace bubbleHeatmap * @returns {function} bubbleHeatmap */ -function bubbleHeatmap( selection ) { +export default function bubbleHeatmap( selection ) { var /** * Data to plot. Assumed to be a object, where each key corresponds to a cell @@ -220,7 +218,7 @@ function bubbleHeatmap( selection ) { */ cellKeys, /** - * Stores the list of unique xValues + * Stores the list of utils.arr.unique xValues * Calculated after bubbleHeatmap called. * @param {string[]} [xValues=undefined] * @memberof bubbleHeatmap# @@ -228,7 +226,7 @@ function bubbleHeatmap( selection ) { */ xValues, /** - * Stores the list of unique yValues + * Stores the list of utils.arr.unique yValues * Calculated after bubbleHeatmap called. * @param {string[]} [yValues=undefined] * @memberof bubbleHeatmap# @@ -236,7 +234,7 @@ function bubbleHeatmap( selection ) { */ yValues, /** - * Stores the list of unique rValues + * Stores the list of utils.arr.unique rValues * Calculated after bubbleHeatmap called. * @param {string[]} [rValues=undefined] * @memberof bubbleHeatmap# @@ -244,7 +242,7 @@ function bubbleHeatmap( selection ) { */ rValues, /** - * Stores the list of unique vValues + * Stores the list of utils.arr.unique vValues * Calculated after bubbleHeatmap called. * @param {string[]} [vValues=undefined] * @memberof bubbleHeatmap# @@ -673,19 +671,19 @@ function bubbleHeatmap( selection ) { function bhm() { var horizontalQ = true; // no orientation in heatmaps. Aids as placeholder in functions that take orient as a parameter var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY} - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); cellKeys = d3.keys(data); cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }) - log('bubbleHeatmap', 'cells are sorted by', cellKeys) + utils.con.log('bubbleHeatmap', 'cells are sorted by', cellKeys) - xValues = unique(cellKeys.map(xExtractor)); - yValues = unique(cellKeys.map(yExtractor)); - rValues = unique(cellKeys.map(rExtractor)); - vValues = unique(cellKeys.map(vExtractor)); - log('bubbleHeatmap', 'x and y keys are', {x: xValues, y:yValues}) + xValues = utils.arr.unique(cellKeys.map(xExtractor)); + yValues = utils.arr.unique(cellKeys.map(yExtractor)); + rValues = utils.arr.unique(cellKeys.map(rExtractor)); + vValues = utils.arr.unique(cellKeys.map(vExtractor)); + utils.con.log('bubbleHeatmap', 'x and y keys are', {x: xValues, y:yValues}) var xDim = xValues.length, yDim = yValues.length; @@ -694,11 +692,11 @@ function bubbleHeatmap( selection ) { var extent = [Math.min(...rValues) - domainPadding,Math.max(...rValues) + domainPadding]; - ySize = calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ) - xSize = calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ) - ySpacerSize = calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ) - xSpacerSize = calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ) - log('bubbleHeatmap', 'size of', {x: xSize, y: ySize}) + ySize = utils.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + xSize = utils.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + ySpacerSize = utils.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ) + xSpacerSize = utils.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ) + utils.con.log('bubbleHeatmap', 'size of', {x: xSize, y: ySize}) scale.domain(extent).range([Math.min(minObjectSize/2, Math.min(ySize, xSize)/2), Math.min(ySize, xSize)/2]) @@ -706,7 +704,7 @@ function bubbleHeatmap( selection ) { var ySpacer = groupingSpacer() .horizontalQ(false) .moveby('category').numberOfObjects(yDim) - .objectClass(hypenate(objectClass, 'row')) + .objectClass(utils.str.hypenate(objectClass, 'row')) .objectSize(ySize).spacerSize(ySpacerSize) .transitionDuration(transitionDuration).easeFunc(easeFunc) .namespace('row') @@ -720,7 +718,7 @@ function bubbleHeatmap( selection ) { ySpacer(container, yValues, 0) - container.selectAll('g.'+hypenate(objectClass, 'row')) + container.selectAll('g.'+utils.str.hypenate(objectClass, 'row')) .each(function(d, i){ xSpacer(d3.select(this), xValues, 0) }) @@ -735,7 +733,7 @@ function bubbleHeatmap( selection ) { : colorFunction.dataExtent([0, Math.max(...vValues)]) cells.each(function(key, i) { - log('bubbleHeatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}) + utils.con.log('bubbleHeatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}) var t = d3.select(this), currentData = data[key], @@ -745,9 +743,9 @@ function bubbleHeatmap( selection ) { fillColor = colorFunction(key, value, i, 'fill'), // prevent duplicate computation strokeColor = colorFunction(key, value, i, 'stroke') - log('bubbleHeatmap', 'radius',{radius: radius, scaled: scale(radius), extent: extent, range:scale.range()}) + utils.con.log('bubbleHeatmap', 'radius',{radius: radius, scaled: scale(radius), extent: extent, range:scale.range()}) - var c = safeSelect(t, 'circle', hypenate(objectClass,'circle')) + var c = utils.sel.safeSelect(t, 'circle', utils.str.hypenate(objectClass,'circle')) c.attr('cx', xSize / 2) .attr('cy', ySize / 2 ) .attr('r', scale(radius)) @@ -757,10 +755,10 @@ function bubbleHeatmap( selection ) { }) - tooltip.selection(cells.selectAll('circle.'+hypenate(objectClass, 'circle'))) + tooltip.selection(cells.selectAll('circle.'+utils.str.hypenate(objectClass, 'circle'))) .data(data) // .keys(['r', 'v']) - // .header(function(d, i){return hypenate(data[d][xKey], data[d][yKey]) }) + // .header(function(d, i){return utils.str.hypenate(data[d][xKey], data[d][yKey]) }) tooltip() @@ -769,5 +767,3 @@ function bubbleHeatmap( selection ) { return bhm; } - -export {bubbleHeatmap} diff --git a/src/scripts/modules/heatmap.js b/src/modules/charts/heatmap.js similarity index 92% rename from src/scripts/modules/heatmap.js rename to src/modules/charts/heatmap.js index a55c7f6..57777c0 100644 --- a/src/scripts/modules/heatmap.js +++ b/src/modules/charts/heatmap.js @@ -1,9 +1,7 @@ -import {hypenate, safeSelect} from './helpers'; -import {setupContainer, calculateWidthOfObject, calculateWidthOfSpacer, log} from './utils'; -import {unique} from './array-functions'; -import {groupingSpacer} from './grouping-spacer'; -import {colorFunction as CF} from './color-function'; -import {tooltip as TTip} from './tooltip'; +import utils from '../utils/index.js' +import groupingSpacer from '../grouping-spacer'; +import CF from '../color-function'; +import TTip from '../tooltip'; /** @@ -15,7 +13,7 @@ import {tooltip as TTip} from './tooltip'; * @namespace heatmap * @returns {function} heatmap */ -function heatmap( selection ) { +export default function heatmap( selection ) { var /** * Data to plot. Assumed to be a object, where each key corresponds to a cell @@ -204,7 +202,7 @@ function heatmap( selection ) { */ cellKeys, /** - * Stores the list of unique xValues + * Stores the list of utils.arr.unique xValues * Calculated after heatmap called. * @param {string[]} [xValues=undefined] * @memberof heatmap# @@ -212,7 +210,7 @@ function heatmap( selection ) { */ xValues, /** - * Stores the list of unique yValues + * Stores the list of utils.arr.unique yValues * Calculated after heatmap called. * @param {string[]} [yValues=undefined] * @memberof heatmap# @@ -220,7 +218,7 @@ function heatmap( selection ) { */ yValues, /** - * Stores the list of unique vValues + * Stores the list of utils.arr.unique vValues * Calculated after heatmap called. * @param {string[]} [vValues=undefined] * @memberof heatmap# @@ -619,29 +617,29 @@ function heatmap( selection ) { function hm() { var horizontalQ = true; // no orientation in heatmaps. Aids as placeholder in functions that take orient as a parameter var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY} - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); cellKeys = d3.keys(data); - xValues = unique(cellKeys.map(xExtractor)); - yValues = unique(cellKeys.map(yExtractor)); - vValues = unique(cellKeys.map(vExtractor)); + xValues = utils.arr.unique(cellKeys.map(xExtractor)); + yValues = utils.arr.unique(cellKeys.map(yExtractor)); + vValues = utils.arr.unique(cellKeys.map(vExtractor)); cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }) - log('heatmap', 'cells are sorted by', cellKeys) + utils.con.log('heatmap', 'cells are sorted by', cellKeys) - log('heatmap', 'x and y keys are', {x: xValues, y:yValues}) + utils.con.log('heatmap', 'x and y keys are', {x: xValues, y:yValues}) var xDim = xValues.length, yDim = yValues.length; - ySize = calculateWidthOfObject(spaceY, yDim, yMinObjectSize, yMaxObjectSize, objectSpacer, overflowQ) - xSize = calculateWidthOfObject(spaceX, xDim, xMinObjectSize, xMaxObjectSize, objectSpacer, overflowQ) - ySpacerSize = calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ) - xSpacerSize = calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ) + ySize = utils.math.calculateWidthOfObject(spaceY, yDim, yMinObjectSize, yMaxObjectSize, objectSpacer, overflowQ) + xSize = utils.math.calculateWidthOfObject(spaceX, xDim, xMinObjectSize, xMaxObjectSize, objectSpacer, overflowQ) + ySpacerSize = utils.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ) + xSpacerSize = utils.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ) // console.table({ // x:{ // object: xSize, @@ -655,7 +653,7 @@ function heatmap( selection ) { // } // // }) - log('heatmap', 'size of', {x: xSize, y: ySize}) + utils.con.log('heatmap', 'size of', {x: xSize, y: ySize}) @@ -663,7 +661,7 @@ function heatmap( selection ) { .horizontalQ(false) .moveby('category') .numberOfObjects(yDim) - .objectClass(hypenate(objectClass, 'row')) + .objectClass(utils.str.hypenate(objectClass, 'row')) .objectSize(ySize + ySpacerSize) .spacerSize(0) .transitionDuration(transitionDuration) @@ -682,7 +680,7 @@ function heatmap( selection ) { ySpacer(container, yValues, 0) - container.selectAll('g.'+hypenate(objectClass, 'row')) + container.selectAll('g.'+utils.str.hypenate(objectClass, 'row')) .each(function(d, i){ xSpacer(d3.select(this), xValues, 0) }) var cells = container.selectAll('g:not(.to-remove).'+objectClass) @@ -725,7 +723,7 @@ function heatmap( selection ) { : colorFunction.dataExtent([0, Math.max(...vValues)]) cells.each(function(key, i) { - log('heatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}) + utils.con.log('heatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}) var t = d3.select(this) if (key == undefined) {return} @@ -735,7 +733,7 @@ function heatmap( selection ) { fillColor = colorFunction(key, value, i, 'fill'), // prevent duplicate computation strokeColor = colorFunction(key, value, i, 'stroke') - var c = safeSelect(t, 'rect', hypenate(objectClass,'rect')) + var c = utils.sel.safeSelect(t, 'rect', utils.str.hypenate(objectClass,'rect')) c.attr('width', xSize + xSpacerSize - objectStrokeWidth) .attr('height', ySize + ySpacerSize - objectStrokeWidth) .attr('fill', fillColor) @@ -746,10 +744,10 @@ function heatmap( selection ) { }) - tooltip.selection(cells.selectAll('rect.'+hypenate(objectClass, 'rect'))) + tooltip.selection(cells.selectAll('rect.'+utils.str.hypenate(objectClass, 'rect'))) .data(data) // .keys(['r', 'v']) - // .header(function(d, i){return hypenate(data[d][xKey], data[d][yKey]) }) + // .header(function(d, i){return utils.str.hypenate(data[d][xKey], data[d][yKey]) }) tooltip() @@ -758,5 +756,3 @@ function heatmap( selection ) { return hm; } - -export {heatmap} diff --git a/src/modules/charts/index.js b/src/modules/charts/index.js new file mode 100644 index 0000000..05d8b08 --- /dev/null +++ b/src/modules/charts/index.js @@ -0,0 +1,14 @@ +import scatter from './scatter' +import bar from './bar' +import bubble from './bubble-heatmap' +import heatmap from './heatmap' +import violin from './violin' +import {neededViolinValues} from './violin' +import upset from './upset' + +let charts = { + scatter, bar, bubble, heatmap, violin, neededViolinValues, upset +} + +export {scatter, bar, bubble, heatmap, violin, neededViolinValues, upset} +export default charts diff --git a/src/scripts/modules/scatter.js b/src/modules/charts/scatter.js similarity index 97% rename from src/scripts/modules/scatter.js rename to src/modules/charts/scatter.js index 2dcb81b..07aea69 100644 --- a/src/scripts/modules/scatter.js +++ b/src/modules/charts/scatter.js @@ -1,9 +1,6 @@ -import {hypenate, safeSelect} from './helpers'; -import {setupContainer, calculateWidthOfObject, calculateWidthOfSpacer} from './utils'; -import {unique, flatten} from './array-functions'; -import {groupingSpacer} from './grouping-spacer'; -import {colorFunction as CF} from './color-function'; -import {tooltip as TTip} from './tooltip'; +import utils from '../utils/index.js' +import CF from '../color-function'; +import TTip from '../tooltip'; /******************************************************************************* ** ** ** ** @@ -20,7 +17,7 @@ import {tooltip as TTip} from './tooltip'; * @namespace scatter * @returns {function} scatter */ -export function scatter ( selection ) { +export default function scatter ( selection ) { var /** @@ -500,7 +497,7 @@ export function scatter ( selection ) { function scatter() { // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY} - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); pointKeys = d3.keys(data) diff --git a/src/scripts/modules/upset.js b/src/modules/charts/upset.js similarity index 84% rename from src/scripts/modules/upset.js rename to src/modules/charts/upset.js index 506f6e3..aedfde4 100644 --- a/src/scripts/modules/upset.js +++ b/src/modules/charts/upset.js @@ -1,10 +1,8 @@ -import {hypenate, safeSelect} from './helpers'; -import {setupContainer, calculateWidthOfObject, calculateWidthOfSpacer, log} from './utils'; -import {unique, flatten} from './array-functions'; -import {groupingSpacer} from './grouping-spacer'; -import {colorFunction as CF} from './color-function'; -import {tooltip as TTip} from './tooltip'; -export function upset ( selection ) { +import utils from '../utils/index.js' +import groupingSpacer from '../grouping-spacer'; +import CF from '../color-function'; +import TTip from '../tooltip'; +export default function upset ( selection ) { var data, orient='horizontal', @@ -88,12 +86,12 @@ export function upset ( selection ) { // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY} - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); cellKeys = d3.keys(data) - setValues = unique(cellKeys.map(setExtractor)).sort() - intersectionValues = unique(cellKeys.map(intersectionExtractor)).sort().sort(function(a, b){ + setValues = utils.arr.unique(cellKeys.map(setExtractor)).sort() + intersectionValues = utils.arr.unique(cellKeys.map(intersectionExtractor)).sort().sort(function(a, b){ return a.split(';').length - b.split(';').length }) @@ -112,13 +110,13 @@ export function upset ( selection ) { xDim = horizontalQ ? xValues.length : yValues.length, yDim = horizontalQ ? yValues.length : xValues.length - // console.log(xValues, yValues) + // console.utils.con.log(xValues, yValues) - xObjectSize = calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, xObjectSpacer, overflowQ) - yObjectSize = calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, yObjectSpacer, overflowQ) - xSpacerSize = calculateWidthOfSpacer(xValues, spaceX, xObjectSize, xDim, xObjectSpacer, overflowQ) - ySpacerSize = calculateWidthOfSpacer(yValues, spaceY, yObjectSize, yDim, yObjectSpacer, overflowQ) + xObjectSize = utils.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, xObjectSpacer, overflowQ) + yObjectSize = utils.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, yObjectSpacer, overflowQ) + xSpacerSize = utils.math.calculateWidthOfSpacer(xValues, spaceX, xObjectSize, xDim, xObjectSpacer, overflowQ) + ySpacerSize = utils.math.calculateWidthOfSpacer(yValues, spaceY, yObjectSize, yDim, yObjectSpacer, overflowQ) var ySpacer = groupingSpacer() .horizontalQ(false) @@ -137,17 +135,17 @@ export function upset ( selection ) { if (verticalQ) { xSpacer.objectClass(objectClass) - ySpacer.namespace('across').objectClass(hypenate(objectClass, 'across')) + ySpacer.namespace('across').objectClass(utils.str.hypenate(objectClass, 'across')) ySpacer(container, yValues, 0) - container.selectAll('g.'+hypenate(objectClass, 'across')) + container.selectAll('g.'+utils.str.hypenate(objectClass, 'across')) .each(function(d, i){ xSpacer(d3.select(this), xValues, 0) }) } else { - xSpacer.namespace('across').objectClass(hypenate(objectClass, 'across')) + xSpacer.namespace('across').objectClass(utils.str.hypenate(objectClass, 'across')) ySpacer.objectClass(objectClass) xSpacer(container, xValues, 0) - container.selectAll('g.'+hypenate(objectClass, 'across')) + container.selectAll('g.'+utils.str.hypenate(objectClass, 'across')) .each(function(d, i){ ySpacer(d3.select(this), yValues, 0) }) } @@ -167,11 +165,11 @@ export function upset ( selection ) { // } else { // positionedCellKeys.push(lookupValue) // } - // console.log(i, j, lookupValue) + // console.utils.con.log(i, j, lookupValue) // } // } // - // // console.log(positionedCellKeys) + // // console.utils.con.log(positionedCellKeys) // // cells.data(positionedCellKeys); @@ -183,17 +181,17 @@ export function upset ( selection ) { if (key == undefined) {return } var currentData = data[key] - // console.log(key, currentData) + // console.utils.con.log(key, currentData) var set = setExtractor(key, i), intersection = intersectionExtractor(key, i) - // console.log(set, intersection) + // console.utils.con.log(set, intersection) t.classed(intersection, true) t.classed(set, true) - var c = safeSelect(t, 'circle', hypenate(objectClass,'circle')) + var c = utils.sel.safeSelect(t, 'circle', utils.str.hypenate(objectClass,'circle')) c.attr('cx', xObjectSize / 2) .attr('cy', yObjectSize / 2 ) .attr('r', radius == undefined ? Math.min(xObjectSize, yObjectSize) / 2 : radius) diff --git a/src/scripts/modules/violin.js b/src/modules/charts/violin.js similarity index 92% rename from src/scripts/modules/violin.js rename to src/modules/charts/violin.js index b6b7eea..564e493 100644 --- a/src/scripts/modules/violin.js +++ b/src/modules/charts/violin.js @@ -1,9 +1,6 @@ -import {hypenate, safeSelect, modifyHexidecimalColorLuminance, extractViolinValues, quartiles} from './helpers'; -import {setupContainer, calculateWidthOfObject, calculateWidthOfSpacer} from './utils'; -import {unique, hasQ, flatten, whichBin} from './array-functions'; -import {groupingSpacer} from './grouping-spacer'; -import {colorFunction as CF} from './color-function'; -import {tooltip as TTip} from './tooltip'; +import groupingSpacer from '../grouping-spacer'; +import CF from '../color-function'; +import TTip from '../tooltip'; /******************************************************************************* ** ** @@ -22,7 +19,7 @@ import {tooltip as TTip} from './tooltip'; * @namespace violin * @returns {function} violin */ -export function violin( selection ) { +export default function violin( selection ) { var /** * Data to plot. Assumed to be a object, where each key corresponds to a violin @@ -156,9 +153,9 @@ export function violin( selection ) { */ pointColorFunc = function (d, type, base, min, max) { var minMaxHexScale = d3.scaleLinear().domain([min, max]).range([-0.25, 0.05]) - var scaledColor = modifyHexidecimalColorLuminance(base.replace('#', ''), minMaxHexScale(d)) + var scaledColor = utils.color.modifyHexidecimalColorLuminance(base.replace('#', ''), minMaxHexScale(d)) var mod = type == "stroke" ? 0 : 0.25 - return modifyHexidecimalColorLuminance(scaledColor.replace('#', ''), mod) + return utils.color.modifyHexidecimalColorLuminance(scaledColor.replace('#', ''), mod) }, /** @@ -213,12 +210,12 @@ export function violin( selection ) { easeFunc = d3.easeExp, /** - * The key containing the quartiles + * The key containing the utils.math.quartiles * @param {string} [quartilesKey=undefined] * @memberof violin# * @property */ - quartilesKey = "quartiles", + quartilesKey = "utils.math.quartiles", /** * The keys corresponding to each quartile * @param {string[]} [quartileKeys=["Q0", "Q1", "Q2", "Q3", "Q4"]] @@ -578,7 +575,7 @@ export function violin( selection ) { * @returns {violin | string} * @memberof violin * @property - * by default quartileKey = "quartiles" + * by default quartileKey = "utils.math.quartiles" */ violin.quartileKey = function(_) { return arguments.length ? (quartileKey = _, violin) : quartileKey; }; /** @@ -666,14 +663,14 @@ export function violin( selection ) { // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY} - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); // if grouping is undefined sort violinKeys by sortingFunction var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping // console.log(ordered) - violinKeys = flatten(ordered) + violinKeys = utils.arr.flatten(ordered) var calcValues = neededViolinValues() @@ -689,8 +686,8 @@ export function violin( selection ) { var numberOfObjects = violinKeys.length - var min = [].concat(...violinKeys.map(function(k, i){return data[k].quartiles[quartileKeys[0]]})) - var max = [].concat(...violinKeys.map(function(k, i){return data[k].quartiles[quartileKeys[quartileKeys.length - 1]]})) + var min = [].concat(...violinKeys.map(function(k, i){return data[k].utils.math.quartiles[quartileKeys[0]]})) + var max = [].concat(...violinKeys.map(function(k, i){return data[k].utils.math.quartiles[quartileKeys[quartileKeys.length - 1]]})) var extent = [Math.min(...min) - domainPadding, Math.max(...max) + domainPadding] // console.log(extent, violinValues, ordered) @@ -698,9 +695,9 @@ export function violin( selection ) { scale.domain(extent).range(horizontalQ ? [0,spaceY] : [0, spaceX]) var space = horizontalQ ? spaceX : spaceY // calculate object size - objectSize = calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) // calculate spacer size if needed - spacerSize = calculateWidthOfSpacer(ordered, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + spacerSize = utils.math.calculateWidthOfSpacer(ordered, space, objectSize, numberOfObjects, objectSpacer, overflowQ) // make the nested groups var spacerFunction = groupingSpacer() .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) @@ -715,7 +712,7 @@ export function violin( selection ) { // for color function var parentIndexArray = [] container.selectAll('g:not(.to-remove).'+objectClass) - .each(function(d, i){if (hasQ(violinKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')))}}) + .each(function(d, i){if (utils.arr.hasQ(violinKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')))}}) // update color function colorFunction = colorFunction.colorBy() == 'index' @@ -746,20 +743,20 @@ export function violin( selection ) { var t = d3.select(this), currentData = data[key] // needed because bug in selecting .to-remove - if (!hasQ(violinKeys, key)) {return} + if (!utils.arr.hasQ(violinKeys, key)) {return} var i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), fillColor = colorFunction(key, currentData, i, 'fill'), // prevent duplicate computation strokeColor = colorFunction(key, currentData, i, 'stroke'), - area = safeSelect(t, 'g', 'area'), - la = safeSelect(area, 'path', 'left'), - ra = safeSelect(area, 'path', 'right'), - quarts = safeSelect(t, 'g', 'quarts'), - lq3 = safeSelect(quarts, 'line', 'q3'), - lq1 = safeSelect(quarts, 'line', 'q1'), - q3 = currentData.quartiles[quartileKeys[3]], - q2 = currentData.quartiles[quartileKeys[2]], - q1 = currentData.quartiles[quartileKeys[1]] + area = utils.sel.safeSelect(t, 'g', 'area'), + la = utils.sel.safeSelect(area, 'path', 'left'), + ra = utils.sel.safeSelect(area, 'path', 'right'), + quarts = utils.sel.safeSelect(t, 'g', 'quarts'), + lq3 = utils.sel.safeSelect(quarts, 'line', 'q3'), + lq1 = utils.sel.safeSelect(quarts, 'line', 'q1'), + q3 = currentData.utils.math.quartiles[quartileKeys[3]], + q2 = currentData.utils.math.quartiles[quartileKeys[2]], + q1 = currentData.utils.math.quartiles[quartileKeys[1]] t.attr('transform', horizontalQ ? 'translate('+objectSize / 2+',0)' : 'translate(0,'+objectSize / 2+')' ) // draw curve @@ -786,7 +783,7 @@ export function violin( selection ) { }) if (pointsQ) { - var ptsContainer = safeSelect(t, 'g', 'points') + var ptsContainer = utils.sel.safeSelect(t, 'g', 'points') var pts = ptsContainer.selectAll('.point').data(currentData.pointKeys) pts.on('mouseover', null) @@ -819,7 +816,7 @@ export function violin( selection ) { .attr('cy', function(pointKey, ii){ var dd = currentData.pointValues[ii] if (horizontalQ) { return scale(extent[1]) - scale(dd) } - var j = whichBin(currentData.binned, dd) + var j = utils.arr.whichBin(currentData.binned, dd) var r = Math.random() var n = vScale(r * currentData.frequencies[j] * 0.5) var k = Math.random() > 0.5 ? n : -n @@ -828,7 +825,7 @@ export function violin( selection ) { .attr('cx', function(pointKey, ii){ var dd = currentData.pointValues[ii] if (horizontalQ) { - var j = whichBin(currentData.binned, dd) + var j = utils.arr.whichBin(currentData.binned, dd) var r = Math.random() var n = vScale(r * currentData.frequencies[j] * 0.5) var k = Math.random() > 0.5 ? n : -n @@ -876,11 +873,11 @@ export function violin( selection ) { tooltip() if (tooltip.values() == undefined) { tooltip.values([ - function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] } + function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] } ]) } @@ -900,7 +897,7 @@ export function violin( selection ) { * @returns {function} calculateViolinValues * @namespace neededViolinValues */ -function neededViolinValues() { +export function neededViolinValues() { var /** * Whether or not the orientation of the violins are horizontal @@ -911,7 +908,7 @@ function neededViolinValues() { */ horizontalQ = true, /** - * Keys to be put into the quartiles if they need to be calculated. + * Keys to be put into the utils.math.quartiles if they need to be calculated. * (see {@link violin#quartileKeys}) * @param {Object} [quartileKeys=['Q0', 'Q1', 'Q2', 'Q3', 'Q4']] * @memberof neededViolinValues# @@ -998,7 +995,7 @@ function neededViolinValues() { * * data[violinKey].contour // the points depicting the contour of 1/2 of the violin * - * data[violinKey].quartiles // the quartiles of the points' values + * data[violinKey].utils.math.quartiles // the utils.math.quartiles of the points' values * * data[violinKey].pointKeys // the keys associated with the points * @@ -1017,8 +1014,8 @@ function neededViolinValues() { // the numerical values of those points var violinPointsValues = violinPointsKeys.map(function(pk, i){return violinPointValueExtractor(pk, violinPoints)}) - // quartiles of those points - var pointQuartiles = quartiles(violinPointsValues, quartileKeys) + // utils.math.quartiles of those points + var pointQuartiles = utils.math.quartiles(violinPointsValues, quartileKeys) // binned points var binned = d3.histogram()(violinPointsValues) @@ -1039,7 +1036,7 @@ function neededViolinValues() { violinData.binned = binned; violinData.frequencies = frequencies violinData.contour = violinContourPoints - violinData.quartiles = pointQuartiles + violinData.utils.math.quartiles = pointQuartiles violinData.pointKeys = violinPointsKeys violinData.pointValues = violinPointsValues } diff --git a/src/scripts/modules/color-function.js b/src/modules/color-function.js similarity index 98% rename from src/scripts/modules/color-function.js rename to src/modules/color-function.js index 5bef0c5..77035b7 100644 --- a/src/scripts/modules/color-function.js +++ b/src/modules/color-function.js @@ -1,4 +1,4 @@ -import {modifyHexidecimalColorLuminance} from './helpers'; +import {color} from './utils/index.js' /** * Creates a colorFunction @@ -6,7 +6,7 @@ import {modifyHexidecimalColorLuminance} from './helpers'; * @namespace colorFunction * @returns {function} colorFunction */ -export function colorFunction() { +export default function colorFunction() { var data, @@ -30,7 +30,7 @@ export function colorFunction() { * @memberof colorFunction# * @property */ - modifyOpacity = modifyHexidecimalColorLuminance, + modifyOpacity = color.modifyHexidecimalColorLuminance, /** * How to modify color for stroke * @param {number} [strokeOpacity=0] diff --git a/src/scripts/modules/d3-prototypes.js b/src/modules/d3-prototypes.js similarity index 95% rename from src/scripts/modules/d3-prototypes.js rename to src/modules/d3-prototypes.js index db19bf0..3033b24 100644 --- a/src/scripts/modules/d3-prototypes.js +++ b/src/modules/d3-prototypes.js @@ -1,4 +1,4 @@ -import {getContainingSVG} from "./helpers"; +import utils from './utils/index.js' // import * as d3 from "d3"; /******************************************************************************* ** ** @@ -13,7 +13,7 @@ import {getContainingSVG} from "./helpers"; * @augments d3.selection * @returns {Element} which is the svg tag, not the d3 selection of that tag */ -d3.selection.prototype.thisSVG = function() { return getContainingSVG(this.node()); } +d3.selection.prototype.thisSVG = function() { return utils.sel.getContainingSVG(this.node()); } /** diff --git a/src/scripts/modules/grouping-spacer.js b/src/modules/grouping-spacer.js similarity index 97% rename from src/scripts/modules/grouping-spacer.js rename to src/modules/grouping-spacer.js index acb6c6f..e5f808e 100644 --- a/src/scripts/modules/grouping-spacer.js +++ b/src/modules/grouping-spacer.js @@ -1,4 +1,4 @@ -import {log, warn, error, info} from './utils'; +import {con} from './utils/index.js' /******************************************************************************* ** ** ** ** @@ -12,7 +12,7 @@ import {log, warn, error, info} from './utils'; * (see {@link groupingSpacer#recursivelyPosition}) * @namespace groupingSpacer */ -export function groupingSpacer() { +export default function groupingSpacer() { var /*@var {boolean} horizontalQ @default*/ @@ -125,7 +125,7 @@ export function groupingSpacer() { x = horizontalQ ? window.outerWidth : 0, y = !horizontalQ ? window.outerWidth : 0, t = 'translate('+x+','+y+')' - // if(y == undefined) {console.log(cur.node(), y, d)} + // if(y == undefined) {console.con.log(cur.node(), y, d)} return t }) }, @@ -147,7 +147,7 @@ export function groupingSpacer() { * }).remove() */ exitFunction = function(cur){ - log("groupingSpacer", "exiting with", {current: cur, currentNode: cur.node()}) + con.log("groupingSpacer", "exiting with", {current: cur, currentNode: cur.node()}) cur.selectAll('g').classed('to-remove', true) cur.transition().duration(transitionDuration*0.9).ease(easeFunc) @@ -158,7 +158,7 @@ export function groupingSpacer() { x = horizontalQ ? window.outerWidth : 0, y = !horizontalQ ? window.outerWidth : 0, t = 'translate('+x+','+y+')' - // if(y == undefined) {console.log(cur.node(), y, d)} + // if(y == undefined) {console.con.log(cur.node(), y, d)} return t }).remove() } diff --git a/src/scripts/modules/categorical-legend.js b/src/modules/legends/categorical-legend.js similarity index 93% rename from src/scripts/modules/categorical-legend.js rename to src/modules/legends/categorical-legend.js index f20fa7c..7257d9f 100644 --- a/src/scripts/modules/categorical-legend.js +++ b/src/modules/legends/categorical-legend.js @@ -1,10 +1,9 @@ -import {hypenate, safeSelect, round, interpolateColors} from './helpers'; -import {setupContainer, calculateWidthOfObject, calculateWidthOfSpacer} from './utils'; -import {colorFunction as CF} from './color-function'; -import {groupingSpacer} from './grouping-spacer'; -import {unique, flatten} from './array-functions'; +import utils from '../utils/index.js' +import CF from '../color-function'; +import groupingSpacer from '../grouping-spacer'; -export function categoricLegend( selection ) { + +export default function categoricLegend( selection ) { var categories, @@ -351,7 +350,7 @@ export function categoricLegend( selection ) { var verticalQ = !horizontalQ // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY} - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); colorFunction.dataExtent([0, categories.length - 1]) @@ -364,13 +363,13 @@ export function categoricLegend( selection ) { // if grouping is undefined sort barKeys by sortingFunction var ordered = (grouping == undefined) ? categories.sort(sortingFunction) : grouping // ordered might be nested depending on grouping - var catKeys = flatten(ordered) + var catKeys = utils.arr.flatten(ordered) var space = horizontalQ ? spaceX : spaceY // calculate object size - var objectSize = calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + var objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) // calculate spacer size if needed - var spacerSize = calculateWidthOfSpacer(catKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + var spacerSize = utils.math.calculateWidthOfSpacer(catKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) // make the nested groups var spacerFunction = groupingSpacer() .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) @@ -383,7 +382,7 @@ export function categoricLegend( selection ) { container.selectAll('g:not(.to-remove).'+objectClass).each(function(cat, i) { var t = d3.select(this) - var c = safeSelect(t, 'circle') + var c = utils.sel.safeSelect(t, 'circle') var fillColor = colorFunction(undefined, cat, i, 'fill'), // prevent duplicate computation strokeColor = colorFunction(undefined, cat, i, 'stroke') @@ -401,7 +400,7 @@ export function categoricLegend( selection ) { .attr('stroke', strokeColor) .attr('stroke-width', bubbleStrokeWidth) - var text = safeSelect(t, 'text') + var text = utils.sel.safeSelect(t, 'text') text.text(cat) .attr('text-anchor', 'middle') .attr("transform", function(d, i){ diff --git a/src/modules/legends/index.js b/src/modules/legends/index.js new file mode 100644 index 0000000..6e8846d --- /dev/null +++ b/src/modules/legends/index.js @@ -0,0 +1,9 @@ +import categorical from './categorical-legend' +import numeric from './numeric-legend' + +let legends = { + categorical, numeric +} + +export {categorical, numeric} +export default legends diff --git a/src/scripts/modules/numeric-legend.js b/src/modules/legends/numeric-legend.js similarity index 75% rename from src/scripts/modules/numeric-legend.js rename to src/modules/legends/numeric-legend.js index 6db2517..bd4824e 100644 --- a/src/scripts/modules/numeric-legend.js +++ b/src/modules/legends/numeric-legend.js @@ -1,9 +1,7 @@ -import {hypenate, safeSelect, round, interpolateColors} from './helpers'; -import {setupContainer} from './utils'; -import {colorFunction as CF} from './color-function'; +import utils from '../utils/index.js' +import CF from '../color-function'; - -export function numericLegend( selection ) { +export default function numericLegend( selection ) { var min=0, @@ -32,15 +30,15 @@ export function numericLegend( selection ) { function legend() { // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY} - var container = setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - var defs = safeSelect(selection, 'defs') - var linearGradient = safeSelect(defs, 'linearGradient') + var defs = utils.sel.safeSelect(selection, 'defs') + var linearGradient = utils.sel.safeSelect(defs, 'linearGradient') .attr("x1", "0%") .attr("y1", "100%") .attr("x2", "0%") .attr("y2", "0%") - .attr('id', hypenate(namespace,'numerical-legend-gradient')) + .attr('id', utils.str.hypenate(namespace,'numerical-legend-gradient')) colorFunction.dataExtent([min, max]) @@ -58,18 +56,18 @@ export function numericLegend( selection ) { - var rect = safeSelect(container, 'rect', 'legend') + var rect = utils.sel.safeSelect(container, 'rect', 'legend') .attr('transform', 'translate(0,'+fontSize+')') - .style("fill", "url(#"+hypenate(namespace,'numerical-legend-gradient')+")") + .style("fill", "url(#"+utils.str.hypenate(namespace,'numerical-legend-gradient')+")") .attr('x', 0) .attr('y', 0) .attr('width', spaceX) .attr('height', spaceY - fontSize*2) .on('mousemove', function(d, i){legendMousemove(d, i, rect, d3.select(this))}) - .on('mouseout', function(d, i){ d3.select("#"+hypenate(namespace,'legend-tooltip')).remove() }) + .on('mouseout', function(d, i){ d3.select("#"+utils.str.hypenate(namespace,'legend-tooltip')).remove() }) - var minText = safeSelect(container, 'text', 'min') - .text(round(min, 2)) + var minText = utils.sel.safeSelect(container, 'text', 'min') + .text(utils.math.round(min, 2)) .attr('text-anchor', 'middle') .attr("font-size", fontSize+'px') .attr('transform', function(d, i){ @@ -80,8 +78,8 @@ export function numericLegend( selection ) { return t }) - var maxText = safeSelect(container, 'text', 'max') - .text(round(max, 2)) + var maxText = utils.sel.safeSelect(container, 'text', 'max') + .text(utils.math.round(max, 2)) .attr('text-anchor', 'middle') .attr("font-size", fontSize+'px') .attr('transform', function(d, i){ @@ -102,13 +100,13 @@ export function numericLegend( selection ) { .domain([0, rect.attr('height')]) .range([max, min]) var m = d3.mouse(rect.node()) - var v = round(s(m[1]),roundTo) + var v = utils.math.round(s(m[1]),roundTo) var strokeColor = colorFunction(undefined, v, undefined, 'stroke') var fillColor = colorFunction(undefined, v, undefined, 'fill') - var div = safeSelect(d3.select('body'), 'div', hypenate(namespace,'legend-tooltip')) - .attr('id', hypenate(namespace,'legend-tooltip')) + var div = utils.sel.safeSelect(d3.select('body'), 'div', utils.str.hypenate(namespace,'legend-tooltip')) + .attr('id', utils.str.hypenate(namespace,'legend-tooltip')) .style('position', 'absolute') .style('left', (d3.event.pageX+15)+'px') .style('top', (d3.event.pageY+15)+'px') @@ -128,7 +126,7 @@ export function numericLegend( selection ) { .style('border-style', 'solid') .style('border-width', 2) - var text = safeSelect(div, 'div') + var text = utils.sel.safeSelect(div, 'div') .text(v) .style('color', textColor) .style('align-self', 'center') diff --git a/src/scripts/modules/tooltip.js b/src/modules/tooltip.js similarity index 85% rename from src/scripts/modules/tooltip.js rename to src/modules/tooltip.js index 77262d5..2ed0abd 100644 --- a/src/scripts/modules/tooltip.js +++ b/src/modules/tooltip.js @@ -1,5 +1,4 @@ -import {safeSelect, round} from './helpers'; -import {log, warn, info, error, consoleGroup, consoleGroupEnd} from './utils'; +import {sel, con, math} from './utils/index.js' /******************************************************************************* ** ** ** ** @@ -15,7 +14,7 @@ import {log, warn, info, error, consoleGroup, consoleGroupEnd} from './utils'; * @returns {tooltip} * @namespace tooltip */ -export function tooltip( selection ) { +export default function tooltip( selection ) { var keys, @@ -83,16 +82,16 @@ export function tooltip( selection ) { * @private */ function mousemove(key, i) { - consoleGroup('d3sm-tooltip') + con.consoleGroup('d3sm-tooltip') var currentData = data[key] var [x, y] = d3.mouse(d3.select("html").node()) - log('tooltip', 'mousemove detected',{key: key, index: i, x:x, y:y}) - log('tooltip', 'current data', currentData) + con.log('tooltip', 'mousemove detected',{key: key, index: i, x:x, y:y}) + con.log('tooltip', 'current data', currentData) - var div = safeSelect(d3.select('html'), 'tooltip', 'd3sm-tooltip') + var div = sel.safeSelect(d3.select('html'), 'tooltip', 'd3sm-tooltip') .classed('card', true) .style('max-width', '300px') .style('background-color', "#212529") @@ -100,14 +99,14 @@ export function tooltip( selection ) { - var cardBody = safeSelect(div, 'div', 'card-body') - var cardTitle = safeSelect(cardBody, 'h5', 'card-title') + var cardBody = sel.safeSelect(div, 'div', 'card-body') + var cardTitle = sel.safeSelect(cardBody, 'h5', 'card-title') .text(header == undefined ? key : typeof header == 'function' ? header(key, currentData, i) : header) .style('color', 'cyan') - var table = safeSelect(cardBody, 'table', 'table').classed('table-dark', true) - var tBody = safeSelect(table, 'tbody') + var table = sel.safeSelect(cardBody, 'table', 'table').classed('table-dark', true) + var tBody = sel.safeSelect(table, 'tbody') tBody = tBody.selectAll('tr') tBody= tBody.data(keys == undefined ? d3.keys(currentData): keys) @@ -120,20 +119,20 @@ export function tooltip( selection ) { .attr('tooltip-row-index', function(d, i){return i}) // tBody = tBody.merge(tr) - consoleGroup('tooltip-rows') + con.consoleGroup('tooltip-rows') tBody.selectAll('.tooltip-key').text(function(d, i){return d}) tBody.selectAll('tr .tooltip-value') .text(function(d, i){ - log('tooltip', 'trying to set value', {rowKey: d, rowIndex: i}) + con.log('tooltip', 'trying to set value', {rowKey: d, rowIndex: i}) var i = d3.select(this).attr('tooltip-row-index') var v = currentData[d]; if (values != undefined) {v = values[i]; if(typeof v == "function") {v = v(currentData, d)}} - return typeof v == 'number' ? round(v, 5) : v + return typeof v == 'number' ? math.round(v, 5) : v }) - consoleGroupEnd() - consoleGroupEnd() + con.consoleGroupEnd() + con.consoleGroupEnd() x += 15 // x += 15 diff --git a/src/scripts/modules/array-functions.js b/src/modules/utils/array.js similarity index 92% rename from src/scripts/modules/array-functions.js rename to src/modules/utils/array.js index 83cb781..fc66bcc 100644 --- a/src/scripts/modules/array-functions.js +++ b/src/modules/utils/array.js @@ -1,11 +1,12 @@ -import {uniqueElements} from './helpers'; -/******************************************************************************* -** ** -** ** -** PROTOTYPES ** -** ** -** ** -*******************************************************************************/ +/** +* Helper function for Array.filter to get unique elements of the array +* @param {*} value current value as mapping over array (self) +* @param {number} index current index in the array +* @param {Array} self passed array from Array.filter method +* @returns {boolean} whether or not value is the first of its kind (i.e. indexOf(value) == index) +*/ +export function uniqueElements(value, index, self) { return self.indexOf(value) === index; } + /** * This function tests to see if all elements of the passed array are true. * @param {Array} array of values diff --git a/src/modules/utils/colors.js b/src/modules/utils/colors.js new file mode 100644 index 0000000..2fb0faf --- /dev/null +++ b/src/modules/utils/colors.js @@ -0,0 +1,32 @@ +/** +* Modifies luminance of hexidecimal number +* @param {string} hex should be hexidecimal value with or without the proceeding octotrope +* @param {number} lum value to increase or decrease luminosity by +* @returns {string} updated hexidecimal value without the proceeding octotrope +*/ +export function modifyHexidecimalColorLuminance(hex, lum) { + // validate hex string + var hex = String(hex).replace(/[^0-9a-f]/gi, ''); + + if (hex.length < 6) { + hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; + } + lum = lum || 0; + + // convert to decimal and change luminosity + var rgb = '#', c, i; + for (i = 0; i < 3; i++) { + c = parseInt(hex.substr(i*2,2), 16); + c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); + rgb += ('00'+c).substr(c.length); + } + + return rgb; +} + +/** +* Maps arguments in to d3.interpolateRgbBasis +* @param arguments +* @returns {Function} +*/ +export function interpolateColors(){return d3.interpolateRgbBasis(arguments)} diff --git a/src/modules/utils/console.js b/src/modules/utils/console.js new file mode 100644 index 0000000..8da9cfa --- /dev/null +++ b/src/modules/utils/console.js @@ -0,0 +1,97 @@ +/** + * calls console.group if d3sm.debugQ == true + * @param {string} name of the group + * @returns {undefined} + */ +export function consoleGroup(name) { + if (window.d3sm.debugQ === true){ + console.group(name) + } +} + +/** + * calls console.groupEnd if d3sm.debugQ == true + * @returns {undefined} + */ +export function consoleGroupEnd() { + if (window.d3sm.debugQ === true){ + console.groupEnd() + } +} + +/** + * Calls console.log if d3sm.debugQ == true + * @param {string} func name of the function logging + * @param {string} msg to log + * @param {Object} data to be logged along side the message + * @returns {undefined} + */ +export function log(func, msg, data) { + if (window.d3sm.debugQ === true){ + console.log( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #6cd1ef', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ) + console.table(data) + // console.trace() + } +} + +/** + * Calls console.warn if d3sm.debugQ == true + * @param {string} func name of the function warning + * @param {string} msg to display + * @param {Object} data to be displayed along side the message + * @returns {undefined} + */ +export function warn(func, msg, data) { + if (window.d3sm.debugQ === true) + console.warn( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #ffd53e', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ) + console.table(data) +} +/** + * Calls the console.info if d3sm.debugQ == true + * @param {string} func name of the function providing info + * @param {string} msg to display + * @param {Object} data to be displayed along side the message + * @returns {undefined} + */ +export function info(func, msg, data) { + if (window.d3sm.debugQ) + console.info( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #009ccd', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ) + console.table(data) +} + + +/** + * Calls console.error if d3sm.debugQ == true + * @param {string} func name of the function which sends the error + * @param {string} msg to display + * @param {Object} data to be displayed along side the message + * @returns {undefined} + */ +export function error(func, msg, data) { + if (window.d3sm.debugQ) + console.error(`[d3sm::${func}]:\t${msg}\t%o`,data) +} diff --git a/src/modules/utils/index.js b/src/modules/utils/index.js new file mode 100644 index 0000000..e117b00 --- /dev/null +++ b/src/modules/utils/index.js @@ -0,0 +1,16 @@ +import * as arr from './array.js' +import * as color from './colors.js' +import * as math from './math.js' +import * as misc from './misc.js' +import * as sel from './selections.js' +import * as str from './strings.js' +import * as con from './console.js' +import * as paths from './paths.js' + + +let utils = { + arr, color, math, misc, sel, str, con, paths +} + +export {arr, color, math, misc, sel, str, con, paths} +export default utils diff --git a/src/modules/utils/math.js b/src/modules/utils/math.js new file mode 100644 index 0000000..eac6234 --- /dev/null +++ b/src/modules/utils/math.js @@ -0,0 +1,160 @@ +/** +* Rounds decimals of number to precision +* @param {number} number +* @param {number} precision +* @returns {number} rounded to precision +*/ +export function round(number, precision) { + var shift = function (number, precision, reverseShift) { + if (reverseShift) { + precision = -precision; + } + var numArray = ('' + number).split('e'); + return +(numArray[0] + 'e' + (numArray[1] ? (+numArray[1] + precision) : precision)); + }; + return shift(Math.round(shift(number, precision, false)), precision, true); +} + +/** +* evenly partitions the range [min, max] into n parts +* @param {number} min +* @param {number} max +* @param {number} n +* @returns {number[]} array of length n evenly partitioned between min and max +*/ +export function tickRange(min, max, n) { + var a = [min] + var d = max-min + var s = d / (n-1) + for (var i = 0; i < n-2; i++) { a.push(min + s * (i+1)) } + a.push(max) + return a +} + +/** +* @deprecated @see{@link tickRange} +* @param {number} min +* @param {number} max +* @param {number} parts +* @returns {number[]} array of length parts evenly partitioned between min and max +*/ +export function partitionRangeInto(min, max, parts) { + var diff = max - min + return Array(parts).map(function (e, i) { return min + diff / parts * i }) +} + + +export function euclideanDistance(p1, p2){ + var a = p1[0] - p2[0], b = p1[1] - p2[1] + return Math.sqrt(a*a + b*b) +} + +/** +* Calculated the quartiles of the passed data and stores them with qKeys +* @param {number[]} data list of numerical values +* @param {string[]} [qKeys=['q0', 'q1', 'q2', 'q3', 'q4']] how returned object with quartiles should be stored +* @returns {Object} with keys qKeys giving only the numerical values for the quartiles +*/ +export function quartiles(data, qKeys) { + var + q2 = d3.median(data), + lower = data.filter(x => x < q2), + upper = data.filter(x => x > q2), + + q1 = d3.median(lower), + q1 = q1 == undefined ? q2 : q1, + + q0 = d3.min(lower), + q0 = q0 == undefined ? q1 : q0, + + q3 = d3.median(upper), + q3 = q3 == undefined ? q2 : q3, + + q4 = d3.max(upper), + q4 = q4 == undefined ? q3 : q4, + + k0 = 'q0', k1 = 'q1', k2 = 'q2', k3 = 'q3', k4 = 'q4', + obj = {} + if (qKeys!=undefined && qKeys.length == 5) { k0 = qKeys[0]; k1 = qKeys[1]; k2 = qKeys[2]; k3 = qKeys[3]; k4 = qKeys[4]; } + obj[k0] = q0; obj[k1] = q1; obj[k2] = q2; obj[k3] = q3; obj[k4] = q4; + + return obj +} + + +/** +* determines the width of an object for the calling plotting function +* @param {number} freeSpace how much space is avalible +* @param {number} numberOfObjects how many object do we need +* @param {number} minObjectWidth how small are these objects allowed to be +* @param {number} maxObjectWidth how large are these object allowed to be +* @param {number} sizeOfSpacer percent of freeSpace that a single spacer should take up (need numberOfObjects - 1 spacers) +* @param {boolean} overflowQ can we go beyond alloted space +* @returns {number} how large object should be +* function tries to keep object within min / max width, but wil default to +* 5e-10 (smallest consistenly visible by svg size of element) if overflowQ is false +*/ +export function calculateWidthOfObject(freeSpace, numberOfObjects, minObjectWidth, maxObjectWidth, sizeOfSpacer, overflowQ) { + var sizeOfSpacer = + sizeOfSpacer == 0 || sizeOfSpacer > 1 + ? sizeOfSpacer + : freeSpace * sizeOfSpacer + + var numberOfSpacers = numberOfObjects - 1 + var spaceTakenBySpacers = numberOfSpacers * sizeOfSpacer + var remainingSpace = freeSpace - spaceTakenBySpacers + remainingSpace = remainingSpace < 0 ? 0 : remainingSpace + var objectWidth = remainingSpace / numberOfObjects + + if ( overflowQ && minObjectWidth != undefined && objectWidth < minObjectWidth ) { objectWidth = minObjectWidth } + // if ( maxObjectWidth != undefined && objectWidth > maxObjectWidth ) { objectWidth = maxObjectWidth } + if ( overflowQ && maxObjectWidth != undefined && objectWidth < maxObjectWidth ) { objectWidth = maxObjectWidth } + return Math.max(objectWidth, 5e-10) +} + +/** +* @param {Array[]} data list data (can be nested). If nested will create more complex spacer size +* @param {number} freeSpace how much space is avalible +* @param {number} objectWidth @see{@link calculateWidthOfObject} +* @param {number} numberOfObjects how many object do we need +* @param {number} baseSpacerSize percent of freeSpace that a single spacer should take up (need numberOfObjects - 1 spacers) +* @param {boolean} overflowQ can we go beyond alloted space +* @returns {number} returns size that spacer should be at level=0 +*/ +export function calculateWidthOfSpacer(data, freeSpace, objectWidth, numberOfObjects, baseSpacerSize, overflowQ) { + if (overflowQ) { + // var limitedNumberOfObjects = numberOfObjects > 6 ? 6 : numberOfObjects + // var spaceLeft = freeSpace - limitedNumberOfObjects * objectWidth + // return spaceLeft / (limitedNumberOfObjects - 1) + return freeSpace * baseSpacerSize + } + var spacersAtEachLevel = spacersNeededAtEachLevel(data) + var totalSpacerPercent = total(spacersAtEachLevel.map(function(e, i) {return e * 1 / (i+1)})) + var baseSpacerSize = (freeSpace - (objectWidth * numberOfObjects)) / totalSpacerPercent + // console.log(freeSpace, objectWidth, numberOfObjects, totalSpacerPercent) + // console.log(totalSpacerPercent, baseSpacerSize, totalSpacerPercent * baseSpacerSize) + return isNaN(baseSpacerSize) ? 0 : baseSpacerSize +} + +/** +* Calculates number of spacers needed to seperate elements at each level. +* @param {Array[]} array list data (can be nested). If nested will create more complex spacer size +* @param {number} [level=0] current level, used in recusrion +* @param {Array} [levelData=[]] how many spacers needed at a given level +* @returns {Array} levelData +* +* @example +* array = [[1,2], [3,4]] +* // returns [1, 2] +* as at level=0 the only spacer needed is between [1,2] and [3,4] +* and at level=1 the only two spacers needed is between 1 and 2 as well as +* 3 and 4 since the spacer between 2 and 3 is handled at level=0 +*/ +export function spacersNeededAtEachLevel (array, level, levelData ) { + if ( level == undefined ) { level = 0; } else { level += 1 } + if ( levelData == undefined ) { levelData = []; } + if ( level >= levelData.length ) { levelData.push(array.length - 1) } + else { levelData[level] += array.length - 1 } + array.map(function(e, i) { if (Array.isArray(e)) { spacersNeededAtEachLevel(e, level, levelData) } } ) + return levelData +} diff --git a/src/modules/utils/misc.js b/src/modules/utils/misc.js new file mode 100644 index 0000000..ac8344f --- /dev/null +++ b/src/modules/utils/misc.js @@ -0,0 +1,139 @@ + +export function resizeDebounce(f, wait) { + var resize = debounce(function(){f()},wait) + window.addEventListener('resize', resize) +} + +function debounce(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +} + +export function setupStandardChartContainers( + selection, + namespace, + container, + margins={top:0.01, bottom:0.01, left:0.01, right:0.01}, + svg={w:0.8, h:0.6}, // percent of container space for svg + axes={y:0.1, x:0.1}, // percent of container space for axes, + leg={x:0, margin:0, pos:'left'} // absolute width of legend and space on either size +) +{ + if (container == undefined) {container = {w:window.innerWidth, h:window.Height}} + // SVG width and height + + var svgSpace = { + w: container.w * svg.w, + h: container.h * svg.h + } + + var margPx = { + top: margins.top * svgSpace.h, + bottom: margins.bottom * svgSpace.h, + left: margins.left * svgSpace.w, + right: margins.right * svgSpace.w + }, + + + + // Space after removing margins + chartSpace = { + w: svgSpace.w - margPx.left - margPx.right, + h: svgSpace.h - margPx.top - margPx.bottom + }, + + // main dimension of x and y axies + // e.g. defines how tall x axis is as length is determined by plotRect.w + axesSpace = { + x: chartSpace.h * axes.x, + y: chartSpace.w * axes.y + }, + + // space left for drawing the chart properly (e.g. bars, violins, etc) + drawingSpace = { + x: chartSpace.w - axesSpace.y - leg.x - 2*leg.margin, + y: chartSpace.h - axesSpace.x + }, + + + legRect = { + x: leg.margin + margPx.left + (leg.pos == 'left' ? 0 : drawingSpace.x + axesSpace.y), + y: margPx.top, // this is soomehow getting calculated incorectly + w: leg.x, + h: drawingSpace.y + }, + + yAxisRect = { + x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), + y: margPx.top, + w: axesSpace.y, + h: drawingSpace.y + }, + + plotRect = { + x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), + y: margPx.top, + w: drawingSpace.x, + h: drawingSpace.y + }, + + xAxisRect = { + x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), + y: margPx.top + drawingSpace.y, + w: drawingSpace.x, + h: axesSpace.x + } + + + + container = d3sm.safeSelect(selection, 'svg', namespace) + .style('width', svgSpace.w+'px') + .style('height', svgSpace.h+'px') + + var axes = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'axes')) + + var leg = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'legend')) + .attr('transform', "translate("+legRect.x+","+legRect.y+")") + + var plot = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'plot')) + .attr('transform', "translate("+plotRect.x+","+plotRect.y+")") + + var xAxis = d3sm.safeSelect(axes, 'g', d3sm.hypenate(namespace, 'x-axis')) + .attr('transform', "translate("+xAxisRect.x+","+xAxisRect.y+")") + + var yAxis = d3sm.safeSelect(axes, 'g', d3sm.hypenate(namespace, 'y-axis')) + .attr('transform', "translate("+yAxisRect.x+","+yAxisRect.y+")") + + return { + svg: { + selection: container, + rect: svgSpace + }, + plot: { + selection: plot, + rect: plotRect + }, + xAxis: { + selection: xAxis, + rect: xAxisRect + }, + yAxis: { + selection: yAxis, + rect: yAxisRect + }, + legend: { + selection: leg, + rect: legRect + } + } +} diff --git a/src/modules/utils/paths.js b/src/modules/utils/paths.js new file mode 100644 index 0000000..cafc8dc --- /dev/null +++ b/src/modules/utils/paths.js @@ -0,0 +1,40 @@ + +/** +* Draws a whisker for @see{@link boxwhisker} +* @param {boolean} dir direction to draw whisker, should be either true (up, top) or false (down or bottom) +* @param {number} x starting x coordinate in which to draw whisker +* @param {number} y starting y coordinate in which to draw whisker +* @param {number} w width of space in which to draw whisker +* @param {number} h height of space in which to draw whisker +* @param {number} per percentage of w or h (depends on o) to make whisker +* @param {boolean} o orientation, true is horizontal and false is vertical +* @returns {string} representing the svg path (i.e. the d attribute for a path tag) +*/ +export function whiskerPath(dir, x, y, w, h, per, o) { + // d = direction (true is up), p = percent width + if (dir == 'up' || dir == 'top' || dir == true) {dir = true} + if (dir == 'down' || dir == 'bottom' || dir == false) {dir = false} + o = o == undefined ? 'horizontal' : o + per = per == undefined ? 1 : per + if (o != "horizontal") { + var hh = h * per , + w = dir ? w : -w , + a = dir ? x + w : x , + b = dir ? x : x + w , + c = dir ? a : b + p = "M " + a + ' ' + ( h / 2 ) + ' ' + + 'L ' + b + ' ' + ( h / 2 ) + ' ' + + 'M ' + c + ' ' + ( h / 2 - hh / 2 ) + ' ' + + 'L ' + c + ' ' + ( h / 2 + hh / 2 ) + ' ' + + return p + } + var ww = w * per, + a = dir ? y + h : y , + b = dir ? y : y + h , + p = "M " + ( w / 2 ) + ' ' + a + ' ' // straight line part + + 'L ' + ( w / 2 ) + ' ' + b + ' ' // straight line part + + 'h ' + ( -ww / 2 ) + ' ' + 0 + ' ' // horizontal line part + + 'h ' + ( ww ) + ' ' + 0 + ' ' + return p +} diff --git a/src/modules/utils/selections.js b/src/modules/utils/selections.js new file mode 100644 index 0000000..044855a --- /dev/null +++ b/src/modules/utils/selections.js @@ -0,0 +1,120 @@ +import {hypenate} from './strings.js' +/** +* Extracts x and y of translate from transform property +* @param {string} transform transform property of svg element +* @returns {number[]} x, y of translate(x, y) +*/ +export function getTranslation(transform) { + // Create a dummy g for calculation purposes only. This will never + // be appended to the DOM and will be discarded once this function + // returns. + var g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + // Set the transform attribute to the provided string value. + transform = transform == undefined ? 'translate(0,0)' : transform; + g.setAttributeNS(null, 'transform', transform); + // consolidate the SVGTransformList containing all transformations + // to a single SVGTransform of type SVG_TRANSFORM_MATRIX and get + // its SVGMatrix. + var matrix = g.transform.baseVal.consolidate().matrix; + // As per definition values e and f are the ones for the translation. + return [matrix.e, matrix.f]; +} + +/** +* recursively ascends element.parentElement to find a svg tag +* @param {Element} element +* @returns {Element | undefined} +*/ +export function getContainingSVG(element) { + var parent = element.parentElement + var tag = parent.tagName.toLowerCase() + if (tag === 'svg') { return parent; } + if (tag === 'html') { return undefined; } + return getContainingSVG(parent); +} + + +/** +* Trys to use d3.selection to get element, if it doesnt exist, makes one +* @param {d3.selection} sel selection in which to try and find object +* @param {string} tag tag of which to try and select +* @param {string} [cls=''] class of tag to try and grab +* @returns {d3.selection} of either append or selected tag.cls within sel +*/ +export function safeSelect(sel, tag, cls) { + var clsStr = cls == undefined ? '' : '.'+cls; + var sSel = sel.select(tag+clsStr).empty() + ? sel.append(tag) + : sel.select(tag+clsStr) + return sSel + .classed(clsStr.replace('.', ''), true) + .attr('transform', sSel.attr('transform') == undefined ? 'translate(0,0)' : sSel.attr('transform')) +} + + + +/** +* Adds a clip-path rect and binds it to container +* @param {d3.selection} container in which to add the clip-path and to which to bind the cliping path to +* @param {Object} rect the coordinates (x, y, width, height) of the clip-path +* @param {string} namespace +* @returns {d3.selection} of the clip-path rect +*/ +export function cpRect(container, rect, namespace) { + var defs = safeSelect(container, 'defs', hypenate(namespace, 'definitions')) + var cp = safeSelect(defs, 'clipPath', hypenate(namespace, 'clip-path')) + .attr('id', hypenate(namespace, 'clip-path')) + + var cpRect = safeSelect(cp, 'rect') + .attr('x', rect.x) + .attr('y', rect.y) + .attr('width', rect.width) + .attr('height', rect.height) + + defs.raise() + // set clipping path to container + container.attr('clip-path', 'url(#'+ hypenate(namespace, 'clip-path')+')') + + return cpRect +} + + +/** +* Adds a background rect t to container +* @param {d3.selection} container in which to add the background rectangle +* @param {Object} rect the coordinates (x, y, width, height) of the background +* @param {string} fill the color of the background +* @returns {d3.selection} of the background fill +*/ +export function bgRect(container, rect, fill) { + return safeSelect(container, 'rect', 'bg') + .attr('x', rect.x) + .attr('y', rect.y) + .attr('width', rect.width) + .attr('height', rect.height) + .attr('fill', fill) +} + + +/** +* Sets up the container for making chart elements. This includes making +* a clip-path rect bound to the passed container, a background rect, and +* a g element with class -object-container. +* @param {d3.selection} container in which to add the clip-path and background +* @param {string} namespace +* @param {Object} rect the coordinates (x, y, width, height) of the background and clip-path +* @param {string} fill the color of the background +* @returns {d3.selection} of g.-object-container +* +* @see{@link bgRect} +* @see{@link cpRect} +*/ +export function setupContainer(selection, namespace, rect, fill) { + // the container for three main items, bg, defs, and object-container + var + container = safeSelect(selection, 'g', namespace), + bg = bgRect(container, rect, fill), + cp = cpRect(container, rect, namespace), + objectContainer = safeSelect(container, 'g', hypenate(namespace, 'object-container')) + return objectContainer +} diff --git a/src/modules/utils/strings.js b/src/modules/utils/strings.js new file mode 100644 index 0000000..e1ae5c5 --- /dev/null +++ b/src/modules/utils/strings.js @@ -0,0 +1,41 @@ + +/** +* Hypenates all strings together +* @param {string[]} arguments +* @returns {string} "arg1-arg2-...-argn" +*/ +export function hypenate(){ return Array.prototype.slice.call(arguments).join('-') } + + +/** +* Trys to reduce text to fit in specified area, made for tick labels as called by +* @see{@link axis} +* @param {d3.selection} t container for specific axis tick +* @param {string} text to be the label of the passed axis tick +* @param {boolean} orient of the axis, true is horizontal, false is vertical +* @param {number} tickLength is the length of the text +* @param {number} space is the amount of availble space for the text and the tick to fit in +* @param {boolean} overflowQ whether or not allowed to go over the alloted space +* @returns {none} +*/ +export function truncateText(t, text, orient, tickLength, space, overflowQ) { + var rect = t.node().getBoundingClientRect() + t.text(text) + while (Math.max(rect.width, rect.height) > space - tickLength) { + text = String(text) + text = text.slice(0, text.length - 1) + t.text(text + '...') + rect = t.node().getBoundingClientRect() + if (text.length == 0) break + } +} + + +export function truncateString(string, space, font) { + var chars = space / font; + if (chars < string.length) { + return string.slice(0, Math.round(chars-5)) + '...' + } else { + return string + } +} diff --git a/src/scripts/main.js b/src/scripts/main.js deleted file mode 100644 index 737feaf..0000000 --- a/src/scripts/main.js +++ /dev/null @@ -1,116 +0,0 @@ -// Import styles (automatically inject into ). -// import '../styles/main.css'; -import {axis} from './modules/axis'; -import {bar} from './modules/bar'; -import {bubbleHeatmap} from './modules/bubble-heatmap'; -import {heatmap} from './modules/heatmap'; -import {boxwhisker} from './modules/box-whisker'; -import {colorFunction} from './modules/color-function'; -import {datatoggle} from './modules/data-toggle'; -import {groupingSpacer} from './modules/grouping-spacer'; -import {tooltip} from './modules/tooltip'; -import {scatter} from './modules/scatter'; -import {plotZoom} from './modules/plot-zoom'; -import {multiPlotZoom} from './modules/multi-plot-zoom'; -import {violin} from './modules/violin'; -import {numericLegend} from './modules/numeric-legend'; -import {categoricLegend} from './modules/categorical-legend'; -import {lasso} from './modules/lasso'; -import {lassoWidget} from './modules/lasso-widget'; -import {selectFilter} from './modules/select-filter'; -import {upset} from './modules/upset'; -import {filterTable} from './modules/filter-table'; - -import {uniqueElements, getTranslation, modifyHexidecimalColorLuminance, tickRange, -quartiles, extractViolinValues, hypenate, round, getContainingSVG, -interpolateColors, truncateText, safeSelect} from './modules/helpers'; - -import { - all, tally, hasQ, first, last, total, unique, get, listOfListsQ, - cut, groupBy, arrayEquals, elementsAtLevels, numberOfElements, - flatten, whichBin -} from './modules/array-functions'; - - -import { - setupStandardChartContainers, log as myLog, warn, info, error, - consoleGroup, consoleGroupEnd, resizeDebounce -} from './modules/utils'; - -// /** @module d3sm */ -var d3sm = {}; -d3sm.axis = axis; -d3sm.bar = bar; -d3sm.bubbleHeatmap = bubbleHeatmap; -d3sm.heatmap = heatmap; -d3sm.boxwhisker = boxwhisker; -d3sm.colorFunction = colorFunction; -d3sm.datatoggle = datatoggle; -d3sm.groupingSpacer = groupingSpacer; -d3sm.tooltip = tooltip; -d3sm.scatter = scatter; -d3sm.plotZoom = plotZoom; -d3sm.multiPlotZoom = multiPlotZoom; -d3sm.violin = violin; -d3sm.numericLegend = numericLegend; -d3sm.categoricLegend = categoricLegend; -d3sm.lasso = lasso; -d3sm.lassoWidget = lassoWidget; -d3sm.selectFilter = selectFilter; -d3sm.upset = upset; -d3sm.filterTable = filterTable; - -d3sm.uniqueElements = uniqueElements; -d3sm.getTranslation = getTranslation; -d3sm.modifyHexidecimalColorLuminance = modifyHexidecimalColorLuminance; -d3sm.tickRange = tickRange; -d3sm.quartiles = quartiles; -d3sm.extractViolinValues = extractViolinValues; -d3sm.hypenate = hypenate; -d3sm.round = round; -d3sm.getContainingSVG = getContainingSVG; -d3sm.interpolateColors = interpolateColors; -d3sm.truncateText = truncateText; -d3sm.safeSelect = safeSelect; - -d3sm.whichBin = whichBin; -d3sm.unique = unique; -d3sm.flatten = flatten; - -d3sm.setupStandardChartContainers = setupStandardChartContainers; -d3sm.log = myLog; -d3sm.warn = warn; -d3sm.info = info; -d3sm.error = error; -d3sm.consoleGroup = consoleGroup; -d3sm.consoleGroupEnd = consoleGroupEnd; -d3sm.resizeDebounce = resizeDebounce; - -d3sm.debugQ = false - - - -// Import a logger for easier debugging -// import debug from 'debug'; -// const log = debug('app:log'); - -// The logger should only be disabled if we're not in production. -// if (ENV !== 'production') { -// // Enable the logger. -// debug.enable('*'); -// log('Logging is enabled!'); -// -// // Enable LiveReload -// document.write( -// ' + + + + + + + + + + + + + + + + + + +
+ + + +
+

By Value

+
+ +
+

By Category

+
+ +
+

By Value Guidelines

+
+
+ + + + + diff --git a/demos/axes/js/v001.js b/demos/axes/js/v001.js new file mode 100644 index 0000000..50cacce --- /dev/null +++ b/demos/axes/js/v001.js @@ -0,0 +1,209 @@ +var data = d3smDemoData['basicViolins'] + +var +selection = d3.select("#axes-by-value") +namespace = 'axes', +container = d3sm.utils.sel.safeSelect(selection, 'svg', namespace).style('width', '1000px'), +lAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'left-axis')).attr('transform', "translate(100,100)"), +rAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'right-axis')).attr('transform', "translate(900,100)") +uAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'up-axis')).attr('transform', "translate(100,100)") +dAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'down-axis')).attr('transform', "translate(100,400)") + + +tickValues = [1,2,3,4,5] + +var uA = d3sm.axis(uAxis) +.spaceX(800) +.spaceY(100) +.orient("top") +.overflowQ(false) +.tickValues(tickValues) +.numberOfTicks(5) +.namespace('u') +// .guideLinesQ(true) +.guidelineSpace(300) + +var dA = d3sm.axis(dAxis) +.spaceX(800) +.spaceY(100) +.orient("bottom") +.label('bottom') +.overflowQ(false) +.tickValues(tickValues) +.numberOfTicks(5) +.namespace('d') +// .guideLinesQ(true) +.guidelineSpace(300) +.tickLabelRotation(315) +// .tickTickLabelSpacer(0) + + + +var lA = d3sm.axis(lAxis) +.spaceX(100) +.spaceY(300) +.orient("left") +.label('left') +.overflowQ(false) +.tickValues(tickValues) +.numberOfTicks(5) +.namespace('l') +// .guideLinesQ(true) +.guidelineSpace(800) +// .tickLabelRotation(45) + + +var rA = d3sm.axis(rAxis) +.spaceX(100) +.spaceY(300) +.orient("right") +.overflowQ(false) +.tickValues(tickValues) +.numberOfTicks(5) +.namespace('r') +// .guideLinesQ(true) +.guidelineSpace(800) +// .tickLabelRotation(45) + + +uA() +dA() +lA() +rA() + + + +var +selection = d3.select("#axes-by-cat") +namespace = 'axes', +container = d3sm.utils.sel.safeSelect(selection, 'svg', namespace).style('width', '1000px'), +lAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'left-axis')).attr('transform', "translate(100,100)"), +rAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'right-axis')).attr('transform', "translate(900,100)") +uAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'up-axis')).attr('transform', "translate(100,100)") +dAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'down-axis')).attr('transform', "translate(100,400)") + + +tickValues = [1,2,3,4,5] +tickLabels = ["this", "this-text", "this-text-gets", "this-text-gets-longer", "this-text-gets-longer-as-we-go-on"] + +var uA = d3sm.axis(uAxis) +.spaceX(800) +.spaceY(100) +.label('top') +.orient("top") +.overflowQ(false) +.categoricalQ(true) +.tickLabels(tickLabels) +.numberOfTicks(5) +.namespace('u-c') +// .guideLinesQ(true) +.guidelineSpace(300) + +var dA = d3sm.axis(dAxis) +.spaceX(800) +.spaceY(100) +.orient("bottom") +.overflowQ(false) +.categoricalQ(true) +.tickLabels(tickLabels) +.numberOfTicks(5) +.namespace('d-c') +// .guideLinesQ(true) +.guidelineSpace(300) +// .tickLabelRotation(315) +.tickTickLabelSpacer(0) + + +var lA = d3sm.axis(lAxis) +.spaceX(100) +.spaceY(300) +.orient("left") +.overflowQ(false) +.categoricalQ(true) +.tickLabels(tickLabels) +.numberOfTicks(5) +.namespace('l-c') +// .guideLinesQ(true) +.guidelineSpace(800) + +var rA = d3sm.axis(rAxis) +.spaceX(100) +.spaceY(300) +.label('right') +.orient("right") +.overflowQ(false) +.categoricalQ(true) +.tickLabels(tickLabels) +.numberOfTicks(5) +.namespace('r-c') +// .guideLinesQ(true) +.guidelineSpace(800) + +uA() +dA() +lA() +rA() + + + +var +selection = d3.select("#axes-by-val-g") +namespace = 'axes', +container = d3sm.utils.sel.safeSelect(selection, 'svg', namespace).style('width', '1000px'), +lAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'left-axis')).attr('transform', "translate(100,100)"), +rAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'right-axis')).attr('transform', "translate(900,100)") +uAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'up-axis')).attr('transform', "translate(100,100)") +dAxis = d3sm.utils.sel.safeSelect(container, 'g', d3sm.utils.str.hypenate(namespace, 'down-axis')).attr('transform', "translate(100,400)") + + +tickValues = [1,2,3,4,5] + +var uA = d3sm.axis(uAxis) +.spaceX(800) +.spaceY(100) +.orient("top") +.overflowQ(false) +.tickValues(tickValues) +.numberOfTicks(5) +.namespace('u-g') +.guideLinesQ(true) +.guidelineSpace(300) + +var dA = d3sm.axis(dAxis) +.spaceX(800) +.spaceY(100) +.orient("bottom") +.overflowQ(false) +.tickValues(tickValues) +.numberOfTicks(5) +.namespace('d-g') +.guideLinesQ(true) +.guidelineSpace(300) + + +var lA = d3sm.axis(lAxis) +.spaceX(100) +.spaceY(300) +.orient("left") +.overflowQ(false) +.tickValues(tickValues) +.numberOfTicks(5) +.namespace('l-g') +.guideLinesQ(true) +.guidelineSpace(800) + +var rA = d3sm.axis(rAxis) +.spaceX(100) +.spaceY(300) +.orient("right") +.overflowQ(false) +.tickValues(tickValues) +.numberOfTicks(5) +.namespace('r-g') +.guideLinesQ(true) +.guidelineSpace(800) + +uA() +dA() +lA() +rA() diff --git a/demos/bar-chart-same-data-complex-grouping/index.html b/demos/bar-chart-same-data-complex-grouping/index.html new file mode 100644 index 0000000..cfdb93c --- /dev/null +++ b/demos/bar-chart-same-data-complex-grouping/index.html @@ -0,0 +1,215 @@ + + + + + Complex Grouping + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/bar-chart-same-data-complex-grouping/js/v001.js b/demos/bar-chart-same-data-complex-grouping/js/v001.js new file mode 100644 index 0000000..0d79b66 --- /dev/null +++ b/demos/bar-chart-same-data-complex-grouping/js/v001.js @@ -0,0 +1,125 @@ +function barPlot( selection ) { + var data, + namespace + plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; + plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; + + + function plot() { + var + databar = d3sm.d3sm.utils.sel.safeSelect(selection, 'form',d3sm.utils.str.hypenate(namespace,'databar')), + dataPrint = d3sm.d3sm.utils.sel.safeSelect(selection, 'div', d3sm.utils.str.hypenate(namespace,'data-print')), + + selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:500}, undefined, {axes:{x:0.1, y:0.1},space:{w:1,h:1}}) + container = selections.svg.selection, + bars = selections.plot.selection, + xAxis = selections.xAxis.selection, + yAxis = selections.yAxis.selection + + + // make data selection + var dataSelect = d3sm.datatoggle(databar) + .keys(d3.keys(data.groupings)) + .updateFunction(function(){plot(); zoom.reset()}) + .namespace(namespace)() + + // grab current selected key + var currentKey = dataSelect.currentKey() + // // extract subdata + // var currentData = data[currentKey] + // + + + dataPrint.text(JSON.stringify(data.groupings[currentKey])) + // make bars + var b = d3sm.bar(bars) + .data(data.data) + .grouping(data.groupings[currentKey]) + .spaceX(selections.plot.rect.w) + .spaceY(selections.plot.rect.h) + .overflowQ(true) + .orient('horizontal') + .maxObjectSize(20) + .minObjectSize(10) + .valueExtractor(function(d, i){return data.data[d]['value'] }) + + + // b.tooltip().keys(['value']) + // + // b.colorFunction().colorBy('index') + b() + + // extract keys and values to safe re-computation + var bk = b.barKeys(), bv = b.barValues() + + // left d3sm.axis + var yA = d3sm.axis(yAxis) + .spaceX(selections.yAxis.rect.w) + .spaceY(selections.yAxis.rect.h) + .orient("left") + .overflowQ(false) + .tickValues(bv) + .guideLinesQ(true) + .guidelineSpace(selections.xAxis.rect.w) + .namespace('axis-left') + .numberOfTicks(5) + yA() + + // bottom d3sm.axis + var xA = d3sm.axis(xAxis) + .spaceX(selections.xAxis.rect.w) + .spaceY(selections.xAxis.rect.h) + .orient("bottom") + .overflowQ(true) + .categoricalQ(true) + .tickLabels(bk) + .namespace('axis-bottom') + .minObjectSize(b.minObjectSize()) + .maxObjectSize(b.maxObjectSize()) + .grouping(data.groupings[currentKey]) + .objectSpacer(b.objectSpacer()) + xA() + + b.selection().selectAll("rect").nodes().map(function(d, i){ + console.log(d.getBoundingClientRect(), i) + return this + }), + console.log( + b.selection().node(), + b.selection().selectAll("rect").nodes(), + b.selection().selectAll("rect").nodes(), + 'BCR:',b.selection().node().getBoundingClientRect(), + 'BBox:',b.selection().node().getBBox() + ) + + + + + + // // set-up zoom, whell and drag + var zoom = d3sm.plotZoom(b, xA, yA).eventType('drag') + .orient('horizontal') + // .xLock( + // objs.getBBox().width + // ) + + console.log(zoom) + + // zoom.setLocks() + + var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ + // zoom.setLocks() + zoom.eventType('drag')() + }) + container.call(pan).on('wheel', function(){ + // zoom.setLocks() + + zoom.eventType('wheel')() + }) + } + + + + + return plot +} diff --git a/demos/basic-violins/index.html b/demos/basic-violins/index.html new file mode 100644 index 0000000..6dd08db --- /dev/null +++ b/demos/basic-violins/index.html @@ -0,0 +1,182 @@ + + + + + Basic Violins + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/box-whiskers/index.html b/demos/box-whiskers/index.html new file mode 100644 index 0000000..ab45578 --- /dev/null +++ b/demos/box-whiskers/index.html @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/bubble-heatmap/index.html b/demos/bubble-heatmap/index.html new file mode 100644 index 0000000..423b32b --- /dev/null +++ b/demos/bubble-heatmap/index.html @@ -0,0 +1,169 @@ + + + + + Basic Bubble Heatmap + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/bubble-heatmap/js/v000.js b/demos/bubble-heatmap/js/v000.js new file mode 100644 index 0000000..03adb85 --- /dev/null +++ b/demos/bubble-heatmap/js/v000.js @@ -0,0 +1,96 @@ +function heatmap( selection ) { + var data, + namespace + plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; + plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; + + function plot(){ + var + selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:750}, undefined, {axes:{x:0.2, y:0.2},space:{w:1,h:1}}) + container = selections.svg.selection, + bubbles = selections.plot.selection, + xAxis = selections.xAxis.selection, + yAxis = selections.yAxis.selection + + // d3sm.debugQ = true + + + var bhm = d3sm.bubbleHeatmap(bubbles) + .spaceX(selections.plot.rect.w) + .spaceY(selections.plot.rect.h) + .xKey('xKey') + .yKey('yKey') + .vKey('val') + .rKey('val') + .overflowQ(true) + // .minObjectSize(100) + .maxObjectSize(100) + + + // .yKeySortingFunction() + // .xKeySortingFunction(function(a,b){return data[a]['xKey']}) + + .data(data) + + bhm.tooltip().keys(['tooltip']) + bhm() + // + // + // + // // extract keys and values to safe re-computation + var xk = bhm.xValues(), yk = bhm.yValues() + // var pd = [] + // vk.map(function(k, i){ pd.push(...d3.values(vv[k].quartiles)) }) + // pd = d3sm.unique(pd.sort(function (a, b) {return d3.ascending(a, b)})) + // + // console.log(pd) + // console.log(yk, bhm.minObjectSize(), bhm.maxObjectSize(), bhm.ySpacerSize()) + + var yA = d3sm.axis(yAxis) + .spaceX(selections.yAxis.rect.w) + .spaceY(selections.yAxis.rect.h) + .orient("left") + .overflowQ(true) + .categoricalQ(true) + .tickLabels(yk) + .namespace('axis-left') + .minObjectSize(bhm.minObjectSize()) + .maxObjectSize(bhm.maxObjectSize()) + .objectSpacer(bhm.ySpacerSize()) + .objectSize(bhm.ySize()) + + .guideLinesQ(true) + .guidelineSpace(selections.xAxis.rect.w) + yA() + + var xA = d3sm.axis(xAxis) + .spaceX(selections.xAxis.rect.w) + .spaceY(selections.xAxis.rect.h) + .orient("bottom") + .overflowQ(true) + .categoricalQ(true) + .tickLabels(xk) + .namespace('axis-bottom') + .minObjectSize(bhm.minObjectSize()) + .maxObjectSize(bhm.maxObjectSize()) + .objectSpacer(bhm.xSpacerSize()) + .objectSize(bhm.xSize()) + .guideLinesQ(true) + .guidelineSpace(selections.yAxis.rect.h) + + xA() + + console.log(bhm.selection().node(), bhm.selection().node().getBBox(), bhm.selection().node().getBoundingClientRect()) + + + var zoom = d3sm.plotZoom(bhm, xA, yA).eventType('drag') + .orient('2D') + + var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){zoom.eventType('drag')()}) + container.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + + } + + + return plot +} diff --git a/demos/heatmap/index.html b/demos/heatmap/index.html new file mode 100644 index 0000000..504290f --- /dev/null +++ b/demos/heatmap/index.html @@ -0,0 +1,70 @@ + + + + + Basic Heatmap + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demos/heatmap/js/v000.js b/demos/heatmap/js/v000.js new file mode 100644 index 0000000..2dff8e0 --- /dev/null +++ b/demos/heatmap/js/v000.js @@ -0,0 +1,98 @@ +function heatmap( selection ) { + var data, + namespace + plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; + plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; + + function plot(){ + var + selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:750}, undefined, {axes:{x:0.2, y:0.2},space:{w:1,h:1}}) + container = selections.svg.selection, + bubbles = selections.plot.selection, + xAxis = selections.xAxis.selection, + yAxis = selections.yAxis.selection + + + d3sm.debugQ = true + + var overflow = true + // var overflow = false + var bhm = d3sm.heatmap(bubbles) + .spaceX(selections.plot.rect.w) + .spaceY(selections.plot.rect.h) + .xKey('xKey') + .yKey('yKey') + .vKey('val') + .overflowQ(overflow) + .objectStrokeWidth(2) + // .minObjectSize(100) + .maxObjectSize(100) + + + // .yKeySortingFunction() + // .xKeySortingFunction(function(a,b){return data[a]['xKey']}) + + .data(data) + + bhm.tooltip().keys(['tooltip']) + bhm() + // + // + // + // // extract keys and values to safe re-computation + var xk = bhm.xValues(), yk = bhm.yValues() + // var pd = [] + // vk.map(function(k, i){ pd.push(...d3.values(vv[k].quartiles)) }) + // pd = d3sm.unique(pd.sort(function (a, b) {return d3.ascending(a, b)})) + // + // console.log(pd) + // console.log(yk, bhm.minObjectSize(), bhm.maxObjectSize(), bhm.ySpacerSize()) + + var yA = d3sm.axis(yAxis) + .spaceX(selections.yAxis.rect.w) + .spaceY(selections.yAxis.rect.h) + .orient("left") + .overflowQ(overflow) + .categoricalQ(true) + .tickLabels(yk) + .namespace('axis-left') + .minObjectSize(bhm.minObjectSize()) + .maxObjectSize(bhm.maxObjectSize()) + .objectSpacer(bhm.ySpacerSize()) + .objectSize(bhm.ySize()) + + .guideLinesQ(true) + .guidelineSpace(selections.xAxis.rect.w) + yA() + + var xA = d3sm.axis(xAxis) + .spaceX(selections.xAxis.rect.w) + .spaceY(selections.xAxis.rect.h) + .orient("bottom") + .overflowQ(overflow) + .categoricalQ(true) + .tickLabels(xk) + .namespace('axis-bottom') + .minObjectSize(bhm.minObjectSize()) + .maxObjectSize(bhm.maxObjectSize()) + .objectSpacer(bhm.xSpacerSize()) + .objectSize(bhm.xSize()) + .guideLinesQ(true) + .guidelineSpace(selections.yAxis.rect.h) + + xA() + + console.log(bhm.selection().node(), bhm.selection().node().getBBox(), bhm.selection().node().getBoundingClientRect()) + + + var zoom = d3sm.plotZoom(bhm, xA, yA).eventType('drag') + .orient('2D') + + var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){zoom.eventType('drag')()}) + container.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + + } + + + return plot +} diff --git a/demos/heatmap/js/v003.js b/demos/heatmap/js/v003.js new file mode 100644 index 0000000..1846d59 --- /dev/null +++ b/demos/heatmap/js/v003.js @@ -0,0 +1,132 @@ +function heatmap ( selection ) { + var + chart, + xAxis, + yAxis, + legend, + + data, + namespace = 'heatmap', + title = 'Heatmap' + + plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; + plot.title = function(_){return arguments.length ? (title = _, plot) : title; }; + plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; + + plot.chart = function(_){return arguments.length ? (chart = _, plot) : chart; }; + plot.xAxis = function(_){return arguments.length ? (xAxis = _, plot) : xAxis; }; + plot.yAxis = function(_){return arguments.length ? (yAxis = _, plot) : yAxis; }; + plot.legend = function(_){return arguments.length ? (legend = _, plot) : legend; }; + + function plot(){ + var + orient = '2D', + + titleContainer = d3sm.utils.sel.safeSelect(selection, 'h5', d3sm.utils.str.hypenate(namespace,'title')).text(title), + + selections = d3sm.utils.misc.setupStandardChartContainers( + selection, + namespace, + {w:Math.min(window.innerWidth,1200), h:Math.min(window.innerHeight,700)}, + {top:0.01, bottom:0.01, left:0.01, right:0.01}, + {w:1, h:1}, // percent of container space for svg + {y:0.1,x:0.2}, // percent of container space for axes, + {x:25, margin:05} // absolute width of legendSelect and space on either size + ), + + container = selections.svg.selection, + chartSelection = selections.plot.selection, + xAxisSelection = selections.xAxis.selection, + yAxisSelection = selections.yAxis.selection, + legendSelect = selections.legend.selection + + + if (chart == undefined){ chart = d3sm.charts.heatmap(chartSelection) } + if (yAxis == undefined) { yAxis = d3sm.axis(yAxisSelection) } + if (xAxis == undefined) { xAxis = d3sm.axis(xAxisSelection) } + if (legend == undefined) { + legend = d3sm.legends.numeric(legendSelect) + .spaceX(selections.legend.rect.w).spaceY(selections.plot.rect.h) + + } + + // d3sm.debugQ = true + var overflowQ = true + + chart + .spaceX(selections.plot.rect.w) + .spaceY(selections.plot.rect.h) + .xKey('ykey') + .yKey('xkey') + .vKey('padj') + // .minObjectSize(25) + // .maxObjectSize(100) + // .domainPadding(0.01) + .overflowQ(overflowQ) + .data(data) + + chart.tooltip() + .header(function(k, cD){return cD.experimentId}) + .keys(['padj', 'log2FoldChange', 'xkey', 'ykey']) + + chart.colorFunction().colorBy('value') + .valueExtractor(function(k, v, i){ return data[k].padj }) + + chart() + + legend + .min(d3.min(chart.vValues())) + .max(d3.max(chart.vValues())) + .colorFunction(chart.colorFunction()) + .roundTo(5) + legend() + + var xk = chart.xValues(), yk = chart.yValues() + + // console.log(xk, yk) + yAxis + .spaceX(selections.yAxis.rect.w) + .spaceY(selections.yAxis.rect.h) + .orient("left") + .overflowQ(overflowQ) + .categoricalQ(true) + .tickLabels(yk) + .namespace(d3sm.utils.str.hypenate(namespace,'axis-left')) + .minObjectSize(chart.yMinObjectSize()) + .maxObjectSize(chart.yMaxObjectSize()) + .objectSpacer(chart.objectSpacer()) + .spacerSize(0) + .objectSize(chart.ySize() + chart.ySpacerSize()) + .guideLinesQ(false) + .guidelineSpace(selections.xAxis.rect.w) + + yAxis() + + xAxis = d3sm.axis(xAxisSelection) + .spaceX(selections.xAxis.rect.w) + .spaceY(selections.xAxis.rect.h) + .orient("bottom") + .overflowQ(overflowQ) + .categoricalQ(true) + .tickLabels(xk) + .namespace(d3sm.utils.str.hypenate(namespace,'axis-bottom')) + .minObjectSize(chart.xMinObjectSize()) + .maxObjectSize(chart.xMaxObjectSize()) + .objectSpacer(chart.objectSpacer()) + .spacerSize(0) + .objectSize(chart.xSize() + chart.xSpacerSize()) + .guideLinesQ(false) + .guidelineSpace(selections.yAxis.rect.h) + xAxis() + + var zoom = d3sm.aux.plotZoom(chart, xAxis, yAxis).eventType('drag') + .orient("vertical") + + var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){zoom.eventType('drag')()}) + chartSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + + } + + +return plot +} diff --git a/demos/index.html b/demos/index.html index 92a645f..3f6112a 100644 --- a/demos/index.html +++ b/demos/index.html @@ -8,32 +8,43 @@ - - - - - - diff --git a/demos/scatter/index.html b/demos/scatter/index.html new file mode 100644 index 0000000..15c2ce7 --- /dev/null +++ b/demos/scatter/index.html @@ -0,0 +1,144 @@ + + + + + Scatter Plot + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+ + +
+
Rendered with v0.0.3
+ + +
+ + + + + + + + + diff --git a/demos/scatter/js/v001.js b/demos/scatter/js/v001.js new file mode 100644 index 0000000..6d0bea9 --- /dev/null +++ b/demos/scatter/js/v001.js @@ -0,0 +1,103 @@ +//VERSION: 0.0.1 +function scatterPlot( selection ) { + var data, + namespace + plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; + plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; + + + function plot() { + var + databar = d3sm.safeSelect(selection, 'form', d3sm.hypenate(namespace,'databar')), + + selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:500}, undefined, {axes:{x:0.1, y:0.1},space:{w:1,h:1}}) + container = selections.svg.selection, + chart = selections.plot.selection, + xAxis = selections.xAxis.selection, + yAxis = selections.yAxis.selection + + leg = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace,'legend')) + .attr('transform','translate(10, 24)') + + var dataSelect = d3sm.datatoggle(databar) + .keys(d3.keys(data.groupings)) + .namespace(namespace) + .updateFunction(function(){ plot(); zoom.reset(); }) + () + + var currentKey = dataSelect.currentKey() + + var currentData = d3.keys(data.data).reduce( (acc, k) => { + if (data.groupings[currentKey].includes(k)) { acc[k] = data.data[k] } + return acc + }, {}) + + + var scale = currentKey == "linearScale" ? d3.scaleLinear() : d3.scaleSqrt() + var ticks = currentKey == "linearScale" ? 5 : 10 + + var s = d3sm.scatter(chart) + .data(currentData) + .spaceX(selections.plot.rect.w) + .spaceY(selections.plot.rect.h) + .domainPaddingX(0.1) + .domainPaddingY(0.1) + .valueExtractorR(function(d, i){return currentData[d]['r']}) + .maxRadius(20) + .minRadius(1) + .scaleY(scale) + .scaleR(d3.scalePow()) + + var rs = d3.keys(currentData).map(function(d, i){return currentData[d].r}) + + s.colorFunction().colorBy('value').valueExtractor(function(k, v, i){ return v.r }) + .dataExtent( [d3.min(rs), d3.max(rs)]) + + s() + + var xV = s.valuesX(), yV = s.valuesY() + + var yA = d3sm.axis(yAxis) + .spaceX(selections.yAxis.rect.w) + .spaceY(selections.yAxis.rect.h) + .orient("left") + .overflowQ(false) + .tickValues(yV) + .guideLinesQ(true) + .guidelineSpace(selections.xAxis.rect.w) + .namespace('axis-left') + .numberOfTicks(ticks) + .domainPadding(s.domainPaddingY()) + .scale(scale) + yA() + + var xA = d3sm.axis(xAxis) + .spaceX(selections.xAxis.rect.w) + .spaceY(selections.xAxis.rect.h) + .orient("bottom") + .overflowQ(false) + .tickValues(xV) + .guideLinesQ(true) + .guidelineSpace(selections.yAxis.rect.h) + .namespace('axis-bottom') + .numberOfTicks(ticks) + .domainPadding(s.domainPaddingX()) + + xA() + + // set-up zoom, whell and drag + var zoom = d3sm.plotZoom(s, xA, yA).eventType('drag').orient('2D') + var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) + container.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + + l = d3sm.numericLegend(leg) + .min(d3.min(s.valuesR())) + .max(d3.max(s.valuesR())) + .spaceX(25) + .spaceY(300) + + l() + + } + return plot +} diff --git a/demos/scatter/js/v002.js b/demos/scatter/js/v002.js new file mode 100644 index 0000000..eb27c94 --- /dev/null +++ b/demos/scatter/js/v002.js @@ -0,0 +1,117 @@ +//VERSION: 0.0.2 +function scatterPlot( selection ) { + var data, + namespace + plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; + plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; + + + function plot() { + var + databar = d3sm.safeSelect(selection, 'form', d3sm.hypenate(namespace,'databar')), + + selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:500}, undefined, {axes:{x:0.1, y:0.1},space:{w:1,h:1}}) + container = selections.svg.selection, + chart = selections.plot.selection, + xAxis = selections.xAxis.selection, + yAxis = selections.yAxis.selection + + leg = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace,'legend')) + .attr('transform','translate(10, 24)') + + var dataSelect = d3sm.datatoggle(databar) + .keys(d3.keys(data.groupings)) + .namespace(namespace) + .updateFunction(function(){ plot(); zoom.reset(); }) + () + + var currentKey = dataSelect.currentKey() + + var currentData = d3.keys(data.data).reduce( (acc, k) => { + if (data.groupings[currentKey].includes(k)) { acc[k] = data.data[k] } + return acc + }, {}) + + + var scale = currentKey == "linearScale" ? d3.scaleLinear() : d3.scaleSqrt() + var ticks = currentKey == "linearScale" ? 5 : 10 + + var s = d3sm.scatter(chart) + .data(currentData) + .spaceX(selections.plot.rect.w) + .spaceY(selections.plot.rect.h) + .domainPaddingX(0.1) + .domainPaddingY(0.1) + .valueExtractorR(function(d, i){return currentData[d]['r']}) + .maxRadius(20) + .minRadius(1) + .scaleY(scale) + .scaleR(d3.scalePow()) + + var rs = d3.keys(currentData).map(function(d, i){return currentData[d].r}) + + s.colorFunction().colorBy('value').valueExtractor(function(k, v, i){ return v.r }) + .dataExtent( [d3.min(rs), d3.max(rs)]) + + s() + + var xV = s.valuesX(), yV = s.valuesY() + + var yA = d3sm.axis(yAxis) + .spaceX(selections.yAxis.rect.w) + .spaceY(selections.yAxis.rect.h) + .orient("left") + .overflowQ(false) + .tickValues(yV) + .guideLinesQ(true) + .guidelineSpace(selections.xAxis.rect.w) + .namespace('axis-left') + .numberOfTicks(ticks) + .domainPadding(s.domainPaddingY()) + .scale(scale) + yA() + + var xA = d3sm.axis(xAxis) + .spaceX(selections.xAxis.rect.w) + .spaceY(selections.xAxis.rect.h) + .orient("bottom") + .overflowQ(false) + .tickValues(xV) + .guideLinesQ(true) + .guidelineSpace(selections.yAxis.rect.h) + .namespace('axis-bottom') + .numberOfTicks(ticks) + .domainPadding(s.domainPaddingX()) + + xA() + // + // + // set-up zoom, whell and drag + var zoom = d3sm.plotZoom(s, xA, yA).eventType('drag').orient('2D') + var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) + container.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + + l = d3sm.numericLegend(leg) + .min(d3.min(s.valuesR())) + .max(d3.max(s.valuesR())) + .spaceX(25) + .spaceY(300) + + l() + + // l = d3sm.categoricLegend(leg) + // .categories(['a','b','c']) + // .spaceX(100) + // .spaceY(300) + // .overflowQ(false) + // .orient('vertical') + // + // l() + + } + + + + + return plot +} diff --git a/demos/scatter/js/v003.js b/demos/scatter/js/v003.js new file mode 100644 index 0000000..b111659 --- /dev/null +++ b/demos/scatter/js/v003.js @@ -0,0 +1,187 @@ +//VERSION: 0.0.3 +function scatterPlot( selection ) { + var + data, + namespace, + chart, + xAxis, + yAxis, + dataSelect, + legend, + lasso, + lassoWidget + + plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; + plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; + plot.chart = function(_){return arguments.length ? (chart = _, plot) : chart; }; + plot.lasso = function(_){return arguments.length ? (lasso = _, plot) : lasso; }; + + function plot() { + var objectClass= 'item' + + var + lassoDiv = d3sm.safeSelect(selection, 'div', d3sm.hypenate(namespace,'lassoWidget')), + databar = d3sm.safeSelect(selection, 'form', d3sm.hypenate(namespace,'databar')), + selections = d3sm.setupStandardChartContainers( + selection, + namespace, + {w:window.innerWidth, h:window.innerHeight}, + {top:0.01, bottom:0.01, left:0.01, right:0.01}, + {w: 0.8, h:0.8}, // percent of container space for svg + {y:0.1,x:0.1}, // percent of container space for axes, + {x:25, margin:05} // absolute width of legendSelect and space on either size + ), + + // selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:500}, undefined, {axes:{x:0.1, y:0.1},space:{w:1,h:1}}) + container = selections.svg.selection, + chartSelection = selections.plot.selection, + xAxisSelection = selections.xAxis.selection, + yAxisSelection = selections.yAxis.selection, + legendSelect = selections.legend.selection + + + var + xAxisToggle = { 'linear': d3.scaleLinear(), 'sqrt': d3.scaleSqrt() }, + yAxisToggle = { 'linear': d3.scaleLinear(), 'sqrt': d3.scaleSqrt() }, + rAxisToggle = { 'linear': d3.scaleLinear(), 'sqrt': d3.scaleSqrt(), 'pow': d3.scalePow() } + + + if (dataSelect == undefined) { dataSelect = d3sm.datatoggle(databar) + .data({ + 'dataset': data.groupings, + 'x-axis': xAxisToggle, + 'y-axis': yAxisToggle, + 'radius scale': rAxisToggle + }) + .namespace(namespace) + .updateFunction(function(){ plot(); zoom.reset(); }) + dataSelect() + } + + if (chart == undefined) { chart = d3sm.scatter(chartSelection) } + if (yAxis == undefined) { yAxis = d3sm.axis(yAxisSelection) } + if (xAxis == undefined) { xAxis = d3sm.axis(xAxisSelection) } + if (legend == undefined) {legend = d3sm.numericLegend(legendSelect) } + + + var currentKeys = dataSelect.currentKeys() + var xScale = xAxisToggle[currentKeys['x-axis']] + var yScale = yAxisToggle[currentKeys['y-axis']] + var rScale = rAxisToggle[currentKeys['radius scale']] + var currentDataKey = currentKeys['dataset'] + + + var currentKey = dataSelect.currentKeys() + var currentData = d3.keys(data.data).reduce( (acc, k) => { + if (data.groupings[currentDataKey].includes(k)) { acc[k] = data.data[k] } + return acc + }, {}) + + + chart + .data(currentData) + .spaceX(selections.plot.rect.w) + .spaceY(selections.plot.rect.h) + .domainPaddingX(0.1) + .domainPaddingY(0.1) + .valueExtractorR(function(d, i){return currentData[d]['r']}) + .maxRadius(20) + .minRadius(1) + .scaleY(yScale) + .scaleX(xScale) + .scaleR(rScale) + .namespace(namespace) + .objectClass(objectClass) + + var rs = d3.keys(currentData).map(function(d, i){return currentData[d].r}) + + chart.colorFunction().colorBy('value').valueExtractor(function(k, v, i){ return v.r }) + .dataExtent( [d3.min(rs), d3.max(rs)]) + chart() + + var xV = chart.valuesX(), yV = chart.valuesY() + + yAxis + .spaceX(selections.yAxis.rect.w) + .spaceY(selections.yAxis.rect.h) + .orient("left") + .overflowQ(false) + .tickValues(yV) + .guideLinesQ(true) + .guidelineSpace(selections.xAxis.rect.w) + .namespace('axis-left') + .numberOfTicks(5) + .domainPadding(chart.domainPaddingY()) + .scale(yScale) + yAxis() + + xAxis = d3sm.axis(xAxisSelection) + .spaceX(selections.xAxis.rect.w) + .spaceY(selections.xAxis.rect.h) + .orient("bottom") + .overflowQ(false) + .tickValues(xV) + .guideLinesQ(true) + .guidelineSpace(selections.yAxis.rect.h) + .namespace('axis-bottom') + .numberOfTicks(5) + .domainPadding(chart.domainPaddingX()) + .scale(xScale) + + xAxis() + + + legend + .min(d3.min(chart.valuesR())) + .max(d3.max(chart.valuesR())) + .spaceX(selections.legend.rect.w) + .spaceY(selections.legend.rect.h) + + legend() + + + + // set-up zoom, whell and drag + var zoom = d3sm.plotZoom(chart, xAxis, yAxis).eventType('drag').orient('2D') + var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) + chartSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + + + // var svg = selection.select('svg') + // var objs = svg.select('g.'+namespace+'-object-container') + // var objC = '.chart-point' + + // if (lasso == undefined) { lasso = d3sm.lasso( ) + // .svg(svg) + // .objectClass(objC) + // .objectContainer(objs) + // .namespace('test') + // .chartContainer(chartSelection) + // } + // + // lasso + // .xScale(chart.scaleX()) + // .yScale(chart.scaleY()) + // + // lasso() + + + if (lassoWidget == undefined) {lassoWidget = d3sm.lassoWidget(lassoDiv) + .svg(selections.svg.selection) + .objectClass("."+objectClass) + .chartContainer(chartSelection) + .objectContainer(chartSelection.select('g.'+namespace+'-object-container')) + .maxNumberOfGroups(2) + .dataExtractor(function(nodeData){ + return {"name": nodeData} + }) + lassoWidget() + } + + } + + + + + return plot +} diff --git a/demos/scatter/js/v004.js b/demos/scatter/js/v004.js new file mode 100644 index 0000000..9f0e5ee --- /dev/null +++ b/demos/scatter/js/v004.js @@ -0,0 +1,194 @@ +//VERSION: 0.0.4 +function scatterPlot( selection ) { + var + data, + namespace, + chart, + xAxis, + yAxis, + dataSelect, + legend, + lasso, + lassoWidget + + plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; + plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; + plot.chart = function(_){return arguments.length ? (chart = _, plot) : chart; }; + plot.lasso = function(_){return arguments.length ? (lasso = _, plot) : lasso; }; + + function plot() { + var objectClass= 'item' + + var + lassoDiv = d3sm.utils.sel.safeSelect(selection, 'div', d3sm.utils.str.hypenate(namespace,'lassoWidget')), + databar = d3sm.utils.sel.safeSelect(selection, 'form', d3sm.utils.str.hypenate(namespace,'databar')), + selections = d3sm.utils.misc.setupStandardChartContainers( + selection, + namespace, + {w:window.innerWidth, h:window.innerHeight}, + {top:0.01, bottom:0.01, left:0.01, right:0.01}, + {w: 0.8, h:0.8}, // percent of container space for svg + {y:0.1,x:0.1}, // percent of container space for axes, + {x:25, margin:05} // absolute width of legendSelect and space on either size + ), + + // selections = d3sm.setupStandardChartContainers(selection, namespace, {w: 1000, h:500}, undefined, {axes:{x:0.1, y:0.1},space:{w:1,h:1}}) + container = selections.svg.selection, + chartSelection = selections.plot.selection, + xAxisSelection = selections.xAxis.selection, + yAxisSelection = selections.yAxis.selection, + legendSelect = selections.legend.selection + + + var + xAxisToggle = { 'linear': d3.scaleLinear(), 'sqrt': d3.scaleSqrt() }, + yAxisToggle = { 'linear': d3.scaleLinear(), 'sqrt': d3.scaleSqrt() }, + rAxisToggle = { 'linear': d3.scaleLinear(), 'sqrt': d3.scaleSqrt(), 'pow': d3.scalePow() } + + // + if (dataSelect == undefined) { dataSelect = d3sm.aux.datatoggle(databar) + .data({ + 'dataset': data.groupings, + 'x-axis': xAxisToggle, + 'y-axis': yAxisToggle, + 'radius scale': rAxisToggle + }) + .namespace(namespace) + .updateFunction(function(){ plot(); zoom.reset(); }) + dataSelect() + } + + if (chart == undefined) { chart = d3sm.charts.scatter(chartSelection) } + if (yAxis == undefined) { yAxis = d3sm.axis(yAxisSelection) } + if (xAxis == undefined) { xAxis = d3sm.axis(xAxisSelection) } + if (legend == undefined) {legend = d3sm.legends.numeric(legendSelect) } + + + var currentKeys = dataSelect.currentKeys() + var xScale = xAxisToggle[currentKeys['x-axis']] + var yScale = yAxisToggle[currentKeys['y-axis']] + var rScale = rAxisToggle[currentKeys['radius scale']] + var currentDataKey = currentKeys['dataset'] + + // + // var xScale = xAxisToggle['linear'] + // var yScale = yAxisToggle['linear'] + // var rScale = rAxisToggle['linear'] + // var currentDataKey = 'datasetOne' + + + + var currentKey = dataSelect.currentKeys() + var currentData = d3.keys(data.data).reduce( (acc, k) => { + if (data.groupings[currentDataKey].includes(k)) { acc[k] = data.data[k] } + return acc + }, {}) + + + chart + .data(currentData) + .spaceX(selections.plot.rect.w) + .spaceY(selections.plot.rect.h) + .domainPaddingX(0.1) + .domainPaddingY(0.1) + .valueExtractorR(function(d, i){return currentData[d]['r']}) + .maxRadius(20) + .minRadius(1) + .scaleY(yScale) + .scaleX(xScale) + .scaleR(rScale) + .namespace(namespace) + .objectClass(objectClass) + + var rs = d3.keys(currentData).map(function(d, i){return currentData[d].r}) + + chart.colorFunction().colorBy('value').valueExtractor(function(k, v, i){ return v.r }) + .dataExtent( [d3.min(rs), d3.max(rs)]) + chart() + + var xV = chart.valuesX(), yV = chart.valuesY() + + yAxis + .spaceX(selections.yAxis.rect.w) + .spaceY(selections.yAxis.rect.h) + .orient("left") + .overflowQ(false) + .tickValues(yV) + .guideLinesQ(true) + .guidelineSpace(selections.xAxis.rect.w) + .namespace('axis-left') + .numberOfTicks(5) + .domainPadding(chart.domainPaddingY()) + .scale(yScale) + yAxis() + + xAxis = d3sm.axis(xAxisSelection) + .spaceX(selections.xAxis.rect.w) + .spaceY(selections.xAxis.rect.h) + .orient("bottom") + .overflowQ(false) + .tickValues(xV) + .guideLinesQ(true) + .guidelineSpace(selections.yAxis.rect.h) + .namespace('axis-bottom') + .numberOfTicks(5) + .domainPadding(chart.domainPaddingX()) + .scale(xScale) + + xAxis() + + + legend + .min(d3.min(chart.valuesR())) + .max(d3.max(chart.valuesR())) + .spaceX(selections.legend.rect.w) + .spaceY(selections.legend.rect.h) + + legend() + + + + // set-up zoom, whell and drag + var zoom = d3sm.aux.plotZoom(chart, xAxis, yAxis).eventType('drag').orient('2D') + var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) + chartSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + + + var svg = selection.select('svg') + var objs = svg.select('g.'+namespace+'-object-container') + var objC = '.chart-point' + + if (lasso == undefined) { lasso = d3sm.aux.lasso( ) + .svg(svg) + .objectClass(objC) + .objectContainer(objs) + .namespace('test') + .chartContainer(chartSelection) + } + + lasso + .xScale(chart.scaleX()) + .yScale(chart.scaleY()) + + lasso() + + + if (lassoWidget == undefined) {lassoWidget = d3sm.aux.lassoWidget(lassoDiv) + .svg(selections.svg.selection) + .objectClass("."+objectClass) + .chartContainer(chartSelection) + .objectContainer(chartSelection.select('g.'+namespace+'-object-container')) + .maxNumberOfGroups(2) + .dataExtractor(function(nodeData){ + return {"name": nodeData} + }) + lassoWidget() + } + + } + + + + + return plot +} diff --git a/demos/upset/index.html b/demos/upset/index.html new file mode 100644 index 0000000..71f5e2e --- /dev/null +++ b/demos/upset/index.html @@ -0,0 +1,133 @@ + + + + + UpSet Plot + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+ + +
+
Rendered with v0.0.3
+ +
+
+ +
+
+
+
+
+ + + + + + + + + + diff --git a/demos/upset/js/v003.js b/demos/upset/js/v003.js new file mode 100644 index 0000000..f08937c --- /dev/null +++ b/demos/upset/js/v003.js @@ -0,0 +1,300 @@ +function upset ( selection ) { + var + data, + namespace='upset' + + + var + upset, + upsetAxis, + setTotalsBarChart, + setTotalsAxis, + intersectionTotalsBarChart, + intersectionTotalsAxis, + intersectionSetsAxis + + plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; + plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; + plot.upset = function(_){return arguments.length ? (upset = _, plot) : upset; }; + plot.upsetAxis = function(_){return arguments.length ? (upsetAxis = _, plot) : upsetAxis; }; + plot.setTotalsBarChart = function(_){return arguments.length ? (setTotalsBarChart = _, plot) : setTotalsBarChart; }; + plot.setTotalsAxis = function(_){return arguments.length ? (setTotalsAxis = _, plot) : setTotalsAxis; }; + plot.intersectionTotalsBarChart = function(_){return arguments.length ? (intersectionTotalsBarChart = _, plot) : intersectionTotalsBarChart; }; + plot.intersectionTotalsAxis = function(_){return arguments.length ? (intersectionTotalsAxis = _, plot) : intersectionTotalsAxis; }; + + + + function plot() { + var + svg = d3sm.safeSelect(selection, 'svg'), + + upsetAxisSelection = d3sm.safeSelect(svg, 'g', namespace+'-upset-chart-axis'), + setTotalsAxisSelection = d3sm.safeSelect(svg, 'g', namespace+'-set-bar-chart-axis'), + intersectionTotalsAxisSelection = d3sm.safeSelect(svg, 'g', namespace+'-intersection-bar-chart-axis'), + intersectionSetsAxisSelection = d3sm.safeSelect(svg, 'g', namespace+'-intersection-bar-set-axis'), + + upsetSelection = d3sm.safeSelect(svg, 'g', namespace+'-upset-chart'), + setTotalsBarChartSelection = d3sm.safeSelect(svg, 'g', namespace+'-set-bar-chart'), + intersectionTotalsBarChartSelection = d3sm.safeSelect(svg, 'g', namespace+'-intersection-bar-chart') + + + + if (upset == undefined) { upset = d3sm.upset(upsetSelection) } + if (upsetAxis == undefined) { upsetAxis = d3sm.axis(upsetAxisSelection) } + if (setTotalsBarChart == undefined) { setTotalsBarChart = d3sm.bar(setTotalsBarChartSelection) } + if (setTotalsAxis == undefined) { setTotalsAxis = d3sm.axis(setTotalsAxisSelection) } + if (intersectionTotalsBarChart == undefined) { intersectionTotalsBarChart = d3sm.bar(intersectionTotalsBarChartSelection) } + if (intersectionTotalsAxis == undefined) { intersectionTotalsAxis = d3sm.axis(intersectionTotalsAxisSelection) } + if (intersectionSetsAxis == undefined) { intersectionSetsAxis = d3sm.axis(intersectionSetsAxisSelection) } + + + + var + setChartSpacer = 10, + intersectionChartSpacer = 10, + + percentages = { + inChart: {w: 0.75, h: 0.5 }, + usChart: {w: 0.75, h: 0.2 }, + stChart: {w: 0.10, h: 0.2 }, + + isAxis : {w: 0.75, h: 0.2 }, + inAxis : {w: 0.15, h: 0.5 }, + usAxis : {w: 0.15, h: 0.2 }, + stAxis : {w: 0.10, h: 0.1 }, + + svg: {w: 1, h: 0.7} + } + + var + page = {w: selection.node().getBoundingClientRect().width, h: window.innerHeight}, + margins = {left: page.w *0.01, right: page.w *0.01, top: page.h*0.01, bottom: page.h*0.01}, + svgRect = { + _w: page.w * percentages.svg.w, + _h: page.h * percentages.svg.h, + w: page.w * percentages.svg.w - margins.left - margins.right, + h: page.h * percentages.svg.h - margins.top - margins.bottom + }, + chartRect = { + w: svgRect.w - setChartSpacer*2, + h: svgRect.h - intersectionChartSpacer*2 + }, + upsetRect = { + x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer*2, + y: margins.top + chartRect.h * percentages.inChart.h + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer, + w: chartRect.w * percentages.usChart.w, + h: chartRect.h * percentages.usChart.h + }, + upsetAxisRect = { + x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer, + y: margins.top + chartRect.h * percentages.inChart.h + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer, + w: chartRect.w * percentages.usAxis.w, + h: chartRect.h * percentages.usAxis.h + }, + setTotalsRect = { + x: margins.left, + y: margins.top + chartRect.h * percentages.inChart.h + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer, + w: chartRect.w * percentages.stChart.w, + h: chartRect.h * percentages.stChart.h + }, + setTotalsAxisRect = { + x: margins.left, + y: margins.top + (chartRect.h * percentages.inChart.h) + (chartRect.h * percentages.stChart.h) + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer*2, + w: chartRect.w * percentages.stAxis.w, + h: chartRect.h * percentages.stAxis.h + }, + intersectionTotalsRect = { + x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer*2, + y: margins.top + (chartRect.h * percentages.isAxis.h), + w: chartRect.w * percentages.inChart.w, + h: chartRect.h * percentages.inChart.h + }, + intersectionTotalsAxisRect = { + x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer, + y: margins.top + (chartRect.h * percentages.isAxis.h), + w: chartRect.w * percentages.inAxis.w, + h: chartRect.h * percentages.inAxis.h + }, + intersectionSetsAxisRect = { + x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer + intersectionChartSpacer, + y: margins.top + (chartRect.h * percentages.isAxis.h), + w: chartRect.w * percentages.isAxis.w, + h: chartRect.h * percentages.isAxis.h + } + + + svg.attr('width', svgRect._w).attr('height', svgRect._h) + upsetAxisSelection.attr('transform', 'translate('+upsetAxisRect.x+','+upsetAxisRect.y+')') + setTotalsAxisSelection.attr('transform', 'translate('+setTotalsAxisRect.x+','+setTotalsAxisRect.y+')') + intersectionTotalsAxisSelection.attr('transform', 'translate('+intersectionTotalsAxisRect.x+','+intersectionTotalsAxisRect.y+')') + intersectionSetsAxisSelection.attr('transform', 'translate('+intersectionSetsAxisRect.x+','+intersectionSetsAxisRect.y+')') + + upsetSelection.attr('transform', 'translate('+upsetRect.x+','+upsetRect.y+')') + setTotalsBarChartSelection.attr('transform', 'translate('+setTotalsRect.x+','+setTotalsRect.y+')') + intersectionTotalsBarChartSelection.attr('transform', 'translate('+intersectionTotalsRect.x+','+intersectionTotalsRect.y+')') + + var overflowQ = true + + upset + .data(data) + .spaceX(upsetRect.w) + .spaceY(upsetRect.h) + .xObjectSpacer(0.01) + .yObjectSpacer(0.01) + .maxObjectSize(5) + .minObjectSize(20) + .overflowQ(overflowQ) + .intersectionExtractor(function(k, i){ return data[k].intersection.join(';') }) + .intersectionKeySortingFunction(function(a, b){ + return ( + data[a].intersection.length - data[b].intersection.length + || + upset.intersectionValues().indexOf(data[a].intersection.join(';')) + -upset.intersectionValues().indexOf(data[b].intersection.join(';')) + ) + }) + // .radius(1) + upset() + + upsetAxis + .spaceX(upsetAxisRect.w) + .spaceY(upsetAxisRect.h) + .orient('left') + .categoricalQ(true) + .tickLabels(upset.setValues()) + .overflowQ(overflowQ) + .minObjectSize(upset.minObjectSize()) + .maxObjectSize(upset.maxObjectSize()) + .objectSpacer(upset.yObjectSpacer()) + .tickLabelMargin(30) + + upsetAxis() + + var intersectionTotals = upset.intersectionTotals() + var setTotals = upset.setTotals() + + + intersectionSetsAxis + .tickLabels(upset.intersectionValues()) + .categoricalQ(true) + .spaceX(intersectionSetsAxisRect.w) + .spaceY(intersectionSetsAxisRect.h) + .orient('top') + .namespace('intersection-sets-name') + .overflowQ(overflowQ) + .minObjectSize(upset.minObjectSize()) + .maxObjectSize(upset.maxObjectSize()) + .objectSpacer(upset.xObjectSpacer()) + .objectSize(upset.xObjectSize()) + .spacerSize(upset.xSpacerSize()) + // .tickLabelRotation(-45) + .tickTickLabelSpacer(0) + + intersectionSetsAxis() + + intersectionTotalsBarChart.data(intersectionTotals) + .grouping(upset.intersectionValues()) + .spaceX(intersectionTotalsRect.w) + .spaceY(intersectionTotalsRect.h) + .namespace('intersections') + .orient('horizontal') + .valueExtractor(function(k, i){return intersectionTotals[k]['total']}) + .overflowQ(overflowQ) + .minObjectSize(upset.minObjectSize()) + .maxObjectSize(upset.maxObjectSize()) + .objectSpacer(upset.xObjectSpacer()) + .objectSize(upset.xObjectSize()) + .spacerSize(upset.xSpacerSize()) + + + intersectionTotalsBarChart.tooltip().keys(['Amount','Values']) + .values([ + function(currentData, d){ + return currentData.total + }, + function(currentData, d){ + var l = currentData.values.length + return currentData.values.join(', ').slice(0,100)+ ((l > 100) ? '...': '') + } + ]) + + // intersectionTotalsBarChart.colorFunction().colors(['cyan', 'blue']).colorBy('value') + + // intersectionSetsAxis() + intersectionTotalsBarChart() + + intersectionTotalsAxis + .spaceX(intersectionTotalsAxisRect.w) + .spaceY(intersectionTotalsAxisRect.h) + .orient('left') + .namespace('intersections-axis-left') + .tickValues(intersectionTotalsBarChart.barValues()) + .guideLinesQ(true) + .guidelineSpace(intersectionTotalsRect.w + intersectionChartSpacer) + .overflowQ(overflowQ) + + intersectionTotalsAxis() + + + + setTotalsBarChart.data(setTotals) + .grouping(upset.setValues()) + .spaceX(setTotalsRect.w) + .spaceY(setTotalsRect.h) + .orient('right') + .valueExtractor(function(k, i){return setTotals[k]['total']}) + .namespace('set') + .overflowQ(overflowQ) + .minObjectSize(upset.minObjectSize()) + .maxObjectSize(upset.maxObjectSize()) + .objectSpacer(upset.yObjectSpacer()) + .objectSize(upset.yObjectSize()) + .spacerSize(upset.ySpacerSize()) + .barPercent(0.75) + + setTotalsBarChart() + + + + setTotalsAxis + .spaceX(setTotalsAxisRect.w) + .spaceY(setTotalsAxisRect.h) + .namespace('set-axis-bottom') + .orient('bottom') + .tickValues(setTotalsBarChart.barValues()) + .reverseScaleQ(true) + // .tickLabelRotation(0) + .overflowQ(overflowQ) + + setTotalsAxis() + + + + + + + var zoom = d3sm.multiPlotZoom(upset).eventType('drag').orient('2D') + .xComponents([ + intersectionTotalsBarChart, + intersectionSetsAxis + ]) + .yComponents([ + upsetAxis, + setTotalsBarChart + ]) + var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) + upsetSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + intersectionTotalsBarChartSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + + + + intersectionTotalsBarChartSelection.selectAll('.bar.intersections > rect.bar-rect').on('click', function(d, i){ + console.log( d, i) + }) + // selection.selectAll('text').attr('pointer-events', 'none') + // .attr('pointer-events', 'none') + + } + + return plot +} diff --git a/demos/upset/js/v004.js b/demos/upset/js/v004.js new file mode 100644 index 0000000..dbaec08 --- /dev/null +++ b/demos/upset/js/v004.js @@ -0,0 +1,301 @@ +function upset ( selection ) { + var + data, + namespace='upset' + + + var + upset, + upsetAxis, + setTotalsBarChart, + setTotalsAxis, + intersectionTotalsBarChart, + intersectionTotalsAxis, + intersectionSetsAxis + + plot.data = function(_){return arguments.length ? (data = _, plot) : data; }; + plot.namespace = function(_){return arguments.length ? (namespace = _, plot) : namespace; }; + plot.upset = function(_){return arguments.length ? (upset = _, plot) : upset; }; + plot.upsetAxis = function(_){return arguments.length ? (upsetAxis = _, plot) : upsetAxis; }; + plot.setTotalsBarChart = function(_){return arguments.length ? (setTotalsBarChart = _, plot) : setTotalsBarChart; }; + plot.setTotalsAxis = function(_){return arguments.length ? (setTotalsAxis = _, plot) : setTotalsAxis; }; + plot.intersectionTotalsBarChart = function(_){return arguments.length ? (intersectionTotalsBarChart = _, plot) : intersectionTotalsBarChart; }; + plot.intersectionTotalsAxis = function(_){return arguments.length ? (intersectionTotalsAxis = _, plot) : intersectionTotalsAxis; }; + + + + function plot() { + var + svg = d3sm.utils.sel.safeSelect(selection, 'svg'), + + upsetAxisSelection = d3sm.utils.sel.safeSelect(svg, 'g', namespace+'-upset-chart-axis'), + setTotalsAxisSelection = d3sm.utils.sel.safeSelect(svg, 'g', namespace+'-set-bar-chart-axis'), + intersectionTotalsAxisSelection = d3sm.utils.sel.safeSelect(svg, 'g', namespace+'-intersection-bar-chart-axis'), + intersectionSetsAxisSelection = d3sm.utils.sel.safeSelect(svg, 'g', namespace+'-intersection-bar-set-axis'), + + upsetSelection = d3sm.utils.sel.safeSelect(svg, 'g', namespace+'-upset-chart'), + setTotalsBarChartSelection = d3sm.utils.sel.safeSelect(svg, 'g', namespace+'-set-bar-chart'), + intersectionTotalsBarChartSelection = d3sm.utils.sel.safeSelect(svg, 'g', namespace+'-intersection-bar-chart') + + + + if (upset == undefined) { upset = d3sm.charts.upset(upsetSelection) } + if (upsetAxis == undefined) { upsetAxis = d3sm.axis(upsetAxisSelection) } + if (setTotalsBarChart == undefined) { setTotalsBarChart = d3sm.charts.bar(setTotalsBarChartSelection) } + if (setTotalsAxis == undefined) { setTotalsAxis = d3sm.axis(setTotalsAxisSelection) } + if (intersectionTotalsBarChart == undefined) { intersectionTotalsBarChart = d3sm.charts.bar(intersectionTotalsBarChartSelection) } + if (intersectionTotalsAxis == undefined) { intersectionTotalsAxis = d3sm.axis(intersectionTotalsAxisSelection) } + if (intersectionSetsAxis == undefined) { intersectionSetsAxis = d3sm.axis(intersectionSetsAxisSelection) } + + + + var + setChartSpacer = 10, + intersectionChartSpacer = 10, + + percentages = { + inChart: {w: 0.75, h: 0.5 }, + usChart: {w: 0.75, h: 0.2 }, + stChart: {w: 0.10, h: 0.2 }, + + isAxis : {w: 0.75, h: 0.2 }, + inAxis : {w: 0.15, h: 0.5 }, + usAxis : {w: 0.15, h: 0.2 }, + stAxis : {w: 0.10, h: 0.1 }, + + svg: {w: 1, h: 0.7} + } + + var + page = {w: selection.node().getBoundingClientRect().width, h: window.innerHeight}, + margins = {left: page.w *0.01, right: page.w *0.01, top: page.h*0.01, bottom: page.h*0.01}, + svgRect = { + _w: page.w * percentages.svg.w, + _h: page.h * percentages.svg.h, + w: page.w * percentages.svg.w - margins.left - margins.right, + h: page.h * percentages.svg.h - margins.top - margins.bottom + }, + chartRect = { + w: svgRect.w - setChartSpacer*2, + h: svgRect.h - intersectionChartSpacer*2 + }, + upsetRect = { + x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer*2, + y: margins.top + chartRect.h * percentages.inChart.h + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer, + w: chartRect.w * percentages.usChart.w, + h: chartRect.h * percentages.usChart.h + }, + upsetAxisRect = { + x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer, + y: margins.top + chartRect.h * percentages.inChart.h + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer, + w: chartRect.w * percentages.usAxis.w, + h: chartRect.h * percentages.usAxis.h + }, + setTotalsRect = { + x: margins.left, + y: margins.top + chartRect.h * percentages.inChart.h + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer, + w: chartRect.w * percentages.stChart.w, + h: chartRect.h * percentages.stChart.h + }, + setTotalsAxisRect = { + x: margins.left, + y: margins.top + (chartRect.h * percentages.inChart.h) + (chartRect.h * percentages.stChart.h) + (chartRect.h * percentages.isAxis.h) + intersectionChartSpacer*2, + w: chartRect.w * percentages.stAxis.w, + h: chartRect.h * percentages.stAxis.h + }, + intersectionTotalsRect = { + x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer*2, + y: margins.top + (chartRect.h * percentages.isAxis.h), + w: chartRect.w * percentages.inChart.w, + h: chartRect.h * percentages.inChart.h + }, + intersectionTotalsAxisRect = { + x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer, + y: margins.top + (chartRect.h * percentages.isAxis.h), + w: chartRect.w * percentages.inAxis.w, + h: chartRect.h * percentages.inAxis.h + }, + intersectionSetsAxisRect = { + x: margins.left + (chartRect.w * percentages.stChart.w) + (chartRect.w * percentages.usAxis.w) + setChartSpacer + intersectionChartSpacer, + y: margins.top + (chartRect.h * percentages.isAxis.h), + w: chartRect.w * percentages.isAxis.w, + h: chartRect.h * percentages.isAxis.h + } + + + svg.attr('width', svgRect._w).attr('height', svgRect._h) + upsetAxisSelection.attr('transform', 'translate('+upsetAxisRect.x+','+upsetAxisRect.y+')') + setTotalsAxisSelection.attr('transform', 'translate('+setTotalsAxisRect.x+','+setTotalsAxisRect.y+')') + intersectionTotalsAxisSelection.attr('transform', 'translate('+intersectionTotalsAxisRect.x+','+intersectionTotalsAxisRect.y+')') + intersectionSetsAxisSelection.attr('transform', 'translate('+intersectionSetsAxisRect.x+','+intersectionSetsAxisRect.y+')') + + upsetSelection.attr('transform', 'translate('+upsetRect.x+','+upsetRect.y+')') + setTotalsBarChartSelection.attr('transform', 'translate('+setTotalsRect.x+','+setTotalsRect.y+')') + intersectionTotalsBarChartSelection.attr('transform', 'translate('+intersectionTotalsRect.x+','+intersectionTotalsRect.y+')') + + var overflowQ = true + + upset + .data(data) + .spaceX(upsetRect.w) + .spaceY(upsetRect.h) + .xObjectSpacer(0.01) + .yObjectSpacer(0.01) + .maxObjectSize(5) + .minObjectSize(20) + .overflowQ(overflowQ) + .intersectionExtractor(function(k, i){ return data[k].intersection.join(';') }) + .intersectionKeySortingFunction(function(a, b){ + return ( + data[a].intersection.length - data[b].intersection.length + || + upset.intersectionValues().indexOf(data[a].intersection.join(';')) + -upset.intersectionValues().indexOf(data[b].intersection.join(';')) + ) + }) + // .radius(1) + upset() + + upsetAxis + .spaceX(upsetAxisRect.w) + .spaceY(upsetAxisRect.h) + .orient('left') + .categoricalQ(true) + .tickLabels(upset.setValues()) + .overflowQ(overflowQ) + .minObjectSize(upset.minObjectSize()) + .maxObjectSize(upset.maxObjectSize()) + .objectSpacer(upset.yObjectSpacer()) + .tickLabelMargin(30) + + upsetAxis() + + var intersectionTotals = upset.intersectionTotals() + var setTotals = upset.setTotals() + + + intersectionSetsAxis + .tickLabels(upset.intersectionValues()) + .categoricalQ(true) + .spaceX(intersectionSetsAxisRect.w) + .spaceY(intersectionSetsAxisRect.h) + .orient('top') + .namespace('intersection-sets-name') + .overflowQ(overflowQ) + .minObjectSize(upset.minObjectSize()) + .maxObjectSize(upset.maxObjectSize()) + .objectSpacer(upset.xObjectSpacer()) + .objectSize(upset.xObjectSize()) + .spacerSize(upset.xSpacerSize()) + // .tickLabelRotation(-45) + .tickTickLabelSpacer(0) + + intersectionSetsAxis() + + intersectionTotalsBarChart.data(intersectionTotals) + .grouping(upset.intersectionValues()) + .spaceX(intersectionTotalsRect.w) + .spaceY(intersectionTotalsRect.h) + .namespace('intersections') + .orient('horizontal') + .valueExtractor(function(k, i){return intersectionTotals[k]['total']}) + .overflowQ(overflowQ) + .minObjectSize(upset.minObjectSize()) + .maxObjectSize(upset.maxObjectSize()) + .objectSpacer(upset.xObjectSpacer()) + .objectSize(upset.xObjectSize()) + .spacerSize(upset.xSpacerSize()) + + + intersectionTotalsBarChart.tooltip().keys(['Amount','Values']) + .values([ + function(currentData, d){ + return currentData.total + }, + function(currentData, d){ + var l = currentData.values.length + // return currentData + return currentData.values.join(', ').slice(0,100)+ ((l > 100) ? '...': '') + } + ]) + + // intersectionTotalsBarChart.colorFunction().colors(['cyan', 'blue']).colorBy('value') + + // intersectionSetsAxis() + intersectionTotalsBarChart() + + intersectionTotalsAxis + .spaceX(intersectionTotalsAxisRect.w) + .spaceY(intersectionTotalsAxisRect.h) + .orient('left') + .namespace('intersections-axis-left') + .tickValues(intersectionTotalsBarChart.barValues()) + .guideLinesQ(true) + .guidelineSpace(intersectionTotalsRect.w + intersectionChartSpacer) + .overflowQ(overflowQ) + + intersectionTotalsAxis() + + + + setTotalsBarChart.data(setTotals) + .grouping(upset.setValues()) + .spaceX(setTotalsRect.w) + .spaceY(setTotalsRect.h) + .orient('right') + .valueExtractor(function(k, i){return setTotals[k]['total']}) + .namespace('set') + .overflowQ(overflowQ) + .minObjectSize(upset.minObjectSize()) + .maxObjectSize(upset.maxObjectSize()) + .objectSpacer(upset.yObjectSpacer()) + .objectSize(upset.yObjectSize()) + .spacerSize(upset.ySpacerSize()) + .barPercent(0.75) + + setTotalsBarChart() + + + + setTotalsAxis + .spaceX(setTotalsAxisRect.w) + .spaceY(setTotalsAxisRect.h) + .namespace('set-axis-bottom') + .orient('bottom') + .tickValues(setTotalsBarChart.barValues()) + .reverseScaleQ(true) + // .tickLabelRotation(0) + .overflowQ(overflowQ) + + setTotalsAxis() + + + + + + + var zoom = d3sm.aux.multiPlotZoom(upset).eventType('drag').orient('2D') + .xComponents([ + intersectionTotalsBarChart, + intersectionSetsAxis + ]) + .yComponents([ + upsetAxis, + setTotalsBarChart + ]) + var pan = d3.zoom().scaleExtent([1,1]).on('zoom', function(){ zoom.eventType('drag')() }) + upsetSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + intersectionTotalsBarChartSelection.call(pan).on('wheel', function(){ zoom.eventType('wheel')() }) + + + + intersectionTotalsBarChartSelection.selectAll('.bar.intersections > rect.bar-rect').on('click', function(d, i){ + console.log( d, i) + }) + // selection.selectAll('text').attr('pointer-events', 'none') + // .attr('pointer-events', 'none') + + } + + return plot +} diff --git a/demos/vertical-violins/index.html b/demos/vertical-violins/index.html new file mode 100644 index 0000000..6f33aef --- /dev/null +++ b/demos/vertical-violins/index.html @@ -0,0 +1,158 @@ + + + + + Basic Violins + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dist/d3sm.esm.js b/dist/d3sm.esm.js index b9ee62a..76be704 100644 --- a/dist/d3sm.esm.js +++ b/dist/d3sm.esm.js @@ -58,7 +58,7 @@ function last( array ) { return array[array.length-1]; } * @param {number[]} array of numerical values * @returns {number} sum over elements in array */ -function total$1( array ) { return array.reduce((a, b) => a + b, 0) } +function total( array ) { return array.reduce((a, b) => a + b, 0) } /** * Removes duplicates in array * @param {Array} array of items @@ -212,7 +212,7 @@ var arr = /*#__PURE__*/Object.freeze({ hasQ: hasQ, first: first, last: last, - total: total$1, + total: total, unique: unique, get: get, listOfListsQ: listOfListsQ, @@ -435,150 +435,6 @@ var math = /*#__PURE__*/Object.freeze({ spacersNeededAtEachLevel: spacersNeededAtEachLevel }); -function resizeDebounce(f, wait) { - var resize = debounce(function(){f();},wait); - window.addEventListener('resize', resize); -} - -function debounce(func, wait, immediate) { - var timeout; - return function() { - var context = this, args = arguments; - var later = function() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; -} - -function setupStandardChartContainers( - selection, - namespace, - container, - margins={top:0.01, bottom:0.01, left:0.01, right:0.01}, - svg={w:0.8, h:0.6}, // percent of container space for svg - axes={y:0.1, x:0.1}, // percent of container space for axes, - leg={x:0, margin:0, pos:'left'} // absolute width of legend and space on either size -) -{ - if (container == undefined) {container = {w:window.innerWidth, h:window.Height};} - // SVG width and height - - var svgSpace = { - w: container.w * svg.w, - h: container.h * svg.h - }; - - var margPx = { - top: margins.top * svgSpace.h, - bottom: margins.bottom * svgSpace.h, - left: margins.left * svgSpace.w, - right: margins.right * svgSpace.w - }, - - - - // Space after removing margins - chartSpace = { - w: svgSpace.w - margPx.left - margPx.right, - h: svgSpace.h - margPx.top - margPx.bottom - }, - - // main dimension of x and y axies - // e.g. defines how tall x axis is as length is determined by plotRect.w - axesSpace = { - x: chartSpace.h * axes.x, - y: chartSpace.w * axes.y - }, - - // space left for drawing the chart properly (e.g. bars, violins, etc) - drawingSpace = { - x: chartSpace.w - axesSpace.y - leg.x - 2*leg.margin, - y: chartSpace.h - axesSpace.x - }, - - - legRect = { - x: leg.margin + margPx.left + (leg.pos == 'left' ? 0 : drawingSpace.x + axesSpace.y), - y: margPx.top, // this is soomehow getting calculated incorectly - w: leg.x, - h: drawingSpace.y - }, - - yAxisRect = { - x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), - y: margPx.top, - w: axesSpace.y, - h: drawingSpace.y - }, - - plotRect = { - x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), - y: margPx.top, - w: drawingSpace.x, - h: drawingSpace.y - }, - - xAxisRect = { - x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), - y: margPx.top + drawingSpace.y, - w: drawingSpace.x, - h: axesSpace.x - }; - - - - container = d3sm.safeSelect(selection, 'svg', namespace) - .style('width', svgSpace.w+'px') - .style('height', svgSpace.h+'px'); - - var axes = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'axes')); - - var leg = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'legend')) - .attr('transform', "translate("+legRect.x+","+legRect.y+")"); - - var plot = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'plot')) - .attr('transform', "translate("+plotRect.x+","+plotRect.y+")"); - - var xAxis = d3sm.safeSelect(axes, 'g', d3sm.hypenate(namespace, 'x-axis')) - .attr('transform', "translate("+xAxisRect.x+","+xAxisRect.y+")"); - - var yAxis = d3sm.safeSelect(axes, 'g', d3sm.hypenate(namespace, 'y-axis')) - .attr('transform', "translate("+yAxisRect.x+","+yAxisRect.y+")"); - - return { - svg: { - selection: container, - rect: svgSpace - }, - plot: { - selection: plot, - rect: plotRect - }, - xAxis: { - selection: xAxis, - rect: xAxisRect - }, - yAxis: { - selection: yAxis, - rect: yAxisRect - }, - legend: { - selection: leg, - rect: legRect - } - } -} - -var misc = /*#__PURE__*/Object.freeze({ - resizeDebounce: resizeDebounce, - setupStandardChartContainers: setupStandardChartContainers -}); - /** * Hypenates all strings together * @param {string[]} arguments @@ -652,12 +508,12 @@ function getTranslation(transform) { * @param {Element} element * @returns {Element | undefined} */ -function getContainingSVG$1(element) { +function getContainingSVG(element) { var parent = element.parentElement; var tag = parent.tagName.toLowerCase(); if (tag === 'svg') { return parent; } if (tag === 'html') { return undefined; } - return getContainingSVG$1(parent); + return getContainingSVG(parent); } @@ -748,100 +604,244 @@ function setupContainer(selection, namespace, rect, fill) { var sel = /*#__PURE__*/Object.freeze({ getTranslation: getTranslation, - getContainingSVG: getContainingSVG$1, + getContainingSVG: getContainingSVG, safeSelect: safeSelect, cpRect: cpRect, bgRect: bgRect, setupContainer: setupContainer }); -/** - * calls console.group if d3sm.debugQ == true - * @param {string} name of the group - * @returns {undefined} - */ -function consoleGroup(name) { - if (window.d3sm.debugQ === true){ - console.group(name); - } +function resizeDebounce(f, wait) { + var resize = debounce(function(){f();},wait); + window.addEventListener('resize', resize); } -/** - * calls console.groupEnd if d3sm.debugQ == true - * @returns {undefined} - */ -function consoleGroupEnd() { - if (window.d3sm.debugQ === true){ - console.groupEnd(); - } +function debounce(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; } -/** - * Calls console.log if d3sm.debugQ == true - * @param {string} func name of the function logging - * @param {string} msg to log - * @param {Object} data to be logged along side the message - * @returns {undefined} - */ -function log(func, msg, data) { - if (window.d3sm.debugQ === true){ - console.log( - `%c[d3sm::${func}]:\t${msg}`, - [ - 'background: #6cd1ef', - 'border-radius: 5000px', - 'padding: 0px 2px', - 'font-size: 14px' - ].join(';') - ); - console.table(data); - // console.trace() - } -} +function setupStandardChartContainers( + selection, + namespace, + container, + margins={top:0.01, bottom:0.01, left:0.01, right:0.01}, + svg={w:0.8, h:0.6}, // percent of container space for svg + axes={y:0.1, x:0.1}, // percent of container space for axes, + leg={x:0, margin:0, pos:'left'} // absolute width of legend and space on either size +) +{ + if (container == undefined) {container = {w:window.innerWidth, h:window.Height};} + // SVG width and height -/** - * Calls console.warn if d3sm.debugQ == true - * @param {string} func name of the function warning - * @param {string} msg to display - * @param {Object} data to be displayed along side the message - * @returns {undefined} - */ -function warn(func, msg, data) { - if (window.d3sm.debugQ === true) - console.warn( - `%c[d3sm::${func}]:\t${msg}`, - [ - 'background: #ffd53e', - 'border-radius: 5000px', - 'padding: 0px 2px', - 'font-size: 14px' - ].join(';') - ); - console.table(data); -} -/** - * Calls the console.info if d3sm.debugQ == true - * @param {string} func name of the function providing info - * @param {string} msg to display - * @param {Object} data to be displayed along side the message - * @returns {undefined} - */ -function info(func, msg, data) { - if (window.d3sm.debugQ) - console.info( - `%c[d3sm::${func}]:\t${msg}`, - [ - 'background: #009ccd', - 'border-radius: 5000px', - 'padding: 0px 2px', - 'font-size: 14px' - ].join(';') - ); - console.table(data); -} + var svgSpace = { + w: container.w * svg.w, + h: container.h * svg.h + }; + var margPx = { + top: margins.top * svgSpace.h, + bottom: margins.bottom * svgSpace.h, + left: margins.left * svgSpace.w, + right: margins.right * svgSpace.w + }, -/** + + + // Space after removing margins + chartSpace = { + w: svgSpace.w - margPx.left - margPx.right, + h: svgSpace.h - margPx.top - margPx.bottom + }, + + // main dimension of x and y axies + // e.g. defines how tall x axis is as length is determined by plotRect.w + axesSpace = { + x: chartSpace.h * axes.x, + y: chartSpace.w * axes.y + }, + + // space left for drawing the chart properly (e.g. bars, violins, etc) + drawingSpace = { + x: chartSpace.w - axesSpace.y - leg.x - 2*leg.margin, + y: chartSpace.h - axesSpace.x + }, + + + legRect = { + x: leg.margin + margPx.left + (leg.pos == 'left' ? 0 : drawingSpace.x + axesSpace.y), + y: margPx.top, // this is soomehow getting calculated incorectly + w: leg.x, + h: drawingSpace.y + }, + + yAxisRect = { + x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), + y: margPx.top, + w: axesSpace.y, + h: drawingSpace.y + }, + + plotRect = { + x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), + y: margPx.top, + w: drawingSpace.x, + h: drawingSpace.y + }, + + xAxisRect = { + x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), + y: margPx.top + drawingSpace.y, + w: drawingSpace.x, + h: axesSpace.x + }; + + + + container = safeSelect(selection, 'svg', namespace) + .style('width', svgSpace.w+'px') + .style('height', svgSpace.h+'px'); + + var axes = safeSelect(container, 'g', hypenate(namespace, 'axes')); + + var leg = safeSelect(container, 'g', hypenate(namespace, 'legend')) + .attr('transform', "translate("+legRect.x+","+legRect.y+")"); + + var plot = safeSelect(container, 'g', hypenate(namespace, 'plot')) + .attr('transform', "translate("+plotRect.x+","+plotRect.y+")"); + + var xAxis = safeSelect(axes, 'g', hypenate(namespace, 'x-axis')) + .attr('transform', "translate("+xAxisRect.x+","+xAxisRect.y+")"); + + var yAxis = safeSelect(axes, 'g', hypenate(namespace, 'y-axis')) + .attr('transform', "translate("+yAxisRect.x+","+yAxisRect.y+")"); + + return { + svg: { + selection: container, + rect: svgSpace + }, + plot: { + selection: plot, + rect: plotRect + }, + xAxis: { + selection: xAxis, + rect: xAxisRect + }, + yAxis: { + selection: yAxis, + rect: yAxisRect + }, + legend: { + selection: leg, + rect: legRect + } + } +} + +var misc = /*#__PURE__*/Object.freeze({ + resizeDebounce: resizeDebounce, + setupStandardChartContainers: setupStandardChartContainers +}); + +/** + * calls console.group if d3sm.debugQ == true + * @param {string} name of the group + * @returns {undefined} + */ +function consoleGroup(name) { + if (window.d3sm.debugQ === true){ + console.group(name); + } +} + +/** + * calls console.groupEnd if d3sm.debugQ == true + * @returns {undefined} + */ +function consoleGroupEnd() { + if (window.d3sm.debugQ === true){ + console.groupEnd(); + } +} + +/** + * Calls console.log if d3sm.debugQ == true + * @param {string} func name of the function logging + * @param {string} msg to log + * @param {Object} data to be logged along side the message + * @returns {undefined} + */ +function log(func, msg, data) { + if (window.d3sm.debugQ === true){ + console.log( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #6cd1ef', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ); + console.table(data); + // console.trace() + } +} + +/** + * Calls console.warn if d3sm.debugQ == true + * @param {string} func name of the function warning + * @param {string} msg to display + * @param {Object} data to be displayed along side the message + * @returns {undefined} + */ +function warn(func, msg, data) { + if (window.d3sm.debugQ === true) + console.warn( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #ffd53e', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ); + console.table(data); +} +/** + * Calls the console.info if d3sm.debugQ == true + * @param {string} func name of the function providing info + * @param {string} msg to display + * @param {Object} data to be displayed along side the message + * @returns {undefined} + */ +function info(func, msg, data) { + if (window.d3sm.debugQ) + console.info( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #009ccd', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ); + console.table(data); +} + + +/** * Calls console.error if d3sm.debugQ == true * @param {string} func name of the function which sends the error * @param {string} msg to display @@ -906,7 +906,7 @@ var paths = /*#__PURE__*/Object.freeze({ whiskerPath: whiskerPath }); -let utils$1 = { +let utils = { arr, color, math, misc, sel, str, con, paths }; @@ -2016,7 +2016,7 @@ function axis ( selection ) { function axis () { // for convenience in handling orientation specific values - var horizontalQ = utils$1.arr.hasQ(['top', 'bottom', 'horizontal'], orient) ? true : false; + var horizontalQ = utils.arr.hasQ(['top', 'bottom', 'horizontal'], orient) ? true : false; var verticalQ = !horizontalQ; // background cliping rectangle @@ -2047,7 +2047,7 @@ function axis ( selection ) { } - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); // defaults for text-anchor and text rotation if (orient == 'top') { @@ -2085,12 +2085,12 @@ function axis ( selection ) { : (grouping == undefined) ? (numberOfTicks != undefined) // ? (tickValues.length < numberOfTicks) - ? (utils$1.math.tickRange(...d3.extent(tickValues), numberOfTicks)) + ? (utils.math.tickRange(...d3.extent(tickValues), numberOfTicks)) : tickValues : grouping; - var flatTickData = utils$1.arr.flatten(tickData); + var flatTickData = utils.arr.flatten(tickData); var numberOfObjects = flatTickData.length; var space = horizontalQ ? spaceX : spaceY; var extent = d3.extent(flatTickData); @@ -2114,16 +2114,16 @@ function axis ( selection ) { // calculate object size if needed objectSize = (objectSize == undefined) - ? utils$1.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) : objectSize; // calculate spacer size if needed spacerSize = (spacerSize == undefined) - ? utils$1.math.calculateWidthOfSpacer(flatTickData, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfSpacer(flatTickData, space, objectSize, numberOfObjects, objectSpacer, overflowQ) : spacerSize; - var objClass = utils$1.str.hypenate(namespace, categoricalQ ? objectClass+'-categorical' : objectClass); + var objClass = utils.str.hypenate(namespace, categoricalQ ? objectClass+'-categorical' : objectClass); var spacerFunction = groupingSpacer() .horizontalQ(horizontalQ).scale(scale).moveby((categoricalQ?'category':'scale')).numberOfObjects(numberOfObjects) @@ -2188,11 +2188,11 @@ function axis ( selection ) { - var labelNameGroup = utils$1.sel.safeSelect(selection, 'g', utils$1.str.hypenate(namespace,'axis-name')); + var labelNameGroup = utils.sel.safeSelect(selection, 'g', utils.str.hypenate(namespace,'axis-name')); - var labelElement = utils$1.sel.safeSelect(labelNameGroup, 'text', utils$1.str.hypenate(namespace, 'name')); + var labelElement = utils.sel.safeSelect(labelNameGroup, 'text', utils.str.hypenate(namespace, 'name')); if (labelElement != undefined) { labelElement.text(label); @@ -2257,7 +2257,7 @@ function axis ( selection ) { var that = d3.select(this).style('opacity', 1); // make and move tick - var tick = utils$1.sel.safeSelect(that, 'line', utils$1.str.hypenate(namespace,'tick')) + var tick = utils.sel.safeSelect(that, 'line', utils.str.hypenate(namespace,'tick')) .attr("x1", 0) .attr("x2", horizontalQ ? 0 : orient == "left" ? -tickLength : tickLength) .attr("y1", 0) @@ -2273,10 +2273,10 @@ function axis ( selection ) { }); // make and move label - var label = utils$1.sel.safeSelect(that, 'text', utils$1.str.hypenate(namespace,'label')) + var label = utils.sel.safeSelect(that, 'text', utils.str.hypenate(namespace,'label')) .text(function(d, i){ - var s = typeof d == 'number' ? utils$1.math.round(d, roundTo) : d; - s = utils$1.str.truncateString(String(s), (horizontalQ ? spaceY : spaceX) - tickLength-tickLabelMargin-tickTickLabelSpacer, tickLabelFontSize * 0.45); + var s = typeof d == 'number' ? utils.math.round(d, roundTo) : d; + s = utils.str.truncateString(String(s), (horizontalQ ? spaceY : spaceX) - tickLength-tickLabelMargin-tickTickLabelSpacer, tickLabelFontSize * 0.45); return s }) .attr('font-size', tickLabelFontSize) @@ -2328,7 +2328,7 @@ function axis ( selection ) { // add guidlines as needed if (guideLinesQ) { - var gline = utils$1.sel.safeSelect(that, 'line', utils$1.str.hypenate(namespace, 'guideline')) + var gline = utils.sel.safeSelect(that, 'line', utils.str.hypenate(namespace, 'guideline')) .transition().duration(transitionDuration).ease(easeFunc) .attr("x1", 0) .attr("x2", horizontalQ ? 0 : orient == "left" ? guidelineSpace : -guidelineSpace) @@ -2341,15 +2341,15 @@ function axis ( selection ) { t = 'translate('+x+','+y+')'; return t }); - } else { that.select('line.'+utils$1.str.hypenate(namespace, 'guideline')).remove(); } + } else { that.select('line.'+utils.str.hypenate(namespace, 'guideline')).remove(); } }); // apply alternating guidline thickness if (guideLinesQ) { - container.selectAll('.'+utils$1.str.hypenate(namespace,'guideline')) + container.selectAll('.'+utils.str.hypenate(namespace,'guideline')) .attr('stroke', function(d, i){ - if (i % 2 == 0) { return utils$1.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } + if (i % 2 == 0) { return utils.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } return guideLineStroke }) .attr('stroke-width', function(d, i){ @@ -2363,7 +2363,7 @@ function axis ( selection ) { /*************************************************************************** ** Make the line of the axis ***************************************************************************/ - var line = utils$1.sel.safeSelect(selection, 'path', utils$1.str.hypenate(namespace,'line')) + var line = utils.sel.safeSelect(selection, 'path', utils.str.hypenate(namespace,'line')) // .attr('x1', 0) // .attr('x2', horizontalQ ? spaceX : 0) // .attr('y1', 0) @@ -2383,21 +2383,21 @@ function axis ( selection ) { // hover of label show full text label in case it is truncated function labelHover(d, i){ var t = d3.select(this).style('fill', 'red'); - d3.select(t.node().parentNode).select("line."+utils$1.str.hypenate(namespace,'tick')) + d3.select(t.node().parentNode).select("line."+utils.str.hypenate(namespace,'tick')) .attr("stroke", 'red') .attr("stroke-width", tickStrokeWidth*2); if (guideLinesQ) { - d3.select(t.node().parentNode).select('line.'+utils$1.str.hypenate(namespace, 'guideline')) + d3.select(t.node().parentNode).select('line.'+utils.str.hypenate(namespace, 'guideline')) .attr('stroke', 'red') .attr('stroke-width', guideLineStrokeWidth*2); } - var s = typeof d == 'number' ? utils$1.math.round(d, roundTo) : d; + var s = typeof d == 'number' ? utils.math.round(d, roundTo) : d; var m = d3.mouse(d3.select('html').node()); - var div = utils$1.sel.safeSelect(d3.select('body'), 'div', utils$1.str.hypenate(namespace,'guideline-tooltip')) - .attr('id', utils$1.str.hypenate(namespace,'guideline-tooltip')) + var div = utils.sel.safeSelect(d3.select('body'), 'div', utils.str.hypenate(namespace,'guideline-tooltip')) + .attr('id', utils.str.hypenate(namespace,'guideline-tooltip')) .style('position', 'absolute') .style('left', (d3.event.pageX+15)+'px') .style('top', (d3.event.pageY+15)+'px') @@ -2414,7 +2414,7 @@ function axis ( selection ) { .style('border-style', 'solid') .style('border-width', 2); - var text = utils$1.sel.safeSelect(div, 'div') + var text = utils.sel.safeSelect(div, 'div') .text(tickLabelOnHoverFunc(s, i)) .style('color', 'black') .style('align-self', 'center'); @@ -2427,15 +2427,15 @@ function axis ( selection ) { function labelHoverOff(d, i){ var t = d3.select(this).style('fill', 'black'); - d3.select(t.node().parentNode).select("line."+utils$1.str.hypenate(namespace,'tick')) + d3.select(t.node().parentNode).select("line."+utils.str.hypenate(namespace,'tick')) .attr("stroke", tickStroke) .attr("stroke-width", tickStrokeWidth); if (guideLinesQ) { - var gline = d3.select(t.node().parentNode).select('line.'+utils$1.str.hypenate(namespace, 'guideline')); + var gline = d3.select(t.node().parentNode).select('line.'+utils.str.hypenate(namespace, 'guideline')); var minorQ = gline.attr('minor'); gline.attr('stroke', function(d, ii){ - if (minorQ == 'true') { return utils$1.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } + if (minorQ == 'true') { return utils.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } return guideLineStroke }) .attr('stroke-width', function(d, ii){ @@ -2443,7 +2443,7 @@ function axis ( selection ) { return guideLineStrokeWidth }); } - d3.select("#"+utils$1.str.hypenate(namespace,'guideline-tooltip')).remove(); + d3.select("#"+utils.str.hypenate(namespace,'guideline-tooltip')).remove(); } @@ -3390,7 +3390,7 @@ function scatter ( selection ) { function scatter() { // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); pointKeys = d3.keys(data); @@ -3969,7 +3969,7 @@ function bar ( selection ) { // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); // to prevent re-calculation and getters to be passed to axes barKeys = d3.keys(data); @@ -3978,7 +3978,7 @@ function bar ( selection ) { // if grouping is undefined sort barKeys by sortingFunction var ordered = (grouping == undefined) ? barKeys.sort(sortingFunction) : grouping; // ordered might be nested depending on grouping - barKeys = utils$1.arr.flatten(ordered); + barKeys = utils.arr.flatten(ordered); var numberOfObjects = barKeys.length; var extent = [Math.min(...barValues) - domainPadding,Math.max(...barValues) + domainPadding]; @@ -3996,12 +3996,12 @@ function bar ( selection ) { var space = horizontalQ ? spaceX : spaceY; // calculate object size objectSize = (objectSize == undefined) - ? utils$1.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) : objectSize; // calculate spacer size if needed spacerSize = (spacerSize == undefined) - ? utils$1.math.calculateWidthOfSpacer(barKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfSpacer(barKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) : spacerSize; // make the nested groups var spacerFunction = groupingSpacer() @@ -4068,7 +4068,7 @@ function bar ( selection ) { strokeColor = colorFunction$$1(key, value, i, 'stroke'); - var bar = utils$1.sel.safeSelect(t, 'rect', 'bar-rect'); + var bar = utils.sel.safeSelect(t, 'rect', 'bar-rect'); if (bar.attr('transform') == undefined) { bar.attr('transform', function(d, i) { @@ -4798,19 +4798,19 @@ function bubbleHeatmap( selection ) { function bhm() { var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); cellKeys = d3.keys(data); cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }); - utils$1.con.log('bubbleHeatmap', 'cells are sorted by', cellKeys); + utils.con.log('bubbleHeatmap', 'cells are sorted by', cellKeys); - xValues = utils$1.arr.unique(cellKeys.map(xExtractor)); - yValues = utils$1.arr.unique(cellKeys.map(yExtractor)); - rValues = utils$1.arr.unique(cellKeys.map(rExtractor)); - vValues = utils$1.arr.unique(cellKeys.map(vExtractor)); - utils$1.con.log('bubbleHeatmap', 'x and y keys are', {x: xValues, y:yValues}); + xValues = utils.arr.unique(cellKeys.map(xExtractor)); + yValues = utils.arr.unique(cellKeys.map(yExtractor)); + rValues = utils.arr.unique(cellKeys.map(rExtractor)); + vValues = utils.arr.unique(cellKeys.map(vExtractor)); + utils.con.log('bubbleHeatmap', 'x and y keys are', {x: xValues, y:yValues}); var xDim = xValues.length, yDim = yValues.length; @@ -4819,11 +4819,11 @@ function bubbleHeatmap( selection ) { var extent = [Math.min(...rValues) - domainPadding,Math.max(...rValues) + domainPadding]; - ySize = utils$1.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - xSize = utils$1.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - ySpacerSize = utils$1.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); - xSpacerSize = utils$1.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); - utils$1.con.log('bubbleHeatmap', 'size of', {x: xSize, y: ySize}); + ySize = utils.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + xSize = utils.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + ySpacerSize = utils.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); + xSpacerSize = utils.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); + utils.con.log('bubbleHeatmap', 'size of', {x: xSize, y: ySize}); scale.domain(extent).range([Math.min(minObjectSize/2, Math.min(ySize, xSize)/2), Math.min(ySize, xSize)/2]); @@ -4831,7 +4831,7 @@ function bubbleHeatmap( selection ) { var ySpacer = groupingSpacer() .horizontalQ(false) .moveby('category').numberOfObjects(yDim) - .objectClass(utils$1.str.hypenate(objectClass, 'row')) + .objectClass(utils.str.hypenate(objectClass, 'row')) .objectSize(ySize).spacerSize(ySpacerSize) .transitionDuration(transitionDuration).easeFunc(easeFunc) .namespace('row'); @@ -4845,7 +4845,7 @@ function bubbleHeatmap( selection ) { ySpacer(container, yValues, 0); - container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'row')) + container.selectAll('g.'+utils.str.hypenate(objectClass, 'row')) .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); @@ -4860,7 +4860,7 @@ function bubbleHeatmap( selection ) { : colorFunction$$1.dataExtent([0, Math.max(...vValues)]); cells.each(function(key, i) { - utils$1.con.log('bubbleHeatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); + utils.con.log('bubbleHeatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); var t = d3.select(this), currentData = data[key], @@ -4870,9 +4870,9 @@ function bubbleHeatmap( selection ) { fillColor = colorFunction$$1(key, value, i, 'fill'), // prevent duplicate computation strokeColor = colorFunction$$1(key, value, i, 'stroke'); - utils$1.con.log('bubbleHeatmap', 'radius',{radius: radius, scaled: scale(radius), extent: extent, range:scale.range()}); + utils.con.log('bubbleHeatmap', 'radius',{radius: radius, scaled: scale(radius), extent: extent, range:scale.range()}); - var c = utils$1.sel.safeSelect(t, 'circle', utils$1.str.hypenate(objectClass,'circle')); + var c = utils.sel.safeSelect(t, 'circle', utils.str.hypenate(objectClass,'circle')); c.attr('cx', xSize / 2) .attr('cy', ySize / 2 ) .attr('r', scale(radius)) @@ -4882,7 +4882,7 @@ function bubbleHeatmap( selection ) { }); - tooltip$$1.selection(cells.selectAll('circle.'+utils$1.str.hypenate(objectClass, 'circle'))) + tooltip$$1.selection(cells.selectAll('circle.'+utils.str.hypenate(objectClass, 'circle'))) .data(data); // .keys(['r', 'v']) // .header(function(d, i){return utils.str.hypenate(data[d][xKey], data[d][yKey]) }) @@ -4895,3046 +4895,4368 @@ function bubbleHeatmap( selection ) { return bhm; } +/******************************************************************************* +** ** +** ** +** BOX AND WHISKER ** +** ** +** ** +*******************************************************************************/ /** - * Creates a heatmap + * Creates a boxwhisker * - * {@link https://sumneuron.gitlab.io/d3sm/demos/heatmap-heatmap/index.html Demo} - * @constructor heatmap + * {@link https://sumneuron.gitlab.io/d3sm/demos/box-whiskers/index.html Demo} + * @constructor boxwhisker * @param {d3.selection} selection - * @namespace heatmap - * @returns {function} heatmap + * @namespace boxwhisker + * @returns {function} boxwhisker */ -function heatmap( selection ) { - var +function boxwhisker( selection ) { + var /** - * Data to plot. Assumed to be a object, where each key corresponds to a cell - * (see {@link heatmap#data}) + * Data to plot. Assumed to be a object, where each key corresponds to a box + * (see {@link boxwhisker#data}) * @param {Object} [data=undefined] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ data, - /** - * Amount of horizontal space (in pixels) avaible to render the heatmap in - * (see {@link heatmap#spaceX}) + * Which direction to render the boxes in + * (see {@link boxwhisker#orient}) + * @param {number} [orient='horizontal'] + * @memberof boxwhisker# + * @property + */ + orient = 'horizontal', + /** + * Amount of horizontal space (in pixels) avaible to render the boxwhisker in + * (see {@link boxwhisker#spaceX}) * @param {number} [spaceX=undefined] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ spaceX, /** - * Amount of vertical space (in pixels) avaible to render the heatmap in - * (see {@link heatmap.spaceY}) + * Amount of vertical space (in pixels) avaible to render the boxwhisker in + * (see {@link boxwhisker.spaceY}) * @param {number} [spaceY=undefined] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ spaceY, - /** - * The internal key of the cell specifiying to which x axis key it belongs - * (see {@link heatmap.xKey}) - * @param {string} [xKey='x'] - * @memberof heatmap# + * Whether or not to allow boxwhisker to render elements pass the main spatial dimension + * given the orientation (see {@link boxwhisker#orient}), where {@link boxwhisker#orient}="horizontal" + * the main dimension is {@link boxwhisker#spaceX} and where {@link boxwhisker#orient}="vertical" + * the main dimension is {@link boxwhisker#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof boxwhisker# * @property */ - xKey = 'x', + overflowQ = true, + /** - * The internal key of the cell specifiying to which y axis key it belongs - * (see {@link heatmap.yKey}) - * @param {string} [yKey='y'] - * @memberof heatmap# + * An array - putatively of other arrays - depicting how boxes should be arranged + * @param {Array[]} [grouping=undefined] + * @memberof boxwhisker# * @property */ - yKey = 'y', + grouping, + quartilesKey = 'quartiles', // key in object where quartiles are stored + quartilesKeys = ["0.00", "0.25", "0.50", "0.75", "1.00"], // keys in quartiles object mapping values to min, q1, q2, q3 and max + /** - * The internal key of the cell specifiying what value to use to determine the color - * (see {@link heatmap.vKey}) - * @param {string} [vKey='v'] - * @memberof heatmap# + * How to get the value of the boxwhisker + * @param {function} [valueExtractor=function(key, index) { return data[key][quartilesKey] }] + * @memberof boxwhisker# * @property */ - vKey = 'v', - + valueExtractor = function(key, index) { return data[key][quartilesKey] }, /** - * Function for extracting the the value from xKey. - * (see {@link heatmap.xExtractor}) - * @param {function} [xExtractor=function(key, i) { return data[key][xKey] }] - * @returns {string} - * @memberof heatmap# + * How to sort the boxes - if {@link boxwhisker#grouping} is not provided. + * @param {function} [sortingFunction=descending] + * @memberof boxwhisker# * @property + * default + * function(keyA, keyB) {return d3.descending( + * valueExtractor(keyA)[quartilesKeys[4]], + * valueExtractor(keyB)[quartilesKeys[4]] + * )} */ - xExtractor = function(key, i) {return data[key][xKey] }, + sortingFunction = function(keyA, keyB) {return d3.descending( + valueExtractor(keyA)[quartilesKeys[4]], + valueExtractor(keyB)[quartilesKeys[4]] + )}, /** - * Function for extracting the the value from yKey. - * (see {@link heatmap.yExtractor}) - * @param {function} [yExtractor=function(key, i) { return data[key][yKey] }] - * @returns {string} - * @memberof heatmap# + * The scale for which boxwhisker values should be transformed by + * @param {d3.scale} [scale=d3.scaleLinear] + * @memberof boxwhisker# * @property */ - yExtractor = function(key, i) { return data[key][yKey] }, - + scale = d3.scaleLinear(), /** - * Function for extracting the the value from vKey. - * (see {@link heatmap.vExtractor}) - * @param {function} [vExtractor=function(key, i) { return data[key][vKey] }] - * @returns {number} - * @memberof heatmap# + * The padding for the domain of the scale (see {@link boxwhisker#scale}) + * @param {number} [domainPadding=0.5] + * @memberof boxwhisker# * @property */ - vExtractor = function(key, i) { return data[key][vKey] }, - - + domainPadding = 0.5, /** - * Whether or not to allow heatmap to render elements pass the main spatial dimension - * given the orientation (see {@link heatmap#orient}), where {@link heatmap#orient}="bottom" or {@link heatmap#orient}="top" - * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="left" or {@link heatmap#orient}="right" - * the main dimension is {@link heatmap#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof heatmap# + * Default space for the spacer (percentage) of main dimension given the orientation + * (see {@link boxwhisker#orient}), where {@link boxwhisker#orient}="horizontal" + * the main dimension is {@link boxwhisker#spaceX} and where {@link boxwhisker#orient}="vertical" + * the main dimension is {@link boxwhisker#spaceY} between boxes + * @param {number} [objectSpacer=0.05] + * @memberof boxwhisker# * @property */ - overflowQ = false, - + objectSpacer = 0.05, /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link heatmap#orient}), where {@link heatmap#orient}="horizontal" - * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="vertical" - * the main dimension is {@link heatmap#spaceY} between bubbles - * @param {number} [objectSpacer=0.0] - * @memberof heatmap# + * The minimum size that an object can be + * @param {number} [minObjectSize=15] + * @memberof boxwhisker# * @property */ - objectSpacer = 0.0, + minObjectSize = 15, /** - * The minimum size that an object can be in the y dimension - * @param {number} [minObjectSize=50] - * @memberof heatmap# + * The maximum size that an object can be + * @param {number} [maxObjectSize=50] + * @memberof boxwhisker# * @property */ - yMinObjectSize = 50, + maxObjectSize = 50, /** - * The minimum size that an object can be in the x dimension - * @param {number} [minObjectSize=50] - * @memberof heatmap# + * Percent of box and whisker size that whiskers will be rendered + * see {@link boxwhisker#objectSize} + * @param {number} [whiskerWidthPercent=0.6] + * @memberof boxwhisker# * @property */ - xMinObjectSize = 50, + whiskerWidthPercent = .6, /** - * The maximum size that an object can be in the x dimension - * @param {number} [maxObjectSize=100] - * @memberof heatmap# + * Instance of ColorFunction + * @param {function} [colorFunction = colorFunction()] + * @memberof boxwhisker# * @property */ - xMaxObjectSize = 100, + colorFunction$$1 = colorFunction(), /** - * The maximum size that an object can be in the y dimension - * @param {number} [maxObjectSize=100] - * @memberof heatmap# + * The stroke width of the boxes + * @param {number} [boxStrokeWidth=2] + * @memberof boxwhisker# * @property */ - yMaxObjectSize = 100, - - + boxStrokeWidth = 2, /** - * The stroke width of the bubbles - * @param {number} [objectStrokeWidth=2] - * @memberof heatmap# + * The stroke width of the whiskers + * @param {number} [whiskerStrokeWidth=2] + * @memberof boxwhisker# * @property */ - objectStrokeWidth = 2, - // colorFunc = colorFunction(), + whiskerStrokeWidth = 2, /** * Color of the background * @param {string} [backgroundFill="transparent"] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ backgroundFill = 'transparent', /** - * Namespace for all items made by this instance of heatmap - * @param {string} [namespace="d3sm-heatmap"] - * @memberof heatmap# + * Namespace for all items made by this instance of boxwhisker + * @param {string} [namespace="d3sm-box-whisker"] + * @memberof boxwhisker# * @property */ - namespace = 'd3sm-heatmap', + namespace = 'd3sm-box-whisker', /** - * Class name for heatmap container ( element) - * @param {string} [objectClass="heatmap"] - * @memberof heatmap# + * Class name for boxwhisker container ( element) + * @param {string} [objectClass="box-whisk"] + * @memberof boxwhisker# * @property */ - objectClass = 'heatmap', + objectClass = 'box-whisk', + /** * Duration of all transitions of this element * @param {number} [transitionDuration=1000] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ transitionDuration = 1000, /** * Easing function for transitions * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ easeFunc = d3.easeExp, /** - * Stores the keys of all the cells - * Calculated after heatmap called. - * @param {string[]} [cellKeys=undefined] - * @memberof heatmap# - * @property - */ - cellKeys, - /** - * Stores the list of utils.arr.unique xValues - * Calculated after heatmap called. - * @param {string[]} [xValues=undefined] - * @memberof heatmap# + * The keys of the boxes + * @param {string[]} [boxKeys=undefined] + * @memberof boxwhisker# * @property */ - xValues, + boxKeys, /** - * Stores the list of utils.arr.unique yValues - * Calculated after heatmap called. - * @param {string[]} [yValues=undefined] - * @memberof heatmap# + * The values of the boxes + * @param {string[]} [boxValues=undefined] + * @memberof boxwhisker# * @property */ - yValues, + boxValues, /** - * Stores the list of utils.arr.unique vValues - * Calculated after heatmap called. - * @param {string[]} [vValues=undefined] - * @memberof heatmap# + * The objectSize (actual width) used by the boxes + * @param {number} [objectSize=undefined] + * @memberof boxwhisker# * @property */ - vValues, - - xKeySortingFunction = function(a, b) { return xValues.indexOf(xExtractor(a)) - xValues.indexOf(xExtractor(b)) }, - yKeySortingFunction = function(a, b) { return yValues.indexOf(yExtractor(a)) - yValues.indexOf(yExtractor(b)) }, + objectSize, /** - * Instance of ColorFunction with .colorBy set to 'category' - * @function colorFunction - * @memberof heatmap# + * The spacerSize (actual width) used by the spacers between the boxes + * @param {number} [spacerSize=undefined] + * @memberof boxwhisker# * @property */ - colorFunction$$1 = colorFunction().colorBy('category'), + spacerSize, /** * Instance of Tooltip - * @function tooltip - * @memberof heatmap# - * @property - */ - tooltip$$1 = tooltip(), - - /** - * store the size the heatmap could be in the x dimension - * the actuall size of the heatmap will be the min of xSize and {@link heatmap#ySize} - * Calculated after heatmap called. - * @param {string[]} [xSize=undefined] - * @memberof heatmap# - * @property - */ - xSize, - /** - * store the size of the spacer in the x dimension - * Calculated after heatmap called. - * @param {string[]} [xSpacerSize=undefined] - * @memberof heatmap# - * @property - */ - xSpacerSize, - - /** - * store the size the heatmap could be in the y dimension - * the actuall size of the heatmap will be the min of xSize and {@link heatmap#xSize} - * Calculated after heatmap called. - * @param {string[]} [ySize=undefined] - * @memberof heatmap# - * @property - */ - ySize, - /** - * store the size of the spacer in the y dimension. - * Calculated after heatmap called. - * @param {string[]} [xSpacerSize=undefined] - * @memberof heatmap# + * @param {function} [tooltip=tooltip()] + * @memberof boxwhisker# * @property */ - ySpacerSize; - + tooltip$$1 = tooltip(); /** * Gets or sets the selection in which items are manipulated * @param {d3.selection} [_=none] - * @returns {heatmap | d3.selection} - * @memberof heatmap + * @returns {boxwhisker | d3.selection} + * @memberof boxwhisker * @property * by default selection = selection */ - hm.selection = function(_) { return arguments.length ? (selection = _, hm) : selection; }; + boxwhisker.selection = function(_) { return arguments.length ? (selection = _, boxwhisker) : selection; }; /** * Gets or sets the data - * (see {@link heatmap#data}) + * (see {@link boxwhisker#data}) * @param {number} [_=none] - * @returns {heatmap | object} - * @memberof heatmap + * @returns {boxwhisker | object} + * @memberof boxwhisker * @property */ - hm.data = function(_) { return arguments.length ? (data = _, hm) : data; }; - // hm.orient = function(_) { return arguments.length ? (orient = _, hm) : orient; } + boxwhisker.data = function(_) { return arguments.length ? (data = _, boxwhisker) : data; }; + /** + * Gets or sets the orient of the boxes + * (see {@link boxwhisker#orient}) + * @param {number} [_=none] + * @returns {boxwhisker | object} + * @memberof boxwhisker + * @property + */ + boxwhisker.orient = function(_) { return arguments.length ? (orient = _, boxwhisker) : orient; }; /** * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link heatmap#spaceX}) + * (see {@link boxwhisker#spaceX}) * @param {number} [_=none] should be a number > 0 - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property * by default spaceX = undefined */ - hm.spaceX = function(_) { return arguments.length ? (spaceX = _, hm) : spaceX; }; + boxwhisker.spaceX = function(_) { return arguments.length ? (spaceX = _, boxwhisker) : spaceX; }; /** * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link heatmap#spaceY}) + * (see {@link boxwhisker#spaceY}) * @param {number} [_=none] should be a number > 0 - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property * by default spaceY = undefined */ - hm.spaceY = function(_) { return arguments.length ? (spaceY = _, hm) : spaceY; }; - + boxwhisker.spaceY = function(_) { return arguments.length ? (spaceY = _, boxwhisker) : spaceY; }; /** - * Gets or sets the xKey - * (see {@link heatmap#xKey}) - * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * Gets / sets whether or not boxwhisker is allowed to go beyond specified dimensions + * (see {@link boxwhisker#overflowQ}) + * @param {boolean} [_=none] + * @returns {boxwhisker | boolean} + * @memberof boxwhisker * @property - * by default xKey = 'x' + * by default overflowQ = false */ - hm.xKey = function(_) { return arguments.length ? (xKey = _, hm) : xKey; }; + boxwhisker.overflowQ = function(_) { return arguments.length ? (overflowQ = _, boxwhisker) : overflowQ; }; /** - * Gets or sets the yKey - * (see {@link heatmap#yKey}) - * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * Gets / sets the grouping of the boxes + * (see {@link boxwhisker#grouping}) + * @param {Array[]} [_=none] + * @returns {boxwhisker | Array[]} + * @memberof boxwhisker * @property - * by default yKey = 'y' + * by default grouping = undefined */ - hm.yKey = function(_) { return arguments.length ? (yKey = _, hm) : yKey; }; - + boxwhisker.grouping = function(_) { return arguments.length ? (grouping = _, boxwhisker) : grouping; }; /** - * Gets or sets the vKey - * (see {@link heatmap#vKey}) + * Gets / sets the quartilesKey + * (see {@link boxwhisker#quartilesKey}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {boxwhisker | string} + * @memberof boxwhisker * @property - * by default vKey = 'y' + * by default quartilesKey = quartiles */ - hm.vKey = function(_) { return arguments.length ? (vKey = _, hm) : vKey; }; - + boxwhisker.quartilesKey = function(_) { return arguments.length ? (quartilesKey = _, boxwhisker) : quartilesKey; }; /** - * Gets or sets the cellKeys - * (see {@link heatmap#cellKeys}) + * Gets / sets the quartilesKeys + * (see {@link boxwhisker#quartilesKeys}) * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap + * @returns {boxwhisker | string[]} + * @memberof boxwhisker * @property - * by default cellKeys = undefined + * by default quartilesKeys = [Q0, Q1, Q2, Q3, Q4] */ - hm.cellKeys = function(_) { return arguments.length ? (cellKeys = _, hm) : cellKeys; }; + boxwhisker.quartilesKeys = function(_) { return arguments.length ? (quartilesKeys = _, boxwhisker) : quartilesKeys; }; /** - * Gets or sets the xValues - * (see {@link heatmap#xValues}) - * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap + * Gets / sets the valueExtractor + * (see {@link boxwhisker#valueExtractor}) + * @param {function} [_=none] + * @returns {boxwhisker | function} + * @memberof boxwhisker * @property - * by default xValues = undefined + * by default valueExtractor = function(key, index) { return data[key][quartilesKey] }, */ - hm.xValues = function(_) { return arguments.length ? (xValues = _, hm) : xValues; }; + + boxwhisker.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, boxwhisker) : valueExtractor; }; /** - * Gets or sets the yValues - * (see {@link heatmap#yValues}) - * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap + * Gets / sets the sortingFunction + * (see {@link boxwhisker#sortingFunction}) + * @param {function} [_=none] + * @returns {boxwhisker | function} + * @memberof boxwhisker * @property - * by default yValues = undefined + * by default sortingFunction = function(keyA, keyB) {return d3.descending( + * valueExtractor(keyA)[quartilesKeys[4]], + * valueExtractor(keyB)[quartilesKeys[4]] + * )}, */ - hm.yValues = function(_) { return arguments.length ? (yValues = _, hm) : yValues; }; + boxwhisker.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, boxwhisker) : sortingFunction; }; /** - * Gets or sets the vValues - * (see {@link heatmap#vValues}) - * @param {number[]} [_=none] - * @returns {heatmap | number[]} - * @memberof heatmap + * Gets / sets the scale for which the boxwhisker values should be transformed by + * (see {@link boxwhisker#scale}) + * @param {d3.scale} [_=none] + * @returns {boxwhisker | d3.scale} + * @memberof boxwhisker * @property - * by default vValues = undefined + * by default scale = d3.scaleLinear() */ - hm.vValues = function(_) { return arguments.length ? (vValues = _, hm) : vValues; }; - - + boxwhisker.scale = function(_) { return arguments.length ? (scale = _, boxwhisker) : scale; }; /** - * Gets or sets the xExtractor - * (see {@link heatmap#xExtractor}) - * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap + * Gets / sets the padding for the domain of the scale + * (see {@link boxwhisker#domainPadding}) + * @param {number} [_=none] + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default xExtractor = undefined + * by default domainPadding = 0.5 */ - hm.xExtractor = function(_) { return arguments.length ? (xExtractor = _, hm) : xExtractor; }; + boxwhisker.domainPadding = function(_) { return arguments.length ? (domainPadding = _, boxwhisker) : domainPadding; }; /** - * Gets or sets the yExtractor - * (see {@link heatmap#yExtractor}) - * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap + * Gets / sets objectSpacer + * (see {@link boxwhisker#objectSpacer}) + * @param {number} [_=none] + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default yExtractor = undefined + * by default objectSpacer = 0.05 */ - hm.yExtractor = function(_) { return arguments.length ? (yExtractor = _, hm) : yExtractor; }; + boxwhisker.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, boxwhisker) : objectSpacer; }; /** - * Gets or sets the vExtractor - * (see {@link heatmap#vExtractor}) - * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap + * Gets / sets the minObjectSize + * (see {@link boxwhisker#minObjectSize}) + * @param {number} [_=none] + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default vExtractor = undefined + * by default minObjectSize = 15 */ - hm.vExtractor = function(_) { return arguments.length ? (vExtractor = _, hm) : vExtractor; }; - - /** - * Gets / sets whether or not heatmap is allowed to go beyond specified dimensions - * (see {@link heatmap#spaceX}) - * @param {boolean} [_=none] - * @returns {heatmap | boolean} - * @memberof heatmap - * @property - * by default overflowQ = false - */ - hm.overflowQ = function(_) { return arguments.length ? (overflowQ = _, hm) : overflowQ; }; - /** - * Gets / sets objectSpacer - * (see {@link heatmap#objectSpacer}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default objectSpacer = 0.0 - */ - hm.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, objectSpacer) : data; }; + boxwhisker.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, boxwhisker) : minObjectSize; }; /** - * Gets / sets the minObjectSize - * (see {@link heatmap#minObjectSize}) + * Gets / sets the maxObjectSize + * (see {@link boxwhisker#maxObjectSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default minObjectSize = 50 + * by default maxObjectSize = 50 */ - hm.yMinObjectSize = function(_) { return arguments.length ? (yMinObjectSize = _, hm) : yMinObjectSize; }; + boxwhisker.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, boxwhisker) : maxObjectSize; }; /** - * Gets / sets the maxObjectSize - * (see {@link heatmap#maxObjectSize}) + * Gets / sets the whiskerWidthPercent + * (see {@link boxwhisker#whiskerWidthPercent}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default maxObjectSize = 100 + * by default maxObjectSize = 0.6 */ - hm.yMaxObjectSize = function(_) { return arguments.length ? (yMaxObjectSize = _, hm) : yMaxObjectSize; }; + boxwhisker.whiskerWidthPercent = function(_) { return arguments.length ? (whiskerWidthPercent = _, boxwhisker) : whiskerWidthPercent; }; /** - * Gets / sets the minObjectSize - * (see {@link heatmap#minObjectSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * Gets / sets the colorFunction + * (see {@link boxwhisker#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {boxwhisker | colorFunction} + * @memberof boxwhisker * @property - * by default minObjectSize = 50 + * by default colorFunction = colorFunction() */ - hm.xMinObjectSize = function(_) { return arguments.length ? (xMinObjectSize = _, hm) : xMinObjectSize; }; + boxwhisker.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, boxwhisker) : colorFunction$$1; }; /** - * Gets / sets the maxObjectSize - * (see {@link heatmap#maxObjectSize}) + * Gets / sets the boxStrokeWidth + * (see {@link boxwhisker#boxStrokeWidth}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default maxObjectSize = 100 + * by default boxStrokeWidth = 2 */ - hm.xMaxObjectSize = function(_) { return arguments.length ? (xMaxObjectSize = _, hm) : xMaxObjectSize; }; + boxwhisker.boxStrokeWidth = function(_) { return arguments.length ? (boxStrokeWidth = _, boxwhisker) : boxStrokeWidth; }; /** - * Gets / sets the objectStrokeWidth - * (see {@link heatmap#objectStrokeWidth}) + * Gets / sets the whiskerStrokeWidth + * (see {@link boxwhisker#whiskerStrokeWidth}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default objectStrokeWidth = 2 + * by default whiskerStrokeWidth = 2 */ - hm.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, hm) : objectStrokeWidth; }; + boxwhisker.whiskerStrokeWidth = function(_) { return arguments.length ? (whiskerStrokeWidth = _, boxwhisker) : whiskerStrokeWidth; }; + /** * Gets / sets the backgroundFill - * (see {@link heatmap#backgroundFill}) + * (see {@link boxwhisker#backgroundFill}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {boxwhisker | string} + * @memberof boxwhisker * @property * by default backgroundFill = 'transparent' */ - hm.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, hm) : backgroundFill; }; + boxwhisker.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, boxwhisker) : backgroundFill; }; /** * Gets / sets the namespace - * (see {@link heatmap#namespace}) + * (see {@link boxwhisker#namespace}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {boxwhisker | string} + * @memberof boxwhisker * @property - * by default namespace = 'd3sm-heatmap' + * by default namespace = 'd3sm-boxwhisker' */ - hm.namespace = function(_) { return arguments.length ? (namespace = _, hm) : namespace; }; + boxwhisker.namespace = function(_) { return arguments.length ? (namespace = _, boxwhisker) : namespace; }; /** * Gets / sets the objectClass - * (see {@link heatmap#objectClass}) + * (see {@link boxwhisker#objectClass}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {boxwhisker | string} + * @memberof boxwhisker * @property * by default objectClass = 'tick-group' */ - hm.objectClass = function(_) { return arguments.length ? (objectClass = _, hm) : objectClass; }; + boxwhisker.objectClass = function(_) { return arguments.length ? (objectClass = _, boxwhisker) : objectClass; }; /** * Gets / sets the transitionDuration - * (see {@link heatmap#transitionDuration}) + * (see {@link boxwhisker#transitionDuration}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property * by default transitionDuration = 1000 */ - hm.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, hm) : transitionDuration; }; + boxwhisker.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, boxwhisker) : transitionDuration; }; /** * Gets / sets the easeFunc - * (see {@link heatmap#easeFunc}) + * (see {@link boxwhisker#easeFunc}) * @param {d3.ease} [_=none] - * @returns {heatmap | d3.ease} - * @memberof heatmap + * @returns {boxwhisker | d3.ease} + * @memberof boxwhisker * @property * by default easeFunc = d3.easeExp */ - hm.easeFunc = function(_) { return arguments.length ? (easeFunc = _, hm) : easeFunc; }; - - /** - * Gets / sets the tooltip - * (see {@link heatmap#tooltip}) - * @param {tooltip} [_=none] - * @returns {heatmap | tooltip} - * @memberof heatmap - * @property - * by default tooltip = tooltip() - */ - hm.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, hm) : tooltip$$1; }; + boxwhisker.easeFunc = function(_) { return arguments.length ? (easeFunc = _, boxwhisker) : easeFunc; }; /** - * Gets / sets the colorFunction - * (see {@link heatmap#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {heatmap | colorFunction} - * @memberof heatmap + * Gets / sets the barKeys + * (see {@link boxwhisker#boxKeys}) + * @param {string[]} [_=none] + * @returns {boxwhisker | string[]} + * @memberof boxwhisker * @property - * by default colorFunction = colorFunction() + * by default boxKeys = undefined */ - hm.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, hm) : colorFunction$$1; }; - + boxwhisker.boxKeys = function(_) { return arguments.length ? (boxKeys = _, boxwhisker) : boxKeys; }; /** - * Gets / sets the xSize - * (see {@link heatmap#xSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * Gets / sets the boxValues + * (see {@link boxwhisker#boxValues}) + * @param {number[]} [_=none] + * @returns {boxwhisker | number[]} + * @memberof boxwhisker * @property - * by default xSize = undefined + * by default boxValues = undefined */ - hm.xSize = function(_) { return arguments.length ? (xSize = _, hm) : xSize; }; + boxwhisker.boxValues = function(_) { return arguments.length ? (boxValues = _, boxwhisker) : boxValues; }; /** - * Gets / sets the xSpacerSize - * (see {@link heatmap#xSpacerSize}) + * Gets / sets the objectSize + * (see {@link boxwhisker#objectSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default xSpacerSize = undefined + * by default objectSize = undefined */ - hm.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, hm) : xSpacerSize; }; + boxwhisker.objectSize = function(_) { return arguments.length ? (objectSize = _, boxwhisker) : objectSize; }; /** - * Gets / sets the ySize - * (see {@link heatmap#ySize}) + * Gets / sets the spacerSize + * (see {@link boxwhisker#spacerSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default ySize = undefined + * by default spacerSize = undefined */ - hm.ySize = function(_) { return arguments.length ? (ySize = _, hm) : ySize; }; + boxwhisker.spacerSize = function(_) { return arguments.length ? (spacerSize = _, boxwhisker) : spacerSize; }; /** - * Gets / sets the ySpacerSize - * (see {@link heatmap#ySpacerSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * Gets / sets the tooltip + * (see {@link boxwhisker#tooltip}) + * @param {tooltip} [_=none] + * @returns {boxwhisker | tooltip} + * @memberof boxwhisker * @property - * by default ySpacerSize = undefined + * by default tooltip = tooltip() */ - hm.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, hm) : ySpacerSize; }; - // hm.yKeySortingFunction = function(_) { return arguments.length ? (yKeySortingFunction = _, hm) : yKeySortingFunction; } - // hm.xKeySortingFunction = function(_) { return arguments.length ? (xKeySortingFunction = _, hm) : xKeySortingFunction; } + boxwhisker.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, boxwhisker) : tooltip$$1; }; + function boxwhisker() { + // for convenience in handling orientation specific values + var horizontalQ = (orient == 'horizontal') ? true : false; + var verticalQ = !horizontalQ; - function hm() { + // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - - cellKeys = d3.keys(data); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - xValues = utils$1.arr.unique(cellKeys.map(xExtractor)); - yValues = utils$1.arr.unique(cellKeys.map(yExtractor)); - vValues = utils$1.arr.unique(cellKeys.map(vExtractor)); + // if grouping is undefined sort keys by sorting funct + var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping; + // to prevent re-calculation and getters to be passed to axes + boxKeys = utils.arr.flatten(ordered); + boxValues = boxKeys.map(valueExtractor); - cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }); - utils$1.con.log('heatmap', 'cells are sorted by', cellKeys); + var numberOfObjects = boxKeys.length; + var extent = [ + Math.min(...boxValues.map(function(d,i){return d[quartilesKeys[0]]})) - domainPadding, + Math.max(...boxValues.map(function(d,i){return d[quartilesKeys[4]]})) + domainPadding + ]; + // set the scale + scale.domain(extent).range(horizontalQ ? [0,spaceY] : [spaceX, 0]); + var space = horizontalQ ? spaceX : spaceY; + // calculate object size + objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + // calculate spacer size if needed + spacerSize = utils.math.calculateWidthOfSpacer(boxKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); + // make the nested groups + var spacerFunction = groupingSpacer() + .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) + .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc) + .namespace(namespace); - utils$1.con.log('heatmap', 'x and y keys are', {x: xValues, y:yValues}); + // move stuff + spacerFunction(container, ordered, 0); + var parentIndexArray = []; + container.selectAll('g:not(.to-remove).'+objectClass) + .each(function(d, i){if (utils.arr.hasQ(boxKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}}); - var xDim = xValues.length, yDim = yValues.length; - ySize = utils$1.math.calculateWidthOfObject(spaceY, yDim, yMinObjectSize, yMaxObjectSize, objectSpacer, overflowQ); - xSize = utils$1.math.calculateWidthOfObject(spaceX, xDim, xMinObjectSize, xMaxObjectSize, objectSpacer, overflowQ); - ySpacerSize = utils$1.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); - xSpacerSize = utils$1.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); - // console.table({ - // x:{ - // object: xSize, - // spacer: xSpacerSize, - // dim: xDim - // }, - // y:{ - // object: ySize, - // spacer: ySpacerSize, - // dim: yDim - // } - // - // }) - utils$1.con.log('heatmap', 'size of', {x: xSize, y: ySize}); + colorFunction$$1 = colorFunction$$1.colorBy() == 'index' + ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) + : colorFunction$$1.dataExtent(extent); + // set attributes for box and whiskers + container.selectAll('g:not(.to-remove).'+objectClass).each(function(key, i) { + var t = d3.select(this), + currentData = data[key], - var ySpacer = groupingSpacer() - .horizontalQ(false) - .moveby('category') - .numberOfObjects(yDim) - .objectClass(utils$1.str.hypenate(objectClass, 'row')) - .objectSize(ySize + ySpacerSize) - .spacerSize(0) - .transitionDuration(transitionDuration) - .easeFunc(easeFunc) - .namespace('row'); + quartiles$$1 = valueExtractor(key, i), + q0 = quartiles$$1[quartilesKeys[0]], + q1 = quartiles$$1[quartilesKeys[1]], + q2 = quartiles$$1[quartilesKeys[2]], + q3 = quartiles$$1[quartilesKeys[3]], + q4 = quartiles$$1[quartilesKeys[4]]; - var xSpacer = groupingSpacer() - .horizontalQ(true) - .moveby('category') - .numberOfObjects(xDim) - .objectClass(objectClass) - .objectSize(xSize + xSpacerSize) - .spacerSize(0) - .transitionDuration(transitionDuration) - .easeFunc(easeFunc); + var i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), + fillColor = colorFunction$$1(key, q2, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(key, q2, i, 'stroke'); - ySpacer(container, yValues, 0); - container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'row')) - .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); + var + whisk = utils.sel.safeSelect(t, 'g', 'whisker'), + uWhisk = utils.sel.safeSelect(whisk, 'path', 'upper'), + lWhisk = utils.sel.safeSelect(whisk, 'path', 'lower'), + quart = utils.sel.safeSelect(t, 'g', 'quartile'), + uQuart = utils.sel.safeSelect(quart, 'rect', 'upper'), + lQuart = utils.sel.safeSelect(quart, 'rect', 'lower'), + mQuart = utils.sel.safeSelect(quart, 'circle', 'median'); + + + // set upper quartile (q3) + uQuart.transition().duration(transitionDuration).ease(easeFunc) + .attr('width', horizontalQ ? objectSize : scale(q3) - scale(q2)) + .attr('height', verticalQ ? objectSize : scale(q3) - scale(q2)) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', boxStrokeWidth) + .attr('transform', function(d, i){ + var + x = horizontalQ ? 0 : scale(q2), + y = verticalQ ? 0 : scale(extent[1]) - scale(q3), + t = 'translate('+x+','+y+')'; + return t + }); - var cells = container.selectAll('g:not(.to-remove).'+objectClass); + // set lower quartile (q1) + lQuart.transition().duration(transitionDuration).ease(easeFunc) + .attr('width', horizontalQ ? objectSize : scale(q2) - scale(q1)) + .attr('height', verticalQ ? objectSize : scale(q2) - scale(q1)) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', boxStrokeWidth) + .attr('transform', function(d, i){ + var + x = horizontalQ ? 0 : scale(q1), + y = verticalQ ? 0 : scale(extent[1]) - scale(q2), + t = 'translate('+x+','+y+')'; + return t + }); - if (cellKeys.length != yValues.length * xValues.length) { - var lookup = {}; - cellKeys.map(function(k, i){ - lookup[xExtractor(k)+'::'+yExtractor(k)] = k; + // set median (q2) + mQuart.transition().duration(transitionDuration).ease(easeFunc) + .attr('r', function(d, i){ + var r = objectSize / 2; + var dif = (scale(q3) - scale(q1)) / 2; + return (r > dif) ? dif : r + }) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', boxStrokeWidth) + .attr('transform', function(d, i){ + var + x = horizontalQ ? objectSize / 2 : scale(q2), + y = verticalQ ? objectSize / 2 : scale(extent[1]) - scale(q2), + t = 'translate('+x+','+y+')'; + return t }); - var positionedCellKeys = []; - for (var i = 0; i < yValues.length; i++) { - for (var j = 0; j < xValues.length; j++) { - var lookupValue = lookup[xValues[j]+"::"+yValues[i]]; - if (lookupValue == undefined) { - positionedCellKeys.push(undefined); - } else { - positionedCellKeys.push(lookupValue); - } - } - } + // set lower whisker (min) + lWhisk.transition().duration(transitionDuration).ease(easeFunc) + .attr('d', function(dd, ii){ + var + dir = false, + x = 0, + y = 0, + h = horizontalQ ? scale(q1) - scale(q0) : objectSize, + w = verticalQ ? scale(q1) - scale(q0) : objectSize; + return utils.paths.whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient) + }) + .attr('transform', function(d, i){ + var + x = horizontalQ ? 0 : scale(q1), + y = verticalQ ? 0 : scale(extent[1]) - scale(q1), + t = 'translate('+x+','+y+')'; + return t + }) + .attr('stroke', 'black').attr('stroke-width', whiskerStrokeWidth) + .attr('fill', 'none'); - cells.data(positionedCellKeys); - - // maybe breaks this - // !!!!!! IMPORTANT NOTE TODO LOOK HERE BUG - cellKeys = positionedCellKeys; - } else { - cells.data(cellKeys); - } - - - - var parentIndexArray = []; - cells.each(function(d, i){ parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); }); - - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' - ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) - : colorFunction$$1.dataExtent([0, Math.max(...vValues)]); - - cells.each(function(key, i) { - utils$1.con.log('heatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); - - var t = d3.select(this); - if (key == undefined) {return} - var currentData = data[key], - value = vExtractor(key, i), - i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, value, i, 'fill'), // prevent duplicate computation - strokeColor = colorFunction$$1(key, value, i, 'stroke'); - - var c = utils$1.sel.safeSelect(t, 'rect', utils$1.str.hypenate(objectClass,'rect')); - c.attr('width', xSize + xSpacerSize - objectStrokeWidth) - .attr('height', ySize + ySpacerSize - objectStrokeWidth) - .attr('fill', fillColor) - .attr('x', objectStrokeWidth/2) - .attr('y', objectStrokeWidth/2) - .attr('stroke', "#000") - .attr('stroke-width', objectStrokeWidth); + // set upper whisker (max) + uWhisk.transition().duration(transitionDuration).ease(easeFunc) + .attr('d', function(dd, ii){ + var + dir = true, + x = 0, + y = 0, + h = horizontalQ ? scale(q4) - scale(q3) : objectSize, + w = verticalQ ? scale(q4) - scale(q3) : objectSize; + return utils.paths.whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient) + }) + .attr('transform', function(d, i){ + var + x = horizontalQ ? 0 : scale(q3), + y = verticalQ ? 0 : scale(extent[1]) - scale(q4), + t = 'translate('+x+','+y+')'; + return t + }) + .attr('stroke', 'black') + .attr('stroke-width', whiskerStrokeWidth) + .attr('fill', 'none'); }); - tooltip$$1.selection(cells.selectAll('rect.'+utils$1.str.hypenate(objectClass, 'rect'))) + tooltip$$1.selection(container.selectAll('g:not(.to-remove).'+objectClass)) .data(data); - // .keys(['r', 'v']) - // .header(function(d, i){return utils.str.hypenate(data[d][xKey], data[d][yKey]) }) - tooltip$$1(); } - return hm; + return boxwhisker } -/******************************************************************************* -** ** -** ** -** VIOLIN ** -** ** -** ** -*******************************************************************************/ - /** - * Creates a violin + * Creates a heatmap * - * {@link https://sumneuron.gitlab.io/d3sm/demos/basic-violins/index.html Demo} - * @constructor violin + * {@link https://sumneuron.gitlab.io/d3sm/demos/heatmap-heatmap/index.html Demo} + * @constructor heatmap * @param {d3.selection} selection - * @namespace violin - * @returns {function} violin + * @namespace heatmap + * @returns {function} heatmap */ -function violin( selection ) { +function heatmap( selection ) { var /** - * Data to plot. Assumed to be a object, where each key corresponds to a violin - * (see {@link violin#data}) + * Data to plot. Assumed to be a object, where each key corresponds to a cell + * (see {@link heatmap#data}) * @param {Object} [data=undefined] - * @memberof violin# + * @memberof heatmap# * @property */ data, + /** - * Which direction to render the bars in - * (see {@link violin#orient}) - * @param {number} [orient='horizontal'] - * @memberof violin# - * @property - */ - orient='horizontal', - /** - * Amount of horizontal space (in pixels) avaible to render the violin in - * (see {@link violin#spaceX}) + * Amount of horizontal space (in pixels) avaible to render the heatmap in + * (see {@link heatmap#spaceX}) * @param {number} [spaceX=undefined] - * @memberof violin# + * @memberof heatmap# * @property */ spaceX, /** - * Amount of vertical space (in pixels) avaible to render the violin in - * (see {@link violin.spaceY}) + * Amount of vertical space (in pixels) avaible to render the heatmap in + * (see {@link heatmap.spaceY}) * @param {number} [spaceY=undefined] - * @memberof violin# + * @memberof heatmap# * @property */ spaceY, + /** - * Whether or not to allow violin to render elements pass the main spatial dimension - * given the orientation (see {@link violin#orient}), where {@link violin#orient}="horizontal" - * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" - * the main dimension is {@link violin#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof violin# + * The internal key of the cell specifiying to which x axis key it belongs + * (see {@link heatmap.xKey}) + * @param {string} [xKey='x'] + * @memberof heatmap# * @property */ - overflowQ = true, + xKey = 'x', /** - * Whether or not to display points inside the points - * @param {boolean} [pointsQ=false] - * @memberof violin# + * The internal key of the cell specifiying to which y axis key it belongs + * (see {@link heatmap.yKey}) + * @param {string} [yKey='y'] + * @memberof heatmap# * @property */ - pointsQ = true, + yKey = 'y', + /** - * An array - putatively of other arrays - depicting how bars should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof violin# + * The internal key of the cell specifiying what value to use to determine the color + * (see {@link heatmap.vKey}) + * @param {string} [vKey='v'] + * @memberof heatmap# * @property */ - grouping, + vKey = 'v', + /** - * How to get the value of the violin - * @param {function} [valueExtractor=function(key, index) { return data[key] }] - * @memberof violin# + * Function for extracting the the value from xKey. + * (see {@link heatmap.xExtractor}) + * @param {function} [xExtractor=function(key, i) { return data[key][xKey] }] + * @returns {string} + * @memberof heatmap# * @property */ - valueExtractor = function(key, index) {return data[key] }, + xExtractor = function(key, i) {return data[key][xKey] }, /** - * How to sort the bars - if {@link violin#grouping} is not provided. - * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] - * @memberof violin# + * Function for extracting the the value from yKey. + * (see {@link heatmap.yExtractor}) + * @param {function} [yExtractor=function(key, i) { return data[key][yKey] }] + * @returns {string} + * @memberof heatmap# * @property */ - sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, + yExtractor = function(key, i) { return data[key][yKey] }, /** - * The scale for which violin values should be transformed by - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof violin# + * Function for extracting the the value from vKey. + * (see {@link heatmap.vExtractor}) + * @param {function} [vExtractor=function(key, i) { return data[key][vKey] }] + * @returns {number} + * @memberof heatmap# * @property */ - scale = d3.scaleLinear(), + vExtractor = function(key, i) { return data[key][vKey] }, + + /** - * The padding for the domain of the scale (see {@link violin#scale}) - * @param {number} [domainPadding=0.5] - * @memberof violin# + * Whether or not to allow heatmap to render elements pass the main spatial dimension + * given the orientation (see {@link heatmap#orient}), where {@link heatmap#orient}="bottom" or {@link heatmap#orient}="top" + * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="left" or {@link heatmap#orient}="right" + * the main dimension is {@link heatmap#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof heatmap# * @property */ - domainPadding = 0.5, + overflowQ = false, + /** * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link violin#orient}), where {@link violin#orient}="horizontal" - * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" - * the main dimension is {@link violin#spaceY} between bars - * @param {number} [objectSpacer=0.05] - * @memberof violin# + * (see {@link heatmap#orient}), where {@link heatmap#orient}="horizontal" + * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="vertical" + * the main dimension is {@link heatmap#spaceY} between bubbles + * @param {number} [objectSpacer=0.0] + * @memberof heatmap# * @property */ - objectSpacer = 0.05, + objectSpacer = 0.0, /** - * The minimum size that an object can be + * The minimum size that an object can be in the y dimension * @param {number} [minObjectSize=50] - * @memberof violin# - * @property - */ - minObjectSize = 50, - /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof violin# + * @memberof heatmap# * @property */ - maxObjectSize = 100, - + yMinObjectSize = 50, /** - * The stroke width of the bars - * @param {number} [barStrokeWidth=2] - * @memberof violin# + * The minimum size that an object can be in the x dimension + * @param {number} [minObjectSize=50] + * @memberof heatmap# * @property */ - objectStrokeWidth = 2, + xMinObjectSize = 50, /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof violin# + * The maximum size that an object can be in the x dimension + * @param {number} [maxObjectSize=100] + * @memberof heatmap# * @property */ - colorFunction$$1 = colorFunction(), + xMaxObjectSize = 100, /** - * Instance of ColorFunction modified by a scale for the points - * @param {function} [pointColorFunc = colorFunction()] - * @memberof violin# + * The maximum size that an object can be in the y dimension + * @param {number} [maxObjectSize=100] + * @memberof heatmap# * @property */ - pointColorFunc = function (d, type, base, min, max) { - var minMaxHexScale = d3.scaleLinear().domain([min, max]).range([-0.25, 0.05]); - var scaledColor = utils.color.modifyHexidecimalColorLuminance(base.replace('#', ''), minMaxHexScale(d)); - var mod = type == "stroke" ? 0 : 0.25; - return utils.color.modifyHexidecimalColorLuminance(scaledColor.replace('#', ''), mod) - }, + yMaxObjectSize = 100, + /** - * The radius of a point - * @param {number} [pointRadius=3] - * @memberof violin# - * @property - */ - pointRadius = 3, - /** - * The stroke width of the oints - * @param {number} [pointStrokeWidth=2] - * @memberof violin# + * The stroke width of the bubbles + * @param {number} [objectStrokeWidth=2] + * @memberof heatmap# * @property */ - pointStrokeWidth = 2, + objectStrokeWidth = 2, + // colorFunc = colorFunction(), /** * Color of the background * @param {string} [backgroundFill="transparent"] - * @memberof violin# + * @memberof heatmap# * @property */ backgroundFill = 'transparent', /** - * Namespace for all items made by this instance of violin - * @param {string} [namespace="d3sm-violin"] - * @memberof violin# + * Namespace for all items made by this instance of heatmap + * @param {string} [namespace="d3sm-heatmap"] + * @memberof heatmap# * @property */ - namespace = 'd3sm-violin', + namespace = 'd3sm-heatmap', /** - * Class name for violin container ( element) - * @param {string} [objectClass="violin"] - * @memberof violin# + * Class name for heatmap container ( element) + * @param {string} [objectClass="heatmap"] + * @memberof heatmap# * @property */ - objectClass = 'violin', + objectClass = 'heatmap', /** * Duration of all transitions of this element * @param {number} [transitionDuration=1000] - * @memberof violin# + * @memberof heatmap# * @property */ transitionDuration = 1000, /** * Easing function for transitions * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof violin# + * @memberof heatmap# * @property */ easeFunc = d3.easeExp, /** - * The keys corresponding to each quartile - * @param {string[]} [quartileKeys=["Q0", "Q1", "Q2", "Q3", "Q4"]] - * @memberof violin# + * Stores the keys of all the cells + * Calculated after heatmap called. + * @param {string[]} [cellKeys=undefined] + * @memberof heatmap# * @property */ - quartileKeys = ["Q0", "Q1", "Q2", "Q3", "Q4"], - + cellKeys, /** - * The keys of the bars - * @param {string[]} [violinKeys=undefined] - * @memberof violin# + * Stores the list of utils.arr.unique xValues + * Calculated after heatmap called. + * @param {string[]} [xValues=undefined] + * @memberof heatmap# * @property */ - violinKeys, + xValues, /** - * The values of the bars - * @param {number[]} [violinValues=undefined] - * @memberof violin# + * Stores the list of utils.arr.unique yValues + * Calculated after heatmap called. + * @param {string[]} [yValues=undefined] + * @memberof heatmap# * @property */ - violinValues, + yValues, /** - * The objectSize (actual width) used by the bars - * @param {number} [objectSize=undefined] - * @memberof violin# + * Stores the list of utils.arr.unique vValues + * Calculated after heatmap called. + * @param {string[]} [vValues=undefined] + * @memberof heatmap# * @property */ - objectSize, + vValues, + + xKeySortingFunction = function(a, b) { return xValues.indexOf(xExtractor(a)) - xValues.indexOf(xExtractor(b)) }, + yKeySortingFunction = function(a, b) { return yValues.indexOf(yExtractor(a)) - yValues.indexOf(yExtractor(b)) }, /** - * The spacerSize (actual width) used by the spacers between the bars - * @param {number} [spacerSize=undefined] - * @memberof violin# + * Instance of ColorFunction with .colorBy set to 'category' + * @function colorFunction + * @memberof heatmap# * @property */ - spacerSize, - + colorFunction$$1 = colorFunction().colorBy('category'), /** * Instance of Tooltip - * @param {function} [tooltip=tooltip()] - * @memberof violin# + * @function tooltip + * @memberof heatmap# * @property */ - tooltip$$1 = tooltip().keys([quartileKeys[4], quartileKeys[3], quartileKeys[2], quartileKeys[1], quartileKeys[0]]), - pointsTooltip = tooltip(), - // pointKeyExtractor = function(violinKey, violinData, violinValues) {return d3.keys(violinValues[violinKey].values)}, - // pointValueExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]}, - + tooltip$$1 = tooltip(), /** - * Function which given the key of the violin and that key's associated value - * returns the object consiting of the points of the violin - * (see {@link violin#violinPointsExtractor}) - * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] - * @memberof violin# + * store the size the heatmap could be in the x dimension + * the actuall size of the heatmap will be the min of xSize and {@link heatmap#ySize} + * Calculated after heatmap called. + * @param {string[]} [xSize=undefined] + * @memberof heatmap# * @property */ - violinPointsExtractor = function (violinKey, violinData) {return violinData.points }, + xSize, /** - * Function which given the key of the current point and the object of points for the - * violin, returns the numerical value of the point - * (see {@link violin#violinPointValueExtractor}) - * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] - * @memberof violin# + * store the size of the spacer in the x dimension + * Calculated after heatmap called. + * @param {string[]} [xSpacerSize=undefined] + * @memberof heatmap# * @property */ - violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }; - + xSpacerSize, /** - * Gets or sets the violinPointsExtractor - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } - */ - violin.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor = _, violin) : violinPointsExtractor; }; + * store the size the heatmap could be in the y dimension + * the actuall size of the heatmap will be the min of xSize and {@link heatmap#xSize} + * Calculated after heatmap called. + * @param {string[]} [ySize=undefined] + * @memberof heatmap# + * @property + */ + ySize, /** - * Gets or sets the violinPointValueExtractor - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - * by default violinPointsExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]} - */ - violin.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor = _, violin) : violinPointValueExtractor; }; - + * store the size of the spacer in the y dimension. + * Calculated after heatmap called. + * @param {string[]} [xSpacerSize=undefined] + * @memberof heatmap# + * @property + */ + ySpacerSize; /** * Gets or sets the selection in which items are manipulated * @param {d3.selection} [_=none] - * @returns {violin | d3.selection} - * @memberof violin + * @returns {heatmap | d3.selection} + * @memberof heatmap * @property * by default selection = selection */ - violin.selection = function(_) { return arguments.length ? (selection = _, violin) : selection; }; + hm.selection = function(_) { return arguments.length ? (selection = _, hm) : selection; }; /** * Gets or sets the data - * (see {@link violin#data}) - * @param {number} [_=none] - * @returns {violin | object} - * @memberof violin - * @property - */ - violin.data = function(_) { return arguments.length ? (data = _, violin) : data; }; - /** - * Gets or sets the orient of the boxes - * (see {@link violin#orient}) + * (see {@link heatmap#data}) * @param {number} [_=none] - * @returns {violin | object} - * @memberof violin + * @returns {heatmap | object} + * @memberof heatmap * @property */ - violin.orient = function(_) { return arguments.length ? (orient = _, violin) : orient; }; + hm.data = function(_) { return arguments.length ? (data = _, hm) : data; }; + // hm.orient = function(_) { return arguments.length ? (orient = _, hm) : orient; } /** * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link violin#spaceX}) + * (see {@link heatmap#spaceX}) * @param {number} [_=none] should be a number > 0 - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property * by default spaceX = undefined */ - violin.spaceX = function(_) { return arguments.length ? (spaceX = _, violin) : spaceX; }; + hm.spaceX = function(_) { return arguments.length ? (spaceX = _, hm) : spaceX; }; /** * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link violin#spaceY}) + * (see {@link heatmap#spaceY}) * @param {number} [_=none] should be a number > 0 - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property * by default spaceY = undefined */ - violin.spaceY = function(_) { return arguments.length ? (spaceY = _, violin) : spaceY; }; - + hm.spaceY = function(_) { return arguments.length ? (spaceY = _, hm) : spaceY; }; /** - * Gets / sets whether or not violin is allowed to go beyond specified dimensions - * (see {@link violin#overflowQ}) - * @param {boolean} [_=none] - * @returns {violin | boolean} - * @memberof violin + * Gets or sets the xKey + * (see {@link heatmap#xKey}) + * @param {string} [_=none] + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default overflowQ = false + * by default xKey = 'x' */ - violin.overflowQ = function(_) { return arguments.length ? (overflowQ = _, violin) : overflowQ; }; + hm.xKey = function(_) { return arguments.length ? (xKey = _, hm) : xKey; }; /** - * Gets / sets whether or not to plot points with the violins - * (see {@link violin#pointsQ}) - * @param {boolean} [_=none] - * @returns {violin | boolean} - * @memberof violin + * Gets or sets the yKey + * (see {@link heatmap#yKey}) + * @param {string} [_=none] + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default pointsQ = false + * by default yKey = 'y' */ - violin.pointsQ = function(_) { return arguments.length ? (pointsQ = _, violin) : pointsQ; }; - + hm.yKey = function(_) { return arguments.length ? (yKey = _, hm) : yKey; }; /** - * Gets / sets the grouping of the boxes - * (see {@link violin#grouping}) - * @param {Array[]} [_=none] - * @returns {violin | Array[]} - * @memberof violin + * Gets or sets the vKey + * (see {@link heatmap#vKey}) + * @param {string} [_=none] + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default grouping = undefined + * by default vKey = 'y' */ - violin.grouping = function(_) { return arguments.length ? (grouping = _, violin) : grouping; }; + hm.vKey = function(_) { return arguments.length ? (vKey = _, hm) : vKey; }; + /** - * Gets / sets the valueExtractor - * (see {@link violin#valueExtractor}) - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin + * Gets or sets the cellKeys + * (see {@link heatmap#cellKeys}) + * @param {string[]} [_=none] + * @returns {heatmap | string[]} + * @memberof heatmap * @property + * by default cellKeys = undefined */ - violin.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, violin) : valueExtractor; }; + hm.cellKeys = function(_) { return arguments.length ? (cellKeys = _, hm) : cellKeys; }; /** - * Gets / sets the sortingFunction - * (see {@link violin#sortingFunction}) - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin + * Gets or sets the xValues + * (see {@link heatmap#xValues}) + * @param {string[]} [_=none] + * @returns {heatmap | string[]} + * @memberof heatmap * @property + * by default xValues = undefined */ - violin.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, violin) : sortingFunction; }; - + hm.xValues = function(_) { return arguments.length ? (xValues = _, hm) : xValues; }; /** - * Gets / sets the scale for which the violin values should be transformed by - * (see {@link violin#scale}) - * @param {d3.scale} [_=none] - * @returns {violin | d3.scale} - * @memberof violin + * Gets or sets the yValues + * (see {@link heatmap#yValues}) + * @param {string[]} [_=none] + * @returns {heatmap | string[]} + * @memberof heatmap * @property - * by default scale = d3.scaleLinear() + * by default yValues = undefined */ - violin.scale = function(_) { return arguments.length ? (scale = _, violin) : scale; }; + hm.yValues = function(_) { return arguments.length ? (yValues = _, hm) : yValues; }; /** - * Gets / sets the padding for the domain of the scale - * (see {@link violin#domainPadding}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * Gets or sets the vValues + * (see {@link heatmap#vValues}) + * @param {number[]} [_=none] + * @returns {heatmap | number[]} + * @memberof heatmap * @property - * by default domainPadding = 0.5 + * by default vValues = undefined */ - violin.domainPadding = function(_) { return arguments.length ? (domainPadding = _, violin) : domainPadding; }; + hm.vValues = function(_) { return arguments.length ? (vValues = _, hm) : vValues; }; /** - * Gets / sets objectSpacer - * (see {@link violin#objectSpacer}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * Gets or sets the xExtractor + * (see {@link heatmap#xExtractor}) + * @param {function} [_=none] + * @returns {heatmap | function} + * @memberof heatmap * @property - * by default objectSpacer = 0.05 + * by default xExtractor = undefined */ - violin.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, violin) : objectSpacer; }; + hm.xExtractor = function(_) { return arguments.length ? (xExtractor = _, hm) : xExtractor; }; /** - * Gets / sets the minObjectSize - * (see {@link violin#minObjectSize}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * Gets or sets the yExtractor + * (see {@link heatmap#yExtractor}) + * @param {function} [_=none] + * @returns {heatmap | function} + * @memberof heatmap * @property - * by default minObjectSize = 15 + * by default yExtractor = undefined */ - violin.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, violin) : minObjectSize; }; + hm.yExtractor = function(_) { return arguments.length ? (yExtractor = _, hm) : yExtractor; }; /** - * Gets / sets the maxObjectSize - * (see {@link violin#maxObjectSize}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * Gets or sets the vExtractor + * (see {@link heatmap#vExtractor}) + * @param {function} [_=none] + * @returns {heatmap | function} + * @memberof heatmap * @property - * by default maxObjectSize = 50 + * by default vExtractor = undefined */ - violin.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, violin) : maxObjectSize; }; + hm.vExtractor = function(_) { return arguments.length ? (vExtractor = _, hm) : vExtractor; }; /** - * Gets / sets the objectStrokeWidth - * (see {@link violin#objectStrokeWidth}) + * Gets / sets whether or not heatmap is allowed to go beyond specified dimensions + * (see {@link heatmap#spaceX}) + * @param {boolean} [_=none] + * @returns {heatmap | boolean} + * @memberof heatmap + * @property + * by default overflowQ = false + */ + hm.overflowQ = function(_) { return arguments.length ? (overflowQ = _, hm) : overflowQ; }; + /** + * Gets / sets objectSpacer + * (see {@link heatmap#objectSpacer}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default objectStrokeWidth = 2 + * by default objectSpacer = 0.0 */ - violin.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, violin) : objectStrokeWidth; }; - - + hm.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, objectSpacer) : data; }; /** - * Gets / sets the colorFunction - * (see {@link violin#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {violin | colorFunction} - * @memberof violin + * Gets / sets the minObjectSize + * (see {@link heatmap#minObjectSize}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default colorFunction = colorFunction() + * by default minObjectSize = 50 */ - violin.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, violin) : colorFunction$$1; }; + hm.yMinObjectSize = function(_) { return arguments.length ? (yMinObjectSize = _, hm) : yMinObjectSize; }; /** - * Gets / sets the colorFunction - * (see {@link violin#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {violin | colorFunction} - * @memberof violin + * Gets / sets the maxObjectSize + * (see {@link heatmap#maxObjectSize}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default colorFunction = colorFunction() + * by default maxObjectSize = 100 */ - violin.pointColorFunc = function(_) { return arguments.length ? (pointColorFunc = _, violin) : pointColorFunc; }; - - + hm.yMaxObjectSize = function(_) { return arguments.length ? (yMaxObjectSize = _, hm) : yMaxObjectSize; }; /** - * Gets / sets the pointRadius - * (see {@link violin#pointRadius}) + * Gets / sets the minObjectSize + * (see {@link heatmap#minObjectSize}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default pointRadius = 2 + * by default minObjectSize = 50 */ - violin.pointRadius = function(_) { return arguments.length ? (pointRadius = _, violin) : pointRadius; }; + hm.xMinObjectSize = function(_) { return arguments.length ? (xMinObjectSize = _, hm) : xMinObjectSize; }; /** - * Gets / sets the pointStrokeWidth - * (see {@link violin#pointStrokeWidth}) + * Gets / sets the maxObjectSize + * (see {@link heatmap#maxObjectSize}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default pointStrokeWidth = 2 + * by default maxObjectSize = 100 */ - violin.pointStrokeWidth = function(_) { return arguments.length ? (pointStrokeWidth = _, violin) : pointStrokeWidth; }; - - + hm.xMaxObjectSize = function(_) { return arguments.length ? (xMaxObjectSize = _, hm) : xMaxObjectSize; }; + /** + * Gets / sets the objectStrokeWidth + * (see {@link heatmap#objectStrokeWidth}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap + * @property + * by default objectStrokeWidth = 2 + */ + hm.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, hm) : objectStrokeWidth; }; /** * Gets / sets the backgroundFill - * (see {@link violin#backgroundFill}) + * (see {@link heatmap#backgroundFill}) * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin + * @returns {heatmap | string} + * @memberof heatmap * @property * by default backgroundFill = 'transparent' */ - violin.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, violin) : backgroundFill; }; + hm.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, hm) : backgroundFill; }; /** * Gets / sets the namespace - * (see {@link violin#namespace}) + * (see {@link heatmap#namespace}) * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default namespace = 'd3sm-violin' + * by default namespace = 'd3sm-heatmap' */ - violin.namespace = function(_) { return arguments.length ? (namespace = _, violin) : namespace; }; + hm.namespace = function(_) { return arguments.length ? (namespace = _, hm) : namespace; }; /** * Gets / sets the objectClass - * (see {@link violin#objectClass}) + * (see {@link heatmap#objectClass}) * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin + * @returns {heatmap | string} + * @memberof heatmap * @property * by default objectClass = 'tick-group' */ - violin.objectClass = function(_) { return arguments.length ? (objectClass = _, violin) : objectClass; }; - - + hm.objectClass = function(_) { return arguments.length ? (objectClass = _, hm) : objectClass; }; /** * Gets / sets the transitionDuration - * (see {@link violin#transitionDuration}) + * (see {@link heatmap#transitionDuration}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property * by default transitionDuration = 1000 */ - violin.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, violin) : transitionDuration; }; + hm.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, hm) : transitionDuration; }; /** * Gets / sets the easeFunc - * (see {@link violin#easeFunc}) + * (see {@link heatmap#easeFunc}) * @param {d3.ease} [_=none] - * @returns {violin | d3.ease} - * @memberof violin + * @returns {heatmap | d3.ease} + * @memberof heatmap * @property * by default easeFunc = d3.easeExp */ - violin.easeFunc = function(_) { return arguments.length ? (easeFunc = _, violin) : easeFunc; }; - + hm.easeFunc = function(_) { return arguments.length ? (easeFunc = _, hm) : easeFunc; }; /** - * Gets / sets the quartileKey - * (see {@link violin#quartileKey}) - * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin + * Gets / sets the tooltip + * (see {@link heatmap#tooltip}) + * @param {tooltip} [_=none] + * @returns {heatmap | tooltip} + * @memberof heatmap * @property - * by default quartileKey = "utils.math.quartiles" + * by default tooltip = tooltip() */ - violin.quartileKey = function(_) { return arguments.length ? (quartileKey = _, violin) : quartileKey; }; + hm.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, hm) : tooltip$$1; }; + /** - * Gets / sets the quartileKeys - * (see {@link violin#quartileKeys}) - * @param {string[]} [_=none] - * @returns {violin | string[]} - * @memberof violin + * Gets / sets the colorFunction + * (see {@link heatmap#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {heatmap | colorFunction} + * @memberof heatmap * @property - * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] + * by default colorFunction = colorFunction() */ - violin.quartileKeys = function(_) { return arguments.length ? (quartileKeys = _, violin) : quartileKeys; }; - + hm.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, hm) : colorFunction$$1; }; /** - * Gets / sets the violinKeys - * (see {@link violin#violinKeys}) - * @param {string[]} [_=none] - * @returns {violin | string[]} - * @memberof violin + * Gets / sets the xSize + * (see {@link heatmap#xSize}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default violinKeys = undefined + * by default xSize = undefined */ - violin.violinKeys = function(_) { return arguments.length ? (violinKeys = _, violin) : violinKeys; }; + hm.xSize = function(_) { return arguments.length ? (xSize = _, hm) : xSize; }; /** - * Gets / sets the violinValues - * (see {@link violin#violinValues}) - * @param {Object[]} [_=none] - * @returns {violin | Object[]} - * @memberof violin + * Gets / sets the xSpacerSize + * (see {@link heatmap#xSpacerSize}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default violinValues = undefined + * by default xSpacerSize = undefined */ - violin.violinValues = function(_) { return arguments.length ? (violinValues = _, violin) : violinValues; }; - + hm.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, hm) : xSpacerSize; }; /** - * Gets / sets the objectSize - * (see {@link violin#objectSize}) + * Gets / sets the ySize + * (see {@link heatmap#ySize}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default objectSize = undefined + * by default ySize = undefined */ - violin.objectSize = function(_) { return arguments.length ? (objectSize = _, violin) : objectSize; }; + hm.ySize = function(_) { return arguments.length ? (ySize = _, hm) : ySize; }; /** - * Gets / sets the spacerSize - * (see {@link violin#spacerSize}) + * Gets / sets the ySpacerSize + * (see {@link heatmap#ySpacerSize}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default spacerSize = undefined + * by default ySpacerSize = undefined */ - violin.spacerSize = function(_) { return arguments.length ? (spacerSize = _, violin) : spacerSize; }; - /** - * Gets / sets the tooltip - * (see {@link violin#tooltip}) - * @param {tooltip} [_=none] - * @returns {violin | tooltip} - * @memberof violin - * @property - * by default tooltip = tooltip() - */ - violin.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, violin) : tooltip$$1; }; - /** - * Gets / sets the pointsTooltip - * (see {@link violin#pointsTooltip}) - * @param {tooltip} [_=none] - * @returns {violin | tooltip} - * @memberof violin - * @property - * by default pointsTooltip = tooltip() - */ - violin.pointsTooltip = function(_) { return arguments.length ? (pointsTooltip = _, violin) : pointsTooltip; }; - - - + hm.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, hm) : ySpacerSize; }; + // hm.yKeySortingFunction = function(_) { return arguments.length ? (yKeySortingFunction = _, hm) : yKeySortingFunction; } + // hm.xKeySortingFunction = function(_) { return arguments.length ? (xKeySortingFunction = _, hm) : xKeySortingFunction; } - function violin () { - // for convenience in handling orientation specific values - var horizontalQ = (orient == 'horizontal') ? true : false; - // background cliping rectangle + function hm() { var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - // if grouping is undefined sort violinKeys by sortingFunction - var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping; - - // console.log(ordered) - - violinKeys = utils.arr.flatten(ordered); - + cellKeys = d3.keys(data); - var calcValues = neededViolinValues() - .horizontalQ(horizontalQ) - .quartileKeys(quartileKeys) - .violinPointsExtractor(violinPointsExtractor) - .violinPointValueExtractor(violinPointValueExtractor); + xValues = utils.arr.unique(cellKeys.map(xExtractor)); + yValues = utils.arr.unique(cellKeys.map(yExtractor)); + vValues = utils.arr.unique(cellKeys.map(vExtractor)); + cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }); + utils.con.log('heatmap', 'cells are sorted by', cellKeys); - // augment valus - violinKeys.map(function(vk, i){ calcValues(vk, data); }); - var numberOfObjects = violinKeys.length; + utils.con.log('heatmap', 'x and y keys are', {x: xValues, y:yValues}); - var min = [].concat(...violinKeys.map(function(k, i){return data[k].utils.math.quartiles[quartileKeys[0]]})); - var max = [].concat(...violinKeys.map(function(k, i){return data[k].utils.math.quartiles[quartileKeys[quartileKeys.length - 1]]})); - var extent = [Math.min(...min) - domainPadding, Math.max(...max) + domainPadding]; - // console.log(extent, violinValues, ordered) - // set the scale - scale.domain(extent).range(horizontalQ ? [0,spaceY] : [0, spaceX]); - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - // calculate spacer size if needed - spacerSize = utils.math.calculateWidthOfSpacer(ordered, space, objectSize, numberOfObjects, objectSpacer, overflowQ); - // make the nested groups - var spacerFunction = groupingSpacer() - .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) - .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc) - .namespace(namespace); + var xDim = xValues.length, yDim = yValues.length; - // move stuff - spacerFunction(container, ordered, 0); - // console.log(violinKeys, ordered, container.selectAll('g:not(.to-remove).'+objectClass).nodes()) - // for color function - var parentIndexArray = []; - container.selectAll('g:not(.to-remove).'+objectClass) - .each(function(d, i){if (utils.arr.hasQ(violinKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}}); + ySize = utils.math.calculateWidthOfObject(spaceY, yDim, yMinObjectSize, yMaxObjectSize, objectSpacer, overflowQ); + xSize = utils.math.calculateWidthOfObject(spaceX, xDim, xMinObjectSize, xMaxObjectSize, objectSpacer, overflowQ); + ySpacerSize = utils.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); + xSpacerSize = utils.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); + // console.table({ + // x:{ + // object: xSize, + // spacer: xSpacerSize, + // dim: xDim + // }, + // y:{ + // object: ySize, + // spacer: ySpacerSize, + // dim: yDim + // } + // + // }) + utils.con.log('heatmap', 'size of', {x: xSize, y: ySize}); - // update color function - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' - ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) - : colorFunction$$1.dataExtent(extent); - /* violiin specific needs */ + var ySpacer = groupingSpacer() + .horizontalQ(false) + .moveby('category') + .numberOfObjects(yDim) + .objectClass(utils.str.hypenate(objectClass, 'row')) + .objectSize(ySize + ySpacerSize) + .spacerSize(0) + .transitionDuration(transitionDuration) + .easeFunc(easeFunc) + .namespace('row'); - var frequencyMax = Math.max(...[].concat(...violinKeys.map(function(k, i){return d3.max(data[k].frequencies)}))); - var vScale = d3.scaleLinear().domain([0, frequencyMax]).range([0, objectSize / 2]); + var xSpacer = groupingSpacer() + .horizontalQ(true) + .moveby('category') + .numberOfObjects(xDim) + .objectClass(objectClass) + .objectSize(xSize + xSpacerSize) + .spacerSize(0) + .transitionDuration(transitionDuration) + .easeFunc(easeFunc); - var lArea = d3.line() - .x(function(d, i){ return horizontalQ ? -vScale(d.x) : scale(d.x)}) - .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : -vScale(d.y)}) - .curve(d3.curveBasis); - var rArea = d3.line() - .x(function(d, i){ return horizontalQ ? vScale(d.x) : scale(d.x)}) - .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : vScale(d.y)}) - .curve(d3.curveBasis); + ySpacer(container, yValues, 0); + container.selectAll('g.'+utils.str.hypenate(objectClass, 'row')) + .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); + var cells = container.selectAll('g:not(.to-remove).'+objectClass); + if (cellKeys.length != yValues.length * xValues.length) { + var lookup = {}; + cellKeys.map(function(k, i){ + lookup[xExtractor(k)+'::'+yExtractor(k)] = k; + }); + var positionedCellKeys = []; + for (var i = 0; i < yValues.length; i++) { + for (var j = 0; j < xValues.length; j++) { + var lookupValue = lookup[xValues[j]+"::"+yValues[i]]; + if (lookupValue == undefined) { + positionedCellKeys.push(undefined); + } else { + positionedCellKeys.push(lookupValue); + } + } + } - container.selectAll('g:not(.to-remove).'+objectClass).each(function(key, i){ - var t = d3.select(this), - currentData = data[key]; - // needed because bug in selecting .to-remove - if (!utils.arr.hasQ(violinKeys, key)) {return} - var - i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, currentData, i, 'fill'), // prevent duplicate computation - strokeColor = colorFunction$$1(key, currentData, i, 'stroke'), - area = utils.sel.safeSelect(t, 'g', 'area'), - la = utils.sel.safeSelect(area, 'path', 'left'), - ra = utils.sel.safeSelect(area, 'path', 'right'), - quarts = utils.sel.safeSelect(t, 'g', 'quarts'), - lq3 = utils.sel.safeSelect(quarts, 'line', 'q3'), - lq1 = utils.sel.safeSelect(quarts, 'line', 'q1'), - q3 = currentData.utils.math.quartiles[quartileKeys[3]], - q2 = currentData.utils.math.quartiles[quartileKeys[2]], - q1 = currentData.utils.math.quartiles[quartileKeys[1]]; + cells.data(positionedCellKeys); - t.attr('transform', horizontalQ ? 'translate('+objectSize / 2+',0)' : 'translate(0,'+objectSize / 2+')' ); - // draw curve - la.transition().duration(transitionDuration).attr('d', function(dd, ii){ return lArea(currentData.contour)}) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', objectStrokeWidth); + // maybe breaks this + // !!!!!! IMPORTANT NOTE TODO LOOK HERE BUG + cellKeys = positionedCellKeys; + } else { + cells.data(cellKeys); + } - ra.transition().duration(transitionDuration).attr('d', function(dd, ii){ return rArea(currentData.contour)}) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', objectStrokeWidth); - area.node().addEventListener('mouseover', function(dd, ii){ - container.selectAll('g.'+objectClass).style('opacity', 0.2); - t.style('opacity', 1); - la.attr('stroke-width',objectStrokeWidth*2); - ra.attr('stroke-width',objectStrokeWidth*2); - }); - area.node().addEventListener('mouseout', function(dd, ii){ - container.selectAll('g.'+objectClass).style('opacity', 1); - la.attr('stroke-width',objectStrokeWidth); - ra.attr('stroke-width',objectStrokeWidth); - }); - if (pointsQ) { - var ptsContainer = utils.sel.safeSelect(t, 'g', 'points'); - var pts = ptsContainer.selectAll('.point').data(currentData.pointKeys); - pts.on('mouseover', null); + var parentIndexArray = []; + cells.each(function(d, i){ parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); }); + colorFunction$$1 = colorFunction$$1.colorBy() == 'index' + ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) + : colorFunction$$1.dataExtent([0, Math.max(...vValues)]); - var ptsExit = pts.exit().transition().ease(easeFunc).duration(transitionDuration) - .attr('r', 0) - .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) - .attr('cx', horizontalQ ? vScale(0) : scale(q2)).remove(); + cells.each(function(key, i) { + utils.con.log('heatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); - var ptsEnter = pts.enter().append('circle').attr('class', 'point').attr('r', 0) - .attr('cx', horizontalQ ? 0 : scale(q2)) - .attr('cy', horizontalQ ? scale(q2) : 0); + var t = d3.select(this); + if (key == undefined) {return} + var currentData = data[key], + value = vExtractor(key, i), + i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), + fillColor = colorFunction$$1(key, value, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(key, value, i, 'stroke'); - pts = pts.merge(ptsEnter); + var c = utils.sel.safeSelect(t, 'rect', utils.str.hypenate(objectClass,'rect')); + c.attr('width', xSize + xSpacerSize - objectStrokeWidth) + .attr('height', ySize + ySpacerSize - objectStrokeWidth) + .attr('fill', fillColor) + .attr('x', objectStrokeWidth/2) + .attr('y', objectStrokeWidth/2) + .attr('stroke', "#000") + .attr('stroke-width', objectStrokeWidth); - // console.log(pointsTooltip.header()) + }); - var pTTips = tooltip().selection(pts).data(violinPointsExtractor(key, currentData)) - .header(pointsTooltip.header()) - .keys(pointsTooltip.keys()) - .values(pointsTooltip.values()); + tooltip$$1.selection(cells.selectAll('rect.'+utils.str.hypenate(objectClass, 'rect'))) + .data(data); + // .keys(['r', 'v']) + // .header(function(d, i){return utils.str.hypenate(data[d][xKey], data[d][yKey]) }) - pTTips(); - - var pMin = d3.min(currentData.pointValues), pMax = d3.max(currentData.pointValues); - - - - pts.transition().duration(transitionDuration).ease(easeFunc).attr('r', pointRadius) - .attr('cy', function(pointKey, ii){ - var dd = currentData.pointValues[ii]; - if (horizontalQ) { return scale(extent[1]) - scale(dd) } - var j = utils.arr.whichBin(currentData.binned, dd); - var r = Math.random(); - var n = vScale(r * currentData.frequencies[j] * 0.5); - var k = Math.random() > 0.5 ? n : -n; - return k - }) - .attr('cx', function(pointKey, ii){ - var dd = currentData.pointValues[ii]; - if (horizontalQ) { - var j = utils.arr.whichBin(currentData.binned, dd); - var r = Math.random(); - var n = vScale(r * currentData.frequencies[j] * 0.5); - var k = Math.random() > 0.5 ? n : -n; - return k - } - return scale(dd) - }) - .attr('stroke', function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'stroke', strokeColor, pMin, pMax) }) - .attr('fill' , function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'fill' , strokeColor, pMin, pMax) }) - .attr('stroke-width', pointStrokeWidth); - - ptsContainer.selectAll('circle.point').on('mouseover', function(dd, ii){ - container.selectAll('g.'+objectClass).style('opacity', 0.2); - t.style('opacity', 1); - la.attr('stroke-width',objectStrokeWidth*2); - ra.attr('stroke-width',objectStrokeWidth*2); - - container.selectAll('.point').style('opacity', 0.2); - d3.select(this).style('opacity', 1).attr('r', pointRadius * 2).attr('stroke-width',pointStrokeWidth*2); - }); - ptsContainer.selectAll('circle.point').on('mouseout', function(dd, ii){ - var e = document.createEvent('SVGEvents'); - e.initEvent('mouseout',true,true); - area.node().dispatchEvent(e); - - container.selectAll('.point').style('opacity', 1); - d3.select(this).attr('stroke-width', pointStrokeWidth).attr('r', pointRadius); - }); - } - else { - cV.selectAll('.point') - .transition().duration(transitionDuration).ease(easeFunc) - .attr('r', 0) - .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) - .attr('cx', horizontalQ ? vScale(0) : scale(q2)) - .remove(); - } - - - }); - - - tooltip$$1.selection(container.selectAll('g:not(.to-remove).'+objectClass + ' .area')); - if (tooltip$$1.data() == undefined) {tooltip$$1.data(data);} - tooltip$$1(); - if (tooltip$$1.values() == undefined) { - tooltip$$1.values([ - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] } - ]); - - } - - } - - return violin -} + tooltip$$1(); + } + return hm; +} +/******************************************************************************* +** ** +** ** +** VIOLIN ** +** ** +** ** +*******************************************************************************/ /** -* Produces the function which manipulates the violin data to have values needed -* for rendering the violins as svg. -* @returns {function} calculateViolinValues -* @namespace neededViolinValues -*/ -function neededViolinValues() { - var + * Creates a violin + * + * {@link https://sumneuron.gitlab.io/d3sm/demos/basic-violins/index.html Demo} + * @constructor violin + * @param {d3.selection} selection + * @namespace violin + * @returns {function} violin + */ +function violin( selection ) { + var /** - * Whether or not the orientation of the violins are horizontal + * Data to plot. Assumed to be a object, where each key corresponds to a violin + * (see {@link violin#data}) + * @param {Object} [data=undefined] + * @memberof violin# + * @property + */ + data, + /** + * Which direction to render the bars in * (see {@link violin#orient}) - * @param {Object} [horizontalQ=true] - * @memberof neededViolinValues# + * @param {number} [orient='horizontal'] + * @memberof violin# * @property */ - horizontalQ = true, + orient='horizontal', /** - * Keys to be put into the utils.math.quartiles if they need to be calculated. - * (see {@link violin#quartileKeys}) - * @param {Object} [quartileKeys=['Q0', 'Q1', 'Q2', 'Q3', 'Q4']] - * @memberof neededViolinValues# + * Amount of horizontal space (in pixels) avaible to render the violin in + * (see {@link violin#spaceX}) + * @param {number} [spaceX=undefined] + * @memberof violin# * @property */ - quartileKeys = ['Q0', 'Q1', 'Q2', 'Q3', 'Q4'], + spaceX, /** - * Function which given the key of the violin and that key's associated value - * returns the object consiting of the points of the violin - * (see {@link violin#violinPointsExtractor}) - * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] - * @memberof neededViolinValues# + * Amount of vertical space (in pixels) avaible to render the violin in + * (see {@link violin.spaceY}) + * @param {number} [spaceY=undefined] + * @memberof violin# * @property */ - violinPointsExtractor, + spaceY, /** - * Function which given the key of the current point and the object of points for the - * violin, returns the numerical value of the point - * (see {@link violin#violinPointValueExtractor}) - * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] - * @memberof neededViolinValues# + * Whether or not to allow violin to render elements pass the main spatial dimension + * given the orientation (see {@link violin#orient}), where {@link violin#orient}="horizontal" + * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" + * the main dimension is {@link violin#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof violin# * @property */ - violinPointValueExtractor; - - + overflowQ = true, /** - * Gets / sets the horizontalQ - * (see {@link violin#orient}) - * @param {boolean} [_=none] - * @returns {calculateViolinValues | boolean} - * @memberof calculateViolinValues - * @property - * - * by default horizontalQ = true - */ - calculateViolinValues.horizontalQ = function(_) { return arguments.length ? (horizontalQ=_, calculateViolinValues) : horizontalQ }; + * Whether or not to display points inside the points + * @param {boolean} [pointsQ=false] + * @memberof violin# + * @property + */ + pointsQ = true, /** - * Gets / sets the quartileKeys - * (see {@link violin#quartileKeys}) - * @param {string[]} [_=none] - * @returns {calculateViolinValues | string[]} - * @memberof calculateViolinValues - * @property - * - * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] - */ - calculateViolinValues.quartileKeys = function(_) { return arguments.length ? (quartileKeys=_, calculateViolinValues) : quartileKeys }; + * An array - putatively of other arrays - depicting how bars should be arranged + * @param {Array[]} [grouping=undefined] + * @memberof violin# + * @property + */ + grouping, /** - * Gets / sets the violinPointsExtractor - * (see {@link violin#violinPointsExtractor}) - * @param {function} [_=none] - * @returns {calculateViolinValues | function} - * @memberof calculateViolinValues - * @property - * - * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } - */ - calculateViolinValues.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor=_, calculateViolinValues) : violinPointsExtractor }; + * How to get the value of the violin + * @param {function} [valueExtractor=function(key, index) { return data[key] }] + * @memberof violin# + * @property + */ + valueExtractor = function(key, index) {return data[key] }, /** - * Gets / sets the violinPointValueExtractor - * (see {@link violin#violinPointValueExtractor}) - * @param {function} [_=none] - * @returns {calculateViolinValues | function} - * @memberof calculateViolinValues - * @property - * - * by default violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value } - */ - calculateViolinValues.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor=_, calculateViolinValues) : violinPointValueExtractor }; - + * How to sort the bars - if {@link violin#grouping} is not provided. + * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] + * @memberof violin# + * @property + */ + sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, /** - * The function produced by neededViolinValues. - * - * Adds the data need to render the violin as an svg - * @param {string} violinKey the key of the current violin - * @param {object} data the object consisting of violinKey - values (violin data) pairs - * @returns {none} this function manipulates the passed data object adding the following keys - * - * data[violinKey].binned // the binned values of the points - * - * data[violinKey].frequencies // the list consisting of the length of each bin - * - * data[violinKey].contour // the points depicting the contour of 1/2 of the violin - * - * data[violinKey].utils.math.quartiles // the utils.math.quartiles of the points' values - * - * data[violinKey].pointKeys // the keys associated with the points - * - * data[violinKey].pointValues // the numerical values of the points - * - * @memberof neededViolinValues# + * The scale for which violin values should be transformed by + * @param {d3.scale} [scale=d3.scaleLinear] + * @memberof violin# * @property */ - function calculateViolinValues(violinKey, data) { - // data for the current violin - var violinData = data[violinKey]; - // the object of points - var violinPoints = violinPointsExtractor(violinKey, violinData); - // - var violinPointsKeys = d3.keys(violinPoints); - // the numerical values of those points - var violinPointsValues = violinPointsKeys.map(function(pk, i){return violinPointValueExtractor(pk, violinPoints)}); - - // utils.math.quartiles of those points - var pointQuartiles = utils.math.quartiles(violinPointsValues, quartileKeys); + scale = d3.scaleLinear(), + /** + * The padding for the domain of the scale (see {@link violin#scale}) + * @param {number} [domainPadding=0.5] + * @memberof violin# + * @property + */ + domainPadding = 0.5, + /** + * Default space for the spacer (percentage) of main dimension given the orientation + * (see {@link violin#orient}), where {@link violin#orient}="horizontal" + * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" + * the main dimension is {@link violin#spaceY} between bars + * @param {number} [objectSpacer=0.05] + * @memberof violin# + * @property + */ + objectSpacer = 0.05, + /** + * The minimum size that an object can be + * @param {number} [minObjectSize=50] + * @memberof violin# + * @property + */ + minObjectSize = 50, + /** + * The maximum size that an object can be + * @param {number} [maxObjectSize=100] + * @memberof violin# + * @property + */ + maxObjectSize = 100, - // binned points - var binned = d3.histogram()(violinPointsValues); - // length of bins - var frequencies = binned.map(bin=>bin.length); - // min and max countour points for nice drawings - var minContourPoint = horizontalQ ? {x: 0, y: d3.min(violinPointsValues)} : {x: d3.min(violinPointsValues), y: 0}; - var maxContourPoint = horizontalQ ? {x: 0, y: d3.max(violinPointsValues)} : {x: d3.max(violinPointsValues), y: 0}; - var violinContourPoints = binned.map(function(bin, i) { - return horizontalQ - ? {y: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), x: frequencies[i]} - : {x: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), y: frequencies[i]} - }); - // points along which to draw the violin shpe - violinContourPoints = [minContourPoint].concat(violinContourPoints).concat([maxContourPoint]); - - // set data - violinData.binned = binned; - violinData.frequencies = frequencies; - violinData.contour = violinContourPoints; - violinData.utils.math.quartiles = pointQuartiles; - violinData.pointKeys = violinPointsKeys; - violinData.pointValues = violinPointsValues; - } + /** + * The stroke width of the bars + * @param {number} [barStrokeWidth=2] + * @memberof violin# + * @property + */ + objectStrokeWidth = 2, + /** + * Instance of ColorFunction + * @param {function} [colorFunction = colorFunction()] + * @memberof violin# + * @property + */ + colorFunction$$1 = colorFunction(), + /** + * Instance of ColorFunction modified by a scale for the points + * @param {function} [pointColorFunc = colorFunction()] + * @memberof violin# + * @property + */ + pointColorFunc = function (d, type, base, min, max) { + var minMaxHexScale = d3.scaleLinear().domain([min, max]).range([-0.25, 0.05]); + var scaledColor = utils.color.modifyHexidecimalColorLuminance(base.replace('#', ''), minMaxHexScale(d)); + var mod = type == "stroke" ? 0 : 0.25; + return utils.color.modifyHexidecimalColorLuminance(scaledColor.replace('#', ''), mod) + }, - return calculateViolinValues -} + /** + * The radius of a point + * @param {number} [pointRadius=3] + * @memberof violin# + * @property + */ + pointRadius = 3, + /** + * The stroke width of the oints + * @param {number} [pointStrokeWidth=2] + * @memberof violin# + * @property + */ + pointStrokeWidth = 2, -function upset ( selection ) { - var - data, - orient='horizontal', - spaceX, - spaceY, - overflowQ=false, - minObjectSize=20, - maxObjectSize=50, - circleStrokeWidth=2, - // colorFunction= + /** + * Color of the background + * @param {string} [backgroundFill="transparent"] + * @memberof violin# + * @property + */ backgroundFill = 'transparent', - namespace='d3sm-upset', - objectClass = 'upset', - + /** + * Namespace for all items made by this instance of violin + * @param {string} [namespace="d3sm-violin"] + * @memberof violin# + * @property + */ + namespace = 'd3sm-violin', + /** + * Class name for violin container ( element) + * @param {string} [objectClass="violin"] + * @memberof violin# + * @property + */ + objectClass = 'violin', + /** + * Duration of all transitions of this element + * @param {number} [transitionDuration=1000] + * @memberof violin# + * @property + */ transitionDuration = 1000, + /** + * Easing function for transitions + * @param {d3.ease} [easeFunc=d3.easeExp] + * @memberof violin# + * @property + */ easeFunc = d3.easeExp, - setKey = "set", - intersectionKey = "intersection", - elementsKey = "elements", - - setExtractor = function(key, i) {return data[key][setKey]}, - intersectionExtractor = function(key, i) {return data[key][intersectionKey]}, - elementExtractor = function(key, i) {return data[key][elementsKey]}, - - cellKeys, - setValues, - intersectionValues, - xObjectSpacer = 0.05, - yObjectSpacer = 0.05, - radius, - - // listDelim = ';' - - yObjectSize, - ySpacerSize, - xObjectSize, - xSpacerSize, - - setKeySortingFunction = function(a, b) { return setValues.indexOf(setExtractor(a)) - setValues.indexOf(setExtractor(b)) }, - intersectionKeySortingFunction = function(a, b) { return intersectionValues.indexOf(intersectionExtractor(a)) - intersectionValues.indexOf(intersectionExtractor(b)) }; - - - upset.selection = function(_) { return arguments.length ? (selection = _, upset) : selection; }; - upset.data = function(_) { return arguments.length ? (data = _, upset) : data; }; - upset.orient = function(_) { return arguments.length ? (orient = _, upset) : orient; }; - upset.spaceX = function(_) { return arguments.length ? (spaceX = _, upset) : spaceX; }; - upset.spaceY = function(_) { return arguments.length ? (spaceY = _, upset) : spaceY; }; - upset.overflowQ = function(_) { return arguments.length ? (overflowQ = _, upset) : overflowQ; }; - upset.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, upset) : minObjectSize; }; - upset.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, upset) : maxObjectSize; }; - upset.circleStrokeWidth = function(_) { return arguments.length ? (circleStrokeWidth = _, upset) : circleStrokeWidth; }; - upset.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, upset) : backgroundFill; }; - upset.namespace = function(_) { return arguments.length ? (namespace = _, upset) : namespace; }; - upset.objectClass = function(_) { return arguments.length ? (objectClass = _, upset) : objectClass; }; - upset.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, upset) : transitionDuration; }; - upset.easeFunc = function(_) { return arguments.length ? (easeFunc = _, upset) : easeFunc; }; - upset.cellKeys = function(_) { return arguments.length ? (cellKeys = _, upset) : cellKeys; }; - upset.setValues = function(_) { return arguments.length ? (setValues = _, upset) : setValues; }; - upset.intersectionValues = function(_) { return arguments.length ? (intersectionValues = _, upset) : intersectionValues; }; - upset.xObjectSpacer = function(_) { return arguments.length ? (xObjectSpacer = _, upset) : xObjectSpacer; }; - upset.yObjectSpacer = function(_) { return arguments.length ? (yObjectSpacer = _, upset) : yObjectSpacer; }; - upset.radius = function(_) { return arguments.length ? (radius = _, upset) : radius; }; - upset.setExtractor = function(_) { return arguments.length ? (setExtractor = _, upset) : setExtractor; }; - upset.intersectionExtractor = function(_) { return arguments.length ? (intersectionExtractor = _, upset) : intersectionExtractor; }; - upset.elementExtractor = function(_) { return arguments.length ? (elementExtractor = _, upset) : elementExtractor; }; - upset.setKeySortingFunction = function(_) { return arguments.length ? (setKeySortingFunction = _, upset) : setKeySortingFunction; }; - upset.intersectionKeySortingFunction = function(_) { return arguments.length ? (intersectionKeySortingFunction = _, upset) : intersectionKeySortingFunction; }; - - upset.yObjectSize = function(_) { return arguments.length ? (yObjectSize = _, upset) : yObjectSize; }; - upset.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, upset) : ySpacerSize; }; - upset.xObjectSize = function(_) { return arguments.length ? (xObjectSize = _, upset) : xObjectSize; }; - upset.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, upset) : xSpacerSize; }; - - function upset() { - // for convenience in handling orientation specific values - var horizontalQ = (orient == 'horizontal') ? true : false; - var verticalQ = !horizontalQ; + /** + * The keys corresponding to each quartile + * @param {string[]} [quartileKeys=["Q0", "Q1", "Q2", "Q3", "Q4"]] + * @memberof violin# + * @property + */ + quartileKeys = ["Q0", "Q1", "Q2", "Q3", "Q4"], - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + /** + * The keys of the bars + * @param {string[]} [violinKeys=undefined] + * @memberof violin# + * @property + */ + violinKeys, + /** + * The values of the bars + * @param {number[]} [violinValues=undefined] + * @memberof violin# + * @property + */ + violinValues, + /** + * The objectSize (actual width) used by the bars + * @param {number} [objectSize=undefined] + * @memberof violin# + * @property + */ + objectSize, + /** + * The spacerSize (actual width) used by the spacers between the bars + * @param {number} [spacerSize=undefined] + * @memberof violin# + * @property + */ + spacerSize, + /** + * Instance of Tooltip + * @param {function} [tooltip=tooltip()] + * @memberof violin# + * @property + */ + tooltip$$1 = tooltip().keys([quartileKeys[4], quartileKeys[3], quartileKeys[2], quartileKeys[1], quartileKeys[0]]), + pointsTooltip = tooltip(), + // pointKeyExtractor = function(violinKey, violinData, violinValues) {return d3.keys(violinValues[violinKey].values)}, + // pointValueExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]}, - cellKeys = d3.keys(data); - setValues = utils$1.arr.unique(cellKeys.map(setExtractor)).sort(); - intersectionValues = utils$1.arr.unique(cellKeys.map(intersectionExtractor)).sort().sort(function(a, b){ - return a.split(';').length - b.split(';').length - }); - if (!horizontalQ) { - cellKeys.sort(function(a, b){ return setKeySortingFunction(a, b) || intersectionKeySortingFunction(a, b) }); - } else { - cellKeys.sort(function(a, b){ return intersectionKeySortingFunction(a, b) || setKeySortingFunction(a, b) }); - } + /** + * Function which given the key of the violin and that key's associated value + * returns the object consiting of the points of the violin + * (see {@link violin#violinPointsExtractor}) + * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] + * @memberof violin# + * @property + */ + violinPointsExtractor = function (violinKey, violinData) {return violinData.points }, + /** + * Function which given the key of the current point and the object of points for the + * violin, returns the numerical value of the point + * (see {@link violin#violinPointValueExtractor}) + * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] + * @memberof violin# + * @property + */ + violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }; + /** + * Gets or sets the violinPointsExtractor + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin + * @property + * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } + */ + violin.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor = _, violin) : violinPointsExtractor; }; + /** + * Gets or sets the violinPointValueExtractor + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin + * @property + * by default violinPointsExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]} + */ + violin.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor = _, violin) : violinPointValueExtractor; }; - var - xValues = horizontalQ ? intersectionValues : setValues, - yValues = horizontalQ ? setValues : intersectionValues, - xDim = horizontalQ ? xValues.length : yValues.length, - yDim = horizontalQ ? yValues.length : xValues.length; - - // console.utils.con.log(xValues, yValues) - - - xObjectSize = utils$1.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, xObjectSpacer, overflowQ); - yObjectSize = utils$1.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, yObjectSpacer, overflowQ); - xSpacerSize = utils$1.math.calculateWidthOfSpacer(xValues, spaceX, xObjectSize, xDim, xObjectSpacer, overflowQ); - ySpacerSize = utils$1.math.calculateWidthOfSpacer(yValues, spaceY, yObjectSize, yDim, yObjectSpacer, overflowQ); - - var ySpacer = groupingSpacer() - .horizontalQ(false) - .moveby('category').numberOfObjects(yDim) - .objectSize(yObjectSize).spacerSize(ySpacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc); + /** + * Gets or sets the selection in which items are manipulated + * @param {d3.selection} [_=none] + * @returns {violin | d3.selection} + * @memberof violin + * @property + * by default selection = selection + */ + violin.selection = function(_) { return arguments.length ? (selection = _, violin) : selection; }; + /** + * Gets or sets the data + * (see {@link violin#data}) + * @param {number} [_=none] + * @returns {violin | object} + * @memberof violin + * @property + */ + violin.data = function(_) { return arguments.length ? (data = _, violin) : data; }; + /** + * Gets or sets the orient of the boxes + * (see {@link violin#orient}) + * @param {number} [_=none] + * @returns {violin | object} + * @memberof violin + * @property + */ + violin.orient = function(_) { return arguments.length ? (orient = _, violin) : orient; }; + /** + * Gets or sets the amount of horizontal space in which items are manipulated + * (see {@link violin#spaceX}) + * @param {number} [_=none] should be a number > 0 + * @returns {violin | number} + * @memberof violin + * @property + * by default spaceX = undefined + */ + violin.spaceX = function(_) { return arguments.length ? (spaceX = _, violin) : spaceX; }; + /** + * Gets or sets the amount of vertical space in which items are manipulated + * (see {@link violin#spaceY}) + * @param {number} [_=none] should be a number > 0 + * @returns {violin | number} + * @memberof violin + * @property + * by default spaceY = undefined + */ + violin.spaceY = function(_) { return arguments.length ? (spaceY = _, violin) : spaceY; }; - var xSpacer = groupingSpacer() - .horizontalQ(true) - .moveby('category').numberOfObjects(xDim) - .objectClass(objectClass) - .objectSize(xObjectSize).spacerSize(xSpacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc); + /** + * Gets / sets whether or not violin is allowed to go beyond specified dimensions + * (see {@link violin#overflowQ}) + * @param {boolean} [_=none] + * @returns {violin | boolean} + * @memberof violin + * @property + * by default overflowQ = false + */ + violin.overflowQ = function(_) { return arguments.length ? (overflowQ = _, violin) : overflowQ; }; + /** + * Gets / sets whether or not to plot points with the violins + * (see {@link violin#pointsQ}) + * @param {boolean} [_=none] + * @returns {violin | boolean} + * @memberof violin + * @property + * by default pointsQ = false + */ + violin.pointsQ = function(_) { return arguments.length ? (pointsQ = _, violin) : pointsQ; }; - if (verticalQ) { - xSpacer.objectClass(objectClass); - ySpacer.namespace('across').objectClass(utils$1.str.hypenate(objectClass, 'across')); + /** + * Gets / sets the grouping of the boxes + * (see {@link violin#grouping}) + * @param {Array[]} [_=none] + * @returns {violin | Array[]} + * @memberof violin + * @property + * by default grouping = undefined + */ + violin.grouping = function(_) { return arguments.length ? (grouping = _, violin) : grouping; }; + /** + * Gets / sets the valueExtractor + * (see {@link violin#valueExtractor}) + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin + * @property + */ + violin.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, violin) : valueExtractor; }; + /** + * Gets / sets the sortingFunction + * (see {@link violin#sortingFunction}) + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin + * @property + */ + violin.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, violin) : sortingFunction; }; - ySpacer(container, yValues, 0); - container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'across')) - .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); - } else { - xSpacer.namespace('across').objectClass(utils$1.str.hypenate(objectClass, 'across')); - ySpacer.objectClass(objectClass); + /** + * Gets / sets the scale for which the violin values should be transformed by + * (see {@link violin#scale}) + * @param {d3.scale} [_=none] + * @returns {violin | d3.scale} + * @memberof violin + * @property + * by default scale = d3.scaleLinear() + */ + violin.scale = function(_) { return arguments.length ? (scale = _, violin) : scale; }; + /** + * Gets / sets the padding for the domain of the scale + * (see {@link violin#domainPadding}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default domainPadding = 0.5 + */ + violin.domainPadding = function(_) { return arguments.length ? (domainPadding = _, violin) : domainPadding; }; - xSpacer(container, xValues, 0); - container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'across')) - .each(function(d, i){ ySpacer(d3.select(this), yValues, 0); }); - } + /** + * Gets / sets objectSpacer + * (see {@link violin#objectSpacer}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default objectSpacer = 0.05 + */ + violin.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, violin) : objectSpacer; }; + /** + * Gets / sets the minObjectSize + * (see {@link violin#minObjectSize}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default minObjectSize = 15 + */ + violin.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, violin) : minObjectSize; }; + /** + * Gets / sets the maxObjectSize + * (see {@link violin#maxObjectSize}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default maxObjectSize = 50 + */ + violin.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, violin) : maxObjectSize; }; - var cells = container.selectAll('g:not(.to-remove).'+objectClass); - var lookup = {}; - cellKeys.map(function(k, i){ - lookup[setExtractor(k)+'::'+intersectionExtractor(k)] = k; - }); + /** + * Gets / sets the objectStrokeWidth + * (see {@link violin#objectStrokeWidth}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default objectStrokeWidth = 2 + */ + violin.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, violin) : objectStrokeWidth; }; - // var positionedCellKeys = [] - // for (var i = 0; i < setValues.length; i++) { - // for (var j = 0; j < intersectionValues.length; j++) { - // var lookupValue = lookup[setValues[j]+"::"+intersectionValues[i]] - // if (lookupValue == undefined) { - // positionedCellKeys.push(undefined) - // } else { - // positionedCellKeys.push(lookupValue) - // } - // console.utils.con.log(i, j, lookupValue) - // } - // } - // - // // console.utils.con.log(positionedCellKeys) - // - // cells.data(positionedCellKeys); + /** + * Gets / sets the colorFunction + * (see {@link violin#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {violin | colorFunction} + * @memberof violin + * @property + * by default colorFunction = colorFunction() + */ + violin.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, violin) : colorFunction$$1; }; + /** + * Gets / sets the colorFunction + * (see {@link violin#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {violin | colorFunction} + * @memberof violin + * @property + * by default colorFunction = colorFunction() + */ + violin.pointColorFunc = function(_) { return arguments.length ? (pointColorFunc = _, violin) : pointColorFunc; }; - cells.data(cellKeys); - cells.each(function(key, i) { - var t = d3.select(this); - if (key == undefined) {return } - var - currentData = data[key]; - // console.utils.con.log(key, currentData) - var - set = setExtractor(key, i), - intersection = intersectionExtractor(key, i); + /** + * Gets / sets the pointRadius + * (see {@link violin#pointRadius}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default pointRadius = 2 + */ + violin.pointRadius = function(_) { return arguments.length ? (pointRadius = _, violin) : pointRadius; }; + /** + * Gets / sets the pointStrokeWidth + * (see {@link violin#pointStrokeWidth}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default pointStrokeWidth = 2 + */ + violin.pointStrokeWidth = function(_) { return arguments.length ? (pointStrokeWidth = _, violin) : pointStrokeWidth; }; - // console.utils.con.log(set, intersection) - t.classed(intersection, true); - t.classed(set, true); + /** + * Gets / sets the backgroundFill + * (see {@link violin#backgroundFill}) + * @param {string} [_=none] + * @returns {violin | string} + * @memberof violin + * @property + * by default backgroundFill = 'transparent' + */ + violin.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, violin) : backgroundFill; }; + /** + * Gets / sets the namespace + * (see {@link violin#namespace}) + * @param {string} [_=none] + * @returns {violin | string} + * @memberof violin + * @property + * by default namespace = 'd3sm-violin' + */ + violin.namespace = function(_) { return arguments.length ? (namespace = _, violin) : namespace; }; + /** + * Gets / sets the objectClass + * (see {@link violin#objectClass}) + * @param {string} [_=none] + * @returns {violin | string} + * @memberof violin + * @property + * by default objectClass = 'tick-group' + */ + violin.objectClass = function(_) { return arguments.length ? (objectClass = _, violin) : objectClass; }; - var c = utils$1.sel.safeSelect(t, 'circle', utils$1.str.hypenate(objectClass,'circle')); - c.attr('cx', xObjectSize / 2) - .attr('cy', yObjectSize / 2 ) - .attr('r', radius == undefined ? Math.min(xObjectSize, yObjectSize) / 2 : radius) - .attr('fill', intersection.includes(set) ? "black": 'rgb(233,233,233)') - .attr('stroke', "black") - .attr("in-intersection", intersection.includes(set)); - }); + /** + * Gets / sets the transitionDuration + * (see {@link violin#transitionDuration}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default transitionDuration = 1000 + */ + violin.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, violin) : transitionDuration; }; + /** + * Gets / sets the easeFunc + * (see {@link violin#easeFunc}) + * @param {d3.ease} [_=none] + * @returns {violin | d3.ease} + * @memberof violin + * @property + * by default easeFunc = d3.easeExp + */ + violin.easeFunc = function(_) { return arguments.length ? (easeFunc = _, violin) : easeFunc; }; - } + /** + * Gets / sets the quartileKey + * (see {@link violin#quartileKey}) + * @param {string} [_=none] + * @returns {violin | string} + * @memberof violin + * @property + * by default quartileKey = "utils.math.quartiles" + */ + violin.quartileKey = function(_) { return arguments.length ? (quartileKey = _, violin) : quartileKey; }; + /** + * Gets / sets the quartileKeys + * (see {@link violin#quartileKeys}) + * @param {string[]} [_=none] + * @returns {violin | string[]} + * @memberof violin + * @property + * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] + */ + violin.quartileKeys = function(_) { return arguments.length ? (quartileKeys = _, violin) : quartileKeys; }; - function intersectionTotals() { - var totals = {}; - // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) - intersectionValues.map(function(k, i){ totals[k] = {'total': 0}; }); - cellKeys.map(function(k, i){ - var e = elementExtractor(k, i); - if (totals[intersectionExtractor(k, i)]['total'] == 0) { - if (Array.isArray(e)) { - totals[intersectionExtractor(k, i)]['total']+= e.length; - totals[intersectionExtractor(k, i)]['values'] = e; - } else { - totals[intersectionExtractor(k, i)]['total']+= e; - } + /** + * Gets / sets the violinKeys + * (see {@link violin#violinKeys}) + * @param {string[]} [_=none] + * @returns {violin | string[]} + * @memberof violin + * @property + * by default violinKeys = undefined + */ + violin.violinKeys = function(_) { return arguments.length ? (violinKeys = _, violin) : violinKeys; }; + /** + * Gets / sets the violinValues + * (see {@link violin#violinValues}) + * @param {Object[]} [_=none] + * @returns {violin | Object[]} + * @memberof violin + * @property + * by default violinValues = undefined + */ + violin.violinValues = function(_) { return arguments.length ? (violinValues = _, violin) : violinValues; }; - } - }); - return totals - } + /** + * Gets / sets the objectSize + * (see {@link violin#objectSize}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default objectSize = undefined + */ + violin.objectSize = function(_) { return arguments.length ? (objectSize = _, violin) : objectSize; }; + /** + * Gets / sets the spacerSize + * (see {@link violin#spacerSize}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default spacerSize = undefined + */ + violin.spacerSize = function(_) { return arguments.length ? (spacerSize = _, violin) : spacerSize; }; + /** + * Gets / sets the tooltip + * (see {@link violin#tooltip}) + * @param {tooltip} [_=none] + * @returns {violin | tooltip} + * @memberof violin + * @property + * by default tooltip = tooltip() + */ + violin.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, violin) : tooltip$$1; }; + /** + * Gets / sets the pointsTooltip + * (see {@link violin#pointsTooltip}) + * @param {tooltip} [_=none] + * @returns {violin | tooltip} + * @memberof violin + * @property + * by default pointsTooltip = tooltip() + */ + violin.pointsTooltip = function(_) { return arguments.length ? (pointsTooltip = _, violin) : pointsTooltip; }; - function setTotals(){ - var totals = {}; - // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) - setValues.map(function(k, i){ totals[k] = {'total': 0}; }); - cellKeys.map(function(k, i){ - var e = elementExtractor(k, i); - if (Array.isArray(e)) { - totals[setExtractor(k, i)]['total']+= e.length; - } else { - totals[setExtractor(k, i)]['total']+= e; - } - }); - return totals - } - upset.intersectionTotals = intersectionTotals; - upset.setTotals = setTotals; - return upset -} + function violin () { + // for convenience in handling orientation specific values + var horizontalQ = (orient == 'horizontal') ? true : false; -let charts = { - scatter, bar, bubble: bubbleHeatmap, heatmap, violin, neededViolinValues, upset -}; + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); -function categoricLegend( selection ) { - var - categories, + // if grouping is undefined sort violinKeys by sortingFunction + var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping; - /** - * Data to plot. Assumed to be a object, where each key corresponds to a legend - * (see {@link legend#data}) - * @param {Object} [data=undefined] - * @memberof legend# - * @property - */ - data, - /** - * Which direction to render the bars in - * (see {@link legend#orient}) - * @param {number} [orient='horizontal'] - * @memberof legend# - * @property - */ - orient='horizontal', - /** - * Amount of horizontal space (in pixels) avaible to render the legend in - * (see {@link legend#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof legend# - * @property - */ - spaceX, - /** - * Amount of vertical space (in pixels) avaible to render the legend in - * (see {@link legend.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof legend# - * @property - */ - spaceY, + // console.log(ordered) - /** - * Whether or not to allow legend to render elements pass the main spatial dimension - * given the orientation (see {@link legend#orient}), where {@link legend#orient}="horizontal" - * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" - * the main dimension is {@link legend#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof legend# - * @property - */ - overflowQ = false, + violinKeys = utils.arr.flatten(ordered); - /** - * An array - putatively of other arrays - depicting how bars should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof legend# - * @property - */ - grouping, - /** - * How to get the value of the legend - * @param {function} [valueExtractor=function(key, index) { return data[key] }] - * @memberof legend# - * @property - */ - valueExtractor = function(key, index) { return data[key] }, - /** - * How to sort the bars - if {@link bar#grouping} is not provided. - * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] - * @memberof bar# - * @property - */ - sortingFunction = function(keyA, keyB) {return d3.ascending(keyA, keyB)}, - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link legend#orient}), where {@link legend#orient}="horizontal" - * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" - * the main dimension is {@link legend#spaceY} between bars - * @param {number} [objectSpacer=0.05] - * @memberof legend# + var calcValues = neededViolinValues() + .horizontalQ(horizontalQ) + .quartileKeys(quartileKeys) + .violinPointsExtractor(violinPointsExtractor) + .violinPointValueExtractor(violinPointValueExtractor); + + + + // augment valus + violinValues = {}; + violinKeys.map(function(vk, i){ + let v = calcValues(vk, data); + violinValues[vk] = v; + }); + + var numberOfObjects = violinKeys.length; + + var min = [].concat(...violinKeys.map(function(k, i){return data[k].quartiles[quartileKeys[0]]})); + var max = [].concat(...violinKeys.map(function(k, i){return data[k].quartiles[quartileKeys[quartileKeys.length - 1]]})); + var extent = [Math.min(...min) - domainPadding, Math.max(...max) + domainPadding]; + // console.log(extent, violinValues, ordered) + + // set the scale + scale.domain(extent).range(horizontalQ ? [0,spaceY] : [0, spaceX]); + var space = horizontalQ ? spaceX : spaceY; + // calculate object size + objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + // calculate spacer size if needed + spacerSize = utils.math.calculateWidthOfSpacer(ordered, space, objectSize, numberOfObjects, objectSpacer, overflowQ); + // make the nested groups + var spacerFunction = groupingSpacer() + .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) + .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc) + .namespace(namespace); + + // move stuff + spacerFunction(container, ordered, 0); + // console.log(violinKeys, ordered, container.selectAll('g:not(.to-remove).'+objectClass).nodes()) + + // for color function + var parentIndexArray = []; + container.selectAll('g:not(.to-remove).'+objectClass) + .each(function(d, i){if (utils.arr.hasQ(violinKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}}); + + // update color function + colorFunction$$1 = colorFunction$$1.colorBy() == 'index' + ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) + : colorFunction$$1.dataExtent(extent); + + /* violiin specific needs */ + + + var frequencyMax = Math.max(...[].concat(...violinKeys.map(function(k, i){return d3.max(data[k].frequencies)}))); + var vScale = d3.scaleLinear().domain([0, frequencyMax]).range([0, objectSize / 2]); + + var lArea = d3.line() + .x(function(d, i){ return horizontalQ ? -vScale(d.x) : scale(d.x)}) + .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : -vScale(d.y)}) + .curve(d3.curveBasis); + var rArea = d3.line() + .x(function(d, i){ return horizontalQ ? vScale(d.x) : scale(d.x)}) + .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : vScale(d.y)}) + .curve(d3.curveBasis); + + + + + + + container.selectAll('g:not(.to-remove).'+objectClass).each(function(key, i){ + var t = d3.select(this), + currentData = data[key]; + // needed because bug in selecting .to-remove + if (!utils.arr.hasQ(violinKeys, key)) {return} + var + i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), + fillColor = colorFunction$$1(key, currentData, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(key, currentData, i, 'stroke'), + area = utils.sel.safeSelect(t, 'g', 'area'), + la = utils.sel.safeSelect(area, 'path', 'left'), + ra = utils.sel.safeSelect(area, 'path', 'right'), + quarts = utils.sel.safeSelect(t, 'g', 'quarts'), + lq3 = utils.sel.safeSelect(quarts, 'line', 'q3'), + lq1 = utils.sel.safeSelect(quarts, 'line', 'q1'), + q3 = currentData.quartiles[quartileKeys[3]], + q2 = currentData.quartiles[quartileKeys[2]], + q1 = currentData.quartiles[quartileKeys[1]]; + + t.attr('transform', horizontalQ ? 'translate('+objectSize / 2+',0)' : 'translate(0,'+objectSize / 2+')' ); + // draw curve + la.transition().duration(transitionDuration).attr('d', function(dd, ii){ return lArea(currentData.contour)}) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', objectStrokeWidth); + + ra.transition().duration(transitionDuration).attr('d', function(dd, ii){ return rArea(currentData.contour)}) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', objectStrokeWidth); + + area.node().addEventListener('mouseover', function(dd, ii){ + container.selectAll('g.'+objectClass).style('opacity', 0.2); + t.style('opacity', 1); + la.attr('stroke-width',objectStrokeWidth*2); + ra.attr('stroke-width',objectStrokeWidth*2); + }); + area.node().addEventListener('mouseout', function(dd, ii){ + container.selectAll('g.'+objectClass).style('opacity', 1); + la.attr('stroke-width',objectStrokeWidth); + ra.attr('stroke-width',objectStrokeWidth); + }); + + if (pointsQ) { + var ptsContainer = utils.sel.safeSelect(t, 'g', 'points'); + var pts = ptsContainer.selectAll('.point').data(currentData.pointKeys); + pts.on('mouseover', null); + + + var ptsExit = pts.exit().transition().ease(easeFunc).duration(transitionDuration) + .attr('r', 0) + .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) + .attr('cx', horizontalQ ? vScale(0) : scale(q2)).remove(); + + var ptsEnter = pts.enter().append('circle').attr('class', 'point').attr('r', 0) + .attr('cx', horizontalQ ? 0 : scale(q2)) + .attr('cy', horizontalQ ? scale(q2) : 0); + + pts = pts.merge(ptsEnter); + + // console.log(pointsTooltip.header()) + + var pTTips = tooltip().selection(pts).data(violinPointsExtractor(key, currentData)) + .header(pointsTooltip.header()) + .keys(pointsTooltip.keys()) + .values(pointsTooltip.values()); + + pTTips(); + + var pMin = d3.min(currentData.pointValues), pMax = d3.max(currentData.pointValues); + + + + pts.transition().duration(transitionDuration).ease(easeFunc).attr('r', pointRadius) + .attr('cy', function(pointKey, ii){ + var dd = currentData.pointValues[ii]; + if (horizontalQ) { return scale(extent[1]) - scale(dd) } + var j = utils.arr.whichBin(currentData.binned, dd); + var r = Math.random(); + var n = vScale(r * currentData.frequencies[j] * 0.5); + var k = Math.random() > 0.5 ? n : -n; + return k + }) + .attr('cx', function(pointKey, ii){ + var dd = currentData.pointValues[ii]; + if (horizontalQ) { + var j = utils.arr.whichBin(currentData.binned, dd); + var r = Math.random(); + var n = vScale(r * currentData.frequencies[j] * 0.5); + var k = Math.random() > 0.5 ? n : -n; + return k + } + return scale(dd) + }) + .attr('stroke', function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'stroke', strokeColor, pMin, pMax) }) + .attr('fill' , function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'fill' , strokeColor, pMin, pMax) }) + .attr('stroke-width', pointStrokeWidth); + + ptsContainer.selectAll('circle.point').on('mouseover', function(dd, ii){ + container.selectAll('g.'+objectClass).style('opacity', 0.2); + t.style('opacity', 1); + la.attr('stroke-width',objectStrokeWidth*2); + ra.attr('stroke-width',objectStrokeWidth*2); + + container.selectAll('.point').style('opacity', 0.2); + d3.select(this).style('opacity', 1).attr('r', pointRadius * 2).attr('stroke-width',pointStrokeWidth*2); + }); + ptsContainer.selectAll('circle.point').on('mouseout', function(dd, ii){ + var e = document.createEvent('SVGEvents'); + e.initEvent('mouseout',true,true); + area.node().dispatchEvent(e); + + container.selectAll('.point').style('opacity', 1); + d3.select(this).attr('stroke-width', pointStrokeWidth).attr('r', pointRadius); + }); + } + else { + cV.selectAll('.point') + .transition().duration(transitionDuration).ease(easeFunc) + .attr('r', 0) + .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) + .attr('cx', horizontalQ ? vScale(0) : scale(q2)) + .remove(); + } + + + }); + + + tooltip$$1.selection(container.selectAll('g:not(.to-remove).'+objectClass + ' .area')); + if (tooltip$$1.data() == undefined) {tooltip$$1.data(data);} + tooltip$$1(); + if (tooltip$$1.values() == undefined) { + tooltip$$1.values([ + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] } + ]); + + } + + } + + return violin +} + + + + + +/** +* Produces the function which manipulates the violin data to have values needed +* for rendering the violins as svg. +* @returns {function} calculateViolinValues +* @namespace neededViolinValues +*/ +function neededViolinValues() { + var + /** + * Whether or not the orientation of the violins are horizontal + * (see {@link violin#orient}) + * @param {Object} [horizontalQ=true] + * @memberof neededViolinValues# * @property */ - objectSpacer = 0.05, + horizontalQ = true, /** - * The minimum size that an object can be - * @param {number} [minObjectSize=50] - * @memberof legend# + * Keys to be put into the utils.math.quartiles if they need to be calculated. + * (see {@link violin#quartileKeys}) + * @param {Object} [quartileKeys=['Q0', 'Q1', 'Q2', 'Q3', 'Q4']] + * @memberof neededViolinValues# * @property */ - minObjectSize = 10, + quartileKeys = ['Q0', 'Q1', 'Q2', 'Q3', 'Q4'], /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof legend# + * Function which given the key of the violin and that key's associated value + * returns the object consiting of the points of the violin + * (see {@link violin#violinPointsExtractor}) + * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] + * @memberof neededViolinValues# * @property */ - maxObjectSize = 100, - + violinPointsExtractor, /** - * The stroke width of the bars - * @param {number} [barStrokeWidth=2] - * @memberof legend# + * Function which given the key of the current point and the object of points for the + * violin, returns the numerical value of the point + * (see {@link violin#violinPointValueExtractor}) + * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] + * @memberof neededViolinValues# * @property */ - bubbleStrokeWidth = 2, - /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof legend# - * @property - */ - colorFunction$$1 = colorFunction(), + violinPointValueExtractor; + /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof legend# - * @property - */ - backgroundFill = 'transparent', + * Gets / sets the horizontalQ + * (see {@link violin#orient}) + * @param {boolean} [_=none] + * @returns {calculateViolinValues | boolean} + * @memberof calculateViolinValues + * @property + * + * by default horizontalQ = true + */ + calculateViolinValues.horizontalQ = function(_) { return arguments.length ? (horizontalQ=_, calculateViolinValues) : horizontalQ }; /** - * Namespace for all items made by this instance of legend - * @param {string} [namespace="d3sm-legend"] - * @memberof legend# - * @property - */ - namespace = 'd3sm-legend', + * Gets / sets the quartileKeys + * (see {@link violin#quartileKeys}) + * @param {string[]} [_=none] + * @returns {calculateViolinValues | string[]} + * @memberof calculateViolinValues + * @property + * + * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] + */ + calculateViolinValues.quartileKeys = function(_) { return arguments.length ? (quartileKeys=_, calculateViolinValues) : quartileKeys }; /** - * Class name for legend container ( element) - * @param {string} [objectClass="legend"] - * @memberof legend# + * Gets / sets the violinPointsExtractor + * (see {@link violin#violinPointsExtractor}) + * @param {function} [_=none] + * @returns {calculateViolinValues | function} + * @memberof calculateViolinValues + * @property + * + * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } + */ + calculateViolinValues.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor=_, calculateViolinValues) : violinPointsExtractor }; + /** + * Gets / sets the violinPointValueExtractor + * (see {@link violin#violinPointValueExtractor}) + * @param {function} [_=none] + * @returns {calculateViolinValues | function} + * @memberof calculateViolinValues + * @property + * + * by default violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value } + */ + calculateViolinValues.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor=_, calculateViolinValues) : violinPointValueExtractor }; + + + /** + * The function produced by neededViolinValues. + * + * Adds the data need to render the violin as an svg + * @param {string} violinKey the key of the current violin + * @param {object} data the object consisting of violinKey - values (violin data) pairs + * @returns {none} this function manipulates the passed data object adding the following keys + * + * data[violinKey].binned // the binned values of the points + * + * data[violinKey].frequencies // the list consisting of the length of each bin + * + * data[violinKey].contour // the points depicting the contour of 1/2 of the violin + * + * data[violinKey].utils.math.quartiles // the utils.math.quartiles of the points' values + * + * data[violinKey].pointKeys // the keys associated with the points + * + * data[violinKey].pointValues // the numerical values of the points + * + * @memberof neededViolinValues# * @property */ - objectClass = 'legend', + function calculateViolinValues(violinKey, data) { + // data for the current violin + var violinData = data[violinKey]; + // the object of points + var violinPoints = violinPointsExtractor(violinKey, violinData); + // + var violinPointsKeys = d3.keys(violinPoints); + // the numerical values of those points + var violinPointsValues = violinPointsKeys.map(function(pk, i){return violinPointValueExtractor(pk, violinPoints)}); + + // utils.math.quartiles of those points + var pointQuartiles = utils.math.quartiles(violinPointsValues, quartileKeys); + + // binned points + var binned = d3.histogram()(violinPointsValues); + // length of bins + var frequencies = binned.map(bin=>bin.length); + // min and max countour points for nice drawings + var minContourPoint = horizontalQ ? {x: 0, y: d3.min(violinPointsValues)} : {x: d3.min(violinPointsValues), y: 0}; + var maxContourPoint = horizontalQ ? {x: 0, y: d3.max(violinPointsValues)} : {x: d3.max(violinPointsValues), y: 0}; + var violinContourPoints = binned.map(function(bin, i) { + return horizontalQ + ? {y: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), x: frequencies[i]} + : {x: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), y: frequencies[i]} + }); + // points along which to draw the violin shpe + violinContourPoints = [minContourPoint].concat(violinContourPoints).concat([maxContourPoint]); + + // set data + violinData.binned = binned; + violinData.frequencies = frequencies; + violinData.contour = violinContourPoints; + violinData.quartiles = pointQuartiles; + violinData.pointKeys = violinPointsKeys; + violinData.pointValues = violinPointsValues; + return violinData + } + + return calculateViolinValues +} + +function upset ( selection ) { + var + data, + orient='horizontal', + spaceX, + spaceY, + overflowQ=false, + minObjectSize=20, + maxObjectSize=50, + circleStrokeWidth=2, + // colorFunction= + backgroundFill = 'transparent', + namespace='d3sm-upset', + objectClass = 'upset', + + transitionDuration = 1000, + easeFunc = d3.easeExp, + + setKey = "set", + intersectionKey = "intersection", + elementsKey = "elements", + + setExtractor = function(key, i) {return data[key][setKey]}, + intersectionExtractor = function(key, i) {return data[key][intersectionKey]}, + elementExtractor = function(key, i) {return data[key][elementsKey]}, + + cellKeys, + setValues, + intersectionValues, + xObjectSpacer = 0.05, + yObjectSpacer = 0.05, + radius, + + // listDelim = ';' + + yObjectSize, + ySpacerSize, + xObjectSize, + xSpacerSize, + + setKeySortingFunction = function(a, b) { return setValues.indexOf(setExtractor(a)) - setValues.indexOf(setExtractor(b)) }, + intersectionKeySortingFunction = function(a, b) { return intersectionValues.indexOf(intersectionExtractor(a)) - intersectionValues.indexOf(intersectionExtractor(b)) }; + + + upset.selection = function(_) { return arguments.length ? (selection = _, upset) : selection; }; + upset.data = function(_) { return arguments.length ? (data = _, upset) : data; }; + upset.orient = function(_) { return arguments.length ? (orient = _, upset) : orient; }; + upset.spaceX = function(_) { return arguments.length ? (spaceX = _, upset) : spaceX; }; + upset.spaceY = function(_) { return arguments.length ? (spaceY = _, upset) : spaceY; }; + upset.overflowQ = function(_) { return arguments.length ? (overflowQ = _, upset) : overflowQ; }; + upset.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, upset) : minObjectSize; }; + upset.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, upset) : maxObjectSize; }; + upset.circleStrokeWidth = function(_) { return arguments.length ? (circleStrokeWidth = _, upset) : circleStrokeWidth; }; + upset.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, upset) : backgroundFill; }; + upset.namespace = function(_) { return arguments.length ? (namespace = _, upset) : namespace; }; + upset.objectClass = function(_) { return arguments.length ? (objectClass = _, upset) : objectClass; }; + upset.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, upset) : transitionDuration; }; + upset.easeFunc = function(_) { return arguments.length ? (easeFunc = _, upset) : easeFunc; }; + upset.cellKeys = function(_) { return arguments.length ? (cellKeys = _, upset) : cellKeys; }; + upset.setValues = function(_) { return arguments.length ? (setValues = _, upset) : setValues; }; + upset.intersectionValues = function(_) { return arguments.length ? (intersectionValues = _, upset) : intersectionValues; }; + upset.xObjectSpacer = function(_) { return arguments.length ? (xObjectSpacer = _, upset) : xObjectSpacer; }; + upset.yObjectSpacer = function(_) { return arguments.length ? (yObjectSpacer = _, upset) : yObjectSpacer; }; + upset.radius = function(_) { return arguments.length ? (radius = _, upset) : radius; }; + upset.setExtractor = function(_) { return arguments.length ? (setExtractor = _, upset) : setExtractor; }; + upset.intersectionExtractor = function(_) { return arguments.length ? (intersectionExtractor = _, upset) : intersectionExtractor; }; + upset.elementExtractor = function(_) { return arguments.length ? (elementExtractor = _, upset) : elementExtractor; }; + upset.setKeySortingFunction = function(_) { return arguments.length ? (setKeySortingFunction = _, upset) : setKeySortingFunction; }; + upset.intersectionKeySortingFunction = function(_) { return arguments.length ? (intersectionKeySortingFunction = _, upset) : intersectionKeySortingFunction; }; + + upset.yObjectSize = function(_) { return arguments.length ? (yObjectSize = _, upset) : yObjectSize; }; + upset.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, upset) : ySpacerSize; }; + upset.xObjectSize = function(_) { return arguments.length ? (xObjectSize = _, upset) : xObjectSize; }; + upset.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, upset) : xSpacerSize; }; + + function upset() { + // for convenience in handling orientation specific values + var horizontalQ = (orient == 'horizontal') ? true : false; + var verticalQ = !horizontalQ; + + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + + + cellKeys = d3.keys(data); + setValues = utils.arr.unique(cellKeys.map(setExtractor)).sort(); + intersectionValues = utils.arr.unique(cellKeys.map(intersectionExtractor)).sort().sort(function(a, b){ + return a.split(';').length - b.split(';').length + }); + + if (!horizontalQ) { + cellKeys.sort(function(a, b){ return setKeySortingFunction(a, b) || intersectionKeySortingFunction(a, b) }); + } else { + cellKeys.sort(function(a, b){ return intersectionKeySortingFunction(a, b) || setKeySortingFunction(a, b) }); + } + + + + + var + xValues = horizontalQ ? intersectionValues : setValues, + yValues = horizontalQ ? setValues : intersectionValues, + xDim = horizontalQ ? xValues.length : yValues.length, + yDim = horizontalQ ? yValues.length : xValues.length; + + // console.utils.con.log(xValues, yValues) + + + xObjectSize = utils.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, xObjectSpacer, overflowQ); + yObjectSize = utils.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, yObjectSpacer, overflowQ); + xSpacerSize = utils.math.calculateWidthOfSpacer(xValues, spaceX, xObjectSize, xDim, xObjectSpacer, overflowQ); + ySpacerSize = utils.math.calculateWidthOfSpacer(yValues, spaceY, yObjectSize, yDim, yObjectSpacer, overflowQ); + + var ySpacer = groupingSpacer() + .horizontalQ(false) + .moveby('category').numberOfObjects(yDim) + .objectSize(yObjectSize).spacerSize(ySpacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc); + + var xSpacer = groupingSpacer() + .horizontalQ(true) + .moveby('category').numberOfObjects(xDim) + .objectClass(objectClass) + .objectSize(xObjectSize).spacerSize(xSpacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc); + + + + if (verticalQ) { + xSpacer.objectClass(objectClass); + ySpacer.namespace('across').objectClass(utils.str.hypenate(objectClass, 'across')); + + ySpacer(container, yValues, 0); + container.selectAll('g.'+utils.str.hypenate(objectClass, 'across')) + .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); + } else { + xSpacer.namespace('across').objectClass(utils.str.hypenate(objectClass, 'across')); + ySpacer.objectClass(objectClass); + + xSpacer(container, xValues, 0); + container.selectAll('g.'+utils.str.hypenate(objectClass, 'across')) + .each(function(d, i){ ySpacer(d3.select(this), yValues, 0); }); + } + + + var cells = container.selectAll('g:not(.to-remove).'+objectClass); + var lookup = {}; + cellKeys.map(function(k, i){ + lookup[setExtractor(k)+'::'+intersectionExtractor(k)] = k; + }); + + // var positionedCellKeys = [] + // for (var i = 0; i < setValues.length; i++) { + // for (var j = 0; j < intersectionValues.length; j++) { + // var lookupValue = lookup[setValues[j]+"::"+intersectionValues[i]] + // if (lookupValue == undefined) { + // positionedCellKeys.push(undefined) + // } else { + // positionedCellKeys.push(lookupValue) + // } + // console.utils.con.log(i, j, lookupValue) + // } + // } + // + // // console.utils.con.log(positionedCellKeys) + // + // cells.data(positionedCellKeys); + + + cells.data(cellKeys); + + cells.each(function(key, i) { + var t = d3.select(this); + if (key == undefined) {return } + var + currentData = data[key]; + // console.utils.con.log(key, currentData) + var + set = setExtractor(key, i), + intersection = intersectionExtractor(key, i); + + // console.utils.con.log(set, intersection) + + t.classed(intersection, true); + t.classed(set, true); + + var c = utils.sel.safeSelect(t, 'circle', utils.str.hypenate(objectClass,'circle')); + c.attr('cx', xObjectSize / 2) + .attr('cy', yObjectSize / 2 ) + .attr('r', radius == undefined ? Math.min(xObjectSize, yObjectSize) / 2 : radius) + .attr('fill', intersection.includes(set) ? "black": 'rgb(233,233,233)') + .attr('stroke', "black") + .attr("in-intersection", intersection.includes(set)); + }); + + + + } + + function intersectionTotals() { + var totals = {}; + // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) + + intersectionValues.map(function(k, i){ totals[k] = {'total': 0}; }); + cellKeys.map(function(k, i){ + var e = elementExtractor(k, i); + if (totals[intersectionExtractor(k, i)]['total'] == 0) { + if (Array.isArray(e)) { + totals[intersectionExtractor(k, i)]['total']+= e.length; + totals[intersectionExtractor(k, i)]['values'] = e; + } else { + totals[intersectionExtractor(k, i)]['total']+= e; + } + + } + }); + return totals + } + + function setTotals(){ + var totals = {}; + // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) + + setValues.map(function(k, i){ totals[k] = {'total': 0}; }); + + cellKeys.map(function(k, i){ + var e = elementExtractor(k, i); + if (Array.isArray(e)) { + totals[setExtractor(k, i)]['total']+= e.length; + } else { + totals[setExtractor(k, i)]['total']+= e; + } + }); + return totals + } + + upset.intersectionTotals = intersectionTotals; + upset.setTotals = setTotals; + + return upset +} + +let charts = { + scatter, bar, bubble: bubbleHeatmap, boxwhisker, heatmap, violin, neededViolinValues, upset +}; + +function categoricLegend( selection ) { + var + categories, + + /** + * Data to plot. Assumed to be a object, where each key corresponds to a legend + * (see {@link legend#data}) + * @param {Object} [data=undefined] + * @memberof legend# + * @property + */ + data, + /** + * Which direction to render the bars in + * (see {@link legend#orient}) + * @param {number} [orient='horizontal'] + * @memberof legend# + * @property + */ + orient='horizontal', + /** + * Amount of horizontal space (in pixels) avaible to render the legend in + * (see {@link legend#spaceX}) + * @param {number} [spaceX=undefined] + * @memberof legend# + * @property + */ + spaceX, + /** + * Amount of vertical space (in pixels) avaible to render the legend in + * (see {@link legend.spaceY}) + * @param {number} [spaceY=undefined] + * @memberof legend# + * @property + */ + spaceY, + + /** + * Whether or not to allow legend to render elements pass the main spatial dimension + * given the orientation (see {@link legend#orient}), where {@link legend#orient}="horizontal" + * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" + * the main dimension is {@link legend#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof legend# + * @property + */ + overflowQ = false, + + /** + * An array - putatively of other arrays - depicting how bars should be arranged + * @param {Array[]} [grouping=undefined] + * @memberof legend# + * @property + */ + grouping, + + /** + * How to get the value of the legend + * @param {function} [valueExtractor=function(key, index) { return data[key] }] + * @memberof legend# + * @property + */ + valueExtractor = function(key, index) { return data[key] }, + /** + * How to sort the bars - if {@link bar#grouping} is not provided. + * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] + * @memberof bar# + * @property + */ + sortingFunction = function(keyA, keyB) {return d3.ascending(keyA, keyB)}, + /** + * Default space for the spacer (percentage) of main dimension given the orientation + * (see {@link legend#orient}), where {@link legend#orient}="horizontal" + * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" + * the main dimension is {@link legend#spaceY} between bars + * @param {number} [objectSpacer=0.05] + * @memberof legend# + * @property + */ + objectSpacer = 0.05, + /** + * The minimum size that an object can be + * @param {number} [minObjectSize=50] + * @memberof legend# + * @property + */ + minObjectSize = 10, + /** + * The maximum size that an object can be + * @param {number} [maxObjectSize=100] + * @memberof legend# + * @property + */ + maxObjectSize = 100, + + /** + * The stroke width of the bars + * @param {number} [barStrokeWidth=2] + * @memberof legend# + * @property + */ + bubbleStrokeWidth = 2, + /** + * Instance of ColorFunction + * @param {function} [colorFunction = colorFunction()] + * @memberof legend# + * @property + */ + colorFunction$$1 = colorFunction(), + + /** + * Color of the background + * @param {string} [backgroundFill="transparent"] + * @memberof legend# + * @property + */ + backgroundFill = 'transparent', + /** + * Namespace for all items made by this instance of legend + * @param {string} [namespace="d3sm-legend"] + * @memberof legend# + * @property + */ + namespace = 'd3sm-legend', + /** + * Class name for legend container ( element) + * @param {string} [objectClass="legend"] + * @memberof legend# + * @property + */ + objectClass = 'legend', + + /** + * Duration of all transitions of this element + * @param {number} [transitionDuration=1000] + * @memberof legend# + * @property + */ + transitionDuration = 1000, + /** + * Easing function for transitions + * @param {d3.ease} [easeFunc=d3.easeExp] + * @memberof legend# + * @property + */ + easeFunc = d3.easeExp; + + + legend.categories = function(_) { return arguments.length ? (categories=_, legend) : categories }; + + /** + * Gets or sets the selection in which items are manipulated + * @param {d3.selection} [_=none] + * @returns {legend | d3.selection} + * @memberof legend + * @property + * by default selection = selection + */ + legend.selection = function(_) { return arguments.length ? (selection = _, legend) : selection; }; + /** + * Gets or sets the data + * (see {@link legend#data}) + * @param {number} [_=none] + * @returns {legend | object} + * @memberof legend + * @property + */ + legend.data = function(_) { return arguments.length ? (data = _, legend) : data; }; + /** + * Gets or sets the orient of the bars + * (see {@link legend#orient}) + * @param {number} [_=none] + * @returns {legend | object} + * @memberof legend + * @property + */ + legend.orient = function(_) { return arguments.length ? (orient = _, legend) : orient; }; + /** + * Gets or sets the amount of horizontal space in which items are manipulated + * (see {@link legend#spaceX}) + * @param {number} [_=none] should be a number > 0 + * @returns {legend | number} + * @memberof legend + * @property + * by default spaceX = undefined + */ + legend.spaceX = function(_) { return arguments.length ? (spaceX = _, legend) : spaceX; }; + /** + * Gets or sets the amount of vertical space in which items are manipulated + * (see {@link legend#spaceY}) + * @param {number} [_=none] should be a number > 0 + * @returns {legend | number} + * @memberof legend + * @property + * by default spaceY = undefined + */ + legend.spaceY = function(_) { return arguments.length ? (spaceY = _, legend) : spaceY; }; + + /** + * Gets / sets whether or not legend is allowed to go beyond specified dimensions + * (see {@link legend#spaceX}) + * @param {boolean} [_=none] + * @returns {legend | boolean} + * @memberof legend + * @property + * by default overflowQ = false + */ + legend.overflowQ = function(_) { return arguments.length ? (overflowQ = _, legend) : overflowQ; }; + /** + * Gets / sets the grouping of the bars + * (see {@link legend#grouping}) + * @param {Array[]} [_=none] + * @returns {legend | Array[]} + * @memberof legend + * @property + * by default grouping = undefined + */ + legend.grouping = function(_) { return arguments.length ? (grouping = _, legend) : grouping; }; + /** + * Gets / sets the valueExtractor + * (see {@link legend#valueExtractor}) + * @param {function} [_=none] + * @returns {legend | function} + * @memberof legend + * @property + * by default valueExtractor = function(key, index) { return data[key] }, + */ + legend.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, legend) : valueExtractor; }; + /** + * Gets / sets the sortingFunction + * (see {@link bar#sortingFunction}) + * @param {function} [_=none] + * @returns {bar | function} + * @memberof bar + * @property + * by default sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, + */ + legend.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, legend) : sortingFunction; }; + /** + * Gets / sets objectSpacer + * (see {@link legend#objectSpacer}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default objectSpacer = 0.05 + */ + legend.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, legend) : objectSpacer; }; + /** + * Gets / sets the minObjectSize + * (see {@link legend#minObjectSize}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default minObjectSize = 50 + */ + legend.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, legend) : minObjectSize; }; + /** + * Gets / sets the maxObjectSize + * (see {@link legend#maxObjectSize}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default maxObjectSize = 100 + */ + legend.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, legend) : maxObjectSize; }; + + /** + * Gets / sets the barStrokeWidth + * (see {@link legend#barStrokeWidth}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default barStrokeWidth = 2 + */ + legend.bubbleStrokeWidth = function(_) { return arguments.length ? (bubbleStrokeWidth = _, legend) : bubbleStrokeWidth; }; + /** + * Gets / sets the colorFunction + * (see {@link legend#colorFunction}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default colorFunction = colorFunction() + */ + legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, legend) : colorFunction$$1; }; + + /** + * Gets / sets the backgroundFill + * (see {@link legend#backgroundFill}) + * @param {string} [_=none] + * @returns {legend | string} + * @memberof legend + * @property + * by default backgroundFill = 'transparent' + */ + legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, legend) : backgroundFill; }; + /** + * Gets / sets the namespace + * (see {@link legend#namespace}) + * @param {string} [_=none] + * @returns {legend | string} + * @memberof legend + * @property + * by default namespace = 'd3sm-legend' + */ + legend.namespace = function(_) { return arguments.length ? (namespace = _, legend) : namespace; }; + /** + * Gets / sets the objectClass + * (see {@link legend#objectClass}) + * @param {string} [_=none] + * @returns {legend | string} + * @memberof legend + * @property + * by default objectClass = 'tick-group' + */ + legend.objectClass = function(_) { return arguments.length ? (objectClass = _, legend) : objectClass; }; + /** + * Gets / sets the transitionDuration + * (see {@link legend#transitionDuration}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default transitionDuration = 1000 + */ + legend.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, legend) : transitionDuration; }; + /** + * Gets / sets the easeFunc + * (see {@link legend#easeFunc}) + * @param {d3.ease} [_=none] + * @returns {legend | d3.ease} + * @memberof legend + * @property + * by default easeFunc = d3.easeExp + */ + legend.easeFunc = function(_) { return arguments.length ? (easeFunc = _, legend) : easeFunc; }; + + + function legend() { + var horizontalQ = (orient == 'horizontal') ? true : false; + var verticalQ = !horizontalQ; + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + + + colorFunction$$1.dataExtent([0, categories.length - 1]) + .colorBy('categories') + .categoryExtractor(function(k, v, i){return v}); + + var r = Math.min(spaceX, spaceY) / 2; + var numberOfObjects = categories.length; + + // if grouping is undefined sort barKeys by sortingFunction + var ordered = (grouping == undefined) ? categories.sort(sortingFunction) : grouping; + // ordered might be nested depending on grouping + var catKeys = utils.arr.flatten(ordered); + + var space = horizontalQ ? spaceX : spaceY; + // calculate object size + var objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + // calculate spacer size if needed + var spacerSize = utils.math.calculateWidthOfSpacer(catKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); + // make the nested groups + var spacerFunction = groupingSpacer() + .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) + .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc) + .namespace(namespace); + + spacerFunction(container, ordered, 0); + var r = Math.min(objectSize, spaceX, spaceY) / 2 - bubbleStrokeWidth; + + container.selectAll('g:not(.to-remove).'+objectClass).each(function(cat, i) { + var t = d3.select(this); + var c = utils.sel.safeSelect(t, 'circle'); + var fillColor = colorFunction$$1(undefined, cat, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(undefined, cat, i, 'stroke'); + + var cx = horizontalQ + ? r+bubbleStrokeWidth + : (spaceX - r*2) / 2 + r; + var cy = verticalQ + ? r+bubbleStrokeWidth + : (spaceX - r*2) / 2 + r; + + c.attr("r", r) + .attr('cx', cx) + .attr('cy', cy) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', bubbleStrokeWidth); + + var text = utils.sel.safeSelect(t, 'text'); + text.text(cat) + .attr('text-anchor', 'middle') + .attr("transform", function(d, i){ + var + x = cx, + y = cy + text.node().getBoundingClientRect().height / 4, + t = 'translate('+x+','+y+')'; + return t + }); + + }); + + + } + + return legend +} + +function numericLegend( selection ) { + + var + min=0, + max=1, + spaceX, + spaceY, + colorFunction$$1 = colorFunction(), + namespace='d3sm-linear-vertical-gradient', + fontSize = 12, + backgroundFill = 'transparent', + textColor = 'black', + roundTo = 2; + + + legend.min = function(_) { return arguments.length ? (min=_, legend) : min }; + legend.max = function(_) { return arguments.length ? (max=_, legend) : max }; + legend.spaceX = function(_) { return arguments.length ? (spaceX=_, legend) : spaceX }; + legend.spaceY = function(_) { return arguments.length ? (spaceY=_, legend) : spaceY }; + legend.namespace = function(_) { return arguments.length ? (namespace=_, legend) : namespace }; + legend.fontSize = function(_) { return arguments.length ? (fontSize=_, legend) : fontSize }; + legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill=_, legend) : backgroundFill }; + legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1=_, legend) : colorFunction$$1 }; + legend.textColor = function(_) { return arguments.length ? (textColor=_, legend) : textColor }; + legend.roundTo = function(_) { return arguments.length ? (roundTo=_, legend) : roundTo }; + + function legend() { + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + + var defs = utils.sel.safeSelect(selection, 'defs'); + var linearGradient = utils.sel.safeSelect(defs, 'linearGradient') + .attr("x1", "0%") + .attr("y1", "100%") + .attr("x2", "0%") + .attr("y2", "0%") + .attr('id', utils.str.hypenate(namespace,'numerical-legend-gradient')); + + + colorFunction$$1.dataExtent([min, max]) + .colorBy('value') + .valueExtractor(function(k, v, i){return v}); + + + linearGradient.selectAll('stop') + .data( colorFunction$$1.colors() ) + .enter() + .append('stop') + .attr("offset", function(d, i){ return i / (colorFunction$$1.colors().length - 1) }) + .attr('stop-color', function(d) {return d}); + + + + + var rect = utils.sel.safeSelect(container, 'rect', 'legend') + .attr('transform', 'translate(0,'+fontSize+')') + .style("fill", "url(#"+utils.str.hypenate(namespace,'numerical-legend-gradient')+")") + .attr('x', 0) + .attr('y', 0) + .attr('width', spaceX) + .attr('height', spaceY - fontSize*2) + .on('mousemove', function(d, i){legendMousemove(d, i, rect, d3.select(this));}) + .on('mouseout', function(d, i){ d3.select("#"+utils.str.hypenate(namespace,'legend-tooltip')).remove(); }); + + var minText = utils.sel.safeSelect(container, 'text', 'min') + .text(utils.math.round(min, 2)) + .attr('text-anchor', 'middle') + .attr("font-size", fontSize+'px') + .attr('transform', function(d, i){ + var + x = spaceX / 2, + y = spaceY - fontSize / 4, + t = 'translate('+x+','+y+')'; + return t + }); + + var maxText = utils.sel.safeSelect(container, 'text', 'max') + .text(utils.math.round(max, 2)) + .attr('text-anchor', 'middle') + .attr("font-size", fontSize+'px') + .attr('transform', function(d, i){ + var + x = spaceX / 2, + y = fontSize, + t = 'translate('+x+','+y+')'; + return t + }); + + + + + } + + function legendMousemove(d, i, rect, t) { + var s = d3.scaleLinear() + .domain([0, rect.attr('height')]) + .range([max, min]); + var m = d3.mouse(rect.node()); + var v = utils.math.round(s(m[1]),roundTo); + + var strokeColor = colorFunction$$1(undefined, v, undefined, 'stroke'); + var fillColor = colorFunction$$1(undefined, v, undefined, 'fill'); + + var div = utils.sel.safeSelect(d3.select('body'), 'div', utils.str.hypenate(namespace,'legend-tooltip')) + .attr('id', utils.str.hypenate(namespace,'legend-tooltip')) + .style('position', 'absolute') + .style('left', (d3.event.pageX+15)+'px') + .style('top', (d3.event.pageY+15)+'px') + .style('background-color', fillColor) + .style('border-color', strokeColor) + + .style('min-width', (fontSize * (String(max).split('.')[0].length+3))+'px') + .style('min-height', (fontSize * (String(max).split('.')[0].length+3))+'px') + .style('border-radius', '50%') + .style('border-radius', '5000px') + + .style('display', 'flex') + .style('justify-content', 'center') + .style('text-align', 'middle') + .style('padding', 2+"px") + + .style('border-style', 'solid') + .style('border-width', 2); + + var text = utils.sel.safeSelect(div, 'div') + .text(v) + .style('color', textColor) + .style('align-self', 'center'); + } + + return legend +} + +let legends = { + categorical: categoricLegend, numeric: numericLegend +}; + +// import * as d3 from "d3"; +/******************************************************************************* +** ** +** ** +** D3 EXTENSIONS ** +** ** +** ** +*******************************************************************************/ +/** +* Recursively ascends parents of selection until it finds an svg tag +* @function d3.selection.thisSVG +* @augments d3.selection +* @returns {Element} which is the svg tag, not the d3 selection of that tag +*/ +d3.selection.prototype.thisSVG = function() { return utils.sel.getContainingSVG(this.node()); }; + + +/** +* Helper for getting absolute position of the mouse +* @function d3.mouse.absolute +* @augments d3.mouse +* @returns {number[]} [x, y] as they relate to `html` not to local scope. +*/ +d3.mouse.absolute = function() { + var html = d3.select('html').node(); + var [x, y] = this(html); + return [x, y] +}; + + +/** +* Gets position of the selection in relation to the containing svg +* @see{@link getContainingSVG} +* @function d3.selection.absolutePosition +* @augments d3.selection +* @returns {Object} with structure similar to getBoundingClientRect, e.g. +* top, left, bottom, right, height, width +*/ +d3.selection.prototype.absolutePosition = function() { + var element = this.node(); + var elementPosition = element.getBoundingClientRect(); + var containerSVG = utils.sel.getContainingSVG(element); + var svgPosition = containerSVG.getBoundingClientRect(); + + return { + top: elementPosition.top - svgPosition.top, + left: elementPosition.left - svgPosition.left, + bottom: elementPosition.bottom - svgPosition.top, + right: elementPosition.right - svgPosition.left, + height: elementPosition.height, + width: elementPosition.width + }; + +}; + + +d3.selection.prototype.relativePositionTo = function(container) { + var element = this.node(); + var elementPosition = element.getBoundingClientRect(); + var containerSVG = container; + var svgPosition = containerSVG.getBoundingClientRect(); + + return { + top: elementPosition.top - svgPosition.top, + left: elementPosition.left - svgPosition.left, + bottom: elementPosition.bottom - svgPosition.top, + right: elementPosition.right - svgPosition.left, + height: elementPosition.height, + width: elementPosition.width + }; + +}; + +function getTranslation$1(selection){ + var transform = selection.attr('transform'); + var [junk, xy] =transform.split('translate('); + var [x, y] = xy.split(','); + junk = y.split(')'); + return [parseFloat(x), parseFloat(y)] +} + +function lasso( selection ) { + var + svg, // svg that is target of events + objectContainer, // container which houses objects we are selecting (allows for transform to be applied to lasso) + objectClass, // class of object we are selecting + namespace="d3sm-lasso", + chartContainer, + chartOffset, + objectsOffset, + eventCatcher, + + xScale, // optional scale for the lasso currentPoints + yScale, // optional scale for the lasso currentPoints + + activeQ = false, // whether or not lasso is active + + currentPoints=[], // mouse points for current lasso + allPoints=[], // list of lists for all points of lassos + + line = d3.line() + .x(function(d, i){ + var x; + if (xScale != undefined) { x = xScale(d[0]); } + else {x = d[0];} + return x //- chartOffset[0]// - objectsOffset[0] + }) + .y(function(d, i){ + var y; + if (yScale != undefined) { y= yScale(d[1]); } + else {y = d[1];} + return y// - chartOffset[1]// - objectsOffset[1] + }) + .curve(d3.curveLinearClosed), + + instance=0, // an indentifier for which instance this lasso is under the current svg + + tickDistance = 10, + + // styles for lasso path + color$$1 = '#17a2b8', + animationRate = '10s', + opacity=0.3, + dashArray = '5, 10', + stroke = 'black', + strokeWidth=2, + + // styles for lassoed objects + lassoedFill = "white", + lassoedStroke = 'black', + lassoedStrokeWidth = 3, + + transitionDuration = 1000, + easeFunc = d3.easeExp; + + var path; + + lasso.svg = function(_) { return arguments.length ? (svg = _, lasso) : svg; }; + lasso.chartContainer = function(_) { return arguments.length ? (chartContainer = _, lasso) : chartContainer; }; + lasso.objectContainer = function(_) { return arguments.length ? (objectContainer = _, lasso) : objectContainer; }; + lasso.objectClass = function(_) { return arguments.length ? (objectClass = _, lasso) : objectClass; }; + lasso.namespace = function(_) { return arguments.length ? (namespace = _, lasso) : namespace; }; + lasso.xScale = function(_) { return arguments.length ? (xScale = _, lasso) : xScale; }; + lasso.yScale = function(_) { return arguments.length ? (yScale = _, lasso) : yScale; }; + lasso.activeQ = function(_) { return arguments.length ? (activeQ = _, lasso) : activeQ; }; + lasso.currentPoints = function(_) { return arguments.length ? (currentPoints = _, lasso) : currentPoints; }; + lasso.allPoints = function(_) { return arguments.length ? (allPoints = _, lasso) : allPoints; }; + lasso.instance = function(_) { return arguments.length ? (instance = _, lasso) : instance; }; + lasso.tickDistance = function(_) { return arguments.length ? (tickDistance = _, lasso) : tickDistance; }; + lasso.color = function(_) { return arguments.length ? (color$$1 = _, lasso) : color$$1; }; + lasso.animationRate = function(_) { return arguments.length ? (animationRate = _, lasso) : animationRate; }; + lasso.opacity = function(_) { return arguments.length ? (opacity = _, lasso) : opacity; }; + lasso.dashArray = function(_) { return arguments.length ? (dashArray = _, lasso) : dashArray; }; + lasso.stroke = function(_) { return arguments.length ? (stroke = _, lasso) : stroke; }; + lasso.lassoedFill = function(_) { return arguments.length ? (lassoedFill = _, lasso) : lassoedFill; }; + lasso.lassoedStroke = function(_) { return arguments.length ? (lassoedStroke = _, lasso) : lassoedStroke; }; + lasso.lassoedStrokeWidth = function(_) { return arguments.length ? (lassoedStrokeWidth = _, lasso) : lassoedStrokeWidth; }; + lasso.eventCatcher = function(_) { return arguments.length ? (eventCatcher = _, lasso) : eventCatcher; }; + + lasso.drag = drag; + lasso.draw = draw; + lasso.tick = tick; + lasso.detect = detect; + lasso.toggle = toggle; + lasso.remove = remove; + lasso.render = render; + lasso.keyFrames = keyFrames; + lasso.updateObjects = updateObjects; + lasso.applyPathAttributes = applyPathAttributes; + lasso.applyObjectAttributes = applyObjectAttributes; + + keyFrames(); + + function lasso() { + // add a dash animation if needed + if (activeQ) { transitionDraw(); } + } + + function toggle(state) { + // use optional param to set state, otherwise toggle state + activeQ = (state!=undefined) ? state : !activeQ; + chartOffset = getTranslation$1(chartContainer); //utils.sel.getTranslation(chartContainer) + objectsOffset = getTranslation$1(objectContainer); //utils.sel.getTranslation(objectContainer) + + if (activeQ) { + svg.node().addEventListener('mousedown', render, true); + } else { + svg.node().removeEventListener('mousedown', render, true); + remove(); + } + + } + + function draw() { + chartOffset = getTranslation$1(chartContainer); //utils.sel.getTranslation(chartContainer) + objectsOffset = getTranslation$1(objectContainer); //utils.sel.getTranslation(objectContainer) + + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + var paths$$1 = container.selectAll('path[instance="'+instance+'"]'); + + // update + paths$$1 = paths$$1.data(allPoints); + + // remove excess + var pExit = paths$$1.exit().remove(); + // add needed paths + var pEnter = paths$$1.enter().append('path'); + + // merge + paths$$1 = paths$$1.merge(pEnter); + + // apply + applyPathAttributes(paths$$1); + } + + function remove() { + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + var paths$$1 = container.selectAll('path[instance="'+instance+'"]').remove(); + container.remove(); + objectContainer.selectAll(objectClass).classed("in-lasso", false); + updateObjects(); + } + + function render( event ) { + // nothing can interefer with drawing the lasso + event.preventDefault(); event.stopPropagation(); + + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + + /* + each time the user presses down, while the state is active, the lasso + the lasso should make a seperate segment. + */ + currentPoints = []; + + svg.node().addEventListener('mousemove', drag); + svg.node().addEventListener('mouseup', function(event) { + svg.node().removeEventListener('mousemove', drag); + allPoints.push(currentPoints); + // BUG: somehow this is pushing currentPoints n times where n is the nth lasso path for the current instance + // NOTE: allPoints = utils.arr.unique(allPoints) is a temporary and inefficient fix + allPoints = utils.arr.unique(allPoints); + }); + + path = container.append('path').data([currentPoints]); + applyPathAttributes(path); + } + + function transitionDraw() { + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + var paths$$1 = container.selectAll('path[instance="'+instance+'"]'); + + // update + paths$$1 = paths$$1.data(allPoints); + + // remove excess + var pExit = paths$$1.exit().remove(); + // add needed paths + var pEnter = paths$$1.enter().append('path'); + + // merge + paths$$1 = paths$$1.merge(pEnter) + .transition().duration(transitionDuration) + .ease(easeFunc); + applyPathAttributes(paths$$1); + + } + + function applyPathAttributes(path) { + path + .attr("class", utils.str.hypenate(namespace, "lasso-path")) + .style('opacity', opacity) + .attr('fill', color$$1) + .attr("d", line) + .attr('instance', instance) + .style("stroke-dasharray", dashArray) + .attr("stroke", stroke) + .attr("stroke-width", strokeWidth) + .style('animation', 'lassoDash '+animationRate+' linear') + .style("animation-iteration-count", "infinite"); + } - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof legend# - * @property - */ - transitionDuration = 1000, - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof legend# - * @property - */ - easeFunc = d3.easeExp; + function drag(event) { + /* + effectively create a mouse down and move event (which normally is inteperated + as 'drag' by the browser) by dynamically adding / removing this event on + mouse down / mouse up. + */ + if (eventCatcher != undefined) {eventCatcher.dispatch(utils.str.hypenate(namespace,"drag"));} + // d3.dispatch(utils.str.hypenate(namespace,"drag")) - legend.categories = function(_) { return arguments.length ? (categories=_, legend) : categories }; + if (event.which != 1) {return} // ensures left mouse button set + d3.event = event; + var pt = d3.mouse(objectContainer.node()); + var pt = d3.mouse(svg.node()); - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {legend | d3.selection} - * @memberof legend - * @property - * by default selection = selection - */ - legend.selection = function(_) { return arguments.length ? (selection = _, legend) : selection; }; - /** - * Gets or sets the data - * (see {@link legend#data}) - * @param {number} [_=none] - * @returns {legend | object} - * @memberof legend - * @property - */ - legend.data = function(_) { return arguments.length ? (data = _, legend) : data; }; - /** - * Gets or sets the orient of the bars - * (see {@link legend#orient}) - * @param {number} [_=none] - * @returns {legend | object} - * @memberof legend - * @property - */ - legend.orient = function(_) { return arguments.length ? (orient = _, legend) : orient; }; - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link legend#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {legend | number} - * @memberof legend - * @property - * by default spaceX = undefined - */ - legend.spaceX = function(_) { return arguments.length ? (spaceX = _, legend) : spaceX; }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link legend#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {legend | number} - * @memberof legend - * @property - * by default spaceY = undefined - */ - legend.spaceY = function(_) { return arguments.length ? (spaceY = _, legend) : spaceY; }; + if (xScale != undefined) {pt[0] = xScale.invert(pt[0]);} + if (yScale != undefined) {pt[1] = yScale.invert(pt[1]);} + pt[0] = pt[0] - chartOffset[0] - objectsOffset[0]; + pt[1] = pt[1] - chartOffset[1] - objectsOffset[1]; - /** - * Gets / sets whether or not legend is allowed to go beyond specified dimensions - * (see {@link legend#spaceX}) - * @param {boolean} [_=none] - * @returns {legend | boolean} - * @memberof legend - * @property - * by default overflowQ = false - */ - legend.overflowQ = function(_) { return arguments.length ? (overflowQ = _, legend) : overflowQ; }; - /** - * Gets / sets the grouping of the bars - * (see {@link legend#grouping}) - * @param {Array[]} [_=none] - * @returns {legend | Array[]} - * @memberof legend - * @property - * by default grouping = undefined - */ - legend.grouping = function(_) { return arguments.length ? (grouping = _, legend) : grouping; }; - /** - * Gets / sets the valueExtractor - * (see {@link legend#valueExtractor}) - * @param {function} [_=none] - * @returns {legend | function} - * @memberof legend - * @property - * by default valueExtractor = function(key, index) { return data[key] }, - */ - legend.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, legend) : valueExtractor; }; - /** - * Gets / sets the sortingFunction - * (see {@link bar#sortingFunction}) - * @param {function} [_=none] - * @returns {bar | function} - * @memberof bar - * @property - * by default sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, - */ - legend.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, legend) : sortingFunction; }; - /** - * Gets / sets objectSpacer - * (see {@link legend#objectSpacer}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default objectSpacer = 0.05 - */ - legend.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, legend) : objectSpacer; }; - /** - * Gets / sets the minObjectSize - * (see {@link legend#minObjectSize}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default minObjectSize = 50 - */ - legend.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, legend) : minObjectSize; }; - /** - * Gets / sets the maxObjectSize - * (see {@link legend#maxObjectSize}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default maxObjectSize = 100 - */ - legend.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, legend) : maxObjectSize; }; + /* if we have a point already, test if it passes a minimum distance to prevent overwhelming with too many tick functions */ + if (currentPoints.length) { + var lastPt = currentPoints[currentPoints.length - 1]; + var a = [pt[0], pt[1]], b = [lastPt[0], lastPt[1]]; - /** - * Gets / sets the barStrokeWidth - * (see {@link legend#barStrokeWidth}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default barStrokeWidth = 2 - */ - legend.bubbleStrokeWidth = function(_) { return arguments.length ? (bubbleStrokeWidth = _, legend) : bubbleStrokeWidth; }; - /** - * Gets / sets the colorFunction - * (see {@link legend#colorFunction}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default colorFunction = colorFunction() - */ - legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, legend) : colorFunction$$1; }; + if (xScale) {b[0] = xScale(b[0]); a[0] = xScale(a[0]);} + if (yScale) {b[1] = yScale(b[1]); a[1] = yScale(a[1]);} - /** - * Gets / sets the backgroundFill - * (see {@link legend#backgroundFill}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default backgroundFill = 'transparent' - */ - legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, legend) : backgroundFill; }; - /** - * Gets / sets the namespace - * (see {@link legend#namespace}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default namespace = 'd3sm-legend' - */ - legend.namespace = function(_) { return arguments.length ? (namespace = _, legend) : namespace; }; - /** - * Gets / sets the objectClass - * (see {@link legend#objectClass}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default objectClass = 'tick-group' - */ - legend.objectClass = function(_) { return arguments.length ? (objectClass = _, legend) : objectClass; }; - /** - * Gets / sets the transitionDuration - * (see {@link legend#transitionDuration}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default transitionDuration = 1000 - */ - legend.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, legend) : transitionDuration; }; - /** - * Gets / sets the easeFunc - * (see {@link legend#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {legend | d3.ease} - * @memberof legend - * @property - * by default easeFunc = d3.easeExp - */ - legend.easeFunc = function(_) { return arguments.length ? (easeFunc = _, legend) : easeFunc; }; + var dist = utils.math.euclideanDistance(b, a); + if (dist > tickDistance) { tick(pt); } + } + else { tick(pt); } + } - function legend() { - var horizontalQ = (orient == 'horizontal') ? true : false; - var verticalQ = !horizontalQ; - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + function tick (pt) { + /* + If a point is provided update data and objects. + Otherwise just call on data we already have. + Why like this?: + 1. currentPoints is current points to allow disjunct lassos, currentPoints is only pushed to + allPoints after mouseup. + 2. to allow render of objects in the lasso class / updating the data list + just by toggling the button + */ - colorFunction$$1.dataExtent([0, categories.length - 1]) - .colorBy('categories') - .categoryExtractor(function(k, v, i){return v}); + if (pt != undefined) { + currentPoints.push(pt); + path.attr("d", line); + if (currentPoints.length < 3) {return} // need at least 3 points to detect anything. + detect(allPoints.concat([currentPoints])); + } else { + detect(allPoints); + } + } - var r = Math.min(spaceX, spaceY) / 2; - var numberOfObjects = categories.length; - // if grouping is undefined sort barKeys by sortingFunction - var ordered = (grouping == undefined) ? categories.sort(sortingFunction) : grouping; - // ordered might be nested depending on grouping - var catKeys = utils$1.arr.flatten(ordered); + function detect(lassos) { + if (lassos == undefined) {lassos = allPoints;} + objectContainer.selectAll(objectClass).each(function(d, i){ + var current = d3.select(this), - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - var objectSize = utils$1.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - // calculate spacer size if needed - var spacerSize = utils$1.math.calculateWidthOfSpacer(catKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); - // make the nested groups - var spacerFunction = groupingSpacer() - .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) - .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc) - .namespace(namespace); + box = current.absolutePosition(), + // box = current.relativePositionTo(objectContainer.node()), - spacerFunction(container, ordered, 0); - var r = Math.min(objectSize, spaceX, spaceY) / 2 - bubbleStrokeWidth; + boxPts = [ + [ + box.left - chartOffset[0] - objectsOffset[0], + box.top - chartOffset[1] - objectsOffset[1] + ], + [ + box.right - chartOffset[0] - objectsOffset[0], + box.top - chartOffset[1] - objectsOffset[1] + ], + [ + box.left - chartOffset[0] - objectsOffset[0], + box.bottom - chartOffset[1] - objectsOffset[1] + ], + [ + box.right - chartOffset[0] - objectsOffset[0], + box.bottom - chartOffset[1] - objectsOffset[1] + ] + ]; - container.selectAll('g:not(.to-remove).'+objectClass).each(function(cat, i) { - var t = d3.select(this); - var c = utils$1.sel.safeSelect(t, 'circle'); - var fillColor = colorFunction$$1(undefined, cat, i, 'fill'), // prevent duplicate computation - strokeColor = colorFunction$$1(undefined, cat, i, 'stroke'); + if (xScale != undefined) { + boxPts[0][0] = xScale.invert(boxPts[0][0]); + boxPts[1][0] = xScale.invert(boxPts[1][0]); + boxPts[2][0] = xScale.invert(boxPts[2][0]); + boxPts[3][0] = xScale.invert(boxPts[3][0]); + } + if (yScale != undefined) { + boxPts[0][1] = yScale.invert(boxPts[0][1]); + boxPts[1][1] = yScale.invert(boxPts[1][1]); + boxPts[2][1] = yScale.invert(boxPts[2][1]); + boxPts[3][1] = yScale.invert(boxPts[3][1]); + } - var cx = horizontalQ - ? r+bubbleStrokeWidth - : (spaceX - r*2) / 2 + r; - var cy = verticalQ - ? r+bubbleStrokeWidth - : (spaceX - r*2) / 2 + r; - c.attr("r", r) - .attr('cx', cx) - .attr('cy', cy) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', bubbleStrokeWidth); + /* + flag needed as we have to test multiple lasso segments, and if the point + is not in one segment, it does not mean it is not in any + */ + var inAnyLassoQ = false; + for (var i = 0; i < lassos.length; i++) { + var lassoPoints = lassos[i]; + // .map(function(pt){ + // var x, y = pt + // if (xScale!=undefined) {x = xScale(x)} + // if (yScale!=undefined) {y = yScale(y)} + // return [x, y] + // }) + var boxInLassoQ = boxPts.every(coord => d3.polygonContains(lassoPoints, coord)); - var text = utils$1.sel.safeSelect(t, 'text'); - text.text(cat) - .attr('text-anchor', 'middle') - .attr("transform", function(d, i){ - var - x = cx, - y = cy + text.node().getBoundingClientRect().height / 4, - t = 'translate('+x+','+y+')'; - return t - }); + if (boxInLassoQ) { inAnyLassoQ = true; } // only update flag in the positive case. + } + current.classed('in-lasso', inAnyLassoQ); + current.classed('in-lasso-'+instance, inAnyLassoQ); }); - + updateObjects(); + return objectContainer.selectAll('.in-lasso-'+instance) } - return legend -} -function numericLegend( selection ) { - var - min=0, - max=1, - spaceX, - spaceY, - colorFunction$$1 = colorFunction(), - namespace='d3sm-linear-vertical-gradient', - fontSize = 12, - backgroundFill = 'transparent', - textColor = 'black', - roundTo = 2; + function updateObjects() { + objectContainer.selectAll(objectClass).each(function(d, i) { + var t = d3.select(this); + applyObjectAttributes(t, t.classed('in-lasso')); + }); + } + function applyObjectAttributes(obj, setQ) { + var + preLassoFill = obj.attr('_pre_lasso_fill'), + preLassoStroke = obj.attr('_pre_lasso_stroke'), + preLassoStrokeWidth = obj.attr('_pre_lasso_stroke-width'); - legend.min = function(_) { return arguments.length ? (min=_, legend) : min }; - legend.max = function(_) { return arguments.length ? (max=_, legend) : max }; - legend.spaceX = function(_) { return arguments.length ? (spaceX=_, legend) : spaceX }; - legend.spaceY = function(_) { return arguments.length ? (spaceY=_, legend) : spaceY }; - legend.namespace = function(_) { return arguments.length ? (namespace=_, legend) : namespace }; - legend.fontSize = function(_) { return arguments.length ? (fontSize=_, legend) : fontSize }; - legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill=_, legend) : backgroundFill }; - legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1=_, legend) : colorFunction$$1 }; - legend.textColor = function(_) { return arguments.length ? (textColor=_, legend) : textColor }; - legend.roundTo = function(_) { return arguments.length ? (roundTo=_, legend) : roundTo }; + if (setQ) { + obj.classed("in-lasso", true); + obj.classed('in-lasso-'+instance, true); + if (preLassoFill == undefined) { obj.attr('_pre_lasso_fill', obj.attr('fill')); } + if (preLassoStroke == undefined) { obj.attr('_pre_lasso_stroke', obj.attr('stroke')); } + if (preLassoStrokeWidth == undefined) { obj.attr('_pre_lasso_stroke-width', obj.attr('stroke-width')); } - function legend() { - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + obj + //BUG: when .raise() + .attr('fill', lassoedFill) + .attr('stroke', lassoedStroke) + .attr('stoke-width', lassoedStrokeWidth); - var defs = utils$1.sel.safeSelect(selection, 'defs'); - var linearGradient = utils$1.sel.safeSelect(defs, 'linearGradient') - .attr("x1", "0%") - .attr("y1", "100%") - .attr("x2", "0%") - .attr("y2", "0%") - .attr('id', utils$1.str.hypenate(namespace,'numerical-legend-gradient')); + } else { + obj.classed("in-lasso", false); + obj.classed('in-lasso-'+instance, false); + if (preLassoFill != undefined) { obj.attr('fill', preLassoFill); } + if (preLassoStroke != undefined) { obj.attr('stroke', preLassoStroke); } + if (preLassoStrokeWidth != undefined) { obj.attr('stroke-width', preLassoStrokeWidth); } + } + } + function keyFrames() { + var style = + d3.select("html").select('style.'+utils.str.hypenate(namespace,"lasso-dash")); + if (style.empty()) { + d3.select("html").append('style') + .classed(utils.str.hypenate(namespace,"lasso-dash"), true) + .html("@keyframes lassoDash {to { stroke-dashoffset: 1000;}}"); + } - colorFunction$$1.dataExtent([min, max]) - .colorBy('value') - .valueExtractor(function(k, v, i){return v}); + } + return lasso +} +function lassoWidget( selection ) { + var + namespace = 'd3sm-lasso', + selection = selection, + svg, + chartContainer, + objectContainer, + objectClass, + lassoLine = d3.line() + .x(function(d, i){return d[0]}) + .y(function(d, i){return d[1]}) + .curve(d3.curveLinearClosed), + colorFunction$$1 = colorFunction(), + maxNumberOfGroups, + dataExtractor, + xScale, + yScale; - linearGradient.selectAll('stop') - .data( colorFunction$$1.colors() ) - .enter() - .append('stop') - .attr("offset", function(d, i){ return i / (colorFunction$$1.colors().length - 1) }) - .attr('stop-color', function(d) {return d}); + function onError(msg, style){ + console.log(msg, style); + } + // setup the container + setupDataselectContainer(); + + lassoWidget.objectClass = function(_){return arguments.length ? (objectClass='.'+_.replace('.',''), lassoWidget) : objectClass}; + lassoWidget.svg = function(_){return arguments.length ? (svg=_, lassoWidget) : svg}; + lassoWidget.submit = function(_){return arguments.length ? (submit=_, lassoWidget) : submit}; + lassoWidget.maxNumberOfGroups = function(_){return arguments.length ? (maxNumberOfGroups=_, lassoWidget) : maxNumberOfGroups}; + lassoWidget.onError = function(_){return arguments.length ? (onError=_, lassoWidget) : onError}; + lassoWidget.objectContainer = function(_){return arguments.length ? (objectContainer=_, lassoWidget) : objectContainer}; + lassoWidget.chartContainer = function(_){return arguments.length ? (chartContainer=_, lassoWidget) : chartContainer}; + lassoWidget.dataExtractor = function(_){return arguments.length ? (dataExtractor=_, lassoWidget) : dataExtractor}; + lassoWidget.xScale = function(_){return arguments.length ? (xScale=_, lassoWidget) : xScale}; + lassoWidget.yScale = function(_){return arguments.length ? (yScale=_, lassoWidget) : yScale}; + + function styles() { + var s = d3.select("html").select("style."+namespace+'lasso-widget'); + if (s.empty()) { + d3.select("html").append("style") + .classed(namespace+'lasso-widget', true) + .html( + "."+utils.str.hypenate(namespace, "data-table") + "{\ + height:100px;\ + overflow:auto;\ + }" + ); + } + } + function lassoWidget() { + styles(); + } - var rect = utils$1.sel.safeSelect(container, 'rect', 'legend') - .attr('transform', 'translate(0,'+fontSize+')') - .style("fill", "url(#"+utils$1.str.hypenate(namespace,'numerical-legend-gradient')+")") - .attr('x', 0) - .attr('y', 0) - .attr('width', spaceX) - .attr('height', spaceY - fontSize*2) - .on('mousemove', function(d, i){legendMousemove(d, i, rect, d3.select(this));}) - .on('mouseout', function(d, i){ d3.select("#"+utils$1.str.hypenate(namespace,'legend-tooltip')).remove(); }); + function submit(data) { + console.log(data); + } - var minText = utils$1.sel.safeSelect(container, 'text', 'min') - .text(utils$1.math.round(min, 2)) - .attr('text-anchor', 'middle') - .attr("font-size", fontSize+'px') - .attr('transform', function(d, i){ - var - x = spaceX / 2, - y = spaceY - fontSize / 4, - t = 'translate('+x+','+y+')'; - return t - }); + function plusTab(tabList) { + var tab = utils.sel.safeSelect(tabList, 'li', utils.str.hypenate(namespace, 'plus-tab')) + .classed('ml-auto', true) + .classed('nav-item', true) + .on('click', makeNewGroup), - var maxText = utils$1.sel.safeSelect(container, 'text', 'max') - .text(utils$1.math.round(max, 2)) - .attr('text-anchor', 'middle') - .attr("font-size", fontSize+'px') - .attr('transform', function(d, i){ - var - x = spaceX / 2, - y = fontSize, - t = 'translate('+x+','+y+')'; - return t - }); + anchor = utils.sel.safeSelect(tab, 'a', 'nav-link'), + icon = utils.sel.safeSelect(anchor, 'i', 'fa') + .classed('fa-plus fa-2x text-success', true); + } + + function sendTab(tabList) { + var tab = utils.sel.safeSelect(tabList, 'li', utils.str.hypenate(namespace, 'send-tab')) + .classed('ml-auto', true) + .classed('nav-item', true) + .on('click', clickSend) + , + anchor = utils.sel.safeSelect(tab, 'a', 'nav-link'), + icon = utils.sel.safeSelect(anchor, 'i', 'fa') + .classed('fa-paper-plane-o fa-2x text-primary', true); + } + function clickSend() { + var data = gatherDataLists(); + submit(data); + } + function closeTab(tabList) { + var tab = utils.sel.safeSelect(tabList, 'li', utils.str.hypenate(namespace, 'close-tab')) + .classed('ml-auto', true) + .classed('nav-item', true) + .on('click', function(){ + var m = d3.mouse(d3.select("html").node()); + selection.select('div.card').style("position", 'relative') + .transition().duration(1000) + .ease(d3.easeBack) + .style('left', window.outerWidth +'px') + .remove(); + }) + , + anchor = utils.sel.safeSelect(tab, 'a', 'nav-link'), + icon = utils.sel.safeSelect(anchor, 'i', 'fa') + .classed('fa-window-close-o fa-2x text-danger', true); } - function legendMousemove(d, i, rect, t) { - var s = d3.scaleLinear() - .domain([0, rect.attr('height')]) - .range([max, min]); - var m = d3.mouse(rect.node()); - var v = utils$1.math.round(s(m[1]),roundTo); - var strokeColor = colorFunction$$1(undefined, v, undefined, 'stroke'); - var fillColor = colorFunction$$1(undefined, v, undefined, 'fill'); - var div = utils$1.sel.safeSelect(d3.select('body'), 'div', utils$1.str.hypenate(namespace,'legend-tooltip')) - .attr('id', utils$1.str.hypenate(namespace,'legend-tooltip')) - .style('position', 'absolute') - .style('left', (d3.event.pageX+15)+'px') - .style('top', (d3.event.pageY+15)+'px') - .style('background-color', fillColor) - .style('border-color', strokeColor) + function setupDataselectContainer() { + var + card = utils.sel.safeSelect(selection, 'div', 'card'), + cardHeader = utils.sel.safeSelect(card, 'div', 'card-header'), - .style('min-width', (fontSize * (String(max).split('.')[0].length+3))+'px') - .style('min-height', (fontSize * (String(max).split('.')[0].length+3))+'px') - .style('border-radius', '50%') - .style('border-radius', '5000px') + tabList = utils.sel.safeSelect(cardHeader, 'ul', 'nav') + .classed('nav-tabs card-header-tabs', true) + .classed(utils.str.hypenate(namespace, 'tab-list'), true) + .attr('role', 'tablist'), - .style('display', 'flex') - .style('justify-content', 'center') - .style('text-align', 'middle') - .style('padding', 2+"px") + cardBody = utils.sel.safeSelect(card, 'div', 'card-body'), + tabContent = utils.sel.safeSelect(cardBody, 'div', 'tab-content') + .classed(utils.str.hypenate(namespace, 'tab-content'), true), - .style('border-style', 'solid') - .style('border-width', 2); + // leftTabs = utils.sel.safeSelect(tabList, 'div', 'nav mr-auto left-aligned-tabs') + rightTabs = utils.sel.safeSelect(tabList, 'div', 'right-aligned-tabs').classed('nav ml-auto ', true); - var text = utils$1.sel.safeSelect(div, 'div') - .text(v) - .style('color', textColor) - .style('align-self', 'center'); + plusTab(rightTabs); + sendTab(rightTabs); + closeTab(rightTabs); + var + defaultTab = utils.sel.safeSelect(tabContent, 'div', 'tab-pane') + .classed(utils.str.hypenate(namespace,'default-tab'), true) + .classed("active", true) + .classed('text-left', true), + + p = utils.sel.safeSelect(defaultTab, 'div') + .html( + "Click to add a new group.
"+ + "Click to submit for re-analysis.
"+ + "Click to close the dataselect widget." + ); + // .text('Click the green plus to add a new group.') } - return legend -} -let legends = { - categorical: categoricLegend, numeric: numericLegend -}; + function makeRemoveButton(paneBtnList) { + var + btn = utils.sel.safeSelect(paneBtnList, 'button', 'remove-btn') + .classed('btn btn-danger', true) + .on('click', removeBtnClickToRemove), + i = utils.sel.safeSelect(btn, 'i', 'fa').classed('fa-trash-o', true), + s = utils.sel.safeSelect(btn, 'span').text('Remove group'); + return btn + } + + function removeBtnClickToRemove(d, i) { + var + tab = selection.select('#'+utils.str.hypenate(namespace,'tab',d)), + pane = selection.select('#'+utils.str.hypenate(namespace,'tab','pane',d)); -// import * as d3 from "d3"; -/******************************************************************************* -** ** -** ** -** D3 EXTENSIONS ** -** ** -** ** -*******************************************************************************/ -/** -* Recursively ascends parents of selection until it finds an svg tag -* @function d3.selection.thisSVG -* @augments d3.selection -* @returns {Element} which is the svg tag, not the d3 selection of that tag -*/ -d3.selection.prototype.thisSVG = function() { return utils$1.sel.getContainingSVG(this.node()); }; + tab.remove(); + pane.remove(); + showRemainingPanes(); + updateTabAndPaneNumbers(); + } -/** -* Helper for getting absolute position of the mouse -* @function d3.mouse.absolute -* @augments d3.mouse -* @returns {number[]} [x, y] as they relate to `html` not to local scope. -*/ -d3.mouse.absolute = function() { - var html = d3.select('html').node(); - var [x, y] = this(html); - return [x, y] -}; + function togglePaneById(id) { + var panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')); + panes.selectAll('div.tab-pane') + .each(function(d, i){ + d3.select(this).classed('active show', d3.select(this).attr('id') == id); + }); + } -/** -* Gets position of the selection in relation to the containing svg -* @see{@link getContainingSVG} -* @function d3.selection.absolutePosition -* @augments d3.selection -* @returns {Object} with structure similar to getBoundingClientRect, e.g. -* top, left, bottom, right, height, width -*/ -d3.selection.prototype.absolutePosition = function() { - var element = this.node(); - var elementPosition = element.getBoundingClientRect(); - var containerSVG = getContainingSVG(element); - var svgPosition = containerSVG.getBoundingClientRect(); + function insertTab(tabs, n) { + + // var li = tabs.insert("li", ':nth-child('+(n)+')') + var li = tabs.insert("li", '.right-aligned-tabs') + .classed('nav-item', true) + .classed('alert', true) + .classed('alert-secondary', true) + .classed('alert-dismissable', true) + .classed('fade', true) + .classed('show', true) + // .classed('mr-auto', true) + .attr("role", 'alert') + .attr("id", utils.str.hypenate(namespace,'tab',n)) + .classed(utils.str.hypenate(namespace,'tab'), true); + + var a = utils.sel.safeSelect(li, 'a') + .attr('data-toggle', 'tab') + .text('Group ' + n) + .attr('href', '#'+utils.str.hypenate(namespace,'tab','pane',n)) + .on('dblclick', function(d, i){ + var t = d3.select(this); + t.attr('contenteditable', true); + d3.select(t.node().parentNode) + .classed('alert-secondary', false) + .classed('alert-warning', true); - return { - top: elementPosition.top - svgPosition.top, - left: elementPosition.left - svgPosition.left, - bottom: elementPosition.bottom - svgPosition.top, - right: elementPosition.right - svgPosition.left, - height: elementPosition.height, - width: elementPosition.width - }; + // d3.select("html").on("click", dispatchBlur) + // + // function dispatchBlur(d, i) { + // if (d3.event.target != d3.select(this)) { + // d3.select(this).dispatch("blur") + // } + // } -}; + }) + .on('blur', function(d, i){ -d3.selection.prototype.relativePositionTo = function(container) { - var element = this.node(); - var elementPosition = element.getBoundingClientRect(); - var containerSVG = container; - var svgPosition = containerSVG.getBoundingClientRect(); + var t = d3.select(this); + t.attr('contenteditable', false); + d3.select(t.node().parentNode) + .classed('alert-secondary', true) + .classed('alert-warning', false); - return { - top: elementPosition.top - svgPosition.top, - left: elementPosition.left - svgPosition.left, - bottom: elementPosition.bottom - svgPosition.top, - right: elementPosition.right - svgPosition.left, - height: elementPosition.height, - width: elementPosition.width - }; + }) + .on('input', function(d, i){ + var t = d3.select(this); + var txt = t.text(); + d3.select(t.attr('href')).select("p.lead").text(txt); + }); -}; -// function getTranslation(selection){ -// var transform = selection.attr('transform') -// var [junk, xy] =transform.split('translate(') -// var [x, y] = xy.split(',') -// y, junk = y.split(')') -// return [parseFloat(x), parseFloat(y)] -// } + return li + } -function lasso( selection ) { - var - svg, // svg that is target of events - objectContainer, // container which houses objects we are selecting (allows for transform to be applied to lasso) - objectClass, // class of object we are selecting - namespace="d3sm-lasso", - chartContainer, - chartOffset, - objectsOffset, - eventCatcher, + function makeTabPane(panes, n) { + var pane = panes.append('div') + .datum(n) + .attr('role', 'tabpanel') + .classed('tab-pane', true) + .classed('text-left', true) + .classed(utils.str.hypenate(namespace,'tab','pane'), true) + .attr('id', utils.str.hypenate(namespace,'tab','pane',n)); + return pane + } - xScale, // optional scale for the lasso currentPoints - yScale, // optional scale for the lasso currentPoints + function populatePane(pane, n) { + var + lead = utils.sel.safeSelect(pane, 'p', 'lead').text('Group '+n), - activeQ = false, // whether or not lasso is active + tableContainer = utils.sel.safeSelect(pane, 'div', 'table-responsive') + .attr("class", utils.str.hypenate(namespace, "data-table")), - currentPoints=[], // mouse points for current lasso - allPoints=[], // list of lists for all points of lassos + table = utils.sel.safeSelect(tableContainer, "table", "table") + .classed("table-sm", true).classed("table-hover", true), - line = d3.line() - .x(function(d, i){ - var x; - if (xScale != undefined) { x = xScale(d[0]); } - else {x = d[0];} - return x //- chartOffset[0]// - objectsOffset[0] - }) - .y(function(d, i){ - var y; - if (yScale != undefined) { y= yScale(d[1]); } - else {y = d[1];} - return y// - chartOffset[1]// - objectsOffset[1] - }) - .curve(d3.curveLinearClosed), + caption = utils.sel.safeSelect(table, "caption", "caption").html("List of selected"), - instance=0, // an indentifier for which instance this lasso is under the current svg + btns = utils.sel.safeSelect(pane, 'div', 'text-right'), - tickDistance = 10, + cN = n % colorFunction$$1.colors().length; + if (n % 2 != 0) { cN = (colorFunction$$1.colors().length - 1) - cN; } - // styles for lasso path - color$$1 = '#17a2b8', - animationRate = '10s', - opacity=0.3, - dashArray = '5, 10', - stroke = 'black', - strokeWidth=2, - // styles for lassoed objects - lassoedFill = "white", - lassoedStroke = 'black', - lassoedStrokeWidth = 3, - transitionDuration = 1000, - easeFunc = d3.easeExp; - var path; - lasso.svg = function(_) { return arguments.length ? (svg = _, lasso) : svg; }; - lasso.chartContainer = function(_) { return arguments.length ? (chartContainer = _, lasso) : chartContainer; }; - lasso.objectContainer = function(_) { return arguments.length ? (objectContainer = _, lasso) : objectContainer; }; - lasso.objectClass = function(_) { return arguments.length ? (objectClass = _, lasso) : objectClass; }; - lasso.namespace = function(_) { return arguments.length ? (namespace = _, lasso) : namespace; }; - lasso.xScale = function(_) { return arguments.length ? (xScale = _, lasso) : xScale; }; - lasso.yScale = function(_) { return arguments.length ? (yScale = _, lasso) : yScale; }; - lasso.activeQ = function(_) { return arguments.length ? (activeQ = _, lasso) : activeQ; }; - lasso.currentPoints = function(_) { return arguments.length ? (currentPoints = _, lasso) : currentPoints; }; - lasso.allPoints = function(_) { return arguments.length ? (allPoints = _, lasso) : allPoints; }; - lasso.instance = function(_) { return arguments.length ? (instance = _, lasso) : instance; }; - lasso.tickDistance = function(_) { return arguments.length ? (tickDistance = _, lasso) : tickDistance; }; - lasso.color = function(_) { return arguments.length ? (color$$1 = _, lasso) : color$$1; }; - lasso.animationRate = function(_) { return arguments.length ? (animationRate = _, lasso) : animationRate; }; - lasso.opacity = function(_) { return arguments.length ? (opacity = _, lasso) : opacity; }; - lasso.dashArray = function(_) { return arguments.length ? (dashArray = _, lasso) : dashArray; }; - lasso.stroke = function(_) { return arguments.length ? (stroke = _, lasso) : stroke; }; - lasso.lassoedFill = function(_) { return arguments.length ? (lassoedFill = _, lasso) : lassoedFill; }; - lasso.lassoedStroke = function(_) { return arguments.length ? (lassoedStroke = _, lasso) : lassoedStroke; }; - lasso.lassoedStrokeWidth = function(_) { return arguments.length ? (lassoedStrokeWidth = _, lasso) : lassoedStrokeWidth; }; - lasso.eventCatcher = function(_) { return arguments.length ? (eventCatcher = _, lasso) : eventCatcher; }; + var lassoBtn = makeLassoButton(btns); + var currentLasso = makeLassoFunction(n, colorFunction$$1.colors()[cN]); + var clearBtn = makeClearButton(btns); - lasso.drag = drag; - lasso.draw = draw; - lasso.tick = tick; - lasso.detect = detect; - lasso.toggle = toggle; - lasso.remove = remove; - lasso.render = render; - lasso.keyFrames = keyFrames; - lasso.updateObjects = updateObjects; - lasso.applyPathAttributes = applyPathAttributes; - lasso.applyObjectAttributes = applyObjectAttributes; + bindButtons(lassoBtn, clearBtn, currentLasso, table); - keyFrames(); + var removeBtn = makeRemoveButton(btns); + + lead.on("mouseover", function(){ + currentLasso.draw(); + }); + lead.on("mouseout", function(){ + currentLasso.remove(); + }); - function lasso() { - // add a dash animation if needed - if (activeQ) { transitionDraw(); } } - function toggle(state) { - // use optional param to set state, otherwise toggle state - activeQ = (state!=undefined) ? state : !activeQ; - chartOffset = utils$1.sel.getTranslation(chartContainer); - objectsOffset = utils$1.sel.getTranslation(objectContainer); + function makeLassoButton(btns) { + var btn = utils.sel.safeSelect(btns, 'button', 'lasso-btn') + .classed('btn btn-info', true) + .classed(namespace, true); + // .datum([lasso]) + var i = utils.sel.safeSelect(btn, 'i', 'fa').classed('fa-hand-pointer-o', true); + var s = utils.sel.safeSelect(btn, 'span').text('Lasso select'); + return btn - if (activeQ) { - svg.node().addEventListener('mousedown', render, true); - } else { - svg.node().removeEventListener('mousedown', render, true); - remove(); - } + } + function makeClearButton(btns) { + var btn = utils.sel.safeSelect(btns, 'button', 'clear-btn') + .classed('btn btn-light', true) + .classed(namespace, true); + var i = utils.sel.safeSelect(btn, 'i', 'fa').classed('fa-eraser', true); + var s = utils.sel.safeSelect(btn, 'span').text('Clear selection'); + return btn } - function draw() { - chartOffset = utils$1.sel.getTranslation(chartContainer); - objectsOffset = utils$1.sel.getTranslation(objectContainer); + function makeLassoFunction(instance, color$$1) { + var currentLasso = lasso() + .namespace(namespace) + .svg(svg) + .objectClass(objectClass) + .chartContainer(chartContainer) + .objectContainer(objectContainer) + .instance(instance) + .color(color$$1) + .yScale(yScale) + .xScale(xScale); + + return currentLasso + } - var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); - var paths$$1 = container.selectAll('path[instance="'+instance+'"]'); + function bindButtons(lassoBtn, clearBtn, currentLasso, table) { + currentLasso.eventCatcher(lassoBtn); + lassoBtn.node().addEventListener("click", lassoBtnToggle); + + lassoBtn.datum([currentLasso]) + .on(utils.str.hypenate(namespace,'render'), function(){ + d3.select(this).datum()[0].draw(); + }) + .on(utils.str.hypenate(namespace,"drag"), function(las){ + var nodes = chartContainer.selectAll(".in-lasso-"+las[0].instance()).nodes(); + var tableData = nodes.map(function(d, i){ + var extracted = dataExtractor(d3.select(d).datum()); + extracted["__node"] = d; + return extracted + }); - // update - paths$$1 = paths$$1.data(allPoints); - // remove excess - var pExit = paths$$1.exit().remove(); - // add needed paths - var pEnter = paths$$1.enter().append('path'); + makeTable(table, tableData, currentLasso); + }); - // merge - paths$$1 = paths$$1.merge(pEnter); + clearBtn.on('click', function(){ + currentLasso.allPoints([]); + currentLasso.currentPoints([]); + lassoBtn.dispatch(utils.str.hypenate(namespace,"drag")); - // apply - applyPathAttributes(paths$$1); - } + }); - function remove() { - var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); - var paths$$1 = container.selectAll('path[instance="'+instance+'"]').remove(); - container.remove(); - objectContainer.selectAll(objectClass).classed("in-lasso", false); - updateObjects(); } - function render( event ) { - // nothing can interefer with drawing the lasso - event.preventDefault(); event.stopPropagation(); + function lassoBtnToggle() { + var that = d3.select(this); + var las = that.datum()[0]; - var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + las.toggle(); + var activeQ = las.activeQ(); - /* - each time the user presses down, while the state is active, the lasso - the lasso should make a seperate segment. - */ - currentPoints = []; + if (las.activeQ()) { + that.classed('btn-info', !activeQ); + that.classed('btn-warning', activeQ); + that.select("span").text("Lasso select (active)"); + selection.selectAll("."+namespace+".lasso-btn").dispatch(utils.str.hypenate(namespace,'render')); + d3.select("html").node().addEventListener('mousedown', monitorLassoButtonState); + } else { + that.classed('btn-info', !activeQ); + that.classed('btn-warning', activeQ); + that.select("span").text("Lasso select"); + d3.select("html").node().removeEventListener('mousedown', monitorLassoButtonState); + } - svg.node().addEventListener('mousemove', drag); - svg.node().addEventListener('mouseup', function(event) { - svg.node().removeEventListener('mousemove', drag); - allPoints.push(currentPoints); - // BUG: somehow this is pushing currentPoints n times where n is the nth lasso path for the current instance - // NOTE: allPoints = utils.arr.unique(allPoints) is a temporary and inefficient fix - allPoints = utils$1.arr.unique(allPoints); - }); + function monitorLassoButtonState(event) { + /* + activeLasso stops event stopPropagation, so this event will not register in + that case. Thus we only need to ensure that the usre is clicking on the lasso button + otherwise, de-spawn lasso. + */ + if ( + event.target != that.node() && + event.target != that.select("span").node() && + event.target != that.select("i").node() + ) { + // event.preventDefault() + // event.stopPropagation() + las.toggle(false); + that.classed('btn-info', true); + that.classed('btn-warning', false); + that.select("span").text("Lasso select"); + // console.log(that, that.node()) + // that.dispatch("focusout") + } + } - path = container.append('path').data([currentPoints]); - applyPathAttributes(path); } - function transitionDraw() { - var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); - var paths$$1 = container.selectAll('path[instance="'+instance+'"]'); - // update - paths$$1 = paths$$1.data(allPoints); + function updateTableHeaderColumns(headR, tableData) { + var headerKeys = d3.keys(tableData[0]).filter(k=>k!="__node"); + if (headerKeys.length > 0) { + // headerKeys = ["remove"].concat(headerKeys) + headerKeys.push("remove"); + } - // remove excess - var pExit = paths$$1.exit().remove(); - // add needed paths - var pEnter = paths$$1.enter().append('path'); + var headerCols = headR.selectAll("th"); - // merge - paths$$1 = paths$$1.merge(pEnter) - .transition().duration(transitionDuration) - .ease(easeFunc); - applyPathAttributes(paths$$1); + headerCols = headerCols.data(headerKeys); + headerCols.exit().remove(); + headerCols = headerCols.merge(headerCols.enter().append("th").attr("scope","col")) + .text(function(d, i){return d}); + return headerKeys } - function applyPathAttributes(path) { - path - .attr("class", utils$1.str.hypenate(namespace, "lasso-path")) - .style('opacity', opacity) - .attr('fill', color$$1) - .attr("d", line) - .attr('instance', instance) - .style("stroke-dasharray", dashArray) - .attr("stroke", stroke) - .attr("stroke-width", strokeWidth) - .style('animation', 'lassoDash '+animationRate+' linear') - .style("animation-iteration-count", "infinite"); + function updateTableRows(body, tableData) { + var bodyRows = body.selectAll("tr"); + + bodyRows = bodyRows.data(tableData); + bodyRows.exit().remove(); + bodyRows = bodyRows.merge(bodyRows.enter().append("tr")); + + return bodyRows } - function drag(event) { - /* - effectively create a mouse down and move event (which normally is inteperated - as 'drag' by the browser) by dynamically adding / removing this event on - mouse down / mouse up. - */ + function updateTableRowColumns(cols, headerKeys, rowData, tableData, lasso$$1, table) { + cols = cols.data(headerKeys); + cols.exit().remove(); + cols = cols.merge(cols.enter().append("td")); + + cols.html(function(d, i){ + if (d != "remove") { return rowData[d] } + return "" + }).classed("text-left", function(d, i){ + if (d != "remove") { return false } + return true + }); + // .attr("scope",function(d, i){ + // if (d != "remove") { return false } + // return true + // }) + + cols.select("i.fa-close").on("click", function(d, i){ + var node = rowData["__node"], + n = d3.select(node); + n.classed("in-lasso", false); + n.classed("in-lasso-"+lasso$$1.instance(), false); + + tableData.map(function(dd, j){ + if (dd["__node"] == node) { + tableData.splice(j, 1); + makeTable(table, tableData, lasso$$1); + } + }); + + }); + } + + function makeTable(table, tableData, lasso$$1) { + var + head = utils.sel.safeSelect(table, "thead"), + headR = utils.sel.safeSelect(head, "tr"), + body = utils.sel.safeSelect(table, "tbody"), + + headerKeys = updateTableHeaderColumns(headR, tableData), - if (eventCatcher != undefined) {eventCatcher.dispatch(utils$1.str.hypenate(namespace,"drag"));} - // d3.dispatch(utils.str.hypenate(namespace,"drag")) + bodyRows = updateTableRows(body, tableData); - if (event.which != 1) {return} // ensures left mouse button set - d3.event = event; - var pt = d3.mouse(objectContainer.node()); - var pt = d3.mouse(svg.node()); + bodyRows.each(function(rowData, i){ + var t = d3.select(this); + var cols = t.selectAll("td"); - if (xScale != undefined) {pt[0] = xScale.invert(pt[0]);} - if (yScale != undefined) {pt[1] = yScale.invert(pt[1]);} - pt[0] = pt[0] - chartOffset[0] - objectsOffset[0]; - pt[1] = pt[1] - chartOffset[1] - objectsOffset[1]; + updateTableRowColumns(cols, headerKeys, rowData, tableData, lasso$$1, table); - /* if we have a point already, test if it passes a minimum distance to prevent overwhelming with too many tick functions */ - if (currentPoints.length) { - var lastPt = currentPoints[currentPoints.length - 1]; - var a = [pt[0], pt[1]], b = [lastPt[0], lastPt[1]]; + t.on("mouseover", function(d, i) { + lasso$$1.applyObjectAttributes(d3.select(d["__node"]),true); + }) + .on("mouseout", function(d, i) { + lasso$$1.applyObjectAttributes(d3.select(d["__node"]),false); + }); + }); - if (xScale) {b[0] = xScale(b[0]); a[0] = xScale(a[0]);} - if (yScale) {b[1] = yScale(b[1]); a[1] = yScale(a[1]);} - var dist = utils$1.math.euclideanDistance(b, a); - if (dist > tickDistance) { tick(pt); } - } - else { tick(pt); } } - function tick (pt) { - /* - If a point is provided update data and objects. - Otherwise just call on data we already have. - Why like this?: - 1. currentPoints is current points to allow disjunct lassos, currentPoints is only pushed to - allPoints after mouseup. - 2. to allow render of objects in the lasso class / updating the data list - just by toggling the button - */ - if (pt != undefined) { - currentPoints.push(pt); - path.attr("d", line); - if (currentPoints.length < 3) {return} // need at least 3 points to detect anything. - detect(allPoints.concat([currentPoints])); - } else { - detect(allPoints); - } - } - function detect(lassos) { - if (lassos == undefined) {lassos = allPoints;} - objectContainer.selectAll(objectClass).each(function(d, i){ - var current = d3.select(this), - box = current.absolutePosition(), - // box = current.relativePositionTo(objectContainer.node()), + function makeNewGroup(d, i) { - boxPts = [ - [ - box.left - chartOffset[0] - objectsOffset[0], - box.top - chartOffset[1] - objectsOffset[1] - ], - [ - box.right - chartOffset[0] - objectsOffset[0], - box.top - chartOffset[1] - objectsOffset[1] - ], - [ - box.left - chartOffset[0] - objectsOffset[0], - box.bottom - chartOffset[1] - objectsOffset[1] - ], - [ - box.right - chartOffset[0] - objectsOffset[0], - box.bottom - chartOffset[1] - objectsOffset[1] - ] - ]; + var tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')), + panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')), + n = newGroupNumber(); - if (xScale != undefined) { - boxPts[0][0] = xScale.invert(boxPts[0][0]); - boxPts[1][0] = xScale.invert(boxPts[1][0]); - boxPts[2][0] = xScale.invert(boxPts[2][0]); - boxPts[3][0] = xScale.invert(boxPts[3][0]); - } - if (yScale != undefined) { - boxPts[0][1] = yScale.invert(boxPts[0][1]); - boxPts[1][1] = yScale.invert(boxPts[1][1]); - boxPts[2][1] = yScale.invert(boxPts[2][1]); - boxPts[3][1] = yScale.invert(boxPts[3][1]); - } + if (tabs.selectAll('.'+utils.str.hypenate(namespace,'tab')).size() == maxNumberOfGroups) { + onError('only '+maxNumberOfGroups+' allowed.', 'warning'); + return + } + var + tab = insertTab(tabs, n), + pane = makeTabPane(panes, n); - /* - flag needed as we have to test multiple lasso segments, and if the point - is not in one segment, it does not mean it is not in any - */ - var inAnyLassoQ = false; - for (var i = 0; i < lassos.length; i++) { - var lassoPoints = lassos[i]; - // .map(function(pt){ - // var x, y = pt - // if (xScale!=undefined) {x = xScale(x)} - // if (yScale!=undefined) {y = yScale(y)} - // return [x, y] - // }) - var boxInLassoQ = boxPts.every(coord => d3.polygonContains(lassoPoints, coord)); + populatePane(pane, n); + togglePaneById(pane.attr('id')); - if (boxInLassoQ) { inAnyLassoQ = true; } // only update flag in the positive case. - } + } - current.classed('in-lasso', inAnyLassoQ); - current.classed('in-lasso-'+instance, inAnyLassoQ); + function newGroupNumber() { + var tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list'));//, + var + n = tabs.selectAll('li').size() - 3, // minus 1 for plus tab + s = 'Group ' + n, + gs = []; + tabs.each(function(d, i){ return gs.push(d3.select(this).select("a").text()) }); + while (gs.includes(s)) { n+=1; s = 'Group ' + n; } + return n + } + + function updateTabAndPaneNumbers() { + var + tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')), + panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')); + + tabs = tabs.selectAll('.'+utils.str.hypenate(namespace,'tab')); + panes = panes.selectAll('.'+utils.str.hypenate(namespace,'tab', 'pane')); + + tabs.each(function(d, i){ + d3.select(this).datum(i) + .attr("id", utils.str.hypenate(namespace,'tab',i)) + .select('a') + .attr('href', '#'+utils.str.hypenate(namespace,'tab','pane',i)) + .text(function(dd, ii){ + var curText = d3.select(this).text(); + if (curText.split(' ')[0] == 'Group') { + return 'Group ' + i + } + return curText + }); + }); + + panes.each(function(d, i){ + d3.select(this).datum(i) + .attr('id', utils.str.hypenate(namespace,'tab','pane',i)); + utils.sel.safeSelect(d3.select(this), 'p', 'lead') + .text(function(dd, ii){ + var curText = d3.select(this).text(); + if (curText.split(' ')[0] == 'Group') { + return 'Group ' + i + } + return curText + }); + utils.sel.safeSelect(d3.select(this), 'button', 'remove-btn') + .on('click', removeBtnClickToRemove); }); - updateObjects(); - return objectContainer.selectAll('.in-lasso-'+instance) } + function gatherDataLists() { + var tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')); + var panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')); + var tables = panes.selectAll('.'+utils.str.hypenate(namespace, "data-table")); + var data = {}; + var textGroups = tabs.selectAll('li.'+utils.str.hypenate(namespace,'tab') + ' > a') + .nodes().map(function(d, i){return d3.select(d).text()}); - function updateObjects() { - objectContainer.selectAll(objectClass).each(function(d, i) { - var t = d3.select(this); - applyObjectAttributes(t, t.classed('in-lasso')); + textGroups.map(function(e, i){ + data[e] = []; }); - } - function applyObjectAttributes(obj, setQ) { - var - preLassoFill = obj.attr('_pre_lasso_fill'), - preLassoStroke = obj.attr('_pre_lasso_stroke'), - preLassoStrokeWidth = obj.attr('_pre_lasso_stroke-width'); - if (setQ) { - obj.classed("in-lasso", true); - obj.classed('in-lasso-'+instance, true); - if (preLassoFill == undefined) { obj.attr('_pre_lasso_fill', obj.attr('fill')); } - if (preLassoStroke == undefined) { obj.attr('_pre_lasso_stroke', obj.attr('stroke')); } - if (preLassoStrokeWidth == undefined) { obj.attr('_pre_lasso_stroke-width', obj.attr('stroke-width')); } + tables.each(function(d, i){ + var table = d3.select(this).select("tbody"); + data[textGroups[i]] = table.selectAll('tr').data(); + }); - obj - //BUG: when .raise() - .attr('fill', lassoedFill) - .attr('stroke', lassoedStroke) - .attr('stoke-width', lassoedStrokeWidth); + return data + } - } else { - obj.classed("in-lasso", false); - obj.classed('in-lasso-'+instance, false); - if (preLassoFill != undefined) { obj.attr('fill', preLassoFill); } - if (preLassoStroke != undefined) { obj.attr('stroke', preLassoStroke); } - if (preLassoStrokeWidth != undefined) { obj.attr('stroke-width', preLassoStrokeWidth); } + function showRemainingPanes() { + var + tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')), + panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')), + remainingTabs = tabs.selectAll('.'+utils.str.hypenate(namespace,'tab')); + + if (remainingTabs.size() == 0) { + panes.select('.'+utils.str.hypenate(namespace,'default-tab')) + .classed("active", true) + .classed('text-left', true); + } + else { + var lastTab = remainingTabs.nodes()[remainingTabs.size()-1], + lastPaneId = d3.select(lastTab).select('a').attr('href'); + panes.select(lastPaneId) + .classed("active", true) + .classed('text-left', true); } } - function keyFrames() { - var style = - d3.select("html").select('style.'+utils$1.str.hypenate(namespace,"lasso-dash")); - if (style.empty()) { - d3.select("html").append('style') - .classed(utils$1.str.hypenate(namespace,"lasso-dash"), true) - .html("@keyframes lassoDash {to { stroke-dashoffset: 1000;}}"); - } - } - return lasso + return lassoWidget } /******************************************************************************* @@ -8064,14 +9386,14 @@ function multiPlotZoom( chart ) { function setLocks() { - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); - var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')); var cos = chartObjSel.attr('transform', 'translate(0,0)'); xLock = chartSel.node().getBBox().width - chart.spaceX();// * .9 yLock = chartSel.node().getBBox().height - chart.spaceY();// * .9 cos.attr('transform', 'translate('+chartObjTrans[0]+','+chartObjTrans[1]+')'); - utils$1.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); + utils.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); } @@ -8131,20 +9453,20 @@ function multiPlotZoom( chart ) { - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); var xComponentObjSel = xComponentsSel.map(function(d, i){ - return d.select('.'+utils$1.str.hypenate(xComponents[i].namespace(),'object-container')) + return d.select('.'+utils.str.hypenate(xComponents[i].namespace(),'object-container')) }); var yComponentObjSel = yComponentsSel.map(function(d, i){ - return d.select('.'+utils$1.str.hypenate(yComponents[i].namespace(),'object-container')) + return d.select('.'+utils.str.hypenate(yComponents[i].namespace(),'object-container')) }); - var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')); var xComponentsObjTrans = xComponentObjSel.map(function(d, i){ - return utils$1.sel.getTranslation(d.attr('transform')) + return utils.sel.getTranslation(d.attr('transform')) }); var yComponentsObjTrans = yComponentObjSel.map(function(d, i){ - return utils$1.sel.getTranslation(d.attr('transform')) + return utils.sel.getTranslation(d.attr('transform')) }); var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0; @@ -8169,9 +9491,9 @@ function multiPlotZoom( chart ) { zoom.reset = function() { - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); - var xAxisObjSel = xAxisSel.select('.'+utils$1.str.hypenate(xAxis.namespace(),'object-container')); - var yAxisObjSel = yAxisSel.select('.'+utils$1.str.hypenate(yAxis.namespace(),'object-container')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); + var xAxisObjSel = xAxisSel.select('.'+utils.str.hypenate(xAxis.namespace(),'object-container')); + var yAxisObjSel = yAxisSel.select('.'+utils.str.hypenate(yAxis.namespace(),'object-container')); chartObjSel.attr('transform', 'translate('+0+','+0+')'); xAxisObjSel.attr('transform', 'translate('+0+','+0+')'); yAxisObjSel.attr('transform', 'translate('+0+','+0+')'); @@ -8302,13 +9624,13 @@ function plotZoom( chart, xAxis, yAxis ) { function setLocks() { - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); - var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')); var cos = chartObjSel.attr('transform', 'translate(0,0)'); xLock = chartSel.node().getBBox().width - chart.spaceX() * .9; yLock = chartSel.node().getBBox().height - chart.spaceY() * .9; cos.attr('transform', 'translate('+chartObjTrans[0]+','+chartObjTrans[1]+')'); - utils$1.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); + utils.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); } @@ -8367,9 +9689,9 @@ function plotZoom( chart, xAxis, yAxis ) { - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); - var xAxisObjSel = xAxisSel.select('.'+utils$1.str.hypenate(xAxis.namespace(),'object-container')); - var yAxisObjSel = yAxisSel.select('.'+utils$1.str.hypenate(yAxis.namespace(),'object-container')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); + var xAxisObjSel = xAxisSel.select('.'+utils.str.hypenate(xAxis.namespace(),'object-container')); + var yAxisObjSel = yAxisSel.select('.'+utils.str.hypenate(yAxis.namespace(),'object-container')); // xLock = chartSel.node().getBBox().width - chart.spaceX() - chartSel.node().getBBox().x // yLock = chartSel.node().getBBox().height - chart.spaceY() @@ -8377,9 +9699,9 @@ function plotZoom( chart, xAxis, yAxis ) { // bhm.selection().node().getBBox().width - bhm.spaceX() - var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); - var xAxisObjTrans = utils$1.sel.getTranslation(xAxisObjSel.attr('transform')); - var yAxisObjTrans = utils$1.sel.getTranslation(yAxisObjSel.attr('transform')); + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')); + var xAxisObjTrans = utils.sel.getTranslation(xAxisObjSel.attr('transform')); + var yAxisObjTrans = utils.sel.getTranslation(yAxisObjSel.attr('transform')); var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0; @@ -8401,9 +9723,9 @@ function plotZoom( chart, xAxis, yAxis ) { zoom.reset = function() { - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); - var xAxisObjSel = xAxisSel.select('.'+utils$1.str.hypenate(xAxis.namespace(),'object-container')); - var yAxisObjSel = yAxisSel.select('.'+utils$1.str.hypenate(yAxis.namespace(),'object-container')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); + var xAxisObjSel = xAxisSel.select('.'+utils.str.hypenate(xAxis.namespace(),'object-container')); + var yAxisObjSel = yAxisSel.select('.'+utils.str.hypenate(yAxis.namespace(),'object-container')); chartObjSel.attr('transform', 'translate('+0+','+0+')'); xAxisObjSel.attr('transform', 'translate('+0+','+0+')'); yAxisObjSel.attr('transform', 'translate('+0+','+0+')'); @@ -8412,11 +9734,297 @@ function plotZoom( chart, xAxis, yAxis ) { return zoom } +function selectFilter(selection) { + + var + data, + namespace = 'd3sm-select-filter', + selectionName = 'Select options:', + defaultValue = undefined; + + + + + var lastValue = undefined; + + selectFilter.data = function(_) { return arguments.length ? (data = _, selectFilter) : data}; + selectFilter.namespace = function(_) { return arguments.length ? (namespace = _, selectFilter) : namespace}; + selectFilter.selectionName = function(_) { return arguments.length ? (selectionName = _, selectFilter) : selectionName}; + selectFilter.defaultValue = function(_) { return arguments.length ? (defaultValue = _, selectFilter) : defaultValue}; + selectFilter.currentOption = currentOption; + + function selectFilter() { + var + container = utils.sel.safeSelect(selection, 'div', 'input-group').classed(utils.str.hypenate(namespace,'container'),true), + + selectPrepend = utils.sel.safeSelect(container, 'div', 'select-prepend').classed('input-group-prepend', true), + selectPrependSpan = utils.sel.safeSelect(selectPrepend, 'span', 'input-group-text').text(selectionName), + + select = utils.sel.safeSelect(container, 'select', 'custom-select').classed(utils.str.hypenate(namespace,'select'),true), + + selectAppend = utils.sel.safeSelect(container, 'div', 'select-append').classed('input-group-prepend', true), + selectAppendButton = utils.sel.safeSelect(selectAppend, 'a', 'filter-button').classed('btn btn-outline-secondary', true), + filterButtonIcon = utils.sel.safeSelect(selectAppendButton, 'i', 'fa fa-filter'), + + inputGroup = utils.sel.safeSelect(container, 'div', 'filter-input-group').classed('input-group',true).classed('d-none', true), + inputPrepend = utils.sel.safeSelect(inputGroup, 'div', 'input-group-prepend'), + inputPrependSpan = utils.sel.safeSelect(inputPrepend, 'span', 'input-group-text').classed('search-button', true), + inputPrependSpanIcon = utils.sel.safeSelect(inputPrependSpan,'i','fa fa-search'), + + input = utils.sel.safeSelect(inputGroup, 'input', 'form-control').attr('placeholder', 'all').attr('type', 'text'), + inputAppend = utils.sel.safeSelect(inputGroup, 'div', 'input-group-append'), + inputAppendButton = utils.sel.safeSelect(inputAppend, 'a', 'close-button').classed('btn btn-outline-secondary', true), + inputAppendButtonIcon = utils.sel.safeSelect(inputAppendButton, 'i', 'fa fa-close'); + + + var keys = d3.keys(data), + options = select.selectAll('option'); + + options = options.data(d3.keys(data)); + options = options.merge(options.enter().append('option')) + .attr('value', function(d, i){return d}) + .text(function(d, i){return d}); + + var + filterButton = selectAppendButton, + closeButton = inputAppendButton; + + filterButton.on('click', function(d, i){ + var currentStyle = inputGroup.classed('d-none'); + inputGroup.classed('d-none', !currentStyle); + }); + + closeButton.on('click', function(d, i){ + input.property('value', '').dispatch('input'); + }); + + input.on('input', function(d, i){ + var + val = input.property('value'), + reg = new RegExp(val, 'gi'), + use; + + if (val == '') {use = keys;} + else { + use = []; + d3.keys(data).map(function(option, j){ + var match = option.match(reg); + if (match == null || match.join('') == '') ; + else { use.push(option); } + }); + } + + options = select.selectAll('option'); + options = options.data(use); + options.exit().remove(); + options = options.merge(options.enter().append('option')) + .attr('value', function(d, i){return d}) + .text(function(d, i){return d}); + + var current = currentOption(); + if (lastValue != current) { + lastValue = current; + select.dispatch('change'); + } + }); + + + } + + function currentOption() { + var val = selection.select("select").property('value'); + return val == undefined || val == '' + ? defaultValue == undefined + ? d3.keys(data)[0] + : defaultValue + : val + } + + return selectFilter +} + +/******************************************************************************* +** ** +** ** +** DATATOGGLE ** +** ** +** ** +*******************************************************************************/ +/** + * Creates a datatoggle + * @constructor datatoggle + * @param {d3.selection} selection + * @namespace datatoggle + * @returns {function} datatoggle + */ +function datatoggle( selection ) { + var + /** + * Keys to make toggle-able options + * (see {@link datatoggle#keys}) + * @param {string[]} [keys=undefined] + * @memberof datatoggle# + * @property + */ + keys, + + /** + * What to do when a different key is clicked + * (see {@link datatoggle#updateFunction}) + * @param {function} [updateFunction=function(){}] + * @memberof datatoggle# + * @property + */ + updateFunction = function(){}, + /** + * Namespace for all items made by this instance of datatoggle + * @param {string} [namespace="d3sm-databar"] + * @memberof datatoggle# + * @property + */ + namespace='d3sm-databar', + /** + * Currently toggled key + * @param {string} [currentKey=undefined] + * @memberof datatoggle# + * @property + */ + currentKey, + + + + xAxisSelectQ = false, + xAxisOptions, + yAxisSelectQ = false, + yAxisOptions, + data; + toggle.xAxisSelectQ = function(_){return arguments.length ? (xAxisSelectQ = _, toggle) : xAxisSelectQ}; + toggle.yAxisSelectQ = function(_){return arguments.length ? (yAxisSelectQ = _, toggle) : yAxisSelectQ}; + toggle.xAxisOptions = function(_){return arguments.length ? (xAxisOptions = _, toggle) : xAxisOptions}; + toggle.yAxisOptions = function(_){return arguments.length ? (yAxisOptions = _, toggle) : yAxisOptions}; + toggle.data = function(_){return arguments.length ? (data = _, toggle) : data}; + toggle.keys = function(_){return arguments.length ? (keys = _, toggle) : keys}; + toggle.currentKey = function(_){return arguments.length ? (currentKey = _, toggle) : currentKey}; + + /** + * Gets / sets the updateFunction + * (see {@link datatoggle#updateFunction}) + * @function datatoggle.updateFunction + * @param {function} [_=none] + * @returns {datatoggle | function} + * @memberof datatoggle + * @property + * by default updateFunction = function(){} + */ + toggle.updateFunction = function(_){return arguments.length ? (updateFunction = _, toggle) : updateFunction}; + /** + * Gets / sets the namespace + * (see {@link datatoggle#namespace}) + * @function datatoggle.namespace + * @param {string} [_=none] + * @returns {datatoggle | string} + * @memberof datatoggle + * @property + * by default namespace = 'd3sm-databar' + */ + toggle.namespace = function(_){return arguments.length ? (namespace = _, toggle) : namespace}; + /** + * Gets / sets the currentKey + * (see {@link datatoggle#currentKey}) + * @function datatoggle.currentKey + * @param {string} [_=none] + * @returns {datatoggle | string} + * @memberof datatoggle + * @property + * by default currentKey = undefined + */ + toggle.currentKeys = + function () { + var vals = {}; + d3.keys(filterSelects).map(function(k, i){ + vals[k]= filterSelects[k].currentOption(); + }); + return vals + }; + + var filterSelects = {}; + function toggle() { + // selection options + + // selection.classed('d-flex flex-row', true) + // var filterButton = utils.sel.safeSelect(selection, 'a', 'slider-buttons') + // var filterI = utils.sel.safeSelect(filterButton, 'i', 'fa fa-sliders') + + /*BUG: unexpected behavior. + - when using bootstrap-eque way for collapse, clicking button submits to + the same page in applications (but not in demo), so using anchor () + - when using anchor, cause page to jump to that location + - when using show, the first open works, but the close does not. + */ + // filterButton.attr('class', 'btn btn-secondary') + // .attr('data-toggle', 'collapse') + // .attr('href', '#'+utils.str.hypenate(namespace, 'data-toggle')) + // .attr('target', '_blank') + // .html(filterButton.html()+' Filters') + // .on('click', function(d, i){ + // d3.event.preventDefault() + // d3.event.stopPropagation() + // var dt = d3.select("#"+utils.str.hypenate(namespace, 'data-toggle')) + // dt.classed('show', !dt.classed('show')) + // dt.classed('d-inline-flex', dt.classed('show')) + // + // filterButton.classed('btn-primary', dt.classed('show')) + // filterButton.classed('btn-secondary', !dt.classed('show')) + // }) + // .classed('d-inline-flex', true) + // .style("margin-right", '10px') + + + + // var datatoggleCollapse = utils.sel.safeSelect(selection, 'div', utils.str.hypenate(namespace,'collapse')) + // .attr('id', utils.str.hypenate(namespace, 'data-toggle')) + // .classed('collapse', true) + + // var flexRow = utils.sel.safeSelect(datatoggleCollapse, 'div', 'd-inline-flex flex-row flex-wrap') + var flexRow = utils.sel.safeSelect(selection, 'div', 'd-inline-flex flex-row flex-wrap'); + + var dataopts = flexRow.selectAll('div.'+utils.str.hypenate(namespace,'select-filter')); + // remove excess + dataopts.exit().remove(); + // bind data + dataopts = dataopts.data(d3.keys(data)); + //enter + var doEnter = dataopts.enter().append('div') + .attr('class', 'select-filter'); + + dataopts = dataopts.merge(doEnter).style('margin-right', "10px"); + + dataopts.each(function(d, i){ + var t = d3.select(this); + var sf = selectFilter(t) + .data(data[d]) + .namespace(utils.str.hypenate(namespace, d)) + .selectionName(d); + sf(); + filterSelects[d] = sf; + }); + + + selection.selectAll('select') + .on('change', function(){updateFunction();}); + // bind update function + // d3.selectAll + return toggle + } + + return toggle +} + let aux = { - lasso, plotZoom, multiPlotZoom + lasso, plotZoom, multiPlotZoom, datatoggle, selectFilter, lassoWidget }; -let d3sm$1 = { +let d3sm = { aux, axis, charts, @@ -8425,11 +10033,11 @@ let d3sm$1 = { groupingSpacer, legends, tooltip, - utils: utils$1, + utils, }; if (typeof window !== 'undefined') { - window.d3sm = d3sm$1; + window.d3sm = d3sm; } -export default d3sm$1; +export default d3sm; diff --git a/dist/d3sm.min.js b/dist/d3sm.min.js index 283cfaa..78dd43f 100644 --- a/dist/d3sm.min.js +++ b/dist/d3sm.min.js @@ -1 +1 @@ -!function(t){"use strict";function e(t,e,n){return n.indexOf(t)===e}function n(t,e){return void 0==e?t.every(function(t){return!0===t}):t.every(function(t){return e(t)})}function r(t){var e={};return t.map(function(t){o(Object.keys(e),t)?e[t]=1:e[t]+=1}),e}function o(t,e){return t.includes(e)}function a(t){return t[0]}function i(t){return t[t.length-1]}function l(t){return t.reduce((t,e)=>t+e,0)}function c(t){return t.filter(e)}function u(t,e){return t.filter(function(t,n){return o(e,n)})}function s(t){return n(t.map(function(t,e){return Array.isArray(t)}))}function h(t,e){return s(t)?e.map(function(e,n){return t.get(e)}):u(t,e)}function f(t,e,n){void 0==n&&(n=c(t.map(function(t,n){return element[e]}))).map(function(t,e){r[t]=[]});var r={};return t.map(function(t,n){r[t[e]].push(t)}),r}function d(t,e){if(!e)return!1;if(t.length!=e.length)return!1;for(var n=0,r=t.length;n=n.length?n.push(t.length):n[e]+=t.length,t.map(function(t,r){Array.isArray(t)&&g(t,e,n)}),n}function p(t,e){return e=void 0==e?0:e,t.map(function(t,n){Array.isArray(t)?e=p(t,e):e+=1}),e}function m(t,e){return e=void 0==e?[]:e,t.map(function(t,n){Array.isArray(t)?e=e.concat(m(t)):e.push(t)}),e}function v(t,e){for(var n=0;ntt>n),a=void 0==(a=d3.median(r))?n:a,i=void 0==(i=d3.min(r))?a:i,l=void 0==(l=d3.median(o))?n:l,c=void 0==(c=d3.max(o))?l:c,u="q0",s="q1",h="q2",f="q3",d="q4",g={};return void 0!=e&&5==e.length&&(u=e[0],s=e[1],h=e[2],f=e[3],d=e[4]),g[u]=i,g[s]=a,g[h]=n,g[f]=l,g[d]=c,g}function z(t,e,n,r,o,a){var i=t-(e-1)*(o=0==o||o>1?o:t*o),l=(i=i<0?0:i)/e;return a&&void 0!=n&&l=n.length?n.push(t.length-1):n[e]+=t.length-1,t.map(function(t,r){Array.isArray(t)&&A(t,e,n)}),n}function C(t,e){var n=E(function(){t()},e);window.addEventListener("resize",n)}function E(t,e,n){var r;return function(){var o=this,a=arguments,i=function(){r=null,n||t.apply(o,a)},l=n&&!r;clearTimeout(r),r=setTimeout(i,e),l&&t.apply(o,a)}}function L(t,e,n,r={top:.01,bottom:.01,left:.01,right:.01},o={w:.8,h:.6},a={y:.1,x:.1},i={x:0,margin:0,pos:"left"}){void 0==n&&(n={w:window.innerWidth,h:window.Height});var l={w:n.w*o.w,h:n.h*o.h},c={top:r.top*l.h,bottom:r.bottom*l.h,left:r.left*l.w,right:r.right*l.w},u={w:l.w-c.left-c.right,h:l.h-c.top-c.bottom},s={x:u.h*a.x,y:u.w*a.y},h={x:u.w-s.y-i.x-2*i.margin,y:u.h-s.x},f={x:i.margin+c.left+("left"==i.pos?0:h.x+s.y),y:c.top,w:i.x,h:h.y},d={x:s.y+c.left+("left"==i.pos?i.x+2*i.margin:0),y:c.top,w:s.y,h:h.y},g={x:s.y+c.left+("left"==i.pos?i.x+2*i.margin:0),y:c.top,w:h.x,h:h.y},p={x:s.y+c.left+("left"==i.pos?i.x+2*i.margin:0),y:c.top+h.y,w:h.x,h:s.x};n=d3sm.safeSelect(t,"svg",e).style("width",l.w+"px").style("height",l.h+"px");var a=d3sm.safeSelect(n,"g",d3sm.hypenate(e,"axes")),i=d3sm.safeSelect(n,"g",d3sm.hypenate(e,"legend")).attr("transform","translate("+f.x+","+f.y+")");return{svg:{selection:n,rect:l},plot:{selection:d3sm.safeSelect(n,"g",d3sm.hypenate(e,"plot")).attr("transform","translate("+g.x+","+g.y+")"),rect:g},xAxis:{selection:d3sm.safeSelect(a,"g",d3sm.hypenate(e,"x-axis")).attr("transform","translate("+p.x+","+p.y+")"),rect:p},yAxis:{selection:d3sm.safeSelect(a,"g",d3sm.hypenate(e,"y-axis")).attr("transform","translate("+d.x+","+d.y+")"),rect:d},legend:{selection:i,rect:f}}}function M(){return Array.prototype.slice.call(arguments).join("-")}function F(t,e,n,r,o,a){var i=t.node().getBoundingClientRect();for(t.text(e);Math.max(i.width,i.height)>o-r&&(e=String(e),e=e.slice(0,e.length-1),t.text(e+"..."),i=t.node().getBoundingClientRect(),0!=e.length););}function B(t,e,n){var r=e/n;return r g.'+l+"."+s);"function"==typeof f?v.each(function(t,e){f(d3.select(this))}):v.remove()}else{x+=n;var m=d.select("g."+s+'[level="'+g+'"] > g.'+l+"."+s);m.empty()&&(m=d.append("g").attr("class",l).classed(s,!0)),m.attr("parent-index",r);var v=d.selectAll("g."+s+'[level="'+(g+1)+'"]');"function"==typeof f?v.each(function(t,e){f(d3.select(this))}):v.remove()}x+=r==p.size()-1?0:y}),x}var e,n,r,o=!0,a=d3.scaleLinear(),i="category",l="d3sm-groupped-item",c=1e3,u=d3.easeSin,s="spacer",h=function(t){t.attr("transform",function(t,e){return"translate("+(o?window.outerWidth:0)+","+(o?0:window.outerWidth)+")"})},f=function(t){K("groupingSpacer","exiting with",{current:t,currentNode:t.node()}),t.selectAll("g").classed("to-remove",!0),t.transition().duration(.9*c).ease(u).attr("transform",function(t,e){return"translate("+(o?window.outerWidth:0)+","+(o?0:window.outerWidth)+")"}).remove()};return t.horizontalQ=function(e){return arguments.length?(o=e,t):o},t.scale=function(e){return arguments.length?(a=e,t):a},t.moveby=function(e){return arguments.length?(i=e,t):i},t.numberOfObjects=function(n){return arguments.length?(e=n,t):e},t.objectClass=function(e){return arguments.length?(l=e,t):l},t.objectSize=function(e){return arguments.length?(n=e,t):n},t.spacerSize=function(e){return arguments.length?(r=e,t):r},t.transitionDuration=function(e){return arguments.length?(c=e,t):c},t.easeFunc=function(e){return arguments.length?(u=e,t):u},t.namespace=function(e){return arguments.length?(s=e,t):s},t.enterFunction=function(e){return arguments.length?(h=e,t):h},t.exitFunction=function(e){return arguments.length?(f=e,t):f},t}function G(t){function e(){function e(t,e,n,r,o){return n&&r?o/2:0}function W(t,e,n,r,o){return n&&r?o/2:0}var D=!!gt.arr.hasQ(["top","bottom","horizontal"],d),R=!D,G={x:0,y:0,width:g,height:p};"left"==d&&(G.x-=g,y&&(G.width+=u),G.y-=V,G.height+=2*V),"bottom"==d&&(G.y=G.y,y&&(G.y-=u,G.height+=u),G.x-=V,G.width+=2*V),"top"==d&&(G.y-=p,y&&(G.height+=u),G.y-=V,G.height+=2*V),"right"==d&&(G.x=0,y&&(G.width+=u,G.x-=u),G.y-=V,G.height+=2*V);var Z=gt.sel.setupContainer(t,z,G,j);"top"==d&&(l=void 0==l?"start":l,c=void 0==c?-90:c),"bottom"==d&&(l=void 0==l?"end":l,c=void 0==c?-90:c),"left"==d&&(l=void 0==l?"end":l,c=void 0==c?0:c),"right"==d&&(l=void 0==l?"start":l,c=void 0==c?0:c);var I=v?void 0==o?a:o:void 0==o?void 0!=A?gt.math.tickRange(...d3.extent(i),A):i:o,J=gt.arr.flatten(I),U=J.length,$=D?g:p,tt=d3.extent(J);N&&tt.reverse();var et=N?[tt[0]+b,tt[1]-b]:[tt[0]-b,tt[1]+b];x.domain(et).range([D?0:p,D?g:0]),s=void 0==s?gt.math.calculateWidthOfObject($,U,w,S,k,m):s,h=void 0==h?gt.math.calculateWidthOfSpacer(J,$,s,U,k,m):h;var nt=gt.str.hypenate(z,v?O+"-categorical":O),rt=H().horizontalQ(D).scale(x).moveby(v?"category":"scale").numberOfObjects(U).objectClass(nt).objectSize(s).spacerSize(h).transitionDuration(X).easeFunc(P).namespace(z),ot=function(t){var e=x(t.datum()),n=2*x(tt[1]),r=ewindow.innerWidth&&o.style("left",d3.event.pageX-15-300+"px")}function r(t,e){var n=d3.select(this).style("fill","black");if(d3.select(n.node().parentNode).select("line."+gt.str.hypenate(z,"tick")).attr("stroke",L).attr("stroke-width",M),y){var r=d3.select(n.node().parentNode).select("line."+gt.str.hypenate(z,"guideline")),o=r.attr("minor");r.attr("stroke",function(t,e){return"true"==o?gt.color.modifyHexidecimalColorLuminance(Y,.8):Y}).attr("stroke-width",function(t,e){return"true"==o?.8*K:K})}d3.select("#"+gt.str.hypenate(z,"guideline-tooltip")).remove()}var o,a,i,l,c,u,s,h,f,d="bottom",g=0,p=0,m=!1,v=!1,y=!1,x=d3.scaleLinear(),b=.5,k=.05,w=15,S=50,j="transparent",z="d3sm-axis",O="tick-group",A=5,C="black",E=3,L="black",M=2,F=10,B=10,Q=10,q=14,W=8,V=20,D=void 0,T=function(t,e){},R=function(t,e){return String(t).replace("-"," ").replace("_"," ")},Y="#333333",K=2,X=1e3,P=d3.easeExp,_=2,N=!1;return e.label=function(t){return arguments.length?(f=t,e):f},e.tickTickLabelSpacer=function(t){return arguments.length?(B=t,e):B},e.tickLabelMargin=function(t){return arguments.length?(Q=t,e):Q},e.selection=function(n){return arguments.length?(t=n,e):t},e.orient=function(t){return arguments.length?(d=t,e):d},e.spaceX=function(t){return arguments.length?(g=t,e):g},e.spaceY=function(t){return arguments.length?(p=t,e):p},e.overflowQ=function(t){return arguments.length?(m=t,e):m},e.categoricalQ=function(t){return arguments.length?(v=t,e):v},e.guideLinesQ=function(t){return arguments.length?(y=t,e):y},e.grouping=function(t){return arguments.length?(o=t,e):o},e.scale=function(t){return arguments.length?(x=t,e):x},e.domainPadding=function(t){return arguments.length?(b=t,e):b},e.objectSpacer=function(t){return arguments.length?(k=t,e):k},e.minObjectSize=function(t){return arguments.length?(w=t,e):w},e.maxObjectSize=function(t){return arguments.length?(S=t,e):S},e.namespace=function(t){return arguments.length?(z=t,e):z},e.backgroundFill=function(t){return arguments.length?(j=t,e):j},e.objectClass=function(t){return arguments.length?(O=t,e):O},e.tickLabels=function(t){return arguments.length?(a=t,e):a},e.tickValues=function(t){return arguments.length?(i=t,e):i},e.numberOfTicks=function(t){return arguments.length?(A=t,e):A},e.lineStroke=function(t){return arguments.length?(C=t,e):C},e.lineStrokeWidth=function(t){return arguments.length?(E=t,e):E},e.tickStroke=function(t){return arguments.length?(L=t,e):L},e.tickStrokeWidth=function(t){return arguments.length?(M=t,e):M},e.tickLength=function(t){return arguments.length?(F=t,e):F},e.tickLabelFontSize=function(t){return arguments.length?(q=t,e):q},e.tickLabelMinFontSize=function(t){return arguments.length?(W=t,e):W},e.tickLabelMaxFontSize=function(t){return arguments.length?(V=t,e):V},e.tickLabelTextAnchor=function(t){return arguments.length?(l=t,e):l},e.tickLabelRotation=function(t){return arguments.length?(c=t,e):c},e.tickLabelFunc=function(t){return arguments.length?(D=t,e):D},e.tickLabelOnClick=function(t){return arguments.length?(T=t,e):T},e.guidelineSpace=function(t){return arguments.length?(u=t,e):u},e.guideLineStroke=function(t){return arguments.length?(Y=t,e):Y},e.guideLineStrokeWidth=function(t){return arguments.length?(K=t,e):K},e.transitionDuration=function(t){return arguments.length?(X=t,e):X},e.easeFunc=function(t){return arguments.length?(P=t,e):P},e.objectSize=function(t){return arguments.length?(s=t,e):s},e.spacerSize=function(t){return arguments.length?(h=t,e):h},e.roundTo=function(t){return arguments.length?(_=t,e):_},e.reverseScaleQ=function(t){return arguments.length?(N=t,e):N},e.tickLabelOnHoverFunc=function(t){return arguments.length?(R=t,e):R},e}function Z(t){function e(){t.on("mouseover",n),t.on("mousemove",n),t.on("mouseout",function(){d3.selectAll(".d3sm-tooltip").remove()})}function n(t,e){R("d3sm-tooltip");var n=i[t],[l,c]=d3.mouse(d3.select("html").node());K("tooltip","mousemove detected",{key:t,index:e,x:l,y:c}),K("tooltip","current data",n);var u=W(d3.select("html"),"tooltip","d3sm-tooltip").classed("card",!0).style("max-width","300px").style("background-color","#212529").style("color","white"),s=W(u,"div","card-body"),h=(W(s,"h5","card-title").text(void 0==a?t:"function"==typeof a?a(t,n,e):a).style("color","cyan"),W(W(s,"table","table").classed("table-dark",!0),"tbody"));(h=(h=h.selectAll("tr")).data(void 0==r?d3.keys(n):r)).exit().remove();var f=h.enter().append("tr").style("max-width","300px");f.append("td").attr("class",function(t,e){return"tooltip-key"}),f.append("td").attr("class",function(t,e,n){return"tooltip-value"}).attr("tooltip-row-index",function(t,e){return e}),R("tooltip-rows"),h.selectAll(".tooltip-key").text(function(t,e){return t}),h.selectAll("tr .tooltip-value").text(function(t,e){K("tooltip","trying to set value",{rowKey:t,rowIndex:e});var e=d3.select(this).attr("tooltip-row-index"),r=n[t];return void 0!=o&&"function"==typeof(r=o[e])&&(r=r(n,t)),"number"==typeof r?b(r,5):r}),Y(),Y(),l+=15;var d=u.node().getBoundingClientRect();l+d.width>window.innerWidth-window.scrollX&&(l=d3.event.pageX-d.width-15),c+d.height>window.innerHeight-window.scrollY&&(c=d3.event.pageY-d.height-15),"relative"==u.style("position")?u.style("position","absolute").style("left",l+"px").style("top",c+"px"):u.style("left",l+"px").style("top",c+"px"),u.attr("z-index",1e4)}var r,o,a,i,t;return e.keys=function(t){return arguments.length?(r=t,e):r},e.values=function(t){return arguments.length?(o=t,e):o},e.header=function(t){return arguments.length?(a=t,e):a},e.data=function(t){return arguments.length?(i=t,e):i},e.selection=function(n){return arguments.length?(t=n,e):t},e}function I(){function t(t,r,o,u,d){var p,m="fill"==u?l:i;if(e(),"index"==c)p=void 0!=u?a(g(f(o)),m):g(f(o));else if("value"==c){y=s(t,r,o);p=void 0!=u?a(g(f(y)),m):g(f(y))}else if("category"==c){var v=h(t,r,o),y=n.indexOf(v);p=void 0!=u?a(g(f(y)),m):g(f(y))}else p=void 0!=u?a(g(f(o)),m):g(f(o));return p}function e(){d.domain([0,r.length]),"category"==c&&void 0!=n?d.range([0,n.length]):d.range(u);var t=Array(r.length).fill(0).map(function(t,e){return d(e)});f.domain(t)}var n,r=["#2c7bb6","#00a6ca","#00ccbc","#90eb9d","#ffff8c","#f9d057","#f29e2e","#e76818","#d7191c"],o=d3.interpolateRgb,a=y,i=0,l=.4,c="index",u=[0,r.length-1],s=function(t,e,n){return e},h=function(t,e,n){return e.category},f=d3.scaleLinear().interpolate(o).domain(u).range(r),d=d3.scaleLinear(),g=function(t){return"#"+t.match(/\d+/g).map(function(t,e){return(+t<16?"0":"")+(+t).toString(16)}).join("")};return t.colors=function(e){return arguments.length?(r=e,f.range(r),t):r},t.interpolation=function(e){return arguments.length?(o=e,f.interpolate(o).range(r),t):o},t.dataExtent=function(e){return arguments.length?(u=e,f.domain(u).interpolate(f.interpolate()),t):u},t.scale=function(e){return arguments.length?(e=e.domain(f.domain()).interpolate(f.interpolate()).range(f.range()),f=e,t):f},t.modifyOpacity=function(e){return arguments.length?(a=e,t):a},t.strokeOpacity=function(e){return arguments.length?(i=e,t):i},t.fillOpacity=function(e){return arguments.length?(l=e,t):l},t.colorBy=function(e){return arguments.length?(c=e,t):c},t.valueExtractor=function(e){return arguments.length?(s=e,t):s},t.categoryExtractor=function(e){return arguments.length?(h=e,t):h},t.categories=function(e){return arguments.length?(n=e,t):n},t}function J(t){function e(){var e={x:0,y:0,width:r,height:o},C=gt.sel.setupContainer(t,S,e,w);a=d3.keys(n),i=a.map(h),l=a.map(g),c=a.map(v);a.length;var E=[Math.min(...i)-s,Math.max(...i)+s],L=[Math.min(...l)-d,Math.max(...l)+d],M=[Math.min(...c)-m,Math.max(...c)+m];u.domain(E).range([0,r]),f.domain(L).range([o,0]),p.domain(M).range([y,x]);var F=C.selectAll("."+j),B=(F=F.data(a)).enter().append("circle").attr("class",j).attr("cx",0).attr("cy",o).attr("r",0),Q=F.exit();(F=F.merge(B)).each(function(t,e){var r=d3.select(this),o=n[t],a=i[e],s=l[e],h=c[e],d=k(t,o,e,"fill"),g=k(t,o,e,"stroke");r.transition().duration(z).ease(O).attr("cx",u(a)).attr("cy",f(s)).attr("r",p(h)).attr("fill",d).attr("stroke",g).attr("stroke-width",b),r.on("mouseover",function(t,e){F.style("opacity",.2),r.style("opacity",1),r.transition().duration(z/2).ease(O).attr("stroke-width",2*b).attr("r",1.5*p(h))}),r.node().addEventListener("mouseout",function(){C.selectAll("."+j).style("opacity",1),r.transition().duration(z/2).ease(O).attr("stroke-width",b).attr("r",p(h))})}),Q.transition().duration(z).ease(O).attr("cx",0).attr("cy",o).attr("r",0).remove(),A.selection(F).data(n),A()}var n,r,o,a,i,l,c,u=d3.scaleLinear(),s=.5,h=function(t,e){return n[t].x},f=d3.scaleLinear(),d=.5,g=function(t,e){return n[t].y},p=d3.scaleLinear(),m=.5,v=function(t,e){return 2},y=2,x=10,b=2,k=I(),w="transparent",S="d3sm-scatter",j="scatter-point",z=1e3,O=d3.easeExp,A=Z();return e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(n=t,e):n},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(o=t,e):o},e.scaleX=function(t){return arguments.length?(u=t,e):u},e.domainPaddingX=function(t){return arguments.length?(s=t,e):s},e.valueExtractorX=function(t){return arguments.length?(h=t,e):h},e.scaleY=function(t){return arguments.length?(f=t,e):f},e.domainPaddingY=function(t){return arguments.length?(d=t,e):d},e.valueExtractorY=function(t){return arguments.length?(g=t,e):g},e.scaleR=function(t){return arguments.length?(p=t,e):p},e.domainPaddingR=function(t){return arguments.length?(m=t,e):m},e.valueExtractorR=function(t){return arguments.length?(v=t,e):v},e.minRadius=function(t){return arguments.length?(y=t,e):y},e.maxRadius=function(t){return arguments.length?(x=t,e):x},e.pointStrokeWidth=function(t){return arguments.length?(b=t,e):b},e.colorFunction=function(t){return arguments.length?(k=t,e):k},e.backgroundFill=function(t){return arguments.length?(w=t,e):w},e.namespace=function(t){return arguments.length?(S=t,e):S},e.objectClass=function(t){return arguments.length?(j=t,e):j},e.transitionDuration=function(t){return arguments.length?(z=t,e):z},e.easeFunc=function(t){return arguments.length?(O=t,e):O},e.pointKeys=function(t){return arguments.length?(a=t,e):a},e.valuesX=function(t){return arguments.length?(i=t,e):i},e.valuesY=function(t){return arguments.length?(l=t,e):l},e.valuesR=function(t){return arguments.length?(c=t,e):c},e.tooltip=function(t){return arguments.length?(A=t,e):A},e}function U(t){function e(){var e="horizontal"==s||"bottom"==s||"top"==s,C=!e,E={x:0,y:0,width:r,height:o},L=gt.sel.setupContainer(t,w,E,k);i=d3.keys(n),l=i.map(f);var M=void 0==a?i.sort(d):a,F=(i=gt.arr.flatten(M)).length,B=[Math.min(...l)-p,Math.max(...l)+p];g.domain(B).range(e?[0,o]:"right"==s?[0,r]:[r,0]);var Q=e?r:o;c=void 0==c?gt.math.calculateWidthOfObject(Q,F,v,y,m,h):c,u=void 0==u?gt.math.calculateWidthOfSpacer(i,Q,c,F,m,h):u;var q=H().horizontalQ(e).scale(g).moveby("category").numberOfObjects(F).objectClass(S).objectSize(c).spacerSize(u).transitionDuration(j).easeFunc(z).namespace(w),W=q.exitFunction();q.exitFunction(function(t){void 0==c&&console.log(t.nodes(),c),W(t),t.selectAll("g").classed("to-remove",!0),t.selectAll("* > rect").transition().duration(j).attr("transform",function(t,e){return"translate(0,"+(C?0:g(B[1]))+")"}).attr("width",e?c:0).attr("height",C?c:0).remove()}),q(L,M,0);var V=[];L.selectAll("g:not(.to-remove)."+S).each(function(t,e){V.push(Number(d3.select(this).attr("parent-index")))}),b="index"==b.colorBy()?b.dataExtent([0,Math.max(...V)]):b.dataExtent(B),L.selectAll("g."+S+":not(.to-remove)").each(function(t,r){var o=d3.select(this),a=(n[t],f(t,r)),r=void 0==o.attr("parent-index")?r:o.attr("parent-index"),i=b(t,a,r,"fill"),l=b(t,a,r,"stroke"),u=gt.sel.safeSelect(o,"rect","bar-rect");void 0==u.attr("transform")&&u.attr("transform",function(t,e){return"translate(0,"+(C?0:g(B[1]))+")"}).attr("width",e?c:0).attr("height",C?c:0),u.transition().duration(j).ease(z).attr("transform",function(t,n){return"translate("+(e?c-c*A:"right"==s?g(B[1])-g(a):c-c*A)+","+(C?c-c*A:g(B[1])-g(a))+")"}).attr("width",e?c*A:g(a)).attr("height",C?c*A:g(a)).attr("fill",i).attr("stroke",l).attr("stroke-width",x),o.on("mouseover",function(t,e){L.selectAll("g."+S).style("opacity",.2),o.style("opacity",1),u.attr("stroke-width",2*x)}),o.on("mouseout",function(){L.selectAll("g."+S).style("opacity",1),u.attr("stroke-width",x)})}),O.selection(L.selectAll(".bar-rect")).data(n),O()}var n,r,o,a,i,l,c,u,s="horizontal",h=!1,f=function(t,e){return n[t]},d=function(t,e){return d3.descending(n[t],n[e])},g=d3.scaleLinear(),p=.5,m=.05,v=50,y=100,x=2,b=I(),k="transparent",w="d3sm-bar",S="bar",j=1e3,z=d3.easeExp,O=Z(),A=1;return e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(n=t,e):n},e.orient=function(t){return arguments.length?(s=t,e):s},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(o=t,e):o},e.overflowQ=function(t){return arguments.length?(h=t,e):h},e.grouping=function(t){return arguments.length?(a=t,e):a},e.valueExtractor=function(t){return arguments.length?(f=t,e):f},e.sortingFunction=function(t){return arguments.length?(d=t,e):d},e.scale=function(t){return arguments.length?(g=t,e):g},e.domainPadding=function(t){return arguments.length?(p=t,e):p},e.objectSpacer=function(t){return arguments.length?(m=t,e):m},e.minObjectSize=function(t){return arguments.length?(v=t,e):v},e.maxObjectSize=function(t){return arguments.length?(y=t,e):y},e.barStrokeWidth=function(t){return arguments.length?(x=t,e):x},e.colorFunction=function(t){return arguments.length?(b=t,e):b},e.backgroundFill=function(t){return arguments.length?(k=t,e):k},e.namespace=function(t){return arguments.length?(w=t,e):w},e.objectClass=function(t){return arguments.length?(S=t,e):S},e.transitionDuration=function(t){return arguments.length?(j=t,e):j},e.easeFunc=function(t){return arguments.length?(z=t,e):z},e.barKeys=function(t){return arguments.length?(i=t,e):i},e.barValues=function(t){return arguments.length?(l=t,e):l},e.objectSize=function(t){return arguments.length?(c=t,e):c},e.spacerSize=function(t){return arguments.length?(u=t,e):u},e.tooltip=function(t){return arguments.length?(O=t,e):O},e.barPercent=function(t){return arguments.length?(A=t,e):A},e}function $(t){function e(){var e={x:0,y:0,width:r,height:o},g=gt.sel.setupContainer(t,L,e,E);(a=d3.keys(n)).sort(function(t,e){return Q(t,e)||q(t,e)}),gt.con.log("bubbleHeatmap","cells are sorted by",a),i=gt.arr.unique(a.map(y)),l=gt.arr.unique(a.map(x)),c=gt.arr.unique(a.map(b)),u=gt.arr.unique(a.map(k)),gt.con.log("bubbleHeatmap","x and y keys are",{x:i,y:l});var p=i.length,m=l.length,v=[Math.min(...c)-j,Math.max(...c)+j];f=gt.math.calculateWidthOfObject(o,m,O,A,z,w),s=gt.math.calculateWidthOfObject(r,p,O,A,z,w),d=gt.math.calculateWidthOfSpacer(l,o,f,m,z,w),h=gt.math.calculateWidthOfSpacer(i,r,s,p,z,w),gt.con.log("bubbleHeatmap","size of",{x:s,y:f}),S.domain(v).range([Math.min(O/2,Math.min(f,s)/2),Math.min(f,s)/2]);var D=H().horizontalQ(!1).moveby("category").numberOfObjects(m).objectClass(gt.str.hypenate(M,"row")).objectSize(f).spacerSize(d).transitionDuration(F).easeFunc(B).namespace("row"),T=H().horizontalQ(!0).moveby("category").numberOfObjects(p).objectClass(M).objectSize(s).spacerSize(h).transitionDuration(F).easeFunc(B);D(g,l,0),g.selectAll("g."+gt.str.hypenate(M,"row")).each(function(t,e){T(d3.select(this),i,0)});var R=g.selectAll("g:not(.to-remove)."+M).data(a),Y=[];R.each(function(t,e){Y.push(Number(d3.select(this).attr("parent-index")))}),W="index"==W.colorBy()?W.dataExtent([0,Math.max(...Y)]):W.dataExtent([0,Math.max(...u)]),R.each(function(t,e){gt.con.log("bubbleHeatmap","each cell",{key:t,index:e,node:d3.select(this).node()});var r=d3.select(this),o=(n[t],k(t,e)),a=b(t,e),e=void 0==r.attr("parent-index")?e:r.attr("parent-index"),i=W(t,o,e,"fill"),l=W(t,o,e,"stroke");gt.con.log("bubbleHeatmap","radius",{radius:a,scaled:S(a),extent:v,range:S.range()}),gt.sel.safeSelect(r,"circle",gt.str.hypenate(M,"circle")).attr("cx",s/2).attr("cy",f/2).attr("r",S(a)).attr("fill",i).attr("stroke",l).attr("stroke-width",C)}),V.selection(R.selectAll("circle."+gt.str.hypenate(M,"circle"))).data(n),V()}var n,r,o,a,i,l,c,u,s,h,f,d,g="x",p="y",m="r",v="v",y=function(t,e){return n[t][g]},x=function(t,e){return n[t][p]},b=function(t,e){return n[t][m]},k=function(t,e){return n[t][v]},w=!1,S=d3.scaleLinear(),j=.5,z=0,O=50,A=100,C=2,E="transparent",L="d3sm-bubble",M="bubble",F=1e3,B=d3.easeExp,Q=function(t,e){return y(t)-y(e)},q=function(t,e){return x(t)-x(e)},W=I().colorBy("value"),V=Z();return e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(n=t,e):n},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(o=t,e):o},e.xKey=function(t){return arguments.length?(g=t,e):g},e.yKey=function(t){return arguments.length?(p=t,e):p},e.rKey=function(t){return arguments.length?(m=t,e):m},e.vKey=function(t){return arguments.length?(v=t,e):v},e.cellKeys=function(t){return arguments.length?(a=t,e):a},e.xValues=function(t){return arguments.length?(i=t,e):i},e.yValues=function(t){return arguments.length?(l=t,e):l},e.rValues=function(t){return arguments.length?(c=t,e):c},e.vValues=function(t){return arguments.length?(u=t,e):u},e.xExtractor=function(t){return arguments.length?(y=t,e):y},e.yExtractor=function(t){return arguments.length?(x=t,e):x},e.rExtractor=function(t){return arguments.length?(b=t,e):b},e.vExtractor=function(t){return arguments.length?(k=t,e):k},e.overflowQ=function(t){return arguments.length?(w=t,e):w},e.scale=function(t){return arguments.length?(S=t,e):S},e.domainPadding=function(t){return arguments.length?(j=t,e):j},e.objectSpacer=function(t){return arguments.length?z=t:n},e.minObjectSize=function(t){return arguments.length?(O=t,e):O},e.maxObjectSize=function(t){return arguments.length?(A=t,e):A},e.bubbleStrokeWidth=function(t){return arguments.length?(C=t,e):C},e.backgroundFill=function(t){return arguments.length?(E=t,e):E},e.namespace=function(t){return arguments.length?(L=t,e):L},e.objectClass=function(t){return arguments.length?(M=t,e):M},e.transitionDuration=function(t){return arguments.length?(F=t,e):F},e.easeFunc=function(t){return arguments.length?(B=t,e):B},e.tooltip=function(t){return arguments.length?(V=t,e):V},e.colorFunction=function(t){return arguments.length?(W=t,e):W},e.xSize=function(t){return arguments.length?(s=t,e):s},e.xSpacerSize=function(t){return arguments.length?(h=t,e):h},e.ySize=function(t){return arguments.length?(f=t,e):f},e.ySpacerSize=function(t){return arguments.length?(d=t,e):d},e}function tt(t){function e(){var e={x:0,y:0,width:r,height:o},d=gt.sel.setupContainer(t,A,e,O);a=d3.keys(n),i=gt.arr.unique(a.map(m)),l=gt.arr.unique(a.map(v)),c=gt.arr.unique(a.map(y)),a.sort(function(t,e){return M(t,e)||F(t,e)}),gt.con.log("heatmap","cells are sorted by",a),gt.con.log("heatmap","x and y keys are",{x:i,y:l});var g=i.length,p=l.length;h=gt.math.calculateWidthOfObject(o,p,k,j,b,x),u=gt.math.calculateWidthOfObject(r,g,w,S,b,x),f=gt.math.calculateWidthOfSpacer(l,o,h,p,b,x),s=gt.math.calculateWidthOfSpacer(i,r,u,g,b,x),gt.con.log("heatmap","size of",{x:u,y:h});var q=H().horizontalQ(!1).moveby("category").numberOfObjects(p).objectClass(gt.str.hypenate(C,"row")).objectSize(h+f).spacerSize(0).transitionDuration(E).easeFunc(L).namespace("row"),W=H().horizontalQ(!0).moveby("category").numberOfObjects(g).objectClass(C).objectSize(u+s).spacerSize(0).transitionDuration(E).easeFunc(L);q(d,l,0),d.selectAll("g."+gt.str.hypenate(C,"row")).each(function(t,e){W(d3.select(this),i,0)});var V=d.selectAll("g:not(.to-remove)."+C);if(a.length!=l.length*i.length){var D={};a.map(function(t,e){D[m(t)+"::"+v(t)]=t});for(var T=[],R=0;R.5?l:-l}).attr("cx",function(t,n){var r=a.pointValues[n];if(e){var o=utils.arr.whichBin(a.binned,r),i=Math.random(),l=P(i*a.frequencies[o]*.5);return Math.random()>.5?l:-l}return p(r)}).attr("stroke",function(t,e){var t=a.pointValues[e];return w(t,"stroke",u,O,M)}).attr("fill",function(t,e){var t=a.pointValues[e];return w(t,"fill",u,O,M)}).attr("stroke-width",j),y.selectAll("circle.point").on("mouseover",function(t,e){d.selectAll("g."+A).style("opacity",.2),o.style("opacity",1),h.attr("stroke-width",2*b),g.attr("stroke-width",2*b),d.selectAll(".point").style("opacity",.2),d3.select(this).style("opacity",1).attr("r",2*S).attr("stroke-width",2*j)}),y.selectAll("circle.point").on("mouseout",function(t,e){var n=document.createEvent("SVGEvents");n.initEvent("mouseout",!0,!0),s.node().dispatchEvent(n),d.selectAll(".point").style("opacity",1),d3.select(this).attr("stroke-width",j).attr("r",S)})}else cV.selectAll(".point").transition().duration(C).ease(E).attr("r",0).attr("cy",e?p(R[1])-p(v):P(0)).attr("cx",e?P(0):p(v)).remove()}}),M.selection(d.selectAll("g:not(.to-remove)."+A+" .area")),void 0==M.data()&&M.data(n),M(),void 0==M.values()&&M.values([function(t,e){return t["utils.math.quartiles"][e]},function(t,e){return t["utils.math.quartiles"][e]},function(t,e){return t["utils.math.quartiles"][e]},function(t,e){return t["utils.math.quartiles"][e]},function(t,e){return t["utils.math.quartiles"][e]}])}var n,r,o,a,i,l,c,u,s="horizontal",h=!0,f=!0,d=function(t,e){return n[t]},g=function(t,e){return d3.descending(n[t],n[e])},p=d3.scaleLinear(),m=.5,v=.05,y=50,x=100,b=2,k=I(),w=function(t,e,n,r,o){var a=d3.scaleLinear().domain([r,o]).range([-.25,.05]),i=utils.color.modifyHexidecimalColorLuminance(n.replace("#",""),a(t)),l="stroke"==e?0:.25;return utils.color.modifyHexidecimalColorLuminance(i.replace("#",""),l)},S=3,j=2,z="transparent",O="d3sm-violin",A="violin",C=1e3,E=d3.easeExp,L=["Q0","Q1","Q2","Q3","Q4"],M=Z().keys([L[4],L[3],L[2],L[1],L[0]]),F=Z(),B=function(t,e){return e.points},Q=function(t,e){return e[t].value};return e.violinPointsExtractor=function(t){return arguments.length?(B=t,e):B},e.violinPointValueExtractor=function(t){return arguments.length?(Q=t,e):Q},e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(n=t,e):n},e.orient=function(t){return arguments.length?(s=t,e):s},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(o=t,e):o},e.overflowQ=function(t){return arguments.length?(h=t,e):h},e.pointsQ=function(t){return arguments.length?(f=t,e):f},e.grouping=function(t){return arguments.length?(a=t,e):a},e.valueExtractor=function(t){return arguments.length?(d=t,e):d},e.sortingFunction=function(t){return arguments.length?(g=t,e):g},e.scale=function(t){return arguments.length?(p=t,e):p},e.domainPadding=function(t){return arguments.length?(m=t,e):m},e.objectSpacer=function(t){return arguments.length?(v=t,e):v},e.minObjectSize=function(t){return arguments.length?(y=t,e):y},e.maxObjectSize=function(t){return arguments.length?(x=t,e):x},e.objectStrokeWidth=function(t){return arguments.length?(b=t,e):b},e.colorFunction=function(t){return arguments.length?(k=t,e):k},e.pointColorFunc=function(t){return arguments.length?(w=t,e):w},e.pointRadius=function(t){return arguments.length?(S=t,e):S},e.pointStrokeWidth=function(t){return arguments.length?(j=t,e):j},e.backgroundFill=function(t){return arguments.length?(z=t,e):z},e.namespace=function(t){return arguments.length?(O=t,e):O},e.objectClass=function(t){return arguments.length?(A=t,e):A},e.transitionDuration=function(t){return arguments.length?(C=t,e):C},e.easeFunc=function(t){return arguments.length?(E=t,e):E},e.quartileKey=function(t){return arguments.length?(quartileKey=t,e):quartileKey},e.quartileKeys=function(t){return arguments.length?(L=t,e):L},e.violinKeys=function(t){return arguments.length?(i=t,e):i},e.violinValues=function(t){return arguments.length?(l=t,e):l},e.objectSize=function(t){return arguments.length?(c=t,e):c},e.spacerSize=function(t){return arguments.length?(u=t,e):u},e.tooltip=function(t){return arguments.length?(M=t,e):M},e.pointsTooltip=function(t){return arguments.length?(F=t,e):F},e}function nt(){function t(t,a){var i=a[t],l=e(t,i),c=d3.keys(l),u=c.map(function(t,e){return n(t,l)}),s=utils.math.quartiles(u,o),h=d3.histogram()(u),f=h.map(t=>t.length),d=r?{x:0,y:d3.min(u)}:{x:d3.min(u),y:0},g=r?{x:0,y:d3.max(u)}:{x:d3.max(u),y:0},p=h.map(function(t,e){return r?{y:t.length?d3.median(t):d3.median([t.x0,t.x1]),x:f[e]}:{x:t.length?d3.median(t):d3.median([t.x0,t.x1]),y:f[e]}});p=[d].concat(p).concat([g]),i.binned=h,i.frequencies=f,i.contour=p,i.utils.math.quartiles=s,i.pointKeys=c,i.pointValues=u}var e,n,r=!0,o=["Q0","Q1","Q2","Q3","Q4"];return t.horizontalQ=function(e){return arguments.length?(r=e,t):r},t.quartileKeys=function(e){return arguments.length?(o=e,t):o},t.violinPointsExtractor=function(n){return arguments.length?(e=n,t):e},t.violinPointValueExtractor=function(e){return arguments.length?(n=e,t):n},t}function rt(t){function e(){var e="horizontal"==p,n=!e,r={x:0,y:0,width:a,height:i},x=gt.sel.setupContainer(t,k,r,b);l=d3.keys(o),c=gt.arr.unique(l.map(z)).sort(),u=gt.arr.unique(l.map(O)).sort().sort(function(t,e){return t.split(";").length-e.split(";").length}),e?l.sort(function(t,e){return M(t,e)||L(t,e)}):l.sort(function(t,e){return L(t,e)||M(t,e)});var A=e?u:c,F=e?c:u,B=e?A.length:F.length,Q=e?F.length:A.length;d=gt.math.calculateWidthOfObject(a,B,v,y,C,m),h=gt.math.calculateWidthOfObject(i,Q,v,y,E,m),g=gt.math.calculateWidthOfSpacer(A,a,d,B,C,m),f=gt.math.calculateWidthOfSpacer(F,i,h,Q,E,m);var q=H().horizontalQ(!1).moveby("category").numberOfObjects(Q).objectSize(h).spacerSize(f).transitionDuration(S).easeFunc(j),W=H().horizontalQ(!0).moveby("category").numberOfObjects(B).objectClass(w).objectSize(d).spacerSize(g).transitionDuration(S).easeFunc(j);n?(W.objectClass(w),q.namespace("across").objectClass(gt.str.hypenate(w,"across")),q(x,F,0),x.selectAll("g."+gt.str.hypenate(w,"across")).each(function(t,e){W(d3.select(this),A,0)})):(W.namespace("across").objectClass(gt.str.hypenate(w,"across")),q.objectClass(w),W(x,A,0),x.selectAll("g."+gt.str.hypenate(w,"across")).each(function(t,e){q(d3.select(this),F,0)}));var V=x.selectAll("g:not(.to-remove)."+w),D={};l.map(function(t,e){D[z(t)+"::"+O(t)]=t}),V.data(l),V.each(function(t,e){var n=d3.select(this);if(void 0!=t){o[t];var r=z(t,e),a=O(t,e);n.classed(a,!0),n.classed(r,!0),gt.sel.safeSelect(n,"circle",gt.str.hypenate(w,"circle")).attr("cx",d/2).attr("cy",h/2).attr("r",void 0==s?Math.min(d,h)/2:s).attr("fill",a.includes(r)?"black":"rgb(233,233,233)").attr("stroke","black").attr("in-intersection",a.includes(r))}})}function n(){var t={};return u.map(function(e,n){t[e]={total:0}}),l.map(function(e,n){var r=A(e,n);0==t[O(e,n)].total&&(Array.isArray(r)?(t[O(e,n)].total+=r.length,t[O(e,n)].values=r):t[O(e,n)].total+=r)}),t}function r(){var t={};return c.map(function(e,n){t[e]={total:0}}),l.map(function(e,n){var r=A(e,n);Array.isArray(r)?t[z(e,n)].total+=r.length:t[z(e,n)].total+=r}),t}var o,a,i,l,c,u,s,h,f,d,g,p="horizontal",m=!1,v=20,y=50,x=2,b="transparent",k="d3sm-upset",w="upset",S=1e3,j=d3.easeExp,z=function(t,e){return o[t].set},O=function(t,e){return o[t].intersection},A=function(t,e){return o[t].elements},C=.05,E=.05,L=function(t,e){return c.indexOf(z(t))-c.indexOf(z(e))},M=function(t,e){return u.indexOf(O(t))-u.indexOf(O(e))};return e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(o=t,e):o},e.orient=function(t){return arguments.length?(p=t,e):p},e.spaceX=function(t){return arguments.length?(a=t,e):a},e.spaceY=function(t){return arguments.length?(i=t,e):i},e.overflowQ=function(t){return arguments.length?(m=t,e):m},e.minObjectSize=function(t){return arguments.length?(v=t,e):v},e.maxObjectSize=function(t){return arguments.length?(y=t,e):y},e.circleStrokeWidth=function(t){return arguments.length?(x=t,e):x},e.backgroundFill=function(t){return arguments.length?(b=t,e):b},e.namespace=function(t){return arguments.length?(k=t,e):k},e.objectClass=function(t){return arguments.length?(w=t,e):w},e.transitionDuration=function(t){return arguments.length?(S=t,e):S},e.easeFunc=function(t){return arguments.length?(j=t,e):j},e.cellKeys=function(t){return arguments.length?(l=t,e):l},e.setValues=function(t){return arguments.length?(c=t,e):c},e.intersectionValues=function(t){return arguments.length?(u=t,e):u},e.xObjectSpacer=function(t){return arguments.length?(C=t,e):C},e.yObjectSpacer=function(t){return arguments.length?(E=t,e):E},e.radius=function(t){return arguments.length?(s=t,e):s},e.setExtractor=function(t){return arguments.length?(z=t,e):z},e.intersectionExtractor=function(t){return arguments.length?(O=t,e):O},e.elementExtractor=function(t){return arguments.length?(A=t,e):A},e.setKeySortingFunction=function(t){return arguments.length?(L=t,e):L},e.intersectionKeySortingFunction=function(t){return arguments.length?(M=t,e):M},e.yObjectSize=function(t){return arguments.length?(h=t,e):h},e.ySpacerSize=function(t){return arguments.length?(f=t,e):f},e.xObjectSize=function(t){return arguments.length?(d=t,e):d},e.xSpacerSize=function(t){return arguments.length?(g=t,e):g},e.intersectionTotals=n,e.setTotals=r,e}function ot(t){function e(){var e="horizontal"==l,r=!e,u={x:0,y:0,width:o,height:a},k=gt.sel.setupContainer(t,v,u,m);p.dataExtent([0,n.length-1]).colorBy("categories").categoryExtractor(function(t,e,n){return e});var w=Math.min(o,a)/2,S=n.length,j=void 0==i?n.sort(s):i,z=gt.arr.flatten(j),O=e?o:a,A=gt.math.calculateWidthOfObject(O,S,f,d,h,c),C=gt.math.calculateWidthOfSpacer(z,O,A,S,h,c);H().horizontalQ(e).scale(scale).moveby("category").numberOfObjects(S).objectClass(y).objectSize(A).spacerSize(C).transitionDuration(x).easeFunc(b).namespace(v)(k,j,0);w=Math.min(A,o,a)/2-g;k.selectAll("g:not(.to-remove)."+y).each(function(t,n){var a=d3.select(this),i=gt.sel.safeSelect(a,"circle"),l=p(void 0,t,n,"fill"),c=p(void 0,t,n,"stroke"),u=e?w+g:(o-2*w)/2+w,s=r?w+g:(o-2*w)/2+w;i.attr("r",w).attr("cx",u).attr("cy",s).attr("fill",l).attr("stroke",c).attr("stroke-width",g);var h=gt.sel.safeSelect(a,"text");h.text(t).attr("text-anchor","middle").attr("transform",function(t,e){return"translate("+u+","+(s+h.node().getBoundingClientRect().height/4)+")"})})}var n,r,o,a,i,l="horizontal",c=!1,u=function(t,e){return r[t]},s=function(t,e){return d3.ascending(t,e)},h=.05,f=10,d=100,g=2,p=I(),m="transparent",v="d3sm-legend",y="legend",x=1e3,b=d3.easeExp;return e.categories=function(t){return arguments.length?(n=t,e):n},e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(r=t,e):r},e.orient=function(t){return arguments.length?(l=t,e):l},e.spaceX=function(t){return arguments.length?(o=t,e):o},e.spaceY=function(t){return arguments.length?(a=t,e):a},e.overflowQ=function(t){return arguments.length?(c=t,e):c},e.grouping=function(t){return arguments.length?(i=t,e):i},e.valueExtractor=function(t){return arguments.length?(u=t,e):u},e.sortingFunction=function(t){return arguments.length?(s=t,e):s},e.objectSpacer=function(t){return arguments.length?(h=t,e):h},e.minObjectSize=function(t){return arguments.length?(f=t,e):f},e.maxObjectSize=function(t){return arguments.length?(d=t,e):d},e.bubbleStrokeWidth=function(t){return arguments.length?(g=t,e):g},e.colorFunction=function(t){return arguments.length?(p=t,e):p},e.backgroundFill=function(t){return arguments.length?(m=t,e):m},e.namespace=function(t){return arguments.length?(v=t,e):v},e.objectClass=function(t){return arguments.length?(y=t,e):y},e.transitionDuration=function(t){return arguments.length?(x=t,e):x},e.easeFunc=function(t){return arguments.length?(b=t,e):b},e}function at(t){function e(){var e={x:0,y:0,width:r,height:o},h=gt.sel.setupContainer(t,c,e,s),f=gt.sel.safeSelect(t,"defs"),d=gt.sel.safeSelect(f,"linearGradient").attr("x1","0%").attr("y1","100%").attr("x2","0%").attr("y2","0%").attr("id",gt.str.hypenate(c,"numerical-legend-gradient"));l.dataExtent([a,i]).colorBy("value").valueExtractor(function(t,e,n){return e}),d.selectAll("stop").data(l.colors()).enter().append("stop").attr("offset",function(t,e){return e/(l.colors().length-1)}).attr("stop-color",function(t){return t});var g=gt.sel.safeSelect(h,"rect","legend").attr("transform","translate(0,"+u+")").style("fill","url(#"+gt.str.hypenate(c,"numerical-legend-gradient")+")").attr("x",0).attr("y",0).attr("width",r).attr("height",o-2*u).on("mousemove",function(t,e){n(t,e,g,d3.select(this))}).on("mouseout",function(t,e){d3.select("#"+gt.str.hypenate(c,"legend-tooltip")).remove()});gt.sel.safeSelect(h,"text","min").text(gt.math.round(a,2)).attr("text-anchor","middle").attr("font-size",u+"px").attr("transform",function(t,e){return"translate("+r/2+","+(o-u/4)+")"}),gt.sel.safeSelect(h,"text","max").text(gt.math.round(i,2)).attr("text-anchor","middle").attr("font-size",u+"px").attr("transform",function(t,e){return"translate("+r/2+","+u+")"})}function n(t,e,n,r){var o=d3.scaleLinear().domain([0,n.attr("height")]).range([i,a]),s=d3.mouse(n.node()),d=gt.math.round(o(s[1]),f),g=l(void 0,d,void 0,"stroke"),p=l(void 0,d,void 0,"fill"),m=gt.sel.safeSelect(d3.select("body"),"div",gt.str.hypenate(c,"legend-tooltip")).attr("id",gt.str.hypenate(c,"legend-tooltip")).style("position","absolute").style("left",d3.event.pageX+15+"px").style("top",d3.event.pageY+15+"px").style("background-color",p).style("border-color",g).style("min-width",u*(String(i).split(".")[0].length+3)+"px").style("min-height",u*(String(i).split(".")[0].length+3)+"px").style("border-radius","50%").style("border-radius","5000px").style("display","flex").style("justify-content","center").style("text-align","middle").style("padding","2px").style("border-style","solid").style("border-width",2);gt.sel.safeSelect(m,"div").text(d).style("color",h).style("align-self","center")}var r,o,a=0,i=1,l=I(),c="d3sm-linear-vertical-gradient",u=12,s="transparent",h="black",f=2;return e.min=function(t){return arguments.length?(a=t,e):a},e.max=function(t){return arguments.length?(i=t,e):i},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(o=t,e):o},e.namespace=function(t){return arguments.length?(c=t,e):c},e.fontSize=function(t){return arguments.length?(u=t,e):u},e.backgroundFill=function(t){return arguments.length?(s=t,e):s},e.colorFunction=function(t){return arguments.length?(l=t,e):l},e.textColor=function(t){return arguments.length?(h=t,e):h},e.roundTo=function(t){return arguments.length?(f=t,e):f},e}function it(t){function e(){z&&i()}function n(t){z=void 0!=t?t:!z,y=gt.sel.getTranslation(v),x=gt.sel.getTranslation(p),z?g.node().addEventListener("mousedown",a,!0):(g.node().removeEventListener("mousedown",a,!0),o())}function r(){y=gt.sel.getTranslation(v),x=gt.sel.getTranslation(p);var t=gt.sel.safeSelect(p,"g","lasso-container").selectAll('path[instance="'+E+'"]'),e=((t=t.data(A)).exit().remove(),t.enter().append("path"));l(t=t.merge(e))}function o(){var t=gt.sel.safeSelect(p,"g","lasso-container");t.selectAll('path[instance="'+E+'"]').remove();t.remove(),p.selectAll(m).classed("in-lasso",!1),h()}function a(t){t.preventDefault(),t.stopPropagation();var e=gt.sel.safeSelect(p,"g","lasso-container");O=[],g.node().addEventListener("mousemove",c),g.node().addEventListener("mouseup",function(t){g.node().removeEventListener("mousemove",c),A.push(O),A=gt.arr.unique(A)}),l(S=e.append("path").data([O]))}function i(){var t=gt.sel.safeSelect(p,"g","lasso-container").selectAll('path[instance="'+E+'"]'),e=((t=t.data(A)).exit().remove(),t.enter().append("path"));l(t=t.merge(e).transition().duration(R).ease(Y))}function l(t){t.attr("class",gt.str.hypenate(j,"lasso-path")).style("opacity",B).attr("fill",M).attr("d",C).attr("instance",E).style("stroke-dasharray",Q).attr("stroke",q).attr("stroke-width",W).style("animation","lassoDash "+F+" linear").style("animation-iteration-count","infinite")}function c(t){if(void 0!=b&&b.dispatch(gt.str.hypenate(j,"drag")),1==t.which){d3.event=t;var e=d3.mouse(p.node()),e=d3.mouse(g.node());if(void 0!=k&&(e[0]=k.invert(e[0])),void 0!=w&&(e[1]=w.invert(e[1])),e[0]=e[0]-y[0]-x[0],e[1]=e[1]-y[1]-x[1],O.length){var n=O[O.length-1],r=[e[0],e[1]],o=[n[0],n[1]];k&&(o[0]=k(o[0]),r[0]=k(r[0])),w&&(o[1]=w(o[1]),r[1]=w(r[1])),gt.math.euclideanDistance(o,r)>L&&u(e)}else u(e)}}function u(t){if(void 0!=t){if(O.push(t),S.attr("d",C),O.length<3)return;s(A.concat([O]))}else s(A)}function s(t){return void 0==t&&(t=A),p.selectAll(m).each(function(e,n){var r=d3.select(this),o=r.absolutePosition(),a=[[o.left-y[0]-x[0],o.top-y[1]-x[1]],[o.right-y[0]-x[0],o.top-y[1]-x[1]],[o.left-y[0]-x[0],o.bottom-y[1]-x[1]],[o.right-y[0]-x[0],o.bottom-y[1]-x[1]]];void 0!=k&&(a[0][0]=k.invert(a[0][0]),a[1][0]=k.invert(a[1][0]),a[2][0]=k.invert(a[2][0]),a[3][0]=k.invert(a[3][0])),void 0!=w&&(a[0][1]=w.invert(a[0][1]),a[1][1]=w.invert(a[1][1]),a[2][1]=w.invert(a[2][1]),a[3][1]=w.invert(a[3][1]));for(var i=!1,n=0;nd3.polygonContains(l,t))&&(i=!0)}r.classed("in-lasso",i),r.classed("in-lasso-"+E,i)}),h(),p.selectAll(".in-lasso-"+E)}function h(){p.selectAll(m).each(function(t,e){var n=d3.select(this);f(n,n.classed("in-lasso"))})}function f(t,e){var n=t.attr("_pre_lasso_fill"),r=t.attr("_pre_lasso_stroke"),o=t.attr("_pre_lasso_stroke-width");e?(t.classed("in-lasso",!0),t.classed("in-lasso-"+E,!0),void 0==n&&t.attr("_pre_lasso_fill",t.attr("fill")),void 0==r&&t.attr("_pre_lasso_stroke",t.attr("stroke")),void 0==o&&t.attr("_pre_lasso_stroke-width",t.attr("stroke-width")),t.attr("fill",V).attr("stroke",D).attr("stoke-width",T)):(t.classed("in-lasso",!1),t.classed("in-lasso-"+E,!1),void 0!=n&&t.attr("fill",n),void 0!=r&&t.attr("stroke",r),void 0!=o&&t.attr("stroke-width",o))}function d(){d3.select("html").select("style."+gt.str.hypenate(j,"lasso-dash")).empty()&&d3.select("html").append("style").classed(gt.str.hypenate(j,"lasso-dash"),!0).html("@keyframes lassoDash {to { stroke-dashoffset: 1000;}}")}var g,p,m,v,y,x,b,k,w,S,j="d3sm-lasso",z=!1,O=[],A=[],C=d3.line().x(function(t,e){return void 0!=k?k(t[0]):t[0]}).y(function(t,e){return void 0!=w?w(t[1]):t[1]}).curve(d3.curveLinearClosed),E=0,L=10,M="#17a2b8",F="10s",B=.3,Q="5, 10",q="black",W=2,V="white",D="black",T=3,R=1e3,Y=d3.easeExp;return e.svg=function(t){return arguments.length?(g=t,e):g},e.chartContainer=function(t){return arguments.length?(v=t,e):v},e.objectContainer=function(t){return arguments.length?(p=t,e):p},e.objectClass=function(t){return arguments.length?(m=t,e):m},e.namespace=function(t){return arguments.length?(j=t,e):j},e.xScale=function(t){return arguments.length?(k=t,e):k},e.yScale=function(t){return arguments.length?(w=t,e):w},e.activeQ=function(t){return arguments.length?(z=t,e):z},e.currentPoints=function(t){return arguments.length?(O=t,e):O},e.allPoints=function(t){return arguments.length?(A=t,e):A},e.instance=function(t){return arguments.length?(E=t,e):E},e.tickDistance=function(t){return arguments.length?(L=t,e):L},e.color=function(t){return arguments.length?(M=t,e):M},e.animationRate=function(t){return arguments.length?(F=t,e):F},e.opacity=function(t){return arguments.length?(B=t,e):B},e.dashArray=function(t){return arguments.length?(Q=t,e):Q},e.stroke=function(t){return arguments.length?(q=t,e):q},e.lassoedFill=function(t){return arguments.length?(V=t,e):V},e.lassoedStroke=function(t){return arguments.length?(D=t,e):D},e.lassoedStrokeWidth=function(t){return arguments.length?(T=t,e):T},e.eventCatcher=function(t){return arguments.length?(b=t,e):b},e.drag=c,e.draw=r,e.tick=u,e.detect=s,e.toggle=n,e.remove=o,e.render=a,e.keyFrames=d,e.updateObjects=h,e.applyPathAttributes=l,e.applyObjectAttributes=f,d(),e}function lt(t){function e(){var e=c.select("."+gt.str.hypenate(t.namespace(),"object-container")),n=gt.sel.getTranslation(e.attr("transform")),r=e.attr("transform","translate(0,0)");i=c.node().getBBox().width-t.spaceX(),l=c.node().getBBox().height-t.spaceY(),r.attr("transform","translate("+n[0]+","+n[1]+")"),gt.con.log("plotZoom","setLocks",{xLock:i,yLock:l})}function n(){e();var n,h,f=u.map(function(t,e){return t.selection()}),d=s.map(function(t,e){return t.selection()});"2D"==a&&(n=!0,h=!0),"horizontal"==a&&(n=!0,h=!1),"vertical"==a&&(h=!0,n=!1);var g=d3.event.transform,p=c.node().getBBox();f.map(function(t,e){return t.node().getBBox()}),f.map(function(t,e){return t.node().getBBox()}),p.width,p.x,p.height,p.y;if("wheel"==r){d3.event.preventDefault();var m=d3.event.deltaY*o,v=d3.event.shiftKey;(g="2D"==a?v?{k:1,x:m,y:0}:{k:1,x:0,y:m}:n?{k:1,x:m,y:0}:{k:1,x:0,y:m}).applyX=function(t){return t*this.k+-1*this.x},g.applyY=function(t){return t*this.k+-1*this.y}}var y=c.select("."+gt.str.hypenate(t.namespace(),"object-container")),x=f.map(function(t,e){return t.select("."+gt.str.hypenate(u[e].namespace(),"object-container"))}),b=d.map(function(t,e){return t.select("."+gt.str.hypenate(s[e].namespace(),"object-container"))}),k=gt.sel.getTranslation(y.attr("transform")),w=(x.map(function(t,e){return gt.sel.getTranslation(t.attr("transform"))}),b.map(function(t,e){return gt.sel.getTranslation(t.attr("transform"))}),n?g.applyX(k[0]):0);n&&(w=w<-i?(g.x=0,-i):(g.x=0,Math.min(w,0)));var S=h?g.applyY(k[1]):0;h&&(S=S<-l?(g.y=0,-l):(g.y=0,Math.min(S,0))),y.attr("transform","translate("+w+","+S+")"),n&&x.map(function(t,e){t.attr("transform","translate("+w+",0)")}),h&&b.map(function(t,e){t.attr("transform","translate(0,"+S+")")})}var r,o=20,a=void 0==t.orient?"horizontal":t.orient(),i=t.spaceX(),l=t.spaceY(),c=t.selection(),u=(d3.select(c.thisSVG()),[]),s=[];return n.eventType=function(t){return arguments.length?(r=t,n):r},n.wheelSpeed=function(t){return arguments.length?(o=t,n):o},n.orient=function(t){return arguments.length?(a=t,n):a},n.xLock=function(t){return arguments.length?(i=t,n):i},n.yLock=function(t){return arguments.length?(l=t,n):l},n.xComponents=function(t){return arguments.length?(u=t,n):u},n.yComponents=function(t){return arguments.length?(s=t,n):s},n.setLocks=e,n.reset=function(){var e=c.select("."+gt.str.hypenate(t.namespace(),"object-container")),n=xAxisSel.select("."+gt.str.hypenate(xAxis.namespace(),"object-container")),r=yAxisSel.select("."+gt.str.hypenate(yAxis.namespace(),"object-container"));e.attr("transform","translate(0,0)"),n.attr("transform","translate(0,0)"),r.attr("transform","translate(0,0)")},n}function ct(t,e,n){function r(){var e=s.select("."+gt.str.hypenate(t.namespace(),"object-container")),n=gt.sel.getTranslation(e.attr("transform")),r=e.attr("transform","translate(0,0)");c=s.node().getBBox().width-.9*t.spaceX(),u=s.node().getBBox().height-.9*t.spaceY(),r.attr("transform","translate("+n[0]+","+n[1]+")"),gt.con.log("plotZoom","setLocks",{xLock:c,yLock:u})}function o(){r();var o,d;"2D"==l&&(o=!0,d=!0),"horizontal"==l&&(o=!0,d=!1),"vertical"==l&&(d=!0,o=!1);var g=d3.event.transform,p=s.node().getBBox(),m=h.node().getBBox(),v=h.node().getBBox();p.width,p.x,p.height,p.y,m.width,m.height,v.width,v.height;if("wheel"==a){d3.event.preventDefault();var y=d3.event.deltaY*i,x=d3.event.shiftKey;(g="2D"==l?x?{k:1,x:y,y:0}:{k:1,x:0,y:y}:o?{k:1,x:y,y:0}:{k:1,x:0,y:y}).applyX=function(t){return t*this.k+-1*this.x},g.applyY=function(t){return t*this.k+-1*this.y}}var b=s.select("."+gt.str.hypenate(t.namespace(),"object-container")),k=h.select("."+gt.str.hypenate(e.namespace(),"object-container")),w=f.select("."+gt.str.hypenate(n.namespace(),"object-container")),S=gt.sel.getTranslation(b.attr("transform")),j=(gt.sel.getTranslation(k.attr("transform")),gt.sel.getTranslation(w.attr("transform")),o?g.applyX(S[0]):0);o&&(j=j<-c?(g.x=0,-c):(g.x=0,Math.min(j,0)));var z=d?g.applyY(S[1]):0;d&&(z=z<-u?(g.y=0,-u):(g.y=0,Math.min(z,0))),b.attr("transform","translate("+j+","+z+")"),o&&k.attr("transform","translate("+j+",0)"),d&&w.attr("transform","translate(0,"+z+")")}var a,i=20,l=void 0==t.orient?"horizontal":t.orient(),c=t.spaceX(),u=t.spaceY(),s=t.selection(),h=e.selection(),f=n.selection();d3.select(s.thisSVG());return o.eventType=function(t){return arguments.length?(a=t,o):a},o.wheelSpeed=function(t){return arguments.length?(i=t,o):i},o.orient=function(t){return arguments.length?(l=t,o):l},o.xLock=function(t){return arguments.length?(c=t,o):c},o.yLock=function(t){return arguments.length?(u=t,o):u},o.setLocks=r,o.reset=function(){var r=s.select("."+gt.str.hypenate(t.namespace(),"object-container")),o=h.select("."+gt.str.hypenate(e.namespace(),"object-container")),a=f.select("."+gt.str.hypenate(n.namespace(),"object-container"));r.attr("transform","translate(0,0)"),o.attr("transform","translate(0,0)"),a.attr("transform","translate(0,0)")},o}var ut=Object.freeze({uniqueElements:e,all:n,tally:r,hasQ:o,first:a,last:i,total:l,unique:c,get:u,listOfListsQ:s,cut:h,groupBy:f,arrayEquals:d,elementsAtLevels:g,numberOfElements:p,flatten:m,whichBin:v}),st=Object.freeze({modifyHexidecimalColorLuminance:y,interpolateColors:x}),ht=Object.freeze({round:b,tickRange:k,partitionRangeInto:w,euclideanDistance:S,quartiles:j,calculateWidthOfObject:z,calculateWidthOfSpacer:O,spacersNeededAtEachLevel:A}),ft=Object.freeze({resizeDebounce:C,setupStandardChartContainers:L}),dt=Object.freeze({hypenate:M,truncateText:F,truncateString:B});let gt={arr:ut,color:st,math:ht,misc:ft,sel:Object.freeze({getTranslation:Q,getContainingSVG:q,safeSelect:W,cpRect:V,bgRect:D,setupContainer:T}),str:dt,con:Object.freeze({consoleGroup:R,consoleGroupEnd:Y,log:K,warn:X,info:P,error:_}),paths:Object.freeze({whiskerPath:N})},pt={scatter:J,bar:U,bubble:$,heatmap:tt,violin:et,neededViolinValues:nt,upset:rt},mt={categorical:ot,numeric:at};d3.selection.prototype.thisSVG=function(){return gt.sel.getContainingSVG(this.node())},d3.mouse.absolute=function(){var t=d3.select("html").node(),[e,n]=this(t);return[e,n]},d3.selection.prototype.absolutePosition=function(){var t=this.node(),e=t.getBoundingClientRect(),n=getContainingSVG(t).getBoundingClientRect();return{top:e.top-n.top,left:e.left-n.left,bottom:e.bottom-n.top,right:e.right-n.left,height:e.height,width:e.width}},d3.selection.prototype.relativePositionTo=function(t){var e=this.node().getBoundingClientRect(),n=t.getBoundingClientRect();return{top:e.top-n.top,left:e.left-n.left,bottom:e.bottom-n.top,right:e.right-n.left,height:e.height,width:e.width}};let vt={aux:{lasso:it,plotZoom:ct,multiPlotZoom:lt},axis:G,charts:pt,colorFunction:I,debugQ:!1,groupingSpacer:H,legends:mt,tooltip:Z,utils:gt};"undefined"!=typeof window&&(window.d3sm=vt),t.default=vt}(this.d3sm=this.d3sm||{}); +!function(t){"use strict";function e(t,e,n){return n.indexOf(t)===e}function n(t,e){return void 0==e?t.every(function(t){return!0===t}):t.every(function(t){return e(t)})}function r(t){var e={};return t.map(function(t){a(Object.keys(e),t)?e[t]=1:e[t]+=1}),e}function a(t,e){return t.includes(e)}function o(t){return t[0]}function i(t){return t[t.length-1]}function l(t){return t.reduce((t,e)=>t+e,0)}function c(t){return t.filter(e)}function s(t,e){return t.filter(function(t,n){return a(e,n)})}function u(t){return n(t.map(function(t,e){return Array.isArray(t)}))}function f(t,e){return u(t)?e.map(function(e,n){return t.get(e)}):s(t,e)}function d(t,e,n){void 0==n&&(n=c(t.map(function(t,n){return element[e]}))).map(function(t,e){r[t]=[]});var r={};return t.map(function(t,n){r[t[e]].push(t)}),r}function h(t,e){if(!e)return!1;if(t.length!=e.length)return!1;for(var n=0,r=t.length;n=n.length?n.push(t.length):n[e]+=t.length,t.map(function(t,r){Array.isArray(t)&&g(t,e,n)}),n}function p(t,e){return e=void 0==e?0:e,t.map(function(t,n){Array.isArray(t)?e=p(t,e):e+=1}),e}function m(t,e){return e=void 0==e?[]:e,t.map(function(t,n){Array.isArray(t)?e=e.concat(m(t)):e.push(t)}),e}function v(t,e){for(var n=0;ntt>n),o=void 0==(o=d3.median(r))?n:o,i=void 0==(i=d3.min(r))?o:i,l=void 0==(l=d3.median(a))?n:l,c=void 0==(c=d3.max(a))?l:c,s="q0",u="q1",f="q2",d="q3",h="q4",g={};return void 0!=e&&5==e.length&&(s=e[0],u=e[1],f=e[2],d=e[3],h=e[4]),g[s]=i,g[u]=o,g[f]=n,g[d]=l,g[h]=c,g}function z(t,e,n,r,a,o){var i=t-(e-1)*(a=0==a||a>1?a:t*a),l=(i=i<0?0:i)/e;return o&&void 0!=n&&l=n.length?n.push(t.length-1):n[e]+=t.length-1,t.map(function(t,r){Array.isArray(t)&&A(t,e,n)}),n}function C(){return Array.prototype.slice.call(arguments).join("-")}function E(t,e,n,r,a,o){var i=t.node().getBoundingClientRect();for(t.text(e);Math.max(i.width,i.height)>a-r&&(e=String(e),e=e.slice(0,e.length-1),t.text(e+"..."),i=t.node().getBoundingClientRect(),0!=e.length););}function L(t,e,n){var r=e/n;return r g.'+l+"."+u);"function"==typeof d?v.each(function(t,e){d(d3.select(this))}):v.remove()}else{x+=n;var m=h.select("g."+u+'[level="'+g+'"] > g.'+l+"."+u);m.empty()&&(m=h.append("g").attr("class",l).classed(u,!0)),m.attr("parent-index",r);var v=h.selectAll("g."+u+'[level="'+(g+1)+'"]');"function"==typeof d?v.each(function(t,e){d(d3.select(this))}):v.remove()}x+=r==p.size()-1?0:y}),x}var e,n,r,a=!0,o=d3.scaleLinear(),i="category",l="d3sm-groupped-item",c=1e3,s=d3.easeSin,u="spacer",f=function(t){t.attr("transform",function(t,e){return"translate("+(a?window.outerWidth:0)+","+(a?0:window.outerWidth)+")"})},d=function(t){R("groupingSpacer","exiting with",{current:t,currentNode:t.node()}),t.selectAll("g").classed("to-remove",!0),t.transition().duration(.9*c).ease(s).attr("transform",function(t,e){return"translate("+(a?window.outerWidth:0)+","+(a?0:window.outerWidth)+")"}).remove()};return t.horizontalQ=function(e){return arguments.length?(a=e,t):a},t.scale=function(e){return arguments.length?(o=e,t):o},t.moveby=function(e){return arguments.length?(i=e,t):i},t.numberOfObjects=function(n){return arguments.length?(e=n,t):e},t.objectClass=function(e){return arguments.length?(l=e,t):l},t.objectSize=function(e){return arguments.length?(n=e,t):n},t.spacerSize=function(e){return arguments.length?(r=e,t):r},t.transitionDuration=function(e){return arguments.length?(c=e,t):c},t.easeFunc=function(e){return arguments.length?(s=e,t):s},t.namespace=function(e){return arguments.length?(u=e,t):u},t.enterFunction=function(e){return arguments.length?(f=e,t):f},t.exitFunction=function(e){return arguments.length?(d=e,t):d},t}function H(t){function e(){function e(t,e,n,r,a){return n&&r?a/2:0}function W(t,e,n,r,a){return n&&r?a/2:0}var D=!!xt.arr.hasQ(["top","bottom","horizontal"],h),_=!D,H={x:0,y:0,width:g,height:p};"left"==h&&(H.x-=g,y&&(H.width+=s),H.y-=V,H.height+=2*V),"bottom"==h&&(H.y=H.y,y&&(H.y-=s,H.height+=s),H.x-=V,H.width+=2*V),"top"==h&&(H.y-=p,y&&(H.height+=s),H.y-=V,H.height+=2*V),"right"==h&&(H.x=0,y&&(H.width+=s,H.x-=s),H.y-=V,H.height+=2*V);var Z=xt.sel.setupContainer(t,z,H,j);"top"==h&&(l=void 0==l?"start":l,c=void 0==c?-90:c),"bottom"==h&&(l=void 0==l?"end":l,c=void 0==c?-90:c),"left"==h&&(l=void 0==l?"end":l,c=void 0==c?0:c),"right"==h&&(l=void 0==l?"start":l,c=void 0==c?0:c);var I=v?void 0==a?o:a:void 0==a?void 0!=A?xt.math.tickRange(...d3.extent(i),A):i:a,J=xt.arr.flatten(I),U=J.length,$=D?g:p,tt=d3.extent(J);N&&tt.reverse();var et=N?[tt[0]+b,tt[1]-b]:[tt[0]-b,tt[1]+b];x.domain(et).range([D?0:p,D?g:0]),u=void 0==u?xt.math.calculateWidthOfObject($,U,k,w,S,m):u,f=void 0==f?xt.math.calculateWidthOfSpacer(J,$,u,U,S,m):f;var nt=xt.str.hypenate(z,v?O+"-categorical":O),rt=G().horizontalQ(D).scale(x).moveby(v?"category":"scale").numberOfObjects(U).objectClass(nt).objectSize(u).spacerSize(f).transitionDuration(Y).easeFunc(X).namespace(z),at=function(t){var e=x(t.datum()),n=2*x(tt[1]),r=ewindow.innerWidth&&a.style("left",d3.event.pageX-15-300+"px")}function r(t,e){var n=d3.select(this).style("fill","black");if(d3.select(n.node().parentNode).select("line."+xt.str.hypenate(z,"tick")).attr("stroke",L).attr("stroke-width",F),y){var r=d3.select(n.node().parentNode).select("line."+xt.str.hypenate(z,"guideline")),a=r.attr("minor");r.attr("stroke",function(t,e){return"true"==a?xt.color.modifyHexidecimalColorLuminance(P,.8):P}).attr("stroke-width",function(t,e){return"true"==a?.8*R:R})}d3.select("#"+xt.str.hypenate(z,"guideline-tooltip")).remove()}var a,o,i,l,c,s,u,f,d,h="bottom",g=0,p=0,m=!1,v=!1,y=!1,x=d3.scaleLinear(),b=.5,S=.05,k=15,w=50,j="transparent",z="d3sm-axis",O="tick-group",A=5,C="black",E=3,L="black",F=2,M=10,Q=10,q=10,B=14,W=8,V=20,D=void 0,K=function(t,e){},_=function(t,e){return String(t).replace("-"," ").replace("_"," ")},P="#333333",R=2,Y=1e3,X=d3.easeExp,T=2,N=!1;return e.label=function(t){return arguments.length?(d=t,e):d},e.tickTickLabelSpacer=function(t){return arguments.length?(Q=t,e):Q},e.tickLabelMargin=function(t){return arguments.length?(q=t,e):q},e.selection=function(n){return arguments.length?(t=n,e):t},e.orient=function(t){return arguments.length?(h=t,e):h},e.spaceX=function(t){return arguments.length?(g=t,e):g},e.spaceY=function(t){return arguments.length?(p=t,e):p},e.overflowQ=function(t){return arguments.length?(m=t,e):m},e.categoricalQ=function(t){return arguments.length?(v=t,e):v},e.guideLinesQ=function(t){return arguments.length?(y=t,e):y},e.grouping=function(t){return arguments.length?(a=t,e):a},e.scale=function(t){return arguments.length?(x=t,e):x},e.domainPadding=function(t){return arguments.length?(b=t,e):b},e.objectSpacer=function(t){return arguments.length?(S=t,e):S},e.minObjectSize=function(t){return arguments.length?(k=t,e):k},e.maxObjectSize=function(t){return arguments.length?(w=t,e):w},e.namespace=function(t){return arguments.length?(z=t,e):z},e.backgroundFill=function(t){return arguments.length?(j=t,e):j},e.objectClass=function(t){return arguments.length?(O=t,e):O},e.tickLabels=function(t){return arguments.length?(o=t,e):o},e.tickValues=function(t){return arguments.length?(i=t,e):i},e.numberOfTicks=function(t){return arguments.length?(A=t,e):A},e.lineStroke=function(t){return arguments.length?(C=t,e):C},e.lineStrokeWidth=function(t){return arguments.length?(E=t,e):E},e.tickStroke=function(t){return arguments.length?(L=t,e):L},e.tickStrokeWidth=function(t){return arguments.length?(F=t,e):F},e.tickLength=function(t){return arguments.length?(M=t,e):M},e.tickLabelFontSize=function(t){return arguments.length?(B=t,e):B},e.tickLabelMinFontSize=function(t){return arguments.length?(W=t,e):W},e.tickLabelMaxFontSize=function(t){return arguments.length?(V=t,e):V},e.tickLabelTextAnchor=function(t){return arguments.length?(l=t,e):l},e.tickLabelRotation=function(t){return arguments.length?(c=t,e):c},e.tickLabelFunc=function(t){return arguments.length?(D=t,e):D},e.tickLabelOnClick=function(t){return arguments.length?(K=t,e):K},e.guidelineSpace=function(t){return arguments.length?(s=t,e):s},e.guideLineStroke=function(t){return arguments.length?(P=t,e):P},e.guideLineStrokeWidth=function(t){return arguments.length?(R=t,e):R},e.transitionDuration=function(t){return arguments.length?(Y=t,e):Y},e.easeFunc=function(t){return arguments.length?(X=t,e):X},e.objectSize=function(t){return arguments.length?(u=t,e):u},e.spacerSize=function(t){return arguments.length?(f=t,e):f},e.roundTo=function(t){return arguments.length?(T=t,e):T},e.reverseScaleQ=function(t){return arguments.length?(N=t,e):N},e.tickLabelOnHoverFunc=function(t){return arguments.length?(_=t,e):_},e}function Z(t){function e(){t.on("mouseover",n),t.on("mousemove",n),t.on("mouseout",function(){d3.selectAll(".d3sm-tooltip").remove()})}function n(t,e){_("d3sm-tooltip");var n=i[t],[l,c]=d3.mouse(d3.select("html").node());R("tooltip","mousemove detected",{key:t,index:e,x:l,y:c}),R("tooltip","current data",n);var s=Q(d3.select("html"),"tooltip","d3sm-tooltip").classed("card",!0).style("max-width","300px").style("background-color","#212529").style("color","white"),u=Q(s,"div","card-body"),f=(Q(u,"h5","card-title").text(void 0==o?t:"function"==typeof o?o(t,n,e):o).style("color","cyan"),Q(Q(u,"table","table").classed("table-dark",!0),"tbody"));(f=(f=f.selectAll("tr")).data(void 0==r?d3.keys(n):r)).exit().remove();var d=f.enter().append("tr").style("max-width","300px");d.append("td").attr("class",function(t,e){return"tooltip-key"}),d.append("td").attr("class",function(t,e,n){return"tooltip-value"}).attr("tooltip-row-index",function(t,e){return e}),_("tooltip-rows"),f.selectAll(".tooltip-key").text(function(t,e){return t}),f.selectAll("tr .tooltip-value").text(function(t,e){R("tooltip","trying to set value",{rowKey:t,rowIndex:e});var e=d3.select(this).attr("tooltip-row-index"),r=n[t];return void 0!=a&&"function"==typeof(r=a[e])&&(r=r(n,t)),"number"==typeof r?b(r,5):r}),P(),P(),l+=15;var h=s.node().getBoundingClientRect();l+h.width>window.innerWidth-window.scrollX&&(l=d3.event.pageX-h.width-15),c+h.height>window.innerHeight-window.scrollY&&(c=d3.event.pageY-h.height-15),"relative"==s.style("position")?s.style("position","absolute").style("left",l+"px").style("top",c+"px"):s.style("left",l+"px").style("top",c+"px"),s.attr("z-index",1e4)}var r,a,o,i,t;return e.keys=function(t){return arguments.length?(r=t,e):r},e.values=function(t){return arguments.length?(a=t,e):a},e.header=function(t){return arguments.length?(o=t,e):o},e.data=function(t){return arguments.length?(i=t,e):i},e.selection=function(n){return arguments.length?(t=n,e):t},e}function I(){function t(t,r,a,s,h){var p,m="fill"==s?l:i;if(e(),"index"==c)p=void 0!=s?o(g(d(a)),m):g(d(a));else if("value"==c){y=u(t,r,a);p=void 0!=s?o(g(d(y)),m):g(d(y))}else if("category"==c){var v=f(t,r,a),y=n.indexOf(v);p=void 0!=s?o(g(d(y)),m):g(d(y))}else p=void 0!=s?o(g(d(a)),m):g(d(a));return p}function e(){h.domain([0,r.length]),"category"==c&&void 0!=n?h.range([0,n.length]):h.range(s);var t=Array(r.length).fill(0).map(function(t,e){return h(e)});d.domain(t)}var n,r=["#2c7bb6","#00a6ca","#00ccbc","#90eb9d","#ffff8c","#f9d057","#f29e2e","#e76818","#d7191c"],a=d3.interpolateRgb,o=y,i=0,l=.4,c="index",s=[0,r.length-1],u=function(t,e,n){return e},f=function(t,e,n){return e.category},d=d3.scaleLinear().interpolate(a).domain(s).range(r),h=d3.scaleLinear(),g=function(t){return"#"+t.match(/\d+/g).map(function(t,e){return(+t<16?"0":"")+(+t).toString(16)}).join("")};return t.colors=function(e){return arguments.length?(r=e,d.range(r),t):r},t.interpolation=function(e){return arguments.length?(a=e,d.interpolate(a).range(r),t):a},t.dataExtent=function(e){return arguments.length?(s=e,d.domain(s).interpolate(d.interpolate()),t):s},t.scale=function(e){return arguments.length?(e=e.domain(d.domain()).interpolate(d.interpolate()).range(d.range()),d=e,t):d},t.modifyOpacity=function(e){return arguments.length?(o=e,t):o},t.strokeOpacity=function(e){return arguments.length?(i=e,t):i},t.fillOpacity=function(e){return arguments.length?(l=e,t):l},t.colorBy=function(e){return arguments.length?(c=e,t):c},t.valueExtractor=function(e){return arguments.length?(u=e,t):u},t.categoryExtractor=function(e){return arguments.length?(f=e,t):f},t.categories=function(e){return arguments.length?(n=e,t):n},t}function J(t){function e(){var e={x:0,y:0,width:r,height:a},C=xt.sel.setupContainer(t,w,e,k);o=d3.keys(n),i=o.map(f),l=o.map(g),c=o.map(v);o.length;var E=[Math.min(...i)-u,Math.max(...i)+u],L=[Math.min(...l)-h,Math.max(...l)+h],F=[Math.min(...c)-m,Math.max(...c)+m];s.domain(E).range([0,r]),d.domain(L).range([a,0]),p.domain(F).range([y,x]);var M=C.selectAll("."+j),Q=(M=M.data(o)).enter().append("circle").attr("class",j).attr("cx",0).attr("cy",a).attr("r",0),q=M.exit();(M=M.merge(Q)).each(function(t,e){var r=d3.select(this),a=n[t],o=i[e],u=l[e],f=c[e],h=S(t,a,e,"fill"),g=S(t,a,e,"stroke");r.transition().duration(z).ease(O).attr("cx",s(o)).attr("cy",d(u)).attr("r",p(f)).attr("fill",h).attr("stroke",g).attr("stroke-width",b),r.on("mouseover",function(t,e){M.style("opacity",.2),r.style("opacity",1),r.transition().duration(z/2).ease(O).attr("stroke-width",2*b).attr("r",1.5*p(f))}),r.node().addEventListener("mouseout",function(){C.selectAll("."+j).style("opacity",1),r.transition().duration(z/2).ease(O).attr("stroke-width",b).attr("r",p(f))})}),q.transition().duration(z).ease(O).attr("cx",0).attr("cy",a).attr("r",0).remove(),A.selection(M).data(n),A()}var n,r,a,o,i,l,c,s=d3.scaleLinear(),u=.5,f=function(t,e){return n[t].x},d=d3.scaleLinear(),h=.5,g=function(t,e){return n[t].y},p=d3.scaleLinear(),m=.5,v=function(t,e){return 2},y=2,x=10,b=2,S=I(),k="transparent",w="d3sm-scatter",j="scatter-point",z=1e3,O=d3.easeExp,A=Z();return e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(n=t,e):n},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(a=t,e):a},e.scaleX=function(t){return arguments.length?(s=t,e):s},e.domainPaddingX=function(t){return arguments.length?(u=t,e):u},e.valueExtractorX=function(t){return arguments.length?(f=t,e):f},e.scaleY=function(t){return arguments.length?(d=t,e):d},e.domainPaddingY=function(t){return arguments.length?(h=t,e):h},e.valueExtractorY=function(t){return arguments.length?(g=t,e):g},e.scaleR=function(t){return arguments.length?(p=t,e):p},e.domainPaddingR=function(t){return arguments.length?(m=t,e):m},e.valueExtractorR=function(t){return arguments.length?(v=t,e):v},e.minRadius=function(t){return arguments.length?(y=t,e):y},e.maxRadius=function(t){return arguments.length?(x=t,e):x},e.pointStrokeWidth=function(t){return arguments.length?(b=t,e):b},e.colorFunction=function(t){return arguments.length?(S=t,e):S},e.backgroundFill=function(t){return arguments.length?(k=t,e):k},e.namespace=function(t){return arguments.length?(w=t,e):w},e.objectClass=function(t){return arguments.length?(j=t,e):j},e.transitionDuration=function(t){return arguments.length?(z=t,e):z},e.easeFunc=function(t){return arguments.length?(O=t,e):O},e.pointKeys=function(t){return arguments.length?(o=t,e):o},e.valuesX=function(t){return arguments.length?(i=t,e):i},e.valuesY=function(t){return arguments.length?(l=t,e):l},e.valuesR=function(t){return arguments.length?(c=t,e):c},e.tooltip=function(t){return arguments.length?(A=t,e):A},e}function U(t){function e(){var e="horizontal"==u||"bottom"==u||"top"==u,C=!e,E={x:0,y:0,width:r,height:a},L=xt.sel.setupContainer(t,k,E,S);i=d3.keys(n),l=i.map(d);var F=void 0==o?i.sort(h):o,M=(i=xt.arr.flatten(F)).length,Q=[Math.min(...l)-p,Math.max(...l)+p];g.domain(Q).range(e?[0,a]:"right"==u?[0,r]:[r,0]);var q=e?r:a;c=void 0==c?xt.math.calculateWidthOfObject(q,M,v,y,m,f):c,s=void 0==s?xt.math.calculateWidthOfSpacer(i,q,c,M,m,f):s;var B=G().horizontalQ(e).scale(g).moveby("category").numberOfObjects(M).objectClass(w).objectSize(c).spacerSize(s).transitionDuration(j).easeFunc(z).namespace(k),W=B.exitFunction();B.exitFunction(function(t){void 0==c&&console.log(t.nodes(),c),W(t),t.selectAll("g").classed("to-remove",!0),t.selectAll("* > rect").transition().duration(j).attr("transform",function(t,e){return"translate(0,"+(C?0:g(Q[1]))+")"}).attr("width",e?c:0).attr("height",C?c:0).remove()}),B(L,F,0);var V=[];L.selectAll("g:not(.to-remove)."+w).each(function(t,e){V.push(Number(d3.select(this).attr("parent-index")))}),b="index"==b.colorBy()?b.dataExtent([0,Math.max(...V)]):b.dataExtent(Q),L.selectAll("g."+w+":not(.to-remove)").each(function(t,r){var a=d3.select(this),o=(n[t],d(t,r)),r=void 0==a.attr("parent-index")?r:a.attr("parent-index"),i=b(t,o,r,"fill"),l=b(t,o,r,"stroke"),s=xt.sel.safeSelect(a,"rect","bar-rect");void 0==s.attr("transform")&&s.attr("transform",function(t,e){return"translate(0,"+(C?0:g(Q[1]))+")"}).attr("width",e?c:0).attr("height",C?c:0),s.transition().duration(j).ease(z).attr("transform",function(t,n){return"translate("+(e?c-c*A:"right"==u?g(Q[1])-g(o):c-c*A)+","+(C?c-c*A:g(Q[1])-g(o))+")"}).attr("width",e?c*A:g(o)).attr("height",C?c*A:g(o)).attr("fill",i).attr("stroke",l).attr("stroke-width",x),a.on("mouseover",function(t,e){L.selectAll("g."+w).style("opacity",.2),a.style("opacity",1),s.attr("stroke-width",2*x)}),a.on("mouseout",function(){L.selectAll("g."+w).style("opacity",1),s.attr("stroke-width",x)})}),O.selection(L.selectAll(".bar-rect")).data(n),O()}var n,r,a,o,i,l,c,s,u="horizontal",f=!1,d=function(t,e){return n[t]},h=function(t,e){return d3.descending(n[t],n[e])},g=d3.scaleLinear(),p=.5,m=.05,v=50,y=100,x=2,b=I(),S="transparent",k="d3sm-bar",w="bar",j=1e3,z=d3.easeExp,O=Z(),A=1;return e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(n=t,e):n},e.orient=function(t){return arguments.length?(u=t,e):u},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(a=t,e):a},e.overflowQ=function(t){return arguments.length?(f=t,e):f},e.grouping=function(t){return arguments.length?(o=t,e):o},e.valueExtractor=function(t){return arguments.length?(d=t,e):d},e.sortingFunction=function(t){return arguments.length?(h=t,e):h},e.scale=function(t){return arguments.length?(g=t,e):g},e.domainPadding=function(t){return arguments.length?(p=t,e):p},e.objectSpacer=function(t){return arguments.length?(m=t,e):m},e.minObjectSize=function(t){return arguments.length?(v=t,e):v},e.maxObjectSize=function(t){return arguments.length?(y=t,e):y},e.barStrokeWidth=function(t){return arguments.length?(x=t,e):x},e.colorFunction=function(t){return arguments.length?(b=t,e):b},e.backgroundFill=function(t){return arguments.length?(S=t,e):S},e.namespace=function(t){return arguments.length?(k=t,e):k},e.objectClass=function(t){return arguments.length?(w=t,e):w},e.transitionDuration=function(t){return arguments.length?(j=t,e):j},e.easeFunc=function(t){return arguments.length?(z=t,e):z},e.barKeys=function(t){return arguments.length?(i=t,e):i},e.barValues=function(t){return arguments.length?(l=t,e):l},e.objectSize=function(t){return arguments.length?(c=t,e):c},e.spacerSize=function(t){return arguments.length?(s=t,e):s},e.tooltip=function(t){return arguments.length?(O=t,e):O},e.barPercent=function(t){return arguments.length?(A=t,e):A},e}function $(t){function e(){var e={x:0,y:0,width:r,height:a},g=xt.sel.setupContainer(t,L,e,E);(o=d3.keys(n)).sort(function(t,e){return q(t,e)||B(t,e)}),xt.con.log("bubbleHeatmap","cells are sorted by",o),i=xt.arr.unique(o.map(y)),l=xt.arr.unique(o.map(x)),c=xt.arr.unique(o.map(b)),s=xt.arr.unique(o.map(S)),xt.con.log("bubbleHeatmap","x and y keys are",{x:i,y:l});var p=i.length,m=l.length,v=[Math.min(...c)-j,Math.max(...c)+j];d=xt.math.calculateWidthOfObject(a,m,O,A,z,k),u=xt.math.calculateWidthOfObject(r,p,O,A,z,k),h=xt.math.calculateWidthOfSpacer(l,a,d,m,z,k),f=xt.math.calculateWidthOfSpacer(i,r,u,p,z,k),xt.con.log("bubbleHeatmap","size of",{x:u,y:d}),w.domain(v).range([Math.min(O/2,Math.min(d,u)/2),Math.min(d,u)/2]);var D=G().horizontalQ(!1).moveby("category").numberOfObjects(m).objectClass(xt.str.hypenate(F,"row")).objectSize(d).spacerSize(h).transitionDuration(M).easeFunc(Q).namespace("row"),K=G().horizontalQ(!0).moveby("category").numberOfObjects(p).objectClass(F).objectSize(u).spacerSize(f).transitionDuration(M).easeFunc(Q);D(g,l,0),g.selectAll("g."+xt.str.hypenate(F,"row")).each(function(t,e){K(d3.select(this),i,0)});var _=g.selectAll("g:not(.to-remove)."+F).data(o),P=[];_.each(function(t,e){P.push(Number(d3.select(this).attr("parent-index")))}),W="index"==W.colorBy()?W.dataExtent([0,Math.max(...P)]):W.dataExtent([0,Math.max(...s)]),_.each(function(t,e){xt.con.log("bubbleHeatmap","each cell",{key:t,index:e,node:d3.select(this).node()});var r=d3.select(this),a=(n[t],S(t,e)),o=b(t,e),e=void 0==r.attr("parent-index")?e:r.attr("parent-index"),i=W(t,a,e,"fill"),l=W(t,a,e,"stroke");xt.con.log("bubbleHeatmap","radius",{radius:o,scaled:w(o),extent:v,range:w.range()}),xt.sel.safeSelect(r,"circle",xt.str.hypenate(F,"circle")).attr("cx",u/2).attr("cy",d/2).attr("r",w(o)).attr("fill",i).attr("stroke",l).attr("stroke-width",C)}),V.selection(_.selectAll("circle."+xt.str.hypenate(F,"circle"))).data(n),V()}var n,r,a,o,i,l,c,s,u,f,d,h,g="x",p="y",m="r",v="v",y=function(t,e){return n[t][g]},x=function(t,e){return n[t][p]},b=function(t,e){return n[t][m]},S=function(t,e){return n[t][v]},k=!1,w=d3.scaleLinear(),j=.5,z=0,O=50,A=100,C=2,E="transparent",L="d3sm-bubble",F="bubble",M=1e3,Q=d3.easeExp,q=function(t,e){return y(t)-y(e)},B=function(t,e){return x(t)-x(e)},W=I().colorBy("value"),V=Z();return e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(n=t,e):n},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(a=t,e):a},e.xKey=function(t){return arguments.length?(g=t,e):g},e.yKey=function(t){return arguments.length?(p=t,e):p},e.rKey=function(t){return arguments.length?(m=t,e):m},e.vKey=function(t){return arguments.length?(v=t,e):v},e.cellKeys=function(t){return arguments.length?(o=t,e):o},e.xValues=function(t){return arguments.length?(i=t,e):i},e.yValues=function(t){return arguments.length?(l=t,e):l},e.rValues=function(t){return arguments.length?(c=t,e):c},e.vValues=function(t){return arguments.length?(s=t,e):s},e.xExtractor=function(t){return arguments.length?(y=t,e):y},e.yExtractor=function(t){return arguments.length?(x=t,e):x},e.rExtractor=function(t){return arguments.length?(b=t,e):b},e.vExtractor=function(t){return arguments.length?(S=t,e):S},e.overflowQ=function(t){return arguments.length?(k=t,e):k},e.scale=function(t){return arguments.length?(w=t,e):w},e.domainPadding=function(t){return arguments.length?(j=t,e):j},e.objectSpacer=function(t){return arguments.length?z=t:n},e.minObjectSize=function(t){return arguments.length?(O=t,e):O},e.maxObjectSize=function(t){return arguments.length?(A=t,e):A},e.bubbleStrokeWidth=function(t){return arguments.length?(C=t,e):C},e.backgroundFill=function(t){return arguments.length?(E=t,e):E},e.namespace=function(t){return arguments.length?(L=t,e):L},e.objectClass=function(t){return arguments.length?(F=t,e):F},e.transitionDuration=function(t){return arguments.length?(M=t,e):M},e.easeFunc=function(t){return arguments.length?(Q=t,e):Q},e.tooltip=function(t){return arguments.length?(V=t,e):V},e.colorFunction=function(t){return arguments.length?(W=t,e):W},e.xSize=function(t){return arguments.length?(u=t,e):u},e.xSpacerSize=function(t){return arguments.length?(f=t,e):f},e.ySize=function(t){return arguments.length?(d=t,e):d},e.ySpacerSize=function(t){return arguments.length?(h=t,e):h},e}function tt(t){function e(){var e="horizontal"==u,d=!e,F={x:0,y:0,width:r,height:a},M=xt.sel.setupContainer(t,O,F,z),Q=void 0==o?d3.keys(n).sort(p):o;i=xt.arr.flatten(Q),l=i.map(g);var q=i.length,B=[Math.min(...l.map(function(t,e){return t[h[0]]}))-v,Math.max(...l.map(function(t,e){return t[h[4]]}))+v];m.domain(B).range(e?[0,a]:[r,0]);var W=e?r:a;c=xt.math.calculateWidthOfObject(W,q,x,b,y,f),s=xt.math.calculateWidthOfSpacer(i,W,c,q,y,f),G().horizontalQ(e).scale(m).moveby("category").numberOfObjects(q).objectClass(A).objectSize(c).spacerSize(s).transitionDuration(C).easeFunc(E).namespace(O)(M,Q,0);var V=[];M.selectAll("g:not(.to-remove)."+A).each(function(t,e){xt.arr.hasQ(i,t)&&V.push(Number(d3.select(this).attr("parent-index")))}),k="index"==k.colorBy()?k.dataExtent([0,Math.max(...V)]):k.dataExtent(B),M.selectAll("g:not(.to-remove)."+A).each(function(t,r){var a=d3.select(this),o=(n[t],g(t,r)),i=o[h[0]],l=o[h[1]],s=o[h[2]],f=o[h[3]],p=o[h[4]],r=void 0==a.attr("parent-index")?r:a.attr("parent-index"),v=k(t,s,r,"fill"),y=k(t,s,r,"stroke"),x=xt.sel.safeSelect(a,"g","whisker"),b=xt.sel.safeSelect(x,"path","upper"),z=xt.sel.safeSelect(x,"path","lower"),O=xt.sel.safeSelect(a,"g","quartile"),A=xt.sel.safeSelect(O,"rect","upper"),L=xt.sel.safeSelect(O,"rect","lower"),F=xt.sel.safeSelect(O,"circle","median");A.transition().duration(C).ease(E).attr("width",e?c:m(f)-m(s)).attr("height",d?c:m(f)-m(s)).attr("fill",v).attr("stroke",y).attr("stroke-width",w).attr("transform",function(t,n){return"translate("+(e?0:m(s))+","+(d?0:m(B[1])-m(f))+")"}),L.transition().duration(C).ease(E).attr("width",e?c:m(s)-m(l)).attr("height",d?c:m(s)-m(l)).attr("fill",v).attr("stroke",y).attr("stroke-width",w).attr("transform",function(t,n){return"translate("+(e?0:m(l))+","+(d?0:m(B[1])-m(s))+")"}),F.transition().duration(C).ease(E).attr("r",function(t,e){var n=c/2,r=(m(f)-m(l))/2;return n>r?r:n}).attr("fill",v).attr("stroke",y).attr("stroke-width",w).attr("transform",function(t,n){return"translate("+(e?c/2:m(s))+","+(d?c/2:m(B[1])-m(s))+")"}),z.transition().duration(C).ease(E).attr("d",function(t,n){var r=e?m(l)-m(i):c,a=d?m(l)-m(i):c;return xt.paths.whiskerPath(!1,0,0,a,r,S,u)}).attr("transform",function(t,n){return"translate("+(e?0:m(l))+","+(d?0:m(B[1])-m(l))+")"}).attr("stroke","black").attr("stroke-width",j).attr("fill","none"),b.transition().duration(C).ease(E).attr("d",function(t,n){var r=e?m(p)-m(f):c,a=d?m(p)-m(f):c;return xt.paths.whiskerPath(!0,0,0,a,r,S,u)}).attr("transform",function(t,n){return"translate("+(e?0:m(f))+","+(d?0:m(B[1])-m(p))+")"}).attr("stroke","black").attr("stroke-width",j).attr("fill","none")}),L.selection(M.selectAll("g:not(.to-remove)."+A)).data(n),L()}var n,r,a,o,i,l,c,s,u="horizontal",f=!0,d="quartiles",h=["0.00","0.25","0.50","0.75","1.00"],g=function(t,e){return n[t][d]},p=function(t,e){return d3.descending(g(t)[h[4]],g(e)[h[4]])},m=d3.scaleLinear(),v=.5,y=.05,x=15,b=50,S=.6,k=I(),w=2,j=2,z="transparent",O="d3sm-box-whisker",A="box-whisk",C=1e3,E=d3.easeExp,L=Z();return e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(n=t,e):n},e.orient=function(t){return arguments.length?(u=t,e):u},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(a=t,e):a},e.overflowQ=function(t){return arguments.length?(f=t,e):f},e.grouping=function(t){return arguments.length?(o=t,e):o},e.quartilesKey=function(t){return arguments.length?(d=t,e):d},e.quartilesKeys=function(t){return arguments.length?(h=t,e):h},e.valueExtractor=function(t){return arguments.length?(g=t,e):g},e.sortingFunction=function(t){return arguments.length?(p=t,e):p},e.scale=function(t){return arguments.length?(m=t,e):m},e.domainPadding=function(t){return arguments.length?(v=t,e):v},e.objectSpacer=function(t){return arguments.length?(y=t,e):y},e.minObjectSize=function(t){return arguments.length?(x=t,e):x},e.maxObjectSize=function(t){return arguments.length?(b=t,e):b},e.whiskerWidthPercent=function(t){return arguments.length?(S=t,e):S},e.colorFunction=function(t){return arguments.length?(k=t,e):k},e.boxStrokeWidth=function(t){return arguments.length?(w=t,e):w},e.whiskerStrokeWidth=function(t){return arguments.length?(j=t,e):j},e.backgroundFill=function(t){return arguments.length?(z=t,e):z},e.namespace=function(t){return arguments.length?(O=t,e):O},e.objectClass=function(t){return arguments.length?(A=t,e):A},e.transitionDuration=function(t){return arguments.length?(C=t,e):C},e.easeFunc=function(t){return arguments.length?(E=t,e):E},e.boxKeys=function(t){return arguments.length?(i=t,e):i},e.boxValues=function(t){return arguments.length?(l=t,e):l},e.objectSize=function(t){return arguments.length?(c=t,e):c},e.spacerSize=function(t){return arguments.length?(s=t,e):s},e.tooltip=function(t){return arguments.length?(L=t,e):L},e}function et(t){function e(){var e={x:0,y:0,width:r,height:a},h=xt.sel.setupContainer(t,A,e,O);o=d3.keys(n),i=xt.arr.unique(o.map(m)),l=xt.arr.unique(o.map(v)),c=xt.arr.unique(o.map(y)),o.sort(function(t,e){return F(t,e)||M(t,e)}),xt.con.log("heatmap","cells are sorted by",o),xt.con.log("heatmap","x and y keys are",{x:i,y:l});var g=i.length,p=l.length;f=xt.math.calculateWidthOfObject(a,p,S,j,b,x),s=xt.math.calculateWidthOfObject(r,g,k,w,b,x),d=xt.math.calculateWidthOfSpacer(l,a,f,p,b,x),u=xt.math.calculateWidthOfSpacer(i,r,s,g,b,x),xt.con.log("heatmap","size of",{x:s,y:f});var B=G().horizontalQ(!1).moveby("category").numberOfObjects(p).objectClass(xt.str.hypenate(C,"row")).objectSize(f+d).spacerSize(0).transitionDuration(E).easeFunc(L).namespace("row"),W=G().horizontalQ(!0).moveby("category").numberOfObjects(g).objectClass(C).objectSize(s+u).spacerSize(0).transitionDuration(E).easeFunc(L);B(h,l,0),h.selectAll("g."+xt.str.hypenate(C,"row")).each(function(t,e){W(d3.select(this),i,0)});var V=h.selectAll("g:not(.to-remove)."+C);if(o.length!=l.length*i.length){var D={};o.map(function(t,e){D[m(t)+"::"+v(t)]=t});for(var K=[],_=0;_.5?l:-l}).attr("cx",function(t,n){var r=o.pointValues[n];if(e){var a=xt.arr.whichBin(o.binned,r),i=Math.random(),l=T(i*o.frequencies[a]*.5);return Math.random()>.5?l:-l}return p(r)}).attr("stroke",function(t,e){var t=o.pointValues[e];return k(t,"stroke",s,z,O)}).attr("fill",function(t,e){var t=o.pointValues[e];return k(t,"fill",s,z,O)}).attr("stroke-width",j),v.selectAll("circle.point").on("mouseover",function(t,e){B.selectAll("g."+A).style("opacity",.2),a.style("opacity",1),f.attr("stroke-width",2*b),h.attr("stroke-width",2*b),B.selectAll(".point").style("opacity",.2),d3.select(this).style("opacity",1).attr("r",2*w).attr("stroke-width",2*j)}),v.selectAll("circle.point").on("mouseout",function(t,e){var n=document.createEvent("SVGEvents");n.initEvent("mouseout",!0,!0),u.node().dispatchEvent(n),B.selectAll(".point").style("opacity",1),d3.select(this).attr("stroke-width",j).attr("r",w)})}else cV.selectAll(".point").transition().duration(C).ease(E).attr("r",0).attr("cy",e?p(P[1])-p(m):T(0)).attr("cx",e?T(0):p(m)).remove()}}),F.selection(B.selectAll("g:not(.to-remove)."+A+" .area")),void 0==F.data()&&F.data(n),F(),void 0==F.values()&&F.values([function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]},function(t,e){return t.quartiles[e]}])}var n,r,a,o,i,l,c,s,u="horizontal",f=!0,d=!0,h=function(t,e){return n[t]},g=function(t,e){return d3.descending(n[t],n[e])},p=d3.scaleLinear(),m=.5,v=.05,y=50,x=100,b=2,S=I(),k=function(t,e,n,r,a){var o=d3.scaleLinear().domain([r,a]).range([-.25,.05]),i=xt.color.modifyHexidecimalColorLuminance(n.replace("#",""),o(t)),l="stroke"==e?0:.25;return xt.color.modifyHexidecimalColorLuminance(i.replace("#",""),l)},w=3,j=2,z="transparent",O="d3sm-violin",A="violin",C=1e3,E=d3.easeExp,L=["Q0","Q1","Q2","Q3","Q4"],F=Z().keys([L[4],L[3],L[2],L[1],L[0]]),M=Z(),Q=function(t,e){return e.points},q=function(t,e){return e[t].value};return e.violinPointsExtractor=function(t){return arguments.length?(Q=t,e):Q},e.violinPointValueExtractor=function(t){return arguments.length?(q=t,e):q},e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(n=t,e):n},e.orient=function(t){return arguments.length?(u=t,e):u},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(a=t,e):a},e.overflowQ=function(t){return arguments.length?(f=t,e):f},e.pointsQ=function(t){return arguments.length?(d=t,e):d},e.grouping=function(t){return arguments.length?(o=t,e):o},e.valueExtractor=function(t){return arguments.length?(h=t,e):h},e.sortingFunction=function(t){return arguments.length?(g=t,e):g},e.scale=function(t){return arguments.length?(p=t,e):p},e.domainPadding=function(t){return arguments.length?(m=t,e):m},e.objectSpacer=function(t){return arguments.length?(v=t,e):v},e.minObjectSize=function(t){return arguments.length?(y=t,e):y},e.maxObjectSize=function(t){return arguments.length?(x=t,e):x},e.objectStrokeWidth=function(t){return arguments.length?(b=t,e):b},e.colorFunction=function(t){return arguments.length?(S=t,e):S},e.pointColorFunc=function(t){return arguments.length?(k=t,e):k},e.pointRadius=function(t){return arguments.length?(w=t,e):w},e.pointStrokeWidth=function(t){return arguments.length?(j=t,e):j},e.backgroundFill=function(t){return arguments.length?(z=t,e):z},e.namespace=function(t){return arguments.length?(O=t,e):O},e.objectClass=function(t){return arguments.length?(A=t,e):A},e.transitionDuration=function(t){return arguments.length?(C=t,e):C},e.easeFunc=function(t){return arguments.length?(E=t,e):E},e.quartileKey=function(t){return arguments.length?(quartileKey=t,e):quartileKey},e.quartileKeys=function(t){return arguments.length?(L=t,e):L},e.violinKeys=function(t){return arguments.length?(i=t,e):i},e.violinValues=function(t){return arguments.length?(l=t,e):l},e.objectSize=function(t){return arguments.length?(c=t,e):c},e.spacerSize=function(t){return arguments.length?(s=t,e):s},e.tooltip=function(t){return arguments.length?(F=t,e):F},e.pointsTooltip=function(t){return arguments.length?(M=t,e):M},e}function rt(){function t(t,o){var i=o[t],l=e(t,i),c=d3.keys(l),s=c.map(function(t,e){return n(t,l)}),u=xt.math.quartiles(s,a),f=d3.histogram()(s),d=f.map(t=>t.length),h=r?{x:0,y:d3.min(s)}:{x:d3.min(s),y:0},g=r?{x:0,y:d3.max(s)}:{x:d3.max(s),y:0},p=f.map(function(t,e){return r?{y:t.length?d3.median(t):d3.median([t.x0,t.x1]),x:d[e]}:{x:t.length?d3.median(t):d3.median([t.x0,t.x1]),y:d[e]}});return p=[h].concat(p).concat([g]),i.binned=f,i.frequencies=d,i.contour=p,i.quartiles=u,i.pointKeys=c,i.pointValues=s,i}var e,n,r=!0,a=["Q0","Q1","Q2","Q3","Q4"];return t.horizontalQ=function(e){return arguments.length?(r=e,t):r},t.quartileKeys=function(e){return arguments.length?(a=e,t):a},t.violinPointsExtractor=function(n){return arguments.length?(e=n,t):e},t.violinPointValueExtractor=function(e){return arguments.length?(n=e,t):n},t}function at(t){function e(){var e="horizontal"==p,n=!e,r={x:0,y:0,width:o,height:i},x=xt.sel.setupContainer(t,S,r,b);l=d3.keys(a),c=xt.arr.unique(l.map(z)).sort(),s=xt.arr.unique(l.map(O)).sort().sort(function(t,e){return t.split(";").length-e.split(";").length}),e?l.sort(function(t,e){return F(t,e)||L(t,e)}):l.sort(function(t,e){return L(t,e)||F(t,e)});var A=e?s:c,M=e?c:s,Q=e?A.length:M.length,q=e?M.length:A.length;h=xt.math.calculateWidthOfObject(o,Q,v,y,C,m),f=xt.math.calculateWidthOfObject(i,q,v,y,E,m),g=xt.math.calculateWidthOfSpacer(A,o,h,Q,C,m),d=xt.math.calculateWidthOfSpacer(M,i,f,q,E,m);var B=G().horizontalQ(!1).moveby("category").numberOfObjects(q).objectSize(f).spacerSize(d).transitionDuration(w).easeFunc(j),W=G().horizontalQ(!0).moveby("category").numberOfObjects(Q).objectClass(k).objectSize(h).spacerSize(g).transitionDuration(w).easeFunc(j);n?(W.objectClass(k),B.namespace("across").objectClass(xt.str.hypenate(k,"across")),B(x,M,0),x.selectAll("g."+xt.str.hypenate(k,"across")).each(function(t,e){W(d3.select(this),A,0)})):(W.namespace("across").objectClass(xt.str.hypenate(k,"across")),B.objectClass(k),W(x,A,0),x.selectAll("g."+xt.str.hypenate(k,"across")).each(function(t,e){B(d3.select(this),M,0)}));var V=x.selectAll("g:not(.to-remove)."+k),D={};l.map(function(t,e){D[z(t)+"::"+O(t)]=t}),V.data(l),V.each(function(t,e){var n=d3.select(this);if(void 0!=t){a[t];var r=z(t,e),o=O(t,e);n.classed(o,!0),n.classed(r,!0),xt.sel.safeSelect(n,"circle",xt.str.hypenate(k,"circle")).attr("cx",h/2).attr("cy",f/2).attr("r",void 0==u?Math.min(h,f)/2:u).attr("fill",o.includes(r)?"black":"rgb(233,233,233)").attr("stroke","black").attr("in-intersection",o.includes(r))}})}function n(){var t={};return s.map(function(e,n){t[e]={total:0}}),l.map(function(e,n){var r=A(e,n);0==t[O(e,n)].total&&(Array.isArray(r)?(t[O(e,n)].total+=r.length,t[O(e,n)].values=r):t[O(e,n)].total+=r)}),t}function r(){var t={};return c.map(function(e,n){t[e]={total:0}}),l.map(function(e,n){var r=A(e,n);Array.isArray(r)?t[z(e,n)].total+=r.length:t[z(e,n)].total+=r}),t}var a,o,i,l,c,s,u,f,d,h,g,p="horizontal",m=!1,v=20,y=50,x=2,b="transparent",S="d3sm-upset",k="upset",w=1e3,j=d3.easeExp,z=function(t,e){return a[t].set},O=function(t,e){return a[t].intersection},A=function(t,e){return a[t].elements},C=.05,E=.05,L=function(t,e){return c.indexOf(z(t))-c.indexOf(z(e))},F=function(t,e){return s.indexOf(O(t))-s.indexOf(O(e))};return e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(a=t,e):a},e.orient=function(t){return arguments.length?(p=t,e):p},e.spaceX=function(t){return arguments.length?(o=t,e):o},e.spaceY=function(t){return arguments.length?(i=t,e):i},e.overflowQ=function(t){return arguments.length?(m=t,e):m},e.minObjectSize=function(t){return arguments.length?(v=t,e):v},e.maxObjectSize=function(t){return arguments.length?(y=t,e):y},e.circleStrokeWidth=function(t){return arguments.length?(x=t,e):x},e.backgroundFill=function(t){return arguments.length?(b=t,e):b},e.namespace=function(t){return arguments.length?(S=t,e):S},e.objectClass=function(t){return arguments.length?(k=t,e):k},e.transitionDuration=function(t){return arguments.length?(w=t,e):w},e.easeFunc=function(t){return arguments.length?(j=t,e):j},e.cellKeys=function(t){return arguments.length?(l=t,e):l},e.setValues=function(t){return arguments.length?(c=t,e):c},e.intersectionValues=function(t){return arguments.length?(s=t,e):s},e.xObjectSpacer=function(t){return arguments.length?(C=t,e):C},e.yObjectSpacer=function(t){return arguments.length?(E=t,e):E},e.radius=function(t){return arguments.length?(u=t,e):u},e.setExtractor=function(t){return arguments.length?(z=t,e):z},e.intersectionExtractor=function(t){return arguments.length?(O=t,e):O},e.elementExtractor=function(t){return arguments.length?(A=t,e):A},e.setKeySortingFunction=function(t){return arguments.length?(L=t,e):L},e.intersectionKeySortingFunction=function(t){return arguments.length?(F=t,e):F},e.yObjectSize=function(t){return arguments.length?(f=t,e):f},e.ySpacerSize=function(t){return arguments.length?(d=t,e):d},e.xObjectSize=function(t){return arguments.length?(h=t,e):h},e.xSpacerSize=function(t){return arguments.length?(g=t,e):g},e.intersectionTotals=n,e.setTotals=r,e}function ot(t){function e(){var e="horizontal"==l,r=!e,s={x:0,y:0,width:a,height:o},S=xt.sel.setupContainer(t,v,s,m);p.dataExtent([0,n.length-1]).colorBy("categories").categoryExtractor(function(t,e,n){return e});var k=Math.min(a,o)/2,w=n.length,j=void 0==i?n.sort(u):i,z=xt.arr.flatten(j),O=e?a:o,A=xt.math.calculateWidthOfObject(O,w,d,h,f,c),C=xt.math.calculateWidthOfSpacer(z,O,A,w,f,c);G().horizontalQ(e).scale(scale).moveby("category").numberOfObjects(w).objectClass(y).objectSize(A).spacerSize(C).transitionDuration(x).easeFunc(b).namespace(v)(S,j,0);k=Math.min(A,a,o)/2-g;S.selectAll("g:not(.to-remove)."+y).each(function(t,n){var o=d3.select(this),i=xt.sel.safeSelect(o,"circle"),l=p(void 0,t,n,"fill"),c=p(void 0,t,n,"stroke"),s=e?k+g:(a-2*k)/2+k,u=r?k+g:(a-2*k)/2+k;i.attr("r",k).attr("cx",s).attr("cy",u).attr("fill",l).attr("stroke",c).attr("stroke-width",g);var f=xt.sel.safeSelect(o,"text");f.text(t).attr("text-anchor","middle").attr("transform",function(t,e){return"translate("+s+","+(u+f.node().getBoundingClientRect().height/4)+")"})})}var n,r,a,o,i,l="horizontal",c=!1,s=function(t,e){return r[t]},u=function(t,e){return d3.ascending(t,e)},f=.05,d=10,h=100,g=2,p=I(),m="transparent",v="d3sm-legend",y="legend",x=1e3,b=d3.easeExp;return e.categories=function(t){return arguments.length?(n=t,e):n},e.selection=function(n){return arguments.length?(t=n,e):t},e.data=function(t){return arguments.length?(r=t,e):r},e.orient=function(t){return arguments.length?(l=t,e):l},e.spaceX=function(t){return arguments.length?(a=t,e):a},e.spaceY=function(t){return arguments.length?(o=t,e):o},e.overflowQ=function(t){return arguments.length?(c=t,e):c},e.grouping=function(t){return arguments.length?(i=t,e):i},e.valueExtractor=function(t){return arguments.length?(s=t,e):s},e.sortingFunction=function(t){return arguments.length?(u=t,e):u},e.objectSpacer=function(t){return arguments.length?(f=t,e):f},e.minObjectSize=function(t){return arguments.length?(d=t,e):d},e.maxObjectSize=function(t){return arguments.length?(h=t,e):h},e.bubbleStrokeWidth=function(t){return arguments.length?(g=t,e):g},e.colorFunction=function(t){return arguments.length?(p=t,e):p},e.backgroundFill=function(t){return arguments.length?(m=t,e):m},e.namespace=function(t){return arguments.length?(v=t,e):v},e.objectClass=function(t){return arguments.length?(y=t,e):y},e.transitionDuration=function(t){return arguments.length?(x=t,e):x},e.easeFunc=function(t){return arguments.length?(b=t,e):b},e}function it(t){function e(){var e={x:0,y:0,width:r,height:a},f=xt.sel.setupContainer(t,c,e,u),d=xt.sel.safeSelect(t,"defs"),h=xt.sel.safeSelect(d,"linearGradient").attr("x1","0%").attr("y1","100%").attr("x2","0%").attr("y2","0%").attr("id",xt.str.hypenate(c,"numerical-legend-gradient"));l.dataExtent([o,i]).colorBy("value").valueExtractor(function(t,e,n){return e}),h.selectAll("stop").data(l.colors()).enter().append("stop").attr("offset",function(t,e){return e/(l.colors().length-1)}).attr("stop-color",function(t){return t});var g=xt.sel.safeSelect(f,"rect","legend").attr("transform","translate(0,"+s+")").style("fill","url(#"+xt.str.hypenate(c,"numerical-legend-gradient")+")").attr("x",0).attr("y",0).attr("width",r).attr("height",a-2*s).on("mousemove",function(t,e){n(t,e,g,d3.select(this))}).on("mouseout",function(t,e){d3.select("#"+xt.str.hypenate(c,"legend-tooltip")).remove()});xt.sel.safeSelect(f,"text","min").text(xt.math.round(o,2)).attr("text-anchor","middle").attr("font-size",s+"px").attr("transform",function(t,e){return"translate("+r/2+","+(a-s/4)+")"}),xt.sel.safeSelect(f,"text","max").text(xt.math.round(i,2)).attr("text-anchor","middle").attr("font-size",s+"px").attr("transform",function(t,e){return"translate("+r/2+","+s+")"})}function n(t,e,n,r){var a=d3.scaleLinear().domain([0,n.attr("height")]).range([i,o]),u=d3.mouse(n.node()),h=xt.math.round(a(u[1]),d),g=l(void 0,h,void 0,"stroke"),p=l(void 0,h,void 0,"fill"),m=xt.sel.safeSelect(d3.select("body"),"div",xt.str.hypenate(c,"legend-tooltip")).attr("id",xt.str.hypenate(c,"legend-tooltip")).style("position","absolute").style("left",d3.event.pageX+15+"px").style("top",d3.event.pageY+15+"px").style("background-color",p).style("border-color",g).style("min-width",s*(String(i).split(".")[0].length+3)+"px").style("min-height",s*(String(i).split(".")[0].length+3)+"px").style("border-radius","50%").style("border-radius","5000px").style("display","flex").style("justify-content","center").style("text-align","middle").style("padding","2px").style("border-style","solid").style("border-width",2);xt.sel.safeSelect(m,"div").text(h).style("color",f).style("align-self","center")}var r,a,o=0,i=1,l=I(),c="d3sm-linear-vertical-gradient",s=12,u="transparent",f="black",d=2;return e.min=function(t){return arguments.length?(o=t,e):o},e.max=function(t){return arguments.length?(i=t,e):i},e.spaceX=function(t){return arguments.length?(r=t,e):r},e.spaceY=function(t){return arguments.length?(a=t,e):a},e.namespace=function(t){return arguments.length?(c=t,e):c},e.fontSize=function(t){return arguments.length?(s=t,e):s},e.backgroundFill=function(t){return arguments.length?(u=t,e):u},e.colorFunction=function(t){return arguments.length?(l=t,e):l},e.textColor=function(t){return arguments.length?(f=t,e):f},e.roundTo=function(t){return arguments.length?(d=t,e):d},e}function lt(t){var e=t.attr("transform"),[n,r]=e.split("translate("),[a,o]=r.split(",");return o.split(")"),[parseFloat(a),parseFloat(o)]}function ct(t){function e(){z&&i()}function n(t){z=void 0!=t?t:!z,y=lt(v),x=lt(p),z?g.node().addEventListener("mousedown",o,!0):(g.node().removeEventListener("mousedown",o,!0),a())}function r(){y=lt(v),x=lt(p);var t=xt.sel.safeSelect(p,"g","lasso-container").selectAll('path[instance="'+E+'"]'),e=((t=t.data(A)).exit().remove(),t.enter().append("path"));l(t=t.merge(e))}function a(){var t=xt.sel.safeSelect(p,"g","lasso-container");t.selectAll('path[instance="'+E+'"]').remove();t.remove(),p.selectAll(m).classed("in-lasso",!1),f()}function o(t){t.preventDefault(),t.stopPropagation();var e=xt.sel.safeSelect(p,"g","lasso-container");O=[],g.node().addEventListener("mousemove",c),g.node().addEventListener("mouseup",function(t){g.node().removeEventListener("mousemove",c),A.push(O),A=xt.arr.unique(A)}),l(w=e.append("path").data([O]))}function i(){var t=xt.sel.safeSelect(p,"g","lasso-container").selectAll('path[instance="'+E+'"]'),e=((t=t.data(A)).exit().remove(),t.enter().append("path"));l(t=t.merge(e).transition().duration(_).ease(P))}function l(t){t.attr("class",xt.str.hypenate(j,"lasso-path")).style("opacity",Q).attr("fill",F).attr("d",C).attr("instance",E).style("stroke-dasharray",q).attr("stroke",B).attr("stroke-width",W).style("animation","lassoDash "+M+" linear").style("animation-iteration-count","infinite")}function c(t){if(void 0!=b&&b.dispatch(xt.str.hypenate(j,"drag")),1==t.which){d3.event=t;var e=d3.mouse(p.node()),e=d3.mouse(g.node());if(void 0!=S&&(e[0]=S.invert(e[0])),void 0!=k&&(e[1]=k.invert(e[1])),e[0]=e[0]-y[0]-x[0],e[1]=e[1]-y[1]-x[1],O.length){var n=O[O.length-1],r=[e[0],e[1]],a=[n[0],n[1]];S&&(a[0]=S(a[0]),r[0]=S(r[0])),k&&(a[1]=k(a[1]),r[1]=k(r[1])),xt.math.euclideanDistance(a,r)>L&&s(e)}else s(e)}}function s(t){if(void 0!=t){if(O.push(t),w.attr("d",C),O.length<3)return;u(A.concat([O]))}else u(A)}function u(t){return void 0==t&&(t=A),p.selectAll(m).each(function(e,n){var r=d3.select(this),a=r.absolutePosition(),o=[[a.left-y[0]-x[0],a.top-y[1]-x[1]],[a.right-y[0]-x[0],a.top-y[1]-x[1]],[a.left-y[0]-x[0],a.bottom-y[1]-x[1]],[a.right-y[0]-x[0],a.bottom-y[1]-x[1]]];void 0!=S&&(o[0][0]=S.invert(o[0][0]),o[1][0]=S.invert(o[1][0]),o[2][0]=S.invert(o[2][0]),o[3][0]=S.invert(o[3][0])),void 0!=k&&(o[0][1]=k.invert(o[0][1]),o[1][1]=k.invert(o[1][1]),o[2][1]=k.invert(o[2][1]),o[3][1]=k.invert(o[3][1]));for(var i=!1,n=0;nd3.polygonContains(l,t))&&(i=!0)}r.classed("in-lasso",i),r.classed("in-lasso-"+E,i)}),f(),p.selectAll(".in-lasso-"+E)}function f(){p.selectAll(m).each(function(t,e){var n=d3.select(this);d(n,n.classed("in-lasso"))})}function d(t,e){var n=t.attr("_pre_lasso_fill"),r=t.attr("_pre_lasso_stroke"),a=t.attr("_pre_lasso_stroke-width");e?(t.classed("in-lasso",!0),t.classed("in-lasso-"+E,!0),void 0==n&&t.attr("_pre_lasso_fill",t.attr("fill")),void 0==r&&t.attr("_pre_lasso_stroke",t.attr("stroke")),void 0==a&&t.attr("_pre_lasso_stroke-width",t.attr("stroke-width")),t.attr("fill",V).attr("stroke",D).attr("stoke-width",K)):(t.classed("in-lasso",!1),t.classed("in-lasso-"+E,!1),void 0!=n&&t.attr("fill",n),void 0!=r&&t.attr("stroke",r),void 0!=a&&t.attr("stroke-width",a))}function h(){d3.select("html").select("style."+xt.str.hypenate(j,"lasso-dash")).empty()&&d3.select("html").append("style").classed(xt.str.hypenate(j,"lasso-dash"),!0).html("@keyframes lassoDash {to { stroke-dashoffset: 1000;}}")}var g,p,m,v,y,x,b,S,k,w,j="d3sm-lasso",z=!1,O=[],A=[],C=d3.line().x(function(t,e){return void 0!=S?S(t[0]):t[0]}).y(function(t,e){return void 0!=k?k(t[1]):t[1]}).curve(d3.curveLinearClosed),E=0,L=10,F="#17a2b8",M="10s",Q=.3,q="5, 10",B="black",W=2,V="white",D="black",K=3,_=1e3,P=d3.easeExp;return e.svg=function(t){return arguments.length?(g=t,e):g},e.chartContainer=function(t){return arguments.length?(v=t,e):v},e.objectContainer=function(t){return arguments.length?(p=t,e):p},e.objectClass=function(t){return arguments.length?(m=t,e):m},e.namespace=function(t){return arguments.length?(j=t,e):j},e.xScale=function(t){return arguments.length?(S=t,e):S},e.yScale=function(t){return arguments.length?(k=t,e):k},e.activeQ=function(t){return arguments.length?(z=t,e):z},e.currentPoints=function(t){return arguments.length?(O=t,e):O},e.allPoints=function(t){return arguments.length?(A=t,e):A},e.instance=function(t){return arguments.length?(E=t,e):E},e.tickDistance=function(t){return arguments.length?(L=t,e):L},e.color=function(t){return arguments.length?(F=t,e):F},e.animationRate=function(t){return arguments.length?(M=t,e):M},e.opacity=function(t){return arguments.length?(Q=t,e):Q},e.dashArray=function(t){return arguments.length?(q=t,e):q},e.stroke=function(t){return arguments.length?(B=t,e):B},e.lassoedFill=function(t){return arguments.length?(V=t,e):V},e.lassoedStroke=function(t){return arguments.length?(D=t,e):D},e.lassoedStrokeWidth=function(t){return arguments.length?(K=t,e):K},e.eventCatcher=function(t){return arguments.length?(b=t,e):b},e.drag=c,e.draw=r,e.tick=s,e.detect=u,e.toggle=n,e.remove=a,e.render=o,e.keyFrames=h,e.updateObjects=f,e.applyPathAttributes=l,e.applyObjectAttributes=d,h(),e}function st(t){function e(t,e){console.log(t,e)}function n(){d3.select("html").select("style."+V+"lasso-widget").empty()&&d3.select("html").append("style").classed(V+"lasso-widget",!0).html("."+xt.str.hypenate(V,"data-table")+"{ height:100px; overflow:auto; }")}function r(){n()}function a(t){console.log(t)}function o(t){var e=xt.sel.safeSelect(t,"li",xt.str.hypenate(V,"plus-tab")).classed("ml-auto",!0).classed("nav-item",!0).on("click",j),n=xt.sel.safeSelect(e,"a","nav-link");xt.sel.safeSelect(n,"i","fa").classed("fa-plus fa-2x text-success",!0)}function i(t){var e=xt.sel.safeSelect(t,"li",xt.str.hypenate(V,"send-tab")).classed("ml-auto",!0).classed("nav-item",!0).on("click",l),n=xt.sel.safeSelect(e,"a","nav-link");xt.sel.safeSelect(n,"i","fa").classed("fa-paper-plane-o fa-2x text-primary",!0)}function l(){a(A())}function c(e){var n=xt.sel.safeSelect(e,"li",xt.str.hypenate(V,"close-tab")).classed("ml-auto",!0).classed("nav-item",!0).on("click",function(){d3.mouse(d3.select("html").node());t.select("div.card").style("position","relative").transition().duration(1e3).ease(d3.easeBack).style("left",window.outerWidth+"px").remove()}),r=xt.sel.safeSelect(n,"a","nav-link");xt.sel.safeSelect(r,"i","fa").classed("fa-window-close-o fa-2x text-danger",!0)}function s(t){var e=xt.sel.safeSelect(t,"button","remove-btn").classed("btn btn-danger",!0).on("click",u);xt.sel.safeSelect(e,"i","fa").classed("fa-trash-o",!0),xt.sel.safeSelect(e,"span").text("Remove group");return e}function u(e,n){var r=t.select("#"+xt.str.hypenate(V,"tab",e)),a=t.select("#"+xt.str.hypenate(V,"tab","pane",e));r.remove(),a.remove(),C(),O()}function f(e){t.select("."+xt.str.hypenate(V,"tab-content")).selectAll("div.tab-pane").each(function(t,n){d3.select(this).classed("active show",d3.select(this).attr("id")==e)})}function d(t,e){var n=t.insert("li",".right-aligned-tabs").classed("nav-item",!0).classed("alert",!0).classed("alert-secondary",!0).classed("alert-dismissable",!0).classed("fade",!0).classed("show",!0).attr("role","alert").attr("id",xt.str.hypenate(V,"tab",e)).classed(xt.str.hypenate(V,"tab"),!0);xt.sel.safeSelect(n,"a").attr("data-toggle","tab").text("Group "+e).attr("href","#"+xt.str.hypenate(V,"tab","pane",e)).on("dblclick",function(t,e){var n=d3.select(this);n.attr("contenteditable",!0),d3.select(n.node().parentNode).classed("alert-secondary",!1).classed("alert-warning",!0)}).on("blur",function(t,e){var n=d3.select(this);n.attr("contenteditable",!1),d3.select(n.node().parentNode).classed("alert-secondary",!0).classed("alert-warning",!1)}).on("input",function(t,e){var n=d3.select(this),r=n.text();d3.select(n.attr("href")).select("p.lead").text(r)});return n}function h(t,e){return t.append("div").datum(e).attr("role","tabpanel").classed("tab-pane",!0).classed("text-left",!0).classed(xt.str.hypenate(V,"tab","pane"),!0).attr("id",xt.str.hypenate(V,"tab","pane",e))}function g(t,e){var n=xt.sel.safeSelect(t,"p","lead").text("Group "+e),r=xt.sel.safeSelect(t,"div","table-responsive").attr("class",xt.str.hypenate(V,"data-table")),a=xt.sel.safeSelect(r,"table","table").classed("table-sm",!0).classed("table-hover",!0),o=(xt.sel.safeSelect(a,"caption","caption").html("List of selected"),xt.sel.safeSelect(t,"div","text-right")),i=e%D.colors().length;e%2!=0&&(i=D.colors().length-1-i);var l=p(o),c=v(e,D.colors()[i]);y(l,m(o),c,a);s(o);n.on("mouseover",function(){c.draw()}),n.on("mouseout",function(){c.remove()})}function p(t){var e=xt.sel.safeSelect(t,"button","lasso-btn").classed("btn btn-info",!0).classed(V,!0);xt.sel.safeSelect(e,"i","fa").classed("fa-hand-pointer-o",!0),xt.sel.safeSelect(e,"span").text("Lasso select");return e}function m(t){var e=xt.sel.safeSelect(t,"button","clear-btn").classed("btn btn-light",!0).classed(V,!0);xt.sel.safeSelect(e,"i","fa").classed("fa-eraser",!0),xt.sel.safeSelect(e,"span").text("Clear selection");return e}function v(t,e){return ct().namespace(V).svg(E).objectClass(M).chartContainer(L).objectContainer(F).instance(t).color(e).yScale(W).xScale(B)}function y(t,e,n,r){n.eventCatcher(t),t.node().addEventListener("click",x),t.datum([n]).on(xt.str.hypenate(V,"render"),function(){d3.select(this).datum()[0].draw()}).on(xt.str.hypenate(V,"drag"),function(t){var e=L.selectAll(".in-lasso-"+t[0].instance()).nodes().map(function(t,e){var n=q(d3.select(t).datum());return n.__node=t,n});w(r,e,n)}),e.on("click",function(){n.allPoints([]),n.currentPoints([]),t.dispatch(xt.str.hypenate(V,"drag"))})}function x(){function e(t){t.target!=n.node()&&t.target!=n.select("span").node()&&t.target!=n.select("i").node()&&(r.toggle(!1),n.classed("btn-info",!0),n.classed("btn-warning",!1),n.select("span").text("Lasso select"))}var n=d3.select(this),r=n.datum()[0];r.toggle();var a=r.activeQ();r.activeQ()?(n.classed("btn-info",!a),n.classed("btn-warning",a),n.select("span").text("Lasso select (active)"),t.selectAll("."+V+".lasso-btn").dispatch(xt.str.hypenate(V,"render")),d3.select("html").node().addEventListener("mousedown",e)):(n.classed("btn-info",!a),n.classed("btn-warning",a),n.select("span").text("Lasso select"),d3.select("html").node().removeEventListener("mousedown",e))}function b(t,e){var n=d3.keys(e[0]).filter(t=>"__node"!=t);n.length>0&&n.push("remove");var r=t.selectAll("th");return(r=r.data(n)).exit().remove(),r=r.merge(r.enter().append("th").attr("scope","col")).text(function(t,e){return t}),n}function S(t,e){var n=t.selectAll("tr");return(n=n.data(e)).exit().remove(),n=n.merge(n.enter().append("tr"))}function k(t,e,n,r,a,o){(t=t.data(e)).exit().remove(),(t=t.merge(t.enter().append("td"))).html(function(t,e){return"remove"!=t?n[t]:""}).classed("text-left",function(t,e){return"remove"==t}),t.select("i.fa-close").on("click",function(t,e){var i=n.__node,l=d3.select(i);l.classed("in-lasso",!1),l.classed("in-lasso-"+a.instance(),!1),r.map(function(t,e){t.__node==i&&(r.splice(e,1),w(o,r,a))})})}function w(t,e,n){var r=xt.sel.safeSelect(t,"thead"),a=xt.sel.safeSelect(r,"tr"),o=xt.sel.safeSelect(t,"tbody"),i=b(a,e);S(o,e).each(function(r,a){var o=d3.select(this);k(o.selectAll("td"),i,r,e,n,t),o.on("mouseover",function(t,e){n.applyObjectAttributes(d3.select(t.__node),!0)}).on("mouseout",function(t,e){n.applyObjectAttributes(d3.select(t.__node),!1)})})}function j(n,r){var a=t.select("."+xt.str.hypenate(V,"tab-list")),o=t.select("."+xt.str.hypenate(V,"tab-content")),i=z();if(a.selectAll("."+xt.str.hypenate(V,"tab")).size()==Q)return void e("only "+Q+" allowed.","warning");d(a,i);var l=h(o,i);g(l,i),f(l.attr("id"))}function z(){var e=t.select("."+xt.str.hypenate(V,"tab-list")),n=e.selectAll("li").size()-3,r="Group "+n,a=[];for(e.each(function(t,e){return a.push(d3.select(this).select("a").text())});a.includes(r);)r="Group "+(n+=1);return n}function O(){var e=t.select("."+xt.str.hypenate(V,"tab-list")),n=t.select("."+xt.str.hypenate(V,"tab-content"));e=e.selectAll("."+xt.str.hypenate(V,"tab")),n=n.selectAll("."+xt.str.hypenate(V,"tab","pane")),e.each(function(t,e){d3.select(this).datum(e).attr("id",xt.str.hypenate(V,"tab",e)).select("a").attr("href","#"+xt.str.hypenate(V,"tab","pane",e)).text(function(t,n){var r=d3.select(this).text();return"Group"==r.split(" ")[0]?"Group "+e:r})}),n.each(function(t,e){d3.select(this).datum(e).attr("id",xt.str.hypenate(V,"tab","pane",e)),xt.sel.safeSelect(d3.select(this),"p","lead").text(function(t,n){var r=d3.select(this).text();return"Group"==r.split(" ")[0]?"Group "+e:r}),xt.sel.safeSelect(d3.select(this),"button","remove-btn").on("click",u)})}function A(){var e=t.select("."+xt.str.hypenate(V,"tab-list")),n=t.select("."+xt.str.hypenate(V,"tab-content")).selectAll("."+xt.str.hypenate(V,"data-table")),r={},a=e.selectAll("li."+xt.str.hypenate(V,"tab")+" > a").nodes().map(function(t,e){return d3.select(t).text()});return a.map(function(t,e){r[t]=[]}),n.each(function(t,e){var n=d3.select(this).select("tbody");r[a[e]]=n.selectAll("tr").data()}),r}function C(){var e=t.select("."+xt.str.hypenate(V,"tab-list")),n=t.select("."+xt.str.hypenate(V,"tab-content")),r=e.selectAll("."+xt.str.hypenate(V,"tab"));if(0==r.size())n.select("."+xt.str.hypenate(V,"default-tab")).classed("active",!0).classed("text-left",!0);else{var a=r.nodes()[r.size()-1],o=d3.select(a).select("a").attr("href");n.select(o).classed("active",!0).classed("text-left",!0)}}var E,L,F,M,Q,q,B,W,V="d3sm-lasso",t=t,D=(d3.line().x(function(t,e){return t[0]}).y(function(t,e){return t[1]}).curve(d3.curveLinearClosed),I());return function(){var e=xt.sel.safeSelect(t,"div","card"),n=xt.sel.safeSelect(e,"div","card-header"),r=xt.sel.safeSelect(n,"ul","nav").classed("nav-tabs card-header-tabs",!0).classed(xt.str.hypenate(V,"tab-list"),!0).attr("role","tablist"),a=xt.sel.safeSelect(e,"div","card-body"),l=xt.sel.safeSelect(a,"div","tab-content").classed(xt.str.hypenate(V,"tab-content"),!0),s=xt.sel.safeSelect(r,"div","right-aligned-tabs").classed("nav ml-auto ",!0);o(s),i(s),c(s);var u=xt.sel.safeSelect(l,"div","tab-pane").classed(xt.str.hypenate(V,"default-tab"),!0).classed("active",!0).classed("text-left",!0);xt.sel.safeSelect(u,"div").html("Click to add a new group.
Click to submit for re-analysis.
Click to close the dataselect widget.")}(),r.objectClass=function(t){return arguments.length?(M="."+t.replace(".",""),r):M},r.svg=function(t){return arguments.length?(E=t,r):E},r.submit=function(t){return arguments.length?(a=t,r):a},r.maxNumberOfGroups=function(t){return arguments.length?(Q=t,r):Q},r.onError=function(t){return arguments.length?(e=t,r):e},r.objectContainer=function(t){return arguments.length?(F=t,r):F},r.chartContainer=function(t){return arguments.length?(L=t,r):L},r.dataExtractor=function(t){return arguments.length?(q=t,r):q},r.xScale=function(t){return arguments.length?(B=t,r):B},r.yScale=function(t){return arguments.length?(W=t,r):W},r}function ut(t){function e(){var e=c.select("."+xt.str.hypenate(t.namespace(),"object-container")),n=xt.sel.getTranslation(e.attr("transform")),r=e.attr("transform","translate(0,0)");i=c.node().getBBox().width-t.spaceX(),l=c.node().getBBox().height-t.spaceY(),r.attr("transform","translate("+n[0]+","+n[1]+")"),xt.con.log("plotZoom","setLocks",{xLock:i,yLock:l})}function n(){e();var n,f,d=s.map(function(t,e){return t.selection()}),h=u.map(function(t,e){return t.selection()});"2D"==o&&(n=!0,f=!0),"horizontal"==o&&(n=!0,f=!1),"vertical"==o&&(f=!0,n=!1);var g=d3.event.transform,p=c.node().getBBox();d.map(function(t,e){return t.node().getBBox()}),d.map(function(t,e){return t.node().getBBox()}),p.width,p.x,p.height,p.y;if("wheel"==r){d3.event.preventDefault();var m=d3.event.deltaY*a,v=d3.event.shiftKey;(g="2D"==o?v?{k:1,x:m,y:0}:{k:1,x:0,y:m}:n?{k:1,x:m,y:0}:{k:1,x:0,y:m}).applyX=function(t){return t*this.k+-1*this.x},g.applyY=function(t){return t*this.k+-1*this.y}}var y=c.select("."+xt.str.hypenate(t.namespace(),"object-container")),x=d.map(function(t,e){return t.select("."+xt.str.hypenate(s[e].namespace(),"object-container"))}),b=h.map(function(t,e){return t.select("."+xt.str.hypenate(u[e].namespace(),"object-container"))}),S=xt.sel.getTranslation(y.attr("transform")),k=(x.map(function(t,e){return xt.sel.getTranslation(t.attr("transform"))}),b.map(function(t,e){return xt.sel.getTranslation(t.attr("transform"))}),n?g.applyX(S[0]):0);n&&(k=k<-i?(g.x=0,-i):(g.x=0,Math.min(k,0)));var w=f?g.applyY(S[1]):0;f&&(w=w<-l?(g.y=0,-l):(g.y=0,Math.min(w,0))),y.attr("transform","translate("+k+","+w+")"),n&&x.map(function(t,e){t.attr("transform","translate("+k+",0)")}),f&&b.map(function(t,e){t.attr("transform","translate(0,"+w+")")})}var r,a=20,o=void 0==t.orient?"horizontal":t.orient(),i=t.spaceX(),l=t.spaceY(),c=t.selection(),s=(d3.select(c.thisSVG()),[]),u=[];return n.eventType=function(t){return arguments.length?(r=t,n):r},n.wheelSpeed=function(t){return arguments.length?(a=t,n):a},n.orient=function(t){return arguments.length?(o=t,n):o},n.xLock=function(t){return arguments.length?(i=t,n):i},n.yLock=function(t){return arguments.length?(l=t,n):l},n.xComponents=function(t){return arguments.length?(s=t,n):s},n.yComponents=function(t){return arguments.length?(u=t,n):u},n.setLocks=e,n.reset=function(){var e=c.select("."+xt.str.hypenate(t.namespace(),"object-container")),n=xAxisSel.select("."+xt.str.hypenate(xAxis.namespace(),"object-container")),r=yAxisSel.select("."+xt.str.hypenate(yAxis.namespace(),"object-container"));e.attr("transform","translate(0,0)"),n.attr("transform","translate(0,0)"),r.attr("transform","translate(0,0)")},n}function ft(t,e,n){function r(){var e=u.select("."+xt.str.hypenate(t.namespace(),"object-container")),n=xt.sel.getTranslation(e.attr("transform")),r=e.attr("transform","translate(0,0)");c=u.node().getBBox().width-.9*t.spaceX(),s=u.node().getBBox().height-.9*t.spaceY(),r.attr("transform","translate("+n[0]+","+n[1]+")"),xt.con.log("plotZoom","setLocks",{xLock:c,yLock:s})}function a(){r();var a,h;"2D"==l&&(a=!0,h=!0),"horizontal"==l&&(a=!0,h=!1),"vertical"==l&&(h=!0,a=!1);var g=d3.event.transform,p=u.node().getBBox(),m=f.node().getBBox(),v=f.node().getBBox();p.width,p.x,p.height,p.y,m.width,m.height,v.width,v.height;if("wheel"==o){d3.event.preventDefault();var y=d3.event.deltaY*i,x=d3.event.shiftKey;(g="2D"==l?x?{k:1,x:y,y:0}:{k:1,x:0,y:y}:a?{k:1,x:y,y:0}:{k:1,x:0,y:y}).applyX=function(t){return t*this.k+-1*this.x},g.applyY=function(t){return t*this.k+-1*this.y}}var b=u.select("."+xt.str.hypenate(t.namespace(),"object-container")),S=f.select("."+xt.str.hypenate(e.namespace(),"object-container")),k=d.select("."+xt.str.hypenate(n.namespace(),"object-container")),w=xt.sel.getTranslation(b.attr("transform")),j=(xt.sel.getTranslation(S.attr("transform")),xt.sel.getTranslation(k.attr("transform")),a?g.applyX(w[0]):0);a&&(j=j<-c?(g.x=0,-c):(g.x=0,Math.min(j,0)));var z=h?g.applyY(w[1]):0;h&&(z=z<-s?(g.y=0,-s):(g.y=0,Math.min(z,0))),b.attr("transform","translate("+j+","+z+")"),a&&S.attr("transform","translate("+j+",0)"),h&&k.attr("transform","translate(0,"+z+")")}var o,i=20,l=void 0==t.orient?"horizontal":t.orient(),c=t.spaceX(),s=t.spaceY(),u=t.selection(),f=e.selection(),d=n.selection();d3.select(u.thisSVG());return a.eventType=function(t){return arguments.length?(o=t,a):o},a.wheelSpeed=function(t){return arguments.length?(i=t,a):i},a.orient=function(t){return arguments.length?(l=t,a):l},a.xLock=function(t){return arguments.length?(c=t,a):c},a.yLock=function(t){return arguments.length?(s=t,a):s},a.setLocks=r,a.reset=function(){var r=u.select("."+xt.str.hypenate(t.namespace(),"object-container")),a=f.select("."+xt.str.hypenate(e.namespace(),"object-container")),o=d.select("."+xt.str.hypenate(n.namespace(),"object-container"));r.attr("transform","translate(0,0)"),a.attr("transform","translate(0,0)"),o.attr("transform","translate(0,0)")},a}function dt(t){function e(){var e=xt.sel.safeSelect(t,"div","input-group").classed(xt.str.hypenate(a,"container"),!0),i=xt.sel.safeSelect(e,"div","select-prepend").classed("input-group-prepend",!0),c=(xt.sel.safeSelect(i,"span","input-group-text").text(o),xt.sel.safeSelect(e,"select","custom-select").classed(xt.str.hypenate(a,"select"),!0)),s=xt.sel.safeSelect(e,"div","select-append").classed("input-group-prepend",!0),u=xt.sel.safeSelect(s,"a","filter-button").classed("btn btn-outline-secondary",!0),f=(xt.sel.safeSelect(u,"i","fa fa-filter"),xt.sel.safeSelect(e,"div","filter-input-group").classed("input-group",!0).classed("d-none",!0)),d=xt.sel.safeSelect(f,"div","input-group-prepend"),h=xt.sel.safeSelect(d,"span","input-group-text").classed("search-button",!0),g=(xt.sel.safeSelect(h,"i","fa fa-search"),xt.sel.safeSelect(f,"input","form-control").attr("placeholder","all").attr("type","text")),p=xt.sel.safeSelect(f,"div","input-group-append"),m=xt.sel.safeSelect(p,"a","close-button").classed("btn btn-outline-secondary",!0),v=(xt.sel.safeSelect(m,"i","fa fa-close"),d3.keys(r)),y=c.selectAll("option");y=y.data(d3.keys(r)),y=y.merge(y.enter().append("option")).attr("value",function(t,e){return t}).text(function(t,e){return t});var x=m;u.on("click",function(t,e){var n=f.classed("d-none");f.classed("d-none",!n)}),x.on("click",function(t,e){g.property("value","").dispatch("input")}),g.on("input",function(t,e){var a,o=g.property("value"),i=new RegExp(o,"gi");""==o?a=v:(a=[],d3.keys(r).map(function(t,e){var n=t.match(i);null==n||""==n.join("")||a.push(t)})),y=c.selectAll("option"),(y=y.data(a)).exit().remove(),y=y.merge(y.enter().append("option")).attr("value",function(t,e){return t}).text(function(t,e){return t});var s=n();l!=s&&(l=s,c.dispatch("change"))})}function n(){var e=t.select("select").property("value");return void 0==e||""==e?void 0==i?d3.keys(r)[0]:i:e}var r,a="d3sm-select-filter",o="Select options:",i=void 0,l=void 0;return e.data=function(t){return arguments.length?(r=t,e):r},e.namespace=function(t){return arguments.length?(a=t,e):a},e.selectionName=function(t){return arguments.length?(o=t,e):o},e.defaultValue=function(t){return arguments.length?(i=t,e):i},e.currentOption=n,e}function ht(t){function e(){var n=xt.sel.safeSelect(t,"div","d-inline-flex flex-row flex-wrap").selectAll("div."+xt.str.hypenate(c,"select-filter"));n.exit().remove();var r=(n=n.data(d3.keys(i))).enter().append("div").attr("class","select-filter");return(n=n.merge(r).style("margin-right","10px")).each(function(t,e){var n=dt(d3.select(this)).data(i[t]).namespace(xt.str.hypenate(c,t)).selectionName(t);n(),f[t]=n}),t.selectAll("select").on("change",function(){l()}),e}var n,r,a,o,i,l=function(){},c="d3sm-databar",s=!1,u=!1;e.xAxisSelectQ=function(t){return arguments.length?(s=t,e):s},e.yAxisSelectQ=function(t){return arguments.length?(u=t,e):u},e.xAxisOptions=function(t){return arguments.length?(a=t,e):a},e.yAxisOptions=function(t){return arguments.length?(o=t,e):o},e.data=function(t){return arguments.length?(i=t,e):i},e.keys=function(t){return arguments.length?(n=t,e):n},e.currentKey=function(t){return arguments.length?(r=t,e):r},e.updateFunction=function(t){return arguments.length?(l=t,e):l},e.namespace=function(t){return arguments.length?(c=t,e):c},e.currentKeys=function(){var t={};return d3.keys(f).map(function(e,n){t[e]=f[e].currentOption()}),t};var f={};return e}var gt=Object.freeze({uniqueElements:e,all:n,tally:r,hasQ:a,first:o,last:i,total:l,unique:c,get:s,listOfListsQ:u,cut:f,groupBy:d,arrayEquals:h,elementsAtLevels:g,numberOfElements:p,flatten:m,whichBin:v}),pt=Object.freeze({modifyHexidecimalColorLuminance:y,interpolateColors:x}),mt=Object.freeze({round:b,tickRange:S,partitionRangeInto:k,euclideanDistance:w,quartiles:j,calculateWidthOfObject:z,calculateWidthOfSpacer:O,spacersNeededAtEachLevel:A}),vt=Object.freeze({hypenate:C,truncateText:E,truncateString:L}),yt=Object.freeze({getTranslation:F,getContainingSVG:M,safeSelect:Q,cpRect:q,bgRect:B,setupContainer:W});let xt={arr:gt,color:pt,math:mt,misc:Object.freeze({resizeDebounce:V,setupStandardChartContainers:K}),sel:yt,str:vt,con:Object.freeze({consoleGroup:_,consoleGroupEnd:P,log:R,warn:Y,info:X,error:T}),paths:Object.freeze({whiskerPath:N})},bt={scatter:J,bar:U,bubble:$,boxwhisker:tt,heatmap:et,violin:nt,neededViolinValues:rt,upset:at},St={categorical:ot,numeric:it};d3.selection.prototype.thisSVG=function(){return xt.sel.getContainingSVG(this.node())},d3.mouse.absolute=function(){var t=d3.select("html").node(),[e,n]=this(t);return[e,n]},d3.selection.prototype.absolutePosition=function(){var t=this.node(),e=t.getBoundingClientRect(),n=xt.sel.getContainingSVG(t).getBoundingClientRect();return{top:e.top-n.top,left:e.left-n.left,bottom:e.bottom-n.top,right:e.right-n.left,height:e.height,width:e.width}},d3.selection.prototype.relativePositionTo=function(t){var e=this.node().getBoundingClientRect(),n=t.getBoundingClientRect();return{top:e.top-n.top,left:e.left-n.left,bottom:e.bottom-n.top,right:e.right-n.left,height:e.height,width:e.width}};let kt={aux:{lasso:ct,plotZoom:ft,multiPlotZoom:ut,datatoggle:ht,selectFilter:dt,lassoWidget:st},axis:H,charts:bt,colorFunction:I,debugQ:!1,groupingSpacer:G,legends:St,tooltip:Z,utils:xt};"undefined"!=typeof window&&(window.d3sm=kt),t.default=kt}(this.d3sm=this.d3sm||{}); diff --git a/dist/d3sm.umd.js b/dist/d3sm.umd.js index 20f1408..2ea4d72 100644 --- a/dist/d3sm.umd.js +++ b/dist/d3sm.umd.js @@ -64,7 +64,7 @@ * @param {number[]} array of numerical values * @returns {number} sum over elements in array */ - function total$1( array ) { return array.reduce((a, b) => a + b, 0) } + function total( array ) { return array.reduce((a, b) => a + b, 0) } /** * Removes duplicates in array * @param {Array} array of items @@ -218,7 +218,7 @@ hasQ: hasQ, first: first, last: last, - total: total$1, + total: total, unique: unique, get: get, listOfListsQ: listOfListsQ, @@ -441,150 +441,6 @@ spacersNeededAtEachLevel: spacersNeededAtEachLevel }); - function resizeDebounce(f, wait) { - var resize = debounce(function(){f();},wait); - window.addEventListener('resize', resize); - } - - function debounce(func, wait, immediate) { - var timeout; - return function() { - var context = this, args = arguments; - var later = function() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - var callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); - }; - } - - function setupStandardChartContainers( - selection, - namespace, - container, - margins={top:0.01, bottom:0.01, left:0.01, right:0.01}, - svg={w:0.8, h:0.6}, // percent of container space for svg - axes={y:0.1, x:0.1}, // percent of container space for axes, - leg={x:0, margin:0, pos:'left'} // absolute width of legend and space on either size - ) - { - if (container == undefined) {container = {w:window.innerWidth, h:window.Height};} - // SVG width and height - - var svgSpace = { - w: container.w * svg.w, - h: container.h * svg.h - }; - - var margPx = { - top: margins.top * svgSpace.h, - bottom: margins.bottom * svgSpace.h, - left: margins.left * svgSpace.w, - right: margins.right * svgSpace.w - }, - - - - // Space after removing margins - chartSpace = { - w: svgSpace.w - margPx.left - margPx.right, - h: svgSpace.h - margPx.top - margPx.bottom - }, - - // main dimension of x and y axies - // e.g. defines how tall x axis is as length is determined by plotRect.w - axesSpace = { - x: chartSpace.h * axes.x, - y: chartSpace.w * axes.y - }, - - // space left for drawing the chart properly (e.g. bars, violins, etc) - drawingSpace = { - x: chartSpace.w - axesSpace.y - leg.x - 2*leg.margin, - y: chartSpace.h - axesSpace.x - }, - - - legRect = { - x: leg.margin + margPx.left + (leg.pos == 'left' ? 0 : drawingSpace.x + axesSpace.y), - y: margPx.top, // this is soomehow getting calculated incorectly - w: leg.x, - h: drawingSpace.y - }, - - yAxisRect = { - x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), - y: margPx.top, - w: axesSpace.y, - h: drawingSpace.y - }, - - plotRect = { - x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), - y: margPx.top, - w: drawingSpace.x, - h: drawingSpace.y - }, - - xAxisRect = { - x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), - y: margPx.top + drawingSpace.y, - w: drawingSpace.x, - h: axesSpace.x - }; - - - - container = d3sm.safeSelect(selection, 'svg', namespace) - .style('width', svgSpace.w+'px') - .style('height', svgSpace.h+'px'); - - var axes = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'axes')); - - var leg = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'legend')) - .attr('transform', "translate("+legRect.x+","+legRect.y+")"); - - var plot = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'plot')) - .attr('transform', "translate("+plotRect.x+","+plotRect.y+")"); - - var xAxis = d3sm.safeSelect(axes, 'g', d3sm.hypenate(namespace, 'x-axis')) - .attr('transform', "translate("+xAxisRect.x+","+xAxisRect.y+")"); - - var yAxis = d3sm.safeSelect(axes, 'g', d3sm.hypenate(namespace, 'y-axis')) - .attr('transform', "translate("+yAxisRect.x+","+yAxisRect.y+")"); - - return { - svg: { - selection: container, - rect: svgSpace - }, - plot: { - selection: plot, - rect: plotRect - }, - xAxis: { - selection: xAxis, - rect: xAxisRect - }, - yAxis: { - selection: yAxis, - rect: yAxisRect - }, - legend: { - selection: leg, - rect: legRect - } - } - } - - var misc = /*#__PURE__*/Object.freeze({ - resizeDebounce: resizeDebounce, - setupStandardChartContainers: setupStandardChartContainers - }); - /** * Hypenates all strings together * @param {string[]} arguments @@ -658,12 +514,12 @@ * @param {Element} element * @returns {Element | undefined} */ - function getContainingSVG$1(element) { + function getContainingSVG(element) { var parent = element.parentElement; var tag = parent.tagName.toLowerCase(); if (tag === 'svg') { return parent; } if (tag === 'html') { return undefined; } - return getContainingSVG$1(parent); + return getContainingSVG(parent); } @@ -754,100 +610,244 @@ var sel = /*#__PURE__*/Object.freeze({ getTranslation: getTranslation, - getContainingSVG: getContainingSVG$1, + getContainingSVG: getContainingSVG, safeSelect: safeSelect, cpRect: cpRect, bgRect: bgRect, setupContainer: setupContainer }); - /** - * calls console.group if d3sm.debugQ == true - * @param {string} name of the group - * @returns {undefined} - */ - function consoleGroup(name) { - if (window.d3sm.debugQ === true){ - console.group(name); - } + function resizeDebounce(f, wait) { + var resize = debounce(function(){f();},wait); + window.addEventListener('resize', resize); } - /** - * calls console.groupEnd if d3sm.debugQ == true - * @returns {undefined} - */ - function consoleGroupEnd() { - if (window.d3sm.debugQ === true){ - console.groupEnd(); - } + function debounce(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; } - /** - * Calls console.log if d3sm.debugQ == true - * @param {string} func name of the function logging - * @param {string} msg to log - * @param {Object} data to be logged along side the message - * @returns {undefined} - */ - function log(func, msg, data) { - if (window.d3sm.debugQ === true){ - console.log( - `%c[d3sm::${func}]:\t${msg}`, - [ - 'background: #6cd1ef', - 'border-radius: 5000px', - 'padding: 0px 2px', - 'font-size: 14px' - ].join(';') - ); - console.table(data); - // console.trace() - } - } + function setupStandardChartContainers( + selection, + namespace, + container, + margins={top:0.01, bottom:0.01, left:0.01, right:0.01}, + svg={w:0.8, h:0.6}, // percent of container space for svg + axes={y:0.1, x:0.1}, // percent of container space for axes, + leg={x:0, margin:0, pos:'left'} // absolute width of legend and space on either size + ) + { + if (container == undefined) {container = {w:window.innerWidth, h:window.Height};} + // SVG width and height - /** - * Calls console.warn if d3sm.debugQ == true - * @param {string} func name of the function warning - * @param {string} msg to display - * @param {Object} data to be displayed along side the message - * @returns {undefined} - */ - function warn(func, msg, data) { - if (window.d3sm.debugQ === true) - console.warn( - `%c[d3sm::${func}]:\t${msg}`, - [ - 'background: #ffd53e', - 'border-radius: 5000px', - 'padding: 0px 2px', - 'font-size: 14px' - ].join(';') - ); - console.table(data); - } - /** - * Calls the console.info if d3sm.debugQ == true - * @param {string} func name of the function providing info - * @param {string} msg to display - * @param {Object} data to be displayed along side the message - * @returns {undefined} - */ - function info(func, msg, data) { - if (window.d3sm.debugQ) - console.info( - `%c[d3sm::${func}]:\t${msg}`, - [ - 'background: #009ccd', - 'border-radius: 5000px', - 'padding: 0px 2px', - 'font-size: 14px' - ].join(';') - ); - console.table(data); - } + var svgSpace = { + w: container.w * svg.w, + h: container.h * svg.h + }; + var margPx = { + top: margins.top * svgSpace.h, + bottom: margins.bottom * svgSpace.h, + left: margins.left * svgSpace.w, + right: margins.right * svgSpace.w + }, - /** + + + // Space after removing margins + chartSpace = { + w: svgSpace.w - margPx.left - margPx.right, + h: svgSpace.h - margPx.top - margPx.bottom + }, + + // main dimension of x and y axies + // e.g. defines how tall x axis is as length is determined by plotRect.w + axesSpace = { + x: chartSpace.h * axes.x, + y: chartSpace.w * axes.y + }, + + // space left for drawing the chart properly (e.g. bars, violins, etc) + drawingSpace = { + x: chartSpace.w - axesSpace.y - leg.x - 2*leg.margin, + y: chartSpace.h - axesSpace.x + }, + + + legRect = { + x: leg.margin + margPx.left + (leg.pos == 'left' ? 0 : drawingSpace.x + axesSpace.y), + y: margPx.top, // this is soomehow getting calculated incorectly + w: leg.x, + h: drawingSpace.y + }, + + yAxisRect = { + x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), + y: margPx.top, + w: axesSpace.y, + h: drawingSpace.y + }, + + plotRect = { + x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), + y: margPx.top, + w: drawingSpace.x, + h: drawingSpace.y + }, + + xAxisRect = { + x: axesSpace.y + margPx.left + (leg.pos == 'left' ? leg.x + 2*leg.margin : 0), + y: margPx.top + drawingSpace.y, + w: drawingSpace.x, + h: axesSpace.x + }; + + + + container = safeSelect(selection, 'svg', namespace) + .style('width', svgSpace.w+'px') + .style('height', svgSpace.h+'px'); + + var axes = safeSelect(container, 'g', hypenate(namespace, 'axes')); + + var leg = safeSelect(container, 'g', hypenate(namespace, 'legend')) + .attr('transform', "translate("+legRect.x+","+legRect.y+")"); + + var plot = safeSelect(container, 'g', hypenate(namespace, 'plot')) + .attr('transform', "translate("+plotRect.x+","+plotRect.y+")"); + + var xAxis = safeSelect(axes, 'g', hypenate(namespace, 'x-axis')) + .attr('transform', "translate("+xAxisRect.x+","+xAxisRect.y+")"); + + var yAxis = safeSelect(axes, 'g', hypenate(namespace, 'y-axis')) + .attr('transform', "translate("+yAxisRect.x+","+yAxisRect.y+")"); + + return { + svg: { + selection: container, + rect: svgSpace + }, + plot: { + selection: plot, + rect: plotRect + }, + xAxis: { + selection: xAxis, + rect: xAxisRect + }, + yAxis: { + selection: yAxis, + rect: yAxisRect + }, + legend: { + selection: leg, + rect: legRect + } + } + } + + var misc = /*#__PURE__*/Object.freeze({ + resizeDebounce: resizeDebounce, + setupStandardChartContainers: setupStandardChartContainers + }); + + /** + * calls console.group if d3sm.debugQ == true + * @param {string} name of the group + * @returns {undefined} + */ + function consoleGroup(name) { + if (window.d3sm.debugQ === true){ + console.group(name); + } + } + + /** + * calls console.groupEnd if d3sm.debugQ == true + * @returns {undefined} + */ + function consoleGroupEnd() { + if (window.d3sm.debugQ === true){ + console.groupEnd(); + } + } + + /** + * Calls console.log if d3sm.debugQ == true + * @param {string} func name of the function logging + * @param {string} msg to log + * @param {Object} data to be logged along side the message + * @returns {undefined} + */ + function log(func, msg, data) { + if (window.d3sm.debugQ === true){ + console.log( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #6cd1ef', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ); + console.table(data); + // console.trace() + } + } + + /** + * Calls console.warn if d3sm.debugQ == true + * @param {string} func name of the function warning + * @param {string} msg to display + * @param {Object} data to be displayed along side the message + * @returns {undefined} + */ + function warn(func, msg, data) { + if (window.d3sm.debugQ === true) + console.warn( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #ffd53e', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ); + console.table(data); + } + /** + * Calls the console.info if d3sm.debugQ == true + * @param {string} func name of the function providing info + * @param {string} msg to display + * @param {Object} data to be displayed along side the message + * @returns {undefined} + */ + function info(func, msg, data) { + if (window.d3sm.debugQ) + console.info( + `%c[d3sm::${func}]:\t${msg}`, + [ + 'background: #009ccd', + 'border-radius: 5000px', + 'padding: 0px 2px', + 'font-size: 14px' + ].join(';') + ); + console.table(data); + } + + + /** * Calls console.error if d3sm.debugQ == true * @param {string} func name of the function which sends the error * @param {string} msg to display @@ -912,7 +912,7 @@ whiskerPath: whiskerPath }); - let utils$1 = { + let utils = { arr, color, math, misc, sel, str, con, paths }; @@ -2022,7 +2022,7 @@ function axis () { // for convenience in handling orientation specific values - var horizontalQ = utils$1.arr.hasQ(['top', 'bottom', 'horizontal'], orient) ? true : false; + var horizontalQ = utils.arr.hasQ(['top', 'bottom', 'horizontal'], orient) ? true : false; var verticalQ = !horizontalQ; // background cliping rectangle @@ -2053,7 +2053,7 @@ } - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); // defaults for text-anchor and text rotation if (orient == 'top') { @@ -2091,12 +2091,12 @@ : (grouping == undefined) ? (numberOfTicks != undefined) // ? (tickValues.length < numberOfTicks) - ? (utils$1.math.tickRange(...d3.extent(tickValues), numberOfTicks)) + ? (utils.math.tickRange(...d3.extent(tickValues), numberOfTicks)) : tickValues : grouping; - var flatTickData = utils$1.arr.flatten(tickData); + var flatTickData = utils.arr.flatten(tickData); var numberOfObjects = flatTickData.length; var space = horizontalQ ? spaceX : spaceY; var extent = d3.extent(flatTickData); @@ -2120,16 +2120,16 @@ // calculate object size if needed objectSize = (objectSize == undefined) - ? utils$1.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) : objectSize; // calculate spacer size if needed spacerSize = (spacerSize == undefined) - ? utils$1.math.calculateWidthOfSpacer(flatTickData, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfSpacer(flatTickData, space, objectSize, numberOfObjects, objectSpacer, overflowQ) : spacerSize; - var objClass = utils$1.str.hypenate(namespace, categoricalQ ? objectClass+'-categorical' : objectClass); + var objClass = utils.str.hypenate(namespace, categoricalQ ? objectClass+'-categorical' : objectClass); var spacerFunction = groupingSpacer() .horizontalQ(horizontalQ).scale(scale).moveby((categoricalQ?'category':'scale')).numberOfObjects(numberOfObjects) @@ -2194,11 +2194,11 @@ - var labelNameGroup = utils$1.sel.safeSelect(selection, 'g', utils$1.str.hypenate(namespace,'axis-name')); + var labelNameGroup = utils.sel.safeSelect(selection, 'g', utils.str.hypenate(namespace,'axis-name')); - var labelElement = utils$1.sel.safeSelect(labelNameGroup, 'text', utils$1.str.hypenate(namespace, 'name')); + var labelElement = utils.sel.safeSelect(labelNameGroup, 'text', utils.str.hypenate(namespace, 'name')); if (labelElement != undefined) { labelElement.text(label); @@ -2263,7 +2263,7 @@ var that = d3.select(this).style('opacity', 1); // make and move tick - var tick = utils$1.sel.safeSelect(that, 'line', utils$1.str.hypenate(namespace,'tick')) + var tick = utils.sel.safeSelect(that, 'line', utils.str.hypenate(namespace,'tick')) .attr("x1", 0) .attr("x2", horizontalQ ? 0 : orient == "left" ? -tickLength : tickLength) .attr("y1", 0) @@ -2279,10 +2279,10 @@ }); // make and move label - var label = utils$1.sel.safeSelect(that, 'text', utils$1.str.hypenate(namespace,'label')) + var label = utils.sel.safeSelect(that, 'text', utils.str.hypenate(namespace,'label')) .text(function(d, i){ - var s = typeof d == 'number' ? utils$1.math.round(d, roundTo) : d; - s = utils$1.str.truncateString(String(s), (horizontalQ ? spaceY : spaceX) - tickLength-tickLabelMargin-tickTickLabelSpacer, tickLabelFontSize * 0.45); + var s = typeof d == 'number' ? utils.math.round(d, roundTo) : d; + s = utils.str.truncateString(String(s), (horizontalQ ? spaceY : spaceX) - tickLength-tickLabelMargin-tickTickLabelSpacer, tickLabelFontSize * 0.45); return s }) .attr('font-size', tickLabelFontSize) @@ -2334,7 +2334,7 @@ // add guidlines as needed if (guideLinesQ) { - var gline = utils$1.sel.safeSelect(that, 'line', utils$1.str.hypenate(namespace, 'guideline')) + var gline = utils.sel.safeSelect(that, 'line', utils.str.hypenate(namespace, 'guideline')) .transition().duration(transitionDuration).ease(easeFunc) .attr("x1", 0) .attr("x2", horizontalQ ? 0 : orient == "left" ? guidelineSpace : -guidelineSpace) @@ -2347,15 +2347,15 @@ t = 'translate('+x+','+y+')'; return t }); - } else { that.select('line.'+utils$1.str.hypenate(namespace, 'guideline')).remove(); } + } else { that.select('line.'+utils.str.hypenate(namespace, 'guideline')).remove(); } }); // apply alternating guidline thickness if (guideLinesQ) { - container.selectAll('.'+utils$1.str.hypenate(namespace,'guideline')) + container.selectAll('.'+utils.str.hypenate(namespace,'guideline')) .attr('stroke', function(d, i){ - if (i % 2 == 0) { return utils$1.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } + if (i % 2 == 0) { return utils.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } return guideLineStroke }) .attr('stroke-width', function(d, i){ @@ -2369,7 +2369,7 @@ /*************************************************************************** ** Make the line of the axis ***************************************************************************/ - var line = utils$1.sel.safeSelect(selection, 'path', utils$1.str.hypenate(namespace,'line')) + var line = utils.sel.safeSelect(selection, 'path', utils.str.hypenate(namespace,'line')) // .attr('x1', 0) // .attr('x2', horizontalQ ? spaceX : 0) // .attr('y1', 0) @@ -2389,21 +2389,21 @@ // hover of label show full text label in case it is truncated function labelHover(d, i){ var t = d3.select(this).style('fill', 'red'); - d3.select(t.node().parentNode).select("line."+utils$1.str.hypenate(namespace,'tick')) + d3.select(t.node().parentNode).select("line."+utils.str.hypenate(namespace,'tick')) .attr("stroke", 'red') .attr("stroke-width", tickStrokeWidth*2); if (guideLinesQ) { - d3.select(t.node().parentNode).select('line.'+utils$1.str.hypenate(namespace, 'guideline')) + d3.select(t.node().parentNode).select('line.'+utils.str.hypenate(namespace, 'guideline')) .attr('stroke', 'red') .attr('stroke-width', guideLineStrokeWidth*2); } - var s = typeof d == 'number' ? utils$1.math.round(d, roundTo) : d; + var s = typeof d == 'number' ? utils.math.round(d, roundTo) : d; var m = d3.mouse(d3.select('html').node()); - var div = utils$1.sel.safeSelect(d3.select('body'), 'div', utils$1.str.hypenate(namespace,'guideline-tooltip')) - .attr('id', utils$1.str.hypenate(namespace,'guideline-tooltip')) + var div = utils.sel.safeSelect(d3.select('body'), 'div', utils.str.hypenate(namespace,'guideline-tooltip')) + .attr('id', utils.str.hypenate(namespace,'guideline-tooltip')) .style('position', 'absolute') .style('left', (d3.event.pageX+15)+'px') .style('top', (d3.event.pageY+15)+'px') @@ -2420,7 +2420,7 @@ .style('border-style', 'solid') .style('border-width', 2); - var text = utils$1.sel.safeSelect(div, 'div') + var text = utils.sel.safeSelect(div, 'div') .text(tickLabelOnHoverFunc(s, i)) .style('color', 'black') .style('align-self', 'center'); @@ -2433,15 +2433,15 @@ function labelHoverOff(d, i){ var t = d3.select(this).style('fill', 'black'); - d3.select(t.node().parentNode).select("line."+utils$1.str.hypenate(namespace,'tick')) + d3.select(t.node().parentNode).select("line."+utils.str.hypenate(namespace,'tick')) .attr("stroke", tickStroke) .attr("stroke-width", tickStrokeWidth); if (guideLinesQ) { - var gline = d3.select(t.node().parentNode).select('line.'+utils$1.str.hypenate(namespace, 'guideline')); + var gline = d3.select(t.node().parentNode).select('line.'+utils.str.hypenate(namespace, 'guideline')); var minorQ = gline.attr('minor'); gline.attr('stroke', function(d, ii){ - if (minorQ == 'true') { return utils$1.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } + if (minorQ == 'true') { return utils.color.modifyHexidecimalColorLuminance(guideLineStroke, 0.8) } return guideLineStroke }) .attr('stroke-width', function(d, ii){ @@ -2449,7 +2449,7 @@ return guideLineStrokeWidth }); } - d3.select("#"+utils$1.str.hypenate(namespace,'guideline-tooltip')).remove(); + d3.select("#"+utils.str.hypenate(namespace,'guideline-tooltip')).remove(); } @@ -3396,7 +3396,7 @@ function scatter() { // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); pointKeys = d3.keys(data); @@ -3975,7 +3975,7 @@ // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); // to prevent re-calculation and getters to be passed to axes barKeys = d3.keys(data); @@ -3984,7 +3984,7 @@ // if grouping is undefined sort barKeys by sortingFunction var ordered = (grouping == undefined) ? barKeys.sort(sortingFunction) : grouping; // ordered might be nested depending on grouping - barKeys = utils$1.arr.flatten(ordered); + barKeys = utils.arr.flatten(ordered); var numberOfObjects = barKeys.length; var extent = [Math.min(...barValues) - domainPadding,Math.max(...barValues) + domainPadding]; @@ -4002,12 +4002,12 @@ var space = horizontalQ ? spaceX : spaceY; // calculate object size objectSize = (objectSize == undefined) - ? utils$1.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ) : objectSize; // calculate spacer size if needed spacerSize = (spacerSize == undefined) - ? utils$1.math.calculateWidthOfSpacer(barKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) + ? utils.math.calculateWidthOfSpacer(barKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ) : spacerSize; // make the nested groups var spacerFunction = groupingSpacer() @@ -4074,7 +4074,7 @@ strokeColor = colorFunction$$1(key, value, i, 'stroke'); - var bar = utils$1.sel.safeSelect(t, 'rect', 'bar-rect'); + var bar = utils.sel.safeSelect(t, 'rect', 'bar-rect'); if (bar.attr('transform') == undefined) { bar.attr('transform', function(d, i) { @@ -4804,19 +4804,19 @@ function bhm() { var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); cellKeys = d3.keys(data); cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }); - utils$1.con.log('bubbleHeatmap', 'cells are sorted by', cellKeys); + utils.con.log('bubbleHeatmap', 'cells are sorted by', cellKeys); - xValues = utils$1.arr.unique(cellKeys.map(xExtractor)); - yValues = utils$1.arr.unique(cellKeys.map(yExtractor)); - rValues = utils$1.arr.unique(cellKeys.map(rExtractor)); - vValues = utils$1.arr.unique(cellKeys.map(vExtractor)); - utils$1.con.log('bubbleHeatmap', 'x and y keys are', {x: xValues, y:yValues}); + xValues = utils.arr.unique(cellKeys.map(xExtractor)); + yValues = utils.arr.unique(cellKeys.map(yExtractor)); + rValues = utils.arr.unique(cellKeys.map(rExtractor)); + vValues = utils.arr.unique(cellKeys.map(vExtractor)); + utils.con.log('bubbleHeatmap', 'x and y keys are', {x: xValues, y:yValues}); var xDim = xValues.length, yDim = yValues.length; @@ -4825,11 +4825,11 @@ var extent = [Math.min(...rValues) - domainPadding,Math.max(...rValues) + domainPadding]; - ySize = utils$1.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - xSize = utils$1.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - ySpacerSize = utils$1.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); - xSpacerSize = utils$1.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); - utils$1.con.log('bubbleHeatmap', 'size of', {x: xSize, y: ySize}); + ySize = utils.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + xSize = utils.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + ySpacerSize = utils.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); + xSpacerSize = utils.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); + utils.con.log('bubbleHeatmap', 'size of', {x: xSize, y: ySize}); scale.domain(extent).range([Math.min(minObjectSize/2, Math.min(ySize, xSize)/2), Math.min(ySize, xSize)/2]); @@ -4837,7 +4837,7 @@ var ySpacer = groupingSpacer() .horizontalQ(false) .moveby('category').numberOfObjects(yDim) - .objectClass(utils$1.str.hypenate(objectClass, 'row')) + .objectClass(utils.str.hypenate(objectClass, 'row')) .objectSize(ySize).spacerSize(ySpacerSize) .transitionDuration(transitionDuration).easeFunc(easeFunc) .namespace('row'); @@ -4851,7 +4851,7 @@ ySpacer(container, yValues, 0); - container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'row')) + container.selectAll('g.'+utils.str.hypenate(objectClass, 'row')) .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); @@ -4866,7 +4866,7 @@ : colorFunction$$1.dataExtent([0, Math.max(...vValues)]); cells.each(function(key, i) { - utils$1.con.log('bubbleHeatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); + utils.con.log('bubbleHeatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); var t = d3.select(this), currentData = data[key], @@ -4876,9 +4876,9 @@ fillColor = colorFunction$$1(key, value, i, 'fill'), // prevent duplicate computation strokeColor = colorFunction$$1(key, value, i, 'stroke'); - utils$1.con.log('bubbleHeatmap', 'radius',{radius: radius, scaled: scale(radius), extent: extent, range:scale.range()}); + utils.con.log('bubbleHeatmap', 'radius',{radius: radius, scaled: scale(radius), extent: extent, range:scale.range()}); - var c = utils$1.sel.safeSelect(t, 'circle', utils$1.str.hypenate(objectClass,'circle')); + var c = utils.sel.safeSelect(t, 'circle', utils.str.hypenate(objectClass,'circle')); c.attr('cx', xSize / 2) .attr('cy', ySize / 2 ) .attr('r', scale(radius)) @@ -4888,7 +4888,7 @@ }); - tooltip$$1.selection(cells.selectAll('circle.'+utils$1.str.hypenate(objectClass, 'circle'))) + tooltip$$1.selection(cells.selectAll('circle.'+utils.str.hypenate(objectClass, 'circle'))) .data(data); // .keys(['r', 'v']) // .header(function(d, i){return utils.str.hypenate(data[d][xKey], data[d][yKey]) }) @@ -4901,3046 +4901,4368 @@ return bhm; } + /******************************************************************************* + ** ** + ** ** + ** BOX AND WHISKER ** + ** ** + ** ** + *******************************************************************************/ /** - * Creates a heatmap + * Creates a boxwhisker * - * {@link https://sumneuron.gitlab.io/d3sm/demos/heatmap-heatmap/index.html Demo} - * @constructor heatmap + * {@link https://sumneuron.gitlab.io/d3sm/demos/box-whiskers/index.html Demo} + * @constructor boxwhisker * @param {d3.selection} selection - * @namespace heatmap - * @returns {function} heatmap + * @namespace boxwhisker + * @returns {function} boxwhisker */ - function heatmap( selection ) { - var + function boxwhisker( selection ) { + var /** - * Data to plot. Assumed to be a object, where each key corresponds to a cell - * (see {@link heatmap#data}) + * Data to plot. Assumed to be a object, where each key corresponds to a box + * (see {@link boxwhisker#data}) * @param {Object} [data=undefined] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ data, - /** - * Amount of horizontal space (in pixels) avaible to render the heatmap in - * (see {@link heatmap#spaceX}) + * Which direction to render the boxes in + * (see {@link boxwhisker#orient}) + * @param {number} [orient='horizontal'] + * @memberof boxwhisker# + * @property + */ + orient = 'horizontal', + /** + * Amount of horizontal space (in pixels) avaible to render the boxwhisker in + * (see {@link boxwhisker#spaceX}) * @param {number} [spaceX=undefined] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ spaceX, /** - * Amount of vertical space (in pixels) avaible to render the heatmap in - * (see {@link heatmap.spaceY}) + * Amount of vertical space (in pixels) avaible to render the boxwhisker in + * (see {@link boxwhisker.spaceY}) * @param {number} [spaceY=undefined] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ spaceY, - /** - * The internal key of the cell specifiying to which x axis key it belongs - * (see {@link heatmap.xKey}) - * @param {string} [xKey='x'] - * @memberof heatmap# + * Whether or not to allow boxwhisker to render elements pass the main spatial dimension + * given the orientation (see {@link boxwhisker#orient}), where {@link boxwhisker#orient}="horizontal" + * the main dimension is {@link boxwhisker#spaceX} and where {@link boxwhisker#orient}="vertical" + * the main dimension is {@link boxwhisker#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof boxwhisker# * @property */ - xKey = 'x', + overflowQ = true, + /** - * The internal key of the cell specifiying to which y axis key it belongs - * (see {@link heatmap.yKey}) - * @param {string} [yKey='y'] - * @memberof heatmap# + * An array - putatively of other arrays - depicting how boxes should be arranged + * @param {Array[]} [grouping=undefined] + * @memberof boxwhisker# * @property */ - yKey = 'y', + grouping, + quartilesKey = 'quartiles', // key in object where quartiles are stored + quartilesKeys = ["0.00", "0.25", "0.50", "0.75", "1.00"], // keys in quartiles object mapping values to min, q1, q2, q3 and max + /** - * The internal key of the cell specifiying what value to use to determine the color - * (see {@link heatmap.vKey}) - * @param {string} [vKey='v'] - * @memberof heatmap# + * How to get the value of the boxwhisker + * @param {function} [valueExtractor=function(key, index) { return data[key][quartilesKey] }] + * @memberof boxwhisker# * @property */ - vKey = 'v', - + valueExtractor = function(key, index) { return data[key][quartilesKey] }, /** - * Function for extracting the the value from xKey. - * (see {@link heatmap.xExtractor}) - * @param {function} [xExtractor=function(key, i) { return data[key][xKey] }] - * @returns {string} - * @memberof heatmap# + * How to sort the boxes - if {@link boxwhisker#grouping} is not provided. + * @param {function} [sortingFunction=descending] + * @memberof boxwhisker# * @property + * default + * function(keyA, keyB) {return d3.descending( + * valueExtractor(keyA)[quartilesKeys[4]], + * valueExtractor(keyB)[quartilesKeys[4]] + * )} */ - xExtractor = function(key, i) {return data[key][xKey] }, + sortingFunction = function(keyA, keyB) {return d3.descending( + valueExtractor(keyA)[quartilesKeys[4]], + valueExtractor(keyB)[quartilesKeys[4]] + )}, /** - * Function for extracting the the value from yKey. - * (see {@link heatmap.yExtractor}) - * @param {function} [yExtractor=function(key, i) { return data[key][yKey] }] - * @returns {string} - * @memberof heatmap# + * The scale for which boxwhisker values should be transformed by + * @param {d3.scale} [scale=d3.scaleLinear] + * @memberof boxwhisker# * @property */ - yExtractor = function(key, i) { return data[key][yKey] }, - + scale = d3.scaleLinear(), /** - * Function for extracting the the value from vKey. - * (see {@link heatmap.vExtractor}) - * @param {function} [vExtractor=function(key, i) { return data[key][vKey] }] - * @returns {number} - * @memberof heatmap# + * The padding for the domain of the scale (see {@link boxwhisker#scale}) + * @param {number} [domainPadding=0.5] + * @memberof boxwhisker# * @property */ - vExtractor = function(key, i) { return data[key][vKey] }, - - + domainPadding = 0.5, /** - * Whether or not to allow heatmap to render elements pass the main spatial dimension - * given the orientation (see {@link heatmap#orient}), where {@link heatmap#orient}="bottom" or {@link heatmap#orient}="top" - * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="left" or {@link heatmap#orient}="right" - * the main dimension is {@link heatmap#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof heatmap# + * Default space for the spacer (percentage) of main dimension given the orientation + * (see {@link boxwhisker#orient}), where {@link boxwhisker#orient}="horizontal" + * the main dimension is {@link boxwhisker#spaceX} and where {@link boxwhisker#orient}="vertical" + * the main dimension is {@link boxwhisker#spaceY} between boxes + * @param {number} [objectSpacer=0.05] + * @memberof boxwhisker# * @property */ - overflowQ = false, - + objectSpacer = 0.05, /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link heatmap#orient}), where {@link heatmap#orient}="horizontal" - * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="vertical" - * the main dimension is {@link heatmap#spaceY} between bubbles - * @param {number} [objectSpacer=0.0] - * @memberof heatmap# + * The minimum size that an object can be + * @param {number} [minObjectSize=15] + * @memberof boxwhisker# * @property */ - objectSpacer = 0.0, + minObjectSize = 15, /** - * The minimum size that an object can be in the y dimension - * @param {number} [minObjectSize=50] - * @memberof heatmap# + * The maximum size that an object can be + * @param {number} [maxObjectSize=50] + * @memberof boxwhisker# * @property */ - yMinObjectSize = 50, + maxObjectSize = 50, /** - * The minimum size that an object can be in the x dimension - * @param {number} [minObjectSize=50] - * @memberof heatmap# + * Percent of box and whisker size that whiskers will be rendered + * see {@link boxwhisker#objectSize} + * @param {number} [whiskerWidthPercent=0.6] + * @memberof boxwhisker# * @property */ - xMinObjectSize = 50, + whiskerWidthPercent = .6, /** - * The maximum size that an object can be in the x dimension - * @param {number} [maxObjectSize=100] - * @memberof heatmap# + * Instance of ColorFunction + * @param {function} [colorFunction = colorFunction()] + * @memberof boxwhisker# * @property */ - xMaxObjectSize = 100, + colorFunction$$1 = colorFunction(), /** - * The maximum size that an object can be in the y dimension - * @param {number} [maxObjectSize=100] - * @memberof heatmap# + * The stroke width of the boxes + * @param {number} [boxStrokeWidth=2] + * @memberof boxwhisker# * @property */ - yMaxObjectSize = 100, - - + boxStrokeWidth = 2, /** - * The stroke width of the bubbles - * @param {number} [objectStrokeWidth=2] - * @memberof heatmap# + * The stroke width of the whiskers + * @param {number} [whiskerStrokeWidth=2] + * @memberof boxwhisker# * @property */ - objectStrokeWidth = 2, - // colorFunc = colorFunction(), + whiskerStrokeWidth = 2, /** * Color of the background * @param {string} [backgroundFill="transparent"] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ backgroundFill = 'transparent', /** - * Namespace for all items made by this instance of heatmap - * @param {string} [namespace="d3sm-heatmap"] - * @memberof heatmap# + * Namespace for all items made by this instance of boxwhisker + * @param {string} [namespace="d3sm-box-whisker"] + * @memberof boxwhisker# * @property */ - namespace = 'd3sm-heatmap', + namespace = 'd3sm-box-whisker', /** - * Class name for heatmap container ( element) - * @param {string} [objectClass="heatmap"] - * @memberof heatmap# + * Class name for boxwhisker container ( element) + * @param {string} [objectClass="box-whisk"] + * @memberof boxwhisker# * @property */ - objectClass = 'heatmap', + objectClass = 'box-whisk', + /** * Duration of all transitions of this element * @param {number} [transitionDuration=1000] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ transitionDuration = 1000, /** * Easing function for transitions * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof heatmap# + * @memberof boxwhisker# * @property */ easeFunc = d3.easeExp, /** - * Stores the keys of all the cells - * Calculated after heatmap called. - * @param {string[]} [cellKeys=undefined] - * @memberof heatmap# - * @property - */ - cellKeys, - /** - * Stores the list of utils.arr.unique xValues - * Calculated after heatmap called. - * @param {string[]} [xValues=undefined] - * @memberof heatmap# + * The keys of the boxes + * @param {string[]} [boxKeys=undefined] + * @memberof boxwhisker# * @property */ - xValues, + boxKeys, /** - * Stores the list of utils.arr.unique yValues - * Calculated after heatmap called. - * @param {string[]} [yValues=undefined] - * @memberof heatmap# + * The values of the boxes + * @param {string[]} [boxValues=undefined] + * @memberof boxwhisker# * @property */ - yValues, + boxValues, /** - * Stores the list of utils.arr.unique vValues - * Calculated after heatmap called. - * @param {string[]} [vValues=undefined] - * @memberof heatmap# + * The objectSize (actual width) used by the boxes + * @param {number} [objectSize=undefined] + * @memberof boxwhisker# * @property */ - vValues, - - xKeySortingFunction = function(a, b) { return xValues.indexOf(xExtractor(a)) - xValues.indexOf(xExtractor(b)) }, - yKeySortingFunction = function(a, b) { return yValues.indexOf(yExtractor(a)) - yValues.indexOf(yExtractor(b)) }, + objectSize, /** - * Instance of ColorFunction with .colorBy set to 'category' - * @function colorFunction - * @memberof heatmap# + * The spacerSize (actual width) used by the spacers between the boxes + * @param {number} [spacerSize=undefined] + * @memberof boxwhisker# * @property */ - colorFunction$$1 = colorFunction().colorBy('category'), + spacerSize, /** * Instance of Tooltip - * @function tooltip - * @memberof heatmap# - * @property - */ - tooltip$$1 = tooltip(), - - /** - * store the size the heatmap could be in the x dimension - * the actuall size of the heatmap will be the min of xSize and {@link heatmap#ySize} - * Calculated after heatmap called. - * @param {string[]} [xSize=undefined] - * @memberof heatmap# - * @property - */ - xSize, - /** - * store the size of the spacer in the x dimension - * Calculated after heatmap called. - * @param {string[]} [xSpacerSize=undefined] - * @memberof heatmap# - * @property - */ - xSpacerSize, - - /** - * store the size the heatmap could be in the y dimension - * the actuall size of the heatmap will be the min of xSize and {@link heatmap#xSize} - * Calculated after heatmap called. - * @param {string[]} [ySize=undefined] - * @memberof heatmap# - * @property - */ - ySize, - /** - * store the size of the spacer in the y dimension. - * Calculated after heatmap called. - * @param {string[]} [xSpacerSize=undefined] - * @memberof heatmap# + * @param {function} [tooltip=tooltip()] + * @memberof boxwhisker# * @property */ - ySpacerSize; - + tooltip$$1 = tooltip(); /** * Gets or sets the selection in which items are manipulated * @param {d3.selection} [_=none] - * @returns {heatmap | d3.selection} - * @memberof heatmap + * @returns {boxwhisker | d3.selection} + * @memberof boxwhisker * @property * by default selection = selection */ - hm.selection = function(_) { return arguments.length ? (selection = _, hm) : selection; }; + boxwhisker.selection = function(_) { return arguments.length ? (selection = _, boxwhisker) : selection; }; /** * Gets or sets the data - * (see {@link heatmap#data}) + * (see {@link boxwhisker#data}) * @param {number} [_=none] - * @returns {heatmap | object} - * @memberof heatmap + * @returns {boxwhisker | object} + * @memberof boxwhisker * @property */ - hm.data = function(_) { return arguments.length ? (data = _, hm) : data; }; - // hm.orient = function(_) { return arguments.length ? (orient = _, hm) : orient; } + boxwhisker.data = function(_) { return arguments.length ? (data = _, boxwhisker) : data; }; + /** + * Gets or sets the orient of the boxes + * (see {@link boxwhisker#orient}) + * @param {number} [_=none] + * @returns {boxwhisker | object} + * @memberof boxwhisker + * @property + */ + boxwhisker.orient = function(_) { return arguments.length ? (orient = _, boxwhisker) : orient; }; /** * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link heatmap#spaceX}) + * (see {@link boxwhisker#spaceX}) * @param {number} [_=none] should be a number > 0 - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property * by default spaceX = undefined */ - hm.spaceX = function(_) { return arguments.length ? (spaceX = _, hm) : spaceX; }; + boxwhisker.spaceX = function(_) { return arguments.length ? (spaceX = _, boxwhisker) : spaceX; }; /** * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link heatmap#spaceY}) + * (see {@link boxwhisker#spaceY}) * @param {number} [_=none] should be a number > 0 - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property * by default spaceY = undefined */ - hm.spaceY = function(_) { return arguments.length ? (spaceY = _, hm) : spaceY; }; - + boxwhisker.spaceY = function(_) { return arguments.length ? (spaceY = _, boxwhisker) : spaceY; }; /** - * Gets or sets the xKey - * (see {@link heatmap#xKey}) - * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * Gets / sets whether or not boxwhisker is allowed to go beyond specified dimensions + * (see {@link boxwhisker#overflowQ}) + * @param {boolean} [_=none] + * @returns {boxwhisker | boolean} + * @memberof boxwhisker * @property - * by default xKey = 'x' + * by default overflowQ = false */ - hm.xKey = function(_) { return arguments.length ? (xKey = _, hm) : xKey; }; + boxwhisker.overflowQ = function(_) { return arguments.length ? (overflowQ = _, boxwhisker) : overflowQ; }; /** - * Gets or sets the yKey - * (see {@link heatmap#yKey}) - * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * Gets / sets the grouping of the boxes + * (see {@link boxwhisker#grouping}) + * @param {Array[]} [_=none] + * @returns {boxwhisker | Array[]} + * @memberof boxwhisker * @property - * by default yKey = 'y' + * by default grouping = undefined */ - hm.yKey = function(_) { return arguments.length ? (yKey = _, hm) : yKey; }; - + boxwhisker.grouping = function(_) { return arguments.length ? (grouping = _, boxwhisker) : grouping; }; /** - * Gets or sets the vKey - * (see {@link heatmap#vKey}) + * Gets / sets the quartilesKey + * (see {@link boxwhisker#quartilesKey}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {boxwhisker | string} + * @memberof boxwhisker * @property - * by default vKey = 'y' + * by default quartilesKey = quartiles */ - hm.vKey = function(_) { return arguments.length ? (vKey = _, hm) : vKey; }; - + boxwhisker.quartilesKey = function(_) { return arguments.length ? (quartilesKey = _, boxwhisker) : quartilesKey; }; /** - * Gets or sets the cellKeys - * (see {@link heatmap#cellKeys}) + * Gets / sets the quartilesKeys + * (see {@link boxwhisker#quartilesKeys}) * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap + * @returns {boxwhisker | string[]} + * @memberof boxwhisker * @property - * by default cellKeys = undefined + * by default quartilesKeys = [Q0, Q1, Q2, Q3, Q4] */ - hm.cellKeys = function(_) { return arguments.length ? (cellKeys = _, hm) : cellKeys; }; + boxwhisker.quartilesKeys = function(_) { return arguments.length ? (quartilesKeys = _, boxwhisker) : quartilesKeys; }; /** - * Gets or sets the xValues - * (see {@link heatmap#xValues}) - * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap + * Gets / sets the valueExtractor + * (see {@link boxwhisker#valueExtractor}) + * @param {function} [_=none] + * @returns {boxwhisker | function} + * @memberof boxwhisker * @property - * by default xValues = undefined + * by default valueExtractor = function(key, index) { return data[key][quartilesKey] }, */ - hm.xValues = function(_) { return arguments.length ? (xValues = _, hm) : xValues; }; + + boxwhisker.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, boxwhisker) : valueExtractor; }; /** - * Gets or sets the yValues - * (see {@link heatmap#yValues}) - * @param {string[]} [_=none] - * @returns {heatmap | string[]} - * @memberof heatmap + * Gets / sets the sortingFunction + * (see {@link boxwhisker#sortingFunction}) + * @param {function} [_=none] + * @returns {boxwhisker | function} + * @memberof boxwhisker * @property - * by default yValues = undefined + * by default sortingFunction = function(keyA, keyB) {return d3.descending( + * valueExtractor(keyA)[quartilesKeys[4]], + * valueExtractor(keyB)[quartilesKeys[4]] + * )}, */ - hm.yValues = function(_) { return arguments.length ? (yValues = _, hm) : yValues; }; + boxwhisker.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, boxwhisker) : sortingFunction; }; /** - * Gets or sets the vValues - * (see {@link heatmap#vValues}) - * @param {number[]} [_=none] - * @returns {heatmap | number[]} - * @memberof heatmap + * Gets / sets the scale for which the boxwhisker values should be transformed by + * (see {@link boxwhisker#scale}) + * @param {d3.scale} [_=none] + * @returns {boxwhisker | d3.scale} + * @memberof boxwhisker * @property - * by default vValues = undefined + * by default scale = d3.scaleLinear() */ - hm.vValues = function(_) { return arguments.length ? (vValues = _, hm) : vValues; }; - - + boxwhisker.scale = function(_) { return arguments.length ? (scale = _, boxwhisker) : scale; }; /** - * Gets or sets the xExtractor - * (see {@link heatmap#xExtractor}) - * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap + * Gets / sets the padding for the domain of the scale + * (see {@link boxwhisker#domainPadding}) + * @param {number} [_=none] + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default xExtractor = undefined + * by default domainPadding = 0.5 */ - hm.xExtractor = function(_) { return arguments.length ? (xExtractor = _, hm) : xExtractor; }; + boxwhisker.domainPadding = function(_) { return arguments.length ? (domainPadding = _, boxwhisker) : domainPadding; }; /** - * Gets or sets the yExtractor - * (see {@link heatmap#yExtractor}) - * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap + * Gets / sets objectSpacer + * (see {@link boxwhisker#objectSpacer}) + * @param {number} [_=none] + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default yExtractor = undefined + * by default objectSpacer = 0.05 */ - hm.yExtractor = function(_) { return arguments.length ? (yExtractor = _, hm) : yExtractor; }; + boxwhisker.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, boxwhisker) : objectSpacer; }; /** - * Gets or sets the vExtractor - * (see {@link heatmap#vExtractor}) - * @param {function} [_=none] - * @returns {heatmap | function} - * @memberof heatmap + * Gets / sets the minObjectSize + * (see {@link boxwhisker#minObjectSize}) + * @param {number} [_=none] + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default vExtractor = undefined + * by default minObjectSize = 15 */ - hm.vExtractor = function(_) { return arguments.length ? (vExtractor = _, hm) : vExtractor; }; - - /** - * Gets / sets whether or not heatmap is allowed to go beyond specified dimensions - * (see {@link heatmap#spaceX}) - * @param {boolean} [_=none] - * @returns {heatmap | boolean} - * @memberof heatmap - * @property - * by default overflowQ = false - */ - hm.overflowQ = function(_) { return arguments.length ? (overflowQ = _, hm) : overflowQ; }; - /** - * Gets / sets objectSpacer - * (see {@link heatmap#objectSpacer}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap - * @property - * by default objectSpacer = 0.0 - */ - hm.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, objectSpacer) : data; }; + boxwhisker.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, boxwhisker) : minObjectSize; }; /** - * Gets / sets the minObjectSize - * (see {@link heatmap#minObjectSize}) + * Gets / sets the maxObjectSize + * (see {@link boxwhisker#maxObjectSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default minObjectSize = 50 + * by default maxObjectSize = 50 */ - hm.yMinObjectSize = function(_) { return arguments.length ? (yMinObjectSize = _, hm) : yMinObjectSize; }; + boxwhisker.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, boxwhisker) : maxObjectSize; }; /** - * Gets / sets the maxObjectSize - * (see {@link heatmap#maxObjectSize}) + * Gets / sets the whiskerWidthPercent + * (see {@link boxwhisker#whiskerWidthPercent}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default maxObjectSize = 100 + * by default maxObjectSize = 0.6 */ - hm.yMaxObjectSize = function(_) { return arguments.length ? (yMaxObjectSize = _, hm) : yMaxObjectSize; }; + boxwhisker.whiskerWidthPercent = function(_) { return arguments.length ? (whiskerWidthPercent = _, boxwhisker) : whiskerWidthPercent; }; /** - * Gets / sets the minObjectSize - * (see {@link heatmap#minObjectSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * Gets / sets the colorFunction + * (see {@link boxwhisker#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {boxwhisker | colorFunction} + * @memberof boxwhisker * @property - * by default minObjectSize = 50 + * by default colorFunction = colorFunction() */ - hm.xMinObjectSize = function(_) { return arguments.length ? (xMinObjectSize = _, hm) : xMinObjectSize; }; + boxwhisker.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, boxwhisker) : colorFunction$$1; }; /** - * Gets / sets the maxObjectSize - * (see {@link heatmap#maxObjectSize}) + * Gets / sets the boxStrokeWidth + * (see {@link boxwhisker#boxStrokeWidth}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default maxObjectSize = 100 + * by default boxStrokeWidth = 2 */ - hm.xMaxObjectSize = function(_) { return arguments.length ? (xMaxObjectSize = _, hm) : xMaxObjectSize; }; + boxwhisker.boxStrokeWidth = function(_) { return arguments.length ? (boxStrokeWidth = _, boxwhisker) : boxStrokeWidth; }; /** - * Gets / sets the objectStrokeWidth - * (see {@link heatmap#objectStrokeWidth}) + * Gets / sets the whiskerStrokeWidth + * (see {@link boxwhisker#whiskerStrokeWidth}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default objectStrokeWidth = 2 + * by default whiskerStrokeWidth = 2 */ - hm.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, hm) : objectStrokeWidth; }; + boxwhisker.whiskerStrokeWidth = function(_) { return arguments.length ? (whiskerStrokeWidth = _, boxwhisker) : whiskerStrokeWidth; }; + /** * Gets / sets the backgroundFill - * (see {@link heatmap#backgroundFill}) + * (see {@link boxwhisker#backgroundFill}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {boxwhisker | string} + * @memberof boxwhisker * @property * by default backgroundFill = 'transparent' */ - hm.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, hm) : backgroundFill; }; + boxwhisker.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, boxwhisker) : backgroundFill; }; /** * Gets / sets the namespace - * (see {@link heatmap#namespace}) + * (see {@link boxwhisker#namespace}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {boxwhisker | string} + * @memberof boxwhisker * @property - * by default namespace = 'd3sm-heatmap' + * by default namespace = 'd3sm-boxwhisker' */ - hm.namespace = function(_) { return arguments.length ? (namespace = _, hm) : namespace; }; + boxwhisker.namespace = function(_) { return arguments.length ? (namespace = _, boxwhisker) : namespace; }; /** * Gets / sets the objectClass - * (see {@link heatmap#objectClass}) + * (see {@link boxwhisker#objectClass}) * @param {string} [_=none] - * @returns {heatmap | string} - * @memberof heatmap + * @returns {boxwhisker | string} + * @memberof boxwhisker * @property * by default objectClass = 'tick-group' */ - hm.objectClass = function(_) { return arguments.length ? (objectClass = _, hm) : objectClass; }; + boxwhisker.objectClass = function(_) { return arguments.length ? (objectClass = _, boxwhisker) : objectClass; }; /** * Gets / sets the transitionDuration - * (see {@link heatmap#transitionDuration}) + * (see {@link boxwhisker#transitionDuration}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property * by default transitionDuration = 1000 */ - hm.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, hm) : transitionDuration; }; + boxwhisker.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, boxwhisker) : transitionDuration; }; /** * Gets / sets the easeFunc - * (see {@link heatmap#easeFunc}) + * (see {@link boxwhisker#easeFunc}) * @param {d3.ease} [_=none] - * @returns {heatmap | d3.ease} - * @memberof heatmap + * @returns {boxwhisker | d3.ease} + * @memberof boxwhisker * @property * by default easeFunc = d3.easeExp */ - hm.easeFunc = function(_) { return arguments.length ? (easeFunc = _, hm) : easeFunc; }; - - /** - * Gets / sets the tooltip - * (see {@link heatmap#tooltip}) - * @param {tooltip} [_=none] - * @returns {heatmap | tooltip} - * @memberof heatmap - * @property - * by default tooltip = tooltip() - */ - hm.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, hm) : tooltip$$1; }; + boxwhisker.easeFunc = function(_) { return arguments.length ? (easeFunc = _, boxwhisker) : easeFunc; }; /** - * Gets / sets the colorFunction - * (see {@link heatmap#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {heatmap | colorFunction} - * @memberof heatmap + * Gets / sets the barKeys + * (see {@link boxwhisker#boxKeys}) + * @param {string[]} [_=none] + * @returns {boxwhisker | string[]} + * @memberof boxwhisker * @property - * by default colorFunction = colorFunction() + * by default boxKeys = undefined */ - hm.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, hm) : colorFunction$$1; }; - + boxwhisker.boxKeys = function(_) { return arguments.length ? (boxKeys = _, boxwhisker) : boxKeys; }; /** - * Gets / sets the xSize - * (see {@link heatmap#xSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * Gets / sets the boxValues + * (see {@link boxwhisker#boxValues}) + * @param {number[]} [_=none] + * @returns {boxwhisker | number[]} + * @memberof boxwhisker * @property - * by default xSize = undefined + * by default boxValues = undefined */ - hm.xSize = function(_) { return arguments.length ? (xSize = _, hm) : xSize; }; + boxwhisker.boxValues = function(_) { return arguments.length ? (boxValues = _, boxwhisker) : boxValues; }; /** - * Gets / sets the xSpacerSize - * (see {@link heatmap#xSpacerSize}) + * Gets / sets the objectSize + * (see {@link boxwhisker#objectSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default xSpacerSize = undefined + * by default objectSize = undefined */ - hm.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, hm) : xSpacerSize; }; + boxwhisker.objectSize = function(_) { return arguments.length ? (objectSize = _, boxwhisker) : objectSize; }; /** - * Gets / sets the ySize - * (see {@link heatmap#ySize}) + * Gets / sets the spacerSize + * (see {@link boxwhisker#spacerSize}) * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * @returns {boxwhisker | number} + * @memberof boxwhisker * @property - * by default ySize = undefined + * by default spacerSize = undefined */ - hm.ySize = function(_) { return arguments.length ? (ySize = _, hm) : ySize; }; + boxwhisker.spacerSize = function(_) { return arguments.length ? (spacerSize = _, boxwhisker) : spacerSize; }; /** - * Gets / sets the ySpacerSize - * (see {@link heatmap#ySpacerSize}) - * @param {number} [_=none] - * @returns {heatmap | number} - * @memberof heatmap + * Gets / sets the tooltip + * (see {@link boxwhisker#tooltip}) + * @param {tooltip} [_=none] + * @returns {boxwhisker | tooltip} + * @memberof boxwhisker * @property - * by default ySpacerSize = undefined + * by default tooltip = tooltip() */ - hm.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, hm) : ySpacerSize; }; - // hm.yKeySortingFunction = function(_) { return arguments.length ? (yKeySortingFunction = _, hm) : yKeySortingFunction; } - // hm.xKeySortingFunction = function(_) { return arguments.length ? (xKeySortingFunction = _, hm) : xKeySortingFunction; } + boxwhisker.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, boxwhisker) : tooltip$$1; }; + function boxwhisker() { + // for convenience in handling orientation specific values + var horizontalQ = (orient == 'horizontal') ? true : false; + var verticalQ = !horizontalQ; - function hm() { + // background cliping rectangle var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - - cellKeys = d3.keys(data); + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - xValues = utils$1.arr.unique(cellKeys.map(xExtractor)); - yValues = utils$1.arr.unique(cellKeys.map(yExtractor)); - vValues = utils$1.arr.unique(cellKeys.map(vExtractor)); + // if grouping is undefined sort keys by sorting funct + var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping; + // to prevent re-calculation and getters to be passed to axes + boxKeys = utils.arr.flatten(ordered); + boxValues = boxKeys.map(valueExtractor); - cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }); - utils$1.con.log('heatmap', 'cells are sorted by', cellKeys); + var numberOfObjects = boxKeys.length; + var extent = [ + Math.min(...boxValues.map(function(d,i){return d[quartilesKeys[0]]})) - domainPadding, + Math.max(...boxValues.map(function(d,i){return d[quartilesKeys[4]]})) + domainPadding + ]; + // set the scale + scale.domain(extent).range(horizontalQ ? [0,spaceY] : [spaceX, 0]); + var space = horizontalQ ? spaceX : spaceY; + // calculate object size + objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + // calculate spacer size if needed + spacerSize = utils.math.calculateWidthOfSpacer(boxKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); + // make the nested groups + var spacerFunction = groupingSpacer() + .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) + .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc) + .namespace(namespace); - utils$1.con.log('heatmap', 'x and y keys are', {x: xValues, y:yValues}); + // move stuff + spacerFunction(container, ordered, 0); + var parentIndexArray = []; + container.selectAll('g:not(.to-remove).'+objectClass) + .each(function(d, i){if (utils.arr.hasQ(boxKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}}); - var xDim = xValues.length, yDim = yValues.length; - ySize = utils$1.math.calculateWidthOfObject(spaceY, yDim, yMinObjectSize, yMaxObjectSize, objectSpacer, overflowQ); - xSize = utils$1.math.calculateWidthOfObject(spaceX, xDim, xMinObjectSize, xMaxObjectSize, objectSpacer, overflowQ); - ySpacerSize = utils$1.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); - xSpacerSize = utils$1.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); - // console.table({ - // x:{ - // object: xSize, - // spacer: xSpacerSize, - // dim: xDim - // }, - // y:{ - // object: ySize, - // spacer: ySpacerSize, - // dim: yDim - // } - // - // }) - utils$1.con.log('heatmap', 'size of', {x: xSize, y: ySize}); + colorFunction$$1 = colorFunction$$1.colorBy() == 'index' + ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) + : colorFunction$$1.dataExtent(extent); + // set attributes for box and whiskers + container.selectAll('g:not(.to-remove).'+objectClass).each(function(key, i) { + var t = d3.select(this), + currentData = data[key], - var ySpacer = groupingSpacer() - .horizontalQ(false) - .moveby('category') - .numberOfObjects(yDim) - .objectClass(utils$1.str.hypenate(objectClass, 'row')) - .objectSize(ySize + ySpacerSize) - .spacerSize(0) - .transitionDuration(transitionDuration) - .easeFunc(easeFunc) - .namespace('row'); + quartiles$$1 = valueExtractor(key, i), + q0 = quartiles$$1[quartilesKeys[0]], + q1 = quartiles$$1[quartilesKeys[1]], + q2 = quartiles$$1[quartilesKeys[2]], + q3 = quartiles$$1[quartilesKeys[3]], + q4 = quartiles$$1[quartilesKeys[4]]; - var xSpacer = groupingSpacer() - .horizontalQ(true) - .moveby('category') - .numberOfObjects(xDim) - .objectClass(objectClass) - .objectSize(xSize + xSpacerSize) - .spacerSize(0) - .transitionDuration(transitionDuration) - .easeFunc(easeFunc); + var i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), + fillColor = colorFunction$$1(key, q2, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(key, q2, i, 'stroke'); - ySpacer(container, yValues, 0); - container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'row')) - .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); + var + whisk = utils.sel.safeSelect(t, 'g', 'whisker'), + uWhisk = utils.sel.safeSelect(whisk, 'path', 'upper'), + lWhisk = utils.sel.safeSelect(whisk, 'path', 'lower'), + quart = utils.sel.safeSelect(t, 'g', 'quartile'), + uQuart = utils.sel.safeSelect(quart, 'rect', 'upper'), + lQuart = utils.sel.safeSelect(quart, 'rect', 'lower'), + mQuart = utils.sel.safeSelect(quart, 'circle', 'median'); + + + // set upper quartile (q3) + uQuart.transition().duration(transitionDuration).ease(easeFunc) + .attr('width', horizontalQ ? objectSize : scale(q3) - scale(q2)) + .attr('height', verticalQ ? objectSize : scale(q3) - scale(q2)) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', boxStrokeWidth) + .attr('transform', function(d, i){ + var + x = horizontalQ ? 0 : scale(q2), + y = verticalQ ? 0 : scale(extent[1]) - scale(q3), + t = 'translate('+x+','+y+')'; + return t + }); - var cells = container.selectAll('g:not(.to-remove).'+objectClass); + // set lower quartile (q1) + lQuart.transition().duration(transitionDuration).ease(easeFunc) + .attr('width', horizontalQ ? objectSize : scale(q2) - scale(q1)) + .attr('height', verticalQ ? objectSize : scale(q2) - scale(q1)) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', boxStrokeWidth) + .attr('transform', function(d, i){ + var + x = horizontalQ ? 0 : scale(q1), + y = verticalQ ? 0 : scale(extent[1]) - scale(q2), + t = 'translate('+x+','+y+')'; + return t + }); - if (cellKeys.length != yValues.length * xValues.length) { - var lookup = {}; - cellKeys.map(function(k, i){ - lookup[xExtractor(k)+'::'+yExtractor(k)] = k; + // set median (q2) + mQuart.transition().duration(transitionDuration).ease(easeFunc) + .attr('r', function(d, i){ + var r = objectSize / 2; + var dif = (scale(q3) - scale(q1)) / 2; + return (r > dif) ? dif : r + }) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', boxStrokeWidth) + .attr('transform', function(d, i){ + var + x = horizontalQ ? objectSize / 2 : scale(q2), + y = verticalQ ? objectSize / 2 : scale(extent[1]) - scale(q2), + t = 'translate('+x+','+y+')'; + return t }); - var positionedCellKeys = []; - for (var i = 0; i < yValues.length; i++) { - for (var j = 0; j < xValues.length; j++) { - var lookupValue = lookup[xValues[j]+"::"+yValues[i]]; - if (lookupValue == undefined) { - positionedCellKeys.push(undefined); - } else { - positionedCellKeys.push(lookupValue); - } - } - } + // set lower whisker (min) + lWhisk.transition().duration(transitionDuration).ease(easeFunc) + .attr('d', function(dd, ii){ + var + dir = false, + x = 0, + y = 0, + h = horizontalQ ? scale(q1) - scale(q0) : objectSize, + w = verticalQ ? scale(q1) - scale(q0) : objectSize; + return utils.paths.whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient) + }) + .attr('transform', function(d, i){ + var + x = horizontalQ ? 0 : scale(q1), + y = verticalQ ? 0 : scale(extent[1]) - scale(q1), + t = 'translate('+x+','+y+')'; + return t + }) + .attr('stroke', 'black').attr('stroke-width', whiskerStrokeWidth) + .attr('fill', 'none'); - cells.data(positionedCellKeys); - - // maybe breaks this - // !!!!!! IMPORTANT NOTE TODO LOOK HERE BUG - cellKeys = positionedCellKeys; - } else { - cells.data(cellKeys); - } - - - - var parentIndexArray = []; - cells.each(function(d, i){ parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); }); - - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' - ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) - : colorFunction$$1.dataExtent([0, Math.max(...vValues)]); - - cells.each(function(key, i) { - utils$1.con.log('heatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); - - var t = d3.select(this); - if (key == undefined) {return} - var currentData = data[key], - value = vExtractor(key, i), - i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, value, i, 'fill'), // prevent duplicate computation - strokeColor = colorFunction$$1(key, value, i, 'stroke'); - - var c = utils$1.sel.safeSelect(t, 'rect', utils$1.str.hypenate(objectClass,'rect')); - c.attr('width', xSize + xSpacerSize - objectStrokeWidth) - .attr('height', ySize + ySpacerSize - objectStrokeWidth) - .attr('fill', fillColor) - .attr('x', objectStrokeWidth/2) - .attr('y', objectStrokeWidth/2) - .attr('stroke', "#000") - .attr('stroke-width', objectStrokeWidth); + // set upper whisker (max) + uWhisk.transition().duration(transitionDuration).ease(easeFunc) + .attr('d', function(dd, ii){ + var + dir = true, + x = 0, + y = 0, + h = horizontalQ ? scale(q4) - scale(q3) : objectSize, + w = verticalQ ? scale(q4) - scale(q3) : objectSize; + return utils.paths.whiskerPath(dir, x, y, w, h, whiskerWidthPercent, orient) + }) + .attr('transform', function(d, i){ + var + x = horizontalQ ? 0 : scale(q3), + y = verticalQ ? 0 : scale(extent[1]) - scale(q4), + t = 'translate('+x+','+y+')'; + return t + }) + .attr('stroke', 'black') + .attr('stroke-width', whiskerStrokeWidth) + .attr('fill', 'none'); }); - tooltip$$1.selection(cells.selectAll('rect.'+utils$1.str.hypenate(objectClass, 'rect'))) + tooltip$$1.selection(container.selectAll('g:not(.to-remove).'+objectClass)) .data(data); - // .keys(['r', 'v']) - // .header(function(d, i){return utils.str.hypenate(data[d][xKey], data[d][yKey]) }) - tooltip$$1(); } - return hm; + return boxwhisker } - /******************************************************************************* - ** ** - ** ** - ** VIOLIN ** - ** ** - ** ** - *******************************************************************************/ - /** - * Creates a violin + * Creates a heatmap * - * {@link https://sumneuron.gitlab.io/d3sm/demos/basic-violins/index.html Demo} - * @constructor violin + * {@link https://sumneuron.gitlab.io/d3sm/demos/heatmap-heatmap/index.html Demo} + * @constructor heatmap * @param {d3.selection} selection - * @namespace violin - * @returns {function} violin + * @namespace heatmap + * @returns {function} heatmap */ - function violin( selection ) { + function heatmap( selection ) { var /** - * Data to plot. Assumed to be a object, where each key corresponds to a violin - * (see {@link violin#data}) + * Data to plot. Assumed to be a object, where each key corresponds to a cell + * (see {@link heatmap#data}) * @param {Object} [data=undefined] - * @memberof violin# + * @memberof heatmap# * @property */ data, + /** - * Which direction to render the bars in - * (see {@link violin#orient}) - * @param {number} [orient='horizontal'] - * @memberof violin# - * @property - */ - orient='horizontal', - /** - * Amount of horizontal space (in pixels) avaible to render the violin in - * (see {@link violin#spaceX}) + * Amount of horizontal space (in pixels) avaible to render the heatmap in + * (see {@link heatmap#spaceX}) * @param {number} [spaceX=undefined] - * @memberof violin# + * @memberof heatmap# * @property */ spaceX, /** - * Amount of vertical space (in pixels) avaible to render the violin in - * (see {@link violin.spaceY}) + * Amount of vertical space (in pixels) avaible to render the heatmap in + * (see {@link heatmap.spaceY}) * @param {number} [spaceY=undefined] - * @memberof violin# + * @memberof heatmap# * @property */ spaceY, + /** - * Whether or not to allow violin to render elements pass the main spatial dimension - * given the orientation (see {@link violin#orient}), where {@link violin#orient}="horizontal" - * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" - * the main dimension is {@link violin#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof violin# + * The internal key of the cell specifiying to which x axis key it belongs + * (see {@link heatmap.xKey}) + * @param {string} [xKey='x'] + * @memberof heatmap# * @property */ - overflowQ = true, + xKey = 'x', /** - * Whether or not to display points inside the points - * @param {boolean} [pointsQ=false] - * @memberof violin# + * The internal key of the cell specifiying to which y axis key it belongs + * (see {@link heatmap.yKey}) + * @param {string} [yKey='y'] + * @memberof heatmap# * @property */ - pointsQ = true, + yKey = 'y', + /** - * An array - putatively of other arrays - depicting how bars should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof violin# + * The internal key of the cell specifiying what value to use to determine the color + * (see {@link heatmap.vKey}) + * @param {string} [vKey='v'] + * @memberof heatmap# * @property */ - grouping, + vKey = 'v', + /** - * How to get the value of the violin - * @param {function} [valueExtractor=function(key, index) { return data[key] }] - * @memberof violin# + * Function for extracting the the value from xKey. + * (see {@link heatmap.xExtractor}) + * @param {function} [xExtractor=function(key, i) { return data[key][xKey] }] + * @returns {string} + * @memberof heatmap# * @property */ - valueExtractor = function(key, index) {return data[key] }, + xExtractor = function(key, i) {return data[key][xKey] }, /** - * How to sort the bars - if {@link violin#grouping} is not provided. - * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] - * @memberof violin# + * Function for extracting the the value from yKey. + * (see {@link heatmap.yExtractor}) + * @param {function} [yExtractor=function(key, i) { return data[key][yKey] }] + * @returns {string} + * @memberof heatmap# * @property */ - sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, + yExtractor = function(key, i) { return data[key][yKey] }, /** - * The scale for which violin values should be transformed by - * @param {d3.scale} [scale=d3.scaleLinear] - * @memberof violin# + * Function for extracting the the value from vKey. + * (see {@link heatmap.vExtractor}) + * @param {function} [vExtractor=function(key, i) { return data[key][vKey] }] + * @returns {number} + * @memberof heatmap# * @property */ - scale = d3.scaleLinear(), + vExtractor = function(key, i) { return data[key][vKey] }, + + /** - * The padding for the domain of the scale (see {@link violin#scale}) - * @param {number} [domainPadding=0.5] - * @memberof violin# + * Whether or not to allow heatmap to render elements pass the main spatial dimension + * given the orientation (see {@link heatmap#orient}), where {@link heatmap#orient}="bottom" or {@link heatmap#orient}="top" + * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="left" or {@link heatmap#orient}="right" + * the main dimension is {@link heatmap#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof heatmap# * @property */ - domainPadding = 0.5, + overflowQ = false, + /** * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link violin#orient}), where {@link violin#orient}="horizontal" - * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" - * the main dimension is {@link violin#spaceY} between bars - * @param {number} [objectSpacer=0.05] - * @memberof violin# + * (see {@link heatmap#orient}), where {@link heatmap#orient}="horizontal" + * the main dimension is {@link heatmap#spaceX} and where {@link heatmap#orient}="vertical" + * the main dimension is {@link heatmap#spaceY} between bubbles + * @param {number} [objectSpacer=0.0] + * @memberof heatmap# * @property */ - objectSpacer = 0.05, + objectSpacer = 0.0, /** - * The minimum size that an object can be + * The minimum size that an object can be in the y dimension * @param {number} [minObjectSize=50] - * @memberof violin# - * @property - */ - minObjectSize = 50, - /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof violin# + * @memberof heatmap# * @property */ - maxObjectSize = 100, - + yMinObjectSize = 50, /** - * The stroke width of the bars - * @param {number} [barStrokeWidth=2] - * @memberof violin# + * The minimum size that an object can be in the x dimension + * @param {number} [minObjectSize=50] + * @memberof heatmap# * @property */ - objectStrokeWidth = 2, + xMinObjectSize = 50, /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof violin# + * The maximum size that an object can be in the x dimension + * @param {number} [maxObjectSize=100] + * @memberof heatmap# * @property */ - colorFunction$$1 = colorFunction(), + xMaxObjectSize = 100, /** - * Instance of ColorFunction modified by a scale for the points - * @param {function} [pointColorFunc = colorFunction()] - * @memberof violin# + * The maximum size that an object can be in the y dimension + * @param {number} [maxObjectSize=100] + * @memberof heatmap# * @property */ - pointColorFunc = function (d, type, base, min, max) { - var minMaxHexScale = d3.scaleLinear().domain([min, max]).range([-0.25, 0.05]); - var scaledColor = utils.color.modifyHexidecimalColorLuminance(base.replace('#', ''), minMaxHexScale(d)); - var mod = type == "stroke" ? 0 : 0.25; - return utils.color.modifyHexidecimalColorLuminance(scaledColor.replace('#', ''), mod) - }, + yMaxObjectSize = 100, + /** - * The radius of a point - * @param {number} [pointRadius=3] - * @memberof violin# - * @property - */ - pointRadius = 3, - /** - * The stroke width of the oints - * @param {number} [pointStrokeWidth=2] - * @memberof violin# + * The stroke width of the bubbles + * @param {number} [objectStrokeWidth=2] + * @memberof heatmap# * @property */ - pointStrokeWidth = 2, + objectStrokeWidth = 2, + // colorFunc = colorFunction(), /** * Color of the background * @param {string} [backgroundFill="transparent"] - * @memberof violin# + * @memberof heatmap# * @property */ backgroundFill = 'transparent', /** - * Namespace for all items made by this instance of violin - * @param {string} [namespace="d3sm-violin"] - * @memberof violin# + * Namespace for all items made by this instance of heatmap + * @param {string} [namespace="d3sm-heatmap"] + * @memberof heatmap# * @property */ - namespace = 'd3sm-violin', + namespace = 'd3sm-heatmap', /** - * Class name for violin container ( element) - * @param {string} [objectClass="violin"] - * @memberof violin# + * Class name for heatmap container ( element) + * @param {string} [objectClass="heatmap"] + * @memberof heatmap# * @property */ - objectClass = 'violin', + objectClass = 'heatmap', /** * Duration of all transitions of this element * @param {number} [transitionDuration=1000] - * @memberof violin# + * @memberof heatmap# * @property */ transitionDuration = 1000, /** * Easing function for transitions * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof violin# + * @memberof heatmap# * @property */ easeFunc = d3.easeExp, /** - * The keys corresponding to each quartile - * @param {string[]} [quartileKeys=["Q0", "Q1", "Q2", "Q3", "Q4"]] - * @memberof violin# + * Stores the keys of all the cells + * Calculated after heatmap called. + * @param {string[]} [cellKeys=undefined] + * @memberof heatmap# * @property */ - quartileKeys = ["Q0", "Q1", "Q2", "Q3", "Q4"], - + cellKeys, /** - * The keys of the bars - * @param {string[]} [violinKeys=undefined] - * @memberof violin# + * Stores the list of utils.arr.unique xValues + * Calculated after heatmap called. + * @param {string[]} [xValues=undefined] + * @memberof heatmap# * @property */ - violinKeys, + xValues, /** - * The values of the bars - * @param {number[]} [violinValues=undefined] - * @memberof violin# + * Stores the list of utils.arr.unique yValues + * Calculated after heatmap called. + * @param {string[]} [yValues=undefined] + * @memberof heatmap# * @property */ - violinValues, + yValues, /** - * The objectSize (actual width) used by the bars - * @param {number} [objectSize=undefined] - * @memberof violin# + * Stores the list of utils.arr.unique vValues + * Calculated after heatmap called. + * @param {string[]} [vValues=undefined] + * @memberof heatmap# * @property */ - objectSize, + vValues, + + xKeySortingFunction = function(a, b) { return xValues.indexOf(xExtractor(a)) - xValues.indexOf(xExtractor(b)) }, + yKeySortingFunction = function(a, b) { return yValues.indexOf(yExtractor(a)) - yValues.indexOf(yExtractor(b)) }, /** - * The spacerSize (actual width) used by the spacers between the bars - * @param {number} [spacerSize=undefined] - * @memberof violin# + * Instance of ColorFunction with .colorBy set to 'category' + * @function colorFunction + * @memberof heatmap# * @property */ - spacerSize, - + colorFunction$$1 = colorFunction().colorBy('category'), /** * Instance of Tooltip - * @param {function} [tooltip=tooltip()] - * @memberof violin# + * @function tooltip + * @memberof heatmap# * @property */ - tooltip$$1 = tooltip().keys([quartileKeys[4], quartileKeys[3], quartileKeys[2], quartileKeys[1], quartileKeys[0]]), - pointsTooltip = tooltip(), - // pointKeyExtractor = function(violinKey, violinData, violinValues) {return d3.keys(violinValues[violinKey].values)}, - // pointValueExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]}, - + tooltip$$1 = tooltip(), /** - * Function which given the key of the violin and that key's associated value - * returns the object consiting of the points of the violin - * (see {@link violin#violinPointsExtractor}) - * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] - * @memberof violin# + * store the size the heatmap could be in the x dimension + * the actuall size of the heatmap will be the min of xSize and {@link heatmap#ySize} + * Calculated after heatmap called. + * @param {string[]} [xSize=undefined] + * @memberof heatmap# * @property */ - violinPointsExtractor = function (violinKey, violinData) {return violinData.points }, + xSize, /** - * Function which given the key of the current point and the object of points for the - * violin, returns the numerical value of the point - * (see {@link violin#violinPointValueExtractor}) - * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] - * @memberof violin# + * store the size of the spacer in the x dimension + * Calculated after heatmap called. + * @param {string[]} [xSpacerSize=undefined] + * @memberof heatmap# * @property */ - violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }; - + xSpacerSize, /** - * Gets or sets the violinPointsExtractor - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } - */ - violin.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor = _, violin) : violinPointsExtractor; }; + * store the size the heatmap could be in the y dimension + * the actuall size of the heatmap will be the min of xSize and {@link heatmap#xSize} + * Calculated after heatmap called. + * @param {string[]} [ySize=undefined] + * @memberof heatmap# + * @property + */ + ySize, /** - * Gets or sets the violinPointValueExtractor - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin - * @property - * by default violinPointsExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]} - */ - violin.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor = _, violin) : violinPointValueExtractor; }; - + * store the size of the spacer in the y dimension. + * Calculated after heatmap called. + * @param {string[]} [xSpacerSize=undefined] + * @memberof heatmap# + * @property + */ + ySpacerSize; /** * Gets or sets the selection in which items are manipulated * @param {d3.selection} [_=none] - * @returns {violin | d3.selection} - * @memberof violin + * @returns {heatmap | d3.selection} + * @memberof heatmap * @property * by default selection = selection */ - violin.selection = function(_) { return arguments.length ? (selection = _, violin) : selection; }; + hm.selection = function(_) { return arguments.length ? (selection = _, hm) : selection; }; /** * Gets or sets the data - * (see {@link violin#data}) - * @param {number} [_=none] - * @returns {violin | object} - * @memberof violin - * @property - */ - violin.data = function(_) { return arguments.length ? (data = _, violin) : data; }; - /** - * Gets or sets the orient of the boxes - * (see {@link violin#orient}) + * (see {@link heatmap#data}) * @param {number} [_=none] - * @returns {violin | object} - * @memberof violin + * @returns {heatmap | object} + * @memberof heatmap * @property */ - violin.orient = function(_) { return arguments.length ? (orient = _, violin) : orient; }; + hm.data = function(_) { return arguments.length ? (data = _, hm) : data; }; + // hm.orient = function(_) { return arguments.length ? (orient = _, hm) : orient; } /** * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link violin#spaceX}) + * (see {@link heatmap#spaceX}) * @param {number} [_=none] should be a number > 0 - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property * by default spaceX = undefined */ - violin.spaceX = function(_) { return arguments.length ? (spaceX = _, violin) : spaceX; }; + hm.spaceX = function(_) { return arguments.length ? (spaceX = _, hm) : spaceX; }; /** * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link violin#spaceY}) + * (see {@link heatmap#spaceY}) * @param {number} [_=none] should be a number > 0 - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property * by default spaceY = undefined */ - violin.spaceY = function(_) { return arguments.length ? (spaceY = _, violin) : spaceY; }; - + hm.spaceY = function(_) { return arguments.length ? (spaceY = _, hm) : spaceY; }; /** - * Gets / sets whether or not violin is allowed to go beyond specified dimensions - * (see {@link violin#overflowQ}) - * @param {boolean} [_=none] - * @returns {violin | boolean} - * @memberof violin + * Gets or sets the xKey + * (see {@link heatmap#xKey}) + * @param {string} [_=none] + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default overflowQ = false + * by default xKey = 'x' */ - violin.overflowQ = function(_) { return arguments.length ? (overflowQ = _, violin) : overflowQ; }; + hm.xKey = function(_) { return arguments.length ? (xKey = _, hm) : xKey; }; /** - * Gets / sets whether or not to plot points with the violins - * (see {@link violin#pointsQ}) - * @param {boolean} [_=none] - * @returns {violin | boolean} - * @memberof violin + * Gets or sets the yKey + * (see {@link heatmap#yKey}) + * @param {string} [_=none] + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default pointsQ = false + * by default yKey = 'y' */ - violin.pointsQ = function(_) { return arguments.length ? (pointsQ = _, violin) : pointsQ; }; - + hm.yKey = function(_) { return arguments.length ? (yKey = _, hm) : yKey; }; /** - * Gets / sets the grouping of the boxes - * (see {@link violin#grouping}) - * @param {Array[]} [_=none] - * @returns {violin | Array[]} - * @memberof violin + * Gets or sets the vKey + * (see {@link heatmap#vKey}) + * @param {string} [_=none] + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default grouping = undefined + * by default vKey = 'y' */ - violin.grouping = function(_) { return arguments.length ? (grouping = _, violin) : grouping; }; + hm.vKey = function(_) { return arguments.length ? (vKey = _, hm) : vKey; }; + /** - * Gets / sets the valueExtractor - * (see {@link violin#valueExtractor}) - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin + * Gets or sets the cellKeys + * (see {@link heatmap#cellKeys}) + * @param {string[]} [_=none] + * @returns {heatmap | string[]} + * @memberof heatmap * @property + * by default cellKeys = undefined */ - violin.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, violin) : valueExtractor; }; + hm.cellKeys = function(_) { return arguments.length ? (cellKeys = _, hm) : cellKeys; }; /** - * Gets / sets the sortingFunction - * (see {@link violin#sortingFunction}) - * @param {function} [_=none] - * @returns {violin | function} - * @memberof violin + * Gets or sets the xValues + * (see {@link heatmap#xValues}) + * @param {string[]} [_=none] + * @returns {heatmap | string[]} + * @memberof heatmap * @property + * by default xValues = undefined */ - violin.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, violin) : sortingFunction; }; - + hm.xValues = function(_) { return arguments.length ? (xValues = _, hm) : xValues; }; /** - * Gets / sets the scale for which the violin values should be transformed by - * (see {@link violin#scale}) - * @param {d3.scale} [_=none] - * @returns {violin | d3.scale} - * @memberof violin + * Gets or sets the yValues + * (see {@link heatmap#yValues}) + * @param {string[]} [_=none] + * @returns {heatmap | string[]} + * @memberof heatmap * @property - * by default scale = d3.scaleLinear() + * by default yValues = undefined */ - violin.scale = function(_) { return arguments.length ? (scale = _, violin) : scale; }; + hm.yValues = function(_) { return arguments.length ? (yValues = _, hm) : yValues; }; /** - * Gets / sets the padding for the domain of the scale - * (see {@link violin#domainPadding}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * Gets or sets the vValues + * (see {@link heatmap#vValues}) + * @param {number[]} [_=none] + * @returns {heatmap | number[]} + * @memberof heatmap * @property - * by default domainPadding = 0.5 + * by default vValues = undefined */ - violin.domainPadding = function(_) { return arguments.length ? (domainPadding = _, violin) : domainPadding; }; + hm.vValues = function(_) { return arguments.length ? (vValues = _, hm) : vValues; }; /** - * Gets / sets objectSpacer - * (see {@link violin#objectSpacer}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * Gets or sets the xExtractor + * (see {@link heatmap#xExtractor}) + * @param {function} [_=none] + * @returns {heatmap | function} + * @memberof heatmap * @property - * by default objectSpacer = 0.05 + * by default xExtractor = undefined */ - violin.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, violin) : objectSpacer; }; + hm.xExtractor = function(_) { return arguments.length ? (xExtractor = _, hm) : xExtractor; }; /** - * Gets / sets the minObjectSize - * (see {@link violin#minObjectSize}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * Gets or sets the yExtractor + * (see {@link heatmap#yExtractor}) + * @param {function} [_=none] + * @returns {heatmap | function} + * @memberof heatmap * @property - * by default minObjectSize = 15 + * by default yExtractor = undefined */ - violin.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, violin) : minObjectSize; }; + hm.yExtractor = function(_) { return arguments.length ? (yExtractor = _, hm) : yExtractor; }; /** - * Gets / sets the maxObjectSize - * (see {@link violin#maxObjectSize}) - * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * Gets or sets the vExtractor + * (see {@link heatmap#vExtractor}) + * @param {function} [_=none] + * @returns {heatmap | function} + * @memberof heatmap * @property - * by default maxObjectSize = 50 + * by default vExtractor = undefined */ - violin.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, violin) : maxObjectSize; }; + hm.vExtractor = function(_) { return arguments.length ? (vExtractor = _, hm) : vExtractor; }; /** - * Gets / sets the objectStrokeWidth - * (see {@link violin#objectStrokeWidth}) + * Gets / sets whether or not heatmap is allowed to go beyond specified dimensions + * (see {@link heatmap#spaceX}) + * @param {boolean} [_=none] + * @returns {heatmap | boolean} + * @memberof heatmap + * @property + * by default overflowQ = false + */ + hm.overflowQ = function(_) { return arguments.length ? (overflowQ = _, hm) : overflowQ; }; + /** + * Gets / sets objectSpacer + * (see {@link heatmap#objectSpacer}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default objectStrokeWidth = 2 + * by default objectSpacer = 0.0 */ - violin.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, violin) : objectStrokeWidth; }; - - + hm.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, objectSpacer) : data; }; /** - * Gets / sets the colorFunction - * (see {@link violin#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {violin | colorFunction} - * @memberof violin + * Gets / sets the minObjectSize + * (see {@link heatmap#minObjectSize}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default colorFunction = colorFunction() + * by default minObjectSize = 50 */ - violin.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, violin) : colorFunction$$1; }; + hm.yMinObjectSize = function(_) { return arguments.length ? (yMinObjectSize = _, hm) : yMinObjectSize; }; /** - * Gets / sets the colorFunction - * (see {@link violin#colorFunction}) - * @param {colorFunction} [_=none] - * @returns {violin | colorFunction} - * @memberof violin + * Gets / sets the maxObjectSize + * (see {@link heatmap#maxObjectSize}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default colorFunction = colorFunction() + * by default maxObjectSize = 100 */ - violin.pointColorFunc = function(_) { return arguments.length ? (pointColorFunc = _, violin) : pointColorFunc; }; - - + hm.yMaxObjectSize = function(_) { return arguments.length ? (yMaxObjectSize = _, hm) : yMaxObjectSize; }; /** - * Gets / sets the pointRadius - * (see {@link violin#pointRadius}) + * Gets / sets the minObjectSize + * (see {@link heatmap#minObjectSize}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default pointRadius = 2 + * by default minObjectSize = 50 */ - violin.pointRadius = function(_) { return arguments.length ? (pointRadius = _, violin) : pointRadius; }; + hm.xMinObjectSize = function(_) { return arguments.length ? (xMinObjectSize = _, hm) : xMinObjectSize; }; /** - * Gets / sets the pointStrokeWidth - * (see {@link violin#pointStrokeWidth}) + * Gets / sets the maxObjectSize + * (see {@link heatmap#maxObjectSize}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default pointStrokeWidth = 2 + * by default maxObjectSize = 100 */ - violin.pointStrokeWidth = function(_) { return arguments.length ? (pointStrokeWidth = _, violin) : pointStrokeWidth; }; - - + hm.xMaxObjectSize = function(_) { return arguments.length ? (xMaxObjectSize = _, hm) : xMaxObjectSize; }; + /** + * Gets / sets the objectStrokeWidth + * (see {@link heatmap#objectStrokeWidth}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap + * @property + * by default objectStrokeWidth = 2 + */ + hm.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, hm) : objectStrokeWidth; }; /** * Gets / sets the backgroundFill - * (see {@link violin#backgroundFill}) + * (see {@link heatmap#backgroundFill}) * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin + * @returns {heatmap | string} + * @memberof heatmap * @property * by default backgroundFill = 'transparent' */ - violin.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, violin) : backgroundFill; }; + hm.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, hm) : backgroundFill; }; /** * Gets / sets the namespace - * (see {@link violin#namespace}) + * (see {@link heatmap#namespace}) * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin + * @returns {heatmap | string} + * @memberof heatmap * @property - * by default namespace = 'd3sm-violin' + * by default namespace = 'd3sm-heatmap' */ - violin.namespace = function(_) { return arguments.length ? (namespace = _, violin) : namespace; }; + hm.namespace = function(_) { return arguments.length ? (namespace = _, hm) : namespace; }; /** * Gets / sets the objectClass - * (see {@link violin#objectClass}) + * (see {@link heatmap#objectClass}) * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin + * @returns {heatmap | string} + * @memberof heatmap * @property * by default objectClass = 'tick-group' */ - violin.objectClass = function(_) { return arguments.length ? (objectClass = _, violin) : objectClass; }; - - + hm.objectClass = function(_) { return arguments.length ? (objectClass = _, hm) : objectClass; }; /** * Gets / sets the transitionDuration - * (see {@link violin#transitionDuration}) + * (see {@link heatmap#transitionDuration}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property * by default transitionDuration = 1000 */ - violin.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, violin) : transitionDuration; }; + hm.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, hm) : transitionDuration; }; /** * Gets / sets the easeFunc - * (see {@link violin#easeFunc}) + * (see {@link heatmap#easeFunc}) * @param {d3.ease} [_=none] - * @returns {violin | d3.ease} - * @memberof violin + * @returns {heatmap | d3.ease} + * @memberof heatmap * @property * by default easeFunc = d3.easeExp */ - violin.easeFunc = function(_) { return arguments.length ? (easeFunc = _, violin) : easeFunc; }; - + hm.easeFunc = function(_) { return arguments.length ? (easeFunc = _, hm) : easeFunc; }; /** - * Gets / sets the quartileKey - * (see {@link violin#quartileKey}) - * @param {string} [_=none] - * @returns {violin | string} - * @memberof violin + * Gets / sets the tooltip + * (see {@link heatmap#tooltip}) + * @param {tooltip} [_=none] + * @returns {heatmap | tooltip} + * @memberof heatmap * @property - * by default quartileKey = "utils.math.quartiles" + * by default tooltip = tooltip() */ - violin.quartileKey = function(_) { return arguments.length ? (quartileKey = _, violin) : quartileKey; }; + hm.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, hm) : tooltip$$1; }; + /** - * Gets / sets the quartileKeys - * (see {@link violin#quartileKeys}) - * @param {string[]} [_=none] - * @returns {violin | string[]} - * @memberof violin + * Gets / sets the colorFunction + * (see {@link heatmap#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {heatmap | colorFunction} + * @memberof heatmap * @property - * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] + * by default colorFunction = colorFunction() */ - violin.quartileKeys = function(_) { return arguments.length ? (quartileKeys = _, violin) : quartileKeys; }; - + hm.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, hm) : colorFunction$$1; }; /** - * Gets / sets the violinKeys - * (see {@link violin#violinKeys}) - * @param {string[]} [_=none] - * @returns {violin | string[]} - * @memberof violin + * Gets / sets the xSize + * (see {@link heatmap#xSize}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default violinKeys = undefined + * by default xSize = undefined */ - violin.violinKeys = function(_) { return arguments.length ? (violinKeys = _, violin) : violinKeys; }; + hm.xSize = function(_) { return arguments.length ? (xSize = _, hm) : xSize; }; /** - * Gets / sets the violinValues - * (see {@link violin#violinValues}) - * @param {Object[]} [_=none] - * @returns {violin | Object[]} - * @memberof violin + * Gets / sets the xSpacerSize + * (see {@link heatmap#xSpacerSize}) + * @param {number} [_=none] + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default violinValues = undefined + * by default xSpacerSize = undefined */ - violin.violinValues = function(_) { return arguments.length ? (violinValues = _, violin) : violinValues; }; - + hm.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, hm) : xSpacerSize; }; /** - * Gets / sets the objectSize - * (see {@link violin#objectSize}) + * Gets / sets the ySize + * (see {@link heatmap#ySize}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default objectSize = undefined + * by default ySize = undefined */ - violin.objectSize = function(_) { return arguments.length ? (objectSize = _, violin) : objectSize; }; + hm.ySize = function(_) { return arguments.length ? (ySize = _, hm) : ySize; }; /** - * Gets / sets the spacerSize - * (see {@link violin#spacerSize}) + * Gets / sets the ySpacerSize + * (see {@link heatmap#ySpacerSize}) * @param {number} [_=none] - * @returns {violin | number} - * @memberof violin + * @returns {heatmap | number} + * @memberof heatmap * @property - * by default spacerSize = undefined + * by default ySpacerSize = undefined */ - violin.spacerSize = function(_) { return arguments.length ? (spacerSize = _, violin) : spacerSize; }; - /** - * Gets / sets the tooltip - * (see {@link violin#tooltip}) - * @param {tooltip} [_=none] - * @returns {violin | tooltip} - * @memberof violin - * @property - * by default tooltip = tooltip() - */ - violin.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, violin) : tooltip$$1; }; - /** - * Gets / sets the pointsTooltip - * (see {@link violin#pointsTooltip}) - * @param {tooltip} [_=none] - * @returns {violin | tooltip} - * @memberof violin - * @property - * by default pointsTooltip = tooltip() - */ - violin.pointsTooltip = function(_) { return arguments.length ? (pointsTooltip = _, violin) : pointsTooltip; }; - - - + hm.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, hm) : ySpacerSize; }; + // hm.yKeySortingFunction = function(_) { return arguments.length ? (yKeySortingFunction = _, hm) : yKeySortingFunction; } + // hm.xKeySortingFunction = function(_) { return arguments.length ? (xKeySortingFunction = _, hm) : xKeySortingFunction; } - function violin () { - // for convenience in handling orientation specific values - var horizontalQ = (orient == 'horizontal') ? true : false; - // background cliping rectangle + function hm() { var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - // if grouping is undefined sort violinKeys by sortingFunction - var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping; - - // console.log(ordered) - - violinKeys = utils.arr.flatten(ordered); - + cellKeys = d3.keys(data); - var calcValues = neededViolinValues() - .horizontalQ(horizontalQ) - .quartileKeys(quartileKeys) - .violinPointsExtractor(violinPointsExtractor) - .violinPointValueExtractor(violinPointValueExtractor); + xValues = utils.arr.unique(cellKeys.map(xExtractor)); + yValues = utils.arr.unique(cellKeys.map(yExtractor)); + vValues = utils.arr.unique(cellKeys.map(vExtractor)); + cellKeys.sort(function(a, b){ return xKeySortingFunction(a, b) || yKeySortingFunction(a, b) }); + utils.con.log('heatmap', 'cells are sorted by', cellKeys); - // augment valus - violinKeys.map(function(vk, i){ calcValues(vk, data); }); - var numberOfObjects = violinKeys.length; + utils.con.log('heatmap', 'x and y keys are', {x: xValues, y:yValues}); - var min = [].concat(...violinKeys.map(function(k, i){return data[k].utils.math.quartiles[quartileKeys[0]]})); - var max = [].concat(...violinKeys.map(function(k, i){return data[k].utils.math.quartiles[quartileKeys[quartileKeys.length - 1]]})); - var extent = [Math.min(...min) - domainPadding, Math.max(...max) + domainPadding]; - // console.log(extent, violinValues, ordered) - // set the scale - scale.domain(extent).range(horizontalQ ? [0,spaceY] : [0, spaceX]); - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - // calculate spacer size if needed - spacerSize = utils.math.calculateWidthOfSpacer(ordered, space, objectSize, numberOfObjects, objectSpacer, overflowQ); - // make the nested groups - var spacerFunction = groupingSpacer() - .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) - .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc) - .namespace(namespace); + var xDim = xValues.length, yDim = yValues.length; - // move stuff - spacerFunction(container, ordered, 0); - // console.log(violinKeys, ordered, container.selectAll('g:not(.to-remove).'+objectClass).nodes()) - // for color function - var parentIndexArray = []; - container.selectAll('g:not(.to-remove).'+objectClass) - .each(function(d, i){if (utils.arr.hasQ(violinKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}}); + ySize = utils.math.calculateWidthOfObject(spaceY, yDim, yMinObjectSize, yMaxObjectSize, objectSpacer, overflowQ); + xSize = utils.math.calculateWidthOfObject(spaceX, xDim, xMinObjectSize, xMaxObjectSize, objectSpacer, overflowQ); + ySpacerSize = utils.math.calculateWidthOfSpacer(yValues, spaceY, ySize, yDim, objectSpacer, overflowQ); + xSpacerSize = utils.math.calculateWidthOfSpacer(xValues, spaceX, xSize, xDim, objectSpacer, overflowQ); + // console.table({ + // x:{ + // object: xSize, + // spacer: xSpacerSize, + // dim: xDim + // }, + // y:{ + // object: ySize, + // spacer: ySpacerSize, + // dim: yDim + // } + // + // }) + utils.con.log('heatmap', 'size of', {x: xSize, y: ySize}); - // update color function - colorFunction$$1 = colorFunction$$1.colorBy() == 'index' - ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) - : colorFunction$$1.dataExtent(extent); - /* violiin specific needs */ + var ySpacer = groupingSpacer() + .horizontalQ(false) + .moveby('category') + .numberOfObjects(yDim) + .objectClass(utils.str.hypenate(objectClass, 'row')) + .objectSize(ySize + ySpacerSize) + .spacerSize(0) + .transitionDuration(transitionDuration) + .easeFunc(easeFunc) + .namespace('row'); - var frequencyMax = Math.max(...[].concat(...violinKeys.map(function(k, i){return d3.max(data[k].frequencies)}))); - var vScale = d3.scaleLinear().domain([0, frequencyMax]).range([0, objectSize / 2]); + var xSpacer = groupingSpacer() + .horizontalQ(true) + .moveby('category') + .numberOfObjects(xDim) + .objectClass(objectClass) + .objectSize(xSize + xSpacerSize) + .spacerSize(0) + .transitionDuration(transitionDuration) + .easeFunc(easeFunc); - var lArea = d3.line() - .x(function(d, i){ return horizontalQ ? -vScale(d.x) : scale(d.x)}) - .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : -vScale(d.y)}) - .curve(d3.curveBasis); - var rArea = d3.line() - .x(function(d, i){ return horizontalQ ? vScale(d.x) : scale(d.x)}) - .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : vScale(d.y)}) - .curve(d3.curveBasis); + ySpacer(container, yValues, 0); + container.selectAll('g.'+utils.str.hypenate(objectClass, 'row')) + .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); + var cells = container.selectAll('g:not(.to-remove).'+objectClass); + if (cellKeys.length != yValues.length * xValues.length) { + var lookup = {}; + cellKeys.map(function(k, i){ + lookup[xExtractor(k)+'::'+yExtractor(k)] = k; + }); + var positionedCellKeys = []; + for (var i = 0; i < yValues.length; i++) { + for (var j = 0; j < xValues.length; j++) { + var lookupValue = lookup[xValues[j]+"::"+yValues[i]]; + if (lookupValue == undefined) { + positionedCellKeys.push(undefined); + } else { + positionedCellKeys.push(lookupValue); + } + } + } - container.selectAll('g:not(.to-remove).'+objectClass).each(function(key, i){ - var t = d3.select(this), - currentData = data[key]; - // needed because bug in selecting .to-remove - if (!utils.arr.hasQ(violinKeys, key)) {return} - var - i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), - fillColor = colorFunction$$1(key, currentData, i, 'fill'), // prevent duplicate computation - strokeColor = colorFunction$$1(key, currentData, i, 'stroke'), - area = utils.sel.safeSelect(t, 'g', 'area'), - la = utils.sel.safeSelect(area, 'path', 'left'), - ra = utils.sel.safeSelect(area, 'path', 'right'), - quarts = utils.sel.safeSelect(t, 'g', 'quarts'), - lq3 = utils.sel.safeSelect(quarts, 'line', 'q3'), - lq1 = utils.sel.safeSelect(quarts, 'line', 'q1'), - q3 = currentData.utils.math.quartiles[quartileKeys[3]], - q2 = currentData.utils.math.quartiles[quartileKeys[2]], - q1 = currentData.utils.math.quartiles[quartileKeys[1]]; + cells.data(positionedCellKeys); - t.attr('transform', horizontalQ ? 'translate('+objectSize / 2+',0)' : 'translate(0,'+objectSize / 2+')' ); - // draw curve - la.transition().duration(transitionDuration).attr('d', function(dd, ii){ return lArea(currentData.contour)}) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', objectStrokeWidth); + // maybe breaks this + // !!!!!! IMPORTANT NOTE TODO LOOK HERE BUG + cellKeys = positionedCellKeys; + } else { + cells.data(cellKeys); + } - ra.transition().duration(transitionDuration).attr('d', function(dd, ii){ return rArea(currentData.contour)}) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', objectStrokeWidth); - area.node().addEventListener('mouseover', function(dd, ii){ - container.selectAll('g.'+objectClass).style('opacity', 0.2); - t.style('opacity', 1); - la.attr('stroke-width',objectStrokeWidth*2); - ra.attr('stroke-width',objectStrokeWidth*2); - }); - area.node().addEventListener('mouseout', function(dd, ii){ - container.selectAll('g.'+objectClass).style('opacity', 1); - la.attr('stroke-width',objectStrokeWidth); - ra.attr('stroke-width',objectStrokeWidth); - }); - if (pointsQ) { - var ptsContainer = utils.sel.safeSelect(t, 'g', 'points'); - var pts = ptsContainer.selectAll('.point').data(currentData.pointKeys); - pts.on('mouseover', null); + var parentIndexArray = []; + cells.each(function(d, i){ parentIndexArray.push(Number(d3.select(this).attr('parent-index'))); }); + colorFunction$$1 = colorFunction$$1.colorBy() == 'index' + ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) + : colorFunction$$1.dataExtent([0, Math.max(...vValues)]); - var ptsExit = pts.exit().transition().ease(easeFunc).duration(transitionDuration) - .attr('r', 0) - .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) - .attr('cx', horizontalQ ? vScale(0) : scale(q2)).remove(); + cells.each(function(key, i) { + utils.con.log('heatmap', 'each cell', {key: key, index: i, node: d3.select(this).node()}); - var ptsEnter = pts.enter().append('circle').attr('class', 'point').attr('r', 0) - .attr('cx', horizontalQ ? 0 : scale(q2)) - .attr('cy', horizontalQ ? scale(q2) : 0); + var t = d3.select(this); + if (key == undefined) {return} + var currentData = data[key], + value = vExtractor(key, i), + i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), + fillColor = colorFunction$$1(key, value, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(key, value, i, 'stroke'); - pts = pts.merge(ptsEnter); + var c = utils.sel.safeSelect(t, 'rect', utils.str.hypenate(objectClass,'rect')); + c.attr('width', xSize + xSpacerSize - objectStrokeWidth) + .attr('height', ySize + ySpacerSize - objectStrokeWidth) + .attr('fill', fillColor) + .attr('x', objectStrokeWidth/2) + .attr('y', objectStrokeWidth/2) + .attr('stroke', "#000") + .attr('stroke-width', objectStrokeWidth); - // console.log(pointsTooltip.header()) + }); - var pTTips = tooltip().selection(pts).data(violinPointsExtractor(key, currentData)) - .header(pointsTooltip.header()) - .keys(pointsTooltip.keys()) - .values(pointsTooltip.values()); + tooltip$$1.selection(cells.selectAll('rect.'+utils.str.hypenate(objectClass, 'rect'))) + .data(data); + // .keys(['r', 'v']) + // .header(function(d, i){return utils.str.hypenate(data[d][xKey], data[d][yKey]) }) - pTTips(); - - var pMin = d3.min(currentData.pointValues), pMax = d3.max(currentData.pointValues); - - - - pts.transition().duration(transitionDuration).ease(easeFunc).attr('r', pointRadius) - .attr('cy', function(pointKey, ii){ - var dd = currentData.pointValues[ii]; - if (horizontalQ) { return scale(extent[1]) - scale(dd) } - var j = utils.arr.whichBin(currentData.binned, dd); - var r = Math.random(); - var n = vScale(r * currentData.frequencies[j] * 0.5); - var k = Math.random() > 0.5 ? n : -n; - return k - }) - .attr('cx', function(pointKey, ii){ - var dd = currentData.pointValues[ii]; - if (horizontalQ) { - var j = utils.arr.whichBin(currentData.binned, dd); - var r = Math.random(); - var n = vScale(r * currentData.frequencies[j] * 0.5); - var k = Math.random() > 0.5 ? n : -n; - return k - } - return scale(dd) - }) - .attr('stroke', function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'stroke', strokeColor, pMin, pMax) }) - .attr('fill' , function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'fill' , strokeColor, pMin, pMax) }) - .attr('stroke-width', pointStrokeWidth); - - ptsContainer.selectAll('circle.point').on('mouseover', function(dd, ii){ - container.selectAll('g.'+objectClass).style('opacity', 0.2); - t.style('opacity', 1); - la.attr('stroke-width',objectStrokeWidth*2); - ra.attr('stroke-width',objectStrokeWidth*2); - - container.selectAll('.point').style('opacity', 0.2); - d3.select(this).style('opacity', 1).attr('r', pointRadius * 2).attr('stroke-width',pointStrokeWidth*2); - }); - ptsContainer.selectAll('circle.point').on('mouseout', function(dd, ii){ - var e = document.createEvent('SVGEvents'); - e.initEvent('mouseout',true,true); - area.node().dispatchEvent(e); - - container.selectAll('.point').style('opacity', 1); - d3.select(this).attr('stroke-width', pointStrokeWidth).attr('r', pointRadius); - }); - } - else { - cV.selectAll('.point') - .transition().duration(transitionDuration).ease(easeFunc) - .attr('r', 0) - .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) - .attr('cx', horizontalQ ? vScale(0) : scale(q2)) - .remove(); - } - - - }); - - - tooltip$$1.selection(container.selectAll('g:not(.to-remove).'+objectClass + ' .area')); - if (tooltip$$1.data() == undefined) {tooltip$$1.data(data);} - tooltip$$1(); - if (tooltip$$1.values() == undefined) { - tooltip$$1.values([ - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] } - ]); - - } - - } - - return violin - } + tooltip$$1(); + } + return hm; + } + /******************************************************************************* + ** ** + ** ** + ** VIOLIN ** + ** ** + ** ** + *******************************************************************************/ /** - * Produces the function which manipulates the violin data to have values needed - * for rendering the violins as svg. - * @returns {function} calculateViolinValues - * @namespace neededViolinValues - */ - function neededViolinValues() { - var + * Creates a violin + * + * {@link https://sumneuron.gitlab.io/d3sm/demos/basic-violins/index.html Demo} + * @constructor violin + * @param {d3.selection} selection + * @namespace violin + * @returns {function} violin + */ + function violin( selection ) { + var /** - * Whether or not the orientation of the violins are horizontal + * Data to plot. Assumed to be a object, where each key corresponds to a violin + * (see {@link violin#data}) + * @param {Object} [data=undefined] + * @memberof violin# + * @property + */ + data, + /** + * Which direction to render the bars in * (see {@link violin#orient}) - * @param {Object} [horizontalQ=true] - * @memberof neededViolinValues# + * @param {number} [orient='horizontal'] + * @memberof violin# * @property */ - horizontalQ = true, + orient='horizontal', /** - * Keys to be put into the utils.math.quartiles if they need to be calculated. - * (see {@link violin#quartileKeys}) - * @param {Object} [quartileKeys=['Q0', 'Q1', 'Q2', 'Q3', 'Q4']] - * @memberof neededViolinValues# + * Amount of horizontal space (in pixels) avaible to render the violin in + * (see {@link violin#spaceX}) + * @param {number} [spaceX=undefined] + * @memberof violin# * @property */ - quartileKeys = ['Q0', 'Q1', 'Q2', 'Q3', 'Q4'], + spaceX, /** - * Function which given the key of the violin and that key's associated value - * returns the object consiting of the points of the violin - * (see {@link violin#violinPointsExtractor}) - * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] - * @memberof neededViolinValues# + * Amount of vertical space (in pixels) avaible to render the violin in + * (see {@link violin.spaceY}) + * @param {number} [spaceY=undefined] + * @memberof violin# * @property */ - violinPointsExtractor, + spaceY, /** - * Function which given the key of the current point and the object of points for the - * violin, returns the numerical value of the point - * (see {@link violin#violinPointValueExtractor}) - * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] - * @memberof neededViolinValues# + * Whether or not to allow violin to render elements pass the main spatial dimension + * given the orientation (see {@link violin#orient}), where {@link violin#orient}="horizontal" + * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" + * the main dimension is {@link violin#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof violin# * @property */ - violinPointValueExtractor; - - + overflowQ = true, /** - * Gets / sets the horizontalQ - * (see {@link violin#orient}) - * @param {boolean} [_=none] - * @returns {calculateViolinValues | boolean} - * @memberof calculateViolinValues - * @property - * - * by default horizontalQ = true - */ - calculateViolinValues.horizontalQ = function(_) { return arguments.length ? (horizontalQ=_, calculateViolinValues) : horizontalQ }; + * Whether or not to display points inside the points + * @param {boolean} [pointsQ=false] + * @memberof violin# + * @property + */ + pointsQ = true, /** - * Gets / sets the quartileKeys - * (see {@link violin#quartileKeys}) - * @param {string[]} [_=none] - * @returns {calculateViolinValues | string[]} - * @memberof calculateViolinValues - * @property - * - * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] - */ - calculateViolinValues.quartileKeys = function(_) { return arguments.length ? (quartileKeys=_, calculateViolinValues) : quartileKeys }; + * An array - putatively of other arrays - depicting how bars should be arranged + * @param {Array[]} [grouping=undefined] + * @memberof violin# + * @property + */ + grouping, /** - * Gets / sets the violinPointsExtractor - * (see {@link violin#violinPointsExtractor}) - * @param {function} [_=none] - * @returns {calculateViolinValues | function} - * @memberof calculateViolinValues - * @property - * - * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } - */ - calculateViolinValues.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor=_, calculateViolinValues) : violinPointsExtractor }; + * How to get the value of the violin + * @param {function} [valueExtractor=function(key, index) { return data[key] }] + * @memberof violin# + * @property + */ + valueExtractor = function(key, index) {return data[key] }, /** - * Gets / sets the violinPointValueExtractor - * (see {@link violin#violinPointValueExtractor}) - * @param {function} [_=none] - * @returns {calculateViolinValues | function} - * @memberof calculateViolinValues - * @property - * - * by default violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value } - */ - calculateViolinValues.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor=_, calculateViolinValues) : violinPointValueExtractor }; - + * How to sort the bars - if {@link violin#grouping} is not provided. + * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] + * @memberof violin# + * @property + */ + sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, /** - * The function produced by neededViolinValues. - * - * Adds the data need to render the violin as an svg - * @param {string} violinKey the key of the current violin - * @param {object} data the object consisting of violinKey - values (violin data) pairs - * @returns {none} this function manipulates the passed data object adding the following keys - * - * data[violinKey].binned // the binned values of the points - * - * data[violinKey].frequencies // the list consisting of the length of each bin - * - * data[violinKey].contour // the points depicting the contour of 1/2 of the violin - * - * data[violinKey].utils.math.quartiles // the utils.math.quartiles of the points' values - * - * data[violinKey].pointKeys // the keys associated with the points - * - * data[violinKey].pointValues // the numerical values of the points - * - * @memberof neededViolinValues# + * The scale for which violin values should be transformed by + * @param {d3.scale} [scale=d3.scaleLinear] + * @memberof violin# * @property */ - function calculateViolinValues(violinKey, data) { - // data for the current violin - var violinData = data[violinKey]; - // the object of points - var violinPoints = violinPointsExtractor(violinKey, violinData); - // - var violinPointsKeys = d3.keys(violinPoints); - // the numerical values of those points - var violinPointsValues = violinPointsKeys.map(function(pk, i){return violinPointValueExtractor(pk, violinPoints)}); - - // utils.math.quartiles of those points - var pointQuartiles = utils.math.quartiles(violinPointsValues, quartileKeys); + scale = d3.scaleLinear(), + /** + * The padding for the domain of the scale (see {@link violin#scale}) + * @param {number} [domainPadding=0.5] + * @memberof violin# + * @property + */ + domainPadding = 0.5, + /** + * Default space for the spacer (percentage) of main dimension given the orientation + * (see {@link violin#orient}), where {@link violin#orient}="horizontal" + * the main dimension is {@link violin#spaceX} and where {@link violin#orient}="vertical" + * the main dimension is {@link violin#spaceY} between bars + * @param {number} [objectSpacer=0.05] + * @memberof violin# + * @property + */ + objectSpacer = 0.05, + /** + * The minimum size that an object can be + * @param {number} [minObjectSize=50] + * @memberof violin# + * @property + */ + minObjectSize = 50, + /** + * The maximum size that an object can be + * @param {number} [maxObjectSize=100] + * @memberof violin# + * @property + */ + maxObjectSize = 100, - // binned points - var binned = d3.histogram()(violinPointsValues); - // length of bins - var frequencies = binned.map(bin=>bin.length); - // min and max countour points for nice drawings - var minContourPoint = horizontalQ ? {x: 0, y: d3.min(violinPointsValues)} : {x: d3.min(violinPointsValues), y: 0}; - var maxContourPoint = horizontalQ ? {x: 0, y: d3.max(violinPointsValues)} : {x: d3.max(violinPointsValues), y: 0}; - var violinContourPoints = binned.map(function(bin, i) { - return horizontalQ - ? {y: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), x: frequencies[i]} - : {x: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), y: frequencies[i]} - }); - // points along which to draw the violin shpe - violinContourPoints = [minContourPoint].concat(violinContourPoints).concat([maxContourPoint]); - - // set data - violinData.binned = binned; - violinData.frequencies = frequencies; - violinData.contour = violinContourPoints; - violinData.utils.math.quartiles = pointQuartiles; - violinData.pointKeys = violinPointsKeys; - violinData.pointValues = violinPointsValues; - } + /** + * The stroke width of the bars + * @param {number} [barStrokeWidth=2] + * @memberof violin# + * @property + */ + objectStrokeWidth = 2, + /** + * Instance of ColorFunction + * @param {function} [colorFunction = colorFunction()] + * @memberof violin# + * @property + */ + colorFunction$$1 = colorFunction(), + /** + * Instance of ColorFunction modified by a scale for the points + * @param {function} [pointColorFunc = colorFunction()] + * @memberof violin# + * @property + */ + pointColorFunc = function (d, type, base, min, max) { + var minMaxHexScale = d3.scaleLinear().domain([min, max]).range([-0.25, 0.05]); + var scaledColor = utils.color.modifyHexidecimalColorLuminance(base.replace('#', ''), minMaxHexScale(d)); + var mod = type == "stroke" ? 0 : 0.25; + return utils.color.modifyHexidecimalColorLuminance(scaledColor.replace('#', ''), mod) + }, - return calculateViolinValues - } + /** + * The radius of a point + * @param {number} [pointRadius=3] + * @memberof violin# + * @property + */ + pointRadius = 3, + /** + * The stroke width of the oints + * @param {number} [pointStrokeWidth=2] + * @memberof violin# + * @property + */ + pointStrokeWidth = 2, - function upset ( selection ) { - var - data, - orient='horizontal', - spaceX, - spaceY, - overflowQ=false, - minObjectSize=20, - maxObjectSize=50, - circleStrokeWidth=2, - // colorFunction= + /** + * Color of the background + * @param {string} [backgroundFill="transparent"] + * @memberof violin# + * @property + */ backgroundFill = 'transparent', - namespace='d3sm-upset', - objectClass = 'upset', - + /** + * Namespace for all items made by this instance of violin + * @param {string} [namespace="d3sm-violin"] + * @memberof violin# + * @property + */ + namespace = 'd3sm-violin', + /** + * Class name for violin container ( element) + * @param {string} [objectClass="violin"] + * @memberof violin# + * @property + */ + objectClass = 'violin', + /** + * Duration of all transitions of this element + * @param {number} [transitionDuration=1000] + * @memberof violin# + * @property + */ transitionDuration = 1000, + /** + * Easing function for transitions + * @param {d3.ease} [easeFunc=d3.easeExp] + * @memberof violin# + * @property + */ easeFunc = d3.easeExp, - setKey = "set", - intersectionKey = "intersection", - elementsKey = "elements", - - setExtractor = function(key, i) {return data[key][setKey]}, - intersectionExtractor = function(key, i) {return data[key][intersectionKey]}, - elementExtractor = function(key, i) {return data[key][elementsKey]}, - - cellKeys, - setValues, - intersectionValues, - xObjectSpacer = 0.05, - yObjectSpacer = 0.05, - radius, - - // listDelim = ';' - - yObjectSize, - ySpacerSize, - xObjectSize, - xSpacerSize, - - setKeySortingFunction = function(a, b) { return setValues.indexOf(setExtractor(a)) - setValues.indexOf(setExtractor(b)) }, - intersectionKeySortingFunction = function(a, b) { return intersectionValues.indexOf(intersectionExtractor(a)) - intersectionValues.indexOf(intersectionExtractor(b)) }; - - - upset.selection = function(_) { return arguments.length ? (selection = _, upset) : selection; }; - upset.data = function(_) { return arguments.length ? (data = _, upset) : data; }; - upset.orient = function(_) { return arguments.length ? (orient = _, upset) : orient; }; - upset.spaceX = function(_) { return arguments.length ? (spaceX = _, upset) : spaceX; }; - upset.spaceY = function(_) { return arguments.length ? (spaceY = _, upset) : spaceY; }; - upset.overflowQ = function(_) { return arguments.length ? (overflowQ = _, upset) : overflowQ; }; - upset.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, upset) : minObjectSize; }; - upset.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, upset) : maxObjectSize; }; - upset.circleStrokeWidth = function(_) { return arguments.length ? (circleStrokeWidth = _, upset) : circleStrokeWidth; }; - upset.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, upset) : backgroundFill; }; - upset.namespace = function(_) { return arguments.length ? (namespace = _, upset) : namespace; }; - upset.objectClass = function(_) { return arguments.length ? (objectClass = _, upset) : objectClass; }; - upset.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, upset) : transitionDuration; }; - upset.easeFunc = function(_) { return arguments.length ? (easeFunc = _, upset) : easeFunc; }; - upset.cellKeys = function(_) { return arguments.length ? (cellKeys = _, upset) : cellKeys; }; - upset.setValues = function(_) { return arguments.length ? (setValues = _, upset) : setValues; }; - upset.intersectionValues = function(_) { return arguments.length ? (intersectionValues = _, upset) : intersectionValues; }; - upset.xObjectSpacer = function(_) { return arguments.length ? (xObjectSpacer = _, upset) : xObjectSpacer; }; - upset.yObjectSpacer = function(_) { return arguments.length ? (yObjectSpacer = _, upset) : yObjectSpacer; }; - upset.radius = function(_) { return arguments.length ? (radius = _, upset) : radius; }; - upset.setExtractor = function(_) { return arguments.length ? (setExtractor = _, upset) : setExtractor; }; - upset.intersectionExtractor = function(_) { return arguments.length ? (intersectionExtractor = _, upset) : intersectionExtractor; }; - upset.elementExtractor = function(_) { return arguments.length ? (elementExtractor = _, upset) : elementExtractor; }; - upset.setKeySortingFunction = function(_) { return arguments.length ? (setKeySortingFunction = _, upset) : setKeySortingFunction; }; - upset.intersectionKeySortingFunction = function(_) { return arguments.length ? (intersectionKeySortingFunction = _, upset) : intersectionKeySortingFunction; }; - - upset.yObjectSize = function(_) { return arguments.length ? (yObjectSize = _, upset) : yObjectSize; }; - upset.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, upset) : ySpacerSize; }; - upset.xObjectSize = function(_) { return arguments.length ? (xObjectSize = _, upset) : xObjectSize; }; - upset.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, upset) : xSpacerSize; }; - - function upset() { - // for convenience in handling orientation specific values - var horizontalQ = (orient == 'horizontal') ? true : false; - var verticalQ = !horizontalQ; + /** + * The keys corresponding to each quartile + * @param {string[]} [quartileKeys=["Q0", "Q1", "Q2", "Q3", "Q4"]] + * @memberof violin# + * @property + */ + quartileKeys = ["Q0", "Q1", "Q2", "Q3", "Q4"], - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + /** + * The keys of the bars + * @param {string[]} [violinKeys=undefined] + * @memberof violin# + * @property + */ + violinKeys, + /** + * The values of the bars + * @param {number[]} [violinValues=undefined] + * @memberof violin# + * @property + */ + violinValues, + /** + * The objectSize (actual width) used by the bars + * @param {number} [objectSize=undefined] + * @memberof violin# + * @property + */ + objectSize, + /** + * The spacerSize (actual width) used by the spacers between the bars + * @param {number} [spacerSize=undefined] + * @memberof violin# + * @property + */ + spacerSize, + /** + * Instance of Tooltip + * @param {function} [tooltip=tooltip()] + * @memberof violin# + * @property + */ + tooltip$$1 = tooltip().keys([quartileKeys[4], quartileKeys[3], quartileKeys[2], quartileKeys[1], quartileKeys[0]]), + pointsTooltip = tooltip(), + // pointKeyExtractor = function(violinKey, violinData, violinValues) {return d3.keys(violinValues[violinKey].values)}, + // pointValueExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]}, - cellKeys = d3.keys(data); - setValues = utils$1.arr.unique(cellKeys.map(setExtractor)).sort(); - intersectionValues = utils$1.arr.unique(cellKeys.map(intersectionExtractor)).sort().sort(function(a, b){ - return a.split(';').length - b.split(';').length - }); - if (!horizontalQ) { - cellKeys.sort(function(a, b){ return setKeySortingFunction(a, b) || intersectionKeySortingFunction(a, b) }); - } else { - cellKeys.sort(function(a, b){ return intersectionKeySortingFunction(a, b) || setKeySortingFunction(a, b) }); - } + /** + * Function which given the key of the violin and that key's associated value + * returns the object consiting of the points of the violin + * (see {@link violin#violinPointsExtractor}) + * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] + * @memberof violin# + * @property + */ + violinPointsExtractor = function (violinKey, violinData) {return violinData.points }, + /** + * Function which given the key of the current point and the object of points for the + * violin, returns the numerical value of the point + * (see {@link violin#violinPointValueExtractor}) + * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] + * @memberof violin# + * @property + */ + violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }; + /** + * Gets or sets the violinPointsExtractor + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin + * @property + * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } + */ + violin.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor = _, violin) : violinPointsExtractor; }; + /** + * Gets or sets the violinPointValueExtractor + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin + * @property + * by default violinPointsExtractor = function(pointKey, violinKey, violinData, violinValues) {return violinValues[violinKey].values[pointKey]} + */ + violin.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor = _, violin) : violinPointValueExtractor; }; - var - xValues = horizontalQ ? intersectionValues : setValues, - yValues = horizontalQ ? setValues : intersectionValues, - xDim = horizontalQ ? xValues.length : yValues.length, - yDim = horizontalQ ? yValues.length : xValues.length; - - // console.utils.con.log(xValues, yValues) - - - xObjectSize = utils$1.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, xObjectSpacer, overflowQ); - yObjectSize = utils$1.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, yObjectSpacer, overflowQ); - xSpacerSize = utils$1.math.calculateWidthOfSpacer(xValues, spaceX, xObjectSize, xDim, xObjectSpacer, overflowQ); - ySpacerSize = utils$1.math.calculateWidthOfSpacer(yValues, spaceY, yObjectSize, yDim, yObjectSpacer, overflowQ); - - var ySpacer = groupingSpacer() - .horizontalQ(false) - .moveby('category').numberOfObjects(yDim) - .objectSize(yObjectSize).spacerSize(ySpacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc); + /** + * Gets or sets the selection in which items are manipulated + * @param {d3.selection} [_=none] + * @returns {violin | d3.selection} + * @memberof violin + * @property + * by default selection = selection + */ + violin.selection = function(_) { return arguments.length ? (selection = _, violin) : selection; }; + /** + * Gets or sets the data + * (see {@link violin#data}) + * @param {number} [_=none] + * @returns {violin | object} + * @memberof violin + * @property + */ + violin.data = function(_) { return arguments.length ? (data = _, violin) : data; }; + /** + * Gets or sets the orient of the boxes + * (see {@link violin#orient}) + * @param {number} [_=none] + * @returns {violin | object} + * @memberof violin + * @property + */ + violin.orient = function(_) { return arguments.length ? (orient = _, violin) : orient; }; + /** + * Gets or sets the amount of horizontal space in which items are manipulated + * (see {@link violin#spaceX}) + * @param {number} [_=none] should be a number > 0 + * @returns {violin | number} + * @memberof violin + * @property + * by default spaceX = undefined + */ + violin.spaceX = function(_) { return arguments.length ? (spaceX = _, violin) : spaceX; }; + /** + * Gets or sets the amount of vertical space in which items are manipulated + * (see {@link violin#spaceY}) + * @param {number} [_=none] should be a number > 0 + * @returns {violin | number} + * @memberof violin + * @property + * by default spaceY = undefined + */ + violin.spaceY = function(_) { return arguments.length ? (spaceY = _, violin) : spaceY; }; - var xSpacer = groupingSpacer() - .horizontalQ(true) - .moveby('category').numberOfObjects(xDim) - .objectClass(objectClass) - .objectSize(xObjectSize).spacerSize(xSpacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc); + /** + * Gets / sets whether or not violin is allowed to go beyond specified dimensions + * (see {@link violin#overflowQ}) + * @param {boolean} [_=none] + * @returns {violin | boolean} + * @memberof violin + * @property + * by default overflowQ = false + */ + violin.overflowQ = function(_) { return arguments.length ? (overflowQ = _, violin) : overflowQ; }; + /** + * Gets / sets whether or not to plot points with the violins + * (see {@link violin#pointsQ}) + * @param {boolean} [_=none] + * @returns {violin | boolean} + * @memberof violin + * @property + * by default pointsQ = false + */ + violin.pointsQ = function(_) { return arguments.length ? (pointsQ = _, violin) : pointsQ; }; - if (verticalQ) { - xSpacer.objectClass(objectClass); - ySpacer.namespace('across').objectClass(utils$1.str.hypenate(objectClass, 'across')); + /** + * Gets / sets the grouping of the boxes + * (see {@link violin#grouping}) + * @param {Array[]} [_=none] + * @returns {violin | Array[]} + * @memberof violin + * @property + * by default grouping = undefined + */ + violin.grouping = function(_) { return arguments.length ? (grouping = _, violin) : grouping; }; + /** + * Gets / sets the valueExtractor + * (see {@link violin#valueExtractor}) + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin + * @property + */ + violin.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, violin) : valueExtractor; }; + /** + * Gets / sets the sortingFunction + * (see {@link violin#sortingFunction}) + * @param {function} [_=none] + * @returns {violin | function} + * @memberof violin + * @property + */ + violin.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, violin) : sortingFunction; }; - ySpacer(container, yValues, 0); - container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'across')) - .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); - } else { - xSpacer.namespace('across').objectClass(utils$1.str.hypenate(objectClass, 'across')); - ySpacer.objectClass(objectClass); + /** + * Gets / sets the scale for which the violin values should be transformed by + * (see {@link violin#scale}) + * @param {d3.scale} [_=none] + * @returns {violin | d3.scale} + * @memberof violin + * @property + * by default scale = d3.scaleLinear() + */ + violin.scale = function(_) { return arguments.length ? (scale = _, violin) : scale; }; + /** + * Gets / sets the padding for the domain of the scale + * (see {@link violin#domainPadding}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default domainPadding = 0.5 + */ + violin.domainPadding = function(_) { return arguments.length ? (domainPadding = _, violin) : domainPadding; }; - xSpacer(container, xValues, 0); - container.selectAll('g.'+utils$1.str.hypenate(objectClass, 'across')) - .each(function(d, i){ ySpacer(d3.select(this), yValues, 0); }); - } + /** + * Gets / sets objectSpacer + * (see {@link violin#objectSpacer}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default objectSpacer = 0.05 + */ + violin.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, violin) : objectSpacer; }; + /** + * Gets / sets the minObjectSize + * (see {@link violin#minObjectSize}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default minObjectSize = 15 + */ + violin.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, violin) : minObjectSize; }; + /** + * Gets / sets the maxObjectSize + * (see {@link violin#maxObjectSize}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default maxObjectSize = 50 + */ + violin.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, violin) : maxObjectSize; }; - var cells = container.selectAll('g:not(.to-remove).'+objectClass); - var lookup = {}; - cellKeys.map(function(k, i){ - lookup[setExtractor(k)+'::'+intersectionExtractor(k)] = k; - }); + /** + * Gets / sets the objectStrokeWidth + * (see {@link violin#objectStrokeWidth}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default objectStrokeWidth = 2 + */ + violin.objectStrokeWidth = function(_) { return arguments.length ? (objectStrokeWidth = _, violin) : objectStrokeWidth; }; - // var positionedCellKeys = [] - // for (var i = 0; i < setValues.length; i++) { - // for (var j = 0; j < intersectionValues.length; j++) { - // var lookupValue = lookup[setValues[j]+"::"+intersectionValues[i]] - // if (lookupValue == undefined) { - // positionedCellKeys.push(undefined) - // } else { - // positionedCellKeys.push(lookupValue) - // } - // console.utils.con.log(i, j, lookupValue) - // } - // } - // - // // console.utils.con.log(positionedCellKeys) - // - // cells.data(positionedCellKeys); + /** + * Gets / sets the colorFunction + * (see {@link violin#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {violin | colorFunction} + * @memberof violin + * @property + * by default colorFunction = colorFunction() + */ + violin.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, violin) : colorFunction$$1; }; + /** + * Gets / sets the colorFunction + * (see {@link violin#colorFunction}) + * @param {colorFunction} [_=none] + * @returns {violin | colorFunction} + * @memberof violin + * @property + * by default colorFunction = colorFunction() + */ + violin.pointColorFunc = function(_) { return arguments.length ? (pointColorFunc = _, violin) : pointColorFunc; }; - cells.data(cellKeys); - cells.each(function(key, i) { - var t = d3.select(this); - if (key == undefined) {return } - var - currentData = data[key]; - // console.utils.con.log(key, currentData) - var - set = setExtractor(key, i), - intersection = intersectionExtractor(key, i); + /** + * Gets / sets the pointRadius + * (see {@link violin#pointRadius}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default pointRadius = 2 + */ + violin.pointRadius = function(_) { return arguments.length ? (pointRadius = _, violin) : pointRadius; }; + /** + * Gets / sets the pointStrokeWidth + * (see {@link violin#pointStrokeWidth}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default pointStrokeWidth = 2 + */ + violin.pointStrokeWidth = function(_) { return arguments.length ? (pointStrokeWidth = _, violin) : pointStrokeWidth; }; - // console.utils.con.log(set, intersection) - t.classed(intersection, true); - t.classed(set, true); + /** + * Gets / sets the backgroundFill + * (see {@link violin#backgroundFill}) + * @param {string} [_=none] + * @returns {violin | string} + * @memberof violin + * @property + * by default backgroundFill = 'transparent' + */ + violin.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, violin) : backgroundFill; }; + /** + * Gets / sets the namespace + * (see {@link violin#namespace}) + * @param {string} [_=none] + * @returns {violin | string} + * @memberof violin + * @property + * by default namespace = 'd3sm-violin' + */ + violin.namespace = function(_) { return arguments.length ? (namespace = _, violin) : namespace; }; + /** + * Gets / sets the objectClass + * (see {@link violin#objectClass}) + * @param {string} [_=none] + * @returns {violin | string} + * @memberof violin + * @property + * by default objectClass = 'tick-group' + */ + violin.objectClass = function(_) { return arguments.length ? (objectClass = _, violin) : objectClass; }; - var c = utils$1.sel.safeSelect(t, 'circle', utils$1.str.hypenate(objectClass,'circle')); - c.attr('cx', xObjectSize / 2) - .attr('cy', yObjectSize / 2 ) - .attr('r', radius == undefined ? Math.min(xObjectSize, yObjectSize) / 2 : radius) - .attr('fill', intersection.includes(set) ? "black": 'rgb(233,233,233)') - .attr('stroke', "black") - .attr("in-intersection", intersection.includes(set)); - }); + /** + * Gets / sets the transitionDuration + * (see {@link violin#transitionDuration}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default transitionDuration = 1000 + */ + violin.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, violin) : transitionDuration; }; + /** + * Gets / sets the easeFunc + * (see {@link violin#easeFunc}) + * @param {d3.ease} [_=none] + * @returns {violin | d3.ease} + * @memberof violin + * @property + * by default easeFunc = d3.easeExp + */ + violin.easeFunc = function(_) { return arguments.length ? (easeFunc = _, violin) : easeFunc; }; - } + /** + * Gets / sets the quartileKey + * (see {@link violin#quartileKey}) + * @param {string} [_=none] + * @returns {violin | string} + * @memberof violin + * @property + * by default quartileKey = "utils.math.quartiles" + */ + violin.quartileKey = function(_) { return arguments.length ? (quartileKey = _, violin) : quartileKey; }; + /** + * Gets / sets the quartileKeys + * (see {@link violin#quartileKeys}) + * @param {string[]} [_=none] + * @returns {violin | string[]} + * @memberof violin + * @property + * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] + */ + violin.quartileKeys = function(_) { return arguments.length ? (quartileKeys = _, violin) : quartileKeys; }; - function intersectionTotals() { - var totals = {}; - // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) - intersectionValues.map(function(k, i){ totals[k] = {'total': 0}; }); - cellKeys.map(function(k, i){ - var e = elementExtractor(k, i); - if (totals[intersectionExtractor(k, i)]['total'] == 0) { - if (Array.isArray(e)) { - totals[intersectionExtractor(k, i)]['total']+= e.length; - totals[intersectionExtractor(k, i)]['values'] = e; - } else { - totals[intersectionExtractor(k, i)]['total']+= e; - } + /** + * Gets / sets the violinKeys + * (see {@link violin#violinKeys}) + * @param {string[]} [_=none] + * @returns {violin | string[]} + * @memberof violin + * @property + * by default violinKeys = undefined + */ + violin.violinKeys = function(_) { return arguments.length ? (violinKeys = _, violin) : violinKeys; }; + /** + * Gets / sets the violinValues + * (see {@link violin#violinValues}) + * @param {Object[]} [_=none] + * @returns {violin | Object[]} + * @memberof violin + * @property + * by default violinValues = undefined + */ + violin.violinValues = function(_) { return arguments.length ? (violinValues = _, violin) : violinValues; }; - } - }); - return totals - } + /** + * Gets / sets the objectSize + * (see {@link violin#objectSize}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default objectSize = undefined + */ + violin.objectSize = function(_) { return arguments.length ? (objectSize = _, violin) : objectSize; }; + /** + * Gets / sets the spacerSize + * (see {@link violin#spacerSize}) + * @param {number} [_=none] + * @returns {violin | number} + * @memberof violin + * @property + * by default spacerSize = undefined + */ + violin.spacerSize = function(_) { return arguments.length ? (spacerSize = _, violin) : spacerSize; }; + /** + * Gets / sets the tooltip + * (see {@link violin#tooltip}) + * @param {tooltip} [_=none] + * @returns {violin | tooltip} + * @memberof violin + * @property + * by default tooltip = tooltip() + */ + violin.tooltip = function(_) { return arguments.length ? (tooltip$$1 = _, violin) : tooltip$$1; }; + /** + * Gets / sets the pointsTooltip + * (see {@link violin#pointsTooltip}) + * @param {tooltip} [_=none] + * @returns {violin | tooltip} + * @memberof violin + * @property + * by default pointsTooltip = tooltip() + */ + violin.pointsTooltip = function(_) { return arguments.length ? (pointsTooltip = _, violin) : pointsTooltip; }; - function setTotals(){ - var totals = {}; - // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) - setValues.map(function(k, i){ totals[k] = {'total': 0}; }); - cellKeys.map(function(k, i){ - var e = elementExtractor(k, i); - if (Array.isArray(e)) { - totals[setExtractor(k, i)]['total']+= e.length; - } else { - totals[setExtractor(k, i)]['total']+= e; - } - }); - return totals - } - upset.intersectionTotals = intersectionTotals; - upset.setTotals = setTotals; - return upset - } + function violin () { + // for convenience in handling orientation specific values + var horizontalQ = (orient == 'horizontal') ? true : false; - let charts = { - scatter, bar, bubble: bubbleHeatmap, heatmap, violin, neededViolinValues, upset - }; + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); - function categoricLegend( selection ) { - var - categories, + // if grouping is undefined sort violinKeys by sortingFunction + var ordered = (grouping == undefined) ? d3.keys(data).sort(sortingFunction) : grouping; - /** - * Data to plot. Assumed to be a object, where each key corresponds to a legend - * (see {@link legend#data}) - * @param {Object} [data=undefined] - * @memberof legend# - * @property - */ - data, - /** - * Which direction to render the bars in - * (see {@link legend#orient}) - * @param {number} [orient='horizontal'] - * @memberof legend# - * @property - */ - orient='horizontal', - /** - * Amount of horizontal space (in pixels) avaible to render the legend in - * (see {@link legend#spaceX}) - * @param {number} [spaceX=undefined] - * @memberof legend# - * @property - */ - spaceX, - /** - * Amount of vertical space (in pixels) avaible to render the legend in - * (see {@link legend.spaceY}) - * @param {number} [spaceY=undefined] - * @memberof legend# - * @property - */ - spaceY, + // console.log(ordered) - /** - * Whether or not to allow legend to render elements pass the main spatial dimension - * given the orientation (see {@link legend#orient}), where {@link legend#orient}="horizontal" - * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" - * the main dimension is {@link legend#spaceY} - * @param {boolean} [overflowQ=false] - * @memberof legend# - * @property - */ - overflowQ = false, + violinKeys = utils.arr.flatten(ordered); - /** - * An array - putatively of other arrays - depicting how bars should be arranged - * @param {Array[]} [grouping=undefined] - * @memberof legend# - * @property - */ - grouping, - /** - * How to get the value of the legend - * @param {function} [valueExtractor=function(key, index) { return data[key] }] - * @memberof legend# - * @property - */ - valueExtractor = function(key, index) { return data[key] }, - /** - * How to sort the bars - if {@link bar#grouping} is not provided. - * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] - * @memberof bar# - * @property - */ - sortingFunction = function(keyA, keyB) {return d3.ascending(keyA, keyB)}, - /** - * Default space for the spacer (percentage) of main dimension given the orientation - * (see {@link legend#orient}), where {@link legend#orient}="horizontal" - * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" - * the main dimension is {@link legend#spaceY} between bars - * @param {number} [objectSpacer=0.05] - * @memberof legend# + var calcValues = neededViolinValues() + .horizontalQ(horizontalQ) + .quartileKeys(quartileKeys) + .violinPointsExtractor(violinPointsExtractor) + .violinPointValueExtractor(violinPointValueExtractor); + + + + // augment valus + violinValues = {}; + violinKeys.map(function(vk, i){ + let v = calcValues(vk, data); + violinValues[vk] = v; + }); + + var numberOfObjects = violinKeys.length; + + var min = [].concat(...violinKeys.map(function(k, i){return data[k].quartiles[quartileKeys[0]]})); + var max = [].concat(...violinKeys.map(function(k, i){return data[k].quartiles[quartileKeys[quartileKeys.length - 1]]})); + var extent = [Math.min(...min) - domainPadding, Math.max(...max) + domainPadding]; + // console.log(extent, violinValues, ordered) + + // set the scale + scale.domain(extent).range(horizontalQ ? [0,spaceY] : [0, spaceX]); + var space = horizontalQ ? spaceX : spaceY; + // calculate object size + objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + // calculate spacer size if needed + spacerSize = utils.math.calculateWidthOfSpacer(ordered, space, objectSize, numberOfObjects, objectSpacer, overflowQ); + // make the nested groups + var spacerFunction = groupingSpacer() + .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) + .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc) + .namespace(namespace); + + // move stuff + spacerFunction(container, ordered, 0); + // console.log(violinKeys, ordered, container.selectAll('g:not(.to-remove).'+objectClass).nodes()) + + // for color function + var parentIndexArray = []; + container.selectAll('g:not(.to-remove).'+objectClass) + .each(function(d, i){if (utils.arr.hasQ(violinKeys, d)){ parentIndexArray.push(Number(d3.select(this).attr('parent-index')));}}); + + // update color function + colorFunction$$1 = colorFunction$$1.colorBy() == 'index' + ? colorFunction$$1.dataExtent([0, Math.max(...parentIndexArray)]) + : colorFunction$$1.dataExtent(extent); + + /* violiin specific needs */ + + + var frequencyMax = Math.max(...[].concat(...violinKeys.map(function(k, i){return d3.max(data[k].frequencies)}))); + var vScale = d3.scaleLinear().domain([0, frequencyMax]).range([0, objectSize / 2]); + + var lArea = d3.line() + .x(function(d, i){ return horizontalQ ? -vScale(d.x) : scale(d.x)}) + .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : -vScale(d.y)}) + .curve(d3.curveBasis); + var rArea = d3.line() + .x(function(d, i){ return horizontalQ ? vScale(d.x) : scale(d.x)}) + .y(function(d, i){ return horizontalQ ? scale(extent[1]) - scale(d.y) : vScale(d.y)}) + .curve(d3.curveBasis); + + + + + + + container.selectAll('g:not(.to-remove).'+objectClass).each(function(key, i){ + var t = d3.select(this), + currentData = data[key]; + // needed because bug in selecting .to-remove + if (!utils.arr.hasQ(violinKeys, key)) {return} + var + i = t.attr('parent-index') == undefined ? i : t.attr('parent-index'), + fillColor = colorFunction$$1(key, currentData, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(key, currentData, i, 'stroke'), + area = utils.sel.safeSelect(t, 'g', 'area'), + la = utils.sel.safeSelect(area, 'path', 'left'), + ra = utils.sel.safeSelect(area, 'path', 'right'), + quarts = utils.sel.safeSelect(t, 'g', 'quarts'), + lq3 = utils.sel.safeSelect(quarts, 'line', 'q3'), + lq1 = utils.sel.safeSelect(quarts, 'line', 'q1'), + q3 = currentData.quartiles[quartileKeys[3]], + q2 = currentData.quartiles[quartileKeys[2]], + q1 = currentData.quartiles[quartileKeys[1]]; + + t.attr('transform', horizontalQ ? 'translate('+objectSize / 2+',0)' : 'translate(0,'+objectSize / 2+')' ); + // draw curve + la.transition().duration(transitionDuration).attr('d', function(dd, ii){ return lArea(currentData.contour)}) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', objectStrokeWidth); + + ra.transition().duration(transitionDuration).attr('d', function(dd, ii){ return rArea(currentData.contour)}) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', objectStrokeWidth); + + area.node().addEventListener('mouseover', function(dd, ii){ + container.selectAll('g.'+objectClass).style('opacity', 0.2); + t.style('opacity', 1); + la.attr('stroke-width',objectStrokeWidth*2); + ra.attr('stroke-width',objectStrokeWidth*2); + }); + area.node().addEventListener('mouseout', function(dd, ii){ + container.selectAll('g.'+objectClass).style('opacity', 1); + la.attr('stroke-width',objectStrokeWidth); + ra.attr('stroke-width',objectStrokeWidth); + }); + + if (pointsQ) { + var ptsContainer = utils.sel.safeSelect(t, 'g', 'points'); + var pts = ptsContainer.selectAll('.point').data(currentData.pointKeys); + pts.on('mouseover', null); + + + var ptsExit = pts.exit().transition().ease(easeFunc).duration(transitionDuration) + .attr('r', 0) + .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) + .attr('cx', horizontalQ ? vScale(0) : scale(q2)).remove(); + + var ptsEnter = pts.enter().append('circle').attr('class', 'point').attr('r', 0) + .attr('cx', horizontalQ ? 0 : scale(q2)) + .attr('cy', horizontalQ ? scale(q2) : 0); + + pts = pts.merge(ptsEnter); + + // console.log(pointsTooltip.header()) + + var pTTips = tooltip().selection(pts).data(violinPointsExtractor(key, currentData)) + .header(pointsTooltip.header()) + .keys(pointsTooltip.keys()) + .values(pointsTooltip.values()); + + pTTips(); + + var pMin = d3.min(currentData.pointValues), pMax = d3.max(currentData.pointValues); + + + + pts.transition().duration(transitionDuration).ease(easeFunc).attr('r', pointRadius) + .attr('cy', function(pointKey, ii){ + var dd = currentData.pointValues[ii]; + if (horizontalQ) { return scale(extent[1]) - scale(dd) } + var j = utils.arr.whichBin(currentData.binned, dd); + var r = Math.random(); + var n = vScale(r * currentData.frequencies[j] * 0.5); + var k = Math.random() > 0.5 ? n : -n; + return k + }) + .attr('cx', function(pointKey, ii){ + var dd = currentData.pointValues[ii]; + if (horizontalQ) { + var j = utils.arr.whichBin(currentData.binned, dd); + var r = Math.random(); + var n = vScale(r * currentData.frequencies[j] * 0.5); + var k = Math.random() > 0.5 ? n : -n; + return k + } + return scale(dd) + }) + .attr('stroke', function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'stroke', strokeColor, pMin, pMax) }) + .attr('fill' , function(dd, ii) { var dd = currentData.pointValues[ii]; return pointColorFunc(dd, 'fill' , strokeColor, pMin, pMax) }) + .attr('stroke-width', pointStrokeWidth); + + ptsContainer.selectAll('circle.point').on('mouseover', function(dd, ii){ + container.selectAll('g.'+objectClass).style('opacity', 0.2); + t.style('opacity', 1); + la.attr('stroke-width',objectStrokeWidth*2); + ra.attr('stroke-width',objectStrokeWidth*2); + + container.selectAll('.point').style('opacity', 0.2); + d3.select(this).style('opacity', 1).attr('r', pointRadius * 2).attr('stroke-width',pointStrokeWidth*2); + }); + ptsContainer.selectAll('circle.point').on('mouseout', function(dd, ii){ + var e = document.createEvent('SVGEvents'); + e.initEvent('mouseout',true,true); + area.node().dispatchEvent(e); + + container.selectAll('.point').style('opacity', 1); + d3.select(this).attr('stroke-width', pointStrokeWidth).attr('r', pointRadius); + }); + } + else { + cV.selectAll('.point') + .transition().duration(transitionDuration).ease(easeFunc) + .attr('r', 0) + .attr('cy', horizontalQ ? scale(extent[1]) - scale(q2) : vScale(0)) + .attr('cx', horizontalQ ? vScale(0) : scale(q2)) + .remove(); + } + + + }); + + + tooltip$$1.selection(container.selectAll('g:not(.to-remove).'+objectClass + ' .area')); + if (tooltip$$1.data() == undefined) {tooltip$$1.data(data);} + tooltip$$1(); + if (tooltip$$1.values() == undefined) { + tooltip$$1.values([ + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] } + ]); + + } + + } + + return violin + } + + + + + + /** + * Produces the function which manipulates the violin data to have values needed + * for rendering the violins as svg. + * @returns {function} calculateViolinValues + * @namespace neededViolinValues + */ + function neededViolinValues() { + var + /** + * Whether or not the orientation of the violins are horizontal + * (see {@link violin#orient}) + * @param {Object} [horizontalQ=true] + * @memberof neededViolinValues# * @property */ - objectSpacer = 0.05, + horizontalQ = true, /** - * The minimum size that an object can be - * @param {number} [minObjectSize=50] - * @memberof legend# + * Keys to be put into the utils.math.quartiles if they need to be calculated. + * (see {@link violin#quartileKeys}) + * @param {Object} [quartileKeys=['Q0', 'Q1', 'Q2', 'Q3', 'Q4']] + * @memberof neededViolinValues# * @property */ - minObjectSize = 10, + quartileKeys = ['Q0', 'Q1', 'Q2', 'Q3', 'Q4'], /** - * The maximum size that an object can be - * @param {number} [maxObjectSize=100] - * @memberof legend# + * Function which given the key of the violin and that key's associated value + * returns the object consiting of the points of the violin + * (see {@link violin#violinPointsExtractor}) + * @param {Object} [violinPointsExtractor=function(violinKey, violinData) { return violinData.points }] + * @memberof neededViolinValues# * @property */ - maxObjectSize = 100, - + violinPointsExtractor, /** - * The stroke width of the bars - * @param {number} [barStrokeWidth=2] - * @memberof legend# + * Function which given the key of the current point and the object of points for the + * violin, returns the numerical value of the point + * (see {@link violin#violinPointValueExtractor}) + * @param {Object} [violinPointValueExtractor=function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value }] + * @memberof neededViolinValues# * @property */ - bubbleStrokeWidth = 2, - /** - * Instance of ColorFunction - * @param {function} [colorFunction = colorFunction()] - * @memberof legend# - * @property - */ - colorFunction$$1 = colorFunction(), + violinPointValueExtractor; + /** - * Color of the background - * @param {string} [backgroundFill="transparent"] - * @memberof legend# - * @property - */ - backgroundFill = 'transparent', + * Gets / sets the horizontalQ + * (see {@link violin#orient}) + * @param {boolean} [_=none] + * @returns {calculateViolinValues | boolean} + * @memberof calculateViolinValues + * @property + * + * by default horizontalQ = true + */ + calculateViolinValues.horizontalQ = function(_) { return arguments.length ? (horizontalQ=_, calculateViolinValues) : horizontalQ }; /** - * Namespace for all items made by this instance of legend - * @param {string} [namespace="d3sm-legend"] - * @memberof legend# - * @property - */ - namespace = 'd3sm-legend', + * Gets / sets the quartileKeys + * (see {@link violin#quartileKeys}) + * @param {string[]} [_=none] + * @returns {calculateViolinValues | string[]} + * @memberof calculateViolinValues + * @property + * + * by default quartileKeys = ["Q0","Q1","Q2","Q3","Q4"] + */ + calculateViolinValues.quartileKeys = function(_) { return arguments.length ? (quartileKeys=_, calculateViolinValues) : quartileKeys }; /** - * Class name for legend container ( element) - * @param {string} [objectClass="legend"] - * @memberof legend# + * Gets / sets the violinPointsExtractor + * (see {@link violin#violinPointsExtractor}) + * @param {function} [_=none] + * @returns {calculateViolinValues | function} + * @memberof calculateViolinValues + * @property + * + * by default violinPointsExtractor = function(violinKey, violinData) { return violinData.points } + */ + calculateViolinValues.violinPointsExtractor = function(_) { return arguments.length ? (violinPointsExtractor=_, calculateViolinValues) : violinPointsExtractor }; + /** + * Gets / sets the violinPointValueExtractor + * (see {@link violin#violinPointValueExtractor}) + * @param {function} [_=none] + * @returns {calculateViolinValues | function} + * @memberof calculateViolinValues + * @property + * + * by default violinPointValueExtractor = function(violinPointKey, violinPointData) { return violinPointData[violinPointKey].value } + */ + calculateViolinValues.violinPointValueExtractor = function(_) { return arguments.length ? (violinPointValueExtractor=_, calculateViolinValues) : violinPointValueExtractor }; + + + /** + * The function produced by neededViolinValues. + * + * Adds the data need to render the violin as an svg + * @param {string} violinKey the key of the current violin + * @param {object} data the object consisting of violinKey - values (violin data) pairs + * @returns {none} this function manipulates the passed data object adding the following keys + * + * data[violinKey].binned // the binned values of the points + * + * data[violinKey].frequencies // the list consisting of the length of each bin + * + * data[violinKey].contour // the points depicting the contour of 1/2 of the violin + * + * data[violinKey].utils.math.quartiles // the utils.math.quartiles of the points' values + * + * data[violinKey].pointKeys // the keys associated with the points + * + * data[violinKey].pointValues // the numerical values of the points + * + * @memberof neededViolinValues# * @property */ - objectClass = 'legend', + function calculateViolinValues(violinKey, data) { + // data for the current violin + var violinData = data[violinKey]; + // the object of points + var violinPoints = violinPointsExtractor(violinKey, violinData); + // + var violinPointsKeys = d3.keys(violinPoints); + // the numerical values of those points + var violinPointsValues = violinPointsKeys.map(function(pk, i){return violinPointValueExtractor(pk, violinPoints)}); + + // utils.math.quartiles of those points + var pointQuartiles = utils.math.quartiles(violinPointsValues, quartileKeys); + + // binned points + var binned = d3.histogram()(violinPointsValues); + // length of bins + var frequencies = binned.map(bin=>bin.length); + // min and max countour points for nice drawings + var minContourPoint = horizontalQ ? {x: 0, y: d3.min(violinPointsValues)} : {x: d3.min(violinPointsValues), y: 0}; + var maxContourPoint = horizontalQ ? {x: 0, y: d3.max(violinPointsValues)} : {x: d3.max(violinPointsValues), y: 0}; + var violinContourPoints = binned.map(function(bin, i) { + return horizontalQ + ? {y: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), x: frequencies[i]} + : {x: (bin.length) ? d3.median(bin): d3.median([bin.x0, bin.x1]), y: frequencies[i]} + }); + // points along which to draw the violin shpe + violinContourPoints = [minContourPoint].concat(violinContourPoints).concat([maxContourPoint]); + + // set data + violinData.binned = binned; + violinData.frequencies = frequencies; + violinData.contour = violinContourPoints; + violinData.quartiles = pointQuartiles; + violinData.pointKeys = violinPointsKeys; + violinData.pointValues = violinPointsValues; + return violinData + } + + return calculateViolinValues + } + + function upset ( selection ) { + var + data, + orient='horizontal', + spaceX, + spaceY, + overflowQ=false, + minObjectSize=20, + maxObjectSize=50, + circleStrokeWidth=2, + // colorFunction= + backgroundFill = 'transparent', + namespace='d3sm-upset', + objectClass = 'upset', + + transitionDuration = 1000, + easeFunc = d3.easeExp, + + setKey = "set", + intersectionKey = "intersection", + elementsKey = "elements", + + setExtractor = function(key, i) {return data[key][setKey]}, + intersectionExtractor = function(key, i) {return data[key][intersectionKey]}, + elementExtractor = function(key, i) {return data[key][elementsKey]}, + + cellKeys, + setValues, + intersectionValues, + xObjectSpacer = 0.05, + yObjectSpacer = 0.05, + radius, + + // listDelim = ';' + + yObjectSize, + ySpacerSize, + xObjectSize, + xSpacerSize, + + setKeySortingFunction = function(a, b) { return setValues.indexOf(setExtractor(a)) - setValues.indexOf(setExtractor(b)) }, + intersectionKeySortingFunction = function(a, b) { return intersectionValues.indexOf(intersectionExtractor(a)) - intersectionValues.indexOf(intersectionExtractor(b)) }; + + + upset.selection = function(_) { return arguments.length ? (selection = _, upset) : selection; }; + upset.data = function(_) { return arguments.length ? (data = _, upset) : data; }; + upset.orient = function(_) { return arguments.length ? (orient = _, upset) : orient; }; + upset.spaceX = function(_) { return arguments.length ? (spaceX = _, upset) : spaceX; }; + upset.spaceY = function(_) { return arguments.length ? (spaceY = _, upset) : spaceY; }; + upset.overflowQ = function(_) { return arguments.length ? (overflowQ = _, upset) : overflowQ; }; + upset.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, upset) : minObjectSize; }; + upset.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, upset) : maxObjectSize; }; + upset.circleStrokeWidth = function(_) { return arguments.length ? (circleStrokeWidth = _, upset) : circleStrokeWidth; }; + upset.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, upset) : backgroundFill; }; + upset.namespace = function(_) { return arguments.length ? (namespace = _, upset) : namespace; }; + upset.objectClass = function(_) { return arguments.length ? (objectClass = _, upset) : objectClass; }; + upset.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, upset) : transitionDuration; }; + upset.easeFunc = function(_) { return arguments.length ? (easeFunc = _, upset) : easeFunc; }; + upset.cellKeys = function(_) { return arguments.length ? (cellKeys = _, upset) : cellKeys; }; + upset.setValues = function(_) { return arguments.length ? (setValues = _, upset) : setValues; }; + upset.intersectionValues = function(_) { return arguments.length ? (intersectionValues = _, upset) : intersectionValues; }; + upset.xObjectSpacer = function(_) { return arguments.length ? (xObjectSpacer = _, upset) : xObjectSpacer; }; + upset.yObjectSpacer = function(_) { return arguments.length ? (yObjectSpacer = _, upset) : yObjectSpacer; }; + upset.radius = function(_) { return arguments.length ? (radius = _, upset) : radius; }; + upset.setExtractor = function(_) { return arguments.length ? (setExtractor = _, upset) : setExtractor; }; + upset.intersectionExtractor = function(_) { return arguments.length ? (intersectionExtractor = _, upset) : intersectionExtractor; }; + upset.elementExtractor = function(_) { return arguments.length ? (elementExtractor = _, upset) : elementExtractor; }; + upset.setKeySortingFunction = function(_) { return arguments.length ? (setKeySortingFunction = _, upset) : setKeySortingFunction; }; + upset.intersectionKeySortingFunction = function(_) { return arguments.length ? (intersectionKeySortingFunction = _, upset) : intersectionKeySortingFunction; }; + + upset.yObjectSize = function(_) { return arguments.length ? (yObjectSize = _, upset) : yObjectSize; }; + upset.ySpacerSize = function(_) { return arguments.length ? (ySpacerSize = _, upset) : ySpacerSize; }; + upset.xObjectSize = function(_) { return arguments.length ? (xObjectSize = _, upset) : xObjectSize; }; + upset.xSpacerSize = function(_) { return arguments.length ? (xSpacerSize = _, upset) : xSpacerSize; }; + + function upset() { + // for convenience in handling orientation specific values + var horizontalQ = (orient == 'horizontal') ? true : false; + var verticalQ = !horizontalQ; + + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + + + cellKeys = d3.keys(data); + setValues = utils.arr.unique(cellKeys.map(setExtractor)).sort(); + intersectionValues = utils.arr.unique(cellKeys.map(intersectionExtractor)).sort().sort(function(a, b){ + return a.split(';').length - b.split(';').length + }); + + if (!horizontalQ) { + cellKeys.sort(function(a, b){ return setKeySortingFunction(a, b) || intersectionKeySortingFunction(a, b) }); + } else { + cellKeys.sort(function(a, b){ return intersectionKeySortingFunction(a, b) || setKeySortingFunction(a, b) }); + } + + + + + var + xValues = horizontalQ ? intersectionValues : setValues, + yValues = horizontalQ ? setValues : intersectionValues, + xDim = horizontalQ ? xValues.length : yValues.length, + yDim = horizontalQ ? yValues.length : xValues.length; + + // console.utils.con.log(xValues, yValues) + + + xObjectSize = utils.math.calculateWidthOfObject(spaceX, xDim, minObjectSize, maxObjectSize, xObjectSpacer, overflowQ); + yObjectSize = utils.math.calculateWidthOfObject(spaceY, yDim, minObjectSize, maxObjectSize, yObjectSpacer, overflowQ); + xSpacerSize = utils.math.calculateWidthOfSpacer(xValues, spaceX, xObjectSize, xDim, xObjectSpacer, overflowQ); + ySpacerSize = utils.math.calculateWidthOfSpacer(yValues, spaceY, yObjectSize, yDim, yObjectSpacer, overflowQ); + + var ySpacer = groupingSpacer() + .horizontalQ(false) + .moveby('category').numberOfObjects(yDim) + .objectSize(yObjectSize).spacerSize(ySpacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc); + + var xSpacer = groupingSpacer() + .horizontalQ(true) + .moveby('category').numberOfObjects(xDim) + .objectClass(objectClass) + .objectSize(xObjectSize).spacerSize(xSpacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc); + + + + if (verticalQ) { + xSpacer.objectClass(objectClass); + ySpacer.namespace('across').objectClass(utils.str.hypenate(objectClass, 'across')); + + ySpacer(container, yValues, 0); + container.selectAll('g.'+utils.str.hypenate(objectClass, 'across')) + .each(function(d, i){ xSpacer(d3.select(this), xValues, 0); }); + } else { + xSpacer.namespace('across').objectClass(utils.str.hypenate(objectClass, 'across')); + ySpacer.objectClass(objectClass); + + xSpacer(container, xValues, 0); + container.selectAll('g.'+utils.str.hypenate(objectClass, 'across')) + .each(function(d, i){ ySpacer(d3.select(this), yValues, 0); }); + } + + + var cells = container.selectAll('g:not(.to-remove).'+objectClass); + var lookup = {}; + cellKeys.map(function(k, i){ + lookup[setExtractor(k)+'::'+intersectionExtractor(k)] = k; + }); + + // var positionedCellKeys = [] + // for (var i = 0; i < setValues.length; i++) { + // for (var j = 0; j < intersectionValues.length; j++) { + // var lookupValue = lookup[setValues[j]+"::"+intersectionValues[i]] + // if (lookupValue == undefined) { + // positionedCellKeys.push(undefined) + // } else { + // positionedCellKeys.push(lookupValue) + // } + // console.utils.con.log(i, j, lookupValue) + // } + // } + // + // // console.utils.con.log(positionedCellKeys) + // + // cells.data(positionedCellKeys); + + + cells.data(cellKeys); + + cells.each(function(key, i) { + var t = d3.select(this); + if (key == undefined) {return } + var + currentData = data[key]; + // console.utils.con.log(key, currentData) + var + set = setExtractor(key, i), + intersection = intersectionExtractor(key, i); + + // console.utils.con.log(set, intersection) + + t.classed(intersection, true); + t.classed(set, true); + + var c = utils.sel.safeSelect(t, 'circle', utils.str.hypenate(objectClass,'circle')); + c.attr('cx', xObjectSize / 2) + .attr('cy', yObjectSize / 2 ) + .attr('r', radius == undefined ? Math.min(xObjectSize, yObjectSize) / 2 : radius) + .attr('fill', intersection.includes(set) ? "black": 'rgb(233,233,233)') + .attr('stroke', "black") + .attr("in-intersection", intersection.includes(set)); + }); + + + + } + + function intersectionTotals() { + var totals = {}; + // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) + + intersectionValues.map(function(k, i){ totals[k] = {'total': 0}; }); + cellKeys.map(function(k, i){ + var e = elementExtractor(k, i); + if (totals[intersectionExtractor(k, i)]['total'] == 0) { + if (Array.isArray(e)) { + totals[intersectionExtractor(k, i)]['total']+= e.length; + totals[intersectionExtractor(k, i)]['values'] = e; + } else { + totals[intersectionExtractor(k, i)]['total']+= e; + } + + } + }); + return totals + } + + function setTotals(){ + var totals = {}; + // intersectionValues.sort(function(a, b){ return intersectionKeySortingFunction(a, b) }) + + setValues.map(function(k, i){ totals[k] = {'total': 0}; }); + + cellKeys.map(function(k, i){ + var e = elementExtractor(k, i); + if (Array.isArray(e)) { + totals[setExtractor(k, i)]['total']+= e.length; + } else { + totals[setExtractor(k, i)]['total']+= e; + } + }); + return totals + } + + upset.intersectionTotals = intersectionTotals; + upset.setTotals = setTotals; + + return upset + } + + let charts = { + scatter, bar, bubble: bubbleHeatmap, boxwhisker, heatmap, violin, neededViolinValues, upset + }; + + function categoricLegend( selection ) { + var + categories, + + /** + * Data to plot. Assumed to be a object, where each key corresponds to a legend + * (see {@link legend#data}) + * @param {Object} [data=undefined] + * @memberof legend# + * @property + */ + data, + /** + * Which direction to render the bars in + * (see {@link legend#orient}) + * @param {number} [orient='horizontal'] + * @memberof legend# + * @property + */ + orient='horizontal', + /** + * Amount of horizontal space (in pixels) avaible to render the legend in + * (see {@link legend#spaceX}) + * @param {number} [spaceX=undefined] + * @memberof legend# + * @property + */ + spaceX, + /** + * Amount of vertical space (in pixels) avaible to render the legend in + * (see {@link legend.spaceY}) + * @param {number} [spaceY=undefined] + * @memberof legend# + * @property + */ + spaceY, + + /** + * Whether or not to allow legend to render elements pass the main spatial dimension + * given the orientation (see {@link legend#orient}), where {@link legend#orient}="horizontal" + * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" + * the main dimension is {@link legend#spaceY} + * @param {boolean} [overflowQ=false] + * @memberof legend# + * @property + */ + overflowQ = false, + + /** + * An array - putatively of other arrays - depicting how bars should be arranged + * @param {Array[]} [grouping=undefined] + * @memberof legend# + * @property + */ + grouping, + + /** + * How to get the value of the legend + * @param {function} [valueExtractor=function(key, index) { return data[key] }] + * @memberof legend# + * @property + */ + valueExtractor = function(key, index) { return data[key] }, + /** + * How to sort the bars - if {@link bar#grouping} is not provided. + * @param {function} [sortingFunction=function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}] + * @memberof bar# + * @property + */ + sortingFunction = function(keyA, keyB) {return d3.ascending(keyA, keyB)}, + /** + * Default space for the spacer (percentage) of main dimension given the orientation + * (see {@link legend#orient}), where {@link legend#orient}="horizontal" + * the main dimension is {@link legend#spaceX} and where {@link legend#orient}="vertical" + * the main dimension is {@link legend#spaceY} between bars + * @param {number} [objectSpacer=0.05] + * @memberof legend# + * @property + */ + objectSpacer = 0.05, + /** + * The minimum size that an object can be + * @param {number} [minObjectSize=50] + * @memberof legend# + * @property + */ + minObjectSize = 10, + /** + * The maximum size that an object can be + * @param {number} [maxObjectSize=100] + * @memberof legend# + * @property + */ + maxObjectSize = 100, + + /** + * The stroke width of the bars + * @param {number} [barStrokeWidth=2] + * @memberof legend# + * @property + */ + bubbleStrokeWidth = 2, + /** + * Instance of ColorFunction + * @param {function} [colorFunction = colorFunction()] + * @memberof legend# + * @property + */ + colorFunction$$1 = colorFunction(), + + /** + * Color of the background + * @param {string} [backgroundFill="transparent"] + * @memberof legend# + * @property + */ + backgroundFill = 'transparent', + /** + * Namespace for all items made by this instance of legend + * @param {string} [namespace="d3sm-legend"] + * @memberof legend# + * @property + */ + namespace = 'd3sm-legend', + /** + * Class name for legend container ( element) + * @param {string} [objectClass="legend"] + * @memberof legend# + * @property + */ + objectClass = 'legend', + + /** + * Duration of all transitions of this element + * @param {number} [transitionDuration=1000] + * @memberof legend# + * @property + */ + transitionDuration = 1000, + /** + * Easing function for transitions + * @param {d3.ease} [easeFunc=d3.easeExp] + * @memberof legend# + * @property + */ + easeFunc = d3.easeExp; + + + legend.categories = function(_) { return arguments.length ? (categories=_, legend) : categories }; + + /** + * Gets or sets the selection in which items are manipulated + * @param {d3.selection} [_=none] + * @returns {legend | d3.selection} + * @memberof legend + * @property + * by default selection = selection + */ + legend.selection = function(_) { return arguments.length ? (selection = _, legend) : selection; }; + /** + * Gets or sets the data + * (see {@link legend#data}) + * @param {number} [_=none] + * @returns {legend | object} + * @memberof legend + * @property + */ + legend.data = function(_) { return arguments.length ? (data = _, legend) : data; }; + /** + * Gets or sets the orient of the bars + * (see {@link legend#orient}) + * @param {number} [_=none] + * @returns {legend | object} + * @memberof legend + * @property + */ + legend.orient = function(_) { return arguments.length ? (orient = _, legend) : orient; }; + /** + * Gets or sets the amount of horizontal space in which items are manipulated + * (see {@link legend#spaceX}) + * @param {number} [_=none] should be a number > 0 + * @returns {legend | number} + * @memberof legend + * @property + * by default spaceX = undefined + */ + legend.spaceX = function(_) { return arguments.length ? (spaceX = _, legend) : spaceX; }; + /** + * Gets or sets the amount of vertical space in which items are manipulated + * (see {@link legend#spaceY}) + * @param {number} [_=none] should be a number > 0 + * @returns {legend | number} + * @memberof legend + * @property + * by default spaceY = undefined + */ + legend.spaceY = function(_) { return arguments.length ? (spaceY = _, legend) : spaceY; }; + + /** + * Gets / sets whether or not legend is allowed to go beyond specified dimensions + * (see {@link legend#spaceX}) + * @param {boolean} [_=none] + * @returns {legend | boolean} + * @memberof legend + * @property + * by default overflowQ = false + */ + legend.overflowQ = function(_) { return arguments.length ? (overflowQ = _, legend) : overflowQ; }; + /** + * Gets / sets the grouping of the bars + * (see {@link legend#grouping}) + * @param {Array[]} [_=none] + * @returns {legend | Array[]} + * @memberof legend + * @property + * by default grouping = undefined + */ + legend.grouping = function(_) { return arguments.length ? (grouping = _, legend) : grouping; }; + /** + * Gets / sets the valueExtractor + * (see {@link legend#valueExtractor}) + * @param {function} [_=none] + * @returns {legend | function} + * @memberof legend + * @property + * by default valueExtractor = function(key, index) { return data[key] }, + */ + legend.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, legend) : valueExtractor; }; + /** + * Gets / sets the sortingFunction + * (see {@link bar#sortingFunction}) + * @param {function} [_=none] + * @returns {bar | function} + * @memberof bar + * @property + * by default sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, + */ + legend.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, legend) : sortingFunction; }; + /** + * Gets / sets objectSpacer + * (see {@link legend#objectSpacer}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default objectSpacer = 0.05 + */ + legend.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, legend) : objectSpacer; }; + /** + * Gets / sets the minObjectSize + * (see {@link legend#minObjectSize}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default minObjectSize = 50 + */ + legend.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, legend) : minObjectSize; }; + /** + * Gets / sets the maxObjectSize + * (see {@link legend#maxObjectSize}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default maxObjectSize = 100 + */ + legend.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, legend) : maxObjectSize; }; + + /** + * Gets / sets the barStrokeWidth + * (see {@link legend#barStrokeWidth}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default barStrokeWidth = 2 + */ + legend.bubbleStrokeWidth = function(_) { return arguments.length ? (bubbleStrokeWidth = _, legend) : bubbleStrokeWidth; }; + /** + * Gets / sets the colorFunction + * (see {@link legend#colorFunction}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default colorFunction = colorFunction() + */ + legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, legend) : colorFunction$$1; }; + + /** + * Gets / sets the backgroundFill + * (see {@link legend#backgroundFill}) + * @param {string} [_=none] + * @returns {legend | string} + * @memberof legend + * @property + * by default backgroundFill = 'transparent' + */ + legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, legend) : backgroundFill; }; + /** + * Gets / sets the namespace + * (see {@link legend#namespace}) + * @param {string} [_=none] + * @returns {legend | string} + * @memberof legend + * @property + * by default namespace = 'd3sm-legend' + */ + legend.namespace = function(_) { return arguments.length ? (namespace = _, legend) : namespace; }; + /** + * Gets / sets the objectClass + * (see {@link legend#objectClass}) + * @param {string} [_=none] + * @returns {legend | string} + * @memberof legend + * @property + * by default objectClass = 'tick-group' + */ + legend.objectClass = function(_) { return arguments.length ? (objectClass = _, legend) : objectClass; }; + /** + * Gets / sets the transitionDuration + * (see {@link legend#transitionDuration}) + * @param {number} [_=none] + * @returns {legend | number} + * @memberof legend + * @property + * by default transitionDuration = 1000 + */ + legend.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, legend) : transitionDuration; }; + /** + * Gets / sets the easeFunc + * (see {@link legend#easeFunc}) + * @param {d3.ease} [_=none] + * @returns {legend | d3.ease} + * @memberof legend + * @property + * by default easeFunc = d3.easeExp + */ + legend.easeFunc = function(_) { return arguments.length ? (easeFunc = _, legend) : easeFunc; }; + + + function legend() { + var horizontalQ = (orient == 'horizontal') ? true : false; + var verticalQ = !horizontalQ; + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + + + colorFunction$$1.dataExtent([0, categories.length - 1]) + .colorBy('categories') + .categoryExtractor(function(k, v, i){return v}); + + var r = Math.min(spaceX, spaceY) / 2; + var numberOfObjects = categories.length; + + // if grouping is undefined sort barKeys by sortingFunction + var ordered = (grouping == undefined) ? categories.sort(sortingFunction) : grouping; + // ordered might be nested depending on grouping + var catKeys = utils.arr.flatten(ordered); + + var space = horizontalQ ? spaceX : spaceY; + // calculate object size + var objectSize = utils.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); + // calculate spacer size if needed + var spacerSize = utils.math.calculateWidthOfSpacer(catKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); + // make the nested groups + var spacerFunction = groupingSpacer() + .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) + .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) + .transitionDuration(transitionDuration).easeFunc(easeFunc) + .namespace(namespace); + + spacerFunction(container, ordered, 0); + var r = Math.min(objectSize, spaceX, spaceY) / 2 - bubbleStrokeWidth; + + container.selectAll('g:not(.to-remove).'+objectClass).each(function(cat, i) { + var t = d3.select(this); + var c = utils.sel.safeSelect(t, 'circle'); + var fillColor = colorFunction$$1(undefined, cat, i, 'fill'), // prevent duplicate computation + strokeColor = colorFunction$$1(undefined, cat, i, 'stroke'); + + var cx = horizontalQ + ? r+bubbleStrokeWidth + : (spaceX - r*2) / 2 + r; + var cy = verticalQ + ? r+bubbleStrokeWidth + : (spaceX - r*2) / 2 + r; + + c.attr("r", r) + .attr('cx', cx) + .attr('cy', cy) + .attr('fill', fillColor) + .attr('stroke', strokeColor) + .attr('stroke-width', bubbleStrokeWidth); + + var text = utils.sel.safeSelect(t, 'text'); + text.text(cat) + .attr('text-anchor', 'middle') + .attr("transform", function(d, i){ + var + x = cx, + y = cy + text.node().getBoundingClientRect().height / 4, + t = 'translate('+x+','+y+')'; + return t + }); + + }); + + + } + + return legend + } + + function numericLegend( selection ) { + + var + min=0, + max=1, + spaceX, + spaceY, + colorFunction$$1 = colorFunction(), + namespace='d3sm-linear-vertical-gradient', + fontSize = 12, + backgroundFill = 'transparent', + textColor = 'black', + roundTo = 2; + + + legend.min = function(_) { return arguments.length ? (min=_, legend) : min }; + legend.max = function(_) { return arguments.length ? (max=_, legend) : max }; + legend.spaceX = function(_) { return arguments.length ? (spaceX=_, legend) : spaceX }; + legend.spaceY = function(_) { return arguments.length ? (spaceY=_, legend) : spaceY }; + legend.namespace = function(_) { return arguments.length ? (namespace=_, legend) : namespace }; + legend.fontSize = function(_) { return arguments.length ? (fontSize=_, legend) : fontSize }; + legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill=_, legend) : backgroundFill }; + legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1=_, legend) : colorFunction$$1 }; + legend.textColor = function(_) { return arguments.length ? (textColor=_, legend) : textColor }; + legend.roundTo = function(_) { return arguments.length ? (roundTo=_, legend) : roundTo }; + + function legend() { + // background cliping rectangle + var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; + var container = utils.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + + var defs = utils.sel.safeSelect(selection, 'defs'); + var linearGradient = utils.sel.safeSelect(defs, 'linearGradient') + .attr("x1", "0%") + .attr("y1", "100%") + .attr("x2", "0%") + .attr("y2", "0%") + .attr('id', utils.str.hypenate(namespace,'numerical-legend-gradient')); + + + colorFunction$$1.dataExtent([min, max]) + .colorBy('value') + .valueExtractor(function(k, v, i){return v}); + + + linearGradient.selectAll('stop') + .data( colorFunction$$1.colors() ) + .enter() + .append('stop') + .attr("offset", function(d, i){ return i / (colorFunction$$1.colors().length - 1) }) + .attr('stop-color', function(d) {return d}); + + + + + var rect = utils.sel.safeSelect(container, 'rect', 'legend') + .attr('transform', 'translate(0,'+fontSize+')') + .style("fill", "url(#"+utils.str.hypenate(namespace,'numerical-legend-gradient')+")") + .attr('x', 0) + .attr('y', 0) + .attr('width', spaceX) + .attr('height', spaceY - fontSize*2) + .on('mousemove', function(d, i){legendMousemove(d, i, rect, d3.select(this));}) + .on('mouseout', function(d, i){ d3.select("#"+utils.str.hypenate(namespace,'legend-tooltip')).remove(); }); + + var minText = utils.sel.safeSelect(container, 'text', 'min') + .text(utils.math.round(min, 2)) + .attr('text-anchor', 'middle') + .attr("font-size", fontSize+'px') + .attr('transform', function(d, i){ + var + x = spaceX / 2, + y = spaceY - fontSize / 4, + t = 'translate('+x+','+y+')'; + return t + }); + + var maxText = utils.sel.safeSelect(container, 'text', 'max') + .text(utils.math.round(max, 2)) + .attr('text-anchor', 'middle') + .attr("font-size", fontSize+'px') + .attr('transform', function(d, i){ + var + x = spaceX / 2, + y = fontSize, + t = 'translate('+x+','+y+')'; + return t + }); + + + + + } + + function legendMousemove(d, i, rect, t) { + var s = d3.scaleLinear() + .domain([0, rect.attr('height')]) + .range([max, min]); + var m = d3.mouse(rect.node()); + var v = utils.math.round(s(m[1]),roundTo); + + var strokeColor = colorFunction$$1(undefined, v, undefined, 'stroke'); + var fillColor = colorFunction$$1(undefined, v, undefined, 'fill'); + + var div = utils.sel.safeSelect(d3.select('body'), 'div', utils.str.hypenate(namespace,'legend-tooltip')) + .attr('id', utils.str.hypenate(namespace,'legend-tooltip')) + .style('position', 'absolute') + .style('left', (d3.event.pageX+15)+'px') + .style('top', (d3.event.pageY+15)+'px') + .style('background-color', fillColor) + .style('border-color', strokeColor) + + .style('min-width', (fontSize * (String(max).split('.')[0].length+3))+'px') + .style('min-height', (fontSize * (String(max).split('.')[0].length+3))+'px') + .style('border-radius', '50%') + .style('border-radius', '5000px') + + .style('display', 'flex') + .style('justify-content', 'center') + .style('text-align', 'middle') + .style('padding', 2+"px") + + .style('border-style', 'solid') + .style('border-width', 2); + + var text = utils.sel.safeSelect(div, 'div') + .text(v) + .style('color', textColor) + .style('align-self', 'center'); + } + + return legend + } + + let legends = { + categorical: categoricLegend, numeric: numericLegend + }; + + // import * as d3 from "d3"; + /******************************************************************************* + ** ** + ** ** + ** D3 EXTENSIONS ** + ** ** + ** ** + *******************************************************************************/ + /** + * Recursively ascends parents of selection until it finds an svg tag + * @function d3.selection.thisSVG + * @augments d3.selection + * @returns {Element} which is the svg tag, not the d3 selection of that tag + */ + d3.selection.prototype.thisSVG = function() { return utils.sel.getContainingSVG(this.node()); }; + + + /** + * Helper for getting absolute position of the mouse + * @function d3.mouse.absolute + * @augments d3.mouse + * @returns {number[]} [x, y] as they relate to `html` not to local scope. + */ + d3.mouse.absolute = function() { + var html = d3.select('html').node(); + var [x, y] = this(html); + return [x, y] + }; + + + /** + * Gets position of the selection in relation to the containing svg + * @see{@link getContainingSVG} + * @function d3.selection.absolutePosition + * @augments d3.selection + * @returns {Object} with structure similar to getBoundingClientRect, e.g. + * top, left, bottom, right, height, width + */ + d3.selection.prototype.absolutePosition = function() { + var element = this.node(); + var elementPosition = element.getBoundingClientRect(); + var containerSVG = utils.sel.getContainingSVG(element); + var svgPosition = containerSVG.getBoundingClientRect(); + + return { + top: elementPosition.top - svgPosition.top, + left: elementPosition.left - svgPosition.left, + bottom: elementPosition.bottom - svgPosition.top, + right: elementPosition.right - svgPosition.left, + height: elementPosition.height, + width: elementPosition.width + }; + + }; + + + d3.selection.prototype.relativePositionTo = function(container) { + var element = this.node(); + var elementPosition = element.getBoundingClientRect(); + var containerSVG = container; + var svgPosition = containerSVG.getBoundingClientRect(); + + return { + top: elementPosition.top - svgPosition.top, + left: elementPosition.left - svgPosition.left, + bottom: elementPosition.bottom - svgPosition.top, + right: elementPosition.right - svgPosition.left, + height: elementPosition.height, + width: elementPosition.width + }; + + }; + + function getTranslation$1(selection){ + var transform = selection.attr('transform'); + var [junk, xy] =transform.split('translate('); + var [x, y] = xy.split(','); + junk = y.split(')'); + return [parseFloat(x), parseFloat(y)] + } + + function lasso( selection ) { + var + svg, // svg that is target of events + objectContainer, // container which houses objects we are selecting (allows for transform to be applied to lasso) + objectClass, // class of object we are selecting + namespace="d3sm-lasso", + chartContainer, + chartOffset, + objectsOffset, + eventCatcher, + + xScale, // optional scale for the lasso currentPoints + yScale, // optional scale for the lasso currentPoints + + activeQ = false, // whether or not lasso is active + + currentPoints=[], // mouse points for current lasso + allPoints=[], // list of lists for all points of lassos + + line = d3.line() + .x(function(d, i){ + var x; + if (xScale != undefined) { x = xScale(d[0]); } + else {x = d[0];} + return x //- chartOffset[0]// - objectsOffset[0] + }) + .y(function(d, i){ + var y; + if (yScale != undefined) { y= yScale(d[1]); } + else {y = d[1];} + return y// - chartOffset[1]// - objectsOffset[1] + }) + .curve(d3.curveLinearClosed), + + instance=0, // an indentifier for which instance this lasso is under the current svg + + tickDistance = 10, + + // styles for lasso path + color$$1 = '#17a2b8', + animationRate = '10s', + opacity=0.3, + dashArray = '5, 10', + stroke = 'black', + strokeWidth=2, + + // styles for lassoed objects + lassoedFill = "white", + lassoedStroke = 'black', + lassoedStrokeWidth = 3, + + transitionDuration = 1000, + easeFunc = d3.easeExp; + + var path; + + lasso.svg = function(_) { return arguments.length ? (svg = _, lasso) : svg; }; + lasso.chartContainer = function(_) { return arguments.length ? (chartContainer = _, lasso) : chartContainer; }; + lasso.objectContainer = function(_) { return arguments.length ? (objectContainer = _, lasso) : objectContainer; }; + lasso.objectClass = function(_) { return arguments.length ? (objectClass = _, lasso) : objectClass; }; + lasso.namespace = function(_) { return arguments.length ? (namespace = _, lasso) : namespace; }; + lasso.xScale = function(_) { return arguments.length ? (xScale = _, lasso) : xScale; }; + lasso.yScale = function(_) { return arguments.length ? (yScale = _, lasso) : yScale; }; + lasso.activeQ = function(_) { return arguments.length ? (activeQ = _, lasso) : activeQ; }; + lasso.currentPoints = function(_) { return arguments.length ? (currentPoints = _, lasso) : currentPoints; }; + lasso.allPoints = function(_) { return arguments.length ? (allPoints = _, lasso) : allPoints; }; + lasso.instance = function(_) { return arguments.length ? (instance = _, lasso) : instance; }; + lasso.tickDistance = function(_) { return arguments.length ? (tickDistance = _, lasso) : tickDistance; }; + lasso.color = function(_) { return arguments.length ? (color$$1 = _, lasso) : color$$1; }; + lasso.animationRate = function(_) { return arguments.length ? (animationRate = _, lasso) : animationRate; }; + lasso.opacity = function(_) { return arguments.length ? (opacity = _, lasso) : opacity; }; + lasso.dashArray = function(_) { return arguments.length ? (dashArray = _, lasso) : dashArray; }; + lasso.stroke = function(_) { return arguments.length ? (stroke = _, lasso) : stroke; }; + lasso.lassoedFill = function(_) { return arguments.length ? (lassoedFill = _, lasso) : lassoedFill; }; + lasso.lassoedStroke = function(_) { return arguments.length ? (lassoedStroke = _, lasso) : lassoedStroke; }; + lasso.lassoedStrokeWidth = function(_) { return arguments.length ? (lassoedStrokeWidth = _, lasso) : lassoedStrokeWidth; }; + lasso.eventCatcher = function(_) { return arguments.length ? (eventCatcher = _, lasso) : eventCatcher; }; + + lasso.drag = drag; + lasso.draw = draw; + lasso.tick = tick; + lasso.detect = detect; + lasso.toggle = toggle; + lasso.remove = remove; + lasso.render = render; + lasso.keyFrames = keyFrames; + lasso.updateObjects = updateObjects; + lasso.applyPathAttributes = applyPathAttributes; + lasso.applyObjectAttributes = applyObjectAttributes; + + keyFrames(); + + function lasso() { + // add a dash animation if needed + if (activeQ) { transitionDraw(); } + } + + function toggle(state) { + // use optional param to set state, otherwise toggle state + activeQ = (state!=undefined) ? state : !activeQ; + chartOffset = getTranslation$1(chartContainer); //utils.sel.getTranslation(chartContainer) + objectsOffset = getTranslation$1(objectContainer); //utils.sel.getTranslation(objectContainer) + + if (activeQ) { + svg.node().addEventListener('mousedown', render, true); + } else { + svg.node().removeEventListener('mousedown', render, true); + remove(); + } + + } + + function draw() { + chartOffset = getTranslation$1(chartContainer); //utils.sel.getTranslation(chartContainer) + objectsOffset = getTranslation$1(objectContainer); //utils.sel.getTranslation(objectContainer) + + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + var paths$$1 = container.selectAll('path[instance="'+instance+'"]'); + + // update + paths$$1 = paths$$1.data(allPoints); + + // remove excess + var pExit = paths$$1.exit().remove(); + // add needed paths + var pEnter = paths$$1.enter().append('path'); + + // merge + paths$$1 = paths$$1.merge(pEnter); + + // apply + applyPathAttributes(paths$$1); + } + + function remove() { + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + var paths$$1 = container.selectAll('path[instance="'+instance+'"]').remove(); + container.remove(); + objectContainer.selectAll(objectClass).classed("in-lasso", false); + updateObjects(); + } + + function render( event ) { + // nothing can interefer with drawing the lasso + event.preventDefault(); event.stopPropagation(); + + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + + /* + each time the user presses down, while the state is active, the lasso + the lasso should make a seperate segment. + */ + currentPoints = []; + + svg.node().addEventListener('mousemove', drag); + svg.node().addEventListener('mouseup', function(event) { + svg.node().removeEventListener('mousemove', drag); + allPoints.push(currentPoints); + // BUG: somehow this is pushing currentPoints n times where n is the nth lasso path for the current instance + // NOTE: allPoints = utils.arr.unique(allPoints) is a temporary and inefficient fix + allPoints = utils.arr.unique(allPoints); + }); + + path = container.append('path').data([currentPoints]); + applyPathAttributes(path); + } + + function transitionDraw() { + var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + var paths$$1 = container.selectAll('path[instance="'+instance+'"]'); + + // update + paths$$1 = paths$$1.data(allPoints); + + // remove excess + var pExit = paths$$1.exit().remove(); + // add needed paths + var pEnter = paths$$1.enter().append('path'); + + // merge + paths$$1 = paths$$1.merge(pEnter) + .transition().duration(transitionDuration) + .ease(easeFunc); + applyPathAttributes(paths$$1); + + } + + function applyPathAttributes(path) { + path + .attr("class", utils.str.hypenate(namespace, "lasso-path")) + .style('opacity', opacity) + .attr('fill', color$$1) + .attr("d", line) + .attr('instance', instance) + .style("stroke-dasharray", dashArray) + .attr("stroke", stroke) + .attr("stroke-width", strokeWidth) + .style('animation', 'lassoDash '+animationRate+' linear') + .style("animation-iteration-count", "infinite"); + } - /** - * Duration of all transitions of this element - * @param {number} [transitionDuration=1000] - * @memberof legend# - * @property - */ - transitionDuration = 1000, - /** - * Easing function for transitions - * @param {d3.ease} [easeFunc=d3.easeExp] - * @memberof legend# - * @property - */ - easeFunc = d3.easeExp; + function drag(event) { + /* + effectively create a mouse down and move event (which normally is inteperated + as 'drag' by the browser) by dynamically adding / removing this event on + mouse down / mouse up. + */ + if (eventCatcher != undefined) {eventCatcher.dispatch(utils.str.hypenate(namespace,"drag"));} + // d3.dispatch(utils.str.hypenate(namespace,"drag")) - legend.categories = function(_) { return arguments.length ? (categories=_, legend) : categories }; + if (event.which != 1) {return} // ensures left mouse button set + d3.event = event; + var pt = d3.mouse(objectContainer.node()); + var pt = d3.mouse(svg.node()); - /** - * Gets or sets the selection in which items are manipulated - * @param {d3.selection} [_=none] - * @returns {legend | d3.selection} - * @memberof legend - * @property - * by default selection = selection - */ - legend.selection = function(_) { return arguments.length ? (selection = _, legend) : selection; }; - /** - * Gets or sets the data - * (see {@link legend#data}) - * @param {number} [_=none] - * @returns {legend | object} - * @memberof legend - * @property - */ - legend.data = function(_) { return arguments.length ? (data = _, legend) : data; }; - /** - * Gets or sets the orient of the bars - * (see {@link legend#orient}) - * @param {number} [_=none] - * @returns {legend | object} - * @memberof legend - * @property - */ - legend.orient = function(_) { return arguments.length ? (orient = _, legend) : orient; }; - /** - * Gets or sets the amount of horizontal space in which items are manipulated - * (see {@link legend#spaceX}) - * @param {number} [_=none] should be a number > 0 - * @returns {legend | number} - * @memberof legend - * @property - * by default spaceX = undefined - */ - legend.spaceX = function(_) { return arguments.length ? (spaceX = _, legend) : spaceX; }; - /** - * Gets or sets the amount of vertical space in which items are manipulated - * (see {@link legend#spaceY}) - * @param {number} [_=none] should be a number > 0 - * @returns {legend | number} - * @memberof legend - * @property - * by default spaceY = undefined - */ - legend.spaceY = function(_) { return arguments.length ? (spaceY = _, legend) : spaceY; }; + if (xScale != undefined) {pt[0] = xScale.invert(pt[0]);} + if (yScale != undefined) {pt[1] = yScale.invert(pt[1]);} + pt[0] = pt[0] - chartOffset[0] - objectsOffset[0]; + pt[1] = pt[1] - chartOffset[1] - objectsOffset[1]; - /** - * Gets / sets whether or not legend is allowed to go beyond specified dimensions - * (see {@link legend#spaceX}) - * @param {boolean} [_=none] - * @returns {legend | boolean} - * @memberof legend - * @property - * by default overflowQ = false - */ - legend.overflowQ = function(_) { return arguments.length ? (overflowQ = _, legend) : overflowQ; }; - /** - * Gets / sets the grouping of the bars - * (see {@link legend#grouping}) - * @param {Array[]} [_=none] - * @returns {legend | Array[]} - * @memberof legend - * @property - * by default grouping = undefined - */ - legend.grouping = function(_) { return arguments.length ? (grouping = _, legend) : grouping; }; - /** - * Gets / sets the valueExtractor - * (see {@link legend#valueExtractor}) - * @param {function} [_=none] - * @returns {legend | function} - * @memberof legend - * @property - * by default valueExtractor = function(key, index) { return data[key] }, - */ - legend.valueExtractor = function(_) { return arguments.length ? (valueExtractor = _, legend) : valueExtractor; }; - /** - * Gets / sets the sortingFunction - * (see {@link bar#sortingFunction}) - * @param {function} [_=none] - * @returns {bar | function} - * @memberof bar - * @property - * by default sortingFunction = function(keyA, keyB) {return d3.descending(data[keyA], data[keyB])}, - */ - legend.sortingFunction = function(_) { return arguments.length ? (sortingFunction = _, legend) : sortingFunction; }; - /** - * Gets / sets objectSpacer - * (see {@link legend#objectSpacer}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default objectSpacer = 0.05 - */ - legend.objectSpacer = function(_) { return arguments.length ? (objectSpacer = _, legend) : objectSpacer; }; - /** - * Gets / sets the minObjectSize - * (see {@link legend#minObjectSize}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default minObjectSize = 50 - */ - legend.minObjectSize = function(_) { return arguments.length ? (minObjectSize = _, legend) : minObjectSize; }; - /** - * Gets / sets the maxObjectSize - * (see {@link legend#maxObjectSize}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default maxObjectSize = 100 - */ - legend.maxObjectSize = function(_) { return arguments.length ? (maxObjectSize = _, legend) : maxObjectSize; }; + /* if we have a point already, test if it passes a minimum distance to prevent overwhelming with too many tick functions */ + if (currentPoints.length) { + var lastPt = currentPoints[currentPoints.length - 1]; + var a = [pt[0], pt[1]], b = [lastPt[0], lastPt[1]]; - /** - * Gets / sets the barStrokeWidth - * (see {@link legend#barStrokeWidth}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default barStrokeWidth = 2 - */ - legend.bubbleStrokeWidth = function(_) { return arguments.length ? (bubbleStrokeWidth = _, legend) : bubbleStrokeWidth; }; - /** - * Gets / sets the colorFunction - * (see {@link legend#colorFunction}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default colorFunction = colorFunction() - */ - legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1 = _, legend) : colorFunction$$1; }; + if (xScale) {b[0] = xScale(b[0]); a[0] = xScale(a[0]);} + if (yScale) {b[1] = yScale(b[1]); a[1] = yScale(a[1]);} - /** - * Gets / sets the backgroundFill - * (see {@link legend#backgroundFill}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default backgroundFill = 'transparent' - */ - legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill = _, legend) : backgroundFill; }; - /** - * Gets / sets the namespace - * (see {@link legend#namespace}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default namespace = 'd3sm-legend' - */ - legend.namespace = function(_) { return arguments.length ? (namespace = _, legend) : namespace; }; - /** - * Gets / sets the objectClass - * (see {@link legend#objectClass}) - * @param {string} [_=none] - * @returns {legend | string} - * @memberof legend - * @property - * by default objectClass = 'tick-group' - */ - legend.objectClass = function(_) { return arguments.length ? (objectClass = _, legend) : objectClass; }; - /** - * Gets / sets the transitionDuration - * (see {@link legend#transitionDuration}) - * @param {number} [_=none] - * @returns {legend | number} - * @memberof legend - * @property - * by default transitionDuration = 1000 - */ - legend.transitionDuration = function(_) { return arguments.length ? (transitionDuration = _, legend) : transitionDuration; }; - /** - * Gets / sets the easeFunc - * (see {@link legend#easeFunc}) - * @param {d3.ease} [_=none] - * @returns {legend | d3.ease} - * @memberof legend - * @property - * by default easeFunc = d3.easeExp - */ - legend.easeFunc = function(_) { return arguments.length ? (easeFunc = _, legend) : easeFunc; }; + var dist = utils.math.euclideanDistance(b, a); + if (dist > tickDistance) { tick(pt); } + } + else { tick(pt); } + } - function legend() { - var horizontalQ = (orient == 'horizontal') ? true : false; - var verticalQ = !horizontalQ; - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + function tick (pt) { + /* + If a point is provided update data and objects. + Otherwise just call on data we already have. + Why like this?: + 1. currentPoints is current points to allow disjunct lassos, currentPoints is only pushed to + allPoints after mouseup. + 2. to allow render of objects in the lasso class / updating the data list + just by toggling the button + */ - colorFunction$$1.dataExtent([0, categories.length - 1]) - .colorBy('categories') - .categoryExtractor(function(k, v, i){return v}); + if (pt != undefined) { + currentPoints.push(pt); + path.attr("d", line); + if (currentPoints.length < 3) {return} // need at least 3 points to detect anything. + detect(allPoints.concat([currentPoints])); + } else { + detect(allPoints); + } + } - var r = Math.min(spaceX, spaceY) / 2; - var numberOfObjects = categories.length; - // if grouping is undefined sort barKeys by sortingFunction - var ordered = (grouping == undefined) ? categories.sort(sortingFunction) : grouping; - // ordered might be nested depending on grouping - var catKeys = utils$1.arr.flatten(ordered); + function detect(lassos) { + if (lassos == undefined) {lassos = allPoints;} + objectContainer.selectAll(objectClass).each(function(d, i){ + var current = d3.select(this), - var space = horizontalQ ? spaceX : spaceY; - // calculate object size - var objectSize = utils$1.math.calculateWidthOfObject(space, numberOfObjects, minObjectSize, maxObjectSize, objectSpacer, overflowQ); - // calculate spacer size if needed - var spacerSize = utils$1.math.calculateWidthOfSpacer(catKeys, space, objectSize, numberOfObjects, objectSpacer, overflowQ); - // make the nested groups - var spacerFunction = groupingSpacer() - .horizontalQ(horizontalQ).scale(scale).moveby('category').numberOfObjects(numberOfObjects) - .objectClass(objectClass).objectSize(objectSize).spacerSize(spacerSize) - .transitionDuration(transitionDuration).easeFunc(easeFunc) - .namespace(namespace); + box = current.absolutePosition(), + // box = current.relativePositionTo(objectContainer.node()), - spacerFunction(container, ordered, 0); - var r = Math.min(objectSize, spaceX, spaceY) / 2 - bubbleStrokeWidth; + boxPts = [ + [ + box.left - chartOffset[0] - objectsOffset[0], + box.top - chartOffset[1] - objectsOffset[1] + ], + [ + box.right - chartOffset[0] - objectsOffset[0], + box.top - chartOffset[1] - objectsOffset[1] + ], + [ + box.left - chartOffset[0] - objectsOffset[0], + box.bottom - chartOffset[1] - objectsOffset[1] + ], + [ + box.right - chartOffset[0] - objectsOffset[0], + box.bottom - chartOffset[1] - objectsOffset[1] + ] + ]; - container.selectAll('g:not(.to-remove).'+objectClass).each(function(cat, i) { - var t = d3.select(this); - var c = utils$1.sel.safeSelect(t, 'circle'); - var fillColor = colorFunction$$1(undefined, cat, i, 'fill'), // prevent duplicate computation - strokeColor = colorFunction$$1(undefined, cat, i, 'stroke'); + if (xScale != undefined) { + boxPts[0][0] = xScale.invert(boxPts[0][0]); + boxPts[1][0] = xScale.invert(boxPts[1][0]); + boxPts[2][0] = xScale.invert(boxPts[2][0]); + boxPts[3][0] = xScale.invert(boxPts[3][0]); + } + if (yScale != undefined) { + boxPts[0][1] = yScale.invert(boxPts[0][1]); + boxPts[1][1] = yScale.invert(boxPts[1][1]); + boxPts[2][1] = yScale.invert(boxPts[2][1]); + boxPts[3][1] = yScale.invert(boxPts[3][1]); + } - var cx = horizontalQ - ? r+bubbleStrokeWidth - : (spaceX - r*2) / 2 + r; - var cy = verticalQ - ? r+bubbleStrokeWidth - : (spaceX - r*2) / 2 + r; - c.attr("r", r) - .attr('cx', cx) - .attr('cy', cy) - .attr('fill', fillColor) - .attr('stroke', strokeColor) - .attr('stroke-width', bubbleStrokeWidth); + /* + flag needed as we have to test multiple lasso segments, and if the point + is not in one segment, it does not mean it is not in any + */ + var inAnyLassoQ = false; + for (var i = 0; i < lassos.length; i++) { + var lassoPoints = lassos[i]; + // .map(function(pt){ + // var x, y = pt + // if (xScale!=undefined) {x = xScale(x)} + // if (yScale!=undefined) {y = yScale(y)} + // return [x, y] + // }) + var boxInLassoQ = boxPts.every(coord => d3.polygonContains(lassoPoints, coord)); - var text = utils$1.sel.safeSelect(t, 'text'); - text.text(cat) - .attr('text-anchor', 'middle') - .attr("transform", function(d, i){ - var - x = cx, - y = cy + text.node().getBoundingClientRect().height / 4, - t = 'translate('+x+','+y+')'; - return t - }); + if (boxInLassoQ) { inAnyLassoQ = true; } // only update flag in the positive case. + } + current.classed('in-lasso', inAnyLassoQ); + current.classed('in-lasso-'+instance, inAnyLassoQ); }); - + updateObjects(); + return objectContainer.selectAll('.in-lasso-'+instance) } - return legend - } - function numericLegend( selection ) { - var - min=0, - max=1, - spaceX, - spaceY, - colorFunction$$1 = colorFunction(), - namespace='d3sm-linear-vertical-gradient', - fontSize = 12, - backgroundFill = 'transparent', - textColor = 'black', - roundTo = 2; + function updateObjects() { + objectContainer.selectAll(objectClass).each(function(d, i) { + var t = d3.select(this); + applyObjectAttributes(t, t.classed('in-lasso')); + }); + } + function applyObjectAttributes(obj, setQ) { + var + preLassoFill = obj.attr('_pre_lasso_fill'), + preLassoStroke = obj.attr('_pre_lasso_stroke'), + preLassoStrokeWidth = obj.attr('_pre_lasso_stroke-width'); - legend.min = function(_) { return arguments.length ? (min=_, legend) : min }; - legend.max = function(_) { return arguments.length ? (max=_, legend) : max }; - legend.spaceX = function(_) { return arguments.length ? (spaceX=_, legend) : spaceX }; - legend.spaceY = function(_) { return arguments.length ? (spaceY=_, legend) : spaceY }; - legend.namespace = function(_) { return arguments.length ? (namespace=_, legend) : namespace }; - legend.fontSize = function(_) { return arguments.length ? (fontSize=_, legend) : fontSize }; - legend.backgroundFill = function(_) { return arguments.length ? (backgroundFill=_, legend) : backgroundFill }; - legend.colorFunction = function(_) { return arguments.length ? (colorFunction$$1=_, legend) : colorFunction$$1 }; - legend.textColor = function(_) { return arguments.length ? (textColor=_, legend) : textColor }; - legend.roundTo = function(_) { return arguments.length ? (roundTo=_, legend) : roundTo }; + if (setQ) { + obj.classed("in-lasso", true); + obj.classed('in-lasso-'+instance, true); + if (preLassoFill == undefined) { obj.attr('_pre_lasso_fill', obj.attr('fill')); } + if (preLassoStroke == undefined) { obj.attr('_pre_lasso_stroke', obj.attr('stroke')); } + if (preLassoStrokeWidth == undefined) { obj.attr('_pre_lasso_stroke-width', obj.attr('stroke-width')); } - function legend() { - // background cliping rectangle - var bgcpRect = {x:0, y:0, width: spaceX, height:spaceY}; - var container = utils$1.sel.setupContainer( selection, namespace, bgcpRect, backgroundFill ); + obj + //BUG: when .raise() + .attr('fill', lassoedFill) + .attr('stroke', lassoedStroke) + .attr('stoke-width', lassoedStrokeWidth); - var defs = utils$1.sel.safeSelect(selection, 'defs'); - var linearGradient = utils$1.sel.safeSelect(defs, 'linearGradient') - .attr("x1", "0%") - .attr("y1", "100%") - .attr("x2", "0%") - .attr("y2", "0%") - .attr('id', utils$1.str.hypenate(namespace,'numerical-legend-gradient')); + } else { + obj.classed("in-lasso", false); + obj.classed('in-lasso-'+instance, false); + if (preLassoFill != undefined) { obj.attr('fill', preLassoFill); } + if (preLassoStroke != undefined) { obj.attr('stroke', preLassoStroke); } + if (preLassoStrokeWidth != undefined) { obj.attr('stroke-width', preLassoStrokeWidth); } + } + } + function keyFrames() { + var style = + d3.select("html").select('style.'+utils.str.hypenate(namespace,"lasso-dash")); + if (style.empty()) { + d3.select("html").append('style') + .classed(utils.str.hypenate(namespace,"lasso-dash"), true) + .html("@keyframes lassoDash {to { stroke-dashoffset: 1000;}}"); + } - colorFunction$$1.dataExtent([min, max]) - .colorBy('value') - .valueExtractor(function(k, v, i){return v}); + } + return lasso + } + function lassoWidget( selection ) { + var + namespace = 'd3sm-lasso', + selection = selection, + svg, + chartContainer, + objectContainer, + objectClass, + lassoLine = d3.line() + .x(function(d, i){return d[0]}) + .y(function(d, i){return d[1]}) + .curve(d3.curveLinearClosed), + colorFunction$$1 = colorFunction(), + maxNumberOfGroups, + dataExtractor, + xScale, + yScale; - linearGradient.selectAll('stop') - .data( colorFunction$$1.colors() ) - .enter() - .append('stop') - .attr("offset", function(d, i){ return i / (colorFunction$$1.colors().length - 1) }) - .attr('stop-color', function(d) {return d}); + function onError(msg, style){ + console.log(msg, style); + } + // setup the container + setupDataselectContainer(); + + lassoWidget.objectClass = function(_){return arguments.length ? (objectClass='.'+_.replace('.',''), lassoWidget) : objectClass}; + lassoWidget.svg = function(_){return arguments.length ? (svg=_, lassoWidget) : svg}; + lassoWidget.submit = function(_){return arguments.length ? (submit=_, lassoWidget) : submit}; + lassoWidget.maxNumberOfGroups = function(_){return arguments.length ? (maxNumberOfGroups=_, lassoWidget) : maxNumberOfGroups}; + lassoWidget.onError = function(_){return arguments.length ? (onError=_, lassoWidget) : onError}; + lassoWidget.objectContainer = function(_){return arguments.length ? (objectContainer=_, lassoWidget) : objectContainer}; + lassoWidget.chartContainer = function(_){return arguments.length ? (chartContainer=_, lassoWidget) : chartContainer}; + lassoWidget.dataExtractor = function(_){return arguments.length ? (dataExtractor=_, lassoWidget) : dataExtractor}; + lassoWidget.xScale = function(_){return arguments.length ? (xScale=_, lassoWidget) : xScale}; + lassoWidget.yScale = function(_){return arguments.length ? (yScale=_, lassoWidget) : yScale}; + + function styles() { + var s = d3.select("html").select("style."+namespace+'lasso-widget'); + if (s.empty()) { + d3.select("html").append("style") + .classed(namespace+'lasso-widget', true) + .html( + "."+utils.str.hypenate(namespace, "data-table") + "{\ + height:100px;\ + overflow:auto;\ + }" + ); + } + } + function lassoWidget() { + styles(); + } - var rect = utils$1.sel.safeSelect(container, 'rect', 'legend') - .attr('transform', 'translate(0,'+fontSize+')') - .style("fill", "url(#"+utils$1.str.hypenate(namespace,'numerical-legend-gradient')+")") - .attr('x', 0) - .attr('y', 0) - .attr('width', spaceX) - .attr('height', spaceY - fontSize*2) - .on('mousemove', function(d, i){legendMousemove(d, i, rect, d3.select(this));}) - .on('mouseout', function(d, i){ d3.select("#"+utils$1.str.hypenate(namespace,'legend-tooltip')).remove(); }); + function submit(data) { + console.log(data); + } - var minText = utils$1.sel.safeSelect(container, 'text', 'min') - .text(utils$1.math.round(min, 2)) - .attr('text-anchor', 'middle') - .attr("font-size", fontSize+'px') - .attr('transform', function(d, i){ - var - x = spaceX / 2, - y = spaceY - fontSize / 4, - t = 'translate('+x+','+y+')'; - return t - }); + function plusTab(tabList) { + var tab = utils.sel.safeSelect(tabList, 'li', utils.str.hypenate(namespace, 'plus-tab')) + .classed('ml-auto', true) + .classed('nav-item', true) + .on('click', makeNewGroup), - var maxText = utils$1.sel.safeSelect(container, 'text', 'max') - .text(utils$1.math.round(max, 2)) - .attr('text-anchor', 'middle') - .attr("font-size", fontSize+'px') - .attr('transform', function(d, i){ - var - x = spaceX / 2, - y = fontSize, - t = 'translate('+x+','+y+')'; - return t - }); + anchor = utils.sel.safeSelect(tab, 'a', 'nav-link'), + icon = utils.sel.safeSelect(anchor, 'i', 'fa') + .classed('fa-plus fa-2x text-success', true); + } + + function sendTab(tabList) { + var tab = utils.sel.safeSelect(tabList, 'li', utils.str.hypenate(namespace, 'send-tab')) + .classed('ml-auto', true) + .classed('nav-item', true) + .on('click', clickSend) + , + anchor = utils.sel.safeSelect(tab, 'a', 'nav-link'), + icon = utils.sel.safeSelect(anchor, 'i', 'fa') + .classed('fa-paper-plane-o fa-2x text-primary', true); + } + function clickSend() { + var data = gatherDataLists(); + submit(data); + } + function closeTab(tabList) { + var tab = utils.sel.safeSelect(tabList, 'li', utils.str.hypenate(namespace, 'close-tab')) + .classed('ml-auto', true) + .classed('nav-item', true) + .on('click', function(){ + var m = d3.mouse(d3.select("html").node()); + selection.select('div.card').style("position", 'relative') + .transition().duration(1000) + .ease(d3.easeBack) + .style('left', window.outerWidth +'px') + .remove(); + }) + , + anchor = utils.sel.safeSelect(tab, 'a', 'nav-link'), + icon = utils.sel.safeSelect(anchor, 'i', 'fa') + .classed('fa-window-close-o fa-2x text-danger', true); } - function legendMousemove(d, i, rect, t) { - var s = d3.scaleLinear() - .domain([0, rect.attr('height')]) - .range([max, min]); - var m = d3.mouse(rect.node()); - var v = utils$1.math.round(s(m[1]),roundTo); - var strokeColor = colorFunction$$1(undefined, v, undefined, 'stroke'); - var fillColor = colorFunction$$1(undefined, v, undefined, 'fill'); - var div = utils$1.sel.safeSelect(d3.select('body'), 'div', utils$1.str.hypenate(namespace,'legend-tooltip')) - .attr('id', utils$1.str.hypenate(namespace,'legend-tooltip')) - .style('position', 'absolute') - .style('left', (d3.event.pageX+15)+'px') - .style('top', (d3.event.pageY+15)+'px') - .style('background-color', fillColor) - .style('border-color', strokeColor) + function setupDataselectContainer() { + var + card = utils.sel.safeSelect(selection, 'div', 'card'), + cardHeader = utils.sel.safeSelect(card, 'div', 'card-header'), - .style('min-width', (fontSize * (String(max).split('.')[0].length+3))+'px') - .style('min-height', (fontSize * (String(max).split('.')[0].length+3))+'px') - .style('border-radius', '50%') - .style('border-radius', '5000px') + tabList = utils.sel.safeSelect(cardHeader, 'ul', 'nav') + .classed('nav-tabs card-header-tabs', true) + .classed(utils.str.hypenate(namespace, 'tab-list'), true) + .attr('role', 'tablist'), - .style('display', 'flex') - .style('justify-content', 'center') - .style('text-align', 'middle') - .style('padding', 2+"px") + cardBody = utils.sel.safeSelect(card, 'div', 'card-body'), + tabContent = utils.sel.safeSelect(cardBody, 'div', 'tab-content') + .classed(utils.str.hypenate(namespace, 'tab-content'), true), - .style('border-style', 'solid') - .style('border-width', 2); + // leftTabs = utils.sel.safeSelect(tabList, 'div', 'nav mr-auto left-aligned-tabs') + rightTabs = utils.sel.safeSelect(tabList, 'div', 'right-aligned-tabs').classed('nav ml-auto ', true); - var text = utils$1.sel.safeSelect(div, 'div') - .text(v) - .style('color', textColor) - .style('align-self', 'center'); + plusTab(rightTabs); + sendTab(rightTabs); + closeTab(rightTabs); + var + defaultTab = utils.sel.safeSelect(tabContent, 'div', 'tab-pane') + .classed(utils.str.hypenate(namespace,'default-tab'), true) + .classed("active", true) + .classed('text-left', true), + + p = utils.sel.safeSelect(defaultTab, 'div') + .html( + "Click to add a new group.
"+ + "Click to submit for re-analysis.
"+ + "Click to close the dataselect widget." + ); + // .text('Click the green plus to add a new group.') } - return legend - } - let legends = { - categorical: categoricLegend, numeric: numericLegend - }; + function makeRemoveButton(paneBtnList) { + var + btn = utils.sel.safeSelect(paneBtnList, 'button', 'remove-btn') + .classed('btn btn-danger', true) + .on('click', removeBtnClickToRemove), + i = utils.sel.safeSelect(btn, 'i', 'fa').classed('fa-trash-o', true), + s = utils.sel.safeSelect(btn, 'span').text('Remove group'); + return btn + } + + function removeBtnClickToRemove(d, i) { + var + tab = selection.select('#'+utils.str.hypenate(namespace,'tab',d)), + pane = selection.select('#'+utils.str.hypenate(namespace,'tab','pane',d)); - // import * as d3 from "d3"; - /******************************************************************************* - ** ** - ** ** - ** D3 EXTENSIONS ** - ** ** - ** ** - *******************************************************************************/ - /** - * Recursively ascends parents of selection until it finds an svg tag - * @function d3.selection.thisSVG - * @augments d3.selection - * @returns {Element} which is the svg tag, not the d3 selection of that tag - */ - d3.selection.prototype.thisSVG = function() { return utils$1.sel.getContainingSVG(this.node()); }; + tab.remove(); + pane.remove(); + showRemainingPanes(); + updateTabAndPaneNumbers(); + } - /** - * Helper for getting absolute position of the mouse - * @function d3.mouse.absolute - * @augments d3.mouse - * @returns {number[]} [x, y] as they relate to `html` not to local scope. - */ - d3.mouse.absolute = function() { - var html = d3.select('html').node(); - var [x, y] = this(html); - return [x, y] - }; + function togglePaneById(id) { + var panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')); + panes.selectAll('div.tab-pane') + .each(function(d, i){ + d3.select(this).classed('active show', d3.select(this).attr('id') == id); + }); + } - /** - * Gets position of the selection in relation to the containing svg - * @see{@link getContainingSVG} - * @function d3.selection.absolutePosition - * @augments d3.selection - * @returns {Object} with structure similar to getBoundingClientRect, e.g. - * top, left, bottom, right, height, width - */ - d3.selection.prototype.absolutePosition = function() { - var element = this.node(); - var elementPosition = element.getBoundingClientRect(); - var containerSVG = getContainingSVG(element); - var svgPosition = containerSVG.getBoundingClientRect(); + function insertTab(tabs, n) { + + // var li = tabs.insert("li", ':nth-child('+(n)+')') + var li = tabs.insert("li", '.right-aligned-tabs') + .classed('nav-item', true) + .classed('alert', true) + .classed('alert-secondary', true) + .classed('alert-dismissable', true) + .classed('fade', true) + .classed('show', true) + // .classed('mr-auto', true) + .attr("role", 'alert') + .attr("id", utils.str.hypenate(namespace,'tab',n)) + .classed(utils.str.hypenate(namespace,'tab'), true); + + var a = utils.sel.safeSelect(li, 'a') + .attr('data-toggle', 'tab') + .text('Group ' + n) + .attr('href', '#'+utils.str.hypenate(namespace,'tab','pane',n)) + .on('dblclick', function(d, i){ + var t = d3.select(this); + t.attr('contenteditable', true); + d3.select(t.node().parentNode) + .classed('alert-secondary', false) + .classed('alert-warning', true); - return { - top: elementPosition.top - svgPosition.top, - left: elementPosition.left - svgPosition.left, - bottom: elementPosition.bottom - svgPosition.top, - right: elementPosition.right - svgPosition.left, - height: elementPosition.height, - width: elementPosition.width - }; + // d3.select("html").on("click", dispatchBlur) + // + // function dispatchBlur(d, i) { + // if (d3.event.target != d3.select(this)) { + // d3.select(this).dispatch("blur") + // } + // } - }; + }) + .on('blur', function(d, i){ - d3.selection.prototype.relativePositionTo = function(container) { - var element = this.node(); - var elementPosition = element.getBoundingClientRect(); - var containerSVG = container; - var svgPosition = containerSVG.getBoundingClientRect(); + var t = d3.select(this); + t.attr('contenteditable', false); + d3.select(t.node().parentNode) + .classed('alert-secondary', true) + .classed('alert-warning', false); - return { - top: elementPosition.top - svgPosition.top, - left: elementPosition.left - svgPosition.left, - bottom: elementPosition.bottom - svgPosition.top, - right: elementPosition.right - svgPosition.left, - height: elementPosition.height, - width: elementPosition.width - }; + }) + .on('input', function(d, i){ + var t = d3.select(this); + var txt = t.text(); + d3.select(t.attr('href')).select("p.lead").text(txt); + }); - }; - // function getTranslation(selection){ - // var transform = selection.attr('transform') - // var [junk, xy] =transform.split('translate(') - // var [x, y] = xy.split(',') - // y, junk = y.split(')') - // return [parseFloat(x), parseFloat(y)] - // } + return li + } - function lasso( selection ) { - var - svg, // svg that is target of events - objectContainer, // container which houses objects we are selecting (allows for transform to be applied to lasso) - objectClass, // class of object we are selecting - namespace="d3sm-lasso", - chartContainer, - chartOffset, - objectsOffset, - eventCatcher, + function makeTabPane(panes, n) { + var pane = panes.append('div') + .datum(n) + .attr('role', 'tabpanel') + .classed('tab-pane', true) + .classed('text-left', true) + .classed(utils.str.hypenate(namespace,'tab','pane'), true) + .attr('id', utils.str.hypenate(namespace,'tab','pane',n)); + return pane + } - xScale, // optional scale for the lasso currentPoints - yScale, // optional scale for the lasso currentPoints + function populatePane(pane, n) { + var + lead = utils.sel.safeSelect(pane, 'p', 'lead').text('Group '+n), - activeQ = false, // whether or not lasso is active + tableContainer = utils.sel.safeSelect(pane, 'div', 'table-responsive') + .attr("class", utils.str.hypenate(namespace, "data-table")), - currentPoints=[], // mouse points for current lasso - allPoints=[], // list of lists for all points of lassos + table = utils.sel.safeSelect(tableContainer, "table", "table") + .classed("table-sm", true).classed("table-hover", true), - line = d3.line() - .x(function(d, i){ - var x; - if (xScale != undefined) { x = xScale(d[0]); } - else {x = d[0];} - return x //- chartOffset[0]// - objectsOffset[0] - }) - .y(function(d, i){ - var y; - if (yScale != undefined) { y= yScale(d[1]); } - else {y = d[1];} - return y// - chartOffset[1]// - objectsOffset[1] - }) - .curve(d3.curveLinearClosed), + caption = utils.sel.safeSelect(table, "caption", "caption").html("List of selected"), - instance=0, // an indentifier for which instance this lasso is under the current svg + btns = utils.sel.safeSelect(pane, 'div', 'text-right'), - tickDistance = 10, + cN = n % colorFunction$$1.colors().length; + if (n % 2 != 0) { cN = (colorFunction$$1.colors().length - 1) - cN; } - // styles for lasso path - color$$1 = '#17a2b8', - animationRate = '10s', - opacity=0.3, - dashArray = '5, 10', - stroke = 'black', - strokeWidth=2, - // styles for lassoed objects - lassoedFill = "white", - lassoedStroke = 'black', - lassoedStrokeWidth = 3, - transitionDuration = 1000, - easeFunc = d3.easeExp; - var path; - lasso.svg = function(_) { return arguments.length ? (svg = _, lasso) : svg; }; - lasso.chartContainer = function(_) { return arguments.length ? (chartContainer = _, lasso) : chartContainer; }; - lasso.objectContainer = function(_) { return arguments.length ? (objectContainer = _, lasso) : objectContainer; }; - lasso.objectClass = function(_) { return arguments.length ? (objectClass = _, lasso) : objectClass; }; - lasso.namespace = function(_) { return arguments.length ? (namespace = _, lasso) : namespace; }; - lasso.xScale = function(_) { return arguments.length ? (xScale = _, lasso) : xScale; }; - lasso.yScale = function(_) { return arguments.length ? (yScale = _, lasso) : yScale; }; - lasso.activeQ = function(_) { return arguments.length ? (activeQ = _, lasso) : activeQ; }; - lasso.currentPoints = function(_) { return arguments.length ? (currentPoints = _, lasso) : currentPoints; }; - lasso.allPoints = function(_) { return arguments.length ? (allPoints = _, lasso) : allPoints; }; - lasso.instance = function(_) { return arguments.length ? (instance = _, lasso) : instance; }; - lasso.tickDistance = function(_) { return arguments.length ? (tickDistance = _, lasso) : tickDistance; }; - lasso.color = function(_) { return arguments.length ? (color$$1 = _, lasso) : color$$1; }; - lasso.animationRate = function(_) { return arguments.length ? (animationRate = _, lasso) : animationRate; }; - lasso.opacity = function(_) { return arguments.length ? (opacity = _, lasso) : opacity; }; - lasso.dashArray = function(_) { return arguments.length ? (dashArray = _, lasso) : dashArray; }; - lasso.stroke = function(_) { return arguments.length ? (stroke = _, lasso) : stroke; }; - lasso.lassoedFill = function(_) { return arguments.length ? (lassoedFill = _, lasso) : lassoedFill; }; - lasso.lassoedStroke = function(_) { return arguments.length ? (lassoedStroke = _, lasso) : lassoedStroke; }; - lasso.lassoedStrokeWidth = function(_) { return arguments.length ? (lassoedStrokeWidth = _, lasso) : lassoedStrokeWidth; }; - lasso.eventCatcher = function(_) { return arguments.length ? (eventCatcher = _, lasso) : eventCatcher; }; + var lassoBtn = makeLassoButton(btns); + var currentLasso = makeLassoFunction(n, colorFunction$$1.colors()[cN]); + var clearBtn = makeClearButton(btns); - lasso.drag = drag; - lasso.draw = draw; - lasso.tick = tick; - lasso.detect = detect; - lasso.toggle = toggle; - lasso.remove = remove; - lasso.render = render; - lasso.keyFrames = keyFrames; - lasso.updateObjects = updateObjects; - lasso.applyPathAttributes = applyPathAttributes; - lasso.applyObjectAttributes = applyObjectAttributes; + bindButtons(lassoBtn, clearBtn, currentLasso, table); - keyFrames(); + var removeBtn = makeRemoveButton(btns); + + lead.on("mouseover", function(){ + currentLasso.draw(); + }); + lead.on("mouseout", function(){ + currentLasso.remove(); + }); - function lasso() { - // add a dash animation if needed - if (activeQ) { transitionDraw(); } } - function toggle(state) { - // use optional param to set state, otherwise toggle state - activeQ = (state!=undefined) ? state : !activeQ; - chartOffset = utils$1.sel.getTranslation(chartContainer); - objectsOffset = utils$1.sel.getTranslation(objectContainer); + function makeLassoButton(btns) { + var btn = utils.sel.safeSelect(btns, 'button', 'lasso-btn') + .classed('btn btn-info', true) + .classed(namespace, true); + // .datum([lasso]) + var i = utils.sel.safeSelect(btn, 'i', 'fa').classed('fa-hand-pointer-o', true); + var s = utils.sel.safeSelect(btn, 'span').text('Lasso select'); + return btn - if (activeQ) { - svg.node().addEventListener('mousedown', render, true); - } else { - svg.node().removeEventListener('mousedown', render, true); - remove(); - } + } + function makeClearButton(btns) { + var btn = utils.sel.safeSelect(btns, 'button', 'clear-btn') + .classed('btn btn-light', true) + .classed(namespace, true); + var i = utils.sel.safeSelect(btn, 'i', 'fa').classed('fa-eraser', true); + var s = utils.sel.safeSelect(btn, 'span').text('Clear selection'); + return btn } - function draw() { - chartOffset = utils$1.sel.getTranslation(chartContainer); - objectsOffset = utils$1.sel.getTranslation(objectContainer); + function makeLassoFunction(instance, color$$1) { + var currentLasso = lasso() + .namespace(namespace) + .svg(svg) + .objectClass(objectClass) + .chartContainer(chartContainer) + .objectContainer(objectContainer) + .instance(instance) + .color(color$$1) + .yScale(yScale) + .xScale(xScale); + + return currentLasso + } - var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); - var paths$$1 = container.selectAll('path[instance="'+instance+'"]'); + function bindButtons(lassoBtn, clearBtn, currentLasso, table) { + currentLasso.eventCatcher(lassoBtn); + lassoBtn.node().addEventListener("click", lassoBtnToggle); + + lassoBtn.datum([currentLasso]) + .on(utils.str.hypenate(namespace,'render'), function(){ + d3.select(this).datum()[0].draw(); + }) + .on(utils.str.hypenate(namespace,"drag"), function(las){ + var nodes = chartContainer.selectAll(".in-lasso-"+las[0].instance()).nodes(); + var tableData = nodes.map(function(d, i){ + var extracted = dataExtractor(d3.select(d).datum()); + extracted["__node"] = d; + return extracted + }); - // update - paths$$1 = paths$$1.data(allPoints); - // remove excess - var pExit = paths$$1.exit().remove(); - // add needed paths - var pEnter = paths$$1.enter().append('path'); + makeTable(table, tableData, currentLasso); + }); - // merge - paths$$1 = paths$$1.merge(pEnter); + clearBtn.on('click', function(){ + currentLasso.allPoints([]); + currentLasso.currentPoints([]); + lassoBtn.dispatch(utils.str.hypenate(namespace,"drag")); - // apply - applyPathAttributes(paths$$1); - } + }); - function remove() { - var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); - var paths$$1 = container.selectAll('path[instance="'+instance+'"]').remove(); - container.remove(); - objectContainer.selectAll(objectClass).classed("in-lasso", false); - updateObjects(); } - function render( event ) { - // nothing can interefer with drawing the lasso - event.preventDefault(); event.stopPropagation(); + function lassoBtnToggle() { + var that = d3.select(this); + var las = that.datum()[0]; - var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); + las.toggle(); + var activeQ = las.activeQ(); - /* - each time the user presses down, while the state is active, the lasso - the lasso should make a seperate segment. - */ - currentPoints = []; + if (las.activeQ()) { + that.classed('btn-info', !activeQ); + that.classed('btn-warning', activeQ); + that.select("span").text("Lasso select (active)"); + selection.selectAll("."+namespace+".lasso-btn").dispatch(utils.str.hypenate(namespace,'render')); + d3.select("html").node().addEventListener('mousedown', monitorLassoButtonState); + } else { + that.classed('btn-info', !activeQ); + that.classed('btn-warning', activeQ); + that.select("span").text("Lasso select"); + d3.select("html").node().removeEventListener('mousedown', monitorLassoButtonState); + } - svg.node().addEventListener('mousemove', drag); - svg.node().addEventListener('mouseup', function(event) { - svg.node().removeEventListener('mousemove', drag); - allPoints.push(currentPoints); - // BUG: somehow this is pushing currentPoints n times where n is the nth lasso path for the current instance - // NOTE: allPoints = utils.arr.unique(allPoints) is a temporary and inefficient fix - allPoints = utils$1.arr.unique(allPoints); - }); + function monitorLassoButtonState(event) { + /* + activeLasso stops event stopPropagation, so this event will not register in + that case. Thus we only need to ensure that the usre is clicking on the lasso button + otherwise, de-spawn lasso. + */ + if ( + event.target != that.node() && + event.target != that.select("span").node() && + event.target != that.select("i").node() + ) { + // event.preventDefault() + // event.stopPropagation() + las.toggle(false); + that.classed('btn-info', true); + that.classed('btn-warning', false); + that.select("span").text("Lasso select"); + // console.log(that, that.node()) + // that.dispatch("focusout") + } + } - path = container.append('path').data([currentPoints]); - applyPathAttributes(path); } - function transitionDraw() { - var container = utils$1.sel.safeSelect(objectContainer, 'g', 'lasso-container'); - var paths$$1 = container.selectAll('path[instance="'+instance+'"]'); - // update - paths$$1 = paths$$1.data(allPoints); + function updateTableHeaderColumns(headR, tableData) { + var headerKeys = d3.keys(tableData[0]).filter(k=>k!="__node"); + if (headerKeys.length > 0) { + // headerKeys = ["remove"].concat(headerKeys) + headerKeys.push("remove"); + } - // remove excess - var pExit = paths$$1.exit().remove(); - // add needed paths - var pEnter = paths$$1.enter().append('path'); + var headerCols = headR.selectAll("th"); - // merge - paths$$1 = paths$$1.merge(pEnter) - .transition().duration(transitionDuration) - .ease(easeFunc); - applyPathAttributes(paths$$1); + headerCols = headerCols.data(headerKeys); + headerCols.exit().remove(); + headerCols = headerCols.merge(headerCols.enter().append("th").attr("scope","col")) + .text(function(d, i){return d}); + return headerKeys } - function applyPathAttributes(path) { - path - .attr("class", utils$1.str.hypenate(namespace, "lasso-path")) - .style('opacity', opacity) - .attr('fill', color$$1) - .attr("d", line) - .attr('instance', instance) - .style("stroke-dasharray", dashArray) - .attr("stroke", stroke) - .attr("stroke-width", strokeWidth) - .style('animation', 'lassoDash '+animationRate+' linear') - .style("animation-iteration-count", "infinite"); + function updateTableRows(body, tableData) { + var bodyRows = body.selectAll("tr"); + + bodyRows = bodyRows.data(tableData); + bodyRows.exit().remove(); + bodyRows = bodyRows.merge(bodyRows.enter().append("tr")); + + return bodyRows } - function drag(event) { - /* - effectively create a mouse down and move event (which normally is inteperated - as 'drag' by the browser) by dynamically adding / removing this event on - mouse down / mouse up. - */ + function updateTableRowColumns(cols, headerKeys, rowData, tableData, lasso$$1, table) { + cols = cols.data(headerKeys); + cols.exit().remove(); + cols = cols.merge(cols.enter().append("td")); + + cols.html(function(d, i){ + if (d != "remove") { return rowData[d] } + return "" + }).classed("text-left", function(d, i){ + if (d != "remove") { return false } + return true + }); + // .attr("scope",function(d, i){ + // if (d != "remove") { return false } + // return true + // }) + + cols.select("i.fa-close").on("click", function(d, i){ + var node = rowData["__node"], + n = d3.select(node); + n.classed("in-lasso", false); + n.classed("in-lasso-"+lasso$$1.instance(), false); + + tableData.map(function(dd, j){ + if (dd["__node"] == node) { + tableData.splice(j, 1); + makeTable(table, tableData, lasso$$1); + } + }); + + }); + } + + function makeTable(table, tableData, lasso$$1) { + var + head = utils.sel.safeSelect(table, "thead"), + headR = utils.sel.safeSelect(head, "tr"), + body = utils.sel.safeSelect(table, "tbody"), + + headerKeys = updateTableHeaderColumns(headR, tableData), - if (eventCatcher != undefined) {eventCatcher.dispatch(utils$1.str.hypenate(namespace,"drag"));} - // d3.dispatch(utils.str.hypenate(namespace,"drag")) + bodyRows = updateTableRows(body, tableData); - if (event.which != 1) {return} // ensures left mouse button set - d3.event = event; - var pt = d3.mouse(objectContainer.node()); - var pt = d3.mouse(svg.node()); + bodyRows.each(function(rowData, i){ + var t = d3.select(this); + var cols = t.selectAll("td"); - if (xScale != undefined) {pt[0] = xScale.invert(pt[0]);} - if (yScale != undefined) {pt[1] = yScale.invert(pt[1]);} - pt[0] = pt[0] - chartOffset[0] - objectsOffset[0]; - pt[1] = pt[1] - chartOffset[1] - objectsOffset[1]; + updateTableRowColumns(cols, headerKeys, rowData, tableData, lasso$$1, table); - /* if we have a point already, test if it passes a minimum distance to prevent overwhelming with too many tick functions */ - if (currentPoints.length) { - var lastPt = currentPoints[currentPoints.length - 1]; - var a = [pt[0], pt[1]], b = [lastPt[0], lastPt[1]]; + t.on("mouseover", function(d, i) { + lasso$$1.applyObjectAttributes(d3.select(d["__node"]),true); + }) + .on("mouseout", function(d, i) { + lasso$$1.applyObjectAttributes(d3.select(d["__node"]),false); + }); + }); - if (xScale) {b[0] = xScale(b[0]); a[0] = xScale(a[0]);} - if (yScale) {b[1] = yScale(b[1]); a[1] = yScale(a[1]);} - var dist = utils$1.math.euclideanDistance(b, a); - if (dist > tickDistance) { tick(pt); } - } - else { tick(pt); } } - function tick (pt) { - /* - If a point is provided update data and objects. - Otherwise just call on data we already have. - Why like this?: - 1. currentPoints is current points to allow disjunct lassos, currentPoints is only pushed to - allPoints after mouseup. - 2. to allow render of objects in the lasso class / updating the data list - just by toggling the button - */ - if (pt != undefined) { - currentPoints.push(pt); - path.attr("d", line); - if (currentPoints.length < 3) {return} // need at least 3 points to detect anything. - detect(allPoints.concat([currentPoints])); - } else { - detect(allPoints); - } - } - function detect(lassos) { - if (lassos == undefined) {lassos = allPoints;} - objectContainer.selectAll(objectClass).each(function(d, i){ - var current = d3.select(this), - box = current.absolutePosition(), - // box = current.relativePositionTo(objectContainer.node()), + function makeNewGroup(d, i) { - boxPts = [ - [ - box.left - chartOffset[0] - objectsOffset[0], - box.top - chartOffset[1] - objectsOffset[1] - ], - [ - box.right - chartOffset[0] - objectsOffset[0], - box.top - chartOffset[1] - objectsOffset[1] - ], - [ - box.left - chartOffset[0] - objectsOffset[0], - box.bottom - chartOffset[1] - objectsOffset[1] - ], - [ - box.right - chartOffset[0] - objectsOffset[0], - box.bottom - chartOffset[1] - objectsOffset[1] - ] - ]; + var tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')), + panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')), + n = newGroupNumber(); - if (xScale != undefined) { - boxPts[0][0] = xScale.invert(boxPts[0][0]); - boxPts[1][0] = xScale.invert(boxPts[1][0]); - boxPts[2][0] = xScale.invert(boxPts[2][0]); - boxPts[3][0] = xScale.invert(boxPts[3][0]); - } - if (yScale != undefined) { - boxPts[0][1] = yScale.invert(boxPts[0][1]); - boxPts[1][1] = yScale.invert(boxPts[1][1]); - boxPts[2][1] = yScale.invert(boxPts[2][1]); - boxPts[3][1] = yScale.invert(boxPts[3][1]); - } + if (tabs.selectAll('.'+utils.str.hypenate(namespace,'tab')).size() == maxNumberOfGroups) { + onError('only '+maxNumberOfGroups+' allowed.', 'warning'); + return + } + var + tab = insertTab(tabs, n), + pane = makeTabPane(panes, n); - /* - flag needed as we have to test multiple lasso segments, and if the point - is not in one segment, it does not mean it is not in any - */ - var inAnyLassoQ = false; - for (var i = 0; i < lassos.length; i++) { - var lassoPoints = lassos[i]; - // .map(function(pt){ - // var x, y = pt - // if (xScale!=undefined) {x = xScale(x)} - // if (yScale!=undefined) {y = yScale(y)} - // return [x, y] - // }) - var boxInLassoQ = boxPts.every(coord => d3.polygonContains(lassoPoints, coord)); + populatePane(pane, n); + togglePaneById(pane.attr('id')); - if (boxInLassoQ) { inAnyLassoQ = true; } // only update flag in the positive case. - } + } - current.classed('in-lasso', inAnyLassoQ); - current.classed('in-lasso-'+instance, inAnyLassoQ); + function newGroupNumber() { + var tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list'));//, + var + n = tabs.selectAll('li').size() - 3, // minus 1 for plus tab + s = 'Group ' + n, + gs = []; + tabs.each(function(d, i){ return gs.push(d3.select(this).select("a").text()) }); + while (gs.includes(s)) { n+=1; s = 'Group ' + n; } + return n + } + + function updateTabAndPaneNumbers() { + var + tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')), + panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')); + + tabs = tabs.selectAll('.'+utils.str.hypenate(namespace,'tab')); + panes = panes.selectAll('.'+utils.str.hypenate(namespace,'tab', 'pane')); + + tabs.each(function(d, i){ + d3.select(this).datum(i) + .attr("id", utils.str.hypenate(namespace,'tab',i)) + .select('a') + .attr('href', '#'+utils.str.hypenate(namespace,'tab','pane',i)) + .text(function(dd, ii){ + var curText = d3.select(this).text(); + if (curText.split(' ')[0] == 'Group') { + return 'Group ' + i + } + return curText + }); + }); + + panes.each(function(d, i){ + d3.select(this).datum(i) + .attr('id', utils.str.hypenate(namespace,'tab','pane',i)); + utils.sel.safeSelect(d3.select(this), 'p', 'lead') + .text(function(dd, ii){ + var curText = d3.select(this).text(); + if (curText.split(' ')[0] == 'Group') { + return 'Group ' + i + } + return curText + }); + utils.sel.safeSelect(d3.select(this), 'button', 'remove-btn') + .on('click', removeBtnClickToRemove); }); - updateObjects(); - return objectContainer.selectAll('.in-lasso-'+instance) } + function gatherDataLists() { + var tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')); + var panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')); + var tables = panes.selectAll('.'+utils.str.hypenate(namespace, "data-table")); + var data = {}; + var textGroups = tabs.selectAll('li.'+utils.str.hypenate(namespace,'tab') + ' > a') + .nodes().map(function(d, i){return d3.select(d).text()}); - function updateObjects() { - objectContainer.selectAll(objectClass).each(function(d, i) { - var t = d3.select(this); - applyObjectAttributes(t, t.classed('in-lasso')); + textGroups.map(function(e, i){ + data[e] = []; }); - } - function applyObjectAttributes(obj, setQ) { - var - preLassoFill = obj.attr('_pre_lasso_fill'), - preLassoStroke = obj.attr('_pre_lasso_stroke'), - preLassoStrokeWidth = obj.attr('_pre_lasso_stroke-width'); - if (setQ) { - obj.classed("in-lasso", true); - obj.classed('in-lasso-'+instance, true); - if (preLassoFill == undefined) { obj.attr('_pre_lasso_fill', obj.attr('fill')); } - if (preLassoStroke == undefined) { obj.attr('_pre_lasso_stroke', obj.attr('stroke')); } - if (preLassoStrokeWidth == undefined) { obj.attr('_pre_lasso_stroke-width', obj.attr('stroke-width')); } + tables.each(function(d, i){ + var table = d3.select(this).select("tbody"); + data[textGroups[i]] = table.selectAll('tr').data(); + }); - obj - //BUG: when .raise() - .attr('fill', lassoedFill) - .attr('stroke', lassoedStroke) - .attr('stoke-width', lassoedStrokeWidth); + return data + } - } else { - obj.classed("in-lasso", false); - obj.classed('in-lasso-'+instance, false); - if (preLassoFill != undefined) { obj.attr('fill', preLassoFill); } - if (preLassoStroke != undefined) { obj.attr('stroke', preLassoStroke); } - if (preLassoStrokeWidth != undefined) { obj.attr('stroke-width', preLassoStrokeWidth); } + function showRemainingPanes() { + var + tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')), + panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')), + remainingTabs = tabs.selectAll('.'+utils.str.hypenate(namespace,'tab')); + + if (remainingTabs.size() == 0) { + panes.select('.'+utils.str.hypenate(namespace,'default-tab')) + .classed("active", true) + .classed('text-left', true); + } + else { + var lastTab = remainingTabs.nodes()[remainingTabs.size()-1], + lastPaneId = d3.select(lastTab).select('a').attr('href'); + panes.select(lastPaneId) + .classed("active", true) + .classed('text-left', true); } } - function keyFrames() { - var style = - d3.select("html").select('style.'+utils$1.str.hypenate(namespace,"lasso-dash")); - if (style.empty()) { - d3.select("html").append('style') - .classed(utils$1.str.hypenate(namespace,"lasso-dash"), true) - .html("@keyframes lassoDash {to { stroke-dashoffset: 1000;}}"); - } - } - return lasso + return lassoWidget } /******************************************************************************* @@ -8070,14 +9392,14 @@ function setLocks() { - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); - var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')); var cos = chartObjSel.attr('transform', 'translate(0,0)'); xLock = chartSel.node().getBBox().width - chart.spaceX();// * .9 yLock = chartSel.node().getBBox().height - chart.spaceY();// * .9 cos.attr('transform', 'translate('+chartObjTrans[0]+','+chartObjTrans[1]+')'); - utils$1.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); + utils.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); } @@ -8137,20 +9459,20 @@ - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); var xComponentObjSel = xComponentsSel.map(function(d, i){ - return d.select('.'+utils$1.str.hypenate(xComponents[i].namespace(),'object-container')) + return d.select('.'+utils.str.hypenate(xComponents[i].namespace(),'object-container')) }); var yComponentObjSel = yComponentsSel.map(function(d, i){ - return d.select('.'+utils$1.str.hypenate(yComponents[i].namespace(),'object-container')) + return d.select('.'+utils.str.hypenate(yComponents[i].namespace(),'object-container')) }); - var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')); var xComponentsObjTrans = xComponentObjSel.map(function(d, i){ - return utils$1.sel.getTranslation(d.attr('transform')) + return utils.sel.getTranslation(d.attr('transform')) }); var yComponentsObjTrans = yComponentObjSel.map(function(d, i){ - return utils$1.sel.getTranslation(d.attr('transform')) + return utils.sel.getTranslation(d.attr('transform')) }); var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0; @@ -8175,9 +9497,9 @@ zoom.reset = function() { - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); - var xAxisObjSel = xAxisSel.select('.'+utils$1.str.hypenate(xAxis.namespace(),'object-container')); - var yAxisObjSel = yAxisSel.select('.'+utils$1.str.hypenate(yAxis.namespace(),'object-container')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); + var xAxisObjSel = xAxisSel.select('.'+utils.str.hypenate(xAxis.namespace(),'object-container')); + var yAxisObjSel = yAxisSel.select('.'+utils.str.hypenate(yAxis.namespace(),'object-container')); chartObjSel.attr('transform', 'translate('+0+','+0+')'); xAxisObjSel.attr('transform', 'translate('+0+','+0+')'); yAxisObjSel.attr('transform', 'translate('+0+','+0+')'); @@ -8308,13 +9630,13 @@ function setLocks() { - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); - var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')); var cos = chartObjSel.attr('transform', 'translate(0,0)'); xLock = chartSel.node().getBBox().width - chart.spaceX() * .9; yLock = chartSel.node().getBBox().height - chart.spaceY() * .9; cos.attr('transform', 'translate('+chartObjTrans[0]+','+chartObjTrans[1]+')'); - utils$1.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); + utils.con.log('plotZoom', 'setLocks', {xLock:xLock, yLock:yLock}); } @@ -8373,9 +9695,9 @@ - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); - var xAxisObjSel = xAxisSel.select('.'+utils$1.str.hypenate(xAxis.namespace(),'object-container')); - var yAxisObjSel = yAxisSel.select('.'+utils$1.str.hypenate(yAxis.namespace(),'object-container')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); + var xAxisObjSel = xAxisSel.select('.'+utils.str.hypenate(xAxis.namespace(),'object-container')); + var yAxisObjSel = yAxisSel.select('.'+utils.str.hypenate(yAxis.namespace(),'object-container')); // xLock = chartSel.node().getBBox().width - chart.spaceX() - chartSel.node().getBBox().x // yLock = chartSel.node().getBBox().height - chart.spaceY() @@ -8383,9 +9705,9 @@ // bhm.selection().node().getBBox().width - bhm.spaceX() - var chartObjTrans = utils$1.sel.getTranslation(chartObjSel.attr('transform')); - var xAxisObjTrans = utils$1.sel.getTranslation(xAxisObjSel.attr('transform')); - var yAxisObjTrans = utils$1.sel.getTranslation(yAxisObjSel.attr('transform')); + var chartObjTrans = utils.sel.getTranslation(chartObjSel.attr('transform')); + var xAxisObjTrans = utils.sel.getTranslation(xAxisObjSel.attr('transform')); + var yAxisObjTrans = utils.sel.getTranslation(yAxisObjSel.attr('transform')); var x = horizontalQ ? transform.applyX(chartObjTrans[0]) : 0; @@ -8407,9 +9729,9 @@ zoom.reset = function() { - var chartObjSel = chartSel.select('.'+utils$1.str.hypenate(chart.namespace(),'object-container')); - var xAxisObjSel = xAxisSel.select('.'+utils$1.str.hypenate(xAxis.namespace(),'object-container')); - var yAxisObjSel = yAxisSel.select('.'+utils$1.str.hypenate(yAxis.namespace(),'object-container')); + var chartObjSel = chartSel.select('.'+utils.str.hypenate(chart.namespace(),'object-container')); + var xAxisObjSel = xAxisSel.select('.'+utils.str.hypenate(xAxis.namespace(),'object-container')); + var yAxisObjSel = yAxisSel.select('.'+utils.str.hypenate(yAxis.namespace(),'object-container')); chartObjSel.attr('transform', 'translate('+0+','+0+')'); xAxisObjSel.attr('transform', 'translate('+0+','+0+')'); yAxisObjSel.attr('transform', 'translate('+0+','+0+')'); @@ -8418,11 +9740,297 @@ return zoom } + function selectFilter(selection) { + + var + data, + namespace = 'd3sm-select-filter', + selectionName = 'Select options:', + defaultValue = undefined; + + + + + var lastValue = undefined; + + selectFilter.data = function(_) { return arguments.length ? (data = _, selectFilter) : data}; + selectFilter.namespace = function(_) { return arguments.length ? (namespace = _, selectFilter) : namespace}; + selectFilter.selectionName = function(_) { return arguments.length ? (selectionName = _, selectFilter) : selectionName}; + selectFilter.defaultValue = function(_) { return arguments.length ? (defaultValue = _, selectFilter) : defaultValue}; + selectFilter.currentOption = currentOption; + + function selectFilter() { + var + container = utils.sel.safeSelect(selection, 'div', 'input-group').classed(utils.str.hypenate(namespace,'container'),true), + + selectPrepend = utils.sel.safeSelect(container, 'div', 'select-prepend').classed('input-group-prepend', true), + selectPrependSpan = utils.sel.safeSelect(selectPrepend, 'span', 'input-group-text').text(selectionName), + + select = utils.sel.safeSelect(container, 'select', 'custom-select').classed(utils.str.hypenate(namespace,'select'),true), + + selectAppend = utils.sel.safeSelect(container, 'div', 'select-append').classed('input-group-prepend', true), + selectAppendButton = utils.sel.safeSelect(selectAppend, 'a', 'filter-button').classed('btn btn-outline-secondary', true), + filterButtonIcon = utils.sel.safeSelect(selectAppendButton, 'i', 'fa fa-filter'), + + inputGroup = utils.sel.safeSelect(container, 'div', 'filter-input-group').classed('input-group',true).classed('d-none', true), + inputPrepend = utils.sel.safeSelect(inputGroup, 'div', 'input-group-prepend'), + inputPrependSpan = utils.sel.safeSelect(inputPrepend, 'span', 'input-group-text').classed('search-button', true), + inputPrependSpanIcon = utils.sel.safeSelect(inputPrependSpan,'i','fa fa-search'), + + input = utils.sel.safeSelect(inputGroup, 'input', 'form-control').attr('placeholder', 'all').attr('type', 'text'), + inputAppend = utils.sel.safeSelect(inputGroup, 'div', 'input-group-append'), + inputAppendButton = utils.sel.safeSelect(inputAppend, 'a', 'close-button').classed('btn btn-outline-secondary', true), + inputAppendButtonIcon = utils.sel.safeSelect(inputAppendButton, 'i', 'fa fa-close'); + + + var keys = d3.keys(data), + options = select.selectAll('option'); + + options = options.data(d3.keys(data)); + options = options.merge(options.enter().append('option')) + .attr('value', function(d, i){return d}) + .text(function(d, i){return d}); + + var + filterButton = selectAppendButton, + closeButton = inputAppendButton; + + filterButton.on('click', function(d, i){ + var currentStyle = inputGroup.classed('d-none'); + inputGroup.classed('d-none', !currentStyle); + }); + + closeButton.on('click', function(d, i){ + input.property('value', '').dispatch('input'); + }); + + input.on('input', function(d, i){ + var + val = input.property('value'), + reg = new RegExp(val, 'gi'), + use; + + if (val == '') {use = keys;} + else { + use = []; + d3.keys(data).map(function(option, j){ + var match = option.match(reg); + if (match == null || match.join('') == '') ; + else { use.push(option); } + }); + } + + options = select.selectAll('option'); + options = options.data(use); + options.exit().remove(); + options = options.merge(options.enter().append('option')) + .attr('value', function(d, i){return d}) + .text(function(d, i){return d}); + + var current = currentOption(); + if (lastValue != current) { + lastValue = current; + select.dispatch('change'); + } + }); + + + } + + function currentOption() { + var val = selection.select("select").property('value'); + return val == undefined || val == '' + ? defaultValue == undefined + ? d3.keys(data)[0] + : defaultValue + : val + } + + return selectFilter + } + + /******************************************************************************* + ** ** + ** ** + ** DATATOGGLE ** + ** ** + ** ** + *******************************************************************************/ + /** + * Creates a datatoggle + * @constructor datatoggle + * @param {d3.selection} selection + * @namespace datatoggle + * @returns {function} datatoggle + */ + function datatoggle( selection ) { + var + /** + * Keys to make toggle-able options + * (see {@link datatoggle#keys}) + * @param {string[]} [keys=undefined] + * @memberof datatoggle# + * @property + */ + keys, + + /** + * What to do when a different key is clicked + * (see {@link datatoggle#updateFunction}) + * @param {function} [updateFunction=function(){}] + * @memberof datatoggle# + * @property + */ + updateFunction = function(){}, + /** + * Namespace for all items made by this instance of datatoggle + * @param {string} [namespace="d3sm-databar"] + * @memberof datatoggle# + * @property + */ + namespace='d3sm-databar', + /** + * Currently toggled key + * @param {string} [currentKey=undefined] + * @memberof datatoggle# + * @property + */ + currentKey, + + + + xAxisSelectQ = false, + xAxisOptions, + yAxisSelectQ = false, + yAxisOptions, + data; + toggle.xAxisSelectQ = function(_){return arguments.length ? (xAxisSelectQ = _, toggle) : xAxisSelectQ}; + toggle.yAxisSelectQ = function(_){return arguments.length ? (yAxisSelectQ = _, toggle) : yAxisSelectQ}; + toggle.xAxisOptions = function(_){return arguments.length ? (xAxisOptions = _, toggle) : xAxisOptions}; + toggle.yAxisOptions = function(_){return arguments.length ? (yAxisOptions = _, toggle) : yAxisOptions}; + toggle.data = function(_){return arguments.length ? (data = _, toggle) : data}; + toggle.keys = function(_){return arguments.length ? (keys = _, toggle) : keys}; + toggle.currentKey = function(_){return arguments.length ? (currentKey = _, toggle) : currentKey}; + + /** + * Gets / sets the updateFunction + * (see {@link datatoggle#updateFunction}) + * @function datatoggle.updateFunction + * @param {function} [_=none] + * @returns {datatoggle | function} + * @memberof datatoggle + * @property + * by default updateFunction = function(){} + */ + toggle.updateFunction = function(_){return arguments.length ? (updateFunction = _, toggle) : updateFunction}; + /** + * Gets / sets the namespace + * (see {@link datatoggle#namespace}) + * @function datatoggle.namespace + * @param {string} [_=none] + * @returns {datatoggle | string} + * @memberof datatoggle + * @property + * by default namespace = 'd3sm-databar' + */ + toggle.namespace = function(_){return arguments.length ? (namespace = _, toggle) : namespace}; + /** + * Gets / sets the currentKey + * (see {@link datatoggle#currentKey}) + * @function datatoggle.currentKey + * @param {string} [_=none] + * @returns {datatoggle | string} + * @memberof datatoggle + * @property + * by default currentKey = undefined + */ + toggle.currentKeys = + function () { + var vals = {}; + d3.keys(filterSelects).map(function(k, i){ + vals[k]= filterSelects[k].currentOption(); + }); + return vals + }; + + var filterSelects = {}; + function toggle() { + // selection options + + // selection.classed('d-flex flex-row', true) + // var filterButton = utils.sel.safeSelect(selection, 'a', 'slider-buttons') + // var filterI = utils.sel.safeSelect(filterButton, 'i', 'fa fa-sliders') + + /*BUG: unexpected behavior. + - when using bootstrap-eque way for collapse, clicking button submits to + the same page in applications (but not in demo), so using anchor (
) + - when using anchor, cause page to jump to that location + - when using show, the first open works, but the close does not. + */ + // filterButton.attr('class', 'btn btn-secondary') + // .attr('data-toggle', 'collapse') + // .attr('href', '#'+utils.str.hypenate(namespace, 'data-toggle')) + // .attr('target', '_blank') + // .html(filterButton.html()+' Filters') + // .on('click', function(d, i){ + // d3.event.preventDefault() + // d3.event.stopPropagation() + // var dt = d3.select("#"+utils.str.hypenate(namespace, 'data-toggle')) + // dt.classed('show', !dt.classed('show')) + // dt.classed('d-inline-flex', dt.classed('show')) + // + // filterButton.classed('btn-primary', dt.classed('show')) + // filterButton.classed('btn-secondary', !dt.classed('show')) + // }) + // .classed('d-inline-flex', true) + // .style("margin-right", '10px') + + + + // var datatoggleCollapse = utils.sel.safeSelect(selection, 'div', utils.str.hypenate(namespace,'collapse')) + // .attr('id', utils.str.hypenate(namespace, 'data-toggle')) + // .classed('collapse', true) + + // var flexRow = utils.sel.safeSelect(datatoggleCollapse, 'div', 'd-inline-flex flex-row flex-wrap') + var flexRow = utils.sel.safeSelect(selection, 'div', 'd-inline-flex flex-row flex-wrap'); + + var dataopts = flexRow.selectAll('div.'+utils.str.hypenate(namespace,'select-filter')); + // remove excess + dataopts.exit().remove(); + // bind data + dataopts = dataopts.data(d3.keys(data)); + //enter + var doEnter = dataopts.enter().append('div') + .attr('class', 'select-filter'); + + dataopts = dataopts.merge(doEnter).style('margin-right', "10px"); + + dataopts.each(function(d, i){ + var t = d3.select(this); + var sf = selectFilter(t) + .data(data[d]) + .namespace(utils.str.hypenate(namespace, d)) + .selectionName(d); + sf(); + filterSelects[d] = sf; + }); + + + selection.selectAll('select') + .on('change', function(){updateFunction();}); + // bind update function + // d3.selectAll + return toggle + } + + return toggle + } + let aux = { - lasso, plotZoom, multiPlotZoom + lasso, plotZoom, multiPlotZoom, datatoggle, selectFilter, lassoWidget }; - let d3sm$1 = { + let d3sm = { aux, axis, charts, @@ -8431,14 +10039,14 @@ groupingSpacer, legends, tooltip, - utils: utils$1, + utils, }; if (typeof window !== 'undefined') { - window.d3sm = d3sm$1; + window.d3sm = d3sm; } - exports.default = d3sm$1; + exports.default = d3sm; Object.defineProperty(exports, '__esModule', { value: true }); diff --git a/index.html b/index.html index 7d3527d..8a08d16 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,10 @@ d3sm - + - - + + diff --git a/src/modules/aux/data-toggle.js b/src/modules/aux/data-toggle.js new file mode 100644 index 0000000..be2e016 --- /dev/null +++ b/src/modules/aux/data-toggle.js @@ -0,0 +1,210 @@ +import utils from '../utils/index.js' +import selectFilter from './select-filter'; + +/******************************************************************************* +** ** +** ** +** DATATOGGLE ** +** ** +** ** +*******************************************************************************/ +/** + * Creates a datatoggle + * @constructor datatoggle + * @param {d3.selection} selection + * @namespace datatoggle + * @returns {function} datatoggle + */ +export default function datatoggle( selection ) { + var + /** + * Keys to make toggle-able options + * (see {@link datatoggle#keys}) + * @param {string[]} [keys=undefined] + * @memberof datatoggle# + * @property + */ + keys, + + /** + * What to do when a different key is clicked + * (see {@link datatoggle#updateFunction}) + * @param {function} [updateFunction=function(){}] + * @memberof datatoggle# + * @property + */ + updateFunction = function(){}, + /** + * Namespace for all items made by this instance of datatoggle + * @param {string} [namespace="d3sm-databar"] + * @memberof datatoggle# + * @property + */ + namespace='d3sm-databar', + /** + * Currently toggled key + * @param {string} [currentKey=undefined] + * @memberof datatoggle# + * @property + */ + currentKey, + + + + xAxisSelectQ = false, + xAxisOptions, + yAxisSelectQ = false, + yAxisOptions, + data + toggle.xAxisSelectQ = function(_){return arguments.length ? (xAxisSelectQ = _, toggle) : xAxisSelectQ} + toggle.yAxisSelectQ = function(_){return arguments.length ? (yAxisSelectQ = _, toggle) : yAxisSelectQ} + toggle.xAxisOptions = function(_){return arguments.length ? (xAxisOptions = _, toggle) : xAxisOptions} + toggle.yAxisOptions = function(_){return arguments.length ? (yAxisOptions = _, toggle) : yAxisOptions} + toggle.data = function(_){return arguments.length ? (data = _, toggle) : data} + toggle.keys = function(_){return arguments.length ? (keys = _, toggle) : keys} + toggle.currentKey = function(_){return arguments.length ? (currentKey = _, toggle) : currentKey} + + /** + * Gets / sets the updateFunction + * (see {@link datatoggle#updateFunction}) + * @function datatoggle.updateFunction + * @param {function} [_=none] + * @returns {datatoggle | function} + * @memberof datatoggle + * @property + * by default updateFunction = function(){} + */ + toggle.updateFunction = function(_){return arguments.length ? (updateFunction = _, toggle) : updateFunction} + /** + * Gets / sets the namespace + * (see {@link datatoggle#namespace}) + * @function datatoggle.namespace + * @param {string} [_=none] + * @returns {datatoggle | string} + * @memberof datatoggle + * @property + * by default namespace = 'd3sm-databar' + */ + toggle.namespace = function(_){return arguments.length ? (namespace = _, toggle) : namespace} + /** + * Gets / sets the currentKey + * (see {@link datatoggle#currentKey}) + * @function datatoggle.currentKey + * @param {string} [_=none] + * @returns {datatoggle | string} + * @memberof datatoggle + * @property + * by default currentKey = undefined + */ + toggle.currentKeys = + function () { + var vals = {} + d3.keys(filterSelects).map(function(k, i){ + vals[k]= filterSelects[k].currentOption() + }) + return vals + } + + var filterSelects = {} + function toggle() { + // selection options + + // selection.classed('d-flex flex-row', true) + // var filterButton = utils.sel.safeSelect(selection, 'a', 'slider-buttons') + // var filterI = utils.sel.safeSelect(filterButton, 'i', 'fa fa-sliders') + + /*BUG: unexpected behavior. + - when using bootstrap-eque way for collapse, clicking button submits to + the same page in applications (but not in demo), so using anchor () + - when using anchor, cause page to jump to that location + - when using show, the first open works, but the close does not. + */ + // filterButton.attr('class', 'btn btn-secondary') + // .attr('data-toggle', 'collapse') + // .attr('href', '#'+utils.str.hypenate(namespace, 'data-toggle')) + // .attr('target', '_blank') + // .html(filterButton.html()+' Filters') + // .on('click', function(d, i){ + // d3.event.preventDefault() + // d3.event.stopPropagation() + // var dt = d3.select("#"+utils.str.hypenate(namespace, 'data-toggle')) + // dt.classed('show', !dt.classed('show')) + // dt.classed('d-inline-flex', dt.classed('show')) + // + // filterButton.classed('btn-primary', dt.classed('show')) + // filterButton.classed('btn-secondary', !dt.classed('show')) + // }) + // .classed('d-inline-flex', true) + // .style("margin-right", '10px') + + + + // var datatoggleCollapse = utils.sel.safeSelect(selection, 'div', utils.str.hypenate(namespace,'collapse')) + // .attr('id', utils.str.hypenate(namespace, 'data-toggle')) + // .classed('collapse', true) + + // var flexRow = utils.sel.safeSelect(datatoggleCollapse, 'div', 'd-inline-flex flex-row flex-wrap') + var flexRow = utils.sel.safeSelect(selection, 'div', 'd-inline-flex flex-row flex-wrap') + + var dataopts = flexRow.selectAll('div.'+utils.str.hypenate(namespace,'select-filter')) + // remove excess + dataopts.exit().remove() + // bind data + dataopts = dataopts.data(d3.keys(data)) + //enter + var doEnter = dataopts.enter().append('div') + .attr('class', 'select-filter') + + dataopts = dataopts.merge(doEnter).style('margin-right', "10px") + + dataopts.each(function(d, i){ + var t = d3.select(this) + var sf = selectFilter(t) + .data(data[d]) + .namespace(utils.str.hypenate(namespace, d)) + .selectionName(d) + sf() + filterSelects[d] = sf + }) + + + selection.selectAll('select') + .on('change', function(){updateFunction()}) + // bind update function + // d3.selectAll + return toggle + } + + + function onlyOne() { + // d3.event.preventDefault() + // d3.event.stopPropagation() + var dataopts = selection.selectAll('div.data-option') + currentKey = dataopts.select(':checked').datum() + updateFunction() + + } + + function axisSelectFilter(selection, axis="x-axis", axisData) { + if (axisData == undefined) { + axisData = { + 'linear': d3.scaleLinear(), + 'log': d3.scaleLog(), + 'pow': d3.scalePow(), + 'sqrt': d3.scaleSqrt() + } + } + + var sf = selectFilter(selection) + .data(axisData) + .namespace(axis) + .selectionName(axis+' scale') + .defaultValue(d3.scaleLinear()) + + + sf() + + } + + return toggle +} diff --git a/src/modules/aux/index.js b/src/modules/aux/index.js index c52b890..b59c312 100644 --- a/src/modules/aux/index.js +++ b/src/modules/aux/index.js @@ -1,9 +1,13 @@ import lasso from './lasso' +import lassoWidget from './lasso-widget' import multiPlotZoom from './multi-plot-zoom' import plotZoom from './plot-zoom' +import datatoggle from './data-toggle' +import selectFilter from './select-filter' + let aux = { - lasso, plotZoom, multiPlotZoom + lasso, plotZoom, multiPlotZoom, datatoggle, selectFilter, lassoWidget } -export {lasso, plotZoom, multiPlotZoom} +export {lasso, plotZoom, multiPlotZoom, datatoggle, selectFilter, lassoWidget} export default aux diff --git a/src/modules/aux/lasso-widget.js b/src/modules/aux/lasso-widget.js new file mode 100644 index 0000000..c90eaca --- /dev/null +++ b/src/modules/aux/lasso-widget.js @@ -0,0 +1,604 @@ +import utils from '../utils/index.js' +import groupingSpacer from '../grouping-spacer'; +import CF from '../color-function'; +import TTip from '../tooltip'; +import lasso from './lasso'; +import '../d3-prototypes'; + +export default function lassoWidget( selection ) { + var + namespace = 'd3sm-lasso', + selection = selection, + svg, + chartContainer, + objectContainer, + objectClass, + lassoLine = d3.line() + .x(function(d, i){return d[0]}) + .y(function(d, i){return d[1]}) + .curve(d3.curveLinearClosed), + path, + colorFunction = CF(), + maxNumberOfGroups, + dataExtractor, + xScale, + yScale + + function onError(msg, style){ + console.log(msg, style) + } + // setup the container + setupDataselectContainer() + + lassoWidget.objectClass = function(_){return arguments.length ? (objectClass='.'+_.replace('.',''), lassoWidget) : objectClass} + lassoWidget.svg = function(_){return arguments.length ? (svg=_, lassoWidget) : svg} + lassoWidget.submit = function(_){return arguments.length ? (submit=_, lassoWidget) : submit} + lassoWidget.maxNumberOfGroups = function(_){return arguments.length ? (maxNumberOfGroups=_, lassoWidget) : maxNumberOfGroups} + lassoWidget.onError = function(_){return arguments.length ? (onError=_, lassoWidget) : onError} + lassoWidget.objectContainer = function(_){return arguments.length ? (objectContainer=_, lassoWidget) : objectContainer} + lassoWidget.chartContainer = function(_){return arguments.length ? (chartContainer=_, lassoWidget) : chartContainer} + lassoWidget.dataExtractor = function(_){return arguments.length ? (dataExtractor=_, lassoWidget) : dataExtractor} + lassoWidget.xScale = function(_){return arguments.length ? (xScale=_, lassoWidget) : xScale} + lassoWidget.yScale = function(_){return arguments.length ? (yScale=_, lassoWidget) : yScale} + + function styles() { + var s = d3.select("html").select("style."+namespace+'lasso-widget') + if (s.empty()) { + d3.select("html").append("style") + .classed(namespace+'lasso-widget', true) + .html( + "."+utils.str.hypenate(namespace, "data-table") + "{\ + height:100px;\ + overflow:auto;\ + }" + ) + } + } + + + function lassoWidget() { + styles() + + } + + function submit(data) { + console.log(data) + } + + function plusTab(tabList) { + var tab = utils.sel.safeSelect(tabList, 'li', utils.str.hypenate(namespace, 'plus-tab')) + .classed('ml-auto', true) + .classed('nav-item', true) + .on('click', makeNewGroup), + + anchor = utils.sel.safeSelect(tab, 'a', 'nav-link'), + icon = utils.sel.safeSelect(anchor, 'i', 'fa') + .classed('fa-plus fa-2x text-success', true) + } + + function sendTab(tabList) { + var tab = utils.sel.safeSelect(tabList, 'li', utils.str.hypenate(namespace, 'send-tab')) + .classed('ml-auto', true) + .classed('nav-item', true) + .on('click', clickSend) + , + + anchor = utils.sel.safeSelect(tab, 'a', 'nav-link'), + icon = utils.sel.safeSelect(anchor, 'i', 'fa') + .classed('fa-paper-plane-o fa-2x text-primary', true) + } + + function clickSend() { + var data = gatherDataLists() + submit(data) + } + + function closeTab(tabList) { + var tab = utils.sel.safeSelect(tabList, 'li', utils.str.hypenate(namespace, 'close-tab')) + .classed('ml-auto', true) + .classed('nav-item', true) + .on('click', function(){ + var m = d3.mouse(d3.select("html").node()) + selection.select('div.card').style("position", 'relative') + .transition().duration(1000) + .ease(d3.easeBack) + .style('left', window.outerWidth +'px') + .remove() + }) + , + + anchor = utils.sel.safeSelect(tab, 'a', 'nav-link'), + icon = utils.sel.safeSelect(anchor, 'i', 'fa') + .classed('fa-window-close-o fa-2x text-danger', true) + } + + + + function setupDataselectContainer() { + var + card = utils.sel.safeSelect(selection, 'div', 'card'), + cardHeader = utils.sel.safeSelect(card, 'div', 'card-header'), + + tabList = utils.sel.safeSelect(cardHeader, 'ul', 'nav') + .classed('nav-tabs card-header-tabs', true) + .classed(utils.str.hypenate(namespace, 'tab-list'), true) + .attr('role', 'tablist'), + + cardBody = utils.sel.safeSelect(card, 'div', 'card-body'), + tabContent = utils.sel.safeSelect(cardBody, 'div', 'tab-content') + .classed(utils.str.hypenate(namespace, 'tab-content'), true), + + // leftTabs = utils.sel.safeSelect(tabList, 'div', 'nav mr-auto left-aligned-tabs') + rightTabs = utils.sel.safeSelect(tabList, 'div', 'right-aligned-tabs').classed('nav ml-auto ', true) + + plusTab(rightTabs) + sendTab(rightTabs) + closeTab(rightTabs) + var + defaultTab = utils.sel.safeSelect(tabContent, 'div', 'tab-pane') + .classed(utils.str.hypenate(namespace,'default-tab'), true) + .classed("active", true) + .classed('text-left', true), + + p = utils.sel.safeSelect(defaultTab, 'div') + .html( + "Click to add a new group.
"+ + "Click to submit for re-analysis.
"+ + "Click to close the dataselect widget." + ) + // .text('Click the green plus to add a new group.') + } + + + function makeRemoveButton(paneBtnList) { + var + btn = utils.sel.safeSelect(paneBtnList, 'button', 'remove-btn') + .classed('btn btn-danger', true) + .on('click', removeBtnClickToRemove), + i = utils.sel.safeSelect(btn, 'i', 'fa').classed('fa-trash-o', true), + s = utils.sel.safeSelect(btn, 'span').text('Remove group') + return btn + } + + function removeBtnClickToRemove(d, i) { + var + tab = selection.select('#'+utils.str.hypenate(namespace,'tab',d)), + pane = selection.select('#'+utils.str.hypenate(namespace,'tab','pane',d)) + + tab.remove() + pane.remove() + + showRemainingPanes() + updateTabAndPaneNumbers() + } + + + function togglePaneById(id) { + var panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')) + panes.selectAll('div.tab-pane') + .each(function(d, i){ + d3.select(this).classed('active show', d3.select(this).attr('id') == id) + }) + } + + function insertTab(tabs, n) { + + // var li = tabs.insert("li", ':nth-child('+(n)+')') + var li = tabs.insert("li", '.right-aligned-tabs') + .classed('nav-item', true) + .classed('alert', true) + .classed('alert-secondary', true) + .classed('alert-dismissable', true) + .classed('fade', true) + .classed('show', true) + // .classed('mr-auto', true) + .attr("role", 'alert') + .attr("id", utils.str.hypenate(namespace,'tab',n)) + .classed(utils.str.hypenate(namespace,'tab'), true) + + var a = utils.sel.safeSelect(li, 'a') + .attr('data-toggle', 'tab') + .text('Group ' + n) + .attr('href', '#'+utils.str.hypenate(namespace,'tab','pane',n)) + .on('dblclick', function(d, i){ + var t = d3.select(this) + t.attr('contenteditable', true) + d3.select(t.node().parentNode) + .classed('alert-secondary', false) + .classed('alert-warning', true) + + // d3.select("html").on("click", dispatchBlur) + // + // function dispatchBlur(d, i) { + // if (d3.event.target != d3.select(this)) { + // d3.select(this).dispatch("blur") + // } + // } + + + }) + .on('blur', function(d, i){ + + var t = d3.select(this) + t.attr('contenteditable', false) + d3.select(t.node().parentNode) + .classed('alert-secondary', true) + .classed('alert-warning', false) + + }) + .on('input', function(d, i){ + var t = d3.select(this) + var txt = t.text() + d3.select(t.attr('href')).select("p.lead").text(txt) + }) + + + return li + } + + function makeTabPane(panes, n) { + var pane = panes.append('div') + .datum(n) + .attr('role', 'tabpanel') + .classed('tab-pane', true) + .classed('text-left', true) + .classed(utils.str.hypenate(namespace,'tab','pane'), true) + .attr('id', utils.str.hypenate(namespace,'tab','pane',n)) + return pane + } + + function populatePane(pane, n) { + var + lead = utils.sel.safeSelect(pane, 'p', 'lead').text('Group '+n), + + tableContainer = utils.sel.safeSelect(pane, 'div', 'table-responsive') + .attr("class", utils.str.hypenate(namespace, "data-table")), + + table = utils.sel.safeSelect(tableContainer, "table", "table") + .classed("table-sm", true).classed("table-hover", true), + + caption = utils.sel.safeSelect(table, "caption", "caption").html("List of selected"), + + btns = utils.sel.safeSelect(pane, 'div', 'text-right'), + + cN = n % colorFunction.colors().length + if (n % 2 != 0) { cN = (colorFunction.colors().length - 1) - cN } + + + + + + var lassoBtn = makeLassoButton(btns) + var currentLasso = makeLassoFunction(n, colorFunction.colors()[cN]) + var clearBtn = makeClearButton(btns) + + bindButtons(lassoBtn, clearBtn, currentLasso, table) + + var removeBtn = makeRemoveButton(btns) + + lead.on("mouseover", function(){ + currentLasso.draw() + }) + lead.on("mouseout", function(){ + currentLasso.remove() + }) + + } + + function makeLassoButton(btns) { + var btn = utils.sel.safeSelect(btns, 'button', 'lasso-btn') + .classed('btn btn-info', true) + .classed(namespace, true) + // .datum([lasso]) + var i = utils.sel.safeSelect(btn, 'i', 'fa').classed('fa-hand-pointer-o', true) + var s = utils.sel.safeSelect(btn, 'span').text('Lasso select') + return btn + + } + + function makeClearButton(btns) { + var btn = utils.sel.safeSelect(btns, 'button', 'clear-btn') + .classed('btn btn-light', true) + .classed(namespace, true) + var i = utils.sel.safeSelect(btn, 'i', 'fa').classed('fa-eraser', true) + var s = utils.sel.safeSelect(btn, 'span').text('Clear selection') + return btn + } + + function makeLassoFunction(instance, color) { + var currentLasso = lasso() + .namespace(namespace) + .svg(svg) + .objectClass(objectClass) + .chartContainer(chartContainer) + .objectContainer(objectContainer) + .instance(instance) + .color(color) + .yScale(yScale) + .xScale(xScale) + + return currentLasso + } + + function bindButtons(lassoBtn, clearBtn, currentLasso, table) { + currentLasso.eventCatcher(lassoBtn) + lassoBtn.node().addEventListener("click", lassoBtnToggle) + + lassoBtn.datum([currentLasso]) + .on(utils.str.hypenate(namespace,'render'), function(){ + d3.select(this).datum()[0].draw() + }) + .on(utils.str.hypenate(namespace,"drag"), function(las){ + var nodes = chartContainer.selectAll(".in-lasso-"+las[0].instance()).nodes() + var tableData = nodes.map(function(d, i){ + var extracted = dataExtractor(d3.select(d).datum()) + extracted["__node"] = d + return extracted + }) + + + makeTable(table, tableData, currentLasso) + }) + + clearBtn.on('click', function(){ + currentLasso.allPoints([]) + currentLasso.currentPoints([]) + lassoBtn.dispatch(utils.str.hypenate(namespace,"drag")) + + }) + + } + + function lassoBtnToggle() { + var that = d3.select(this) + var las = that.datum()[0] + + las.toggle() + var activeQ = las.activeQ() + + if (las.activeQ()) { + that.classed('btn-info', !activeQ) + that.classed('btn-warning', activeQ) + that.select("span").text("Lasso select (active)") + selection.selectAll("."+namespace+".lasso-btn").dispatch(utils.str.hypenate(namespace,'render')) + d3.select("html").node().addEventListener('mousedown', monitorLassoButtonState) + } else { + that.classed('btn-info', !activeQ) + that.classed('btn-warning', activeQ) + that.select("span").text("Lasso select") + d3.select("html").node().removeEventListener('mousedown', monitorLassoButtonState) + } + + function monitorLassoButtonState(event) { + /* + activeLasso stops event stopPropagation, so this event will not register in + that case. Thus we only need to ensure that the usre is clicking on the lasso button + otherwise, de-spawn lasso. + */ + if ( + event.target != that.node() && + event.target != that.select("span").node() && + event.target != that.select("i").node() + ) { + // event.preventDefault() + // event.stopPropagation() + las.toggle(false) + that.classed('btn-info', true) + that.classed('btn-warning', false) + that.select("span").text("Lasso select") + // console.log(that, that.node()) + // that.dispatch("focusout") + } + } + + } + + + function updateTableHeaderColumns(headR, tableData) { + var headerKeys = d3.keys(tableData[0]).filter(k=>k!="__node") + if (headerKeys.length > 0) { + // headerKeys = ["remove"].concat(headerKeys) + headerKeys.push("remove") + } + + var headerCols = headR.selectAll("th") + + headerCols = headerCols.data(headerKeys) + headerCols.exit().remove() + headerCols = headerCols.merge(headerCols.enter().append("th").attr("scope","col")) + .text(function(d, i){return d}) + + return headerKeys + } + + function updateTableRows(body, tableData) { + var bodyRows = body.selectAll("tr") + + bodyRows = bodyRows.data(tableData) + bodyRows.exit().remove() + bodyRows = bodyRows.merge(bodyRows.enter().append("tr")) + + return bodyRows + } + + function updateTableRowColumns(cols, headerKeys, rowData, tableData, lasso, table) { + cols = cols.data(headerKeys) + cols.exit().remove() + cols = cols.merge(cols.enter().append("td")) + + cols.html(function(d, i){ + if (d != "remove") { return rowData[d] } + return "" + }).classed("text-left", function(d, i){ + if (d != "remove") { return false } + return true + }) + // .attr("scope",function(d, i){ + // if (d != "remove") { return false } + // return true + // }) + + cols.select("i.fa-close").on("click", function(d, i){ + var node = rowData["__node"], + n = d3.select(node) + n.classed("in-lasso", false) + n.classed("in-lasso-"+lasso.instance(), false) + + tableData.map(function(dd, j){ + if (dd["__node"] == node) { + tableData.splice(j, 1) + makeTable(table, tableData, lasso) + } + }) + + }) + } + + function makeTable(table, tableData, lasso) { + var + head = utils.sel.safeSelect(table, "thead"), + headR = utils.sel.safeSelect(head, "tr"), + body = utils.sel.safeSelect(table, "tbody"), + + headerKeys = updateTableHeaderColumns(headR, tableData), + + bodyRows = updateTableRows(body, tableData) + + bodyRows.each(function(rowData, i){ + var t = d3.select(this) + var cols = t.selectAll("td") + + updateTableRowColumns(cols, headerKeys, rowData, tableData, lasso, table) + + t.on("mouseover", function(d, i) { + lasso.applyObjectAttributes(d3.select(d["__node"]),true) + }) + .on("mouseout", function(d, i) { + lasso.applyObjectAttributes(d3.select(d["__node"]),false) + }) + }) + + + } + + + + + + + + function makeNewGroup(d, i) { + + var tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')), + panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')), + n = newGroupNumber() + + if (tabs.selectAll('.'+utils.str.hypenate(namespace,'tab')).size() == maxNumberOfGroups) { + onError('only '+maxNumberOfGroups+' allowed.', 'warning') + return + } + + var + tab = insertTab(tabs, n), + pane = makeTabPane(panes, n) + + populatePane(pane, n) + togglePaneById(pane.attr('id')) + + } + + function newGroupNumber() { + var tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list'))//, + var + n = tabs.selectAll('li').size() - 3, // minus 1 for plus tab + s = 'Group ' + n, + gs = [] + tabs.each(function(d, i){ return gs.push(d3.select(this).select("a").text()) }) + while (gs.includes(s)) { n+=1; s = 'Group ' + n; } + return n + } + + function updateTabAndPaneNumbers() { + var + tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')), + panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')) + + tabs = tabs.selectAll('.'+utils.str.hypenate(namespace,'tab')) + panes = panes.selectAll('.'+utils.str.hypenate(namespace,'tab', 'pane')) + + tabs.each(function(d, i){ + d3.select(this).datum(i) + .attr("id", utils.str.hypenate(namespace,'tab',i)) + .select('a') + .attr('href', '#'+utils.str.hypenate(namespace,'tab','pane',i)) + .text(function(dd, ii){ + var curText = d3.select(this).text(); + if (curText.split(' ')[0] == 'Group') { + return 'Group ' + i + } + return curText + }) + }) + + panes.each(function(d, i){ + d3.select(this).datum(i) + .attr('id', utils.str.hypenate(namespace,'tab','pane',i)) + utils.sel.safeSelect(d3.select(this), 'p', 'lead') + .text(function(dd, ii){ + var curText = d3.select(this).text(); + if (curText.split(' ')[0] == 'Group') { + return 'Group ' + i + } + return curText + }) + utils.sel.safeSelect(d3.select(this), 'button', 'remove-btn') + .on('click', removeBtnClickToRemove) + }) + + } + + function gatherDataLists() { + var tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')) + var panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')) + var tables = panes.selectAll('.'+utils.str.hypenate(namespace, "data-table")) + var data = {} + + var textGroups = tabs.selectAll('li.'+utils.str.hypenate(namespace,'tab') + ' > a') + .nodes().map(function(d, i){return d3.select(d).text()}) + + textGroups.map(function(e, i){ + data[e] = [] + }) + + + tables.each(function(d, i){ + var table = d3.select(this).select("tbody") + data[textGroups[i]] = table.selectAll('tr').data() + }) + + return data + } + + function showRemainingPanes() { + var + tabs = selection.select('.'+utils.str.hypenate(namespace, 'tab-list')), + panes = selection.select('.'+utils.str.hypenate(namespace, 'tab-content')), + remainingTabs = tabs.selectAll('.'+utils.str.hypenate(namespace,'tab')) + + if (remainingTabs.size() == 0) { + panes.select('.'+utils.str.hypenate(namespace,'default-tab')) + .classed("active", true) + .classed('text-left', true) + } + else { + var lastTab = remainingTabs.nodes()[remainingTabs.size()-1], + lastPaneId = d3.select(lastTab).select('a').attr('href') + panes.select(lastPaneId) + .classed("active", true) + .classed('text-left', true) + } + } + + + return lassoWidget +} diff --git a/src/modules/aux/lasso.js b/src/modules/aux/lasso.js index d3bbb24..78e3049 100644 --- a/src/modules/aux/lasso.js +++ b/src/modules/aux/lasso.js @@ -4,13 +4,13 @@ import CF from '../color-function'; import TTip from '../tooltip'; import '../d3-prototypes'; -// function getTranslation(selection){ -// var transform = selection.attr('transform') -// var [junk, xy] =transform.split('translate(') -// var [x, y] = xy.split(',') -// y, junk = y.split(')') -// return [parseFloat(x), parseFloat(y)] -// } +function getTranslation(selection){ + var transform = selection.attr('transform') + var [junk, xy] =transform.split('translate(') + var [x, y] = xy.split(',') + y, junk = y.split(')') + return [parseFloat(x), parseFloat(y)] +} export default function lasso( selection ) { var @@ -112,8 +112,8 @@ export default function lasso( selection ) { function toggle(state) { // use optional param to set state, otherwise toggle state activeQ = (state!=undefined) ? state : !activeQ - chartOffset = utils.sel.getTranslation(chartContainer) - objectsOffset = utils.sel.getTranslation(objectContainer) + chartOffset = getTranslation(chartContainer) //utils.sel.getTranslation(chartContainer) + objectsOffset = getTranslation(objectContainer) //utils.sel.getTranslation(objectContainer) if (activeQ) { svg.node().addEventListener('mousedown', render, true) @@ -125,8 +125,8 @@ export default function lasso( selection ) { } function draw() { - chartOffset = utils.sel.getTranslation(chartContainer) - objectsOffset = utils.sel.getTranslation(objectContainer) + chartOffset = getTranslation(chartContainer) //utils.sel.getTranslation(chartContainer) + objectsOffset = getTranslation(objectContainer) //utils.sel.getTranslation(objectContainer) var container = utils.sel.safeSelect(objectContainer, 'g', 'lasso-container') var paths = container.selectAll('path[instance="'+instance+'"]') diff --git a/src/modules/aux/select-filter.js b/src/modules/aux/select-filter.js new file mode 100644 index 0000000..288eb41 --- /dev/null +++ b/src/modules/aux/select-filter.js @@ -0,0 +1,110 @@ +import utils from '../utils/index.js' + +export default function selectFilter(selection) { + + var + data, + namespace = 'd3sm-select-filter', + selectionName = 'Select options:', + defaultValue = undefined + + + + + var lastValue = undefined + + selectFilter.data = function(_) { return arguments.length ? (data = _, selectFilter) : data} + selectFilter.namespace = function(_) { return arguments.length ? (namespace = _, selectFilter) : namespace} + selectFilter.selectionName = function(_) { return arguments.length ? (selectionName = _, selectFilter) : selectionName} + selectFilter.defaultValue = function(_) { return arguments.length ? (defaultValue = _, selectFilter) : defaultValue} + selectFilter.currentOption = currentOption + + function selectFilter() { + var + container = utils.sel.safeSelect(selection, 'div', 'input-group').classed(utils.str.hypenate(namespace,'container'),true), + + selectPrepend = utils.sel.safeSelect(container, 'div', 'select-prepend').classed('input-group-prepend', true), + selectPrependSpan = utils.sel.safeSelect(selectPrepend, 'span', 'input-group-text').text(selectionName), + + select = utils.sel.safeSelect(container, 'select', 'custom-select').classed(utils.str.hypenate(namespace,'select'),true), + + selectAppend = utils.sel.safeSelect(container, 'div', 'select-append').classed('input-group-prepend', true), + selectAppendButton = utils.sel.safeSelect(selectAppend, 'a', 'filter-button').classed('btn btn-outline-secondary', true), + filterButtonIcon = utils.sel.safeSelect(selectAppendButton, 'i', 'fa fa-filter'), + + inputGroup = utils.sel.safeSelect(container, 'div', 'filter-input-group').classed('input-group',true).classed('d-none', true), + inputPrepend = utils.sel.safeSelect(inputGroup, 'div', 'input-group-prepend'), + inputPrependSpan = utils.sel.safeSelect(inputPrepend, 'span', 'input-group-text').classed('search-button', true), + inputPrependSpanIcon = utils.sel.safeSelect(inputPrependSpan,'i','fa fa-search'), + + input = utils.sel.safeSelect(inputGroup, 'input', 'form-control').attr('placeholder', 'all').attr('type', 'text'), + inputAppend = utils.sel.safeSelect(inputGroup, 'div', 'input-group-append'), + inputAppendButton = utils.sel.safeSelect(inputAppend, 'a', 'close-button').classed('btn btn-outline-secondary', true), + inputAppendButtonIcon = utils.sel.safeSelect(inputAppendButton, 'i', 'fa fa-close') + + + var keys = d3.keys(data), + options = select.selectAll('option') + + options = options.data(d3.keys(data)) + options = options.merge(options.enter().append('option')) + .attr('value', function(d, i){return d}) + .text(function(d, i){return d}) + + var + filterButton = selectAppendButton, + closeButton = inputAppendButton + + filterButton.on('click', function(d, i){ + var currentStyle = inputGroup.classed('d-none') + inputGroup.classed('d-none', !currentStyle) + }) + + closeButton.on('click', function(d, i){ + input.property('value', '').dispatch('input') + }) + + input.on('input', function(d, i){ + var + val = input.property('value'), + reg = new RegExp(val, 'gi'), + use + + if (val == '') {use = keys} + else { + use = [] + d3.keys(data).map(function(option, j){ + var match = option.match(reg) + if (match == null || match.join('') == '') {} + else { use.push(option) } + }) + } + + options = select.selectAll('option') + options = options.data(use) + options.exit().remove() + options = options.merge(options.enter().append('option')) + .attr('value', function(d, i){return d}) + .text(function(d, i){return d}) + + var current = currentOption() + if (lastValue != current) { + lastValue = current + select.dispatch('change') + } + }) + + + } + + function currentOption() { + var val = selection.select("select").property('value') + return val == undefined || val == '' + ? defaultValue == undefined + ? d3.keys(data)[0] + : defaultValue + : val + } + + return selectFilter +} diff --git a/src/modules/charts/box-whisker.js b/src/modules/charts/box-whisker.js index 55e0638..ee7121f 100644 --- a/src/modules/charts/box-whisker.js +++ b/src/modules/charts/box-whisker.js @@ -18,7 +18,7 @@ import TTip from '../tooltip'; * @namespace boxwhisker * @returns {function} boxwhisker */ -export function boxwhisker( selection ) { +export default function boxwhisker( selection ) { var /** * Data to plot. Assumed to be a object, where each key corresponds to a box diff --git a/src/modules/charts/index.js b/src/modules/charts/index.js index 05d8b08..7f9cc56 100644 --- a/src/modules/charts/index.js +++ b/src/modules/charts/index.js @@ -1,14 +1,15 @@ import scatter from './scatter' import bar from './bar' import bubble from './bubble-heatmap' +import boxwhisker from './box-whisker' import heatmap from './heatmap' import violin from './violin' import {neededViolinValues} from './violin' import upset from './upset' let charts = { - scatter, bar, bubble, heatmap, violin, neededViolinValues, upset + scatter, bar, bubble, boxwhisker, heatmap, violin, neededViolinValues, upset } -export {scatter, bar, bubble, heatmap, violin, neededViolinValues, upset} +export {scatter, bar, bubble, boxwhisker, heatmap, violin, neededViolinValues, upset} export default charts diff --git a/src/modules/charts/violin.js b/src/modules/charts/violin.js index 564e493..ca6651b 100644 --- a/src/modules/charts/violin.js +++ b/src/modules/charts/violin.js @@ -1,3 +1,4 @@ +import utils from '../utils/index.js'; import groupingSpacer from '../grouping-spacer'; import CF from '../color-function'; import TTip from '../tooltip'; @@ -682,12 +683,16 @@ export default function violin( selection ) { // augment valus - violinKeys.map(function(vk, i){ calcValues(vk, data) }) + violinValues = {} + violinKeys.map(function(vk, i){ + let v = calcValues(vk, data) + violinValues[vk] = v + }) var numberOfObjects = violinKeys.length - var min = [].concat(...violinKeys.map(function(k, i){return data[k].utils.math.quartiles[quartileKeys[0]]})) - var max = [].concat(...violinKeys.map(function(k, i){return data[k].utils.math.quartiles[quartileKeys[quartileKeys.length - 1]]})) + var min = [].concat(...violinKeys.map(function(k, i){return data[k].quartiles[quartileKeys[0]]})) + var max = [].concat(...violinKeys.map(function(k, i){return data[k].quartiles[quartileKeys[quartileKeys.length - 1]]})) var extent = [Math.min(...min) - domainPadding, Math.max(...max) + domainPadding] // console.log(extent, violinValues, ordered) @@ -754,9 +759,9 @@ export default function violin( selection ) { quarts = utils.sel.safeSelect(t, 'g', 'quarts'), lq3 = utils.sel.safeSelect(quarts, 'line', 'q3'), lq1 = utils.sel.safeSelect(quarts, 'line', 'q1'), - q3 = currentData.utils.math.quartiles[quartileKeys[3]], - q2 = currentData.utils.math.quartiles[quartileKeys[2]], - q1 = currentData.utils.math.quartiles[quartileKeys[1]] + q3 = currentData.quartiles[quartileKeys[3]], + q2 = currentData.quartiles[quartileKeys[2]], + q1 = currentData.quartiles[quartileKeys[1]] t.attr('transform', horizontalQ ? 'translate('+objectSize / 2+',0)' : 'translate(0,'+objectSize / 2+')' ) // draw curve @@ -873,11 +878,11 @@ export default function violin( selection ) { tooltip() if (tooltip.values() == undefined) { tooltip.values([ - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] }, - function(currentData, tooltipKey){ return currentData['utils.math.quartiles'][tooltipKey] } + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] }, + function(currentData, tooltipKey){ return currentData['quartiles'][tooltipKey] } ]) } @@ -1036,9 +1041,10 @@ export function neededViolinValues() { violinData.binned = binned; violinData.frequencies = frequencies violinData.contour = violinContourPoints - violinData.utils.math.quartiles = pointQuartiles + violinData.quartiles = pointQuartiles violinData.pointKeys = violinPointsKeys violinData.pointValues = violinPointsValues + return violinData } return calculateViolinValues diff --git a/src/modules/d3-prototypes.js b/src/modules/d3-prototypes.js index 3033b24..13c50e7 100644 --- a/src/modules/d3-prototypes.js +++ b/src/modules/d3-prototypes.js @@ -40,7 +40,7 @@ d3.mouse.absolute = function() { d3.selection.prototype.absolutePosition = function() { var element = this.node(); var elementPosition = element.getBoundingClientRect(); - var containerSVG = getContainingSVG(element) + var containerSVG = utils.sel.getContainingSVG(element) var svgPosition = containerSVG.getBoundingClientRect(); return { diff --git a/src/modules/utils/math.js b/src/modules/utils/math.js index eac6234..824475f 100644 --- a/src/modules/utils/math.js +++ b/src/modules/utils/math.js @@ -1,3 +1,4 @@ +import {total} from './array.js' /** * Rounds decimals of number to precision * @param {number} number diff --git a/src/modules/utils/misc.js b/src/modules/utils/misc.js index ac8344f..cbc3dad 100644 --- a/src/modules/utils/misc.js +++ b/src/modules/utils/misc.js @@ -1,4 +1,5 @@ - +import {safeSelect} from './selections.js' +import {hypenate} from './strings.js' export function resizeDebounce(f, wait) { var resize = debounce(function(){f()},wait) window.addEventListener('resize', resize) @@ -96,22 +97,22 @@ export function setupStandardChartContainers( - container = d3sm.safeSelect(selection, 'svg', namespace) + container = safeSelect(selection, 'svg', namespace) .style('width', svgSpace.w+'px') .style('height', svgSpace.h+'px') - var axes = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'axes')) + var axes = safeSelect(container, 'g', hypenate(namespace, 'axes')) - var leg = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'legend')) + var leg = safeSelect(container, 'g', hypenate(namespace, 'legend')) .attr('transform', "translate("+legRect.x+","+legRect.y+")") - var plot = d3sm.safeSelect(container, 'g', d3sm.hypenate(namespace, 'plot')) + var plot = safeSelect(container, 'g', hypenate(namespace, 'plot')) .attr('transform', "translate("+plotRect.x+","+plotRect.y+")") - var xAxis = d3sm.safeSelect(axes, 'g', d3sm.hypenate(namespace, 'x-axis')) + var xAxis = safeSelect(axes, 'g', hypenate(namespace, 'x-axis')) .attr('transform', "translate("+xAxisRect.x+","+xAxisRect.y+")") - var yAxis = d3sm.safeSelect(axes, 'g', d3sm.hypenate(namespace, 'y-axis')) + var yAxis = safeSelect(axes, 'g', hypenate(namespace, 'y-axis')) .attr('transform', "translate("+yAxisRect.x+","+yAxisRect.y+")") return { -- GitLab