1/*
2jQuery grab
3https://github.com/jussi-kalliokoski/jQuery.grab
4Ported from Jin.js::gestures
5https://github.com/jussi-kalliokoski/jin.js/
6Created by Jussi Kalliokoski
7Licensed under MIT License.
8
9Includes fix for IE
10*/
11
12
13(function($){
14	var	extend		= $.extend,
15		mousedown	= 'mousedown',
16		mousemove	= 'mousemove',
17		mouseup		= 'mouseup',
18		touchstart	= 'touchstart',
19		touchmove	= 'touchmove',
20		touchend	= 'touchend',
21		touchcancel	= 'touchcancel';
22
23	function unbind(elem, type, func){
24		if (type.substr(0,5) !== 'touch'){ // A temporary fix for IE8 data passing problem in Jin.
25			return $(elem).unbind(type, func);
26		}
27		var fnc, i;
28		for (i=0; i<bind._binds.length; i++){
29			if (bind._binds[i].elem === elem && bind._binds[i].type === type && bind._binds[i].func === func){
30				if (document.addEventListener){
31					elem.removeEventListener(type, bind._binds[i].fnc, false);
32				} else {
33					elem.detachEvent('on'+type, bind._binds[i].fnc);
34				}
35				bind._binds.splice(i--, 1);
36			}
37		}
38	}
39
40	function bind(elem, type, func, pass){
41		if (type.substr(0,5) !== 'touch'){ // A temporary fix for IE8 data passing problem in Jin.
42			return $(elem).bind(type, pass, func);
43		}
44		var fnc, i;
45		if (bind[type]){
46			return bind[type].bind(elem, type, func, pass);
47		}
48		fnc = function(e){
49			if (!e){ // Fix some ie bugs...
50				e = window.event;
51			}
52			if (!e.stopPropagation){
53				e.stopPropagation = function(){ this.cancelBubble = true; };
54			}
55			e.data = pass;
56			func.call(elem, e);
57		};
58		if (document.addEventListener){
59			elem.addEventListener(type, fnc, false);
60		} else {
61			elem.attachEvent('on' + type, fnc);
62		}
63		bind._binds.push({elem: elem, type: type, func: func, fnc: fnc});
64	}
65
66	function grab(elem, options)
67	{
68		var data = {
69			move: {x: 0, y: 0},
70			offset: {x: 0, y: 0},
71			position: {x: 0, y: 0},
72			start: {x: 0, y: 0},
73			affects: document.documentElement,
74			stopPropagation: false,
75			preventDefault: true,
76			touch: true // Implementation unfinished, and doesn't support multitouch
77		};
78		extend(data, options);
79		data.element = elem;
80		bind(elem, mousedown, mouseDown, data);
81		if (data.touch){
82			bind(elem, touchstart, touchStart, data);
83		}
84	}
85	function ungrab(elem){
86		unbind(elem, mousedown, mousedown);
87	}
88	function mouseDown(e){
89		e.data.position.x = e.pageX;
90		e.data.position.y = e.pageY;
91		e.data.start.x = e.pageX;
92		e.data.start.y = e.pageY;
93		e.data.event = e;
94		if (e.data.onstart && e.data.onstart.call(e.data.element, e.data)){
95			return;
96		}
97		if (e.preventDefault && e.data.preventDefault){
98			e.preventDefault();
99		}
100		if (e.stopPropagation && e.data.stopPropagation){
101			e.stopPropagation();
102		}
103		bind(e.data.affects, mousemove, mouseMove, e.data);
104		bind(e.data.affects, mouseup, mouseUp, e.data);
105	}
106	function mouseMove(e){
107		if (e.preventDefault && e.data.preventDefault){
108			e.preventDefault();
109		}
110		if (e.stopPropagation && e.data.preventDefault){
111			e.stopPropagation();
112		}
113		e.data.move.x = e.pageX - e.data.position.x;
114		e.data.move.y = e.pageY - e.data.position.y;
115		e.data.position.x = e.pageX;
116		e.data.position.y = e.pageY;
117		e.data.offset.x = e.pageX - e.data.start.x;
118		e.data.offset.y = e.pageY - e.data.start.y;
119		e.data.event = e;
120		if (e.data.onmove){
121			e.data.onmove.call(e.data.element, e.data);
122		}
123	}
124	function mouseUp(e){
125		if (e.preventDefault && e.data.preventDefault){
126			e.preventDefault();
127		}
128		if (e.stopPropagation && e.data.stopPropagation){
129			e.stopPropagation();
130		}
131		unbind(e.data.affects, mousemove, mouseMove);
132		unbind(e.data.affects, mouseup, mouseUp);
133		e.data.event = e;
134		if (e.data.onfinish){
135			e.data.onfinish.call(e.data.element, e.data);
136		}
137	}
138	function touchStart(e){
139		e.data.position.x = e.touches[0].pageX;
140		e.data.position.y = e.touches[0].pageY;
141		e.data.start.x = e.touches[0].pageX;
142		e.data.start.y = e.touches[0].pageY;
143		e.data.event = e;
144		if (e.data.onstart && e.data.onstart.call(e.data.element, e.data)){
145			return;
146		}
147		if (e.preventDefault && e.data.preventDefault){
148			e.preventDefault();
149		}
150		if (e.stopPropagation && e.data.stopPropagation){
151			e.stopPropagation();
152		}
153		bind(e.data.affects, touchmove, touchMove, e.data);
154		bind(e.data.affects, touchend, touchEnd, e.data);
155	}
156	function touchMove(e){
157		if (e.preventDefault && e.data.preventDefault){
158			e.preventDefault();
159		}
160		if (e.stopPropagation && e.data.stopPropagation){
161			e.stopPropagation();
162		}
163		e.data.move.x = e.touches[0].pageX - e.data.position.x;
164		e.data.move.y = e.touches[0].pageY - e.data.position.y;
165		e.data.position.x = e.touches[0].pageX;
166		e.data.position.y = e.touches[0].pageY;
167		e.data.offset.x = e.touches[0].pageX - e.data.start.x;
168		e.data.offset.y = e.touches[0].pageY - e.data.start.y;
169		e.data.event = e;
170		if (e.data.onmove){
171			e.data.onmove.call(e.data.elem, e.data);
172		}
173	}
174	function touchEnd(e){
175		if (e.preventDefault && e.data.preventDefault){
176			e.preventDefault();
177		}
178		if (e.stopPropagation && e.data.stopPropagation){
179			e.stopPropagation();
180		}
181		unbind(e.data.affects, touchmove, touchMove);
182		unbind(e.data.affects, touchend, touchEnd);
183		e.data.event = e;
184		if (e.data.onfinish){
185			e.data.onfinish.call(e.data.element, e.data);
186		}
187	}
188
189	bind._binds = [];
190
191	$.fn.grab = function(a, b){
192		return this.each(function(){
193			return grab(this, a, b);
194		});
195	};
196	$.fn.ungrab = function(a){
197		return this.each(function(){
198			return ungrab(this, a);
199		});
200	};
201})(jQuery);