michielbdejong/solid-panes

View on GitHub
src/outline/dragDrop.js

Summary

Maintainability
C
1 day
Test Coverage
/**
    This is for outliner features that only work in the extension version, say drag and drop.
    I am always happy creating new files.
                                                             2007.07.11  kennyluck
    2017: Todo: Repace the old Firefox specific code with HTML5 standard code - timbl
**/
// Firefox internals:
/* global Components TransferData TransferDataSet FlavourSet nsTransferable transferUtils */

// #includedIn chrome://tabulator/tabulator.xul
// #require_once chrome://global/nsDragAndDrop.js

/* alert(gBrowser);alert(gBrowser.tagName)
if (!tabulator_gBrowser) {
    var tabulator_wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
               .getService(Components.interfaces.nsIWindowMediator)
    var tabulator_gBrowser = tabulator_wm.getMostRecentWindow("navigator:browser")
}
*/

/* 2007: adapted from dragAndDrop UI Library */

var UI = require('solid-ui')
const $rdf = require('rdflib')
var dragAndDrop = (module.exports = {})

dragAndDrop.util = {}
dragAndDrop.util.Event = (function () {
  var listeners = []
  return {
    on: function (el, sType, fn, obj, fnId /* ,override */) {
      var wrappedFn = function (e) {
        return fn.call(obj, e, obj)
      }
      el.addEventListener(sType, wrappedFn, false)
      var li = [el, sType, fnId, wrappedFn]
      listeners.push(li)
    },
    off: function (el, sType, fnId) {
      // removeListener, fnId to identify a function
      var index = this._getCacheIndex(el, sType, fnId)
      if (index === -1) return false
      var cacheItem = listeners[index]
      el.removeEventListener(sType, cacheItem[this.WFN], false)

      delete listeners[index][this.WFN]
      delete listeners[index][this.FN]
      listeners.splice(index, 1)
      return true
    },
    EL: 0,
    TYPE: 1,
    FNID: 2,
    WFN: 3,
    _getCacheIndex: function (el, sType, fnId) {
      for (var i = 0, len = listeners.length; i < len; ++i) {
        var li = listeners[i]
        if (
          li &&
          li[this.FNID] === fnId &&
          li[this.EL] === el &&
          li[this.TYPE] === sType
        ) {
          return i
        }
      }

      return -1
    }
  }
})()
dragAndDrop.util.DDExternalProxy = function DDExternalProxy (el) {
  this.initTarget(el)
  // dragAndDrop.util.Event.on(this.el, "mousedown", this.handleMouseDown, this, 'dragMouseDown'/*, true*/)
}
// dragAndDrop.util.DDExternalProxy extends dragAndDrop.utilDDProxy
dragAndDrop.util.DDExternalProxy.prototype = {
  initTarget: function (el) {
    // create a local reference to the drag and drop manager
    this.DDM = dragAndDrop.util.DDM

    // set the el
    this.el = el

    /*
    // We don't want to register this as the handle with the manager
    // so we just set the id rather than calling the setter.
    this.handleElId = id

    Event.onAvailable(id, this.handleOnAvailable, this, true)
    */

    // the linked element is the element that gets dragged by default
    // this.setDragElId(id)

    // by default, clicked anchors will not start drag operations.
    // @TODO what else should be here?  Probably form fields.
    // this.invalidHandleTypes = { A: "A" }
    // this.invalidHandleIds = {}
    // this.invalidHandleClasses = []

    // this.applyConfig()
  },
  b4StartDrag: function (x, y) {
    // show the drag frame
    // this.logger.log("start drag show frame, x: " + x + ", y: " + y)
    // alert("test startDrag")
    TabulatorOutlinerObserver.onDragStart(x, y, this.el)
    // this.showFrame(x, y)
  },
  b4Drag: function (_e) {
    // this.setDragElPos(dragAndDrop.util.Event.getPageX(e),
    //                    dragAndDrop.util.Event.getPageY(e))
  },
  handleMouseDown: function (e, _oDD) {
    var button = e.which || e.button
    if (button > 1) return

    // firing the mousedown events prior to calculating positions
    // this.b4MouseDown(e)
    // this.onMouseDown(e)

    // this.DDM.refreshCache(this.groups)
    // var self = this
    // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0)

    // Only process the event if we really clicked within the linked
    // element.  The reason we make this check is that in the case that
    // another element was moved between the clicked element and the
    // cursor in the time between the mousedown and mouseup events. When
    // this happens, the element gets the next mousedown event
    // regardless of where on the screen it happened.
    // var pt = new dragAndDrop.util.Point(Event.getPageX(e), Event.getPageY(e))
    // if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
    //        this.logger.log("Click was not over the element: " + this.id)
    // } else {
    //    if (this.clickValidator(e)) {

    // set the initial element position
    // this.setStartPosition()

    // start tracking mousemove distance and mousedown time to
    // determine when to start the actual drag
    this.DDM.handleMouseDown(e, this)

    // this mousedown is mine
    // this.DDM.stopEvent(e)
    //    } else {

    // this.logger.log("clickValidator returned false, drag not initiated")

    //    }
    // }
  }
}

