Skip to content
Snippets Groups Projects
view.js 30.1 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
    
    } 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
    
    function View() {
    
    Jake Read's avatar
    Jake Read committed
      Hunkify(this)
    
      let verbose = true
      let msgverbose = true
    
      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()
    
    
      // 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)
    
      // 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
      /* ---------------------------    ---------------------------- */
      /* -------------------- INIT, LISTENERS ---------------------- */
      /* ---------------------------    ---------------------------- */
    
      this.init = () => {
        // in the case of UIs, we have the dom before init runs,
        // so this is kind of like the 'window.onload' function
        this.log('hello ui')
        this.dom = $('<div>').addClass('view').get(0)
        // for nested dom elements,
        this.plane = $('<div>').addClass('plane').get(0)
    
    Jake Read's avatar
    Jake Read committed
        msgbox.init()
    
    Jake Read's avatar
    Jake Read committed
        // for programs
        //patchset.init()
    
    Jake Read's avatar
    Jake Read committed
        // init transform of the plane,
    
    Jake Read's avatar
    Jake Read committed
        let dft = {
          s: 1,
          x: 0,
          y: 0,
          ox: 0,
          oy: 0
    
    Jake Read's avatar
    Jake Read committed
        }
    
        dt.writeTransform(this.plane, dft)
        dt.writeBackgroundTransform(this.dom, dft)
    
    Jake Read's avatar
    Jake Read committed
    
    
    Jake Read's avatar
    Jake Read committed
        // to zoom,
    
    Jake Read's avatar
    Jake Read committed
        this.dom.addEventListener('wheel', mouseWheelListener)
    
    
    Jake Read's avatar
    Jake Read committed
        // to pan,
    
    Jake Read's avatar
    Jake Read committed
        this.dom.addEventListener('mousedown', (evt) => {
          if ($(evt.target).is('.view')) {
    
            evt.preventDefault()
            evt.stopPropagation()
    
    Jake Read's avatar
    Jake Read committed
            window.addEventListener('mousemove', canvasDragListener)
            window.addEventListener('mouseup', canvasUpListener)
          } else if ($(evt.target).is('input')) {
            // no op
          } else {
            // prevents bubbling up outside of the view, potentially a bug farm ?
            // evt.preventDefault()
            // evt.stopPropagation()
          }
        })
    
        // get the context menu on righ click
        this.dom.addEventListener('contextmenu', onContextMenu)
    
        // append
        $(this.dom).append(this.plane)
    
    
        // key listeners
        // keys are global, so ... idk how to unfoof this yet, but
    
    Jake Read's avatar
    Jake Read committed
        if (this.id === 'TLView') {
    
          document.addEventListener('keydown', (evt) => {
    
    Jake Read's avatar
    Jake Read committed
            if (evt.key === 'l') {
    
    Jake Read's avatar
    Jake Read committed
            } else if (evt.key === 'c') {
    
              //writeMessage('addprogram', 'ntlink')
    
    Jake Read's avatar
    Jake Read committed
            } else if (evt.key === 'v') {
    
              //writeMessage('addprogram', 'llink')
    
            } else if (evt.keyCode === 27) {
              // escapekey
              $(this.plane).children('.contextmenu').remove()
    
      this.refresh = () => {
        // wipe ya docs, and ask yonder manager for a complete description
    
        // everything is friggen jquery, so check it out
        $(this.plane).children('.block').remove()
        // and then say hello,
        writeMessage([MK.REQDESCRIBESELF])
    
        // that's fine, we can wait for a response, but we have to track and setup the next move
      }
    
    
    Jake Read's avatar
    Jake Read committed
      let mouseWheelListener = (evt) => {
    
    Jake Read's avatar
    Jake Read committed
        if (!$(evt.target).is('.view') && !$(evt.target).is('#floater')) {
    
    Jake Read's avatar
    Jake Read committed
          return false
        }
        evt.preventDefault()
        evt.stopPropagation()
    
    Jake Read's avatar
    Jake Read committed
        let ox = evt.clientX
        let oy = evt.clientY
    
    Jake Read's avatar
    Jake Read committed
        let ds
        if (evt.deltaY > 0) {
          ds = 0.025
        } else {
          ds = -0.025
        }
    
        let ct = dt.readTransform(this.plane)
    
    Jake Read's avatar
    Jake Read committed
        ct.s *= 1 + ds
    
    Jake Read's avatar
    Jake Read committed
        ct.x += (ct.x - ox) * ds
        ct.y += (ct.y - oy) * ds
    
        dt.writeTransform(this.plane, ct)
        dt.writeBackgroundTransform(this.dom, ct)
    
    Jake Read's avatar
    Jake Read committed
        // now redraw links
      }
    
    Jake Read's avatar
    Jake Read committed
    
    
    Jake Read's avatar
    Jake Read committed
      let canvasDragListener = (evt) => {
        evt.preventDefault()
        evt.stopPropagation()
    
        let ct = dt.readTransform(this.plane)
    
    Jake Read's avatar
    Jake Read committed
        ct.x += evt.movementX
        ct.y += evt.movementY
    
        dt.writeTransform(this.plane, ct)
        dt.writeBackgroundTransform(this.dom, ct)
    
    Jake Read's avatar
    Jake Read committed
    
        /*
        this.pan.x += evt.movementX
        this.pan.y += evt.movementY
        this.plane.style.backgroundPosition = `${this.pan.x}px ${this.pan.y}px`
        $(this.plane).children().each((index, div) => {
    
            let tf = dt.readTransform(div)
    
    Jake Read's avatar
    Jake Read committed
            tf.x += evt.movementX
            tf.y += evt.movementY
    
            dt.writeTransform(div, tf)
    
    Jake Read's avatar
    Jake Read committed
        })
        */
      }
    
      let canvasUpListener = (evt) => {
        window.removeEventListener('mousemove', canvasDragListener)
        window.removeEventListener('mouseup', canvasUpListener)
      }
    
      // CONTEXT MENU
      let onContextMenu = (evt) => {
    
    Jake Read's avatar
    Jake Read committed
        $(this.plane).find('.contextmenu').remove()
    
    Jake Read's avatar
    Jake Read committed
        //console.log(evt)
        evt.preventDefault()
        evt.stopPropagation()
    
        // more like
        /*
         -> add a hunk
         -> load a program
         -> save this program
         -> reload this view
         -> heartbeat checkin
        */
    
    Jake Read's avatar
    Jake Read committed
        console.log('context...', evt)
        // make the menu,
        let menu = $('<div>').addClass('contextmenu').get(0)
        // and place,
    
        let pt = dt.readTransform(this.plane)
    
    Jake Read's avatar
    Jake Read committed
        console.log('plane transform', pt)
        // HERE: there's still some mess, not landing in the right place,
        // (and there's compounding scale issues on dragging the canvas)
    
        dt.writeTransform(menu, {
    
    Jake Read's avatar
    Jake Read committed
          s: 1,
    
    Jake Read's avatar
    Jake Read committed
          x: ((evt.clientX - pt.x) / pt.s), // (pt.s + -0.1 * (pt.s-1))),
    
    Jake Read's avatar
    Jake Read committed
          y: ((evt.clientY - pt.y) / pt.s) // + -0.1 * (pt.s-1)))
    
    Jake Read's avatar
    Jake Read committed
        })
    
    Jake Read's avatar
    Jake Read committed
        //
    
        $(this.plane).append(menu)
    
    Jake Read's avatar
    Jake Read committed
        // hmmm
        this.changeContextTitle('you can... ')
        // on of the options will ...
        // add a hunk menu
        this.addContextOption('add a hunk', (evt) => {
          msgbox.write('requested a list of hunks...')
          $(evt.target).text('requested a list of hunks...')
          writeMessage([MK.REQLISTAVAIL])
        })
    
        this.addContextOption('load a patch', (evt) => {
    
    Jake Read's avatar
    Jake Read committed
        })
    
        this.addContextOption('save this patch', (evt) => {
          patchset.saveCurrent(evt, true)
        })
        // writeMessage([MK.REQLISTAVAIL])
    
    Jake Read's avatar
    Jake Read committed
      // takes 'under' argument
      this.addContextOption = (text, click) => {
        $(this.plane).find('.contextmenu').get(0).append($('<li>' + text + '</li>').click((click)).get(0))
      }
    
      this.changeContextTitle = (text) => {
        // clear,
        $(this.plane).find('.contextmenu').children().remove()
        // overkill, but fun
        let menu = $(this.plane).find('.contextmenu').get(0)
        let title = $(`<div>${text}</div>`).addClass('contextTitle').get(0)
        title.onmousedown = (evt) => {
          evt.preventDefault()
          evt.stopPropagation()
          let domElemMouseMove = (evt) => {
            // TRANSFORMS here to move div about on drag
            evt.preventDefault()
            evt.stopPropagation()
            let ct = dt.readTransform(menu)
            let pt = dt.readTransform(menu.parentElement) // think that's just this.plane ?
            ct.x += evt.movementX / pt.s
            ct.y += evt.movementY / pt.s
            dt.writeTransform(menu, ct)
            this.drawLinks()
          }
    
          function rmOnMouseUp(evt) {
            document.removeEventListener('mousemove', domElemMouseMove)
            document.removeEventListener('mouseup', rmOnMouseUp)
          }
          document.addEventListener('mousemove', domElemMouseMove)
          document.addEventListener('mouseup', rmOnMouseUp)
    
    Jake Read's avatar
    Jake Read committed
        }
    
    Jake Read's avatar
    Jake Read committed
        $(this.plane).find('.contextmenu').append(title)
    
    Jake Read's avatar
    Jake Read committed
      /*
      this.changeContextTitle('list of available:')
      for(let item of list){
        this.addContextOption(item, (evt) => {
          let msg = [MK.REQADDHUNK]
          MSGS.writeTo(msg, item, 'string')
    
    Jake Read's avatar
    Jake Read committed
          $(evt.target).append(' > requested ... ')
    
    Jake Read's avatar
    Jake Read committed
        })
    
    Jake Read's avatar
    Jake Read committed
      */
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
      /* ---------------------- FORCE LAYOUT ----------------------- */
      /* ---------------------------    ---------------------------- */
    
      // ok, my thoughts on this
      /*
    
    
    Jake Read's avatar
    Jake Read committed
      when you're up with big programs, spend a day / a handful, just making the UI sing
      - https://bl.ocks.org/mbostock/3750558
    
    
    Jake Read's avatar
    Jake Read committed
      at the moment this is kind of 'fine'
       - starting condition is mostly random (and elsewhere) - maybe some graph analysis
        - for who-is-generally-downstream-of-whomst
        - this is nice code golf for boring times when you have lots of graphs
       - still not looking at links for layout force: do that first
       - want to connect this notion with the 'design patterns' ides ...
        - (links) find (comm/*) connected, arrange in a stack
        - (view) finds (link) connected, also stackup ...
         - the links / split through views -> this is actually a lot of the work,
    
    Jake Read's avatar
    Jake Read committed
    
      */
    
      let blocks = new Array()
      let flsimrun = false
      let flsim = {}
      let flnodes = []
    
    
    Jake Read's avatar
    Jake Read committed
      let finAlpha = 0.1
      let sizemultiple = 0.5
    
      // happens when items added, deleted (changing topology)
    
    Jake Read's avatar
    Jake Read committed
      let updateForceLoop = () => {
        // init and/or update
    
    Jake Read's avatar
    Jake Read committed
        if (!flsimrun && blocks.length > 3) {
    
    Jake Read's avatar
    Jake Read committed
          // Case for starting sim
    
    Jake Read's avatar
    Jake Read committed
          msgbox.write('starting force sim')
    
    Jake Read's avatar
    Jake Read committed
          flsimrun = true
          // start with two nodes
          let positions = this.getAllHunkPositions()
    
          let sizes = this.getAllHunkSizes()
    
    Jake Read's avatar
    Jake Read committed
          for (let i in positions) {
    
    Jake Read's avatar
    Jake Read committed
            let nd = {
    
    Jake Read's avatar
    Jake Read committed
              index: i,
              x: positions[i].x,
              y: positions[i].y,
              vx: 0,
    
    Jake Read's avatar
    Jake Read committed
              vy: 0,
              r: sizes[i].width * sizemultiple
    
    Jake Read's avatar
    Jake Read committed
            }
            flnodes.push(nd)
    
    Jake Read's avatar
    Jake Read committed
          }
          flsim = d3.forceSimulation(flnodes)
    
    Jake Read's avatar
    Jake Read committed
            .force('charge', d3.forceManyBody().strength(250))
    
    Jake Read's avatar
    Jake Read committed
            .force('center', d3.forceCenter(600, 600))
    
    Jake Read's avatar
    Jake Read committed
            .force('collide', d3.forceCollide((node, i, nodes) => {
              return node.r
            }))
    
    Jake Read's avatar
    Jake Read committed
            .alphaMin(finAlpha)
            .on('tick', flTick)
            .on('end', flEnd)
    
    Jake Read's avatar
    Jake Read committed
        } else if (blocks.length <= 3) {
    
    Jake Read's avatar
    Jake Read committed
          // donot
        } else {
          // case for adding / rming from sim
    
    Jake Read's avatar
    Jake Read committed
          msgbox.write('UPD8 Force Sim')
    
    Jake Read's avatar
    Jake Read committed
          let positions = this.getAllHunkPositions()
    
    Jake Read's avatar
    Jake Read committed
          let sizes = this.getAllHunkSizes()
    
    Jake Read's avatar
    Jake Read committed
          if (positions.length > flnodes.length) {
            let last = positions.length - 1
            //console.log('to add new node like', positions[last])
    
    Jake Read's avatar
    Jake Read committed
            let nd = {
    
    Jake Read's avatar
    Jake Read committed
              index: last,
              x: positions[last].x,
              y: positions[last].y,
              vx: 0,
    
    Jake Read's avatar
    Jake Read committed
              vy: 0,
              r: sizes[last].width * sizemultiple
    
    Jake Read's avatar
    Jake Read committed
            flnodes.push(nd)
    
    Jake Read's avatar
    Jake Read committed
            // console.log('SIM adds now this', newNode.x, newNode.y)
          } else {
    
    Jake Read's avatar
    Jake Read committed
            //msgbox.write("SIM DELETE CASE NOT WRITTEN")
    
    Jake Read's avatar
    Jake Read committed
          }
          flsim.nodes(flnodes)
          flsim.alpha(1)
            .alphaMin(finAlpha)
            .restart()
    
    Jake Read's avatar
    Jake Read committed
        }
    
      // happens when things perterbed in existing state (i.e. drags)
      let kickForceLoop = () => {
        // hmm... but fix the one you're dragging, say?
        flsim.alpha(1).restart()
      }
    
    
    Jake Read's avatar
    Jake Read committed
      let flTick = () => {
        // called on sim update
    
    Jake Read's avatar
    Jake Read committed
        let blks = $(this.plane).children('.block').not('#NROL39_0').not('#TLView')
    
    Jake Read's avatar
    Jake Read committed
        if (blks.length !== flnodes.length) {
          console.log('FLOOP NODES MISMATCH', blks.length, flnodes.length)
        } else {
          for (let i = 0; i < blks.length; i++) {
            blks[i].style.left = flnodes[i].x + 'px'
            blks[i].style.top = flnodes[i].y + 'px'
          }
    
    Jake Read's avatar
    Jake Read committed
        }
    
    Jake Read's avatar
    Jake Read committed
        if ($(msgbox.zeCheckbox).prop('checked')) {
          this.zoomExtents()
    
    Jake Read's avatar
    Jake Read committed
      }
    
      let flEnd = () => {
        console.log('FIN DU SIM')
      }
    
    
    Jake Read's avatar
    Jake Read committed
      this.zoomExtents = () => {
    
        // to zoom-extends
    
    Jake Read's avatar
    Jake Read committed
        let psns = this.getAllHunkPositions()
    
        let sizes = this.getAllHunkSizes()
    
    Jake Read's avatar
    Jake Read committed
        // bless up, these are all in 0,0 relative space
    
    Jake Read's avatar
    Jake Read committed
        let maxx, minx, maxy, miny
    
    Jake Read's avatar
    Jake Read committed
          maxx = psns[ind].x + sizes[ind].width
          minx = psns[ind].x
          maxy = psns[ind].y + sizes[ind].height
          miny = psns[ind].y
          // max cases
    
    Jake Read's avatar
    Jake Read committed
            maxxy.x = maxx
          }
    
    Jake Read's avatar
    Jake Read committed
            maxxy.y = maxy
          }
          // min cases
    
    Jake Read's avatar
    Jake Read committed
            minxy.x = minx
          }
    
    Jake Read's avatar
    Jake Read committed
            minxy.y = miny
          }
        }
        // margin
    
    Jake Read's avatar
    Jake Read committed
        let margin = 100
    
    Jake Read's avatar
    Jake Read committed
        minxy.x -= margin
        minxy.y -= margin
        maxxy.x += margin
        maxxy.y += margin
    
        // ok, compare bounding box to current frustrum ?
    
        let ct = dt.readTransform(this.plane)
    
        let wd = this.dom.clientWidth
        let ht = this.dom.clientHeight
    
    Jake Read's avatar
    Jake Read committed
        // to find scale, do
        let pfsx = (wd) / (maxxy.x - minxy.x)
        let pfsy = (ht) / (maxxy.y - minxy.y)
        let pfs = Math.min(pfsx, pfsy)
        // write em
        ct.s = pfs
    
        dt.writeTransform(this.plane, ct)
        dt.writeBackgroundTransform(this.dom, ct)
    
    Jake Read's avatar
    Jake Read committed
      this.getAllHunkPositions = () => {
    
    Jake Read's avatar
    Jake Read committed
        // returns positions as numbers,
    
    Jake Read's avatar
    Jake Read committed
        let nds = $(this.plane).children('.block')
        let positions = new Array()
        for (let nd of nds) {
    
    Jake Read's avatar
    Jake Read committed
          if ($(nd).attr('id') === "NROL39_0" || $(nd).attr('id') === "TLView") {
            //console.log('skip')
          } else {
    
    Jake Read's avatar
    Jake Read committed
            let pos = dt.readXY(nd)
            pos.id = nd.id
            positions.push(pos)
    
    Jake Read's avatar
    Jake Read committed
        return positions
        // should do transform here ?
      }
    
    
      this.getAllHunkSizes = () => {
        let nds = $(this.plane).children('.block')
        let sizes = new Array()
    
    Jake Read's avatar
    Jake Read committed
          if ($(nd).attr('id') === "NROL39_0" || $(nd).attr('id') === "TLView") {
            //console.log('skip')
          } else {
    
    Jake Read's avatar
    Jake Read committed
            let sz = dt.readSize(nd)
            sz.id = nd.id
            sizes.push(sz)
    
      // here is where you rm'd drawing & moving
    
      this.drawLinks = () => {
    
        // drawing from scratch every time ! could be faster, probably
    
        bzt.clear(this.plane)
    
        // draw 'em all
        for(let def of defs){
          for(let output of def.outputs){
            for(let input of output.connections){
              bzt.drawLink(output, input)
            }
            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
      }
    
      /* ---------------------------    ---------------------------- */
      /* -------------------- DEFS in the DOM ---------------------- */
      /* ---------------------------    ---------------------------- */
    
    
      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,
    
        outer: while (i < bytes.length) {
          switch (bytes[i]) {
    
              i += 1
    
              temp = MSGS.readFrom(bytes, i, 'uint16')
    
    Jake Read's avatar
    Jake Read committed
              spec.ind = temp.item
    
              i += temp.increment
              break
            case HK.NAME:
              i += 1
              temp = MSGS.readFrom(bytes, i, 'string')
    
    Jake Read's avatar
    Jake Read committed
              spec.name = temp.item
    
              i += temp.increment
              break
            case HK.INPUT:
              i += 1
              // expecting two strings here, name and then type
    
    Jake Read's avatar
    Jake Read committed
              i += deserializeNameType(spec.inputs, bytes, i)
    
              break
            case HK.OUTPUT:
              i += 1
    
    Jake Read's avatar
    Jake Read committed
              i += deserializeNameType(spec.outputs, bytes, i)
    
              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,
    
              temp = MSGS.readFrom(bytes, i, spec.states[spec.states.length - 1].type)
    
    Jake Read's avatar
    Jake Read committed
              spec.states[spec.states.length - 1].value = temp.item
    
              i += temp.increment
              break
            default:
    
              throw new Error(`unexpected key encountered during hunk deserialization at position ${i}: ${bytes[i]}`)
    
              break outer
          }
        }
        // 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)
    
        // we gucc ?
    
    Jake Read's avatar
    Jake Read committed
        return spec
    
      /* ---------------------------    ---------------------------- */
      /* ------------------- SERIAL -> HOTMESSES ------------------- */
      /* ---------------------------    ---------------------------- */
    
    
    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)
    
        // do we have this id already? could be an update;
    
    Jake Read's avatar
    Jake Read committed
        let def = new HunkDefinition(spec, this, dt, true)
    
        // ...
    
        let menu = $(this.plane).children('.contextmenu').get(0)
    
    Jake Read's avatar
    Jake Read committed
        if (menu !== undefined) {
    
          mt = dt.readTransform(menu)
    
    Jake Read's avatar
    Jake Read committed
          $(menu).remove()
        } else if (def.ind === 0 && def.name === 'manager') { // TEMPORARY until better management with force layout
    
    Jake Read's avatar
    Jake Read committed
          mt = {
            s: 1,
            x: 100,
            y: 100
          }
    
    Jake Read's avatar
    Jake Read committed
        } else if (def.ind === 1 && def.name === 'view') { // TEMPORARY until better management with force layout
    
    Jake Read's avatar
    Jake Read committed
          mt = {
            s: 1,
            x: 100,
            y: 200
          }
    
    Jake Read's avatar
    Jake Read committed
        } else { // also, initial placement should be determined by force layout
    
    Jake Read's avatar
    Jake Read committed
          mt = {
            s: 1,
            x: Math.random() * 1000,
            y: Math.random() * 800
          }
    
    Jake Read's avatar
    Jake Read committed
        // if it's in the ref-to-add *this is confusing*
        let native = cuttlefishHunkRef.find((hunk) => {
          return hunk.ind === def.ind
        })
    
    Jake Read's avatar
    Jake Read committed
        if (native !== undefined && native !== null) {
          console.log("trying to add native hunk's dom: native:", native)
          try { // try to add the dom element to the def's domelement
            $(def.de).append($(native.dom).addClass('cuttlefishhunkdom'))
            if ($(native.dom).is('.view')) {
              de.style.height = '800px'
              de.style.width = '1200px'
              // leader on starting to reize these ...
              /*
              $(de).append($('<div>').addClass('rsHandle').on('mousedown', (evt) => {
                console.log('rshandle down')
              }))*/
    
    Jake Read's avatar
    Jake Read committed
          } catch (err) {
            msgbox.write(`native hunk attach fails ${err}`)
    
    Jake Read's avatar
    Jake Read committed
        // ok, ready to place and append,
        dt.writeTransform(def.de, mt)
        // add the def's domelement to the view,
        $(this.plane).append(def.de)
    
        // and add it to our list
        defs.push(def)
    
    Jake Read's avatar
    Jake Read committed
        // i.e. we won't return it: it contains everything we need
    
      let replaceDef = (spec) => {
        // the old boy,
        let od = defs[spec.ind]
        // as a rule, when we replace defs, we unhook everything.
        // the manager (in its wisdom) 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){
          for(let op in od.inputs[ip].connections){
            // a bit back asswards here ...
            od.inputs[ip].connections[op].disconnect(od.inputs[ip])
          }
        }
        // that was considerate of the others, but this whole thing is just going to get rm'd, so outputs don't matter
        let mt = dt.readTransform(od.de)
        // now we can *eliminate it*
        $(od.de).remove()
        defs[spec.ind] = null
        // and replace it,
        let nd = new HunkDefinition(spec, this, dt, true)
        // write-over and cut all connections, we'll receive new ones
        // ok, ready to place and append,
        dt.writeTransform(nd.de, mt)
        // add the def's domelement to the view,
        $(this.plane).append(nd.de)
        // and replace it in the list,
        defs[spec.ind] = nd
        // and re-render
        this.drawLinks()
      }
    
    
      let cuttlefishHunkRef = new Array()
    
      this.take = (hunk) => {
        cuttlefishHunkRef.push(hunk)
      }
    
    
    Jake Read's avatar
    Jake Read committed
      // TODO INDSWAP
    
      let removeDef = (index) => {
        /* like ?
        let def = defs[index]
        if(!def) throw new Error('attempt to remove def that doth not exist')
        */
    
    Jake Read's avatar
    Jake Read committed
        // get the inputs that we'll have to watch removal for
        let inpts = $(this.plane).children('#' + id).children('.inputs').children('.input')
        // remove the def / block
    
    Jake Read's avatar
    Jake Read committed
        if ($(this.plane).children('#' + id).length === 0) throw new Error('trouble on remove, cannot find block with given id')
    
    Jake Read's avatar
    Jake Read committed
        $(this.plane).children('#' + id).remove()
        // we have to walk over the other outputs and disconnect them,
        let otps = $(this.plane).find('.output')
        for (let otp of otps) {
          for (let inp of inpts) {
            if (otp.connectedTo.includes('#' + inp.id)) {
              otp.connectedTo.splice(otp.connectedTo.indexOf('#' + inp.id))
    
    Jake Read's avatar
    Jake Read committed
        updateForceLoop()
    
      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
          updateForceLoop()
    
    Jake Read's avatar
    Jake Read committed
        } catch (err) {
          console.log('ERR at putlink', err)
    
    Jake Read's avatar
    Jake Read committed
          msgbox.write('ERR at putlink' + err)
    
    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('to disconn', outputdef, 'from', inputdef)
          outputdef.disconnect(inputdef)
    
    Jake Read's avatar
    Jake Read committed
        } catch (err) {
          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 true
        }
        return false
      }
    
      /* ---------------------------    ---------------------------- */
    
      /* ----------------- HOTMESSES -> SERIALMSGS ----------------- */
    
    Jake Read's avatar
    Jake Read committed
      /* ---------------------------    ---------------------------- */
    
    
      let helloTime = 0
    
      this.sayHelloToManager = () => {
        helloTime = performance.now()
        msgbox.write(`said hello to manager at ${performance.now()}ms`)
        writeMessage([MK.HELLO])
      }
    
    
        let msg = [MK.REQADDHUNK]
        MSGS.writeTo(msg, name, 'string')
        writeMessage(msg)
    
        let msg = [MK.REQRMHUNK]
    
      this.requestAddLink = (output, input) => {
    
    Jake Read's avatar
    Jake Read committed
        let msg = [MK.REQADDLINK]
    
        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')
    
    Jake Read's avatar
    Jake Read committed
        writeMessage(msg)
      }
    
    
      this.requestRemoveLink = (output, input) => {
    
    Jake Read's avatar
    Jake Read committed
        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')
    
    Jake Read's avatar
    Jake Read committed
        writeMessage(msg)
      }
    
    
    Jake Read's avatar
    Jake Read committed
      // , numoot (number)
      this.requestStateChange = (state, value) => {
        // ok then, THISNOW
    
    Jake Read's avatar
    Jake Read committed
        MSGS.writeTo(msg, state.parent.ind, 'uint16')
        MSGS.writeTo(msg, state.ind, 'uint8')
    
    Jake Read's avatar
    Jake Read committed
        try {
    
          MSGS.writeTo(msg, value, state.type)
          writeMessage(msg)
        } catch (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')
    
    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)
    
    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 ...
    
    Jake Read's avatar
    Jake Read committed
          let msg = msgsin.get()
          // at view, read in and deserialize list
    
          if (msgverbose) console.log('VIEW RX MSG:', msg)
    
          if (!Array.isArray(msg)) throw new Error(`view throwing object message, having header ${msg.header}`)
    
    Jake Read's avatar
    Jake Read committed
          // ok,
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is an error')
    
    Jake Read's avatar
    Jake Read committed
              msgbox.write(MSGS.readFrom(msg, 1, 'string').item)
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is hello')
    
              msgbox.write(`manager says hello, took ${performance.now() - helloTime}ms`)
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is a manager brief')
    
              // title, name of manager, lsit of unloaded hunks ?
    
    Jake Read's avatar
    Jake Read committed
              msgbox.write('manger sends program brief, will begin loading...')
    
              // serial -> js, by procedure du jakhey
              let brief = {}
              let bi = 1
              // reading strings returns item, increment
    
              temp = MSGS.readFrom(msg, bi, 'string')
              bi += temp.increment
              brief.interpreterName = temp.item
              temp = MSGS.readFrom(msg, bi, 'string')
              bi += temp.increment
              brief.interpreterVersion = temp.item
              temp = MSGS.readFrom(msg, bi, 'uint16')
              bi += temp.increment
              brief.numHunks = temp.item
              brief.numLinks = MSGS.readFrom(msg, bi, 'uint16').item
              // set local,
    
              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
              break
            case MK.LISTOFAVAIL:
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is a list of available items')
    
    Jake Read's avatar
    Jake Read committed
              let stringlist = MSGS.readListFrom(msg, 1, 'string')
    
    Jake Read's avatar
    Jake Read committed
              for (let item of stringlist) {
                this.addContextOption(item, (evt) => {
                  let msg = [MK.REQADDHUNK]
                  MSGS.writeTo(msg, item, 'string')
                  writeMessage(msg)
                  $(evt.target).append(' > requested ... ')
                })
              }
    
    Jake Read's avatar
    Jake Read committed
              break
    
            case MK.HUNKALIVE:
    
              // this can refer to new hunks, and modified hunk descriptions
              // i.e. changing a list of outputs and inputs
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is a new hunk')
    
              console.log('hunk alive, going to deserialize')
    
    Jake Read's avatar
    Jake Read committed
              let spec = specBySerial(msg, 1, true)
    
              // so first we check for an existing hunk,
              if(defs[spec.ind] === undefined){
                newDef(spec)
                msgbox.briefState.decrementHunks()
              } else {
                console.log('replacing hunk at', spec.ind)
                replaceDef(spec)
              }
    
    Jake Read's avatar
    Jake Read committed
              // hmmm ...
              // patchset.onHunkLoaded(def)
    
              break
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is a state change')
              let stchHnkInd = MSGS.readFrom(msg, 1, 'uint16').item
              let stchStInd = MSGS.readFrom(msg, 4, 'uint8').item
              let stDef
              try {
                stDef = defs[stchHnkInd].states[stchStInd]
              } catch (err) {
                console.error('likely error from state change indexing', err)
              }
              let stValUpdate = MSGS.readFrom(msg, 6, stDef.type).item
              stDef.set(stValUpdate)
    
            case MK.HUNKREMOVED:
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is a hunk to remove')
    
              let rmid = MSGS.readFrom(msg, 1, 'string').item
              removeDef(rmid)
    
    Jake Read's avatar
    Jake Read committed
              break
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is a link to put')
    
              let addOutInd = MSGS.readFrom(msg, 1, 'uint16').item
              let addOutputInt = MSGS.readFrom(msg, 4, 'uint8').item
              let addInInd = MSGS.readFrom(msg, 6, 'uint16').item
              let addInputInd = MSGS.readFrom(msg, 9, 'uint8').item
              putLink(addOutInd, addOutputInt, addInInd, addInputInd)
              //patchset.onLinkLoaded()
    
    Jake Read's avatar
    Jake Read committed
              msgbox.briefState.decrementLinks()
    
    Jake Read's avatar
    Jake Read committed
              break
    
            case MK.LINKREMOVED:
    
    Jake Read's avatar
    Jake Read committed
              if (msgverbose) console.log('VIEW MSG is a link to cut')
    
              let rmOutInd = MSGS.readFrom(msg, 1, 'uint16').item
              let rmOutputInt = MSGS.readFrom(msg, 4, 'uint8').item
              let rmInInd = MSGS.readFrom(msg, 6, 'uint16').item
              let rmInputInd = MSGS.readFrom(msg, 9, 'uint8').item
              cutLink(rmOutInd, rmOutputInt, rmInInd, rmInputInd)
    
    Jake Read's avatar
    Jake Read committed
              break
            default:
    
              throw new Error(`view receives message with no switch: ${msg[0]}`)
          } // end msgs switch
        } // end if-have-message
    
        // MSGS output check,
        if (outmsgbuffer.length > 0) {
          let debug = true
          if (!msgsout.io) {
            if (debug) {
              let msg = outmsgbuffer.shift()
              if (msgverbose) this.log(`buffer release msg type: ${msg.header}`)
              //console.log(msg.content)
              msgsout.put(msg)
            } else {
              msgsout.put(outmsgbuffer.shift())
            }
          }
        } // end msgs output check
    
      } // end loop
    
    Jake Read's avatar
    Jake Read committed
    }
    
    
    Jake Read's avatar
    Jake Read committed
    export default View