diff --git a/data/csv.js b/data/csv.json similarity index 99% rename from data/csv.js rename to data/csv.json index f24ed1a..4a4fd27 100644 --- a/data/csv.js +++ b/data/csv.json @@ -1,4 +1,4 @@ -var CSV = [ +[ ["fspr","plnt","promo","terms","bill_cust_descr","ship_cust_descr","dsm","quota_rep_descr","director","billto_group","shipto_group","chan","chansub","chan_retail","part","part_descr","part_group","branding","majg_descr","ming_descr","majs_descr","mins_descr","segm","substance","fs_line","r_currency","r_rate","c_currency","c_rate","units","value_loc","value_usd","cost_loc","cost_usd","calc_status","flag","order_date","order_month","order_season","request_date","request_month","request_season","ship_date","ship_month","ship_season","version","iter","logid","tag","comment","module"], ["2102","152","NONE","1A","BWIC0001 - BWI COMPANIES INC","BLOO0017 - BLOOMING COLOR","10032","BRYAN HILL","Baggetta","BWI","BLOOMING COLOR","DRP","DRP","DRP","HZP3E100G18D050","HZP3E100G18D050 - E-10 3 STRAND HGR BLACK","HZP3E100","","110 - INJECTION","000 - NON BRANDED","108 - ACCESSORIES","A06 - HANGERS","Greenhouse","Plastic","41010","US","1.0000000000","US","1.0000000000","25600.0000000000","2099.2000000000","2099.20","1697.2800000000","1697.28","CLOSED","SHIPMENT","2020-06-08","01 - Jun","2021","2020-06-15","01 - Jun","2021","2020-07-09","02 - Jul","2021","15mo","actuals","1","Initial Build","don't undo","build_pool"], ["2211","152","NONE","1A","PAST0002 - PASTANCH LLC","PAST0002 - PASTANCH LLC","13028","RICHARD MEULE","Soltis","PASTANCH LLC","PASTANCH LLC","WHS","WHS","WHS","AMK12000G18D050","AMK12000G18D050 - 3G 1200 REG BM POT BLACK","AMK12000","","210 - BLOW MOLD","000 - NON BRANDED","104 - ROUND POTS AND TRAYS","A19 - NURSERY POTS >= 1 GAL","Nursery","Plastic","41010","US","1.0000000000","US","1.0000000000","24000.0000000000","9144.0000000000","9144.00","4936.0800000000","4936.08","OPEN","REMAINDER","2022-03-26","10 - Mar","2022","2022-04-28","11 - Apr","2022","2022-04-28","11 - Apr","2022","b22","copy","1","Initial Build","don't undo","build_pool"], diff --git a/index.html b/index.html index 5ca13ce..e49dfdb 100755 --- a/index.html +++ b/index.html @@ -2,490 +2,21 @@ - - - - +
- - - - + diff --git a/libraries/n.js b/libraries/n.js deleted file mode 100644 index 7813650..0000000 --- a/libraries/n.js +++ /dev/null @@ -1,172 +0,0 @@ -var N = -{ -ID:{ - Walk:0, - Instance:0 -}, -Create(inMeta) -{ - return { - ID:{ - Walk:0, - Instance:N.ID.Instance++ - }, - Meta:inMeta||{}, - Link:{} - }; -}, -Connect(inNodeMajor, inNodeMinor, inKey, inUnique) -{ - if(inUnique) // bail if the nodes are already connected - { - let check = N.Step(inNodeMajor, inKey, true); - if(check) - { - if(check.indexOf(inNodeMinor) !== -1) - { - return; - } - } - } - N.Step(inNodeMajor, inKey, true, true).push(inNodeMinor); - N.Step(inNodeMinor, inKey, false, true).push(inNodeMajor); -}, -Disconnect(inNodeMajor, inNodeMinor, inKey) -{ - let remove = (inArray, inMatch) => inArray.findIndex( (inMember, inIndex, inArray) => (inMember === inMatch) ? inArray.splice(inIndex, 1) : false ); - - // if no specific child was passed - if(inNodeMinor === null) - { - // get all the children - let check = N.Step(inNodeMajor, inKey); - if(!check){ return; } - - // go down to each child ... - check.forEach( inNodeMinor => - { - let connections = inNodeMinor.Link[inKey]; - remove( connections.Get, inNodeMajor); // ... and remove any reference to the parent - - // if after the remove operation, this child has no connections on inKey, scrub the key - if(!connections.Set.length && !connections.Get.length) - { - delete inNodeMinor.Link[inKey]; - } - }); - - // we just wiped out all outgoing connections to the parent, if incoming connections are empty too we can purge the key there as well - if(inNodeMajor.Link[inKey].Get.length == 0) - { - delete inNodeMajor.Link[inKey]; - } - return; - } - - // if no specific parent was passed - if(inNodeMajor === null) - { - // get all the parents - let check = N.Step(inNodeMinor, inKey, false); - if(!check){ return; } - - // go up to each parent ... - check.forEach( inNodeMajor => - { - let connections = inNodeMajor.Link[inKey]; - remove( connections.Set, inNodeMinor); // ... and remove any reference to the child - - // if after the remove operation, this parent has no connections on inKey, scrub the key - if( !connections.Set.length && !connections.Get.length ) - { - delete inNodeMajor.Link[inKey]; - } - }); - - // we just wiped out all incoming connections to the child, if outgoing connections are empty too we can purge the key there as well - if(inNodeMinor.Link[inKey].Set.length == 0) - { - delete inNodeMinor.Link[inKey]; - } - return; - } - - // if a specific parent and child were passed - if(inNodeMajor.Link[inKey].Set.length == 1) - { - delete inNodeMajor.Link[inKey]; - } - else - { - remove(inNodeMajor.Link[inKey].Set, inNodeMinor); - } - if(inNodeMinor.Link[inKey].Get.length == 1) - { - delete inNodeMinor.Link[inKey]; - } - else - { - remove(inNodeMinor.Link[inKey].Get, inNodeMajor); - } - -}, -Step(inNode, inKey, inForward, inForceCreate) -{ - let connectionGroup = inNode.Link[inKey]; - if(!connectionGroup) - { - if(inForceCreate === true) - { - inNode.Link[inKey] = connectionGroup = {Get:[], Set:[]}; - } - else - { - return false; - } - } - return (inForward === undefined || inForward === true) ? connectionGroup.Set : connectionGroup.Get; - -}, -Walk(inIterator, inNode, inKey, inForward, inTerminal) -{ - let array = N.Step(inNode, inKey, inForward); - - if(!array.length && inTerminal) - { - return inTerminal(inNode); - } - - for(let i=0; i=this._config.preview;if(o)f.postMessage({results:n,workerId:b.WORKER_ID,finished:a});else if(q(this._config.chunk)&&!t){if(this._config.chunk(n,this._handle),this._handle.paused()||this._handle.aborted())return void(this._halted=!0);n=void 0,this._completeResults=void 0}return this._config.step||this._config.chunk||(this._completeResults.data=this._completeResults.data.concat(n.data),this._completeResults.errors=this._completeResults.errors.concat(n.errors),this._completeResults.meta=n.meta),this._completed||!a||!q(this._config.complete)||n&&n.meta.aborted||(this._config.complete(this._completeResults,this._input),this._completed=!0),a||n&&n.meta.paused||this._nextChunk(),n}this._halted=!0},this._sendError=function(e){q(this._config.error)?this._config.error(e):o&&this._config.error&&f.postMessage({workerId:b.WORKER_ID,error:e,finished:!1})}}function l(e){var i;(e=e||{}).chunkSize||(e.chunkSize=b.RemoteChunkSize),u.call(this,e),this._nextChunk=n?function(){this._readChunk(),this._chunkLoaded()}:function(){this._readChunk()},this.stream=function(e){this._input=e,this._nextChunk()},this._readChunk=function(){if(this._finished)this._chunkLoaded();else{if(i=new XMLHttpRequest,this._config.withCredentials&&(i.withCredentials=this._config.withCredentials),n||(i.onload=y(this._chunkLoaded,this),i.onerror=y(this._chunkError,this)),i.open("GET",this._input,!n),this._config.downloadRequestHeaders){var e=this._config.downloadRequestHeaders;for(var t in e)i.setRequestHeader(t,e[t])}if(this._config.chunkSize){var r=this._start+this._config.chunkSize-1;i.setRequestHeader("Range","bytes="+this._start+"-"+r)}try{i.send()}catch(e){this._chunkError(e.message)}n&&0===i.status?this._chunkError():this._start+=this._config.chunkSize}},this._chunkLoaded=function(){4===i.readyState&&(i.status<200||400<=i.status?this._chunkError():(this._finished=!this._config.chunkSize||this._start>function(e){var t=e.getResponseHeader("Content-Range");if(null===t)return-1;return parseInt(t.substr(t.lastIndexOf("/")+1))}(i),this.parseChunk(i.responseText)))},this._chunkError=function(e){var t=i.statusText||e;this._sendError(new Error(t))}}function c(e){var i,n;(e=e||{}).chunkSize||(e.chunkSize=b.LocalChunkSize),u.call(this,e);var s="undefined"!=typeof FileReader;this.stream=function(e){this._input=e,n=e.slice||e.webkitSlice||e.mozSlice,s?((i=new FileReader).onload=y(this._chunkLoaded,this),i.onerror=y(this._chunkError,this)):i=new FileReaderSync,this._nextChunk()},this._nextChunk=function(){this._finished||this._config.preview&&!(this._rowCount=this._input.size,this.parseChunk(e.target.result)},this._chunkError=function(){this._sendError(i.error)}}function p(e){var r;u.call(this,e=e||{}),this.stream=function(e){return r=e,this._nextChunk()},this._nextChunk=function(){if(!this._finished){var e=this._config.chunkSize,t=e?r.substr(0,e):r;return r=e?r.substr(e):"",this._finished=!r,this.parseChunk(t)}}}function m(e){u.call(this,e=e||{});var t=[],r=!0,i=!1;this.pause=function(){u.prototype.pause.apply(this,arguments),this._input.pause()},this.resume=function(){u.prototype.resume.apply(this,arguments),this._input.resume()},this.stream=function(e){this._input=e,this._input.on("data",this._streamData),this._input.on("end",this._streamEnd),this._input.on("error",this._streamError)},this._checkIsFinished=function(){i&&1===t.length&&(this._finished=!0)},this._nextChunk=function(){this._checkIsFinished(),t.length?this.parseChunk(t.shift()):r=!0},this._streamData=y(function(e){try{t.push("string"==typeof e?e:e.toString(this._config.encoding)),r&&(r=!1,this._checkIsFinished(),this.parseChunk(t.shift()))}catch(e){this._streamError(e)}},this),this._streamError=y(function(e){this._streamCleanUp(),this._sendError(e)},this),this._streamEnd=y(function(){this._streamCleanUp(),i=!0,this._streamData("")},this),this._streamCleanUp=y(function(){this._input.removeListener("data",this._streamData),this._input.removeListener("end",this._streamEnd),this._input.removeListener("error",this._streamError)},this)}function r(g){var a,o,h,i=Math.pow(2,53),n=-i,s=/^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i,u=/(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))/,t=this,r=0,f=0,d=!1,e=!1,l=[],c={data:[],errors:[],meta:{}};if(q(g.step)){var p=g.step;g.step=function(e){if(c=e,_())m();else{if(m(),0===c.data.length)return;r+=e.data.length,g.preview&&r>g.preview?o.abort():p(c,t)}}}function v(e){return"greedy"===g.skipEmptyLines?""===e.join("").trim():1===e.length&&0===e[0].length}function m(){if(c&&h&&(k("Delimiter","UndetectableDelimiter","Unable to auto-detect delimiting character; defaulted to '"+b.DefaultDelimiter+"'"),h=!1),g.skipEmptyLines)for(var e=0;e=l.length?"__parsed_extra":l[r]),g.transform&&(s=g.transform(s,n)),s=y(n,s),"__parsed_extra"===n?(i[n]=i[n]||[],i[n].push(s)):i[n]=s}return g.header&&(r>l.length?k("FieldMismatch","TooManyFields","Too many fields: expected "+l.length+" fields but parsed "+r,f+t):r=i.length/2?"\r\n":"\r"}(e,i)),h=!1,g.delimiter)q(g.delimiter)&&(g.delimiter=g.delimiter(e),c.meta.delimiter=g.delimiter);else{var n=function(e,t,r,i,n){var s,a,o,h;n=n||[",","\t","|",";",b.RECORD_SEP,b.UNIT_SEP];for(var u=0;u=L)return R(!0)}else for(g=M,M++;;){if(-1===(g=a.indexOf(O,g+1)))return t||u.push({type:"Quotes",code:"MissingQuotes",message:"Quoted field unterminated",row:h.length,index:M}),w();if(g===i-1)return w(a.substring(M,g).replace(_,O));if(O!==z||a[g+1]!==z){if(O===z||0===g||a[g-1]!==z){var y=E(-1===m?p:Math.min(p,m));if(a[g+1+y]===D){f.push(a.substring(M,g).replace(_,O)),a[M=g+1+y+e]!==O&&(g=a.indexOf(O,M)),p=a.indexOf(D,M),m=a.indexOf(I,M);break}var k=E(m);if(a.substr(g+1+k,n)===I){if(f.push(a.substring(M,g).replace(_,O)),C(g+1+k+n),p=a.indexOf(D,M),g=a.indexOf(O,M),o&&(S(),j))return R();if(L&&h.length>=L)return R(!0);break}u.push({type:"Quotes",code:"InvalidQuotes",message:"Trailing quote on quoted field is malformed",row:h.length,index:M}),g++}}else g++}return w();function b(e){h.push(e),d=M}function E(e){var t=0;if(-1!==e){var r=a.substring(g+1,e);r&&""===r.trim()&&(t=r.length)}return t}function w(e){return t||(void 0===e&&(e=a.substr(M)),f.push(e),M=i,b(f),o&&S()),R()}function C(e){M=e,b(f),f=[],m=a.indexOf(I,M)}function R(e,t){return{data:t||!1?h[0]:h,errors:u,meta:{delimiter:D,linebreak:I,aborted:j,truncated:!!e,cursor:d+(r||0)}}}function S(){A(R(void 0,!0)),h=[],u=[]}function x(e,t,r){var i={nextDelim:void 0,quoteSearch:void 0},n=a.indexOf(O,t+1);if(t +{ + let styles = css` + position:realtive; + box-sizing: border-box; + padding: 10px; + color:black; + font-family:sans-serif; + + .Title + { + font-size:24px; + font-weight:100; + } + + .Section + { + padding:10px 0 10px 0; + + .Heading + { + display:inline-block; + color:#666; + font-family:sans-serif; + font-size:12px; + font-weight:900; + text-transform:uppercase; + } + .Group + { + display:inline-block; + padding:5px; + border-radius:5px; + margin:3px; + background:rgba(0, 0, 0, 0.3) + } + } + `; + + + let pivotColumns = N.Step(Pivot.Schema, "label")||[]; + let pivotColumnsUsed = N.Step(Pivot.Proto, "used-pivot")||[]; + + let sumColumns = N.Step(Pivot.Schema, "sum")||[]; + //let sumColumnsUsed = N.Step(Pivot.Proto, "used-sum")||[]; + + let indiciesPivot = pivotColumnsUsed.map(node=>node.Meta.Index); + let indiciesSum = sumColumns.map(node=>node.Meta.Index); + //let indiciesSum = sumColumnsUsed.map(node=>node.Meta.Index); + + let displayPivotsAll = html` +
+
Available Columns
+
+ ${pivotColumns.map( columnPivot => + { + let attributes = {}; + if(N.Step(columnPivot, "used-pivot", false)) + { + attributes.disabled = true; + } + else + { + attributes.onClick = e=> + { + N.Connect(Pivot.Proto, columnPivot, "used-pivot"); + Render(); + } + } + return html``; + })} +
+
+ `; + + let displayPivotsPending = null; + if(pivotColumnsUsed.length) + { + displayPivotsPending = html` +
+
Pending Pivot
+
+ ${pivotColumnsUsed.map(columnPivot=>html` + + `)} +
+ + +
+ `; + } + + return html` +
+
Create New Pivot
+ ${displayPivotsAll} + ${displayPivotsPending} +
+ `; +} + +let Section = props => +{ + let styles = css` + .Heading + { + padding:6px 0 6px 0; + color:#666; + font-weight:900; + font-size:12px; + text-transform:uppercase; + cursor:pointer; + + span + { + display:inline-block; + width:20px; + height:20px; + margin-right:10px; + border-radius:20px; + background:black; + color:white; + text-align:center; + } + } + .Heading:hover + { + color:black; + } + .Body + { + position:relative; + padding:10px 0 20px 30px; + &::before + { + content: " "; + display:block; + position:absolute; + top:-8px; + left:8px; + width:3px; + height:100%; + background:black; + } + } + `; + + let [openGet, openSet] = useState(false); + return html` +
+
openSet(!openGet)}> + ${openGet ? `−` : `+`} + ${props.label} +
+ ${ openGet ? html`
${props.children}
` : null } +
+ `; +} + +let ModificationsIcon = ({node}) => +{ + let modsUp = N.Step(node, "ModifyUp", false)||[]; + let modsDown = N.Step(node, "ModifyDown", false)||[]; + let modsAt = N.Step(node, "ModifyAt", false)||[]; + let modsOut = N.Step(node, "ModifyOut", false)||[]; + + var button = null; + if(modsAt.length) + { + button = html``; + } + else + { + button = html``; + } + + let padding = 7; + let icon = 0; + let styles = css` + position:relative; + display:inline-block; + vertical-align:middle; + width:${padding*2 + icon}px; + height:${padding*2 + icon}px; + margin:${padding*2}; + .Icon + { + position:absolute; + display:inline-block; + width:${padding*2 + icon}px; + height:${padding*2 + icon}px; + text-align:center; + font-size:9px; + font-family:sans-serif; + font-weight:900; + line-height:${padding*2 + icon}px; + + &::after + { + content:" "; + display:block; + position:absolute; + width:${icon}px; + height:${icon}px; + border:${padding}px solid transparent; + } + + &.Down + { + left:0; + bottom:100%; + &::after + { + top:100%; + border-top-color:green; + border-bottom:0px solid transparent; + } + } + &.At + { + top:0; + left:100%; + &::after + { + top:0; + right:100%; + border-right-color:red; + border-left:0px solid transparent; + } + } + &.Up + { + left:0; + top:100%; + &::after + { + bottom:100%; + border-bottom-color:orange; + border-top:0px solid transparent; + } + } + + &.Out + { + top:0; + right:100%; + &::after + { + top:0; + left:100%; + border-left-color:grey; + border-right:0px solid transparent; + } + } + } + + `; + + return html` +
+ ${modsDown.length ? html`
${modsDown.length}
` : null} + ${modsAt.length ? html`${modsAt.length}` : null} + ${modsUp.length ? html`${modsUp.length}` : null} + ${modsOut.length ? html`${modsOut.length}` : null} +
+ ${button} + `; + +}; + +let PivotBranch = props => +{ + let row = props.node.Meta.Row; + let displayCellsModify = row.map(column=>false); + props.node.Meta.IndexSum.forEach(i=> + { + displayCellsModify[i] = html``; + }); + displayCellsModify.forEach((cell, i)=> + { + if(!cell) + { + displayCellsModify[i] = html`${row[i]}` + } + }); + + let displayCells = (node, visible) => + { + let output = []; + node.Meta.Row.forEach((column, i)=> + { + if(visible[i]) + { + output.push( h("td", null, column) ); + } + } + ); + return output; + } + + let stylesLeaf = css` + background:#ddd; + color:#333; + `; + + return html` + + + + ${props.node.Meta.Label} + + + <${ModificationsIcon} node=${props.node}> + + ${displayCells(props.node, props.visible)} + + ${(N.Step(props.node, "Leaf")||[]).map(leaf => + { + return html` + + + <${ModificationsIcon} node=${leaf}> + ${displayCells(leaf, props.visible)} + + `; + } + )} + + `; +}; + +let PivotRoot = ({pivot}) => +{ + let labelsDefault = (N.Step(Pivot.Schema, "hidden")||[]).map(column => column.Meta.Index); + let labelsSum = (N.Step(Pivot.Schema, "sum")||[]).map(column => column.Meta.Index); + let labelsPivot = (N.Step(Pivot.Schema, "label")||[]).map(column => column.Meta.Index); + let labelsAll = (N.Step(Pivot.Schema, "all")||[]).map(column => column.Meta.Label); + + let labelsAllState = useState(labelsAll.map(column=>true)) + + let headersDisplay = []; + labelsAllState[0].forEach((visible, index)=> + { + if(visible) + { + headersDisplay.push(html`${labelsAll[index]}`); + } + } + ); + + let stylesRoot = css` + display:block; + box-sizing:border-box; + padding:15px; + font-family:sans-serif; + `; + let stylesHeading = css` + margin: 0 0 15px 0; + font-weight:0; + font-size:24px; + `; + + let modifiers = N.Step(pivot, "ModifyUp", false) || []; + let handleDelete = ()=> + { + Pivot.Delete(pivot); + Render(); + }; + + + let displayColumnGroup = (inAllLabels, inAllState, inIndicies) => + { + return html` +
+ + +
+
+ ${inIndicies.map((index) => html``)} +
`; + }; + + let rows = []; + N.ID.Walk++; + N.Walk(n=>rows.push(h(PivotBranch, {node:n, visible:labelsAllState[0]}, null)), pivot, "Hierarchy"); + + return html` +
+
${pivot.Meta.Label}
+ <${Section} key="settings" label=${`Settings`}> + + + <${Section} key="columns" label="Columns"> +

Unused

+ ${displayColumnGroup(labelsAll, labelsAllState, labelsDefault)} +

Summation

+ ${displayColumnGroup(labelsAll, labelsAllState, labelsSum)} +

Pivot

+ ${displayColumnGroup(labelsAll, labelsAllState, labelsPivot)} + + <${Section} key="tree" label=${"Tree"}> + + + + ${headersDisplay} + + + ${rows} +
LabelModifications
+ +
+ `; +}; + +let ElRoot = props => +{ + let pivots = N.Step(Pivot.Root, "Pivot")||[]; + return h("div", null, [ + h(PivotForm), + pivots.map(pivot=>h(PivotRoot, {key:pivot.Meta.Label, pivot})) + ]) +}; + +const Root = createRoot(document.querySelector("#app")); +const Render = () => Root.render(h(ElRoot)); +Render(); \ No newline at end of file diff --git a/src/n.js b/src/n.js new file mode 100644 index 0000000..c6225fc --- /dev/null +++ b/src/n.js @@ -0,0 +1,174 @@ +const N = +{ + ID:{ + Walk:0, + Instance:0 + }, + Create(inMeta) + { + return { + ID:{ + Walk:0, + Instance:N.ID.Instance++ + }, + Meta:inMeta||{}, + Link:{} + }; + }, + Connect(inNodeMajor, inNodeMinor, inKey, inUnique) + { + if(inUnique) // bail if the nodes are already connected + { + let check = N.Step(inNodeMajor, inKey, true); + if(check) + { + if(check.indexOf(inNodeMinor) !== -1) + { + return; + } + } + } + N.Step(inNodeMajor, inKey, true, true).push(inNodeMinor); + N.Step(inNodeMinor, inKey, false, true).push(inNodeMajor); + }, + Disconnect(inNodeMajor, inNodeMinor, inKey) + { + let remove = (inArray, inMatch) => inArray.findIndex( (inMember, inIndex, inArray) => (inMember === inMatch) ? inArray.splice(inIndex, 1) : false ); + + // if no specific child was passed + if(inNodeMinor === null) + { + // get all the children + let check = N.Step(inNodeMajor, inKey); + if(!check){ return; } + + // go down to each child ... + check.forEach( inNodeMinor => + { + let connections = inNodeMinor.Link[inKey]; + remove( connections.Get, inNodeMajor); // ... and remove any reference to the parent + + // if after the remove operation, this child has no connections on inKey, scrub the key + if(!connections.Set.length && !connections.Get.length) + { + delete inNodeMinor.Link[inKey]; + } + }); + + // we just wiped out all outgoing connections to the parent, if incoming connections are empty too we can purge the key there as well + if(inNodeMajor.Link[inKey].Get.length == 0) + { + delete inNodeMajor.Link[inKey]; + } + return; + } + + // if no specific parent was passed + if(inNodeMajor === null) + { + // get all the parents + let check = N.Step(inNodeMinor, inKey, false); + if(!check){ return; } + + // go up to each parent ... + check.forEach( inNodeMajor => + { + let connections = inNodeMajor.Link[inKey]; + remove( connections.Set, inNodeMinor); // ... and remove any reference to the child + + // if after the remove operation, this parent has no connections on inKey, scrub the key + if( !connections.Set.length && !connections.Get.length ) + { + delete inNodeMajor.Link[inKey]; + } + }); + + // we just wiped out all incoming connections to the child, if outgoing connections are empty too we can purge the key there as well + if(inNodeMinor.Link[inKey].Set.length == 0) + { + delete inNodeMinor.Link[inKey]; + } + return; + } + + // if a specific parent and child were passed + if(inNodeMajor.Link[inKey].Set.length == 1) + { + delete inNodeMajor.Link[inKey]; + } + else + { + remove(inNodeMajor.Link[inKey].Set, inNodeMinor); + } + if(inNodeMinor.Link[inKey].Get.length == 1) + { + delete inNodeMinor.Link[inKey]; + } + else + { + remove(inNodeMinor.Link[inKey].Get, inNodeMajor); + } + + }, + Step(inNode, inKey, inForward, inForceCreate) + { + let connectionGroup = inNode.Link[inKey]; + if(!connectionGroup) + { + if(inForceCreate === true) + { + inNode.Link[inKey] = connectionGroup = {Get:[], Set:[]}; + } + else + { + return false; + } + } + return (inForward === undefined || inForward === true) ? connectionGroup.Set : connectionGroup.Get; + + }, + Walk(inIterator, inNode, inKey, inForward, inTerminal) + { + let array = N.Step(inNode, inKey, inForward); + + if(!array.length && inTerminal) + { + return inTerminal(inNode); + } + + for(let i=0; i