src/outline/dragDrop.js
/**
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)