Skip to content
Snippets Groups Projects
Select Git revision
  • 97ddc4437b60a18a6913947da67b2a672ae05b46
  • master default protected
  • leo
  • dex
  • pendulum
  • apfelstruder
  • littlerascal
7 results

vdef.js

Blame
  • vdef.js 24.49 KiB
    // a def is not a hunk, it's a definition of a hunk - the mirror image
    
    import {
      isIntType
    } from '../typeset.js'
    
    import Floater from './vfloater.js'
    
    // new this -
    function HunkDefinition(spec, view, dt, debug) {
      // the basics,
      this.ind = spec.ind
      this.name = spec.name
      this.type = spec.type
      this.inputs = new Array()
      this.outputs = new Array()
      this.states = new Array()
      // yep,
      this.parentView = view
    
      // dom elements group, containing (but not limited to)
      this.deg = {
        core: {}
      }
      this.isExploded = false
      this.floatGroupType = 'collected'
    
      // core ...
      this.deg.core = $('<div>').addClass('defcore').attr('id', `${this.name}_${this.ind}`).get(0)
      let title = $(`<div>${this.name}</div>`).addClass('deftitle').addClass('header').get(0)
      $(title).append($(`<span style="float:right">(${this.type})</span>`))
    
      $(this.deg.core).append(title)
      if (spec.states.length > 0) {
        let sdom = $('<div>').addClass('states')
        for (let st in spec.states) {
          let state = new StateDefinition(spec.states[st], st, this, view, debug)
          this.states.push(state)
          $(sdom).append(state.de)
        }
        $(this.deg.core).append(sdom)
      }
    
      // init our floater array, and startup with 0th entry (one floater per def == standard)
      this.floaters = []
      this.floaters.push(new Floater(view, 'std'))
    
      // write inputs
      if (spec.inputs.length > 0) {
        // idom, odom want title bars ...
        let idom = $('<div>').addClass('inputs')
        $(idom).append($(`<div>inputs >></div>`).addClass('inputheader').addClass('header'))
        for (let ip in spec.inputs) {
          let input = new InputDefinition(spec.inputs[ip], ip, this, debug)
          this.inputs.push(input)
          $(idom).append(input.de)
        }
        this.deg.inputs = $(idom).get(0)
        this.floaters[0].take(this.deg.inputs, this, true)
      }
      // write outputs,
      if (spec.outputs.length > 0) {
        let odom = $('<div>').addClass('outputs')
        $(odom).append($(`<div>>> outputs</div>`).addClass('outputheader').addClass('header'))
        for (let op in spec.outputs) {
          let output = new OutputDefinition(spec.outputs[op], op, this, view, dt, debug)
          this.outputs.push(output)
          $(odom).append(output.de)
        }
        this.deg.outputs = $(odom).get(0)
        this.floaters[0].take(this.deg.outputs, this, true)
      }
    
      // title right-click handler
      title.oncontextmenu = (evt) => {
        evt.preventDefault()
        evt.stopPropagation()
        // write menu for requesting delete and copy
        let menu = $('<div>').addClass('contextmenu').get(0)
        $(menu).append($('<li><i class="em em-coffin"></i> remove hunk</li>').on('click', (evt) => {
          view.requestRemoveHunk(this.ind)
          $(menu).remove()
        }))
        $(menu).append($('<li><i class="em em-abc"></i> rename hunk</li>').on('click', (evt) => {
          $(evt.target).text('')
          let tinput = $('<input>').attr('type', 'text').attr('size', 24).attr('value', 'new name').get(0)
          $(evt.target).append(tinput)
          $(tinput).focus()
          $(tinput).select()
          $(tinput).on('keyup', (evt) => {
            if (evt.keyCode == 13) {
              view.requestRenameHunk(this.ind, tinput.value).then((def) => {
                console.log('cleared new name for', def)
                $(menu).remove()
              })
            }
          })
        }))
        $(menu).append($('<li><i class="em em-repeat_one"></i> copy hunk</li>').on('click', (evt) => {
          // todo: maybe this should copy state and req it down?
          view.requestAddHunk(this.type)
          $(menu).remove()
        }))
        // place it,
        // title.offsetWidth is 400
        // de.style.left, de.style.top, de.clientWidth,
        let ct = dt.readTransform(this.deg.core)
        let mp = {
          s: 1,
          x: ct.x,
          y: ct.y - 31*3 - 10
        }
        if(this.containsButtons()) mp.y -= 25
        if(view.isTopLevel){
          // write it,
          $(menu).append($('<li><i class="em em-arrows_counterclockwise"></i> reload from source</li>').on('click', (evt) => {
            view.reloadHunk(this.ind, false)
          }))
          mp.y -= 31
        }
        dt.writeTransform(menu, mp)
        $(view.plane).append(menu)
      }
    
      // take the core, not stalling,
      this.floaters[0].take(this.deg.core, this, false)
    
      /* ---------------------------    ---------------------------- */
      /* ------------------------- PHYSICS ------------------------- */
      /* ---------------------------    ---------------------------- */
    
      this.highlight = () => {
        $(this.deg.core).find('.deftitle').css('background-color', '#969696')
        if (this.deg.inputs) $(this.deg.inputs).find('.inputheader').css('background-color', 'white').css('color', 'black')
        if (this.deg.outputs) $(this.deg.outputs).find('.outputheader').css('background-color', 'white').css('color', 'black')
        if (this.deg.native) $(this.deg.native).find('.nativeheader').css('background-color', 'white').css('color', 'black')
      }
    
      this.unhighlight = () => {
        $(this.deg.core).find('.deftitle').css('background-color', '#303030')
        if (this.deg.inputs) $(this.deg.inputs).find('.inputheader').css('background-color', 'red').css('color', 'white')
        if (this.deg.outputs) $(this.deg.outputs).find('.outputheader').css('background-color', 'blue').css('color', 'white')
        if (this.deg.native) $(this.deg.native).find('.nativeheader').css('background-color', 'green').css('color', 'white')
      }
    
      /* ---------------------------    ---------------------------- */
      /* ------------------------- UPDATES ------------------------- */
      /* ---------------------------    ---------------------------- */
    
      // UPDATE Index
      this.newInd = (ind) => {
        this.ind = ind
        // and those titles, and ids ...
        $(this.de).attr('id', `${this.name}_${this.ind}`)
        $(title).text(`${this.name}`)
        title.append($(`<span style="float:right">(${this.type})</span>`).get(0))
        for (let ip of this.inputs) {
          ip.onNewParentInfo()
        }
        for (let op of this.outputs) {
          op.onNewParentInfo()
        }
        for (let st of this.states) {
          st.onNewParentInfo()
        }
      }
    
      // UPDATE Name
      this.updateName = (newName) => {
        this.name = newName
        $(this.de).attr('id', `${this.name}_${this.ind}`)
        $(title).text(`${this.name}`)
        title.append($(`<span style="float:right">(${this.type})</span>`).get(0))
        for (let ip of this.inputs) {
          ip.onNewParentInfo()
        }
        for (let op of this.outputs) {
          op.onNewParentInfo()
        }
        for (let st of this.states) {
          st.onNewParentInfo()
        }
      }
    
      this.cleanup = () => {
        cleanButtons()
        cleanFloaters()
        for (let od in this.deg) {
          $(this.deg[od]).remove()
        }
      }
    
      let cleanFloaters = () => {
        for (let flt of this.floaters) {
          flt.cleanup()
        }
        this.floaters.length = 0
      }
    
      this.containsButtons = () => {
        for(let fl of this.floaters){
          for(let it of fl.bag){
            if(it.type === 'button') return true
          }
        }
        return false
      }
    
      let cleanButtons = () => {
        // sloppy! there is probably one in one of the cores,
        try {
          // max 5...
          for (let i = 0; i < 5; i++) {
            let bindex = this.floaters[0].bag.findIndex((cnd) => {
              return cnd.type === 'button'
            })
            if (bindex !== -1) {
              // also going to get that typeset issue ...
              // pls to god this is reworked before a bug resulting appears
              // rm from the dom,
              $(this.floaters[0].bag[bindex].de).remove()
              this.floaters[0].bag.splice(bindex, 1)
            } else {
              break
            }
          }
          // now we can
          this.floaters[0].onChange()
        } catch (err) {
          console.error(`couldn't clean buttons from ${this.name}, for reasons:`, err)
        }
      }
    
      /* ---------------------------    ---------------------------- */
      /* ----------------- RM/ADD/ECT to FLOATERS ------------------ */
      /* ---------------------------    ---------------------------- */
    
      this.collect = () => {
        // restore state, collect .deg under one hood (what of buttons?)
        console.error('not yet collecting')
      }
    
      this.unwrap = () => {
        // never twice
        if (this.floatGroupType === 'unwrapped') return
        // otherwise
        this.floatGroupType = 'unwrapped'
        // free willy
        // ok, can we just murder everything and start from scratch?
        // still have those .deg elements, so ...
        // will have to write a remove
        //console.log('UNWRAP')
        cleanFloaters()
        // (unrwapped) has a core with the core, outputs, and native
        let cflt = new Floater(view, 'unwrapped')
        if (this.deg.outputs) {
          cflt.take(this.deg.outputs, this, true)
        }
        if (this.deg.native) {
          if (this.type === 'view' && view) {
            //console.log('ommitting view deg from this def')
          } else {
            cflt.take(this.deg.native, this, true)
          }
        }
        cflt.take(this.deg.core, this)
        if (this.deg.native) {
          if (this.type === 'view' && view) {
            //console.log('ommitting view deg from this def')
          } else {
            cflt.fitNativeToFloater()
          }
        }
        this.floaters.push(cflt)
        // if there are inputs, they are separate
        if (this.deg.inputs) {
          let iflt = new Floater(view, 'unwrapped')
          iflt.take(this.deg.inputs, this)
          this.floaters.push(iflt)
        }
        // and kick that
        view.floop.reset()
      }
    
      // for links, accept a view domain element, add to ur floater, wrap that floater
      this.wrapon = (viewDef) => {
        // don't do it twice
        if (this.floatGroupType === 'wrappedon') return
        // set state
        this.floatGroupType = 'wrappedon'
        // errs when not a link,
        if (this.type !== 'link') throw new Error('non-links doth not wrap')
        // first stat, we want to keep this ref,
        this.reciprocalView = viewDef.hunk
        // ok, and we want viewdef objects to highlight ...
        // if we're not gathered to start, that's probably bad news
        if (this.floaters.length > 1) console.error('wyd here? many floaters during a wrap', this.floaters)
        // get the floater's bag-item containing that view ...
        let vflt = viewDef.floaters.find((cnd) => {
          return cnd.typeset.includes('native')
        })
        if (!vflt) throw new Error('could not find view floater on swap')
        //
        let vbagindex = vflt.bag.findIndex((cnd) => {
          return cnd.type === 'native'
        })
        if (vbagindex === -1) throw new Error('could not find floater native item on swap')
        let vbagitem = vflt.bag.splice(vbagindex, 1)[0]
        // before we do this, let's push out some extra space
        // this *is a hack*
        let bnds = view.getCurrentBounds()
        view.requestResize(1800, bnds.h + 600)
        // we need to make sure it's in the link's plane, jquery is smart about this,
        //console.log(view.plane, vbagitem.de)
        $(view.plane).append(vbagitem.de)
        // the link's 1st floater,
        let flt = this.floaters[0]
        flt.grouptype = 'wrapped'
        // flt.take au manuel
        flt.bag.push(vbagitem)
        flt.typeset.push('native')
        vbagitem.de.onResizeCustomCallback = flt.onElementResize
        // can just do recalc now?
        flt.onChange()
        // and also,
        vflt.onChange()
        // and *also*
        this.removeButton('<i class="em em-squid"></i>')
        this.addButton('<i class="em em-shell"></i>', (evt) => {
          console.error('no collapse yet')
        })
      }
    
      this.edgecase = () => {
        // check
        if (this.floatGroupType === 'edgecased') return
        // or set
        this.floatGroupType = 'edgecased'
        // also links only pls,
        if (this.type !== 'link') throw new Error('non-links doth not edge')
        // almost forgot, we want to get rid of this button:
        cleanButtons()
        // similar to the unwrap spec,
        // we're also assuming at this point that this view isn't wrapped around a view
        cleanFloaters()
        // now we want a left floater,
        // free willy
        let lflt = new Floater(view, 'edges')
        lflt.take(this.deg.core, this, true)
        lflt.take(this.deg.outputs, this, true)
        lflt.makeEdges()
        this.floaters.push(lflt)
        // and the right,
        let rflt = new Floater(view, 'edges')
        rflt.take(this.deg.inputs, this, true)
        rflt.makeEdges()
        this.floaters.push(rflt)
        // ok,
        view.floop.reset()
        // put inputs / outputs at edges of view body
        // put core below inputs, at left
        // make message box of the view sit low
        // handle view resizes ?
      }
    
      // adden em
      this.addButton = (text, callback) => {
        // write a button,
        let btn = $(`<div>${text}</div>`).addClass('defbutton').get(0)
        $(btn).on('click', callback).on('mouseover', (evt) => {
          document.body.style.cursor = 'pointer'
        }).on('mouseout', (evt) => [
          document.body.style.cursor = 'auto'
        ])
        // ok ok, then ...
        // we find the floater that contains the core,
        let coreFloater = this.floaters.find((cnd) => {
          return cnd.typeset.includes('core')
        })
        if (!coreFloater) throw new Error(`couldn't find a floater for this def button`, text)
        // attach the button element to its bag ...
        coreFloater.take(btn, this)
      }
    
      this.removeButton = (text, callback) => {
        // find the core-containing floater
        // we find the floater that contains the core,
        let coreFloater = this.floaters.find((cnd) => {
          return cnd.typeset.includes('core')
        })
        if (!coreFloater) throw new Error(`couldn't find a floater for this def button`, text)
        let success = false
        // au manuel splice outta the bag
        for (let item in coreFloater.bag) {
          let itm = coreFloater.bag[item]
          if (itm.type === 'button') {
            if ($(itm.de).html() === text) {
              // splice it out,
              coreFloater.bag.splice(item, 1)
              $(itm.de).remove()
              coreFloater.onChange()
              success = true
            }
          }
        }
        if (!success) console.error(`couldn't remove the button having text ${text}`)
    
      }
    
      // absorbing a native hunk's domain element,
      this.takeNative = (hunk) => {
        this.hunk = hunk
        // wants a wrapper,
        this.deg.native = $('<div>').addClass('nativewrap').get(0)
        // wrapper includes this title bar,
        $(this.deg.native).append($(`<div>${this.name}'s html element</div>`).addClass('nativeheader').addClass('header'))
        // and the hunk's element goes in that wrapper.
        $(this.deg.native).append(this.hunk.dom)
        // now we can hook it 2 a floater ... this will depend on our state, which we can mod later
        // atm, we'll put it with the core,
        let coreFloater = this.floaters.find((cnd) => {
          return cnd.typeset.includes('core')
        })
        if (!coreFloater) throw new Error(`couldn't find a floater for this def native`)
        // hook it
        coreFloater.take(this.deg.native, this, false, this.hunk.onresize)
        // has this handle,
        this.hunk.requestResize = (x, y) => {
          // set manual and call update,
          // doesn't matter which hunk this is in...
          // console.log(`REQRESIZE hunk ${this.name} requesting size of ${x}, ${y}`)
          // console.log('native', this.deg.native)
          this.deg.native.requestResize(x, y)
          view.tick()
        }
        // now we should be able to,
        this.hunk.onload()
      }
    
      // add special case buttons, just this so far
      if (this.type === 'link') {
        // todo:
        let expander = () => {
          this.addButton('<i class="em em-squid"></i>', (evt) => {
            // ???
            view.tlv.expandLink(this).then((view) => {
              console.log("EXPANDEEED --> expandLink promise completes", view)
            }).catch((err) =>{
              console.log("EXP catches err", err)
            })
          })
        }
        expander()
      }
    
      this.fixWithDataPort = () => {
        if(this.floaters[0].isFixed){
          // find the data port,
          let dp = view.tlv.trace(this.outputs[0])
          if(dp){
            dp.parent.floaters[0].fixTo(this.floaters[0].fx + 500, this.floaters[0].fy - 100)
          } else {
            console.error("no data port from trace")
          }
        } else {
          console.error("won't fix with data port: not fixed")
        }
      }
    
    } // end def def
    
    /* ---------------------------    ---------------------------- */
    /* ------------------------ INPUT DEF ------------------------ */
    /* ---------------------------    ---------------------------- */
    
    function InputDefinition(ipspec, ind, def, debug) {
      // keep track of name and type,
      this.name = ipspec.name
      this.type = ipspec.type
      this.parent = def
      this.ind = parseInt(ind)
      // a dom element
      this.de = $(`<li>${this.name} (${this.type})</li>`).addClass('input').get(0)
      this.de.id = `${this.parent.name}_${this.parent.ind}_input_${this.name}`
      this.onNewParentInfo = () => {
        this.de.id = `${this.parent.name}_${this.parent.ind}_input_${this.name}`
      }
      // we also keep a list,
      this.connections = new Array()
      this.disconnect = (output) => {
        let index = this.connections.findIndex((cand) => {
          return (cand.parent.ind === output.parent.ind && cand.name === output.name && cand.type === output.type)
        })
        if (index === -1) throw new Error('during output disconnect, input cannot find output...')
        this.connections.splice(index, 1)
      }
      this.disconnectAll = () => {
        for (let op of this.connections) {
          op.disconnect(this)
        }
        this.connections.length = 0
      }
      // to get this object via the dom, circular... apparently that is fine ?
      this.de.hookup = this
    }
    
    /* ---------------------------    ---------------------------- */
    /* ----------------------- OUTPUT DEF ------------------------ */
    /* ---------------------------    ---------------------------- */
    
    function OutputDefinition(opspec, ind, def, view, dt, debug) {
      // keep track of name and type,
      this.parent = def
      this.name = opspec.name
      this.type = opspec.type
      this.ind = parseInt(ind)
      // a dom element
      this.de = $(`<li>(${this.type}) ${this.name}</li>`).addClass('output').get(0)
      this.de.id = `${this.parent.name}_${this.parent.ind}_output_${this.name}`
      this.onNewParentInfo = () => {
        this.de.id = `${this.parent.name}_${this.parent.ind}_output_${this.name}`
      }
      // outputs handle all of the dragging-etc
      if (opspec.connections !== undefined) {
        this.specConnections = opspec.connections
      }
      this.connections = new Array() // of inputdefs, !
      this.connect = (inputdef) => {
        inputdef.connections.push(this)
        this.connections.push(inputdef)
      }
      this.disconnect = (inputdef) => {
        inputdef.disconnect(this)
        let iof = this.connections.findIndex((cand) => {
          return (cand.name === inputdef.name && cand.parent.ind === inputdef.parent.ind)
        })
        if (iof === -1) throw new Error('could not find input to disconnect')
        this.connections.splice(iof, 1)
        return true
      }
      this.disconnectAll = () => {
        for (let ip of this.connections) {
          ip.disconnect(this)
        }
        this.connections.length = 0
      }
      // the dragging
      this.floater = {}
      this.hasFloater = false
      // ondrag, attached later
      let evtDrag = (evt) => {
        evt.preventDefault()
        evt.stopPropagation()
        let pt = dt.readTransform(view.tlv.plane)
        let thet = dt.readTransform(this.floater)
        // ... set delta
        thet.x += evt.movementX / pt.s
        thet.y += evt.movementY / pt.s
        dt.writeTransform(this.floater, thet)
        view.drawLinks()
      }
      // to remove the below
      let dragMouseUp = (evt) => {
        if (debug) console.log('MOUSEUP ON', evt.target.id)
        // 1st, make sure it's an input
        if ($(evt.target).is('.input')) {
          // HERE: searcheth by input id text? or
          let hk = evt.target.hookup
          // hookups are hooks to the input's def-object
          // console.log('ah hookup looks like:', hk)
          if (!hk) throw new Error('missing some data at this input...')
          // use a dom data flag, to find that input and output id?
          // are we in the same context?
          if(hk.parent.parentView === this.parent.parentView){
            view.requestAddLink(this, hk)
          } else {
            console.warn("UI Route Builder Begins...")
            this.parent.parentView.tlv.buildRoute(this, hk).then(() => {
              console.warn("UI Route Builder Resolves !")
            })
          }
          // do things to conn, then
        }
        // cleanup
        document.removeEventListener('mouseup', dragMouseUp)
        document.removeEventListener('mousemove', evtDrag)
        // remove the floater flag
        this.hasFloater = false
        // remove the floater itself
        $(view.plane).find('#floater').remove()
        view.drawLinks()
      }
      // eeeeehntr for link-hookup-dragging
      this.de.onmousedown = (evt) => {
        evt.stopPropagation()
        evt.preventDefault()
        if (debug) console.log('mousedown for', this)
        // this and that flag will be read-in on drawlinks, to draw that link
        this.floater = $('<div>').attr('id', 'floater').append(this.type).get(0)
        this.hasFloater = true
        this.floater.style.zIndex = '1'
        // set initial position by the outputs' position,
        let dparent = $(this.de).parent().get(0)
        let opp = dt.readTransform(dparent)
        // top-level plane ...
        let pt = dt.readTransform(view.tlv.plane)
        // plonk: have to do this now or else clientHeight / width are 0
        view.plane.appendChild(this.floater)
        // init out floater position, and put it in the dom
        dt.writeTransform(this.floater, {
          s: 1,
          x: opp.x - ((this.floater.clientWidth + 5)),
          y: opp.y + this.floater.clientHeight * (this.ind + 0.5) // - ((fltheight * pt.s) / 2) / pt.s
        })
        // handlers to drag, and remove
        document.addEventListener('mousemove', evtDrag)
        // and delete / act when mouse comes up
        document.addEventListener('mouseup', dragMouseUp)
      }
    }
    
    /* ---------------------------    ---------------------------- */
    /* ------------------------ STATE DEF ------------------------ */
    /* ---------------------------    ---------------------------- */
    
    function StateDefinition(stspec, ind, def, view, debug) {
      this.parent = def
      this.name = stspec.name
      this.type = stspec.type
      this.ind = parseInt(ind)
      // business,
      this.value = stspec.value
      // ok,
      this.de = $('<div>' + this.name + " (" + this.type + ")" + '</div>').addClass('stateItem').get(0)
      this.de.id = `${this.parent.name}_${this.parent.ind}_state_${this.name}`
      this.onNewParentInfo = () => {
        this.de.id = `${this.parent.name}_${this.parent.ind}_state_${this.name}`
      }
      // ui for these ... we can just cover the basics of js types because yonder serializations
      // etc will throw errors for other types. of course, this could help more, but we're in a rush
      switch (typeof this.value) {
        case 'string':
          //dom.append($('<br>').get(0))
          let strinput = $('<input>').attr('type', 'text').attr('size', 32).attr('value', this.value).css('width', '240px').get(0)
          strinput.addEventListener('change', (evt) => {
            // ask for a change,
            // TODO HERE NOW: this is the state change request you want to write
            // do it like writeMessage() instead
            // requestStateChange(def.id, state, strinput.value)
            // but assert that we don't change the definition unless
            view.requestStateChange(this, strinput.value)
            strinput.value = this.value
          })
          this.de.append(strinput)
          this.set = (value) => {
            if (typeof value === 'string') {
              strinput.value = value
              this.value = value
            } else {
              throw new Error('bad type put into state dom')
            }
          }
          break // end string types
        case 'number':
          let ninput = $('<input>').addClass('stateNumInput').attr('type', 'text').attr('size', 24).attr('value', this.value.toString()).css('width', '100px').get(0)
          ninput.addEventListener('change', (evt) => {
            // ask for a change, watch for float or for int ...
            if (isIntType(this.type)) {
              view.requestStateChange(this, parseInt(ninput.value))
            } else {
              view.requestStateChange(this, parseFloat(ninput.value))
            }
            // but assert that we don't change the definition unless
            ninput.value = this.value
          })
          this.de.append(ninput)
          this.set = (value) => {
            if (typeof value === 'number') {
              // quite sure js does this conversion no problem
              ninput.value = value
              this.value = value
            } else {
              throw new Error('bad type put into state dom')
            }
          }
          break // end numnber type
        case 'boolean':
          let span = $('<span style="float:right;">' + this.value.toString() + '</span>').get(0)
          $(this.de).addClass('stateBooleanItem')
          this.de.append(span)
          this.de.addEventListener('click', (evt) => {
            // read the current 'state' (as written) and send the opposite
            let txt = $(span).text()
            if (txt === 'true') {
              view.requestStateChange(this, false)
            } else {
              view.requestStateChange(this, true)
            }
          })
          this.set = (value) => {
            if (typeof value === 'boolean') {
              $(span).text(value.toString())
              this.value = value
            } else {
              throw new Error('bad type put into state dom')
            }
          }
          break // end boolean type
        default:
          console.error(`unaccounted for type at input pull for state change, ${typeof state.value}`)
          break
      }
    }
    
    export default HunkDefinition