dragAndDrop.util.DDM = (function DDM () {
  return {
    handleMouseDown: function (e, oDD) {
      // this.currentTarget = dragAndDrop.util.Event.getTarget(e)

      this.dragCurrent = oDD

      var el = oDD.el

      // track start position
      this.startX = e.pageX
      this.startY = e.pageY

      this.deltaX = this.startX - el.offsetLeft
      this.deltaY = this.startY - el.offsetTop

      this.dragThreshMet = false

      // this.clickTimeout = setTimeout(
      //        function() {
      //            var DDM = dragAndDrop.util.DDM
      //            DDM.startDrag(DDM.startX, DDM.startY)
      //        },
      //        this.clickTimeThresh )
      // dragAndDrop.util.Event.on(el,'mousemove',this.handleMouseMove,this,'dragMouseMove')
      // dragAndDrop.util.Event.on(el,'mouseup'  ,this.handleMouseUp  ,this,'dragMouseUp')
    },

    handleMouseMove: function (e) {
      // dragAndDrop.log("handlemousemove")
      if (!this.dragCurrent) {
        // dragAndDrop.log("no current drag obj")
        return true
      }
      // var button = e.which || e.button
      // dragAndDrop.log("which: " + e.which + ", button: "+ e.button)

      // check for IE mouseup outside of page boundary
      if (dragAndDrop.util.Event.isIE && !e.button) {
        dragAndDrop.log('button failure', 'info', 'DragDropMgr')
        this.stopEvent(e)
        return this.handleMouseUp(e)
      }

      if (!this.dragThreshMet) {
        var diffX = Math.abs(this.startX - e.pageX)
        var diffY = Math.abs(this.startY - e.pageY)
        // dragAndDrop.log("diffX: " + diffX + "diffY: " + diffY)
        if (diffX > this.clickPixelThresh || diffY > this.clickPixelThresh) {
          // dragAndDrop.log("pixel threshold met", "info", "DragDropMgr")
          this.startDrag(this.startX, this.startY)
        }
      }

      if (this.dragThreshMet) {
        // this.dragCurrent.b4Drag(e)
        // this.dragCurrent.onDrag(e)
        // this.fireEvents(e, false)
      }

      e.preventDefault()
      // this.stopEvent(e)

      return true
    },
    handleMouseUp: function (e) {
      if (!this.dragCurrent) return // Error...
      dragAndDrop.util.Event.off(
        this.dragCurrent.el,
        'mousemove',
        'dragMouseMove'
      )
      // there are two mouseup for unknown reason...
      dragAndDrop.util.Event.off(this.dragCurrent.el, 'mouseup', 'dragMouseUp')
      dragAndDrop.util.Event.off(this.dragCurrent.el, 'mouseup', 'dragMouseUp')
      // have to do this as attribute ondragdrop does not recognize any dragdrop event
      // initialized inside <tabbrowser> (strange, I think)
      if (this.dragThreshMet) {
        TabulatorOutlinerObserver.onDropInside(e.target)
        this.dragThreshMet = false
      }
      this.dragCurrent = null
    },
    startDrag: function (x, y) {
      // dragAndDrop.log("firing drag start events", "info", "DragDropMgr")
      // clearTimeout(this.clickTimeout)
      if (this.dragCurrent) {
        this.dragCurrent.b4StartDrag(x, y)
        // this.dragCurrent.startDrag(x, y)
      }
      this.dragThreshMet = true
    },
    clickPixelThresh: 3
  }
})()

