Skip to content
Snippets Groups Projects
view.js 37.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jake Read's avatar
    Jake Read committed
    /*
    
    Jake Read's avatar
    Jake Read committed
    
    
    Jake Read's avatar
    Jake Read committed
    
    
    Jake Read's avatar
    Jake Read committed
     - scrape to manager,
     - manager gogetter ... and then, hello click
     - and then, nicely:
     - also - link for flowcontrol when downstream non-conn ? the init-over-link question? a few states ...
     - div structure ... ? open something up just barely, to test node and scraper
     - beer 2 celly
     - div structure: the div(div) scaling unfuckery
    
    Jake Read's avatar
    Jake Read committed
    
    */
    
    
    Jake Read's avatar
    Jake Read committed
    import {
      Hunkify,
      Input,
      Output,
      State
    } from './hunks.js'
    
    
    import {
    
      TSET, // typset
      MK, // manager keys,
      HK, // hunk keys,
    
      MSGS, // messaging
      isIntType,
      isFloatType,
      isNumType
    
    } from '../typeset.js'
    
    
    Jake Read's avatar
    Jake Read committed
    // yonder def, the ui mirror on hunks
    import HunkDefinition from '../view/vdef.js'
    
    
    // to file-organize view.js, a monster
    
    import DomTools from '../view/vdom.js'
    
    import BezierTools from '../view/vbzt.js'
    
    Jake Read's avatar
    Jake Read committed
    import MessageBox from '../view/vmsg.js'
    
    Jake Read's avatar
    Jake Read committed
    import PatchSet from '../view/vptch.js'
    
    Jake Read's avatar
    Jake Read committed
    
    
    Jake Read's avatar
    Jake Read committed
    // the force loop
    import Floop from '../view/vfloop.js'
    
    
    Jake Read's avatar
    Jake Read committed
    function View() {
    
    Jake Read's avatar
    Jake Read committed
      Hunkify(this)
    
    Jake Read's avatar
    Jake Read committed
      let verbose = false
      let msgverbose = false
    
      let msgsin = new Input('byteArray', 'msgs', this)
    
      let msgsout = new Output('byteArray', 'msgs', this)
    
    Jake Read's avatar
    Jake Read committed
    
      // das UI globals
      // our dom is the space we're allotted,
      this.dom = {}
      // the plane, one layer beneath, is where divs live
      this.plane = {}
    
    Jake Read's avatar
    Jake Read committed
      // we have a list of definitions,
      let defs = new Array()
    
    Jake Read's avatar
    Jake Read committed
      // #ref, sloppy
      this.defs = defs
    
      // tools to write dom-representations of hunks,
      let dt = new DomTools(this)
    
      // a handy box, for stuff
    
    Jake Read's avatar
    Jake Read committed
      let msgbox = new MessageBox(this)
    
    Jake Read's avatar
    Jake Read committed
      this.msgbox = msgbox
    
    Jake Read's avatar
    Jake Read committed
      this.interpreterName = null
      this.interpreterVersion = null
    
      // and tools for the links,
      let bzt = new BezierTools(this)
    
    Jake Read's avatar
    Jake Read committed
      // and for program (patch) management
      let patchset = new PatchSet(this, msgbox)
    
    Jake Read's avatar
    Jake Read committed
      this.patchset = patchset
    
    Jake Read's avatar
    Jake Read committed
      // here ... at this point,
      /*
      an init order, etc, for each view to have top-level access ...
      for tls stuff
      for contextmenu , do (tlv.oncontext(this, event))
      ... aaaand ?
      */
      // the toplevel view,
      this.tls = 1
    
      // this is one of the biggest outstanding issues w/ this whole thing:
      // views have child issues
      this.tlv = this
    
    Jake Read's avatar
    Jake Read committed
    
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
      /* -------------------- INIT, LISTENERS ---------------------- */
      /* ---------------------------    ---------------------------- */
    
    
    Jake Read's avatar
    Jake Read committed
      // oddity, init happens when loaded into the runtime
      // onload when we have a dom element
      // these are separate events due to cuttlefish implementation, sawry
    
    Jake Read's avatar
    Jake Read committed
      this.init = () => {
        // in the case of UIs, we have the dom before init runs,
        // so this is kind of like the 'window.onload' function
    
    Jake Read's avatar
    Jake Read committed
        // but we do need to write our dom object to start,
        // then cf will drop it wherever we plan ...
    
        console.log(`INIT for ${this.name}`)
    
    Jake Read's avatar
    Jake Read committed
        this.dom = $('<div>').addClass('view').get(0)
    
    Jake Read's avatar
    Jake Read committed
        this.dom.hunk = this
    
    Jake Read's avatar
    Jake Read committed
      }
      // END INIT CODE
    
    
    Jake Read's avatar
    Jake Read committed
      // onload is called in vdef.js ~ L350
    
    Jake Read's avatar
    Jake Read committed
      this.onload = () => {
    
        console.log(`ONLOAD for ${this.name}`)
    
    Jake Read's avatar
    Jake Read committed
        // for nested dom elements,
        this.plane = $('<div>').addClass('plane').get(0)
    
    Jake Read's avatar
    Jake Read committed
        // this needs to start with some transform ..
    
        dt.writeTransform(this.plane, {
          x: 0,
          y: 0,
          s: 1
        })
    
    Jake Read's avatar
    Jake Read committed
        // append
        $(this.dom).append(this.plane)
    
    Jake Read's avatar
    Jake Read committed
        // more space
    
        this.requestResize(1100, 500)
    
    Jake Read's avatar
    Jake Read committed
      // we also have events that a level above us can fire...
      this.onresize = () => {
    
        // don't think too hard, do
        this.kick()
        // console.error('onresize: need to move things about, probably')
        // this.drawLinks()
    
    Jake Read's avatar
    Jake Read committed
      }
    
      // dummy, will b replaced
      this.requestResize = (x, y) => {
    
    Jake Read's avatar
    Jake Read committed
        let bounds = this.getCurrentBounds()
        if (x < bounds.w && y < bounds.h) {
          // it's fine
        } else {
    
          console.warn('requestResize not yet implemented by parent')
    
    Jake Read's avatar
    Jake Read committed
      this.requestSizeIncrease = (x, y) => {
    
        //console.log('req', x, y)
        let bounds = this.getCurrentBounds()
        this.requestResize(bounds.w + x, bounds.h + y)
    
      // here bc related to resizing, sizes
      // this is re-written during .makeTopLevel
      this.getCurrentBounds = () => {
        // more like
        let x1 = 0
        let y1 = 0
        let x2 = this.dom.clientWidth
        let y2 = this.dom.clientHeight
        // console.log(`bounds: ${this.name}: ${w}, ${h}`, this.dom)
        return {
          x1: x1,
          y1: y1,
          x2: x2,
          y2: y2,
    
    Jake Read's avatar
    Jake Read committed
          w: x2 - x1,
          h: y2 - y1
    
    Jake Read's avatar
    Jake Read committed
      this.isTopLevel = false
    
    Jake Read's avatar
    Jake Read committed
      // the top level view, gifted to this hunk by omniscience
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
      /* ------------------- END MAKETOPLEVEL ---------------------- */
      /* ---------------------------    ---------------------------- */
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
    
    Jake Read's avatar
    Jake Read committed
      /* ----------------------- REDRAWING ------------------------- */
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
    
    
    Jake Read's avatar
    Jake Read committed
      this.floop = new Floop(this)
    
    
      this.drawLinks = () => {
    
        // drawing from scratch every time ! could be faster, probably
    
        bzt.clear(this.plane)
    
    Jake Read's avatar
    Jake Read committed
        //this.followRules()
    
        // draw 'em all
    
    Jake Read's avatar
    Jake Read committed
        for (let def of defs) {
          for (let output of def.outputs) {
            for (let input of output.connections) {
    
              bzt.drawLink(output, input)
            }
    
    Jake Read's avatar
    Jake Read committed
            if (output.hasFloater) {
    
              let head = bzt.getRightHandle(output.de)
              let tail = bzt.getFloaterHandle(output.floater)
              bzt.writeBezier(head, tail)
    
    Jake Read's avatar
    Jake Read committed
      // kick: topology changes
    
      this.kick = () => {
    
    Jake Read's avatar
    Jake Read committed
        //msgbox.checkHeight()
    
    Jake Read's avatar
    Jake Read committed
        this.floop.reset()
        this.drawLinks()
      }
    
    
    Jake Read's avatar
    Jake Read committed
      // tick: size / position changes
    
    Jake Read's avatar
    Jake Read committed
      this.tick = () => {
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
    
    Jake Read's avatar
    Jake Read committed
      /* ------------------- SERIAL -> HOTMESSES ------------------- */
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
    
    
      let deserializeNameType = (arr, bytes, i) => {
        let nameresp = MSGS.readFrom(bytes, i, 'string')
        let typeresp = MSGS.readFrom(bytes, i + nameresp.increment, 'string')
        arr.push({
          name: nameresp.item,
          type: typeresp.item
        })
        return nameresp.increment + typeresp.increment
      }
    
    
    Jake Read's avatar
    Jake Read committed
      let specBySerial = (bytes, start, debug) => {
    
        // deserialize
    
    Jake Read's avatar
    Jake Read committed
        let spec = {
    
        // inputs, outputs, state
    
    Jake Read's avatar
    Jake Read committed
        spec.inputs = new Array()
        spec.outputs = new Array()
        spec.states = new Array()
    
        // hold,
        let temp
        // starting at 2, msgs[0] is 'hnkalive'
    
    Jake Read's avatar
    Jake Read committed
        // lets write a goddang real structure an object (spec) with mirror type dom and access fn's ...
    
        // this will make building better and better code mucho bueno
    
        // ripperoni,
    
    Jake Read's avatar
    Jake Read committed
        try {
          outer: while (i < bytes.length) {
            switch (bytes[i]) {
              case HK.IND:
                i += 1
                temp = MSGS.readFrom(bytes, i, 'uint16')
                spec.ind = temp.item
                i += temp.increment
                break
              case HK.TYPE:
                i += 1
                temp = MSGS.readFrom(bytes, i, 'string')
                spec.type = temp.item
                i += temp.increment
                break
              case HK.NAME:
                i += 1
                temp = MSGS.readFrom(bytes, i, 'string')
                spec.name = temp.item
                i += temp.increment
                break
              case HK.INPUT:
                i += 1
                // expecting two strings here, name and then type
                i += deserializeNameType(spec.inputs, bytes, i)
                break
              case HK.OUTPUT:
                i += 1
                i += deserializeNameType(spec.outputs, bytes, i)
                if (bytes[i] === HK.CONNECTIONS) {
                  let cn = 0
                  i++
                  spec.outputs[spec.outputs.length - 1].connections = []
                  //console.log("HK B4 CONN")
                  while (bytes[i] === HK.CONNECTION) {
                    //console.log("HK CONNECTION");
                    let frdarr = bytes.slice(i + 1, i + 3)
                    //console.log("HK bytes for inHunkIndice", frdarr)
                    let fbtarr = Uint8Array.from(frdarr)
                    let fvlarr = new Uint16Array(fbtarr.buffer)
                    i += 2
                    spec.outputs[spec.outputs.length - 1].connections[cn] = [fvlarr[0], bytes[i + 1]]
                    i += 2
                  }
    
    Jake Read's avatar
    Jake Read committed
                break
              case HK.STATE:
                i += 1
                i += deserializeNameType(spec.states, bytes, i)
                // ok, and the value should trail
                // we don't *need* to know the type, could just read by the key, but,
                //console.log(`${this.name} STATE READ from ${i}`, bytes[i], bytes);
                temp = MSGS.readFrom(bytes, i, spec.states[spec.states.length - 1].type)
                //console.log(`${this.name} TEMP`, temp);
                spec.states[spec.states.length - 1].value = temp.item
                i += temp.increment
                break
              default:
                console.log('spec so far', spec)
                console.log('bytes', bytes)
                throw new Error(`unexpected key encountered during hunk deserialization at position ${i}: ${bytes[i]}`)
                break outer
            }
    
    Jake Read's avatar
    Jake Read committed
        catch (err) {
          console.warn("caught in try/catch:")
          console.error(err)
          console.log('spec so far', spec)
          console.log('bytes', bytes)
          throw new Error("halt")
        }
    
        // a check,
    
    Jake Read's avatar
    Jake Read committed
        if (debug) console.log(`broke outer, len at ${bytes.length}, i at ${i}`)
    
        // we should be able to kind of pick-thru and append based on,
    
    Jake Read's avatar
    Jake Read committed
        if (debug) console.log('spec apres deserialize', spec, 'from bytes', bytes)
    
        // we gucc ?
    
    Jake Read's avatar
    Jake Read committed
        if (spec.name == null || spec.type == null) {
          throw new Error('null spec, wyd?')
        }
    
    Jake Read's avatar
    Jake Read committed
        return spec
    
      /* ---------------------------    ---------------------------- */
    
    Jake Read's avatar
    Jake Read committed
      /* -------------------- DEFS in the DOM ---------------------- */
    
      /* ---------------------------    ---------------------------- */
    
    
    Jake Read's avatar
    Jake Read committed
      let newDef = (spec) => {
        // hmmm ok, we have checked that this is new, not an update,
        if (verbose) console.log('ready write new spec to def', spec)
    
    Jake Read's avatar
    Jake Read committed
        // writing the hunk definition auto-drops it into this.plane ...
    
    Jake Read's avatar
    Jake Read committed
        let def = new HunkDefinition(spec, this, dt, false)
    
    Jake Read's avatar
    Jake Read committed
        defs.push(def)
    
    Jake Read's avatar
    Jake Read committed
        // got 'em, and writing it sets it into the dom, at 0,0
    
    Jake Read's avatar
    Jake Read committed
        // now let's see if there is any 'native' content
        // we can only do this in a top level view,
    
    Jake Read's avatar
    Jake Read committed
        if (this.isTopLevel) {
    
          ni = cuttlefishHunkRef.findIndex((hunk) => {
    
    Jake Read's avatar
    Jake Read committed
            return hunk.ind === def.ind
          })
    
          native = cuttlefishHunkRef[ni]
    
    Jake Read's avatar
    Jake Read committed
          if (native && def.name !== 'tlview') {
    
            try { // try to add the dom element to the def's domelement
              // native is a handle on the hunk itself,
              //console.log('def', def)
    
    Jake Read's avatar
    Jake Read committed
              def.takeNative(native)
    
              // clear from the other list,
              cuttlefishHunkRef.splice(ni, 1)
    
              if (native.type === 'view' && this.name === 'tlview') {
    
              console.log(`VW ${this.name} wrote a native hunk into the dom for ${def.name}`)
    
    Jake Read's avatar
    Jake Read committed
              //msgbox.write(`wrote a native hunk into the dom:  ${def.name}`)
    
    Jake Read's avatar
    Jake Read committed
            } catch (err) {
    
    Jake Read's avatar
    Jake Read committed
              console.log(err)
    
    Jake Read's avatar
    Jake Read committed
              //msgbox.write(`native hunk attach fails ${err}`)
    
    Jake Read's avatar
    Jake Read committed
        // ok, we have the def, now we need to (1) place it and also (2)
        // hook it up to any native dom elements (cuttlefish only)
        // we'll find the menu, if it's about, this is a nice place to place
    
    Jake Read's avatar
    Jake Read committed
        let mt = this.tlv.getPositionForPlacement(this)
    
        // we want to make sure that's in-bounds ...
        let bounds = this.getCurrentBounds()
    
        // console.log('bounds', bounds)
    
        // console.log(`view ${this.name} adding ${def.name}, bounds`, this.getCurrentBounds())
    
    Jake Read's avatar
    Jake Read committed
          // todo: put this somewhere on-screen ?
          // but if no menu is up, likely a load from patch
    
          mt = {}
          mt.x = Math.random() * bounds.w - 100
          mt.y = Math.random() * bounds.h - 100
          def.floaters[0].moveTo(mt.x, mt.y)
    
    Jake Read's avatar
    Jake Read committed
        } else {
    
    Jake Read's avatar
    Jake Read committed
          if (mt.x > bounds.w) mt.x = bounds.w - 100
          if (mt.y > bounds.h) mt.y = bounds.h - 100
    
    Jake Read's avatar
    Jake Read committed
          def.floaters[0].fixTo(mt.x, mt.y)
    
    Jake Read's avatar
    Jake Read committed
        }
    
        // kick does mucho stuff, mostly incl. restarting the sim
    
        this.kick()
    
    Jake Read's avatar
    Jake Read committed
        // ...
    
    Jake Read's avatar
    Jake Read committed
      } // FIN NEWDEF
    
    Jake Read's avatar
    Jake Read committed
        // console.error('updateDef probably error-filled after floop (?)')
    
        // update should change names, values, but not types or orders
        // with an update, we maintain link topology
        // with a replace, we wipe link topology,
        // and manager is responsible for updating us with new links
        console.log('updateDef with this spec:', spec)
        let cdef = defs[spec.ind]
        // check name,
        if (spec.name !== cdef.name) {
          cdef.updateName(spec.name)
        }
        // check states,
        for (let st in spec.states) {
          if (spec.states[st].value !== cdef.states[st].value) {
            cdef.states[st].set(spec.states[st].value)
          }
        }
      } // end update
    
    
    Jake Read's avatar
    Jake Read committed
        console.warn(`REPLACE DEF at ${spec.ind} in ${this.name} has name ${spec.name}`)
    
        // as a rule, when we replace defs, we unhook everything.
    
    Jake Read's avatar
    Jake Read committed
        // the manager is responsible for following up by sending us
    
        // links that are still alive,
        // console.log('the od', od)
        // so first rm those links (this won't properly be tested until program loading, i'd bet)
        for (let ip in od.inputs) {
          od.inputs[ip].disconnectAll()
        }
        for (let op in od.outputs) {
          od.outputs[op].disconnectAll()
        }
    
        // now we can pull any state out that we'd like, i.e.
        let oldFloatGroupType = od.floatGroupType
        // probably want this special case,
        let nt
    
        if (oldFloatGroupType === 'wrappedon') {
    
          // get it ...
          nt = od.floaters[0].bag.find((cnd) => {
            return cnd.type === 'native'
          })
    
          if (!nt) throw new Error("couldn't grab wrapped item during def replace")
    
        }
        let oldFloatPositions = []
        for (let flt of od.floaters) {
    
    Jake Read's avatar
    Jake Read committed
          oldFloatPositions.push({
    
            x: flt.x,
            y: flt.y,
            isFixed: flt.isFixed
    
    Jake Read's avatar
    Jake Read committed
          })
    
        }
        // now the rm,
        od.cleanup()
    
        delete defs[spec.ind]
        // and replace it,
    
    Jake Read's avatar
    Jake Read committed
        let nd = new HunkDefinition(spec, this, dt, false)
    
        // write-over and cut all connections, we'll receive new ones
        // ok, ready to place and append,
    
        if (oldFloatGroupType === 'unwrapped') {
          nd.unwrap()
        } else if (oldFloatGroupType === 'wrappedon') {
          // ok, so, a likely-bugs-forward au-manuel implementation,
          nd.floatGroupType = 'wrappedon'
          nd.reciprocalView = $(nt.de).find('.view').get(0).hunk
          // oh god, look away !
          // this works, but doesn't retain the resizing element, soooo
          nd.floaters[0].grouptype = 'wrapped'
          nd.floaters[0].bag.push(nt)
          nd.floaters[0].typeset.push('native')
          dt.makeResizeable(nt.de, nd.floaters[0].onElementResize, nd.reciprocalView.onresize)
          nd.floaters[0].onChange()
        } else if (oldFloatGroupType === 'edgecased') {
          nd.edgecase()
        }
        // and reset the positions
    
        for (let ps in oldFloatPositions) {
    
          let pos = oldFloatPositions[ps]
    
    Jake Read's avatar
    Jake Read committed
          let odf = nd.floaters[ps]
    
          if (odf && pos) {
            if (pos.isFixed) {
    
    Jake Read's avatar
    Jake Read committed
              odf.fixTo(pos.x, pos.y)
            } else {
              odf.moveTo(pos.x, pos.y)
            }
          }
    
        this.kick()
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
      /* ------------------ NATIVE HUNK MANAGEMENT ----------------- */
      /* ---------------------------    ---------------------------- */
    
    
    Jake Read's avatar
    Jake Read committed
      // for cuttlefish hunks, with dom elements ...
    
      let cuttlefishHunkRef = new Array()
    
      this.take = (hunk) => {
        cuttlefishHunkRef.push(hunk)
      }
    
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
      /* ---------------------- REMOVE DEFS ------------------------ */
      /* ---------------------------    ---------------------------- */
    
    
    Jake Read's avatar
    Jake Read committed
        // console.log('removeDef, also many floop likely errors')
    
    Jake Read's avatar
    Jake Read committed
        // ok ok ok
        let od = defs[index]
    
        if (od === undefined) throw new Error('no hunk to delete!')
    
    Jake Read's avatar
    Jake Read committed
        // else,
    
    Jake Read's avatar
    Jake Read committed
          od.inputs[ip].disconnectAll()
        }
    
    Jake Read's avatar
    Jake Read committed
          od.outputs[op].disconnectAll()
        }
        // ok ...
        defs.splice(index, 1)
        // ordering
        onDefReorder()
    
    Jake Read's avatar
    Jake Read committed
        // and x2thegrave
    
        od.cleanup()
    
    Jake Read's avatar
    Jake Read committed
      }
    
      let onDefReorder = () => {
    
        for (let ind in defs) {
          if (defs[ind].ind !== parseInt(ind)) {
    
    Jake Read's avatar
    Jake Read committed
            // console.log(`swapping ${defs[ind].ind} for ${parseInt(ind)}`)
    
    Jake Read's avatar
    Jake Read committed
            defs[ind].newInd(parseInt(ind))
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
      /* ------------------- PUT AND CUT LINKS --------------------- */
      /* ---------------------------    ---------------------------- */
    
    
      let putLink = (outInd, outputInd, inInd, inputInd) => {
    
    Jake Read's avatar
    Jake Read committed
        try {
    
          let outputdef = defs[outInd].outputs[outputInd]
          let inputdef = defs[inInd].inputs[inputInd]
          outputdef.connect(inputdef)
    
    Jake Read's avatar
    Jake Read committed
          this.kick()
    
    Jake Read's avatar
    Jake Read committed
        } catch (err) {
    
    Jake Read's avatar
    Jake Read committed
          console.error('ERR at putlink', err)
    
    Jake Read's avatar
    Jake Read committed
          //msgbox.write('ERR at putlink: ' + err)
    
    Jake Read's avatar
    Jake Read committed
          console.log(`outInd ${outInd}, outputInd ${outputInd}, inInd ${inInd}, inputInd ${inputInd}`)
    
          console.log(`from ${this.name} having manager ${this.interpreterName}`)
    
    Jake Read's avatar
    Jake Read committed
          return false
    
    Jake Read's avatar
    Jake Read committed
        }
    
    Jake Read's avatar
    Jake Read committed
        return true
      }
    
    
      let cutLink = (outInd, outputInd, inInd, inputInd) => {
    
    Jake Read's avatar
    Jake Read committed
        try {
    
          let outputdef = defs[outInd].outputs[outputInd]
          let inputdef = defs[inInd].inputs[inputInd]
    
          //console.log('dc', performance.now())
    
    Jake Read's avatar
    Jake Read committed
          //console.log('to disconn', outputdef, 'from', inputdef)
    
          outputdef.disconnect(inputdef)
    
    Jake Read's avatar
    Jake Read committed
          this.kick()
    
    Jake Read's avatar
    Jake Read committed
          return true
    
    Jake Read's avatar
    Jake Read committed
        } catch (err) {
    
    Jake Read's avatar
    Jake Read committed
          //console.log('ERR at rmlink', err)
    
    Jake Read's avatar
    Jake Read committed
          //msgbox.write('ERR at rmlink' + err)
    
    Jake Read's avatar
    Jake Read committed
          return false
    
    Jake Read's avatar
    Jake Read committed
        }
        return false
      }
    
      /* ---------------------------    ---------------------------- */
    
      /* ----------------- HOTMESSES -> SERIALMSGS ----------------- */
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
    
    
    Jake Read's avatar
    Jake Read committed
      // herein lay each of our unit requests, as promises,
    
      // hello is a ping,
    
        return new Promise((resolve, reject) => {
          let helloTime = performance.now()
    
    Jake Read's avatar
    Jake Read committed
          //msgbox.write(`said hello to manager at ${helloTime}ms`)
    
          promiseThis([MK.HELLO], () => {
    
            //msgbox.write(`manager says hello, took ${performance.now() - helloTime}ms`)
    
            console.log(`hello from ${this.name}`)
    
            resolve(performance.now() - helloTime)
    
    Jake Read's avatar
    Jake Read committed
          }, (errmsg) => {
            reject(errmsg)
    
      // we can ask for top-level descriptions, or hunks descriptions (by index)
    
    Jake Read's avatar
    Jake Read committed
      this.queryManager = (ind) => {
        return new Promise((resolve, reject) => {
    
          if (ind !== undefined) {
    
    Jake Read's avatar
    Jake Read committed
            //msgbox.write(`querying manager for hunk at ${ind}`)
    
    Jake Read's avatar
    Jake Read committed
            let req = [MK.QUERY]
            MSGS.writeTo(req, ind, 'uint16')
            promiseThis(req, (def) => {
              resolve(def)
            }, (errmsg) => {
              reject(errmsg)
            })
          } else {
    
    Jake Read's avatar
    Jake Read committed
            //msgbox.write(`querying top level of manager`)
    
    Jake Read's avatar
    Jake Read committed
            promiseThis([MK.QUERY], (brief) => {
    
    Jake Read's avatar
    Jake Read committed
              //msgbox.write(`manager has ${brief.numHunks} hunks`)
    
    Jake Read's avatar
    Jake Read committed
              resolve(brief)
            }, (errmsg) => {
              reject(errmsg)
            })
          }
        })
      }
    
    
      // we can ask for a list of available new-stuff to add,
    
    Jake Read's avatar
    Jake Read committed
      this.requestListAvail = (prefix) => {
    
    Jake Read's avatar
    Jake Read committed
        // todo: where prefix is like dir/subdir/etc .. to heirarchy dive
    
        if (prefix) console.error('list query with prefixes not written...')
    
    Jake Read's avatar
    Jake Read committed
        return new Promise((resolve, reject) => {
          promiseThis([MK.REQLISTAVAIL], (list) => {
            resolve(list)
          }, (errmsg) => {
            reject(errmsg)
          })
        })
      }
    
    
      this.requestAddHunk = (type, name, states) => {
    
    Jake Read's avatar
    Jake Read committed
        //console.log('type, states, name', type, states, name)
    
    Jake Read's avatar
    Jake Read committed
        return new Promise((resolve, reject) => {
          let msg = [MK.REQADDHUNK]
    
    Jake Read's avatar
    Jake Read committed
          MSGS.writeTo(msg, type, 'string')
          // when requesting a hunk with state option,
          // we just throw down (similar to descriptions) a list of
          // names, types, values ...
          // these will not always be used, but this is an
          // exercise in consistency at the moment
          // in an array
    
          // if its named,
          if (name) {
            msg.push(HK.NAME)
            MSGS.writeTo(msg, name, 'string')
          }
    
    Jake Read's avatar
    Jake Read committed
            for (let st of states) {
              msg.push(HK.STATE)
    
    Jake Read's avatar
    Jake Read committed
              // later, of course, you have discovered that you don't need the 2nd string here...
    
              let msgbegin = msg.length
    
    Jake Read's avatar
    Jake Read committed
              MSGS.writeTo(msg, st.name, 'string')
              MSGS.writeTo(msg, st.type, 'string')
    
    Jake Read's avatar
    Jake Read committed
              MSGS.writeTo(msg, st.value, st.type)
    
              if (this.interpreterName === "ponyo" && type === "link") {
    
                console.log(`STATE SER FOR: ${st.value} into ${name}`)
                console.log(msg.slice(msgbegin))
              }
    
    Jake Read's avatar
    Jake Read committed
            }
          }
          promiseThis(msg, (def) => {
            resolve(def)
          }, (errmsg) => {
            console.error('error while adding hunk', errmsg)
            reject(errmsg)
          })
        })
    
      // to name them
      this.requestRenameHunk = (ind, name) => {
        return new Promise((resolve, reject) => {
          let msg = [MK.REQNAMECHANGE]
          MSGS.writeTo(msg, ind, 'uint16')
          MSGS.writeTo(msg, name, 'string')
          promiseThis(msg, (def) => {
            resolve(def)
          }, (errmsg) => {
            reject(errmsg)
          })
        })
      }
    
      // to change their state
      this.requestStateChange = (state, value) => {
        return new Promise((resolve, reject) => {
          let msg = [MK.REQSTATECHANGE]
          MSGS.writeTo(msg, state.parent.ind, 'uint16')
          MSGS.writeTo(msg, state.ind, 'uint8')
          try {
            MSGS.writeTo(msg, value, state.type)
          } catch (err) {
            reject(err)
            console.log('that type err:', err)
    
    Jake Read's avatar
    Jake Read committed
            //msgbox.write('err serializing state change request, see console for that type err')
    
          }
          promiseThis(msg, (stdef) => {
    
    Jake Read's avatar
    Jake Read committed
            //console.log('promise returns for state change for def', stdef)
    
            resolve(stdef)
          }, (errmsg) => {
            //console.log('promise rejects for state change bc error', errmsg)
            reject(errmsg)
          })
        })
      }
    
      // or remove them,
    
    Jake Read's avatar
    Jake Read committed
        return new Promise((resolve, reject) => {
          let msg = [MK.REQRMHUNK]
          MSGS.writeTo(msg, ind, 'uint16')
    
    Jake Read's avatar
    Jake Read committed
          promiseThis(msg, (rmid) => {
            resolve(rmid)
    
    Jake Read's avatar
    Jake Read committed
          }, (errmsg) => {
            console.error('error while removing hunk', errmsg)
            reject(errmsg)
          })
        })
    
      this.requestAddLink = (output, input) => {
    
    Jake Read's avatar
    Jake Read committed
        return this.requestAddLinkLikeACaveman(output.parent.ind, output.ind, input.parent.ind, input.ind)
    
    Jake Read's avatar
    Jake Read committed
      }
    
    
      this.requestAddLinkLikeACaveman = (outInd, outputInd, inInd, inputInd) => {
    
    Jake Read's avatar
    Jake Read committed
        return new Promise((resolve, reject) => {
          let msg = [MK.REQADDLINK]
          MSGS.writeTo(msg, outInd, 'uint16')
          MSGS.writeTo(msg, outputInd, 'uint8')
          MSGS.writeTo(msg, inInd, 'uint16')
          MSGS.writeTo(msg, inputInd, 'uint8')
          promiseThis(msg, (link) => {
            resolve(link)
          }, (errmsg) => {
            reject(errmsg)
          })
        })
    
      this.requestRemoveLink = (output, input) => {
    
    Jake Read's avatar
    Jake Read committed
        return new Promise((resolve, reject) => {
          let msg = [MK.REQRMLINK]
          MSGS.writeTo(msg, output.parent.ind, 'uint16')
          MSGS.writeTo(msg, output.ind, 'uint8')
          MSGS.writeTo(msg, input.parent.ind, 'uint16')
          MSGS.writeTo(msg, input.ind, 'uint8')
          promiseThis(msg, (link) => {
            resolve(link)
          }, (errmsg) => {
            reject(errmsg)
          })
        })
    
    Jake Read's avatar
    Jake Read committed
      }
    
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
      /* ------------------------ RECEPIES ------------------------- */
      /* ---------------------------    ---------------------------- */
    
    
      // with recepies, we take the atomic units above and do larger order operations
    
    
    Jake Read's avatar
    Jake Read committed
      this.refresh = () => {
    
    Jake Read's avatar
    Jake Read committed
        return new Promise((resolve, reject) => {
          // wipe ya docs, and ask yonder manager for a complete description
          // everything is friggen jquery, so check it out
          console.log("REFRESHING THE VIEW")
    
    Jake Read's avatar
    Jake Read committed
          //msgbox.clear()
          //msgbox.write('setting up to refresh the view')
    
    Jake Read's avatar
    Jake Read committed
          for (let def of defs) {
    
    Jake Read's avatar
    Jake Read committed
          }
          // also,
          defs.length = 0
    
          // since this occasionally takes time, complete the wipe:
          this.kick()
    
    Jake Read's avatar
    Jake Read committed
          // hello first,
          this.sayHelloToManager().then(() => {
            this.queryManager().then((brief) => {
    
    Jake Read's avatar
    Jake Read committed
              // console.log('BRIEF', brief)
    
    Jake Read's avatar
    Jake Read committed
              //this.msgbox.briefState.setFromBrief(brief)
    
    Jake Read's avatar
    Jake Read committed
              // should have a list then, do that brief then ?
              let nh = brief.numHunks
              // the repeater / promise walker
              let cbnh = (n) => {
                this.queryManager(n).then((def) => {
                  n++
                  if (n < nh) {
                    // case not-complete, so continue
    
    Jake Read's avatar
    Jake Read committed
                    //msgbox.write(`caught ${n}th hunk, continuing...`)
    
    Jake Read's avatar
    Jake Read committed
                    cbnh(n)
                  } else {
                    // now that we have it all, we can read in the
                    // yikes w/r/t this tree
    
    Jake Read's avatar
    Jake Read committed
                    for (let df in defs) {
                      let def = defs[df]
                      for (let op in def.outputs) {
                        let otp = def.outputs[op]
                        if (otp.specConnections) {
                          for (let sc of otp.specConnections) {
    
    Jake Read's avatar
    Jake Read committed
                            try {
    
    Jake Read's avatar
    Jake Read committed
                              putLink(df, op, sc[0], sc[1])
    
    Jake Read's avatar
    Jake Read committed
                            } catch (err) {
                              console.error('cannot reconnect from spec', err)
    
    Jake Read's avatar
    Jake Read committed
                              //msgbox.write(`err while trying to make links from spec`)
    
    Jake Read's avatar
    Jake Read committed
                            }
    
    Jake Read's avatar
    Jake Read committed
                          delete op.specConnections
    
    Jake Read's avatar
    Jake Read committed
                    // finally
    
    Jake Read's avatar
    Jake Read committed
                    //msgbox.write(`~ view refresh complete ~`)
    
                    this.hasRefreshed = true
                    this.tlv.globalOrganize()
    
                    resolve(this)
    
    Jake Read's avatar
    Jake Read committed
                }).catch((err) => {
                  console.error(err)
    
    Jake Read's avatar
    Jake Read committed
                  //msgbox.write(`trouble while requesting ${n}th hunk`)
    
    Jake Read's avatar
    Jake Read committed
                  reject(err)
                })
              }
              // the kickoff,
              cbnh(0)
            })
    
      this.reloadHunk = (indice, restoreState) => {
        // reloads from source (devtool) if restoreState is set,
    
        // ... not going to write the version of this that restores state
        // we ask for state on the reset
    
        let od = this.defs[indice]
    
    Jake Read's avatar
    Jake Read committed
        let odx = od.floaters[0].x
        let ody = od.floaters[0].y
    
        // this in conjunction with error catching during the addhunk step ...
        // we can wrap all of this up in try / catch ? and delete things only when
        // those all pass ??
        // first, let's add in the new hunk:
        this.requestAddHunk(od.type).then((def) => {
    
    Jake Read's avatar
    Jake Read committed
          // let's compare these ? for matching inputs, outputs, reconnect:
          let matching = (odp, ndp, ind) => {
    
    Jake Read's avatar
    Jake Read committed
            if (odp[ind]) {
              if (odp[ind].name === ndp[ind].name && odp[ind].type === ndp[ind].type) return true
    
    Jake Read's avatar
    Jake Read committed
            } else {
              return false
            }
          }
          // these links are all added on a promise, meaning we aren't sure (by the time this runs to completion)
          // whether or not they are successful...
    
    Jake Read's avatar
    Jake Read committed
          // with better view -> manager integration, this would be mucho easier
          for (let ip in def.inputs) {
            if (matching(od.inputs, def.inputs, ip)) {
              for (let cn of od.inputs[ip].connections) {
    
    Jake Read's avatar
    Jake Read committed
                this.requestAddLink(cn, def.inputs[ip])
              }
            }
          }
    
    Jake Read's avatar
    Jake Read committed
          for (let op in def.outputs) {
            if (matching(od.outputs, def.outputs, op)) {
              for (let cn of od.outputs[op].connections) {
    
    Jake Read's avatar
    Jake Read committed
                this.requestAddLink(def.outputs[op], cn)
              }
            }
          }
          // we should wait for everything all async like, but
    
    Jake Read's avatar
    Jake Read committed
          this.requestRemoveHunk(od.ind).then(() => {
            $(this.dom).find('.contextmenu').fadeOut(400, function() {
              $(this).remove()
            })
            // and also,
            def.floaters[0].fixTo(odx, ody)
          })
    
    Jake Read's avatar
    Jake Read committed
          this.changeContextTitle('error on reload, see consoles')
          setTimeout(() => {
            $(this.dom).find('.contextmenu').fadeOut(400, function() {
              $(this).remove()
            })
          }, 2000)
    
          console.error('caught request add hunk error from reload site')
        })
        // let's load one in w/ the changes ... if we can do this we'll go thru the rest
    
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
      /* --------------------- MESSAGES OUTPUT --------------------- */
      /* ---------------------------    ---------------------------- */
    
      let outmsgbuffer = new Array()
    
        if (!msgsout.io() && outmsgbuffer.length < 1) {
    
    Jake Read's avatar
    Jake Read committed
          // str8 shooters
    
          if (msgverbose) this.log('msg out', bytes)
          msgsout.put(bytes)
    
    Jake Read's avatar
    Jake Read committed
        } else {
          // gotta buffer
    
          outmsgbuffer.push(bytes)
          this.log('VIEW OUTBUFFER LEN', outmsgbuffer.length)
    
      // it's often very useful tracking completion, so
      let callbackSets = new Array()
      let cbid = 0
    
    
    Jake Read's avatar
    Jake Read committed
      let promiseThis = (bytes, callback, errback) => {
    
        cbid++
        if (cbid > 254) {
          cbid = 0
        } // could be safe about this, for now we take for granted less than 255 out,
        // remember it
        callbackSets.push({
          key: bytes[0],
          id: cbid,
    
    Jake Read's avatar
    Jake Read committed
          callback: callback,
          errback: errback
    
        })
        // so our header is like - we don't explicitly type the id, it's a byte
        let head = [MK.MSGID, cbid]
        // and we concat this to
        bytes = head.concat(bytes)
        writeMessage(bytes)
      }
    
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
      /* ------------------ MESSAGES INPUT, LOOP ------------------- */
      /* ---------------------------    ---------------------------- */
    
      // still looking for clear function naming / message naming
    
      this.loop = () => {
        // THE Q: is it trees or is it inputs / outputs ? ports ...
    
        if (msgsin.io()) {
    
    Jake Read's avatar
    Jake Read committed
          let msg = msgsin.get()
    
          if (msgverbose) console.log('VIEW RX MSG:', msg)
    
          if (!Array.isArray(msg)) throw new Error(`view throwing object message, having header ${msg.header}`)
    
          // ... mirror for views
    
          let inc = 0
          // track the msg id response, and that callback
          let msgid, cbd
    
    Jake Read's avatar
    Jake Read committed
          // find the element, set to cbd ref and delete from array
    
    Jake Read's avatar
    Jake Read committed
          if (msg[0] === MK.MSGID) {
    
            msgid = msg[1]
            inc = 2
            // and find the callback we registered
    
    Jake Read's avatar
    Jake Read committed
            let cbdi = callbackSets.findIndex((cand) => {
    
              return cand.id === msgid
            })
    
    Jake Read's avatar
    Jake Read committed
              cbd = callbackSets[cbdi]
    
    Jake Read's avatar
    Jake Read committed
              callbackSets.splice(cbdi, 1)
            }
    
          }
          // if a msgid, find the callback
          // ok, get the id and increment ...
          switch (msg[inc]) {
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is an error')
    
    Jake Read's avatar
    Jake Read committed
              let errmsg = MSGS.readFrom(msg, inc + 1, 'string').item
    
    Jake Read's avatar
    Jake Read committed
              console.error('manager returns an error', errmsg)
    
    Jake Read's avatar
    Jake Read committed
              //msgbox.write(errmsg)
    
    Jake Read's avatar
    Jake Read committed
              if (cbd) cbd.errback(errmsg)
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is hello')
    
    Jake Read's avatar
    Jake Read committed
              if (cbd) {
    
    Jake Read's avatar
    Jake Read committed
                cbd.callback()
    
    Jake Read's avatar
    Jake Read committed
                console.log(`manager says hello!`)
                //msgbox.write(`manager says hello!`)
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is a manager brief')
    
    Jake Read's avatar
    Jake Read committed
              inc++
    
              // reading strings returns item, increment
    
              temp = MSGS.readFrom(msg, inc, 'string')
              inc += temp.increment
    
              temp = MSGS.readFrom(msg, inc, 'string')
              inc += temp.increment
    
              brief.interpreterVersion = temp.item
    
              temp = MSGS.readFrom(msg, inc, 'uint16')
              inc += temp.increment
    
              this.interpreterName = brief.interpreterName
              this.interpreterVersion = brief.interpreterVersion
    
    Jake Read's avatar
    Jake Read committed
              //msgbox.briefState.setFromBrief(brief)
    
    Jake Read's avatar
    Jake Read committed
              if (cbd) cbd.callback(brief)
    
    Jake Read's avatar
    Jake Read committed
              break
    
    Jake Read's avatar
    Jake Read committed
            case MK.LISTOFAVAIL: