Skip to content
Snippets Groups Projects
vfloater.js 14.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jake Read's avatar
    Jake Read committed
    // ui divs / doms / and tigers
    
    
    Jake Read's avatar
    Jake Read committed
    import DomTools from './vdom.js'
    
    
    Jake Read's avatar
    Jake Read committed
    function Floater(view, grouptype) {
    
    Jake Read's avatar
    Jake Read committed
      // want one,
      // altho, non-ideal ...
      let dt = new DomTools(view)
    
    Jake Read's avatar
    Jake Read committed
      // TODO:
      /*
      ... think ... think ...
      -> button to expand, and gather? div-related?
      -> button drags as well ? titlematter does hover, not drag, two funcitons ?
      -> !!!!!!!!!!!!! button for fixed ! fixed !!!!!!!!!!!!!!!
      -> roll sim back, so that fixed / unfixed is meaningful
      -> explosions afterwards
      */
    
    Jake Read's avatar
    Jake Read committed
      // a floater is an individual that can be dragged, fixed/unfixed, etc
      // it is exactly the thing we give to d3 to lay out
      this.x = 0
      this.y = 0
    
    Jake Read's avatar
    Jake Read committed
      // want these,
      this.w = 101
      this.h = 101
    
    Jake Read's avatar
    Jake Read committed
      // if it has fx, fy, it is fixed
    
    Jake Read's avatar
    Jake Read committed
      this.r = 101 // to default,
    
    Jake Read's avatar
    Jake Read committed
      // these are handy,
      this.bb = {}
      this.bb.x1 = -50
      this.bb.y1 = -50
      this.bb.x2 = 51
      this.bb.y2 = 51
    
    Jake Read's avatar
    Jake Read committed
      // by default, no
      this.isFixed = false
    
      // will have this to track within simulation links
      this.simIndex = null
    
    
    Jake Read's avatar
    Jake Read committed
      // every floater has *at least* this ...
      // this is where we put the buttonz
      this.ownElement = $(`<div>`).addClass('float').get(0)
    
      $(this.ownElement).append(`<i class="em em-pushpin"></i>`)
    
    Jake Read's avatar
    Jake Read committed
      $(this.ownElement).on('click', (evt) => {
    
    Jake Read's avatar
    Jake Read committed
        if (this.isFixed === true) {
    
    Jake Read's avatar
    Jake Read committed
          this.free()
        } else {
          this.fix()
        }
      }).on('mouseover', (evt) => {
        document.body.style.cursor = 'pointer'
      }).on('mouseout', (evt) => [
        document.body.style.cursor = 'auto'
      ])
    
      $(view.plane).append(this.ownElement)
    
      // fixed <i class="em em-round_pushpin"></i>
      // open <i class="em em-pushpin"></i>
    
    Jake Read's avatar
    Jake Read committed
      // this is a list of elements to carry about
      this.bag = [] // gets {element, x, y}
      // fuuu
    
    Jake Read's avatar
    Jake Read committed
      let writeTitleMatter = (div, def) => {
        let md = false
        div.onmouseover = () => {
          def.highlight()
    
    Jake Read's avatar
    Jake Read committed
          if (!md) document.body.style.cursor = 'grab'
    
    Jake Read's avatar
    Jake Read committed
        }
        div.onmouseout = () => {
          def.unhighlight()
    
    Jake Read's avatar
    Jake Read committed
          if (!md) document.body.style.cursor = 'auto'
    
    Jake Read's avatar
    Jake Read committed
        }
        div.onmousedown = (evt) => {
          md = true
          document.body.style.cursor = 'grabbing'
          // pin to current position,
          this.fix()
          evt.preventDefault()
          evt.stopPropagation()
          let domElemMouseMove = (evt) => {
            // TRANSFORMS here to move div about on drag
            evt.preventDefault()
            evt.stopPropagation()
            // need to find that top-level view's scale transform
    
            this.fx += evt.movementX / view.tls
    
    Jake Read's avatar
    Jake Read committed
            this.fy += evt.movementY / view.tls
            this.newFix()
            this.tick()
          }
    
          function rmOnMouseUp(evt) {
            md = false
            document.body.style.cursor = 'auto'
            document.removeEventListener('mousemove', domElemMouseMove)
            document.removeEventListener('mouseup', rmOnMouseUp)
          }
    
          document.addEventListener('mousemove', domElemMouseMove)
          document.addEventListener('mouseup', rmOnMouseUp)
        }
      }
    
    
      // -------------------------------------------- TAKE
    
    
    Jake Read's avatar
    Jake Read committed
      // take up new things
    
      this.typeset = []
    
    Jake Read's avatar
    Jake Read committed
      this.take = (div, def, stall, resize) => {
    
    Jake Read's avatar
    Jake Read committed
        let type = ''
        // cover view-containing ... ??
        // if ($(div).is('.native') && $(div).children().contains('.view'))
    
    Jake Read's avatar
    Jake Read committed
        let item = {
          type: '',
          de: div,
          def: def,
          offsetX: 0,
          offsetY: 0
        }
        // always nice to do this first,
        // defs -> the dom
        $(view.plane).append(item.de)
    
    Jake Read's avatar
    Jake Read committed
        // only a few cases we will allow,
    
    Jake Read's avatar
    Jake Read committed
        if ($(div).is('.inputs')) {
    
    Jake Read's avatar
    Jake Read committed
          item.type = 'inputs'
    
    Jake Read's avatar
    Jake Read committed
        } else if ($(div).is('.outputs')) {
    
    Jake Read's avatar
    Jake Read committed
          item.type = 'outputs'
    
    Jake Read's avatar
    Jake Read committed
        } else if ($(div).is('.defcore')) {
    
    Jake Read's avatar
    Jake Read committed
          item.type = 'core'
    
        } else if ($(div).is('.nativewrap')) {
    
    Jake Read's avatar
    Jake Read committed
          item.type = 'native'
          // at the gitgo, set this to our current width...
    
    Jake Read's avatar
    Jake Read committed
          // this width thing is happening because you're rewriting the
    
    Jake Read's avatar
    Jake Read committed
          // so ... not a take, a swap ? !!! FFUUUUU
    
    Jake Read's avatar
    Jake Read committed
          // native element's wrapper ...
    
    Jake Read's avatar
    Jake Read committed
          this.calculateSizes()
    
    Jake Read's avatar
    Jake Read committed
          if (this.w < 200) {
            $(item.de).css('width', `200px`)
          } else {
            $(item.de).css('width', `${this.w}px`)
          }
    
    Jake Read's avatar
    Jake Read committed
          // and for non-spec'd heights,
    
    Jake Read's avatar
    Jake Read committed
          if (item.de.clientHeight < 10) {
    
    Jake Read's avatar
    Jake Read committed
            $(item.de).css('height', '200px')
          }
    
    Jake Read's avatar
    Jake Read committed
          // ok then, resizeable also
    
          dt.makeResizeable(item.de, this.onElementResize, resize)
    
        } else if ($(div).is('.defbutton')) {
    
    Jake Read's avatar
    Jake Read committed
          item.type = 'button'
    
    Jake Read's avatar
    Jake Read committed
        } else {
    
    Jake Read's avatar
    Jake Read committed
          // best guess, but..
          item.type = 'core'
    
    Jake Read's avatar
    Jake Read committed
          console.error('no thx, no recognized type when taking div into floater')
        }
    
    Jake Read's avatar
    Jake Read committed
        // put it in the baaaag
    
    Jake Read's avatar
    Jake Read committed
        this.typeset.push(item.type)
        this.bag.push(item)
    
        // (also) write the drag content, if there's a header on board
    
    Jake Read's avatar
    Jake Read committed
        let header = $(div).children('.header').get(0)
    
    Jake Read's avatar
    Jake Read committed
        if (header) {
    
    Jake Read's avatar
    Jake Read committed
          writeTitleMatter(header, item.def)
    
    Jake Read's avatar
    Jake Read committed
        }
    
        // just want to do this once,
    
    Jake Read's avatar
    Jake Read committed
        if (stall !== true) this.onChange()
    
      } // end take
    
    Jake Read's avatar
    Jake Read committed
    
      this.fitNativeToFloater = () => {
        this.calculateSizes(true)
        // find it,
        let nt = this.bag.find((cnd) => {
          return cnd.type === 'native'
        })
    
    Jake Read's avatar
    Jake Read committed
        if (!nt) {
    
    Jake Read's avatar
    Jake Read committed
          console.error('not finding a native element to resize here')
        } else {
          $(nt.de).css('width', `${this.w}px`)
          // want also to reset the resizing handle ...
          // kind of messy call, but
          let w = nt.de.clientWidth
          let h = nt.de.clientHeight
          let handle = $(nt.de).children('.resizehandle').get(0)
    
    Jake Read's avatar
    Jake Read committed
          dt.writeTransform(handle, {
            x: w,
            y: h
          })
    
    Jake Read's avatar
    Jake Read committed
        }
    
    Jake Read's avatar
    Jake Read committed
      }
    
    
    Jake Read's avatar
    Jake Read committed
      // to catch,
    
    Jake Read's avatar
    Jake Read committed
      this.onElementResize = () => {
    
    Jake Read's avatar
    Jake Read committed
        // this is a lot,
        this.onChange()
    
        // our parent,
        view.kick()
    
    Jake Read's avatar
    Jake Read committed
      }
    
    
    Jake Read's avatar
    Jake Read committed
      // GROUP TYPES:
      // 'edges' ... (core) with (outputs) and .edges || (inputs)
      // 'unwrapped' ... (core) with (outputs) and .unwrapped || (inputs)
      // 'wrapped' ... (core) with (inputs) and (outputs) and (native .view) and .wrapped
      // 'std' ... (core) with some set of (inputs) and (outputs) and (native)
    
    
      this.makeEdges = () => {
        this.grouptype === 'edges'
        this.onChange()
      }
    
      // -------------------------------------------- CHANGE
    
    Jake Read's avatar
    Jake Read committed
    
    
      this.grouptype = grouptype
    
    Jake Read's avatar
    Jake Read committed
      // don't want to do this all the time
      // to 'open' ... put core with outputs,
      // to 'unwrap' ... put core with inputs, it will go below
      this.onChange = () => {
    
        // find types,
    
    Jake Read's avatar
    Jake Read committed
        let core = this.bag.find((cnd) => {
          return cnd.type === 'core'
        })
        let inputs = this.bag.find((cnd) => {
          return cnd.type === 'inputs'
        })
        let outputs = this.bag.find((cnd) => {
          return cnd.type === 'outputs'
        })
        let native = this.bag.find((cnd) => {
          return cnd.type === 'native'
        })
    
        // possible to have many,
        let buttons = []
    
    Jake Read's avatar
    Jake Read committed
        for (let item of this.bag) {
          if (item.type === 'button') buttons.push(item)
    
    Jake Read's avatar
    Jake Read committed
        let spaces = 5
    
        // TYPE CASES BY TYPESET
    
    Jake Read's avatar
    Jake Read committed
        //console.log('FLT onchange, floater has', core ? '(core)' : '', inputs ? '(inputs)' : '', outputs ? '(outputs)' : '', native ? '(native)' : '')
    
    Jake Read's avatar
    Jake Read committed
    
        if (this.grouptype === 'std') {
          // 'std' ... (core) with some set of (inputs) and (outputs) and (native)
    
    Jake Read's avatar
    Jake Read committed
          core.offsetX = -core.de.clientWidth
    
    Jake Read's avatar
    Jake Read committed
          core.offsetY = 0
    
    Jake Read's avatar
    Jake Read committed
          if (inputs) {
    
    Jake Read's avatar
    Jake Read committed
            inputs.offsetX = core.offsetX - inputs.de.clientWidth - spaces - 2
    
    Jake Read's avatar
    Jake Read committed
            inputs.offsetY = 0
          }
    
    Jake Read's avatar
    Jake Read committed
          if (outputs) {
    
    Jake Read's avatar
    Jake Read committed
            outputs.offsetX = spaces
            outputs.offsetY = 0
    
    Jake Read's avatar
    Jake Read committed
          if (native) {
            // calc sizes, ignoring native
            this.calculateSizes(true)
            native.offsetX = this.bb.x1
            native.offsetY = this.bb.y2 + spaces
            // for resizing,
            if (outputs) {
              let gap = native.de.clientWidth - this.w
              if (gap > 1) {
                outputs.offsetX += gap
              }
    
    Jake Read's avatar
    Jake Read committed
          }
    
    Jake Read's avatar
    Jake Read committed
        } else if (this.grouptype === 'unwrapped') {
          // 'unwrapped' ... (core) with (outputs) and .unwrapped || (inputs)
          if (core) {
            // unwrapped, left side, core, outputs, etc
            core.offsetX = -core.de.clientWidth
            core.offsetY = 0
            if (outputs) {
              outputs.offsetX = spaces
              outputs.offsetY = 0
            }
            if (native) {
              this.calculateSizes(true)
              native.offsetX = this.bb.x1
              native.offsetY = this.bb.y2 + spaces
              if (outputs) {
                let gap = native.de.clientWidth - this.w
                if (gap > 1) {
                  outputs.offsetX += gap
                }
              }
            }
          } else {
            // unwrapped, right side, it's the lonely inputs
            if (inputs) {
              inputs.offsetX = -inputs.de.clientWidth
              inputs.offsetY = 0
    
    Jake Read's avatar
    Jake Read committed
            }
          }
    
    Jake Read's avatar
    Jake Read committed
        } else if (this.grouptype === 'wrapped') {
          // should have 'em all let's just wyld out
          // the view attached is king,
          let ntw = native.de.clientWidth
          let nth = native.de.clientHeight
          // left ...
    
    Jake Read's avatar
    Jake Read committed
          native.offsetX = -core.de.clientWidth
          native.offsetY = core.de.clientHeight + spaces
    
    Jake Read's avatar
    Jake Read committed
          // core
    
    Jake Read's avatar
    Jake Read committed
          core.offsetX = -core.de.clientWidth
          core.offsetY = 0
    
    Jake Read's avatar
    Jake Read committed
          // inputs, well left
    
    Jake Read's avatar
    Jake Read committed
          inputs.offsetX = core.offsetX - inputs.de.clientWidth - spaces
    
          inputs.offsetY = $(inputs.de).children('.header').get(0).clientHeight + spaces + native.offsetY
    
    Jake Read's avatar
    Jake Read committed
          // ouputs, as is tradition
    
    Jake Read's avatar
    Jake Read committed
          outputs.offsetX = ntw + spaces + core.offsetX
          outputs.offsetY = inputs.offsetY
    
        } else if (this.grouptype === 'edges') {
          if(core && outputs){
            this.edgetype = 'left'
            // and on top of self,
            core.offsetX = -core.de.clientWidth
            core.offsetY = -core.de.clientHeight - 25
            outputs.offsetX = core.offsetX
            outputs.offsetY = core.offsetY - outputs.de.clientHeight - spaces
          } else {
            this.edgetype = 'right'
            inputs.offsetX = -inputs.de.clientWidth
            inputs.offsetY = -inputs.de.clientHeight - 25
            // aaand
            view.msgbox.setTopMargin( - inputs.offsetY + 20)
          }
    
    Jake Read's avatar
    Jake Read committed
        } else {
    
    Jake Read's avatar
    Jake Read committed
          console.error(`this floater grouptype not written or recognized: '${this.grouptype}'`)
    
    Jake Read's avatar
    Jake Read committed
        }
    
    Jake Read's avatar
    Jake Read committed
    
    
    Jake Read's avatar
    Jake Read committed
        // now we can cover buttons, using core offsets
        // cover buttons, if(buttons.length){
        if (buttons.length > 0) {
          if (!core) console.error('watch buttons not-on core?')
          let inc = 0
          for (let btn of buttons) {
            btn.offsetX = core.offsetX + inc
            btn.offsetY = -20 - core.offsetY
            inc += 20
          }
        }
        // ok,
    
    
    Jake Read's avatar
    Jake Read committed
        // now we can execute this,
        this.moveTo()
    
    Jake Read's avatar
    Jake Read committed
        // oy oy oy
    
      } // end onChange
    
    Jake Read's avatar
    Jake Read committed
    
      let writePosition = (element, x, y) => {
        $(element).css('left', `${x}px`).css('top', `${y}px`)
      }
    
    
    Jake Read's avatar
    Jake Read committed
      let readPosition = (element) => {
        let it = $(element)
        return {
          x: parseInt(it.css('left')),
          y: parseInt(it.css('top'))
        }
      }
    
      let readSize = (element) => {
        return {
          w: element.clientWidth,
          h: element.clientHeight
        }
      }
    
    
    Jake Read's avatar
    Jake Read committed
      this.moveTo = (x, y, backdoor) => {
    
    Jake Read's avatar
    Jake Read committed
        // but still probably want to check our offsets,
    
    Jake Read's avatar
    Jake Read committed
        if (this.isFixed && !backdoor) {
    
    Jake Read's avatar
    Jake Read committed
          this.x = this.fx
          this.y = this.fy
    
    Jake Read's avatar
    Jake Read committed
        }
    
    Jake Read's avatar
    Jake Read committed
        // to just draw new position, run with no args
    
        if (x === undefined) {
    
    Jake Read's avatar
    Jake Read committed
          x = this.x
          y = this.y
    
    Jake Read's avatar
    Jake Read committed
        } else {
          this.x = x
          this.y = y
    
    Jake Read's avatar
    Jake Read committed
        }
    
        // will return this ...
        let desire = {
          x: 0,
          y: 0
        }
    
    Jake Read's avatar
    Jake Read committed
        // check bounds,
        if(!view.isTopLevel){
          // bummer to do so many times !
          let bounds = view.getCurrentBounds()
    
          this.calculateSizes()
          //
          if(this.grouptype === 'edges'){
            //console.log('bounds', bounds)
            // x1, y1, x2, y2, w, h
            if(this.edgetype === 'left'){
              this.x = - this.bb.x1
              this.y = - this.bb.y1 + 17 + 5
            } else {
    
    Jake Read's avatar
    Jake Read committed
              this.x = bounds.x2 - this.bb.x2 - 2
    
              this.y = bounds.y1 - this.bb.y1 + 17 + 5
            }
          } else {
            //console.log('place bounds', this.bb)
            //console.log('bounds', bounds)
            // x needs to contend with offset,
            // this.bb.x1 probably -ve,
            // this.bb.y1 probably -ve,
            // want to track, but won't push things left or up,
            this.x = Math.max(bounds.x1 - this.bb.x1, this.x) // lower bounds
            this.y = Math.max(bounds.y1 - this.bb.y1, this.y)
    
            // want-right term,
            let rbnd = bounds.x2 - this.bb.x2
            if(this.x > rbnd){
              desire.x = this.x - rbnd
              this.x = rbnd
              // wants to go right
            }
            // want-down term,
            let lbnd = bounds.y2 - this.bb.y2
            if(this.y > lbnd){
              desire.y = this.y - lbnd
              this.y = lbnd
              // wants to go down
            }
    
    Jake Read's avatar
    Jake Read committed
          }
    
    Jake Read's avatar
    Jake Read committed
        }
    
    Jake Read's avatar
    Jake Read committed
        // do work
        writePosition(this.ownElement, x - 16.5, y - 20)
    
    Jake Read's avatar
    Jake Read committed
        for (let item of this.bag) {
    
    Jake Read's avatar
    Jake Read committed
          writePosition(item.de, this.x + item.offsetX, this.y + item.offsetY)
    
    Jake Read's avatar
    Jake Read committed
        }
    
        // aaaand tell em what we waaaaant
        return desire
    
    Jake Read's avatar
    Jake Read committed
      }
    
    
    Jake Read's avatar
    Jake Read committed
      this.newFix = () => {
        this.moveTo(this.fx, this.fy, true)
      }
    
      // as is tradition, we'll keep things local,
      // and just make sure we re-calculate during relevant events... ?
    
    Jake Read's avatar
    Jake Read committed
      this.calculateSizes = (nonNative) => {
    
    Jake Read's avatar
    Jake Read committed
        // from core of xy, values like
        //
        //   * -------------- (y1) -------- *
        //   |                 |            |
        //   |(x1) --------- (x,y) ---- (x2)|
        //   |                |             |
        //   |                |             |
        //   * ------------ (y2) ---------- *
    
    
    Jake Read's avatar
    Jake Read committed
        let x1 = 0
        let y1 = 0
        let x2 = 0
        let y2 = 0
    
    Jake Read's avatar
    Jake Read committed
        for (let item of this.bag) {
    
    Jake Read's avatar
    Jake Read committed
          // items are str8 cut dom elements,
    
    Jake Read's avatar
    Jake Read committed
          if (nonNative && item.type === 'native') continue
    
    Jake Read's avatar
    Jake Read committed
          let wt = readSize(item.de)
          // we know item offsets, so
    
    Jake Read's avatar
    Jake Read committed
          if (item.offsetX < x1) x1 = item.offsetX
          if (item.offsetY < y1) y1 = item.offsetY
          if (item.offsetX + wt.w > x2) x2 = item.offsetX + wt.w
          if (item.offsetY + wt.h > y2) y2 = item.offsetY + wt.h
    
    Jake Read's avatar
    Jake Read committed
          // corners
        }
    
    Jake Read's avatar
    Jake Read committed
        this.bb.x1 = x1
        this.bb.y1 = y1
        this.bb.x2 = x2
        this.bb.y2 = y2
    
    Jake Read's avatar
    Jake Read committed
        // ok,
        this.w = x2 - x1
        this.h = y2 - y1
        // also,
        this.r = Math.max(this.w, this.h) / 2
        // check,
        let aspect = this.w / this.h
    
    Jake Read's avatar
    Jake Read committed
        if (aspect > 8 || aspect < 0.125) {
    
    Jake Read's avatar
    Jake Read committed
          //console.error(`warning: deviously large aspect for 'circular' collisions: ${aspect}, logging floater`, this)
        }
      }
    
      this.tick = () => {
        // will use to bother the simulation
        view.tick()
      }
    
      this.free = () => {
        this.x = this.fx
        this.y = this.fy
        delete this.fx
        delete this.fy
        this.isFixed = false
    
        $(this.ownElement).html(`<i class="em em-pushpin"></i>`)
    
    Jake Read's avatar
    Jake Read committed
        this.tick()
      }
    
      this.fix = () => {
    
        $(this.ownElement).html(`<i class="em em-round_pushpin"></i>`)
    
    Jake Read's avatar
    Jake Read committed
        this.isFixed = true
    
    Jake Read's avatar
    Jake Read committed
        this.fx = this.x
        this.fy = this.y
        this.moveTo(this.fx, this.fy, true)
      }
    
      this.fixTo = (x, y) => {
        this.x = x
        this.y = y
        this.fix()
    
    Jake Read's avatar
    Jake Read committed
      }
    
    
      // RIP
      this.cleanup = () => {
    
    Jake Read's avatar
    Jake Read committed
        // js deletes things that are unreachable, so
        // there are a few objects that are on the canvas we want to get rid of
        $(this.ownElement).remove()
        // also, any handle added to a native element
        // not being able to search by js subset is kind of a bummer here, but
        let native = this.bag.find((cnd) => {
          return cnd.type === 'native'
        })
    
    Jake Read's avatar
    Jake Read committed
        if (native) {
    
    Jake Read's avatar
    Jake Read committed
          $(native.de).children('.resizehandle').remove()
        }
    
    Jake Read's avatar
    Jake Read committed
      //
    }
    
    export default Floater