From ed518f4868febbbe743a1dccad72369f12b8cd41 Mon Sep 17 00:00:00 2001 From: Jake Read <jake.read@cba.mit.edu> Date: Tue, 10 Dec 2019 15:09:40 -0500 Subject: [PATCH] global hunks --- bootstrap.js | 4 +- core/manager.js | 3 +- core/scope.js | 625 ++++------------------------------ hunks/hunks.js | 2 +- hunks/view.js | 10 +- scratch/old-defs.js | 734 ++++++++++++++++++++++++++++++++++++++++ scratch/old-view.js | 495 +++++++++++++++++++++++++++ style.css | 7 + view/blocks.js | 800 +++----------------------------------------- 9 files changed, 1361 insertions(+), 1319 deletions(-) create mode 100644 scratch/old-defs.js create mode 100644 scratch/old-view.js diff --git a/bootstrap.js b/bootstrap.js index 4910920..7a124fe 100644 --- a/bootstrap.js +++ b/bootstrap.js @@ -49,7 +49,9 @@ window.onload = () => { nrol.addHunk('view', 'tlview').then((vw) => { tlv = vw // manager wants view handle to render hunk DOM elements into plane ... - nrol.tlv = tlv + // there's only one of each, so just + window.tlv = tlv + window.nrol = nrol }).then(() => { // make ah graph, // outHunkIndex, outIndex, inHunkIndex, inIndex, debug diff --git a/core/manager.js b/core/manager.js index 404d564..80e4866 100644 --- a/core/manager.js +++ b/core/manager.js @@ -40,6 +40,7 @@ export default function Manager() { // we have hunks, let hunks = new Array() this.hunks = hunks + window.hunks = hunks // ohk we go global bigtime ok // we keep track of whether-or-not we have any connections ... this.isConnectedTo = false @@ -267,7 +268,7 @@ export default function Manager() { if (hunk.dom !== null && hunk.dom !== undefined) { // this means that the hunk called self.document(), and has ahn hunk.dom element, // a div container to render into ... - $(this.tlv.plane).append(hunk.dom) + $(window.tlv.plane).append(hunk.dom) } // end if-have-dom // run init code (apres state setup!) diff --git a/core/scope.js b/core/scope.js index 7648496..d560ec8 100644 --- a/core/scope.js +++ b/core/scope.js @@ -16,13 +16,12 @@ import cm from '../view/contextmenu.js' import dt from '../view/domtools.js' import blocks from '../view/blocks.js' +// scope is our UI tool: it draws *everything* export default function Scope(wrapper, tlv) { // toplevel scroll-around / playa let plane = $('<div>').addClass('plane').attr('id', 'NROLPLANE').get(0) - // HEADS UP: need these / somehow - ok: I think we start by figuring how to navigate between views / scopes / links ... + plane.handle = tlv // the dom->graph handle is to the view -> plane.handle.scope = this tlv.plane = plane - plane.handle = tlv let cs = 1 // our current scale $(plane).css('background', 'url("asset/bg.png")').css('width', '100px').css('height', '100px') $(wrapper).append(plane) @@ -209,67 +208,85 @@ export default function Scope(wrapper, tlv) { // and update those blocks only // wherein setting something in the view with (setUpdate) or whatever bubbles up the tree - this.check = () => { - // first off, if we wipe the whole thing, - // we don't have to handle anything else - if(tlv.hasCompleteRedraw){ - console.log("SCOPE run for complete redraw") - console.time('redraw-complete') - blocks.wipeContext(tlv) - for(let i = 0; i < tlv.defs.length; i ++){ - blocks.redrawDef(tlv.defs[i], plane) - tlv.defs[i].hasUpdate = false + let checkView = (view) => { + // first off, if we wipe the whole thing, + // we don't have to handle anything else + if(view.hasCompleteRedraw){ + console.log("SCOPE run for complete redraw") + console.time('redraw-complete') + blocks.wipeContext(view) + for(let i = 0; i < view.defs.length; i ++){ + blocks.redrawDef(view.defs[i]) + view.defs[i].hasUpdate = false + } + blocks.redrawContexWires(view) + view.hasConnectivityChange = false + view.hasDefUpdate = false + view.hasCompleteRedraw = false + console.timeEnd('redraw-complete') + return } - blocks.redrawContexWires(tlv) - tlv.hasConnectivityChange = false - tlv.hasDefUpdate = false - tlv.hasCompleteRedraw = false - console.timeEnd('redraw-complete') - return - } - // otherwise, we should first handle def updates - // (this also counts new definitions) - // eventually will do / switch this per ll item - // defs w/ updates are for: state items, name changes, - // and probably connection / rewires (add / sub inputs) - if (tlv.hasDefUpdate) { - console.log('SCOPE run for hasDefUpdate') - console.time('redraw-defupdate') - for (let i = 0; i < tlv.defs.length; i++) { - if (tlv.defs[i].hasUpdate) { - // for now, we just redraw the entire definition, whenever we have updates for it - blocks.redrawDef(tlv.defs[i], plane) - // ah yeah, so we also occasionally load defs in here w/o having their link-drawing - // so we should just draw links from outputs as well as from inputs, then - // want a clean way to do that next... - tlv.defs[i].hasUpdate = false + // otherwise, we should first handle def updates + // (this also counts new definitions) + // eventually will do / switch this per ll item + // defs w/ updates are for: state items, name changes, + // and probably connection / rewires (add / sub inputs) + if (view.hasDefUpdate) { + console.log('SCOPE run for hasDefUpdate') + console.time('redraw-defupdate') + for (let i = 0; i < view.defs.length; i++) { + if (view.defs[i].hasUpdate) { + // for now, we just redraw the entire definition, whenever we have updates for it + blocks.redrawDef(view.defs[i]) + // ah yeah, so we also occasionally load defs in here w/o having their link-drawing + // so we should just draw links from outputs as well as from inputs, then + // want a clean way to do that next... + view.defs[i].hasUpdate = false + } } + view.hasDefUpdate = false + console.timeEnd('redraw-defupdate') } - tlv.hasDefUpdate = false - console.timeEnd('redraw-defupdate') - } - // also, we do this globally. tough. scrape and move on - if(tlv.hasConnectivityChange){ - console.log('SCOPE run for connectivity change') - console.time('redraw-connectivity') - blocks.redrawContexWires(tlv) - tlv.hasConnectivityChange = false - console.timeEnd('redraw-connectivity') - } + // also, we do this globally. tough. scrape and move on + if(view.hasConnectivityChange){ + console.log('SCOPE run for connectivity change') + console.time('redraw-connectivity') + blocks.redrawContexWires(view) + view.hasConnectivityChange = false + console.timeEnd('redraw-connectivity') + } - if(tlv.hasDefPositionUpdate){ - console.log('SCOPE run for reposition from view') - console.time('redraw-position') - for(let i = 0; i < tlv.defs.length; i ++){ - if(tlv.defs[i].hasPositionUpdate){ - blocks.repositionDef(tlv.defs[i]) - tlv.defs[i].hasPositionUpdate = false + if(view.hasDefPositionUpdate){ + console.log('SCOPE run for reposition from view') + console.time('redraw-position') + for(let i = 0; i < view.defs.length; i ++){ + if(view.defs[i].hasPositionUpdate){ + blocks.repositionDef(view.defs[i]) + view.defs[i].hasPositionUpdate = false + } + } + view.hasDefPositionUpdate = false + console.timeEnd('redraw-position') + } + } + + this.check = () => { + // check levels recursively, + checkView(tlv) + for(let def of tlv.defs){ + // def.contains is a hook to the view object that operates the MVC for the context + // on the other side of this link ... + if(def.contains){ + if(!def.contains.plane){ + def.contains.plane = $('<div>').addClass('subplane').attr('id', `${def.name}_plane`).get(0) + $(plane).append(def.contains.plane) + // add this, as a block, to that link-def ? + } else { + checkView(def.contains) } } - tlv.hasDefPositionUpdate = false - console.timeEnd('redraw-position') } } @@ -285,499 +302,3 @@ export default function Scope(wrapper, tlv) { // ---------------------------------------------------------------------- END HOT NEWNESS } - -let oldTLV = () => { - // code just here for reference, - - view.getCurrentBounds = () => { - let ct = dt.readTransform(view.plane) - let w = view.dom.clientWidth / ct.s - let h = view.dom.clientHeight / ct.s - let x1 = -ct.x / ct.s - let y1 = -ct.y / ct.s - let x2 = w - x1 - let y2 = h - y1 - // move & shimmy by - return { - x1: x1, - y1: y1, - x2: x2, - y2: y2, - w: w, - h: h - } - } - - let zoomExtents = () => { - // collector - let psns = [] - for (let def of view.defs) { - for (let fltr of def.floaters) { - fltr.calculateSizes() - psns.push({ - x: fltr.x, - y: fltr.y, - x1: fltr.bb.x1, - y1: fltr.bb.y1, - x2: fltr.bb.x2, - y2: fltr.bb.y2 - }) - } - } - // ok then, probably bounds like - let minx = 0 - let miny = 0 - let maxx = 500 - let maxy = 500 - for (let ps of psns) { - if (ps.x + ps.x1 < minx) minx = ps.x + ps.x1 - if (ps.x + ps.x2 > maxx) maxx = ps.x + ps.x2 - if (ps.y + ps.y1 < miny) miny = ps.y + ps.y1 - if (ps.y + ps.y2 > maxy) maxy = ps.y + ps.y2 - } - // currently, - let ct = dt.readTransform(view.plane) - let wd = view.dom.clientWidth - let ht = view.dom.clientHeight - // so, scale is - let pfsx = (wd) / (maxx - minx) - let pfsy = (ht) / (maxy - miny) - let pfs = Math.min(pfsx, pfsy) - // and we can write - ct.s = pfs * 0.8 // breathing room, - ct.x = -minx * pfs - ct.y = -miny * pfs - // and then, - if (ct.s > 1) { - ct.s = 1 - ct.x = -minx - ct.y = -miny - } - // also, - view.tls = ct.s - for (let def of view.defs) { - if (def.type === 'view' && def.name !== 'tlview') { - def.hunk.tls = ct.s - } - } - // contact, - dt.writeTransform(view.plane, ct) - dt.writeBackgroundTransform(view.dom, ct) - view.drawLinks() - } // end zoom extents - - // trace should return, for an output, the next input. - // if the input is a link, it should try to traverse - let trace = (output, debug) => { - //console.log(`TRACE: tracing from ${output.name} in hunk ${output.parent.name} in view ${this.name}`) - // ok, traces through links / heirarchy, returning final destination - try { - if (debug) console.log(`TRACE: begin or recurse from ${output.name} in ${output.parent.name} from ${output.parent.parentView.name} of ${output.parent.parentView.interpreterName}`) - // of *connected* links - if (output.connections.length !== 1) { - // no connections exist, er, we can only do this for singleton lines - if (debug) console.log('TRACE: no connections...') - return false - } - let next = output.connections[0] - //console.log(`TRACE: NEXT:`, next) - if (next.parent.type === 'link') { - // this is the heirarchy dive - let thru = next.parent.reciprocalLink - if (thru) { - if (debug) console.log(`TRACE: next link`, thru) - // a mirror, - try { - let otp = thru.outputs[next.ind] - if (otp) { - // return / recurse - if (debug) console.log(`TRACE: diving -> ${otp.name}`) - return trace(otp, debug) - } else { - if (debug) console.log(`TRACE: terminates at link, but no reciprocal to dive`) - console.warn('on trace, found link, but no output on the other side') - return false - } - } catch (err) { - console.error('TRACE: err...') - console.error(err) - return false - } - } else { - // could try doing a globalOrganize, or refresh ... cumbersome - if (debug) console.log(`TRACE: terminates at link, but no reciprocal to dive`) - console.warn('on trace, at link boundary, find no reciprocal link') - return false - } - } else { - if (debug) console.log(`TRACE: finally returns ${next.name} in ${next.parent.name} from ${next.parent.parentView.name} of ${next.parent.parentView.interpreterName}`) - return next - } - } catch (err) { - console.error("yep") - console.error(err) - } - } - - view.trace = trace - - // notes on building a route: - // this is a pretty critical routine, and it's *perty neet* - // to make it real, needs to - // - operate when possible paths (unoccupied & same-type) or partial paths already exist - // - operate across multiple levels! - // - in deep future: choose shortest (& least busy?) path through existing graph - // - also: atm if links are not already spread (i.e. if link outputs don't match opposite inputs) .... - // - then we add one side at, say, index 2, and the other at, say. index 5 ... no bueno - - view.buildRoute = (output, input, debug) => { - return new Promise((resolve, reject) => { - // first, we can check - let pt = trace(output) - if (pt) { - resolve() - return - } - // ok, first off, are these things in the same view? can I find a view from an outputs? - let opv = output.parent.parentView - let ipv = input.parent.parentView - if (debug) console.log(`output parentview is ${opv.name} and input parent view is ${ipv.name}`) - if (opv === ipv) { - // we r on par - if (debug) console.log(`BR: inputs are the same to ${view.name}`) - opv.requestAddLink(output, input).then(() => { - resolve() - }) - } else { - // ok, we have two views, and some string of links between them - // let's first see if we can just find the route, - // to find another bug (courtesy of the def-replace-polymorphism bugfarm) - // we should run a .go before this... - view.globalOrganize() - // now, - if (debug) console.log(`BR: GO completes, now build for:`) - if (debug) console.log(`BR: from ${output.name} in ${output.parent.name} to ${input.name} in ${input.parent.name}`) - let finroute = [] - // we are the top level ... but we should hunt by link, - var recurse = (view, entrance, trace) => { - if (debug) console.log(`BR: recurse to ${view.name}`) - for (let df of view.defs) { - if (df.type === 'link' && df !== entrance) { - if (df.reciprocalLink) { - if (debug) console.log(`BR: pushes to ntrace of len ${trace.length} exit for ${df.reciprocalLink.name}`) - let borkit = JSON.parse(JSON.stringify(trace)) - borkit.push({ - entrance: { - link: df.name, - view: view.name - }, - exit: { - link: df.reciprocalLink.name, - view: df.reciprocalLink.parentView.name - } - }) - if (df.reciprocalLink.parentView === ipv) { - if (debug) console.log(`BR: Makes view ${df.reciprocalLink.parentView.name} and ${ipv.name}`) - finroute = JSON.parse(JSON.stringify(borkit)); - } else { - recurse(df.reciprocalLink.parentView, df.reciprocalLink, JSON.parse(JSON.stringify(borkit))) - } - } else { - if (debug) console.log(`BR: no reciprocal link for ${df.name} within ${view.name}`) - } - } // not a link - } - } - recurse(opv, null, []) - // ... - if (debug) console.log(`BR: recursion ran to completion, route is len ${finroute.length}`) - if (debug) console.log(`BR: the route:`, finroute) - if (finroute.length < 1) { - if (debug) console.log(`BR: no route returned...`) - console.error("no route to build...") - reject() - } - // should we resolve those objects? probably safe to assume that view names are unique, - // these are the kinds of code snippets that happen when jake is betwixt cpp and js - let resolver = (obj) => { - // jeez - let realView = view.defs.find((cand) => { - return cand.name === obj.view - }) - let handle = view - if (realView.name !== "tlview") handle = realView.hunk - let realLink = handle.defs.find((cand) => { - return cand.name === obj.link - }) - obj.view = handle - obj.link = realLink - } - for (let item of finroute) { - resolver(item.entrance) - resolver(item.exit) - } - if (debug) console.log("BR: resolved to", finroute) - // OK: we gotem - // so! - view.constructRoute(output, input, finroute).then(() => { - resolve() - }) - } // end not-same-view case, - }) - } - - let rndByte = () => { - return Math.round(Math.random() * 255) - } - - view.constructRoute = (output, input, route) => { - return new Promise((resolve, reject) => { - // entrance to 0th is from the - let lcounter = 0 - let wrap = async () => { - // to start, - // do first in route's entrance ... - let entview = route[0].entrance.view - let entlink = route[0].entrance.link - let entiplist = entlink.states[2].value - entiplist += `, auto_${rndByte()}_${entlink.outputs.length} (${output.type})` - await entview.requestStateChange(entlink.states[2], entiplist) - // it's new now, recall ? - entlink = entview.defs[entlink.ind] - await entview.requestAddLink(output, entlink.inputs[entlink.inputs.length - 1]) - // cover insides, - for (let rt = 0; rt < route.length - 1; rt++) { - let midview = route[rt].exit.view - if (midview !== route[rt + 1].entrance.view) throw new Error("these should bridge... ") - // ok, - let from = route[rt].exit.link - let to = route[rt + 1].entrance.link - // and so, - let tolist = to.states[2].value - tolist += `, auto_${rndByte()}_${to.outputs.length} (${output.type})` - console.log("FOR ST CHANGE TO", tolist) - await midview.requestStateChange(to.states[2], tolist) - to = midview.defs[to.ind] - // similarely, - let fromlist = from.states[3].value - fromlist += `, auto_${rndByte()}_${from.outputs.length} (${output.type})` - console.log("FOR ST CHANGE TO", fromlist) - await midview.requestStateChange(from.states[3], fromlist) - from = midview.defs[from.ind] - // goddang, so then - await midview.requestAddLink(from.outputs[from.outputs.length - 1], to.inputs[to.inputs.length - 1]) - } - // cover outside, - let outview = route[route.length - 1].exit.view - let outlink = route[route.length - 1].exit.link - let exitoplist = outlink.states[3].value - exitoplist += `, auto_${rndByte()}_${entlink.outputs.length} (${output.type})` - await outview.requestStateChange(outlink.states[3], exitoplist) - // again, it new - outlink = outview.defs[outlink.ind] - await outview.requestAddLink(outlink.outputs[outlink.outputs.length - 1], input) - console.log("FIN") - resolve() - } - wrap() - }) - } - - view.globalOrganize = (debug) => { - if (debug) console.log("GO: KICKOFF ORGANIZING PARTY") - // this is a request made: - /* - (1) when any view refreshes, - (2) when we load a new patch - (3) when we load a new system - (4) when we build a route with .buildRoute(output, input) - - this doesn't change topologies, or make any requests to managers, - - it just organizes visually - */ - // we need to recurse here, - let recursor = (scope, order) => { - if (debug) console.log(`Global Organize: recurses ${scope.name} at ${order}`) - // scope is a view (hunk) - // order is nth- level down tree, with us (toplevel) at root 0 - if (debug) console.log(`GO: scope defs, tl defs`) - for (let df of scope.defs) { - if (df.type === 'link' && df.grouptype !== 'edgecased') { - // find recirprocal view relationship via trace, which returns an input, thru links, - // given some output - if (debug) console.log(`GO: trace from ${df.name} in ${scope.name}`) - let rvi = trace(df.outputs[1], debug) - if (debug) console.log(`GO: trace returns`, rvi) - if (rvi) { - // we have ah link definition, and ah view definition, connected by routing, - // so we are safe to do - let rvd = rvi.parent - // if the rvd is a manager, this is the bottom level -> a link thru to a manager, - if (rvd.type === 'manager') continue - // and, - if (debug) console.log(`GO: wrap ${df.name} around ${rvd.name}`) - df.wrapon(rvd) - rvd.unwrap() - // find the dataport - let dtprt = trace(df.outputs[0]) - if (dtprt) { - dtprt.parent.unwrap() - } - // now, if we have ll data, - if (rvd.hunk.hasRefreshed) { - // find the interior link (by search for ol state) - let oind = df.states.find((cnd) => { - return cnd.name === 'otherLink' - }).value - if (!oind) throw new Error('cannot find link oind state for hookup') - // doth it ? - let internalLink = rvd.hunk.defs[oind] - if (internalLink) { - if (internalLink.type !== 'link') { - console.error('link mixup alert') - console.error(internalLink); - } - // hook em up - df.reciprocalLink = internalLink - internalLink.reciprocalLink = df - // and do, - internalLink.edgecase() - // still logging these, bc it's nice 2 kno - if (debug) console.log(`GO cn link ${df.name} to ${internalLink.name}`) - // done w/ internal, now we can - recursor(rvd.hunk, ++order) - } else { - console.error("organizing ... cannot find a reciprocal link") - } - } - } - } - } - // and roll zerbraHeirarchy in here also, using order ... - } - // kickoff w/ - recursor(view, 1) - view.zebraHeirarchy() - } // end globalOrganize - - view.zebraHeirarchy = (debug) => { - // we can go about this just by the way the visual relationship is organized, - let traverse = (view, lvl) => { - if (lvl > 6) { - console.warn('zebraHeirarchy traverses 6+ levels, you sure about this? exiting to avoid infinite loop') - return - } - lvl++ - if (debug) console.log(`ZH traverses to ${view.name} at lvl${lvl}`) - for (let df of view.defs) { - if (df.type === 'link') { - if (df.floatGroupType === 'wrappedon') { - // this link 'contains' a view, - if (lvl % 2 === 0) { - if (debug) console.log(`ZH sets ${view.name} to f0`) - $(df.reciprocalView.dom).css('background-color', '#f0f0f0') - //$(df.deg.native).children('.view').css('background-color', '#f0f0f0') - } else { - if (debug) console.log(`ZH sets ${view.name} to e0`) - $(df.reciprocalView.dom).css('background-color', '#e0e0e0') - //$(df.deg.native).children('.view').css('background-color', '#e0e0e0') - } - traverse(df.reciprocalView, lvl) - } - } - } - } - traverse(view, 0) - } - - view.expandLink = (linkDef) => { - return new Promise((resolve, reject) => { - // to avoid mayhem, do - linkDef.floaters[0].fix() - // and then, - view.requestAddHunk('view').then((viewDef) => { - // jquery moves automatically ? - console.log('EL: the view', viewDef.name) - console.log('EL: the link', linkDef.name) - // now we'd like to find a route from the view to the link - // since we're global, we could try to build the 1st link, - view.buildRoute(viewDef.outputs[0], linkDef.inputs[1]).then(() => { - console.log("EL: Build Route Down Complete") - return view.buildRoute(linkDef.outputs[1], viewDef.inputs[0]) - }).then(() => { - console.log("EL: Build Route UP Complete") - view.globalOrganize() - resolve(viewDef) - }).catch((err) => { - console.error('EL: probable error during route construction') - reject(err) - }) - }) - }) - } // end expand recipe - - /* QUEEN HANDLERS */ - - window.onresize = () => { - view.onresize() - } - - // built fast, should live with patchset - view.restoreEntireSystem = (name, debug) => { - // force it - debug = true - return new Promise((resolve, reject) => { - view.patchset.getSystem(name).then((sys) => { - if (debug) console.log('RESTORE SYSTEM: sys object', sys) - // startup, track views to look for - let recount = []; - // ah recursor: - let recursor = (scope, slice) => { - if (debug) console.log(`RESTORE SYSTEM: ${scope.name}`) - scope.patchset.mergePatch(slice, false).then(() => { - // done here, - recount.splice(recount.indexOf(scope.name), 1) - // check if more to do - for (let df of scope.defs) { - if (df.type === 'link') { - if (debug) console.log('RESTORE SYSTEM: found this link', df.name) - // match link / contains to ... - let vw = trace(df.outputs[1]) - if (vw) { - // this is a hack, - // if we're going to add more shit, we should increase by at least ... - if (vw.name === 'tlview') continue - vw = vw.parent.hunk - // if there's lower level work to do... (if this link 'contains' another patch, recursing) - let nl = slice.hunks[df.ind].contains - if (nl) { - if (debug) console.log('RESTORE SYSTEM: would like to load', nl) - recount.push(vw.name); - vw.refresh().then(() => { - if (debug) console.log('RESTORE SYSTEM: refreshed the context for, now recursing') - recursor(vw, nl) - }) - } else { - if (debug) { - console.log(`RESTORE SYSTEM: nothing contained in next link`) - } - } - } else { - if (debug) console.log(`RESTORE SYSTEM: no return from trace`) - } - } - } - //console.warn("RECOUNT NOW: ", recount) - if (recount.length < 1) { - //console.warn("RESTORE COMPLETE") - resolve() - } - }) - } - // startup, - recount.push(view.name) - recursor(view, sys) - }) - }) - } - -} diff --git a/hunks/hunks.js b/hunks/hunks.js index f08c78d..a8a6b60 100644 --- a/hunks/hunks.js +++ b/hunks/hunks.js @@ -66,7 +66,7 @@ function Hunkify(hunk) { // making ah dom element, hunk.document = () => { hunk.dom = $('<div>').addClass('hunk').get(0) - hunk.dom.handle = hunk + hunk.dom.hunk = hunk return hunk.dom } diff --git a/hunks/view.js b/hunks/view.js index 6f76cb5..50bd61a 100644 --- a/hunks/view.js +++ b/hunks/view.js @@ -880,7 +880,6 @@ function View() { patch.hunks.push(hnk) } resolve(patch) - reject('nah') }) } @@ -951,7 +950,8 @@ function View() { let recursor = async (n) => { // we want to walk the list of outputs in the patch, and reconn. for (let op in patch.hunks[n].outputs) { - if (!defs[n].outputs[op]) break + if (!defs[n].outputs[op]) break // if the thing-actually-loaded doesn't have an output here, pass + if (!patch.hunks[n].outputs[op].conn) break // during some serializations / deserializations, empty arrays are culled, so for (let cn of patch.hunks[n].outputs[op].conn) { if (debug) console.log('mergeLinkList would like to conn', n, op, 'to', cn) // now! we would like to see about whether / not this link already exist. @@ -1008,13 +1008,15 @@ function View() { // some view that exists in the browser level // and return ... ? this.routelink = (link) => { + // oof async so nice return new Promise(async (resolve, reject) => { // *and* need to do some intelligent work to trace our way up to // the top level view, where we would be able to instate a view ... // *should* check if trace up exists, but basically: try { - let view = await this.requestAddHunk('view') - console.log('view', view) + let vdef = await this.requestAddHunk('view') + // almost definitely the easiest way to handle this is to give our local defs + // a .hunk, this way (1) we know they're local - the view will always be local - await this.requestAddLink(view.outputs[0], link.inputs[1]) await this.requestAddLink(link.inputs[1], view.outputs[0]) resolve(view) diff --git a/scratch/old-defs.js b/scratch/old-defs.js new file mode 100644 index 0000000..0f803b7 --- /dev/null +++ b/scratch/old-defs.js @@ -0,0 +1,734 @@ +// ---------------------------------------------------------------------- END HOT NEWNESS +// ---------------------------------------------------------------------- END HOT NEWNESS +// ---------------------------------------------------------------------- END HOT NEWNESS +// ---------------------------------------------------------------------- END HOT NEWNESS +// ---------------------------------------------------------------------- END HOT NEWNESS + +// new this - +function HunkDefinition(spec, view, dt, debug) { + // the basics, + this.ind = spec.ind + this.name = spec.name + this.type = spec.type + this.inputs = new Array() + this.outputs = new Array() + this.states = new Array() + // yep, + this.parentView = view + + // dom elements group, containing (but not limited to) + this.deg = { + core: {} + } + this.isExploded = false + this.floatGroupType = 'collected' + + // core ... + this.deg.core = $('<div>').addClass('defcore').attr('id', `${this.name}_${this.ind}`).get(0) + let title = $(`<div>${this.name}</div>`).addClass('deftitle').addClass('header').get(0) + $(title).append($(`<span style="float:right">(${this.type})</span>`)) + + $(this.deg.core).append(title) + if (spec.states.length > 0) { + let statedefom = $('<div>').addClass('states') + for (let st in spec.states) { + let state = new StateDefinition(spec.states[st], st, this, view, debug) + this.states.push(state) + $(statedefom).append(state.de) + } + $(this.deg.core).append(statedefom) + } + + // init our floater array, and startup with 0th entry (one floater per def == standard) + this.floaters = [] + this.floaters.push(new Floater(view, 'std')) + + // write inputs + if (spec.inputs.length > 0) { + // idom, odom want title bars ... + let idom = $('<div>').addClass('inputs') + $(idom).append($(`<div>inputs >></div>`).addClass('inputheader').addClass('header')) + for (let ip in spec.inputs) { + let input = new InputDefinition(spec.inputs[ip], ip, this, debug) + this.inputs.push(input) + $(idom).append(input.de) + } + this.deg.inputs = $(idom).get(0) + this.floaters[0].take(this.deg.inputs, this, true) + } + // write outputs, + if (spec.outputs.length > 0) { + let odom = $('<div>').addClass('outputs') + $(odom).append($(`<div>>> outputs</div>`).addClass('outputheader').addClass('header')) + for (let op in spec.outputs) { + let output = new OutputDefinition(spec.outputs[op], op, this, view, dt, debug) + this.outputs.push(output) + $(odom).append(output.de) + } + this.deg.outputs = $(odom).get(0) + this.floaters[0].take(this.deg.outputs, this, true) + } + + // title right-click handler + title.oncontextmenu = (evt) => { + evt.preventDefault() + evt.stopPropagation() + // write menu for requesting delete and copy + let menu = $('<div>').addClass('contextmenu').get(0) + $(menu).append($('<li><i class="em em-coffin"></i> remove hunk</li>').on('click', (evt) => { + view.requestRemoveHunk(this.ind) + $(menu).remove() + })) + $(menu).append($('<li><i class="em em-abc"></i> rename hunk</li>').on('click', (evt) => { + $(evt.target).text('') + let tinput = $('<input>').attr('type', 'text').attr('size', 24).attr('value', 'new name').get(0) + $(evt.target).append(tinput) + $(tinput).focus() + $(tinput).select() + $(tinput).on('keyup', (evt) => { + if (evt.keyCode == 13) { + view.requestRenameHunk(this.ind, tinput.value).then((def) => { + console.log('cleared new name for', def) + $(menu).remove() + }) + } + }) + })) + $(menu).append($('<li><i class="em em-repeat_one"></i> copy hunk</li>').on('click', (evt) => { + // todo: maybe this should copy state and req it down? + view.requestAddHunk(this.type) + $(menu).remove() + })) + // place it, + // title.offsetWidth is 400 + // de.style.left, de.style.top, de.clientWidth, + let ct = dt.readTransform(this.deg.core) + let mp = { + s: 1, + x: ct.x, + y: ct.y - 31 * 3 - 10 + } + if (this.containsButtons()) + mp.y -= 25 + if (view.isTopLevel) { + // write it, + $(menu).append($('<li><i class="em em-arrows_counterclockwise"></i> reload from source</li>').on('click', (evt) => { + view.reloadHunk(this.ind, false) + })) + mp.y -= 31 + } + dt.writeTransform(menu, mp) + $(view.plane).append(menu) + } + + // take the core, not stalling, + this.floaters[0].take(this.deg.core, this, false) + + /* --------------------------- ---------------------------- */ + /* ------------------------- PHYSICS ------------------------- */ + /* --------------------------- ---------------------------- */ + + this.highlight = () => { + $(this.deg.core).find('.deftitle').css('background-color', '#969696') + if (this.deg.inputs) + $(this.deg.inputs).find('.inputheader').css('background-color', 'white').css('color', 'black') + if (this.deg.outputs) + $(this.deg.outputs).find('.outputheader').css('background-color', 'white').css('color', 'black') + if (this.deg.native) + $(this.deg.native).find('.nativeheader').css('background-color', 'white').css('color', 'black') + } + + this.unhighlight = () => { + $(this.deg.core).find('.deftitle').css('background-color', '#303030') + if (this.deg.inputs) + $(this.deg.inputs).find('.inputheader').css('background-color', 'red').css('color', 'white') + if (this.deg.outputs) + $(this.deg.outputs).find('.outputheader').css('background-color', 'blue').css('color', 'white') + if (this.deg.native) + $(this.deg.native).find('.nativeheader').css('background-color', 'green').css('color', 'white') + } + + /* --------------------------- ---------------------------- */ + /* ------------------------- UPDATES ------------------------- */ + /* --------------------------- ---------------------------- */ + + // UPDATE Index + this.newInd = (ind) => { + this.ind = ind + // and those titles, and ids ... + $(this.de).attr('id', `${this.name}_${this.ind}`) + $(title).text(`${this.name}`) + title.append($(`<span style="float:right">(${this.type})</span>`).get(0)) + for (let ip of this.inputs) { + ip.onNewParentInfo() + } + for (let op of this.outputs) { + op.onNewParentInfo() + } + for (let st of this.states) { + st.onNewParentInfo() + } + } + + // UPDATE Name + this.updateName = (newName) => { + this.name = newName + $(this.de).attr('id', `${this.name}_${this.ind}`) + $(title).text(`${this.name}`) + title.append($(`<span style="float:right">(${this.type})</span>`).get(0)) + for (let ip of this.inputs) { + ip.onNewParentInfo() + } + for (let op of this.outputs) { + op.onNewParentInfo() + } + for (let st of this.states) { + st.onNewParentInfo() + } + } + + this.cleanup = () => { + cleanButtons() + cleanFloaters() + for (let od in this.deg) { + $(this.deg[od]).remove() + } + } + + let cleanFloaters = () => { + for (let flt of this.floaters) { + flt.cleanup() + } + this.floaters.length = 0 + } + + this.containsButtons = () => { + for (let fl of this.floaters) { + for (let it of fl.bag) { + if (it.type === 'button') + return true + } + } + return false + } + + let cleanButtons = () => { + // sloppy! there is probably one in one of the cores, + try { + // max 5... + for (let i = 0; i < 5; i++) { + let bindex = this.floaters[0].bag.findIndex((cnd) => { + return cnd.type === 'button' + }) + if (bindex !== -1) { + // also going to get that typeset issue ... + // pls to god this is reworked before a bug resulting appears + // rm from the dom, + $(this.floaters[0].bag[bindex].de).remove() + this.floaters[0].bag.splice(bindex, 1) + } else { + break + } + } + // now we can + this.floaters[0].onChange() + } catch (err) { + console.error(`couldn't clean buttons from ${this.name}, for reasons:`, err) + } + } + + /* --------------------------- ---------------------------- */ + /* ----------------- RM/ADD/ECT to FLOATERS ------------------ */ + /* --------------------------- ---------------------------- */ + + this.collect = () => { + // restore state, collect .deg under one hood (what of buttons?) + console.error('not yet collecting') + } + + this.unwrap = () => { + // never twice + if (this.floatGroupType === 'unwrapped') + return + // otherwise + this.floatGroupType = 'unwrapped' + // free willy + // ok, can we just murder everything and start from scratch? + // still have those .deg elements, so ... + // will have to write a remove + //console.log('UNWRAP') + cleanFloaters() + // (unrwapped) has a core with the core, outputs, and native + let cflt = new Floater(view, 'unwrapped') + if (this.deg.outputs) { + cflt.take(this.deg.outputs, this, true) + } + if (this.deg.native) { + if (this.type === 'view' && view) { + //console.log('ommitting view deg from this def') + } else { + cflt.take(this.deg.native, this, true) + } + } + cflt.take(this.deg.core, this) + if (this.deg.native) { + if (this.type === 'view' && view) { + //console.log('ommitting view deg from this def') + } else { + cflt.fitNativeToFloater() + } + } + this.floaters.push(cflt) + // if there are inputs, they are separate + if (this.deg.inputs) { + let iflt = new Floater(view, 'unwrapped') + iflt.take(this.deg.inputs, this) + this.floaters.push(iflt) + } + // and kick that + view.floop.reset() + } + + // for links, accept a view domain element, add to ur floater, wrap that floater + this.wrapon = (viewDef) => { + // don't do it twice + if (this.floatGroupType === 'wrappedon') + return + // set state + this.floatGroupType = 'wrappedon' + // errs when not a link, + if (this.type !== 'link') + throw new Error('non-links doth not wrap') + // first stat, we want to keep this ref, + this.reciprocalView = viewDef.hunk + // ok, and we want viewdef objects to highlight ... + // if we're not gathered to start, that's probably bad news + if (this.floaters.length > 1) + console.error('wyd here? many floaters during a wrap', this.floaters) + // get the floater's bag-item containing that view ... + let vflt = viewDef.floaters.find((cnd) => { + return cnd.typeset.includes('native') + }) + if (!vflt) + throw new Error('could not find view floater on swap') + // + let vbagindex = vflt.bag.findIndex((cnd) => { + return cnd.type === 'native' + }) + if (vbagindex === -1) + throw new Error('could not find floater native item on swap') + let vbagitem = vflt.bag.splice(vbagindex, 1)[0] + // before we do this, let's push out some extra space + // this *is a hack* + let bnds = view.getCurrentBounds() + view.requestResize(1800, bnds.h + 600) + // we need to make sure it's in the link's plane, jquery is smart about this, + //console.log(view.plane, vbagitem.de) + $(view.plane).append(vbagitem.de) + // the link's 1st floater, + let flt = this.floaters[0] + flt.grouptype = 'wrapped' + // flt.take au manuel + flt.bag.push(vbagitem) + flt.typeset.push('native') + vbagitem.de.onResizeCustomCallback = flt.onElementResize + // can just do recalc now? + flt.onChange() + // and also, + vflt.onChange() + // and *also* + this.removeButton('<i class="em em-squid"></i>') + this.addButton('<i class="em em-shell"></i>', (evt) => { + console.error('no collapse yet') + }) + } + + this.edgecase = () => { + // check + if (this.floatGroupType === 'edgecased') + return + // or set + this.floatGroupType = 'edgecased' + // also links only pls, + if (this.type !== 'link') + throw new Error('non-links doth not edge') + // almost forgot, we want to get rid of this button: + cleanButtons() + // similar to the unwrap spec, + // we're also assuming at this point that this view isn't wrapped around a view + cleanFloaters() + // now we want a left floater, + // free willy + let lflt = new Floater(view, 'edges') + lflt.take(this.deg.core, this, true) + lflt.take(this.deg.outputs, this, true) + lflt.makeEdges() + this.floaters.push(lflt) + // and the right, + let rflt = new Floater(view, 'edges') + rflt.take(this.deg.inputs, this, true) + rflt.makeEdges() + this.floaters.push(rflt) + // ok, + view.floop.reset() + // put inputs / outputs at edges of view body + // put core below inputs, at left + // make message box of the view sit low + // handle view resizes ? + } + + // adden em + this.addButton = (text, callback) => { + // write a button, + let btn = $(`<div>${text}</div>`).addClass('defbutton').get(0) + $(btn).on('click', callback).on('mouseover', (evt) => { + document.body.style.cursor = 'pointer' + }).on('mouseout', (evt) => [document.body.style.cursor = 'auto']) + // ok ok, then ... + // we find the floater that contains the core, + let coreFloater = this.floaters.find((cnd) => { + return cnd.typeset.includes('core') + }) + if (!coreFloater) + throw new Error(`couldn't find a floater for this def button`, text) + // attach the button element to its bag ... + coreFloater.take(btn, this) + } + + this.removeButton = (text, callback) => { + // find the core-containing floater + // we find the floater that contains the core, + let coreFloater = this.floaters.find((cnd) => { + return cnd.typeset.includes('core') + }) + if (!coreFloater) + throw new Error(`couldn't find a floater for this def button`, text) + let success = false + // au manuel splice outta the bag + for (let item in coreFloater.bag) { + let itm = coreFloater.bag[item] + if (itm.type === 'button') { + if ($(itm.de).html() === text) { + // splice it out, + coreFloater.bag.splice(item, 1) + $(itm.de).remove() + coreFloater.onChange() + success = true + } + } + } + if (!success) + console.error(`couldn't remove the button having text ${text}`) + + } + + // absorbing a native hunk's domain element, + this.takeNative = (hunk) => { + this.hunk = hunk + // wants a wrapper, + this.deg.native = $('<div>').addClass('nativewrap').get(0) + // wrapper includes this title bar, + $(this.deg.native).append($(`<div>${this.name}'s html element</div>`).addClass('nativeheader').addClass('header')) + // and the hunk's element goes in that wrapper. + $(this.deg.native).append(this.hunk.dom) + // now we can hook it 2 a floater ... this will depend on our state, which we can mod later + // atm, we'll put it with the core, + let coreFloater = this.floaters.find((cnd) => { + return cnd.typeset.includes('core') + }) + if (!coreFloater) + throw new Error(`couldn't find a floater for this def native`) + // hook it + coreFloater.take(this.deg.native, this, false, this.hunk.onresize) + // has this handle, + this.hunk.requestResize = (x, y) => { + // set manual and call update, + // doesn't matter which hunk this is in... + // console.log(`REQRESIZE hunk ${this.name} requesting size of ${x}, ${y}`) + // console.log('native', this.deg.native) + this.deg.native.requestResize(x, y) + view.tick() + } + // now we should be able to, + this.hunk.onload() + } + + // add special case buttons, just this so far + if (this.type === 'link') { + // todo: + let expander = () => { + this.addButton('<i class="em em-squid"></i>', (evt) => { + // ??? + view.tlv.expandLink(this).then((view) => { + console.log("EXPANDEEED --> expandLink promise completes", view) + }).catch((err) => { + console.log("EXP catches err", err) + }) + }) + } + expander() + } + + this.fixWithDataPort = () => { + if (this.floaters[0].isFixed) { + // find the data port, + let dp = view.tlv.trace(this.outputs[0]) + if (dp) { + dp.parent.floaters[0].fixTo(this.floaters[0].fx + 500, this.floaters[0].fy - 100) + } else { + console.error("no data port from trace") + } + } else { + console.error("won't fix with data port: not fixed") + } + } + +} // end def def + +/* --------------------------- ---------------------------- */ +/* ------------------------ INPUT DEF ------------------------ */ +/* --------------------------- ---------------------------- */ + +function InputDefinition(ipspec, ind, def, debug) { + // keep track of name and type, + this.name = ipspec.name + this.type = ipspec.type + this.parent = def + this.ind = parseInt(ind) + // a dom element + this.de = $(`<li>${this.name} (${this.type})</li>`).addClass('input').get(0) + this.de.id = `${this.parent.name}_${this.parent.ind}_input_${this.name}` + this.onNewParentInfo = () => { + this.de.id = `${this.parent.name}_${this.parent.ind}_input_${this.name}` + } + // we also keep a list, + this.connections = new Array() + this.disconnect = (output) => { + let index = this.connections.findIndex((cand) => { + return (cand.parent.ind === output.parent.ind && cand.name === output.name && cand.type === output.type) + }) + if (index === -1) + throw new Error('during output disconnect, input cannot find output...') + this.connections.splice(index, 1) + } + this.disconnectAll = () => { + for (let op of this.connections) { + op.disconnect(this) + } + this.connections.length = 0 + } + // to get this object via the dom, circular... apparently that is fine ? + this.de.hookup = this +} + +/* --------------------------- ---------------------------- */ +/* ----------------------- OUTPUT DEF ------------------------ */ +/* --------------------------- ---------------------------- */ + +function OutputDefinition(opspec, ind, def, view, dt, debug) { + // keep track of name and type, + this.parent = def + this.name = opspec.name + this.type = opspec.type + this.ind = parseInt(ind) + // a dom element + this.de = $(`<li>(${this.type}) ${this.name}</li>`).addClass('output').get(0) + this.de.id = `${this.parent.name}_${this.parent.ind}_output_${this.name}` + this.onNewParentInfo = () => { + this.de.id = `${this.parent.name}_${this.parent.ind}_output_${this.name}` + } + // outputs handle all of the dragging-etc + if (opspec.connections !== undefined) { + this.specConnections = opspec.connections + } + this.connections = new Array() // of inputdefs, ! + this.connect = (inputdef) => { + inputdef.connections.push(this) + this.connections.push(inputdef) + } + this.disconnect = (inputdef) => { + inputdef.disconnect(this) + let iof = this.connections.findIndex((cand) => { + return (cand.name === inputdef.name && cand.parent.ind === inputdef.parent.ind) + }) + if (iof === -1) + throw new Error('could not find input to disconnect') + this.connections.splice(iof, 1) + return true + } + this.disconnectAll = () => { + for (let ip of this.connections) { + ip.disconnect(this) + } + this.connections.length = 0 + } + // the dragging + this.floater = {} + this.hasFloater = false + // ondrag, attached later + let evtDrag = (evt) => { + evt.preventDefault() + evt.stopPropagation() + let pt = dt.readTransform(view.tlv.plane) + let thet = dt.readTransform(this.floater) + // ... set delta + thet.x += evt.movementX / pt.s + thet.y += evt.movementY / pt.s + dt.writeTransform(this.floater, thet) + view.drawLinks() + } + // to remove the below + let dragMouseUp = (evt) => { + if (debug) + console.log('MOUSEUP ON', evt.target.id) + // 1st, make sure it's an input + if ($(evt.target).is('.input')) { + // HERE: searcheth by input id text? or + let hk = evt.target.hookup + // hookups are hooks to the input's def-object + // console.log('ah hookup looks like:', hk) + if (!hk) + throw new Error('missing some data at this input...') + // use a dom data flag, to find that input and output id? + // are we in the same context? + if (hk.parent.parentView === this.parent.parentView) { + view.requestAddLink(this, hk) + } else { + console.warn("UI Route Builder Begins...") + this.parent.parentView.tlv.buildRoute(this, hk).then(() => { + console.warn("UI Route Builder Resolves !") + }) + } + // do things to conn, then + } + // cleanup + document.removeEventListener('mouseup', dragMouseUp) + document.removeEventListener('mousemove', evtDrag) + // remove the floater flag + this.hasFloater = false + // remove the floater itself + $(view.plane).find('#floater').remove() + view.drawLinks() + } + // eeeeehntr for link-hookup-dragging + this.de.onmousedown = (evt) => { + evt.stopPropagation() + evt.preventDefault() + if (debug) + console.log('mousedown for', this) + // this and that flag will be read-in on drawlinks, to draw that link + this.floater = $('<div>').attr('id', 'floater').append(this.type).get(0) + this.hasFloater = true + this.floater.style.zIndex = '1' + // set initial position by the outputs' position, + let dparent = $(this.de).parent().get(0) + let opp = dt.readTransform(dparent) + // top-level plane ... + let pt = dt.readTransform(view.tlv.plane) + // plonk: have to do this now or else clientHeight / width are 0 + view.plane.appendChild(this.floater) + // init out floater position, and put it in the dom + dt.writeTransform(this.floater, { + s: 1, + x: opp.x - ((this.floater.clientWidth + 5)), + y: opp.y + this.floater.clientHeight * (this.ind + 0.5) // - ((fltheight * pt.s) / 2) / pt.s + }) + // handlers to drag, and remove + document.addEventListener('mousemove', evtDrag) + // and delete / act when mouse comes up + document.addEventListener('mouseup', dragMouseUp) + } +} + +/* --------------------------- ---------------------------- */ +/* ------------------------ STATE DEF ------------------------ */ +/* --------------------------- ---------------------------- */ + +function StateDefinition(stspec, ind, def, view, debug) { + this.parent = def + this.name = stspec.name + this.type = stspec.type + this.ind = parseInt(ind) + // business, + this.value = stspec.value + // ok, + this.de = $('<div>' + this.name + " (" + this.type + ")" + '</div>').addClass('stateItem').get(0) + this.de.id = `${this.parent.name}_${this.parent.ind}_state_${this.name}` + this.onNewParentInfo = () => { + this.de.id = `${this.parent.name}_${this.parent.ind}_state_${this.name}` + } + // ui for these ... we can just cover the basics of js types because yonder serializations + // etc will throw errors for other types. of course, this could help more, but we're in a rush + switch (typeof this.value) { + case 'string': + //dom.append($('<br>').get(0)) + let strinput = $('<input>').attr('type', 'text').attr('size', 32).attr('value', this.value).css('width', '240px').get(0) + strinput.addEventListener('change', (evt) => { + // ask for a change, + // TODO HERE NOW: this is the state change request you want to write + // do it like writeMessage() instead + // requestStateChange(def.id, state, strinput.value) + // but assert that we don't change the definition unless + view.requestStateChange(this, strinput.value) + strinput.value = this.value + }) + this.de.append(strinput) + this.set = (value) => { + if (typeof value === 'string') { + strinput.value = value + this.value = value + } else { + throw new Error('bad type put into state dom') + } + } + break // end string types + case 'number': + let ninput = $('<input>').addClass('stateNumInput').attr('type', 'text').attr('size', 24).attr('value', this.value.toString()).css('width', '100px').get(0) + ninput.addEventListener('change', (evt) => { + // ask for a change, watch for float or for int ... + if (isIntType(this.type)) { + view.requestStateChange(this, parseInt(ninput.value)) + } else { + view.requestStateChange(this, parseFloat(ninput.value)) + } + // but assert that we don't change the definition unless + ninput.value = this.value + }) + this.de.append(ninput) + this.set = (value) => { + if (typeof value === 'number') { + // quite sure js does this conversion no problem + ninput.value = value + this.value = value + } else { + throw new Error('bad type put into state dom') + } + } + break // end numnber type + case 'boolean': + let span = $('<span style="float:right;">' + this.value.toString() + '</span>').get(0) + $(this.de).addClass('stateBooleanItem') + this.de.append(span) + this.de.addEventListener('click', (evt) => { + // read the current 'state' (as written) and send the opposite + let txt = $(span).text() + if (txt === 'true') { + view.requestStateChange(this, false) + } else { + view.requestStateChange(this, true) + } + }) + this.set = (value) => { + if (typeof value === 'boolean') { + $(span).text(value.toString()) + this.value = value + } else { + throw new Error('bad type put into state dom') + } + } + break // end boolean type + default: + console.error(`unaccounted for type at input pull for state change, ${typeof state.value}`) + break + } +} diff --git a/scratch/old-view.js b/scratch/old-view.js new file mode 100644 index 0000000..8891d14 --- /dev/null +++ b/scratch/old-view.js @@ -0,0 +1,495 @@ +let oldTLV = () => { + // code just here for reference, + + view.getCurrentBounds = () => { + let ct = dt.readTransform(view.plane) + let w = view.dom.clientWidth / ct.s + let h = view.dom.clientHeight / ct.s + let x1 = -ct.x / ct.s + let y1 = -ct.y / ct.s + let x2 = w - x1 + let y2 = h - y1 + // move & shimmy by + return { + x1: x1, + y1: y1, + x2: x2, + y2: y2, + w: w, + h: h + } + } + + let zoomExtents = () => { + // collector + let psns = [] + for (let def of view.defs) { + for (let fltr of def.floaters) { + fltr.calculateSizes() + psns.push({ + x: fltr.x, + y: fltr.y, + x1: fltr.bb.x1, + y1: fltr.bb.y1, + x2: fltr.bb.x2, + y2: fltr.bb.y2 + }) + } + } + // ok then, probably bounds like + let minx = 0 + let miny = 0 + let maxx = 500 + let maxy = 500 + for (let ps of psns) { + if (ps.x + ps.x1 < minx) minx = ps.x + ps.x1 + if (ps.x + ps.x2 > maxx) maxx = ps.x + ps.x2 + if (ps.y + ps.y1 < miny) miny = ps.y + ps.y1 + if (ps.y + ps.y2 > maxy) maxy = ps.y + ps.y2 + } + // currently, + let ct = dt.readTransform(view.plane) + let wd = view.dom.clientWidth + let ht = view.dom.clientHeight + // so, scale is + let pfsx = (wd) / (maxx - minx) + let pfsy = (ht) / (maxy - miny) + let pfs = Math.min(pfsx, pfsy) + // and we can write + ct.s = pfs * 0.8 // breathing room, + ct.x = -minx * pfs + ct.y = -miny * pfs + // and then, + if (ct.s > 1) { + ct.s = 1 + ct.x = -minx + ct.y = -miny + } + // also, + view.tls = ct.s + for (let def of view.defs) { + if (def.type === 'view' && def.name !== 'tlview') { + def.hunk.tls = ct.s + } + } + // contact, + dt.writeTransform(view.plane, ct) + dt.writeBackgroundTransform(view.dom, ct) + view.drawLinks() + } // end zoom extents + + // trace should return, for an output, the next input. + // if the input is a link, it should try to traverse + let trace = (output, debug) => { + //console.log(`TRACE: tracing from ${output.name} in hunk ${output.parent.name} in view ${this.name}`) + // ok, traces through links / heirarchy, returning final destination + try { + if (debug) console.log(`TRACE: begin or recurse from ${output.name} in ${output.parent.name} from ${output.parent.parentView.name} of ${output.parent.parentView.interpreterName}`) + // of *connected* links + if (output.connections.length !== 1) { + // no connections exist, er, we can only do this for singleton lines + if (debug) console.log('TRACE: no connections...') + return false + } + let next = output.connections[0] + //console.log(`TRACE: NEXT:`, next) + if (next.parent.type === 'link') { + // this is the heirarchy dive + let thru = next.parent.reciprocalLink + if (thru) { + if (debug) console.log(`TRACE: next link`, thru) + // a mirror, + try { + let otp = thru.outputs[next.ind] + if (otp) { + // return / recurse + if (debug) console.log(`TRACE: diving -> ${otp.name}`) + return trace(otp, debug) + } else { + if (debug) console.log(`TRACE: terminates at link, but no reciprocal to dive`) + console.warn('on trace, found link, but no output on the other side') + return false + } + } catch (err) { + console.error('TRACE: err...') + console.error(err) + return false + } + } else { + // could try doing a globalOrganize, or refresh ... cumbersome + if (debug) console.log(`TRACE: terminates at link, but no reciprocal to dive`) + console.warn('on trace, at link boundary, find no reciprocal link') + return false + } + } else { + if (debug) console.log(`TRACE: finally returns ${next.name} in ${next.parent.name} from ${next.parent.parentView.name} of ${next.parent.parentView.interpreterName}`) + return next + } + } catch (err) { + console.error("yep") + console.error(err) + } + } + + view.trace = trace + + // notes on building a route: + // this is a pretty critical routine, and it's *perty neet* + // to make it real, needs to + // - operate when possible paths (unoccupied & same-type) or partial paths already exist + // - operate across multiple levels! + // - in deep future: choose shortest (& least busy?) path through existing graph + // - also: atm if links are not already spread (i.e. if link outputs don't match opposite inputs) .... + // - then we add one side at, say, index 2, and the other at, say. index 5 ... no bueno + + view.buildRoute = (output, input, debug) => { + return new Promise((resolve, reject) => { + // first, we can check + let pt = trace(output) + if (pt) { + resolve() + return + } + // ok, first off, are these things in the same view? can I find a view from an outputs? + let opv = output.parent.parentView + let ipv = input.parent.parentView + if (debug) console.log(`output parentview is ${opv.name} and input parent view is ${ipv.name}`) + if (opv === ipv) { + // we r on par + if (debug) console.log(`BR: inputs are the same to ${view.name}`) + opv.requestAddLink(output, input).then(() => { + resolve() + }) + } else { + // ok, we have two views, and some string of links between them + // let's first see if we can just find the route, + // to find another bug (courtesy of the def-replace-polymorphism bugfarm) + // we should run a .go before this... + view.globalOrganize() + // now, + if (debug) console.log(`BR: GO completes, now build for:`) + if (debug) console.log(`BR: from ${output.name} in ${output.parent.name} to ${input.name} in ${input.parent.name}`) + let finroute = [] + // we are the top level ... but we should hunt by link, + var recurse = (view, entrance, trace) => { + if (debug) console.log(`BR: recurse to ${view.name}`) + for (let df of view.defs) { + if (df.type === 'link' && df !== entrance) { + if (df.reciprocalLink) { + if (debug) console.log(`BR: pushes to ntrace of len ${trace.length} exit for ${df.reciprocalLink.name}`) + let borkit = JSON.parse(JSON.stringify(trace)) + borkit.push({ + entrance: { + link: df.name, + view: view.name + }, + exit: { + link: df.reciprocalLink.name, + view: df.reciprocalLink.parentView.name + } + }) + if (df.reciprocalLink.parentView === ipv) { + if (debug) console.log(`BR: Makes view ${df.reciprocalLink.parentView.name} and ${ipv.name}`) + finroute = JSON.parse(JSON.stringify(borkit)); + } else { + recurse(df.reciprocalLink.parentView, df.reciprocalLink, JSON.parse(JSON.stringify(borkit))) + } + } else { + if (debug) console.log(`BR: no reciprocal link for ${df.name} within ${view.name}`) + } + } // not a link + } + } + recurse(opv, null, []) + // ... + if (debug) console.log(`BR: recursion ran to completion, route is len ${finroute.length}`) + if (debug) console.log(`BR: the route:`, finroute) + if (finroute.length < 1) { + if (debug) console.log(`BR: no route returned...`) + console.error("no route to build...") + reject() + } + // should we resolve those objects? probably safe to assume that view names are unique, + // these are the kinds of code snippets that happen when jake is betwixt cpp and js + let resolver = (obj) => { + // jeez + let realView = view.defs.find((cand) => { + return cand.name === obj.view + }) + let handle = view + if (realView.name !== "tlview") handle = realView.hunk + let realLink = handle.defs.find((cand) => { + return cand.name === obj.link + }) + obj.view = handle + obj.link = realLink + } + for (let item of finroute) { + resolver(item.entrance) + resolver(item.exit) + } + if (debug) console.log("BR: resolved to", finroute) + // OK: we gotem + // so! + view.constructRoute(output, input, finroute).then(() => { + resolve() + }) + } // end not-same-view case, + }) + } + + let rndByte = () => { + return Math.round(Math.random() * 255) + } + + view.constructRoute = (output, input, route) => { + return new Promise((resolve, reject) => { + // entrance to 0th is from the + let lcounter = 0 + let wrap = async () => { + // to start, + // do first in route's entrance ... + let entview = route[0].entrance.view + let entlink = route[0].entrance.link + let entiplist = entlink.states[2].value + entiplist += `, auto_${rndByte()}_${entlink.outputs.length} (${output.type})` + await entview.requestStateChange(entlink.states[2], entiplist) + // it's new now, recall ? + entlink = entview.defs[entlink.ind] + await entview.requestAddLink(output, entlink.inputs[entlink.inputs.length - 1]) + // cover insides, + for (let rt = 0; rt < route.length - 1; rt++) { + let midview = route[rt].exit.view + if (midview !== route[rt + 1].entrance.view) throw new Error("these should bridge... ") + // ok, + let from = route[rt].exit.link + let to = route[rt + 1].entrance.link + // and so, + let tolist = to.states[2].value + tolist += `, auto_${rndByte()}_${to.outputs.length} (${output.type})` + console.log("FOR ST CHANGE TO", tolist) + await midview.requestStateChange(to.states[2], tolist) + to = midview.defs[to.ind] + // similarely, + let fromlist = from.states[3].value + fromlist += `, auto_${rndByte()}_${from.outputs.length} (${output.type})` + console.log("FOR ST CHANGE TO", fromlist) + await midview.requestStateChange(from.states[3], fromlist) + from = midview.defs[from.ind] + // goddang, so then + await midview.requestAddLink(from.outputs[from.outputs.length - 1], to.inputs[to.inputs.length - 1]) + } + // cover outside, + let outview = route[route.length - 1].exit.view + let outlink = route[route.length - 1].exit.link + let exitoplist = outlink.states[3].value + exitoplist += `, auto_${rndByte()}_${entlink.outputs.length} (${output.type})` + await outview.requestStateChange(outlink.states[3], exitoplist) + // again, it new + outlink = outview.defs[outlink.ind] + await outview.requestAddLink(outlink.outputs[outlink.outputs.length - 1], input) + console.log("FIN") + resolve() + } + wrap() + }) + } + + view.globalOrganize = (debug) => { + if (debug) console.log("GO: KICKOFF ORGANIZING PARTY") + // this is a request made: + /* + (1) when any view refreshes, + (2) when we load a new patch + (3) when we load a new system + (4) when we build a route with .buildRoute(output, input) + - this doesn't change topologies, or make any requests to managers, + - it just organizes visually + */ + // we need to recurse here, + let recursor = (scope, order) => { + if (debug) console.log(`Global Organize: recurses ${scope.name} at ${order}`) + // scope is a view (hunk) + // order is nth- level down tree, with us (toplevel) at root 0 + if (debug) console.log(`GO: scope defs, tl defs`) + for (let df of scope.defs) { + if (df.type === 'link' && df.grouptype !== 'edgecased') { + // find recirprocal view relationship via trace, which returns an input, thru links, + // given some output + if (debug) console.log(`GO: trace from ${df.name} in ${scope.name}`) + let rvi = trace(df.outputs[1], debug) + if (debug) console.log(`GO: trace returns`, rvi) + if (rvi) { + // we have ah link definition, and ah view definition, connected by routing, + // so we are safe to do + let rvd = rvi.parent + // if the rvd is a manager, this is the bottom level -> a link thru to a manager, + if (rvd.type === 'manager') continue + // and, + if (debug) console.log(`GO: wrap ${df.name} around ${rvd.name}`) + df.wrapon(rvd) + rvd.unwrap() + // find the dataport + let dtprt = trace(df.outputs[0]) + if (dtprt) { + dtprt.parent.unwrap() + } + // now, if we have ll data, + if (rvd.hunk.hasRefreshed) { + // find the interior link (by search for ol state) + let oind = df.states.find((cnd) => { + return cnd.name === 'otherLink' + }).value + if (!oind) throw new Error('cannot find link oind state for hookup') + // doth it ? + let internalLink = rvd.hunk.defs[oind] + if (internalLink) { + if (internalLink.type !== 'link') { + console.error('link mixup alert') + console.error(internalLink); + } + // hook em up + df.reciprocalLink = internalLink + internalLink.reciprocalLink = df + // and do, + internalLink.edgecase() + // still logging these, bc it's nice 2 kno + if (debug) console.log(`GO cn link ${df.name} to ${internalLink.name}`) + // done w/ internal, now we can + recursor(rvd.hunk, ++order) + } else { + console.error("organizing ... cannot find a reciprocal link") + } + } + } + } + } + // and roll zerbraHeirarchy in here also, using order ... + } + // kickoff w/ + recursor(view, 1) + view.zebraHeirarchy() + } // end globalOrganize + + view.zebraHeirarchy = (debug) => { + // we can go about this just by the way the visual relationship is organized, + let traverse = (view, lvl) => { + if (lvl > 6) { + console.warn('zebraHeirarchy traverses 6+ levels, you sure about this? exiting to avoid infinite loop') + return + } + lvl++ + if (debug) console.log(`ZH traverses to ${view.name} at lvl${lvl}`) + for (let df of view.defs) { + if (df.type === 'link') { + if (df.floatGroupType === 'wrappedon') { + // this link 'contains' a view, + if (lvl % 2 === 0) { + if (debug) console.log(`ZH sets ${view.name} to f0`) + $(df.reciprocalView.dom).css('background-color', '#f0f0f0') + //$(df.deg.native).children('.view').css('background-color', '#f0f0f0') + } else { + if (debug) console.log(`ZH sets ${view.name} to e0`) + $(df.reciprocalView.dom).css('background-color', '#e0e0e0') + //$(df.deg.native).children('.view').css('background-color', '#e0e0e0') + } + traverse(df.reciprocalView, lvl) + } + } + } + } + traverse(view, 0) + } + + view.expandLink = (linkDef) => { + return new Promise((resolve, reject) => { + // to avoid mayhem, do + linkDef.floaters[0].fix() + // and then, + view.requestAddHunk('view').then((viewDef) => { + // jquery moves automatically ? + console.log('EL: the view', viewDef.name) + console.log('EL: the link', linkDef.name) + // now we'd like to find a route from the view to the link + // since we're global, we could try to build the 1st link, + view.buildRoute(viewDef.outputs[0], linkDef.inputs[1]).then(() => { + console.log("EL: Build Route Down Complete") + return view.buildRoute(linkDef.outputs[1], viewDef.inputs[0]) + }).then(() => { + console.log("EL: Build Route UP Complete") + view.globalOrganize() + resolve(viewDef) + }).catch((err) => { + console.error('EL: probable error during route construction') + reject(err) + }) + }) + }) + } // end expand recipe + + /* QUEEN HANDLERS */ + + window.onresize = () => { + view.onresize() + } + + // built fast, should live with patchset + view.restoreEntireSystem = (name, debug) => { + // force it + debug = true + return new Promise((resolve, reject) => { + view.patchset.getSystem(name).then((sys) => { + if (debug) console.log('RESTORE SYSTEM: sys object', sys) + // startup, track views to look for + let recount = []; + // ah recursor: + let recursor = (scope, slice) => { + if (debug) console.log(`RESTORE SYSTEM: ${scope.name}`) + scope.patchset.mergePatch(slice, false).then(() => { + // done here, + recount.splice(recount.indexOf(scope.name), 1) + // check if more to do + for (let df of scope.defs) { + if (df.type === 'link') { + if (debug) console.log('RESTORE SYSTEM: found this link', df.name) + // match link / contains to ... + let vw = trace(df.outputs[1]) + if (vw) { + // this is a hack, + // if we're going to add more shit, we should increase by at least ... + if (vw.name === 'tlview') continue + vw = vw.parent.hunk + // if there's lower level work to do... (if this link 'contains' another patch, recursing) + let nl = slice.hunks[df.ind].contains + if (nl) { + if (debug) console.log('RESTORE SYSTEM: would like to load', nl) + recount.push(vw.name); + vw.refresh().then(() => { + if (debug) console.log('RESTORE SYSTEM: refreshed the context for, now recursing') + recursor(vw, nl) + }) + } else { + if (debug) { + console.log(`RESTORE SYSTEM: nothing contained in next link`) + } + } + } else { + if (debug) console.log(`RESTORE SYSTEM: no return from trace`) + } + } + } + //console.warn("RECOUNT NOW: ", recount) + if (recount.length < 1) { + //console.warn("RESTORE COMPLETE") + resolve() + } + }) + } + // startup, + recount.push(view.name) + recursor(view, sys) + }) + }) + } + +} diff --git a/style.css b/style.css index 8c0ea3a..70fef02 100644 --- a/style.css +++ b/style.css @@ -70,6 +70,13 @@ body { height: 10px; } +.subplane{ + position: absolute; + background-color: #c1c1c1;; + width: 800px; + height: 800px; +} + #programMenu { position: absolute; width: 245px; diff --git a/view/blocks.js b/view/blocks.js index c99f83e..da3537c 100644 --- a/view/blocks.js +++ b/view/blocks.js @@ -258,31 +258,40 @@ let rebuildDef = (def, position) => { def.blocks.push(stateblock) } - // claim dom elements, global search - let native = $(def.context.plane).find('.hunk').toArray().find((cand) => { - return cand.handle.ind === def.ind - }) - if (native) { - // issa block, - $(native).addClass('block') - // ok, I think I want to position this above everything else, - native.position = () => { - // this is more difficult, as we are looking @ the other blocks, - let y = 0 - for (let bl of def.blocks) { - if ($(bl).is('.input') || $(bl).is('.output') || $(bl).is('.hunk')){ - continue - } else { - y += bl.clientHeight + 5 + // find yourself in t h e g l o b a l d o m a i n + if(def.context === window.tlv){ + let hunk = window.hunks[def.ind] + if(!((hunk.name === def.name) && (hunk.type === def.type))) throw new Error('this should never be the case') + def.hunk = hunk + // additionally, + // claim dom elements, global search + let native = $(def.context.plane).find('.hunk').toArray().find((cand) => { + return cand.hunk.ind === def.ind + }) + if (native) { + // issa block, + $(native).addClass('block') + native.handle = def + // ok, I think I want to position this above everything else, + native.position = () => { + // this is more difficult, as we are looking @ the other blocks, + let y = 0 + for (let bl of def.blocks) { + if ($(bl).is('.input') || $(bl).is('.output') || $(bl).is('.hunk')){ + continue + } else { + y += bl.clientHeight + 5 + } } + return { x: 0, y: y } } - return { x: 0, y: y } + // attach to title, + titleblock.take(native) + def.blocks.push(native) } - // attach to title, - titleblock.take(native) - def.blocks.push(native) } + // now the hairier work, if(def.type == 'link'){ let squid = $('<div>').addClass('loosebutton').addClass('block').get(0) @@ -294,13 +303,19 @@ let rebuildDef = (def, position) => { def.blocks.push(squid) let open = (evt) =>{ // returns the view that connects to this link, - def.context.routelink(def).then((view) => { + def.context.routelink(def).then((vdef) => { console.log('route ok') - // we *do* want to walk these, so, - def.contains = view + // we have vdev.tlv, so, the definition has a handle to the + // top level view, why? + // we *do* want to walk these, so we want a handle to + // the view element - not just its def ... + def.contains = vdef.hunk + console.log('"view"', view) // toggle button state, $(squid).html('<i class="em em-shell"></i> collapse link') $(squid).one('click', close) + // nooow... + //view.refresh() }).catch((err) => { console.error(err) }) @@ -371,13 +386,13 @@ let repositionDef = (def) => { // use block.wires ... if exists, // but there is an order issue: we need to see about inputs also, update on both ends -let redrawDef = (def, plane) => { +let redrawDef = (def) => { console.log('redraw for def', def.name) // this wipes and redraws, rebuildDef(def) // place them all in the context, for (let bl of def.blocks) { - $(plane).append(bl) + $(def.context.plane).append(bl) } // arrange (should draw links as well) repositionDef(def) @@ -402,738 +417,3 @@ export default { redrawContexWires, // when context link topology changes wipeContext // to shutdown / cleanup } - -// ---------------------------------------------------------------------- END HOT NEWNESS -// ---------------------------------------------------------------------- END HOT NEWNESS -// ---------------------------------------------------------------------- END HOT NEWNESS -// ---------------------------------------------------------------------- END HOT NEWNESS -// ---------------------------------------------------------------------- END HOT NEWNESS - -// new this - -function HunkDefinition(spec, view, dt, debug) { - // the basics, - this.ind = spec.ind - this.name = spec.name - this.type = spec.type - this.inputs = new Array() - this.outputs = new Array() - this.states = new Array() - // yep, - this.parentView = view - - // dom elements group, containing (but not limited to) - this.deg = { - core: {} - } - this.isExploded = false - this.floatGroupType = 'collected' - - // core ... - this.deg.core = $('<div>').addClass('defcore').attr('id', `${this.name}_${this.ind}`).get(0) - let title = $(`<div>${this.name}</div>`).addClass('deftitle').addClass('header').get(0) - $(title).append($(`<span style="float:right">(${this.type})</span>`)) - - $(this.deg.core).append(title) - if (spec.states.length > 0) { - let statedefom = $('<div>').addClass('states') - for (let st in spec.states) { - let state = new StateDefinition(spec.states[st], st, this, view, debug) - this.states.push(state) - $(statedefom).append(state.de) - } - $(this.deg.core).append(statedefom) - } - - // init our floater array, and startup with 0th entry (one floater per def == standard) - this.floaters = [] - this.floaters.push(new Floater(view, 'std')) - - // write inputs - if (spec.inputs.length > 0) { - // idom, odom want title bars ... - let idom = $('<div>').addClass('inputs') - $(idom).append($(`<div>inputs >></div>`).addClass('inputheader').addClass('header')) - for (let ip in spec.inputs) { - let input = new InputDefinition(spec.inputs[ip], ip, this, debug) - this.inputs.push(input) - $(idom).append(input.de) - } - this.deg.inputs = $(idom).get(0) - this.floaters[0].take(this.deg.inputs, this, true) - } - // write outputs, - if (spec.outputs.length > 0) { - let odom = $('<div>').addClass('outputs') - $(odom).append($(`<div>>> outputs</div>`).addClass('outputheader').addClass('header')) - for (let op in spec.outputs) { - let output = new OutputDefinition(spec.outputs[op], op, this, view, dt, debug) - this.outputs.push(output) - $(odom).append(output.de) - } - this.deg.outputs = $(odom).get(0) - this.floaters[0].take(this.deg.outputs, this, true) - } - - // title right-click handler - title.oncontextmenu = (evt) => { - evt.preventDefault() - evt.stopPropagation() - // write menu for requesting delete and copy - let menu = $('<div>').addClass('contextmenu').get(0) - $(menu).append($('<li><i class="em em-coffin"></i> remove hunk</li>').on('click', (evt) => { - view.requestRemoveHunk(this.ind) - $(menu).remove() - })) - $(menu).append($('<li><i class="em em-abc"></i> rename hunk</li>').on('click', (evt) => { - $(evt.target).text('') - let tinput = $('<input>').attr('type', 'text').attr('size', 24).attr('value', 'new name').get(0) - $(evt.target).append(tinput) - $(tinput).focus() - $(tinput).select() - $(tinput).on('keyup', (evt) => { - if (evt.keyCode == 13) { - view.requestRenameHunk(this.ind, tinput.value).then((def) => { - console.log('cleared new name for', def) - $(menu).remove() - }) - } - }) - })) - $(menu).append($('<li><i class="em em-repeat_one"></i> copy hunk</li>').on('click', (evt) => { - // todo: maybe this should copy state and req it down? - view.requestAddHunk(this.type) - $(menu).remove() - })) - // place it, - // title.offsetWidth is 400 - // de.style.left, de.style.top, de.clientWidth, - let ct = dt.readTransform(this.deg.core) - let mp = { - s: 1, - x: ct.x, - y: ct.y - 31 * 3 - 10 - } - if (this.containsButtons()) - mp.y -= 25 - if (view.isTopLevel) { - // write it, - $(menu).append($('<li><i class="em em-arrows_counterclockwise"></i> reload from source</li>').on('click', (evt) => { - view.reloadHunk(this.ind, false) - })) - mp.y -= 31 - } - dt.writeTransform(menu, mp) - $(view.plane).append(menu) - } - - // take the core, not stalling, - this.floaters[0].take(this.deg.core, this, false) - - /* --------------------------- ---------------------------- */ - /* ------------------------- PHYSICS ------------------------- */ - /* --------------------------- ---------------------------- */ - - this.highlight = () => { - $(this.deg.core).find('.deftitle').css('background-color', '#969696') - if (this.deg.inputs) - $(this.deg.inputs).find('.inputheader').css('background-color', 'white').css('color', 'black') - if (this.deg.outputs) - $(this.deg.outputs).find('.outputheader').css('background-color', 'white').css('color', 'black') - if (this.deg.native) - $(this.deg.native).find('.nativeheader').css('background-color', 'white').css('color', 'black') - } - - this.unhighlight = () => { - $(this.deg.core).find('.deftitle').css('background-color', '#303030') - if (this.deg.inputs) - $(this.deg.inputs).find('.inputheader').css('background-color', 'red').css('color', 'white') - if (this.deg.outputs) - $(this.deg.outputs).find('.outputheader').css('background-color', 'blue').css('color', 'white') - if (this.deg.native) - $(this.deg.native).find('.nativeheader').css('background-color', 'green').css('color', 'white') - } - - /* --------------------------- ---------------------------- */ - /* ------------------------- UPDATES ------------------------- */ - /* --------------------------- ---------------------------- */ - - // UPDATE Index - this.newInd = (ind) => { - this.ind = ind - // and those titles, and ids ... - $(this.de).attr('id', `${this.name}_${this.ind}`) - $(title).text(`${this.name}`) - title.append($(`<span style="float:right">(${this.type})</span>`).get(0)) - for (let ip of this.inputs) { - ip.onNewParentInfo() - } - for (let op of this.outputs) { - op.onNewParentInfo() - } - for (let st of this.states) { - st.onNewParentInfo() - } - } - - // UPDATE Name - this.updateName = (newName) => { - this.name = newName - $(this.de).attr('id', `${this.name}_${this.ind}`) - $(title).text(`${this.name}`) - title.append($(`<span style="float:right">(${this.type})</span>`).get(0)) - for (let ip of this.inputs) { - ip.onNewParentInfo() - } - for (let op of this.outputs) { - op.onNewParentInfo() - } - for (let st of this.states) { - st.onNewParentInfo() - } - } - - this.cleanup = () => { - cleanButtons() - cleanFloaters() - for (let od in this.deg) { - $(this.deg[od]).remove() - } - } - - let cleanFloaters = () => { - for (let flt of this.floaters) { - flt.cleanup() - } - this.floaters.length = 0 - } - - this.containsButtons = () => { - for (let fl of this.floaters) { - for (let it of fl.bag) { - if (it.type === 'button') - return true - } - } - return false - } - - let cleanButtons = () => { - // sloppy! there is probably one in one of the cores, - try { - // max 5... - for (let i = 0; i < 5; i++) { - let bindex = this.floaters[0].bag.findIndex((cnd) => { - return cnd.type === 'button' - }) - if (bindex !== -1) { - // also going to get that typeset issue ... - // pls to god this is reworked before a bug resulting appears - // rm from the dom, - $(this.floaters[0].bag[bindex].de).remove() - this.floaters[0].bag.splice(bindex, 1) - } else { - break - } - } - // now we can - this.floaters[0].onChange() - } catch (err) { - console.error(`couldn't clean buttons from ${this.name}, for reasons:`, err) - } - } - - /* --------------------------- ---------------------------- */ - /* ----------------- RM/ADD/ECT to FLOATERS ------------------ */ - /* --------------------------- ---------------------------- */ - - this.collect = () => { - // restore state, collect .deg under one hood (what of buttons?) - console.error('not yet collecting') - } - - this.unwrap = () => { - // never twice - if (this.floatGroupType === 'unwrapped') - return - // otherwise - this.floatGroupType = 'unwrapped' - // free willy - // ok, can we just murder everything and start from scratch? - // still have those .deg elements, so ... - // will have to write a remove - //console.log('UNWRAP') - cleanFloaters() - // (unrwapped) has a core with the core, outputs, and native - let cflt = new Floater(view, 'unwrapped') - if (this.deg.outputs) { - cflt.take(this.deg.outputs, this, true) - } - if (this.deg.native) { - if (this.type === 'view' && view) { - //console.log('ommitting view deg from this def') - } else { - cflt.take(this.deg.native, this, true) - } - } - cflt.take(this.deg.core, this) - if (this.deg.native) { - if (this.type === 'view' && view) { - //console.log('ommitting view deg from this def') - } else { - cflt.fitNativeToFloater() - } - } - this.floaters.push(cflt) - // if there are inputs, they are separate - if (this.deg.inputs) { - let iflt = new Floater(view, 'unwrapped') - iflt.take(this.deg.inputs, this) - this.floaters.push(iflt) - } - // and kick that - view.floop.reset() - } - - // for links, accept a view domain element, add to ur floater, wrap that floater - this.wrapon = (viewDef) => { - // don't do it twice - if (this.floatGroupType === 'wrappedon') - return - // set state - this.floatGroupType = 'wrappedon' - // errs when not a link, - if (this.type !== 'link') - throw new Error('non-links doth not wrap') - // first stat, we want to keep this ref, - this.reciprocalView = viewDef.hunk - // ok, and we want viewdef objects to highlight ... - // if we're not gathered to start, that's probably bad news - if (this.floaters.length > 1) - console.error('wyd here? many floaters during a wrap', this.floaters) - // get the floater's bag-item containing that view ... - let vflt = viewDef.floaters.find((cnd) => { - return cnd.typeset.includes('native') - }) - if (!vflt) - throw new Error('could not find view floater on swap') - // - let vbagindex = vflt.bag.findIndex((cnd) => { - return cnd.type === 'native' - }) - if (vbagindex === -1) - throw new Error('could not find floater native item on swap') - let vbagitem = vflt.bag.splice(vbagindex, 1)[0] - // before we do this, let's push out some extra space - // this *is a hack* - let bnds = view.getCurrentBounds() - view.requestResize(1800, bnds.h + 600) - // we need to make sure it's in the link's plane, jquery is smart about this, - //console.log(view.plane, vbagitem.de) - $(view.plane).append(vbagitem.de) - // the link's 1st floater, - let flt = this.floaters[0] - flt.grouptype = 'wrapped' - // flt.take au manuel - flt.bag.push(vbagitem) - flt.typeset.push('native') - vbagitem.de.onResizeCustomCallback = flt.onElementResize - // can just do recalc now? - flt.onChange() - // and also, - vflt.onChange() - // and *also* - this.removeButton('<i class="em em-squid"></i>') - this.addButton('<i class="em em-shell"></i>', (evt) => { - console.error('no collapse yet') - }) - } - - this.edgecase = () => { - // check - if (this.floatGroupType === 'edgecased') - return - // or set - this.floatGroupType = 'edgecased' - // also links only pls, - if (this.type !== 'link') - throw new Error('non-links doth not edge') - // almost forgot, we want to get rid of this button: - cleanButtons() - // similar to the unwrap spec, - // we're also assuming at this point that this view isn't wrapped around a view - cleanFloaters() - // now we want a left floater, - // free willy - let lflt = new Floater(view, 'edges') - lflt.take(this.deg.core, this, true) - lflt.take(this.deg.outputs, this, true) - lflt.makeEdges() - this.floaters.push(lflt) - // and the right, - let rflt = new Floater(view, 'edges') - rflt.take(this.deg.inputs, this, true) - rflt.makeEdges() - this.floaters.push(rflt) - // ok, - view.floop.reset() - // put inputs / outputs at edges of view body - // put core below inputs, at left - // make message box of the view sit low - // handle view resizes ? - } - - // adden em - this.addButton = (text, callback) => { - // write a button, - let btn = $(`<div>${text}</div>`).addClass('defbutton').get(0) - $(btn).on('click', callback).on('mouseover', (evt) => { - document.body.style.cursor = 'pointer' - }).on('mouseout', (evt) => [document.body.style.cursor = 'auto']) - // ok ok, then ... - // we find the floater that contains the core, - let coreFloater = this.floaters.find((cnd) => { - return cnd.typeset.includes('core') - }) - if (!coreFloater) - throw new Error(`couldn't find a floater for this def button`, text) - // attach the button element to its bag ... - coreFloater.take(btn, this) - } - - this.removeButton = (text, callback) => { - // find the core-containing floater - // we find the floater that contains the core, - let coreFloater = this.floaters.find((cnd) => { - return cnd.typeset.includes('core') - }) - if (!coreFloater) - throw new Error(`couldn't find a floater for this def button`, text) - let success = false - // au manuel splice outta the bag - for (let item in coreFloater.bag) { - let itm = coreFloater.bag[item] - if (itm.type === 'button') { - if ($(itm.de).html() === text) { - // splice it out, - coreFloater.bag.splice(item, 1) - $(itm.de).remove() - coreFloater.onChange() - success = true - } - } - } - if (!success) - console.error(`couldn't remove the button having text ${text}`) - - } - - // absorbing a native hunk's domain element, - this.takeNative = (hunk) => { - this.hunk = hunk - // wants a wrapper, - this.deg.native = $('<div>').addClass('nativewrap').get(0) - // wrapper includes this title bar, - $(this.deg.native).append($(`<div>${this.name}'s html element</div>`).addClass('nativeheader').addClass('header')) - // and the hunk's element goes in that wrapper. - $(this.deg.native).append(this.hunk.dom) - // now we can hook it 2 a floater ... this will depend on our state, which we can mod later - // atm, we'll put it with the core, - let coreFloater = this.floaters.find((cnd) => { - return cnd.typeset.includes('core') - }) - if (!coreFloater) - throw new Error(`couldn't find a floater for this def native`) - // hook it - coreFloater.take(this.deg.native, this, false, this.hunk.onresize) - // has this handle, - this.hunk.requestResize = (x, y) => { - // set manual and call update, - // doesn't matter which hunk this is in... - // console.log(`REQRESIZE hunk ${this.name} requesting size of ${x}, ${y}`) - // console.log('native', this.deg.native) - this.deg.native.requestResize(x, y) - view.tick() - } - // now we should be able to, - this.hunk.onload() - } - - // add special case buttons, just this so far - if (this.type === 'link') { - // todo: - let expander = () => { - this.addButton('<i class="em em-squid"></i>', (evt) => { - // ??? - view.tlv.expandLink(this).then((view) => { - console.log("EXPANDEEED --> expandLink promise completes", view) - }).catch((err) => { - console.log("EXP catches err", err) - }) - }) - } - expander() - } - - this.fixWithDataPort = () => { - if (this.floaters[0].isFixed) { - // find the data port, - let dp = view.tlv.trace(this.outputs[0]) - if (dp) { - dp.parent.floaters[0].fixTo(this.floaters[0].fx + 500, this.floaters[0].fy - 100) - } else { - console.error("no data port from trace") - } - } else { - console.error("won't fix with data port: not fixed") - } - } - -} // end def def - -/* --------------------------- ---------------------------- */ -/* ------------------------ INPUT DEF ------------------------ */ -/* --------------------------- ---------------------------- */ - -function InputDefinition(ipspec, ind, def, debug) { - // keep track of name and type, - this.name = ipspec.name - this.type = ipspec.type - this.parent = def - this.ind = parseInt(ind) - // a dom element - this.de = $(`<li>${this.name} (${this.type})</li>`).addClass('input').get(0) - this.de.id = `${this.parent.name}_${this.parent.ind}_input_${this.name}` - this.onNewParentInfo = () => { - this.de.id = `${this.parent.name}_${this.parent.ind}_input_${this.name}` - } - // we also keep a list, - this.connections = new Array() - this.disconnect = (output) => { - let index = this.connections.findIndex((cand) => { - return (cand.parent.ind === output.parent.ind && cand.name === output.name && cand.type === output.type) - }) - if (index === -1) - throw new Error('during output disconnect, input cannot find output...') - this.connections.splice(index, 1) - } - this.disconnectAll = () => { - for (let op of this.connections) { - op.disconnect(this) - } - this.connections.length = 0 - } - // to get this object via the dom, circular... apparently that is fine ? - this.de.hookup = this -} - -/* --------------------------- ---------------------------- */ -/* ----------------------- OUTPUT DEF ------------------------ */ -/* --------------------------- ---------------------------- */ - -function OutputDefinition(opspec, ind, def, view, dt, debug) { - // keep track of name and type, - this.parent = def - this.name = opspec.name - this.type = opspec.type - this.ind = parseInt(ind) - // a dom element - this.de = $(`<li>(${this.type}) ${this.name}</li>`).addClass('output').get(0) - this.de.id = `${this.parent.name}_${this.parent.ind}_output_${this.name}` - this.onNewParentInfo = () => { - this.de.id = `${this.parent.name}_${this.parent.ind}_output_${this.name}` - } - // outputs handle all of the dragging-etc - if (opspec.connections !== undefined) { - this.specConnections = opspec.connections - } - this.connections = new Array() // of inputdefs, ! - this.connect = (inputdef) => { - inputdef.connections.push(this) - this.connections.push(inputdef) - } - this.disconnect = (inputdef) => { - inputdef.disconnect(this) - let iof = this.connections.findIndex((cand) => { - return (cand.name === inputdef.name && cand.parent.ind === inputdef.parent.ind) - }) - if (iof === -1) - throw new Error('could not find input to disconnect') - this.connections.splice(iof, 1) - return true - } - this.disconnectAll = () => { - for (let ip of this.connections) { - ip.disconnect(this) - } - this.connections.length = 0 - } - // the dragging - this.floater = {} - this.hasFloater = false - // ondrag, attached later - let evtDrag = (evt) => { - evt.preventDefault() - evt.stopPropagation() - let pt = dt.readTransform(view.tlv.plane) - let thet = dt.readTransform(this.floater) - // ... set delta - thet.x += evt.movementX / pt.s - thet.y += evt.movementY / pt.s - dt.writeTransform(this.floater, thet) - view.drawLinks() - } - // to remove the below - let dragMouseUp = (evt) => { - if (debug) - console.log('MOUSEUP ON', evt.target.id) - // 1st, make sure it's an input - if ($(evt.target).is('.input')) { - // HERE: searcheth by input id text? or - let hk = evt.target.hookup - // hookups are hooks to the input's def-object - // console.log('ah hookup looks like:', hk) - if (!hk) - throw new Error('missing some data at this input...') - // use a dom data flag, to find that input and output id? - // are we in the same context? - if (hk.parent.parentView === this.parent.parentView) { - view.requestAddLink(this, hk) - } else { - console.warn("UI Route Builder Begins...") - this.parent.parentView.tlv.buildRoute(this, hk).then(() => { - console.warn("UI Route Builder Resolves !") - }) - } - // do things to conn, then - } - // cleanup - document.removeEventListener('mouseup', dragMouseUp) - document.removeEventListener('mousemove', evtDrag) - // remove the floater flag - this.hasFloater = false - // remove the floater itself - $(view.plane).find('#floater').remove() - view.drawLinks() - } - // eeeeehntr for link-hookup-dragging - this.de.onmousedown = (evt) => { - evt.stopPropagation() - evt.preventDefault() - if (debug) - console.log('mousedown for', this) - // this and that flag will be read-in on drawlinks, to draw that link - this.floater = $('<div>').attr('id', 'floater').append(this.type).get(0) - this.hasFloater = true - this.floater.style.zIndex = '1' - // set initial position by the outputs' position, - let dparent = $(this.de).parent().get(0) - let opp = dt.readTransform(dparent) - // top-level plane ... - let pt = dt.readTransform(view.tlv.plane) - // plonk: have to do this now or else clientHeight / width are 0 - view.plane.appendChild(this.floater) - // init out floater position, and put it in the dom - dt.writeTransform(this.floater, { - s: 1, - x: opp.x - ((this.floater.clientWidth + 5)), - y: opp.y + this.floater.clientHeight * (this.ind + 0.5) // - ((fltheight * pt.s) / 2) / pt.s - }) - // handlers to drag, and remove - document.addEventListener('mousemove', evtDrag) - // and delete / act when mouse comes up - document.addEventListener('mouseup', dragMouseUp) - } -} - -/* --------------------------- ---------------------------- */ -/* ------------------------ STATE DEF ------------------------ */ -/* --------------------------- ---------------------------- */ - -function StateDefinition(stspec, ind, def, view, debug) { - this.parent = def - this.name = stspec.name - this.type = stspec.type - this.ind = parseInt(ind) - // business, - this.value = stspec.value - // ok, - this.de = $('<div>' + this.name + " (" + this.type + ")" + '</div>').addClass('stateItem').get(0) - this.de.id = `${this.parent.name}_${this.parent.ind}_state_${this.name}` - this.onNewParentInfo = () => { - this.de.id = `${this.parent.name}_${this.parent.ind}_state_${this.name}` - } - // ui for these ... we can just cover the basics of js types because yonder serializations - // etc will throw errors for other types. of course, this could help more, but we're in a rush - switch (typeof this.value) { - case 'string': - //dom.append($('<br>').get(0)) - let strinput = $('<input>').attr('type', 'text').attr('size', 32).attr('value', this.value).css('width', '240px').get(0) - strinput.addEventListener('change', (evt) => { - // ask for a change, - // TODO HERE NOW: this is the state change request you want to write - // do it like writeMessage() instead - // requestStateChange(def.id, state, strinput.value) - // but assert that we don't change the definition unless - view.requestStateChange(this, strinput.value) - strinput.value = this.value - }) - this.de.append(strinput) - this.set = (value) => { - if (typeof value === 'string') { - strinput.value = value - this.value = value - } else { - throw new Error('bad type put into state dom') - } - } - break // end string types - case 'number': - let ninput = $('<input>').addClass('stateNumInput').attr('type', 'text').attr('size', 24).attr('value', this.value.toString()).css('width', '100px').get(0) - ninput.addEventListener('change', (evt) => { - // ask for a change, watch for float or for int ... - if (isIntType(this.type)) { - view.requestStateChange(this, parseInt(ninput.value)) - } else { - view.requestStateChange(this, parseFloat(ninput.value)) - } - // but assert that we don't change the definition unless - ninput.value = this.value - }) - this.de.append(ninput) - this.set = (value) => { - if (typeof value === 'number') { - // quite sure js does this conversion no problem - ninput.value = value - this.value = value - } else { - throw new Error('bad type put into state dom') - } - } - break // end numnber type - case 'boolean': - let span = $('<span style="float:right;">' + this.value.toString() + '</span>').get(0) - $(this.de).addClass('stateBooleanItem') - this.de.append(span) - this.de.addEventListener('click', (evt) => { - // read the current 'state' (as written) and send the opposite - let txt = $(span).text() - if (txt === 'true') { - view.requestStateChange(this, false) - } else { - view.requestStateChange(this, true) - } - }) - this.set = (value) => { - if (typeof value === 'boolean') { - $(span).text(value.toString()) - this.value = value - } else { - throw new Error('bad type put into state dom') - } - } - break // end boolean type - default: - console.error(`unaccounted for type at input pull for state change, ${typeof state.value}`) - break - } -} -- GitLab