822 lines
27 KiB
JavaScript
822 lines
27 KiB
JavaScript
/*
|
|
* Jquery plugin for unified mouse and touch events
|
|
*
|
|
* Copyright (c) 2013 Michael S. Mikowski
|
|
* (mike[dot]mikowski[at]gmail[dotcom])
|
|
*
|
|
* Dual licensed under the MIT or GPL Version 2
|
|
* http://jquery.org/license
|
|
*
|
|
* Versions
|
|
* 0.3.0 - Initial jQuery plugin site release
|
|
* - Replaced scrollwheel zoom with drag motion.
|
|
* This resolved a conflict with scrollable areas.
|
|
* 0.3.1 - Change for jQuery plugins site
|
|
* 0.3.2 - Updated to jQuery 1.9.1.
|
|
* Confirmed 1.7.0-1.9.1 compatibility.
|
|
* 0.4.2 - Updated documentation
|
|
* 0.4.3 - Removed fatal execption possibility if originalEvent
|
|
* is not defined on event object
|
|
*
|
|
*/
|
|
|
|
/*jslint browser : true, continue : true,
|
|
devel : true, indent : 2, maxerr : 50,
|
|
newcap : true, plusplus : true, regexp : true,
|
|
sloppy : true, vars : true, white : true
|
|
*/
|
|
/*global jQuery, sl */
|
|
|
|
(function ($) {
|
|
//---------------- BEGIN MODULE SCOPE VARIABLES --------------
|
|
var
|
|
$Special = $.event.special, // shortcut for special event
|
|
motionMapMap = {}, // map of pointer motions by cursor
|
|
isMoveBound = false, // flag if move handlers bound
|
|
pxPinchZoom = -1, // distance between pinch-zoom points
|
|
optionKey = 'ue_bound', // data key for storing options
|
|
doDisableMouse = false, // flag to discard mouse input
|
|
defaultOptMap = { // Default option hash
|
|
bound_ns_map: {}, // namspace hash e.g. bound_ns_map.utap.fred
|
|
wheel_ratio: 15, // multiplier for mousewheel delta
|
|
px_radius: 3, // 'distance' dragged before dragstart
|
|
ignore_class: ':input', // 'not' suppress matching elements
|
|
tap_time: 200, // millisecond max time to consider tap
|
|
held_tap_time: 300 // millisecond min time to consider taphold
|
|
},
|
|
callbackList = [], // global callback stack
|
|
zoomMouseNum = 1, // multiplier for mouse zoom
|
|
zoomTouchNum = 4, // multiplier for touch zoom
|
|
|
|
boundList, Ue,
|
|
motionDragId, motionHeldId, motionDzoomId,
|
|
motion1ZoomId, motion2ZoomId,
|
|
|
|
checkMatchVal, removeListVal, pushUniqVal, makeListPlus,
|
|
fnHeld, fnMotionStart, fnMotionMove,
|
|
fnMotionEnd, onMouse, onTouch,
|
|
onMousewheel
|
|
;
|
|
//----------------- END MODULE SCOPE VARIABLES ---------------
|
|
|
|
//------------------- BEGIN UTILITY METHODS ------------------
|
|
// Begin utiltity /makeListPlus/
|
|
// Returns an array with much desired methods:
|
|
// * remove_val(value) : remove element that matches
|
|
// the provided value. Returns number of elements
|
|
// removed.
|
|
// * match_val(value) : shows if a value exists
|
|
// * push_uniq(value) : pushes a value onto the stack
|
|
// iff it does not already exist there
|
|
// Note: the reason I need this is to compare objects to
|
|
// objects (perhaps jQuery has something similar?)
|
|
checkMatchVal = function (data) {
|
|
var match_count = 0, idx;
|
|
for (idx = this.length; idx; 0) {
|
|
if (this[--idx] === data) {
|
|
match_count++;
|
|
}
|
|
}
|
|
return match_count;
|
|
};
|
|
removeListVal = function (data) {
|
|
var removed_count = 0, idx;
|
|
for (idx = this.length; idx; 0) {
|
|
if (this[--idx] === data) {
|
|
this.splice(idx, 1);
|
|
removed_count++;
|
|
idx++;
|
|
}
|
|
}
|
|
return removed_count;
|
|
};
|
|
pushUniqVal = function (data) {
|
|
if (checkMatchVal.call(this, data)) {
|
|
return false;
|
|
}
|
|
this.push(data);
|
|
return true;
|
|
};
|
|
// primary utility
|
|
makeListPlus = function (input_list) {
|
|
if (input_list && $.isArray(input_list)) {
|
|
if (input_list.remove_val) {
|
|
console.warn('The array appears to already have listPlus capabilities');
|
|
return input_list;
|
|
}
|
|
}
|
|
else {
|
|
input_list = [];
|
|
}
|
|
input_list.remove_val = removeListVal;
|
|
input_list.match_val = checkMatchVal;
|
|
input_list.push_uniq = pushUniqVal;
|
|
|
|
return input_list;
|
|
};
|
|
// End utility /makeListPlus/
|
|
//-------------------- END UTILITY METHODS -------------------
|
|
|
|
//--------------- BEGIN JQUERY SPECIAL EVENTS ----------------
|
|
// Unique array for bound objects
|
|
boundList = makeListPlus();
|
|
|
|
// Begin define special event handlers
|
|
Ue = {
|
|
setup: function (data, a_names, fn_bind) {
|
|
var
|
|
this_el = this,
|
|
$to_bind = $(this_el),
|
|
seen_map = {},
|
|
option_map, idx, namespace_key, ue_namespace_code, namespace_list
|
|
;
|
|
|
|
// if previous related event bound do not rebind, but do add to
|
|
// type of event bound to this element, if not already noted
|
|
if ($.data(this, optionKey)) {
|
|
return;
|
|
}
|
|
|
|
option_map = {};
|
|
$.extend(true, option_map, defaultOptMap);
|
|
$.data(this_el, optionKey, option_map);
|
|
|
|
namespace_list = makeListPlus(a_names.slice(0));
|
|
if (!namespace_list.length
|
|
|| namespace_list[0] === ""
|
|
) {
|
|
namespace_list = ["000"];
|
|
}
|
|
|
|
NSPACE_00:
|
|
for (idx = 0; idx < namespace_list.length; idx++) {
|
|
namespace_key = namespace_list[idx];
|
|
|
|
if (!namespace_key) {
|
|
continue NSPACE_00;
|
|
}
|
|
if (seen_map.hasOwnProperty(namespace_key)) {
|
|
continue NSPACE_00;
|
|
}
|
|
|
|
seen_map[namespace_key] = true;
|
|
|
|
ue_namespace_code = '.__ue' + namespace_key;
|
|
|
|
$to_bind.bind('mousedown' + ue_namespace_code, onMouse);
|
|
$to_bind.bind('touchstart' + ue_namespace_code, onTouch);
|
|
$to_bind.bind('mousewheel' + ue_namespace_code, onMousewheel);
|
|
}
|
|
|
|
boundList.push_uniq(this_el); // record as bound element
|
|
|
|
if (!isMoveBound) {
|
|
// console.log('first element bound - adding global binds');
|
|
$(document).bind('mousemove.__ue', onMouse);
|
|
$(document).bind('touchmove.__ue', onTouch);
|
|
$(document).bind('mouseup.__ue', onMouse);
|
|
$(document).bind('touchend.__ue', onTouch);
|
|
$(document).bind('touchcancel.__ue', onTouch);
|
|
isMoveBound = true;
|
|
}
|
|
},
|
|
|
|
// arg_map.type = string - name of event to bind
|
|
// arg_map.data = poly - whatever (optional) data was passed when binding
|
|
// arg_map.namespace = string - A sorted, dot-delimited list of namespaces
|
|
// specified when binding the event
|
|
// arg_map.handler = fn - the event handler the developer wishes to be bound
|
|
// to the event. This function should be called whenever the event
|
|
// is triggered
|
|
// arg_map.guid = number - unique ID for event handler, provided by jQuery
|
|
// arg_map.selector = string - selector used by 'delegate' or 'live' jQuery
|
|
// methods. Only available when these methods are used.
|
|
//
|
|
// this - the element to which the event handler is being bound
|
|
// this always executes immediate after setup (if first binding)
|
|
add: function (arg_map) {
|
|
var
|
|
this_el = this,
|
|
option_map = $.data(this_el, optionKey),
|
|
namespace_str = arg_map.namespace,
|
|
event_type = arg_map.type,
|
|
bound_ns_map, namespace_list, idx, namespace_key
|
|
;
|
|
if (!option_map) {
|
|
return;
|
|
}
|
|
|
|
bound_ns_map = option_map.bound_ns_map;
|
|
|
|
if (!bound_ns_map[event_type]) {
|
|
// this indicates a non-namespaced entry
|
|
bound_ns_map[event_type] = {};
|
|
}
|
|
|
|
if (!namespace_str) {
|
|
return;
|
|
}
|
|
|
|
namespace_list = namespace_str.split('.');
|
|
|
|
for (idx = 0; idx < namespace_list.length; idx++) {
|
|
namespace_key = namespace_list[idx];
|
|
bound_ns_map[event_type][namespace_key] = true;
|
|
}
|
|
},
|
|
|
|
remove: function (arg_map) {
|
|
var
|
|
elem_bound = this,
|
|
option_map = $.data(elem_bound, optionKey),
|
|
bound_ns_map = option_map.bound_ns_map,
|
|
event_type = arg_map.type,
|
|
namespace_str = arg_map.namespace,
|
|
namespace_list, idx, namespace_key
|
|
;
|
|
|
|
if (!bound_ns_map[event_type]) {
|
|
return;
|
|
}
|
|
|
|
// No namespace(s) provided:
|
|
// Remove complete record for custom event type (e.g. utap)
|
|
if (!namespace_str) {
|
|
delete bound_ns_map[event_type];
|
|
return;
|
|
}
|
|
|
|
// Namespace(s) provided:
|
|
// Remove namespace flags from each custom event typei (e.g. utap)
|
|
// record. If all claimed namespaces are removed, remove
|
|
// complete record.
|
|
namespace_list = namespace_str.split('.');
|
|
|
|
for (idx = 0; idx < namespace_list.length; idx++) {
|
|
namespace_key = namespace_list[idx];
|
|
if (bound_ns_map[event_type][namespace_key]) {
|
|
delete bound_ns_map[event_type][namespace_key];
|
|
}
|
|
}
|
|
|
|
if ($.isEmptyObject(bound_ns_map[event_type])) {
|
|
delete bound_ns_map[event_type];
|
|
}
|
|
},
|
|
|
|
teardown: function (a_names) {
|
|
var
|
|
elem_bound = this,
|
|
$bound = $(elem_bound),
|
|
option_map = $.data(elem_bound, optionKey),
|
|
bound_ns_map = option_map.bound_ns_map,
|
|
idx, namespace_key, ue_namespace_code, namespace_list
|
|
;
|
|
|
|
// do not tear down if related handlers are still bound
|
|
if (!$.isEmptyObject(bound_ns_map)) {
|
|
return;
|
|
}
|
|
|
|
namespace_list = makeListPlus(a_names);
|
|
namespace_list.push_uniq('000');
|
|
|
|
NSPACE_01:
|
|
for (idx = 0; idx < namespace_list.length; idx++) {
|
|
namespace_key = namespace_list[idx];
|
|
|
|
if (!namespace_key) {
|
|
continue NSPACE_01;
|
|
}
|
|
|
|
ue_namespace_code = '.__ue' + namespace_key;
|
|
$bound.unbind('mousedown' + ue_namespace_code);
|
|
$bound.unbind('touchstart' + ue_namespace_code);
|
|
$bound.unbind('mousewheel' + ue_namespace_code);
|
|
}
|
|
|
|
$.removeData(elem_bound, optionKey);
|
|
|
|
// Unbind document events only after last element element is removed
|
|
boundList.remove_val(this);
|
|
if (boundList.length === 0) {
|
|
// console.log('last bound element removed - removing global binds');
|
|
$(document).unbind('mousemove.__ue');
|
|
$(document).unbind('touchmove.__ue');
|
|
$(document).unbind('mouseup.__ue');
|
|
$(document).unbind('touchend.__ue');
|
|
$(document).unbind('touchcancel.__ue');
|
|
isMoveBound = false;
|
|
}
|
|
}
|
|
};
|
|
// End define special event handlers
|
|
//--------------- BEGIN JQUERY SPECIAL EVENTS ----------------
|
|
|
|
//------------------ BEGIN MOTION CONTROLS -------------------
|
|
// Begin motion control /fnHeld/
|
|
fnHeld = function (arg_map) {
|
|
var
|
|
timestamp = +new Date(),
|
|
motion_id = arg_map.motion_id,
|
|
motion_map = arg_map.motion_map,
|
|
bound_ns_map = arg_map.bound_ns_map,
|
|
event_ue
|
|
;
|
|
|
|
delete motion_map.idto_tapheld;
|
|
|
|
if (!motion_map.do_allow_tap) {
|
|
return;
|
|
}
|
|
|
|
motion_map.px_end_x = motion_map.px_start_x;
|
|
motion_map.px_end_y = motion_map.px_start_y;
|
|
motion_map.ms_timestop = timestamp;
|
|
motion_map.ms_elapsed = timestamp - motion_map.ms_timestart;
|
|
|
|
if (bound_ns_map.uheld) {
|
|
event_ue = $.Event('uheld');
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
}
|
|
|
|
// remove tracking, as we want no futher action on this motion
|
|
if (bound_ns_map.uheldstart) {
|
|
event_ue = $.Event('uheldstart');
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
motionHeldId = motion_id;
|
|
}
|
|
else {
|
|
delete motionMapMap[motion_id];
|
|
}
|
|
};
|
|
// End motion control /fnHeld/
|
|
|
|
|
|
// Begin motion control /fnMotionStart/
|
|
fnMotionStart = function (arg_map) {
|
|
var
|
|
motion_id = arg_map.motion_id,
|
|
event_src = arg_map.event_src,
|
|
request_dzoom = arg_map.request_dzoom,
|
|
|
|
option_map = $.data(arg_map.elem, optionKey),
|
|
bound_ns_map = option_map.bound_ns_map,
|
|
$target = $(event_src.target),
|
|
do_zoomstart = false,
|
|
motion_map, cb_map, do_allow_tap, event_ue
|
|
;
|
|
|
|
// this should never happen, but it does
|
|
if (motionMapMap[motion_id]) {
|
|
return;
|
|
}
|
|
|
|
if (request_dzoom && !bound_ns_map.uzoomstart) {
|
|
return;
|
|
}
|
|
|
|
// :input selector includes text areas
|
|
if ($target.is(option_map.ignore_class)) {
|
|
return;
|
|
}
|
|
|
|
do_allow_tap = bound_ns_map.utap
|
|
|| bound_ns_map.uheld || bound_ns_map.uheldstart
|
|
? true : false;
|
|
|
|
cb_map = callbackList.pop();
|
|
|
|
while (cb_map) {
|
|
if ($target.is(cb_map.selector_str)
|
|
|| $(arg_map.elem).is(cb_map.selector_str)
|
|
) {
|
|
if (cb_map.callback_match) {
|
|
cb_map.callback_match(arg_map);
|
|
}
|
|
}
|
|
else {
|
|
if (cb_map.callback_nomatch) {
|
|
cb_map.callback_nomatch(arg_map);
|
|
}
|
|
}
|
|
cb_map = callbackList.pop();
|
|
}
|
|
|
|
motion_map = {
|
|
do_allow_tap: do_allow_tap,
|
|
elem_bound: arg_map.elem,
|
|
elem_target: event_src.target,
|
|
ms_elapsed: 0,
|
|
ms_timestart: event_src.timeStamp,
|
|
ms_timestop: undefined,
|
|
option_map: option_map,
|
|
orig_target: event_src.target,
|
|
px_current_x: event_src.clientX,
|
|
px_current_y: event_src.clientY,
|
|
px_end_x: undefined,
|
|
px_end_y: undefined,
|
|
px_start_x: event_src.clientX,
|
|
px_start_y: event_src.clientY,
|
|
timeStamp: event_src.timeStamp
|
|
};
|
|
|
|
motionMapMap[motion_id] = motion_map;
|
|
|
|
if (bound_ns_map.uzoomstart) {
|
|
if (request_dzoom) {
|
|
motionDzoomId = motion_id;
|
|
}
|
|
else if (!motion1ZoomId) {
|
|
motion1ZoomId = motion_id;
|
|
}
|
|
else if (!motion2ZoomId) {
|
|
motion2ZoomId = motion_id;
|
|
event_ue = $.Event('uzoomstart');
|
|
do_zoomstart = true;
|
|
}
|
|
|
|
if (do_zoomstart) {
|
|
event_ue = $.Event('uzoomstart');
|
|
motion_map.px_delta_zoom = 0;
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (bound_ns_map.uheld || bound_ns_map.uheldstart) {
|
|
motion_map.idto_tapheld = setTimeout(
|
|
function () {
|
|
fnHeld({
|
|
motion_id: motion_id,
|
|
motion_map: motion_map,
|
|
bound_ns_map: bound_ns_map
|
|
});
|
|
},
|
|
option_map.held_tap_time
|
|
);
|
|
}
|
|
};
|
|
// End motion control /fnMotionStart/
|
|
|
|
// Begin motion control /fnMotionMove/
|
|
fnMotionMove = function (arg_map) {
|
|
var
|
|
motion_id = arg_map.motion_id,
|
|
event_src = arg_map.event_src,
|
|
do_zoommove = false,
|
|
motion_map, option_map, bound_ns_map,
|
|
event_ue, px_pinch_zoom, px_delta_zoom,
|
|
mzoom1_map, mzoom2_map
|
|
;
|
|
|
|
if (!motionMapMap[motion_id]) {
|
|
return;
|
|
}
|
|
|
|
motion_map = motionMapMap[motion_id];
|
|
option_map = motion_map.option_map;
|
|
bound_ns_map = option_map.bound_ns_map;
|
|
|
|
motion_map.timeStamp = event_src.timeStamp;
|
|
motion_map.elem_target = event_src.target;
|
|
motion_map.ms_elapsed = event_src.timeStamp - motion_map.ms_timestart;
|
|
|
|
motion_map.px_delta_x = event_src.clientX - motion_map.px_current_x;
|
|
motion_map.px_delta_y = event_src.clientY - motion_map.px_current_y;
|
|
|
|
motion_map.px_current_x = event_src.clientX;
|
|
motion_map.px_current_y = event_src.clientY;
|
|
|
|
// native event object override
|
|
motion_map.timeStamp = event_src.timeStamp;
|
|
|
|
// disallow tap if outside of zone or time elapsed
|
|
// we use this for other events, so we do it every time
|
|
if (motion_map.do_allow_tap) {
|
|
if (Math.abs(motion_map.px_delta_x) > option_map.px_radius
|
|
|| Math.abs(motion_map.pd_delta_y) > option_map.px_radius
|
|
|| motion_map.ms_elapsed > option_map.tap_time
|
|
) {
|
|
motion_map.do_allow_tap = false;
|
|
}
|
|
}
|
|
|
|
if (motion1ZoomId && motion2ZoomId
|
|
&& ( motion_id === motion1ZoomId
|
|
|| motion_id === motion2ZoomId
|
|
)) {
|
|
motionMapMap[motion_id] = motion_map;
|
|
mzoom1_map = motionMapMap[motion1ZoomId];
|
|
mzoom2_map = motionMapMap[motion2ZoomId];
|
|
|
|
px_pinch_zoom = Math.floor(
|
|
Math.sqrt(
|
|
Math.pow((mzoom1_map.px_current_x - mzoom2_map.px_current_x), 2)
|
|
+ Math.pow((mzoom1_map.px_current_y - mzoom2_map.px_current_y), 2)
|
|
) + 0.5
|
|
);
|
|
|
|
if (pxPinchZoom === -1) {
|
|
px_delta_zoom = 0;
|
|
}
|
|
else {
|
|
px_delta_zoom = ( px_pinch_zoom - pxPinchZoom ) * zoomTouchNum;
|
|
}
|
|
|
|
// save value for next iteration delta comparison
|
|
pxPinchZoom = px_pinch_zoom;
|
|
do_zoommove = true;
|
|
}
|
|
else if (motionDzoomId === motion_id) {
|
|
if (bound_ns_map.uzoommove) {
|
|
px_delta_zoom = motion_map.px_delta_y * zoomMouseNum;
|
|
do_zoommove = true;
|
|
}
|
|
}
|
|
|
|
if (do_zoommove) {
|
|
event_ue = $.Event('uzoommove');
|
|
motion_map.px_delta_zoom = px_delta_zoom;
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
return;
|
|
}
|
|
|
|
if (motionHeldId === motion_id) {
|
|
if (bound_ns_map.uheldmove) {
|
|
event_ue = $.Event('uheldmove');
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
event_src.preventDefault();
|
|
}
|
|
}
|
|
else if (motionDragId === motion_id) {
|
|
if (bound_ns_map.udragmove) {
|
|
event_ue = $.Event('udragmove');
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
event_src.preventDefault();
|
|
}
|
|
}
|
|
|
|
if (!motionDragId
|
|
&& !motionHeldId
|
|
&& bound_ns_map.udragstart
|
|
&& motion_map.do_allow_tap === false
|
|
) {
|
|
motionDragId = motion_id;
|
|
event_ue = $.Event('udragstart');
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
event_src.preventDefault();
|
|
|
|
if (motion_map.idto_tapheld) {
|
|
clearTimeout(motion_map.idto_tapheld);
|
|
delete motion_map.idto_tapheld;
|
|
}
|
|
}
|
|
};
|
|
// End motion control /fnMotionMove/
|
|
|
|
// Begin motion control /fnMotionEnd/
|
|
fnMotionEnd = function (arg_map) {
|
|
var
|
|
motion_id = arg_map.motion_id,
|
|
event_src = arg_map.event_src,
|
|
do_zoomend = false,
|
|
motion_map, option_map, bound_ns_map, event_ue
|
|
;
|
|
|
|
doDisableMouse = false;
|
|
|
|
if (!motionMapMap[motion_id]) {
|
|
return;
|
|
}
|
|
|
|
motion_map = motionMapMap[motion_id];
|
|
option_map = motion_map.option_map;
|
|
bound_ns_map = option_map.bound_ns_map;
|
|
|
|
motion_map.elem_target = event_src.target;
|
|
motion_map.ms_elapsed = event_src.timeStamp - motion_map.ms_timestart;
|
|
motion_map.ms_timestop = event_src.timeStamp;
|
|
|
|
if (motion_map.px_current_x) {
|
|
motion_map.px_delta_x = event_src.clientX - motion_map.px_current_x;
|
|
motion_map.px_delta_y = event_src.clientY - motion_map.px_current_y;
|
|
}
|
|
|
|
motion_map.px_current_x = event_src.clientX;
|
|
motion_map.px_current_y = event_src.clientY;
|
|
|
|
motion_map.px_end_x = event_src.clientX;
|
|
motion_map.px_end_y = event_src.clientY;
|
|
|
|
// native event object override
|
|
motion_map.timeStamp = event_src.timeStamp
|
|
;
|
|
|
|
// clear-out any long-hold tap timer
|
|
if (motion_map.idto_tapheld) {
|
|
clearTimeout(motion_map.idto_tapheld);
|
|
delete motion_map.idto_tapheld;
|
|
}
|
|
|
|
// trigger utap
|
|
if (bound_ns_map.utap
|
|
&& motion_map.ms_elapsed <= option_map.tap_time
|
|
&& motion_map.do_allow_tap
|
|
) {
|
|
event_ue = $.Event('utap');
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
}
|
|
|
|
// trigger udragend
|
|
if (motion_id === motionDragId) {
|
|
if (bound_ns_map.udragend) {
|
|
event_ue = $.Event('udragend');
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
event_src.preventDefault();
|
|
}
|
|
motionDragId = undefined;
|
|
}
|
|
|
|
// trigger heldend
|
|
if (motion_id === motionHeldId) {
|
|
if (bound_ns_map.uheldend) {
|
|
event_ue = $.Event('uheldend');
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
}
|
|
motionHeldId = undefined;
|
|
}
|
|
|
|
// trigger uzoomend
|
|
if (motion_id === motionDzoomId) {
|
|
do_zoomend = true;
|
|
motionDzoomId = undefined;
|
|
}
|
|
|
|
// cleanup zoom info
|
|
else if (motion_id === motion1ZoomId) {
|
|
if (motion2ZoomId) {
|
|
motion1ZoomId = motion2ZoomId;
|
|
motion2ZoomId = undefined;
|
|
do_zoomend = true;
|
|
}
|
|
else {
|
|
motion1ZoomId = undefined;
|
|
}
|
|
pxPinchZoom = -1;
|
|
}
|
|
if (motion_id === motion2ZoomId) {
|
|
motion2ZoomId = undefined;
|
|
pxPinchZoom = -1;
|
|
do_zoomend = true;
|
|
}
|
|
|
|
if (do_zoomend && bound_ns_map.uzoomend) {
|
|
event_ue = $.Event('uzoomend');
|
|
motion_map.px_delta_zoom = 0;
|
|
$.extend(event_ue, motion_map);
|
|
$(motion_map.elem_bound).trigger(event_ue);
|
|
}
|
|
// remove pointer from consideration
|
|
delete motionMapMap[motion_id];
|
|
};
|
|
// End motion control /fnMotionEnd/
|
|
//------------------ END MOTION CONTROLS -------------------
|
|
|
|
//------------------- BEGIN EVENT HANDLERS -------------------
|
|
// Begin event handler /onTouch/ for all touch events.
|
|
// We use the 'type' attribute to dispatch to motion control
|
|
onTouch = function (event) {
|
|
var
|
|
this_el = this,
|
|
timestamp = +new Date(),
|
|
o_event = event.originalEvent,
|
|
touch_list = o_event ? o_event.changedTouches || [] : [],
|
|
touch_count = touch_list.length,
|
|
idx, touch_event, motion_id, handler_fn
|
|
;
|
|
|
|
doDisableMouse = true;
|
|
|
|
event.timeStamp = timestamp;
|
|
|
|
switch (event.type) {
|
|
case 'touchstart' :
|
|
handler_fn = fnMotionStart;
|
|
event.preventDefault();
|
|
break;
|
|
case 'touchmove' :
|
|
handler_fn = fnMotionMove;
|
|
break;
|
|
case 'touchend' :
|
|
case 'touchcancel' :
|
|
handler_fn = fnMotionEnd;
|
|
break;
|
|
default :
|
|
handler_fn = null;
|
|
}
|
|
|
|
if (!handler_fn) {
|
|
return;
|
|
}
|
|
|
|
for (idx = 0; idx < touch_count; idx++) {
|
|
touch_event = touch_list[idx];
|
|
|
|
motion_id = 'touch' + String(touch_event.identifier);
|
|
|
|
event.clientX = touch_event.clientX;
|
|
event.clientY = touch_event.clientY;
|
|
handler_fn({
|
|
elem: this_el,
|
|
motion_id: motion_id,
|
|
event_src: event
|
|
});
|
|
}
|
|
};
|
|
// End event handler /onTouch/
|
|
|
|
|
|
// Begin event handler /onMouse/ for all mouse events
|
|
// We use the 'type' attribute to dispatch to motion control
|
|
onMouse = function (event) {
|
|
var
|
|
this_el = this,
|
|
motion_id = 'mouse' + String(event.button),
|
|
request_dzoom = false,
|
|
handler_fn
|
|
;
|
|
|
|
if (doDisableMouse) {
|
|
event.stopImmediatePropagation();
|
|
return;
|
|
}
|
|
|
|
if (event.shiftKey) {
|
|
request_dzoom = true;
|
|
}
|
|
|
|
// skip left or middle clicks
|
|
if (event.type !== 'mousemove') {
|
|
if (event.button !== 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
switch (event.type) {
|
|
case 'mousedown' :
|
|
handler_fn = fnMotionStart;
|
|
event.preventDefault();
|
|
break;
|
|
case 'mouseup' :
|
|
handler_fn = fnMotionEnd;
|
|
break;
|
|
case 'mousemove' :
|
|
handler_fn = fnMotionMove;
|
|
break;
|
|
default:
|
|
handler_fn = null;
|
|
}
|
|
|
|
if (!handler_fn) {
|
|
return;
|
|
}
|
|
|
|
handler_fn({
|
|
elem: this_el,
|
|
event_src: event,
|
|
request_dzoom: request_dzoom,
|
|
motion_id: motion_id
|
|
});
|
|
};
|
|
// End event handler /onMouse/
|
|
//-------------------- END EVENT HANDLERS --------------------
|
|
|
|
|
|
// Export special events through jQuery API
|
|
$Special.ue
|
|
= $Special.utap = $Special.uheld
|
|
= $Special.uzoomstart = $Special.uzoommove = $Special.uzoomend
|
|
= $Special.udragstart = $Special.udragmove = $Special.udragend
|
|
= $Special.uheldstart = $Special.uheldmove = $Special.uheldend
|
|
= Ue
|
|
;
|
|
$.ueSetGlobalCb = function (selector_str, callback_match, callback_nomatch) {
|
|
callbackList.push({
|
|
selector_str: selector_str || '',
|
|
callback_match: callback_match || null,
|
|
callback_nomatch: callback_nomatch || null
|
|
});
|
|
};
|
|
|
|
}(jQuery)); |