// ToDos
// 1.Recover normal funtionality
// 2.Investigate into Gecko drag and drop
// 3.Cross Tag Drag And Drop
// 4.Firefox native rdf store
var TabulatorOutlinerObserver = {
  onDrop: function (e, aXferData, _dragSession) {
    var selection = UI.utils.ancestor(
      UI.utils.ancestor(e.originalTarget, 'TABLE').parentNode,
      'TABLE'
    ).outline.selection
    var contentType = aXferData.flavour.contentType
    var url = transferUtils.retrieveURLFromData(aXferData.data, contentType)
    if (!url) return
    if (contentType === 'application/x-moz-file') {
      if (aXferData.data.fileSize === 0) {
        var templateDoc = $rdf.sym(
          'chrome://tabulator/content/internalKnowledge.n3#defaultNew'
        )
        UI.store.copyTo(templateDoc, $rdf.sym(url))
        /*
      function WriteToFileRepresentedBy (subject){
          var outputFormulaTerm=kb.any(subject,OWL('unionOf'))
          var theClass =  kb.constructor.SuperClass
          var outputFormula= theClass.instances[kb.the(outputFormulaTerm,tabont('accesskey')).value]
      }
      */
      }
    }
    var targetTd = selection[0]
    var table = UI.utils.ancestor(
      UI.utils.ancestor(targetTd, 'TABLE').parentNode,
      'TABLE'
    )
    var thisOutline = table.outline
    thisOutline.UserInput.insertTermTo(targetTd, $rdf.sym(url))
  },

  onDragEnter: function (e, _dragSession) {
    // enter or exit something
    try {
      var selection = UI.utils.ancestor(
        UI.utils.ancestor(e.originalTarget, 'TABLE').parentNode,
        'TABLE'
      ).outline.selection
    } catch (e) {
      /* because e.orginalTarget is not defined */ return
    }
    for (
      var targetTd = e.originalTarget;
      targetTd;
      targetTd = targetTd.parentNode
    ) {
      if (targetTd.tabulatorSelect) {
        if (selection[0]) {
          try {
            selection[0].tabulatorDeselect()
          } catch (e) {
            throw new Error(selection[0] + ' causes ' + e)
          }
          dragAndDrop.util.Event.off(targetTd, 'mouseup', 'dragMouseUp')
        }
        targetTd.tabulatorSelect()
        // dragAndDrop.util.Event.on(targetTd,'mouseup',this.DDM.handleMouseUp,this.DDM,'dragMouseUp')
        break
      }
    }
  },

  onDragExit: function (_e, _dragSession) {
    // if (e.originalTarget.tabulatorDeselect) e.originalTarget.tabulatorDeselect()
  },
  onDropInside: function (targetTd) {
    // a special case that you draganddrop totally inside a <tabbrowser>
    // var selection = ancestor(ancestor(targetTd,'TABLE').parentNode,'TABLE').outline.selection
    // var targetTd=selection[0]
    var table = targetTd.ownerDocument.getElementById('outline')
    // var table=ancestor(ancestor(targetTd,'TABLE').parentNode,'TABLE')
    var thisOutline = table.outline
    thisOutline.UserInput.insertTermTo(
      targetTd,
      UI.utils.getAbout(UI.store, this.dragTarget)
    )
  },
  onDragStart: function (x, y, td) {
    /* seeAlso nsDragAndDrop.js::nsDragAndDrop.startDrag */
    // ToDo for myself: understand the connections in firefox, x, screenX

    this.dragTarget = td
    var kDSIID = Components.interfaces.nsIDragService
    var dragAction = {
      action:
        kDSIID.DRAGDROP_ACTION_COPY +
        kDSIID.DRAGDROP_ACTION_MOVE +
        kDSIID.DRAGDROP_ACTION_LINK
    }

    // alert(td.ownerDocument.getBoxObjectFor(td))
    // alert(td.ownerDocument.getBoxObjectFor(td).screenX)
    var tdBox = td.ownerDocument.getBoxObjectFor(td) // nsIBoxObject
    var region = Components.classes['@mozilla.org/gfx/region;1'].createInstance(
      Components.interfaces.nsIScriptableRegion
    )
    region.init() // this is important
    region.unionRect(tdBox.screenX, tdBox.screenY, tdBox.width, tdBox.height)
    var transferDataSet = { data: null }
    var term = UI.Util.getTerm(td)
    switch (term.termType) {
      case 'NamedNode':
        transferDataSet.data = this.URItoTransferDataSet(term.uri)
        break
      case 'BlankNode':
        transferDataSet.data = this.URItoTransferDataSet(term.toNT())
        break
      case 'Literal':
        transferDataSet.data = this.URItoTransferDataSet(term.value)
        break
    }

    transferDataSet = transferDataSet.data // quite confusing, anyway...
    var transArray = Components.classes[
      '@mozilla.org/supports-array;1'
    ].createInstance(Components.interfaces.nsISupportsArray)
    var trans = nsTransferable.set(transferDataSet.dataList[0])
    transArray.AppendElement(
      trans.QueryInterface(Components.interfaces.nsISupports)
    )
    this.mDragService.invokeDragSession(
      td,
      transArray,
      region,
      dragAction.action
    )
  },
  /*
  onDragStart: function(aEvent,aXferData,aDragAction){
      var dragTarget=ancestor(aEvent.target,'TD')
      //var nt=dragTarget.getAttribute('about')
      //ToDo:text terms
      var term=getAbout(kb,dragTarget)
      aXferData.data = this.URItoTransferDataSet(term.uri)
      alert("start")
  },
  */

  getSupportedFlavours: function () {
    var flavourSet = new FlavourSet()
    // flavourSet.appendFlavour("text/rdfitem")
    // flavourSet.appendFlavour("moz/rdfitem")
    flavourSet.appendFlavour('text/x-moz-url')
    flavourSet.appendFlavour('text/unicode')
    flavourSet.appendFlavour('application/x-moz-file', 'nsIFile')
    return flavourSet
  },

  URItoTransferDataSet: function (uri) {
    var dataSet = new TransferDataSet()
    var data = new TransferData()
    data.addDataForFlavour('text/x-moz-url', uri)
    data.addDataForFlavour('text/unicode', uri)
    dataSet.push(data)
    return dataSet
  },
  _mDS: null,
  get_mDragService: function () {
    // some syntax I don't understand -- was get mDragService()
    if (!this._mDS) {
      var kDSContractID = '@mozilla.org/widget/dragservice;1'
      var kDSIID = Components.interfaces.nsIDragService
      this._mDS = Components.classes[kDSContractID].getService(kDSIID)
    }
    return this._mDS
  },
  DDM: dragAndDrop.util.DDM
}
/*
var ondraging=false; //global variable indicating whether ondraging (better choice?)
var testGlobal = function test(e){alert(e.originalTarget);e.originalTarget.className='selected';e.preventDefault();}
var activateDrag = function (e){
   if (!ondraging){
       alert('activate test')
       ondraging=true
   }
}
*/
// tabulator_gBrowser.setAttribute('ondragdrop','testGlobal(event)')
// tabulator_gBrowser.setAttribute('ondragenter','activateDrag(event)')

// gBrowser.addEventListener('dragdrop',test,true)