snapsvg
Version:
JavaScript Vector Library
477 lines (462 loc) • 16.2 kB
JavaScript
// Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Snap.plugin(function (Snap, Element, Paper, glob) {
var elproto = Element.prototype,
has = "hasOwnProperty",
supportsTouch = "createTouch" in glob.doc,
events = [
"click", "dblclick", "mousedown", "mousemove", "mouseout",
"mouseover", "mouseup", "touchstart", "touchmove", "touchend",
"touchcancel"
],
touchMap = {
mousedown: "touchstart",
mousemove: "touchmove",
mouseup: "touchend"
},
getScroll = function (xy, el) {
var name = xy == "y" ? "scrollTop" : "scrollLeft",
doc = el && el.node ? el.node.ownerDocument : glob.doc;
return doc[name in doc.documentElement ? "documentElement" : "body"][name];
},
preventDefault = function () {
this.returnValue = false;
},
preventTouch = function () {
return this.originalEvent.preventDefault();
},
stopPropagation = function () {
this.cancelBubble = true;
},
stopTouch = function () {
return this.originalEvent.stopPropagation();
},
addEvent = function (obj, type, fn, element) {
var realName = supportsTouch && touchMap[type] ? touchMap[type] : type,
f = function (e) {
var scrollY = getScroll("y", element),
scrollX = getScroll("x", element);
if (supportsTouch && touchMap[has](type)) {
for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
if (e.targetTouches[i].target == obj || obj.contains(e.targetTouches[i].target)) {
var olde = e;
e = e.targetTouches[i];
e.originalEvent = olde;
e.preventDefault = preventTouch;
e.stopPropagation = stopTouch;
break;
}
}
}
var x = e.clientX + scrollX,
y = e.clientY + scrollY;
return fn.call(element, e, x, y);
};
if (type !== realName) {
obj.addEventListener(type, f, false);
}
obj.addEventListener(realName, f, false);
return function () {
if (type !== realName) {
obj.removeEventListener(type, f, false);
}
obj.removeEventListener(realName, f, false);
return true;
};
},
drag = [],
dragMove = function (e) {
var x = e.clientX,
y = e.clientY,
scrollY = getScroll("y"),
scrollX = getScroll("x"),
dragi,
j = drag.length;
while (j--) {
dragi = drag[j];
if (supportsTouch) {
var i = e.touches && e.touches.length,
touch;
while (i--) {
touch = e.touches[i];
if (touch.identifier == dragi.el._drag.id || dragi.el.node.contains(touch.target)) {
x = touch.clientX;
y = touch.clientY;
(e.originalEvent ? e.originalEvent : e).preventDefault();
break;
}
}
} else {
e.preventDefault();
}
var node = dragi.el.node,
o,
next = node.nextSibling,
parent = node.parentNode,
display = node.style.display;
// glob.win.opera && parent.removeChild(node);
// node.style.display = "none";
// o = dragi.el.paper.getElementByPoint(x, y);
// node.style.display = display;
// glob.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
// o && eve("snap.drag.over." + dragi.el.id, dragi.el, o);
x += scrollX;
y += scrollY;
eve("snap.drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
}
},
dragUp = function (e) {
Snap.unmousemove(dragMove).unmouseup(dragUp);
var i = drag.length,
dragi;
while (i--) {
dragi = drag[i];
dragi.el._drag = {};
eve("snap.drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
eve.off("snap.drag.*." + dragi.el.id);
}
drag = [];
};
/*\
* Element.click
[ method ]
**
* Adds a click event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unclick
[ method ]
**
* Removes a click event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.dblclick
[ method ]
**
* Adds a double click event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.undblclick
[ method ]
**
* Removes a double click event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.mousedown
[ method ]
**
* Adds a mousedown event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unmousedown
[ method ]
**
* Removes a mousedown event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.mousemove
[ method ]
**
* Adds a mousemove event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unmousemove
[ method ]
**
* Removes a mousemove event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.mouseout
[ method ]
**
* Adds a mouseout event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unmouseout
[ method ]
**
* Removes a mouseout event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.mouseover
[ method ]
**
* Adds a mouseover event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unmouseover
[ method ]
**
* Removes a mouseover event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.mouseup
[ method ]
**
* Adds a mouseup event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.unmouseup
[ method ]
**
* Removes a mouseup event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.touchstart
[ method ]
**
* Adds a touchstart event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.untouchstart
[ method ]
**
* Removes a touchstart event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.touchmove
[ method ]
**
* Adds a touchmove event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.untouchmove
[ method ]
**
* Removes a touchmove event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.touchend
[ method ]
**
* Adds a touchend event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.untouchend
[ method ]
**
* Removes a touchend event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.touchcancel
[ method ]
**
* Adds a touchcancel event handler to the element
- handler (function) handler for the event
= (object) @Element
\*/
/*\
* Element.untouchcancel
[ method ]
**
* Removes a touchcancel event handler from the element
- handler (function) handler for the event
= (object) @Element
\*/
for (var i = events.length; i--;) {
(function (eventName) {
Snap[eventName] = elproto[eventName] = function (fn, scope) {
if (Snap.is(fn, "function")) {
this.events = this.events || [];
this.events.push({
name: eventName,
f: fn,
unbind: addEvent(this.node || document, eventName, fn, scope || this)
});
} else {
for (var i = 0, ii = this.events.length; i < ii; i++) if (this.events[i].name == eventName) {
try {
this.events[i].f.call(this);
} catch (e) {}
}
}
return this;
};
Snap["un" + eventName] =
elproto["un" + eventName] = function (fn) {
var events = this.events || [],
l = events.length;
while (l--) if (events[l].name == eventName &&
(events[l].f == fn || !fn)) {
events[l].unbind();
events.splice(l, 1);
!events.length && delete this.events;
return this;
}
return this;
};
})(events[i]);
}
/*\
* Element.hover
[ method ]
**
* Adds hover event handlers to the element
- f_in (function) handler for hover in
- f_out (function) handler for hover out
- icontext (object) #optional context for hover in handler
- ocontext (object) #optional context for hover out handler
= (object) @Element
\*/
elproto.hover = function (f_in, f_out, scope_in, scope_out) {
return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
};
/*\
* Element.unhover
[ method ]
**
* Removes hover event handlers from the element
- f_in (function) handler for hover in
- f_out (function) handler for hover out
= (object) @Element
\*/
elproto.unhover = function (f_in, f_out) {
return this.unmouseover(f_in).unmouseout(f_out);
};
var draggable = [];
// SIERRA unclear what _context_ refers to for starting, ending, moving the drag gesture.
// SIERRA Element.drag(): _x position of the mouse_: Where are the x/y values offset from?
// SIERRA Element.drag(): much of this member's doc appears to be duplicated for some reason.
// SIERRA Unclear about this sentence: _Additionally following drag events will be triggered: drag.start.<id> on start, drag.end.<id> on end and drag.move.<id> on every move._ Is there a global _drag_ object to which you can assign handlers keyed by an element's ID?
/*\
* Element.drag
[ method ]
**
* Adds event handlers for an element's drag gesture
**
- onmove (function) handler for moving
- onstart (function) handler for drag start
- onend (function) handler for drag end
- mcontext (object) #optional context for moving handler
- scontext (object) #optional context for drag start handler
- econtext (object) #optional context for drag end handler
* Additionaly following `drag` events are triggered: `drag.start.<id>` on start,
* `drag.end.<id>` on end and `drag.move.<id>` on every move. When element is dragged over another element
* `drag.over.<id>` fires as well.
*
* Start event and start handler are called in specified context or in context of the element with following parameters:
o x (number) x position of the mouse
o y (number) y position of the mouse
o event (object) DOM event object
* Move event and move handler are called in specified context or in context of the element with following parameters:
o dx (number) shift by x from the start point
o dy (number) shift by y from the start point
o x (number) x position of the mouse
o y (number) y position of the mouse
o event (object) DOM event object
* End event and end handler are called in specified context or in context of the element with following parameters:
o event (object) DOM event object
= (object) @Element
\*/
elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
var el = this;
if (!arguments.length) {
var origTransform;
return el.drag(function (dx, dy) {
this.attr({
transform: origTransform + (origTransform ? "T" : "t") + [dx, dy]
});
}, function () {
origTransform = this.transform().local;
});
}
function start(e, x, y) {
(e.originalEvent || e).preventDefault();
el._drag.x = x;
el._drag.y = y;
el._drag.id = e.identifier;
!drag.length && Snap.mousemove(dragMove).mouseup(dragUp);
drag.push({el: el, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
onstart && eve.on("snap.drag.start." + el.id, onstart);
onmove && eve.on("snap.drag.move." + el.id, onmove);
onend && eve.on("snap.drag.end." + el.id, onend);
eve("snap.drag.start." + el.id, start_scope || move_scope || el, x, y, e);
}
function init(e, x, y) {
eve("snap.draginit." + el.id, el, e, x, y);
}
eve.on("snap.draginit." + el.id, start);
el._drag = {};
draggable.push({el: el, start: start, init: init});
el.mousedown(init);
return el;
};
/*
* Element.onDragOver
[ method ]
**
* Shortcut to assign event handler for `drag.over.<id>` event, where `id` is the element's `id` (see @Element.id)
- f (function) handler for event, first argument would be the element you are dragging over
\*/
// elproto.onDragOver = function (f) {
// f ? eve.on("snap.drag.over." + this.id, f) : eve.unbind("snap.drag.over." + this.id);
// };
/*\
* Element.undrag
[ method ]
**
* Removes all drag event handlers from the given element
\*/
elproto.undrag = function () {
var i = draggable.length;
while (i--) if (draggable[i].el == this) {
this.unmousedown(draggable[i].init);
draggable.splice(i, 1);
eve.unbind("snap.drag.*." + this.id);
eve.unbind("snap.draginit." + this.id);
}
!draggable.length && Snap.unmousemove(dragMove).unmouseup(dragUp);
return this;
};
});