hal/hal-core/resources/web/js/lib/svg.draggable.js

221 lines
No EOL
6.1 KiB
JavaScript
Vendored

/*! svg.draggable.js - v2.2.0 - 2016-05-21
* https://github.com/wout/svg.draggable.js
* Copyright (c) 2016 Wout Fierens; Licensed MIT */
;(function() {
// creates handler, saves it
function DragHandler(el){
el.remember('_draggable', this)
this.el = el
}
// Sets new parameter, starts dragging
DragHandler.prototype.init = function(constraint, val){
var _this = this
this.constraint = constraint
this.value = val
this.el.on('mousedown.drag', function(e){ _this.start(e) })
this.el.on('touchstart.drag', function(e){ _this.start(e) })
}
// transforms one point from screen to user coords
DragHandler.prototype.transformPoint = function(event, offset){
event = event || window.event
var touches = event.changedTouches && event.changedTouches[0] || event
this.p.x = touches.pageX - (offset || 0)
this.p.y = touches.pageY
return this.p.matrixTransform(this.m)
}
// gets elements bounding box with special handling of groups, nested and use
DragHandler.prototype.getBBox = function(){
var box = this.el.bbox()
if(this.el instanceof SVG.Nested) box = this.el.rbox()
if (this.el instanceof SVG.G || this.el instanceof SVG.Use || this.el instanceof SVG.Nested) {
box.x = this.el.x()
box.y = this.el.y()
}
return box
}
// start dragging
DragHandler.prototype.start = function(e){
// check for left button
if(e.type == 'click'|| e.type == 'mousedown' || e.type == 'mousemove'){
if((e.which || e.buttons) != 1){
return
}
}
var _this = this
// fire beforedrag event
this.el.fire('beforedrag', { event: e, handler: this })
// search for parent on the fly to make sure we can call
// draggable() even when element is not in the dom currently
this.parent = this.parent || this.el.parent(SVG.Nested) || this.el.parent(SVG.Doc)
this.p = this.parent.node.createSVGPoint()
// save current transformation matrix
this.m = this.el.node.getScreenCTM().inverse()
var box = this.getBBox()
var anchorOffset;
// fix text-anchor in text-element (#37)
if(this.el instanceof SVG.Text){
anchorOffset = this.el.node.getComputedTextLength();
switch(this.el.attr('text-anchor')){
case 'middle':
anchorOffset /= 2;
break
case 'start':
anchorOffset = 0;
break;
}
}
this.startPoints = {
// We take absolute coordinates since we are just using a delta here
point: this.transformPoint(e, anchorOffset),
box: box
}
// add drag and end events to window
SVG.on(window, 'mousemove.drag', function(e){ _this.drag(e) })
SVG.on(window, 'touchmove.drag', function(e){ _this.drag(e) })
SVG.on(window, 'mouseup.drag', function(e){ _this.end(e) })
SVG.on(window, 'touchend.drag', function(e){ _this.end(e) })
// fire dragstart event
this.el.fire('dragstart', {event: e, p: this.startPoints.point, m: this.m, handler: this})
// prevent browser drag behavior
e.preventDefault()
// prevent propagation to a parent that might also have dragging enabled
e.stopPropagation();
}
// while dragging
DragHandler.prototype.drag = function(e){
var box = this.getBBox()
, p = this.transformPoint(e)
, x = this.startPoints.box.x + p.x - this.startPoints.point.x
, y = this.startPoints.box.y + p.y - this.startPoints.point.y
, c = this.constraint
var event = new CustomEvent('dragmove', {
detail: {
event: e
, p: p
, m: this.m
, handler: this
}
, cancelable: true
})
this.el.fire(event)
if(event.defaultPrevented) return p
// move the element to its new position, if possible by constraint
if (typeof c == 'function') {
var coord = c.call(this.el, x, y, this.m)
// bool, just show us if movement is allowed or not
if (typeof coord == 'boolean') {
coord = {
x: coord,
y: coord
}
}
// if true, we just move. If !false its a number and we move it there
if (coord.x === true) {
this.el.x(x)
} else if (coord.x !== false) {
this.el.x(coord.x)
}
if (coord.y === true) {
this.el.y(y)
} else if (coord.y !== false) {
this.el.y(coord.y)
}
} else if (typeof c == 'object') {
// keep element within constrained box
if (c.minX != null && x < c.minX)
x = c.minX
else if (c.maxX != null && x > c.maxX - box.width){
x = c.maxX - box.width
}if (c.minY != null && y < c.minY)
y = c.minY
else if (c.maxY != null && y > c.maxY - box.height)
y = c.maxY - box.height
this.el.move(x, y)
}
// so we can use it in the end-method, too
return p
}
DragHandler.prototype.end = function(e){
// final drag
var p = this.drag(e);
// fire dragend event
this.el.fire('dragend', { event: e, p: p, m: this.m, handler: this })
// unbind events
SVG.off(window, 'mousemove.drag')
SVG.off(window, 'touchmove.drag')
SVG.off(window, 'mouseup.drag')
SVG.off(window, 'touchend.drag')
}
SVG.extend(SVG.Element, {
// Make element draggable
// Constraint might be an object (as described in readme.md) or a function in the form "function (x, y)" that gets called before every move.
// The function can return a boolean or an object of the form {x, y}, to which the element will be moved. "False" skips moving, true moves to raw x, y.
draggable: function(value, constraint) {
// Check the parameters and reassign if needed
if (typeof value == 'function' || typeof value == 'object') {
constraint = value
value = true
}
var dragHandler = this.remember('_draggable') || new DragHandler(this)
// When no parameter is given, value is true
value = typeof value === 'undefined' ? true : value
if(value) dragHandler.init(constraint || {}, value)
else {
this.off('mousedown.drag')
this.off('touchstart.drag')
}
return this
}
})
}).call(this);