mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-31 03:10:07 +00:00 
			
		
		
		
	added more generic libs, and some minor changes...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									0c4c25fa0b
								
							
						
					
					
						commit
						9cda74ef12
					
				| @ -5,6 +5,7 @@ | |||||||
| 	[_] write basic align code | 	[_] write basic align code | ||||||
| 		 | 		 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| Generation 3 UI implementation | Generation 3 UI implementation | ||||||
| 	Infinite ribbon (lib) | 	Infinite ribbon (lib) | ||||||
| 		cyclic ribbon | 		cyclic ribbon | ||||||
| @ -46,7 +47,7 @@ Generation 3 UI implementation | |||||||
| 		| +/- 90 deg | 		| +/- 90 deg | ||||||
| 	UI modes | 	UI modes | ||||||
| 		single ribbon | 		single ribbon | ||||||
| 			show images above/below in current ribbon | 			show images shifted (all / above only / below only) in current ribbon | ||||||
| 			show marked only | 			show marked only | ||||||
| 		multiple ribbons | 		multiple ribbons | ||||||
| 			show images above/below | 			show images above/below | ||||||
|  | |||||||
| @ -80,6 +80,9 @@ | |||||||
| 
 | 
 | ||||||
| <script src="jquery.js"></script> | <script src="jquery.js"></script> | ||||||
| 
 | 
 | ||||||
|  | <script src="lib/jli.js"></script> | ||||||
|  | <script src="lib/keyboard.js"></script> | ||||||
|  | 
 | ||||||
| <script> | <script> | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
| @ -195,6 +198,10 @@ function focusImage(image){ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | // Alignment API... | ||||||
|  | function alignImage(){ | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| // NOTE: this is on purpose done relative... | // NOTE: this is on purpose done relative... | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								ui/lib/_template.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								ui/lib/_template.js
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | /********************************************************************** | ||||||
|  | *  | ||||||
|  | * | ||||||
|  | * | ||||||
|  | **********************************************************************/ | ||||||
|  | 
 | ||||||
|  | //var DEBUG = DEBUG != null ? DEBUG : true
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*********************************************************************/ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /********************************************************************** | ||||||
|  | * vim:set ts=4 sw=4 :                                                */ | ||||||
							
								
								
									
										571
									
								
								ui/lib/jli.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										571
									
								
								ui/lib/jli.js
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,571 @@ | |||||||
|  | /********************************************************************** | ||||||
|  | * JavaScript Lib | ||||||
|  | * at this point this is just a place I put most of the generic stuff I  | ||||||
|  | * use. | ||||||
|  | *  | ||||||
|  | * P.S. the name "jli" just stands for Java script LIb, like how it  | ||||||
|  | * looks... | ||||||
|  | **********************************************************************/ | ||||||
|  | 
 | ||||||
|  | //var DEBUG = DEBUG != null ? DEBUG : true
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*********************************************************************/ | ||||||
|  | 
 | ||||||
|  | // This will create a function that will cycle through a class_list on elem 
 | ||||||
|  | // calling the optional callbacks before and/or after.
 | ||||||
|  | // If class_list is given as a string, then this will create a toggler that 
 | ||||||
|  | // will turn the given class on the element on and off.
 | ||||||
|  | //
 | ||||||
|  | // Elem is a jquery compatible object; default use-case: a css selector.
 | ||||||
|  | //
 | ||||||
|  | // This will return a function with the folowing signature:
 | ||||||
|  | //
 | ||||||
|  | // 	func() -> <state>
 | ||||||
|  | // 	func(<action>) -> <state>
 | ||||||
|  | // 	func(<target>, <action>) -> <state>
 | ||||||
|  | //
 | ||||||
|  | //
 | ||||||
|  | // In the first form this just toggles the state.
 | ||||||
|  | //
 | ||||||
|  | // In forms 2 and 3, if class_list is a string, the <action> can be :
 | ||||||
|  | // 	- <index>		: 0 for 'off' and 1 for 'on' (see below)
 | ||||||
|  | // 	- 'on'			: switch mode on -- add class
 | ||||||
|  | // 	- 'off'			: switch mode off -- remove class
 | ||||||
|  | // 	- '?'			: return current state ('on'|'off')
 | ||||||
|  | //
 | ||||||
|  | // In forms 2 and 3, if class_list is a list of strings, the <action> can be:
 | ||||||
|  | //  - <index>		: explicitly set the state to index in class_list
 | ||||||
|  | //  - <class-name>	: explicitly set a class from the list
 | ||||||
|  | // 	- '?'			: return current state ('on'|'off')
 | ||||||
|  | // 
 | ||||||
|  | // In the third form the <target> is a jquery-compatible object.
 | ||||||
|  | //
 | ||||||
|  | // In all forms this will return the current state string or null if the
 | ||||||
|  | // action argument given is invalid.
 | ||||||
|  | //
 | ||||||
|  | // NOTE: there is a special action 'next', passing it will have the same
 | ||||||
|  | // 		effect as not passing any action.
 | ||||||
|  | // NOTE: if it is needed to apply this to an explicit target but with 
 | ||||||
|  | // 		no explicit action, just pass 'next' as the second argument.
 | ||||||
|  | // NOTE: a special class name 'none' means no class is set, if it is present 
 | ||||||
|  | // 		in the class_list then that state will be with all other state 
 | ||||||
|  | // 		classes removed.
 | ||||||
|  | // NOTE: <class-name> must be an exact match to a string given in class_list
 | ||||||
|  | // NOTE: of only one callback is given then it will be called after the 
 | ||||||
|  | // 		class change...
 | ||||||
|  | // 		a way around this is to pass an empty function as callback_b
 | ||||||
|  | // NOTE: leading dots in class names in class_list are optional. 
 | ||||||
|  | // 		this is due to several times I've repeated the same mistake of 
 | ||||||
|  | // 		forgetting to write the classes without leading dots, this now 
 | ||||||
|  | // 		will normalize the class list...
 | ||||||
|  | //
 | ||||||
|  | //
 | ||||||
|  | // This also takes one or two callbacks. If only one is given then it is
 | ||||||
|  | // called after (post) the change is made. If two are given then the first
 | ||||||
|  | // is called before the change and the second after the change.
 | ||||||
|  | // The callbacks are passed the current action.
 | ||||||
|  | //
 | ||||||
|  | // NOTE: the pre-callback will get the "intent" action, i.e. the state the
 | ||||||
|  | // 		we are changing into but the changes are not yet made.
 | ||||||
|  | // NOTE: if the pre-callback explicitly returns false, then the change will
 | ||||||
|  | // 		not be made.
 | ||||||
|  | function createCSSClassToggler(elem, class_list, callback_a, callback_b){ | ||||||
|  | 	var bool_action = false | ||||||
|  | 	if(typeof(class_list) == typeof('')){ | ||||||
|  | 		class_list = ['none', class_list] | ||||||
|  | 		bool_action = true | ||||||
|  | 	} | ||||||
|  | 	// Normalize classes -- remove the dot from class names...
 | ||||||
|  | 	// NOTE: this is here because I've made the error of including a 
 | ||||||
|  | 	// 		leading "." almost every time I use this after I forget 
 | ||||||
|  | 	// 		the UI...
 | ||||||
|  | 	class_list = $(class_list).map(function(_, e){ | ||||||
|  | 		return $(e.split(' ')).map(function(_, c){ | ||||||
|  | 			c = c.trim() | ||||||
|  | 			return c[0] == '.' ? c.slice(1) : c | ||||||
|  | 		}).toArray().join(' ') | ||||||
|  | 	}).toArray() | ||||||
|  | 	// normalize the callbacks...
 | ||||||
|  | 	if(callback_b == null){ | ||||||
|  | 		var callback_pre = null | ||||||
|  | 		var callback_post = callback_a | ||||||
|  | 	} else { | ||||||
|  | 		var callback_pre = callback_a | ||||||
|  | 		var callback_post = callback_b | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// XXX make this generic...
 | ||||||
|  | 	var func = function(a, b){ | ||||||
|  | 		if(b == null){ | ||||||
|  | 			var action = a == 'next' ? null : a | ||||||
|  | 			var e = elem | ||||||
|  | 		} else { | ||||||
|  | 			var e = a | ||||||
|  | 			var action = b == 'next' ? null : b | ||||||
|  | 		} | ||||||
|  | 		e = $(e) | ||||||
|  | 		// option number...
 | ||||||
|  | 		if(typeof(action) == typeof(1)){ | ||||||
|  | 			// range check...
 | ||||||
|  | 			if(action < 0 || action >= class_list.length){ | ||||||
|  | 				return null | ||||||
|  | 			} | ||||||
|  | 			if(bool_action){ | ||||||
|  | 				action = action == 0 ? 'off' : 'on' | ||||||
|  | 			} else { | ||||||
|  | 				action = class_list[action] | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// we need to get the current state...
 | ||||||
|  | 		if(action == null || action == '?'){ | ||||||
|  | 			// get current state...
 | ||||||
|  | 			var cur = 'none' | ||||||
|  | 			for(var i=0; i < class_list.length; i++){ | ||||||
|  | 				if(e.hasClass(class_list[i])){ | ||||||
|  | 					cur = class_list[i] | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			}  | ||||||
|  | 			// just asking for info...
 | ||||||
|  | 			if(action == '?'){ | ||||||
|  | 				return bool_action ? (cur == 'none' ? 'off' : 'on') : cur | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		// invalid action...
 | ||||||
|  | 		} else if((bool_action && ['on', 'off'].indexOf(action) == -1) | ||||||
|  | 				|| (!bool_action && class_list.indexOf(action) == -1)){ | ||||||
|  | 			return null | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		var cls = bool_action ? class_list[1] : action | ||||||
|  | 		// get the right class...
 | ||||||
|  | 		if(action == null){ | ||||||
|  | 			var i = class_list.indexOf(cur)+1 | ||||||
|  | 			i = i == -1 ? 0 : i | ||||||
|  | 			i = i == class_list.length ? 0 : i | ||||||
|  | 			cls = class_list[i] | ||||||
|  | 
 | ||||||
|  | 			if(bool_action){ | ||||||
|  | 				action = cls == 'none' ? 'off' : 'on' | ||||||
|  | 			} else { | ||||||
|  | 				action = cls | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// pre callback...
 | ||||||
|  | 		if(callback_pre != null){ | ||||||
|  | 			if(callback_pre(action) === false){ | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// update the element...
 | ||||||
|  | 		e.removeClass(class_list.join(' ')) | ||||||
|  | 		if(cls != 'none' && action != 'off'){ | ||||||
|  | 			e.addClass(cls) | ||||||
|  | 		} | ||||||
|  | 		// post callback...
 | ||||||
|  | 		if(callback_post != null){ | ||||||
|  | 			callback_post(action) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return action | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	func.class_list = class_list | ||||||
|  | 	if(bool_action){ | ||||||
|  | 		func.doc = 'With no arguments this will toggle between "on" and '+ | ||||||
|  | 			'"off".\n'+ | ||||||
|  | 			'If either "on" or "off" are given then this will switch '+ | ||||||
|  | 			'to that mode.\n'+ | ||||||
|  | 			'If "?" is given, this will return either "on" or "off" '+ | ||||||
|  | 			'depending on the current state.' | ||||||
|  | 	}else{ | ||||||
|  | 		func.doc = 'With no arguments this will toggle between '+ | ||||||
|  | 			class_list +' in cycle.\n' +  | ||||||
|  | 			'if any of the state names or its number is given then that '+ | ||||||
|  | 			'state is switched on.'+ | ||||||
|  | 			'If "?" is given, this will return the current state.' | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return func | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | // show a jQuary opject in viewer overlay...
 | ||||||
|  | // XXX need to set .scrollTop(0) when showing different UI... 
 | ||||||
|  | // 		...and not set it when the UI is the same
 | ||||||
|  | // XXX this must create it's own overlay...
 | ||||||
|  | function showInOverlay(obj){ | ||||||
|  | 	obj.click(function(){ return false }) | ||||||
|  | 	// XXX 
 | ||||||
|  | 	$('.viewer').addClass('overlay-mode') | ||||||
|  | 	// clean things up...
 | ||||||
|  | 	$('.overlay .content').children().remove() | ||||||
|  | 	// put it in the overlay...
 | ||||||
|  | 	$('.overlay .content').append(obj) | ||||||
|  | 	// prepare the overlay...
 | ||||||
|  | 	$('.overlay') | ||||||
|  | 		.one('click', function(){ | ||||||
|  | 			$('.overlay') | ||||||
|  | 				.fadeOut(function(){ | ||||||
|  | 					$('.overlay .content') | ||||||
|  | 						.children() | ||||||
|  | 							.remove() | ||||||
|  | 					$('.overlay-mode').removeClass('overlay-mode') | ||||||
|  | 				}) | ||||||
|  | 		}) | ||||||
|  | 		.fadeIn() | ||||||
|  | 	return obj | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function overlayMessage(text){ | ||||||
|  | 	return showInOverlay($('<div class="overlay-message">' +text+ '</div>')) | ||||||
|  | } | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function unanimated(obj, func, time){ | ||||||
|  | 	return function(){ | ||||||
|  | 		if(time == null){ | ||||||
|  | 			time = 5 | ||||||
|  | 		}	 | ||||||
|  | 		obj = $(obj) | ||||||
|  | 		obj.addClass('unanimated') | ||||||
|  | 		var res = func.apply(func, arguments) | ||||||
|  | 		setTimeout(function(){obj.removeClass('unanimated')}, time) | ||||||
|  | 		return res | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // NOTE: this will only use the first element in a set.
 | ||||||
|  | // NOTE: if no element is given this will return null.
 | ||||||
|  | function makeCSSVendorAttrGetter(attr, dfl, callback){ | ||||||
|  | 	return function(elem){ | ||||||
|  | 		elem = $(elem) | ||||||
|  | 		if(elem.length == 0){ | ||||||
|  | 			return null | ||||||
|  | 		} | ||||||
|  | 		// using the attr...
 | ||||||
|  | 		var vendors = ['O', 'Moz', 'ms', 'webkit'] | ||||||
|  | 		var data = elem[0].style[attr] | ||||||
|  | 
 | ||||||
|  | 		// go through vendor prefixes... (hate this!)
 | ||||||
|  | 		if(!data || data == 'none'){ | ||||||
|  | 			for(var i in vendors){ | ||||||
|  | 				data = elem[0].style[vendors[i] + attr.capitalize()] | ||||||
|  | 				if(data && data != 'none'){ | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// no data is set...
 | ||||||
|  | 		if(!data || data == 'none'){ | ||||||
|  | 			return dfl | ||||||
|  | 		} | ||||||
|  | 		return callback(data) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // Return a scale value for the given element(s).
 | ||||||
|  | // NOTE: this will only return a single scale value...
 | ||||||
|  | var getElementScale = makeCSSVendorAttrGetter( | ||||||
|  | 		'transform', | ||||||
|  | 		1, | ||||||
|  | 		function(data){ | ||||||
|  | 			return parseFloat((/(scale|matrix)\(([^),]*)\)/).exec(data)[2]) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | var getElementShift = makeCSSVendorAttrGetter( | ||||||
|  | 		'transform', | ||||||
|  | 		{left: 0, top: 0}, | ||||||
|  | 		function(data){ | ||||||
|  | 			res = /(translate\(|matrix\([^,]*,[^,]*,[^,]*,[^,]*,)([^,]*),([^\)]*)\)/.exec(data) | ||||||
|  | 			return { | ||||||
|  | 				left: parseFloat(res[2]), | ||||||
|  | 				top: parseFloat(res[3]) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | var DEFAULT_TRANSITION_DURATION = 200 | ||||||
|  | 
 | ||||||
|  | var getElementTransitionDuration = makeCSSVendorAttrGetter( | ||||||
|  | 		'transitionDuration',  | ||||||
|  | 		DEFAULT_TRANSITION_DURATION,  | ||||||
|  | 		parseInt) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | var USE_3D_TRANSFORM = true | ||||||
|  | 
 | ||||||
|  | // NOTE: at this point this works only on the X axis...
 | ||||||
|  | function setElementTransform(elem, offset, scale, duration){ | ||||||
|  | 	elem = $(elem) | ||||||
|  | 	var t3d = USE_3D_TRANSFORM ? 'translateZ(0px)' : '' | ||||||
|  | 
 | ||||||
|  | 	if(offset == null){ | ||||||
|  | 		offset = getElementShift(elem) | ||||||
|  | 	// number -- only the x coord...
 | ||||||
|  | 	} else if(typeof(offset) == typeof(1)){ | ||||||
|  | 		offset = { | ||||||
|  | 			left: offset, | ||||||
|  | 			top: 0 | ||||||
|  | 		} | ||||||
|  | 	// array...
 | ||||||
|  | 	} else if(offset.indexOf){ | ||||||
|  | 		offset = { | ||||||
|  | 			left: offset[0] ? offset[0] : 0, | ||||||
|  | 			top: offset[1] ? offset[1] : 0 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if(scale == null){ | ||||||
|  | 		var scale = getElementScale(elem) | ||||||
|  | 	} | ||||||
|  | 	if(USE_TRANSFORM){ | ||||||
|  | 		var transform = 'translate('+  | ||||||
|  | 				Math.round(offset.left) +'px, '+ | ||||||
|  | 				Math.round(offset.top) +'px) scale('+ scale +') ' + t3d | ||||||
|  | 		elem.css({ | ||||||
|  | 			'-ms-transform' : transform,  | ||||||
|  | 			'-webkit-transform' : transform,  | ||||||
|  | 			'-moz-transform' : transform,  | ||||||
|  | 			'-o-transform' : transform,  | ||||||
|  | 			'transform' : transform,  | ||||||
|  | 
 | ||||||
|  | 			// XXX can we avoid this here?? 
 | ||||||
|  | 			left: 0, | ||||||
|  | 			// XXX is this correct???
 | ||||||
|  | 			top: '' | ||||||
|  | 		}, duration) | ||||||
|  | 	} else { | ||||||
|  | 		var transform = 'translate(0px, 0px) scale('+ scale +') ' + t3d | ||||||
|  | 		elem.css({ | ||||||
|  | 			// NOTE: this will be wrong during a transition, that's why we 
 | ||||||
|  | 			// 		can pass the pre-calculated offset as an argument...
 | ||||||
|  | 			left: Math.round(offset.left), | ||||||
|  | 			top: Math.round(offset.top), | ||||||
|  | 
 | ||||||
|  | 			// XXX can we avoid this here?? 
 | ||||||
|  | 			'-ms-transform' : transform,  | ||||||
|  | 			'-webkit-transform' : transform,  | ||||||
|  | 			'-moz-transform' : transform,  | ||||||
|  | 			'-o-transform' : transform,  | ||||||
|  | 			'transform' : transform,  | ||||||
|  | 		}, duration) | ||||||
|  | 	} | ||||||
|  | 	return elem | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // XXX this affects only the innertial part, not setCurrentPage...
 | ||||||
|  | var USE_TRANSITIONS_FOR_ANIMATION = false | ||||||
|  | 
 | ||||||
|  | // XXX make this a drop-in replacement for setElementTransform...
 | ||||||
|  | // XXX cleanup, still flacky...
 | ||||||
|  | function animateElementTo(elem, to, duration, easing, speed, use_transitions){ | ||||||
|  | 	// stop all ongoing animations on the current elem...
 | ||||||
|  | 	stopAnimation(elem) | ||||||
|  | 	use_transitions = use_transitions != null ?  | ||||||
|  | 							use_transitions  | ||||||
|  | 							: USE_TRANSITIONS_FOR_ANIMATION | ||||||
|  | 	// use transition for animation...
 | ||||||
|  | 	if(use_transitions){ | ||||||
|  | 		setTransitionEasing(elem, easing) | ||||||
|  | 		duration == null && setTransitionDuration(elem, duration) | ||||||
|  | 		setElementTransform(elem, to) | ||||||
|  | 
 | ||||||
|  | 	// manually animate...
 | ||||||
|  | 	} else { | ||||||
|  | 		if(typeof(to) == typeof(1)){ | ||||||
|  | 			to = { | ||||||
|  | 				left: to, | ||||||
|  | 				top: 0, | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if(typeof(speed) == typeof(2)){ | ||||||
|  | 			speed = { | ||||||
|  | 				x: speed, | ||||||
|  | 				y: 0, | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if(duration == null){ | ||||||
|  | 			duration = getElementTransitionDuration(elem) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		setTransitionDuration(elem, 0) | ||||||
|  | 
 | ||||||
|  | 		var start = Date.now() | ||||||
|  | 		var then = start + duration | ||||||
|  | 		var from = getElementShift(elem) | ||||||
|  | 		var cur = { | ||||||
|  | 			top: from.top, | ||||||
|  | 			left: from.left | ||||||
|  | 		} | ||||||
|  | 		var dist = { | ||||||
|  | 			top: to.top - from.top, | ||||||
|  | 			left: to.left - from.left, | ||||||
|  | 		} | ||||||
|  | 		elem.animating = true | ||||||
|  | 
 | ||||||
|  | 		function animate(){ | ||||||
|  | 			var t = Date.now() | ||||||
|  | 			// end of the animation...
 | ||||||
|  | 			if(t >= then){ | ||||||
|  | 				setElementTransform(elem, to) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			if(!elem.animating){ | ||||||
|  | 				// XXX jittery...
 | ||||||
|  | 				setElementTransform(elem, cur) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// do an intermediate step...
 | ||||||
|  | 			// XXX do propper easing...
 | ||||||
|  | 			// XXX sometimes results in jumping around...
 | ||||||
|  | 			// 		...result of jumping over the to position...
 | ||||||
|  | 			if(speed != null){ | ||||||
|  | 
 | ||||||
|  | 				// XXX the folowing two blocks are the same...
 | ||||||
|  | 				// XXX looks a bit too complex, revise...
 | ||||||
|  | 				if(Math.abs(dist.top) >= 1){ | ||||||
|  | 					dy = ((t - start) * speed.y) | ||||||
|  | 					if(Math.abs(dist.top) > Math.abs(dy)){ | ||||||
|  | 						dist.top -= dy | ||||||
|  | 						cur.top = Math.round(cur.top + dy) | ||||||
|  | 						// normalize...
 | ||||||
|  | 						cur.top = Math.abs(dist.top) <= 1 ? to.top : cur.top | ||||||
|  | 						// calc speed for next step...
 | ||||||
|  | 						speed.y = dist.top / (duration - (t - start)) | ||||||
|  | 					} else { | ||||||
|  | 						cur.top = to.top | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// XXX looks a bit too complex, revise...
 | ||||||
|  | 				if(Math.abs(dist.left) >= 1){ | ||||||
|  | 					dx = ((t - start) * speed.x) | ||||||
|  | 					if(Math.abs(dist.left) > Math.abs(dx)){ | ||||||
|  | 						dist.left -= dx | ||||||
|  | 						cur.left = Math.round(cur.left + dx) | ||||||
|  | 						// normalize...
 | ||||||
|  | 						cur.left = Math.abs(dist.left) <= 1 ? to.left : cur.left | ||||||
|  | 						// calc speed for next step...
 | ||||||
|  | 						speed.x = dist.left / (duration - (t - start)) | ||||||
|  | 					} else { | ||||||
|  | 						cur.left = to.left | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 			// XXX this is a staright forward linear function...
 | ||||||
|  | 			} else { | ||||||
|  | 				var r = (t - start) / duration | ||||||
|  | 				cur.top = Math.round(from.top + (dist.top * r)) | ||||||
|  | 				cur.left = Math.round(from.left + (dist.left * r))  | ||||||
|  | 			} | ||||||
|  | 			setElementTransform(elem, cur) | ||||||
|  | 			// sched next frame...
 | ||||||
|  | 			elem.next_frame = getAnimationFrame(animate) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		animate() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function stopAnimation(elem){ | ||||||
|  | 	if(elem.next_frame){ | ||||||
|  | 		cancelAnimationFrame(elem.next_frame) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // XXX account for other transitions...
 | ||||||
|  | function setElementScale(elem, scale){ | ||||||
|  | 	return setElementTransform(elem, null, scale) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function setTransitionEasing(elem, ease){ | ||||||
|  | 	if(typeof(ms) == typeof(0)){ | ||||||
|  | 		ms = ms + 'ms' | ||||||
|  | 	} | ||||||
|  | 	return elem.css({ | ||||||
|  | 		'transition-timing-function': ease,  | ||||||
|  | 		'-moz-transition-timing-function': ease, | ||||||
|  | 		'-o-transition-timing-function': ease, | ||||||
|  | 		'-ms-transition-timing-function': ease, | ||||||
|  | 		'-webkit-transition-timing-function': ease | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function setTransitionDuration(elem, ms){ | ||||||
|  | 	if(typeof(ms) == typeof(0)){ | ||||||
|  | 		ms = ms + 'ms' | ||||||
|  | 	} | ||||||
|  | 	return elem.css({ | ||||||
|  | 		'transition-duration': ms,  | ||||||
|  | 		'-moz-transition-duration': ms, | ||||||
|  | 		'-o-transition-duration': ms, | ||||||
|  | 		'-ms-transition-duration': ms, | ||||||
|  | 		'-webkit-transition-duration': ms | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /************************************************ jQuery extensions **/ | ||||||
|  | 
 | ||||||
|  | jQuery.fn.reverseChildren = function(){ | ||||||
|  | 	return $(this).each(function(_, e){ | ||||||
|  | 		return $(e).append($(e).children().detach().get().reverse()) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | jQuery.fn.sortChildren = function(func){ | ||||||
|  | 	return $(this).each(function(_, e){ | ||||||
|  | 		return $(e).append($(e).children().detach().get().sort(func)) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**************************************************** JS utilities ***/ | ||||||
|  | 
 | ||||||
|  | String.prototype.capitalize = function(){ | ||||||
|  | 	return this[0].toUpperCase() + this.slice(1) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | var getAnimationFrame = (window.requestAnimationFrame | ||||||
|  | 		|| window.webkitRequestAnimationFrame  | ||||||
|  | 		|| window.mozRequestAnimationFrame | ||||||
|  | 		|| window.oRequestAnimationFrame | ||||||
|  | 		|| window.msRequestAnimationFrame | ||||||
|  | 		|| function(callback){  | ||||||
|  | 			setTimeout(callback, 1000/60)  | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | var cancelAnimationFrame = (window.cancelRequestAnimationFrame  | ||||||
|  | 		|| window.webkitCancelAnimationFrame  | ||||||
|  | 		|| window.webkitCancelRequestAnimationFrame  | ||||||
|  | 		|| window.mozCancelRequestAnimationFrame | ||||||
|  | 		|| window.oCancelRequestAnimationFrame | ||||||
|  | 		|| window.msCancelRequestAnimationFrame | ||||||
|  | 		|| clearTimeout) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /********************************************************************** | ||||||
|  | * vim:set ts=4 sw=4 :                                                */ | ||||||
							
								
								
									
										326
									
								
								ui/lib/keyboard.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										326
									
								
								ui/lib/keyboard.js
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,326 @@ | |||||||
|  | /********************************************************************** | ||||||
|  | *  | ||||||
|  | * | ||||||
|  | * | ||||||
|  | **********************************************************************/ | ||||||
|  | 
 | ||||||
|  | //var DEBUG = DEBUG != null ? DEBUG : true
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*********************************************************************/ | ||||||
|  | 
 | ||||||
|  | // Neither _SPECIAL_KEYS nor _KEY_CODES are meant for direct access, use
 | ||||||
|  | // toKeyName(<code>) and toKeyCode(<name>) for a more uniform access.
 | ||||||
|  | //
 | ||||||
|  | // NOTE: these are un-shifted ASCII key names rather than actual key 
 | ||||||
|  | // 		code translations.
 | ||||||
|  | // NOTE: ASCII letters (capital) are not present because they actually 
 | ||||||
|  | // 		match their key codes and are accessible via:
 | ||||||
|  | // 			String.fromCharCode(<code>) or <letter>.charCodeAt(0)
 | ||||||
|  | // NOTE: the lower case letters are accessible by adding 32 to the 
 | ||||||
|  | // 		capital key code.
 | ||||||
|  | // NOTE: don't understand why am I the one who has to write this...
 | ||||||
|  | var _SPECIAL_KEYS = { | ||||||
|  | 	// Special Keys...
 | ||||||
|  | 	9:		'Tab',		33:		'PgUp',		45:		'Ins',		 | ||||||
|  | 	13:		'Enter',	34:		'PgDown',	46:		'Del',		 | ||||||
|  | 	16:		'Shift',	35:		'End',		80:		'Backspace', | ||||||
|  | 	17:		'Ctrl',		36:		'Home',		91:		'Win',		 | ||||||
|  | 	18:		'Alt',		37:		'Left',		93:		'Menu',		 | ||||||
|  | 	20:		'Caps Lock',38:		'Up',	  | ||||||
|  | 	27:		'Esc',		39:		'Right',   | ||||||
|  | 	32:		'Space',	40:		'Down',   | ||||||
|  | 
 | ||||||
|  | 	// Function Keys...
 | ||||||
|  | 	112:	'F1',		116:	'F5',		120:	'F9',  | ||||||
|  | 	113:	'F2',		117:	'F6',		121:	'F10', | ||||||
|  | 	114:	'F3',		118:	'F7',		122:	'F11', | ||||||
|  | 	115:	'F4',		119:	'F8',		123:	'F12', | ||||||
|  | 
 | ||||||
|  | 	// Number row..
 | ||||||
|  | 	49: '1',	50: '2',	51: '3',	52: '4',	53: '5', | ||||||
|  | 	54: '6', 	55: '7',	56: '8',	57: '9',	48: '0', | ||||||
|  | 
 | ||||||
|  | 	// Punctuation...
 | ||||||
|  | 	// top row...
 | ||||||
|  | 	192: '`',		/* Numbers */		189: '-',	187: '=', | ||||||
|  | 	// right side of keyboard...
 | ||||||
|  | 				219: '[',	221: ']',	220: '\\', | ||||||
|  | 				186: ';',	222: '\'', | ||||||
|  | 	188: ',',	190: '.',	191: '/', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var _KEY_CODES = {} | ||||||
|  | for(var k in _SPECIAL_KEYS){ | ||||||
|  | 	_KEY_CODES[_SPECIAL_KEYS[k]] = k | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // XXX some keys look really wrong...
 | ||||||
|  | function toKeyName(code){ | ||||||
|  | 	// check for special keys...
 | ||||||
|  | 	var k = _SPECIAL_KEYS[code] | ||||||
|  | 	if(k != null){ | ||||||
|  | 		return k | ||||||
|  | 	} | ||||||
|  | 	// chars...
 | ||||||
|  | 	k = String.fromCharCode(code) | ||||||
|  | 	if(k != ''){ | ||||||
|  | 		//return k.toLowerCase()
 | ||||||
|  | 		return k | ||||||
|  | 	} | ||||||
|  | 	return null | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function toKeyCode(c){ | ||||||
|  | 	if(c in _KEY_CODES){ | ||||||
|  | 		return _KEY_CODES[c] | ||||||
|  | 	} | ||||||
|  | 	return c.charCodeAt(0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // if set to false the event handlers will always return false...
 | ||||||
|  | var KEYBOARD_HANDLER_PROPAGATE = true | ||||||
|  | 
 | ||||||
|  | /* Basic key binding format: | ||||||
|  |  * | ||||||
|  |  * { | ||||||
|  |  * 		<css-selector>: { | ||||||
|  |  *			// meta-data used to generate user docs/help/config
 | ||||||
|  |  * 			title: <text>, | ||||||
|  |  * 			doc: <text>, | ||||||
|  |  * | ||||||
|  |  *			// this defines the list of keys to ignore by the handler.
 | ||||||
|  |  *			// NOTE: use "*" to ignore all keys other than explicitly 
 | ||||||
|  |  *			// 		defined in the current section.
 | ||||||
|  |  * 			ignore: <ignored-keys> | ||||||
|  |  * | ||||||
|  |  * 			<key-def> : <callback>, | ||||||
|  |  * | ||||||
|  |  * 			<key-def> : { | ||||||
|  |  * 				'default': <callback>, | ||||||
|  |  *				// a modifier can be any single modifier, like shift or a 
 | ||||||
|  |  *				// combination of modifers like 'ctrl+shift', given in order 
 | ||||||
|  |  *				// of priority.
 | ||||||
|  |  *				// supported modifiers are (in order of priority):
 | ||||||
|  |  *				//	- ctrl
 | ||||||
|  |  *				//	- alt
 | ||||||
|  |  *				//	- shift
 | ||||||
|  |  * 				<modifer>: [...], | ||||||
|  |  * 				... | ||||||
|  |  * 			}, | ||||||
|  |  * | ||||||
|  |  * 			<key-def> : [ | ||||||
|  |  *				// this can be any type of handler except for an alias...
 | ||||||
|  |  * 				<handler>,  | ||||||
|  |  * 				<doc> | ||||||
|  |  * 			], | ||||||
|  |  * | ||||||
|  |  *			// alias...
 | ||||||
|  |  * 			<key-def-a> : <key-def-b>, | ||||||
|  |  * | ||||||
|  |  *			... | ||||||
|  |  * 		}, | ||||||
|  |  * | ||||||
|  |  * 		... | ||||||
|  |  * } | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * <key-def> can be: | ||||||
|  |  * 	- explicit key code, e.g. 65 | ||||||
|  |  * 	- key name, if present in _SPECIAL_KEYS, e.g. Enter | ||||||
|  |  * 	- key char (uppercase), as is returned by String.fromCharCode(...) e.g. A | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * NOTE: to rest what to use as <key-def> use toKeyCode(..) / toKeyName(..). | ||||||
|  |  * NOTE: all fields are optional. | ||||||
|  |  * NOTE: if a handler explicitly returns false then that will break the  | ||||||
|  |  * 		event propagation chain and exit the handler. | ||||||
|  |  * 		i.e. no other matching handlers will be called. | ||||||
|  |  * NOTE: a <css-selector> is used as a predicate to select a section to  | ||||||
|  |  * 		use. if multiple selectors match something then multiple sections  | ||||||
|  |  * 		will be resolved in order of occurrence. | ||||||
|  |  * | ||||||
|  |  * XXX might need to add meta information to generate sensible help... | ||||||
|  |  */ | ||||||
|  | function makeKeyboardHandler(keybindings, unhandled){ | ||||||
|  | 	if(unhandled == null){ | ||||||
|  | 		//unhandled = function(){return false}
 | ||||||
|  | 		unhandled = function(){return KEYBOARD_HANDLER_PROPAGATE} | ||||||
|  | 	} | ||||||
|  | 	return function(evt){ | ||||||
|  | 		var did_handling = false | ||||||
|  | 		var res = null | ||||||
|  | 		for(var mode in keybindings){ | ||||||
|  | 			if($(mode).length > 0){ | ||||||
|  | 				var bindings = keybindings[mode] | ||||||
|  | 
 | ||||||
|  | 				var key = evt.keyCode | ||||||
|  | 				var chr = toKeyName(evt.keyCode) | ||||||
|  | 				// normalize the modifiers...
 | ||||||
|  | 				var modifers = evt.ctrlKey ? 'ctrl' : '' | ||||||
|  | 				modifers += evt.altKey ? (modifers != '' ? '+alt' : 'alt') : '' | ||||||
|  | 				modifers += evt.shiftKey ? (modifers != '' ? '+shift' : 'shift') : '' | ||||||
|  | 
 | ||||||
|  | 				if(chr in bindings){ | ||||||
|  | 					var handler = bindings[chr] | ||||||
|  | 				} else { | ||||||
|  | 					var handler = bindings[key] | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// alias...
 | ||||||
|  | 				while (typeof(handler) == typeof(123) || typeof(handler) == typeof('str')) { | ||||||
|  | 					if(handler in bindings){ | ||||||
|  | 						// XXX need to take care of that we can always be a number or a string...
 | ||||||
|  | 						handler = bindings[handler] | ||||||
|  | 					} else if(typeof(h) == typeof(1)) { | ||||||
|  | 						handler = bindings[toKeyName(handler)] | ||||||
|  | 					} else { | ||||||
|  | 						handler = bindings[toKeyCode(handler)] | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				// no handler...
 | ||||||
|  | 				if(handler == null){ | ||||||
|  | 					// if something is ignored then just breakout and stop handling...
 | ||||||
|  | 					if(bindings.ignore == '*'  | ||||||
|  | 							|| bindings.ignore != null && bindings.ignore.indexOf(key) != -1){ | ||||||
|  | 						res = res == null ? true : res | ||||||
|  | 						did_handling = true | ||||||
|  | 						// ignoring a key will stop processing it...
 | ||||||
|  | 						break | ||||||
|  | 					} | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 				// Array, lisp style with docs...
 | ||||||
|  | 				// XXX for some odd reason typeof([]) == typeof({})!!!
 | ||||||
|  | 				if(typeof(handler) == typeof([]) && handler.constructor.name == 'Array'){ | ||||||
|  | 					// we do not care about docs here, so just get the handler...
 | ||||||
|  | 					handler = handler[0] | ||||||
|  | 				} | ||||||
|  | 				// complex handler...
 | ||||||
|  | 				if(typeof(handler) == typeof({})){ | ||||||
|  | 					var callback = handler[modifers] | ||||||
|  | 					if(callback == null){ | ||||||
|  | 						callback = handler['default'] | ||||||
|  | 					} | ||||||
|  | 					if(callback != null){ | ||||||
|  | 						res = callback() | ||||||
|  | 						did_handling = true | ||||||
|  | 						continue | ||||||
|  | 					} | ||||||
|  | 				} else { | ||||||
|  | 					// simple callback...
 | ||||||
|  | 					//res = handler(evt) 
 | ||||||
|  | 					res = handler()  | ||||||
|  | 					// if the handler explicitly returned false break out...
 | ||||||
|  | 					if(res === false){ | ||||||
|  | 						// XXX is this corrent???
 | ||||||
|  | 						// XXX should we just break here instead of return...
 | ||||||
|  | 						return KEYBOARD_HANDLER_PROPAGATE ? res : null | ||||||
|  | 					} | ||||||
|  | 					did_handling = true | ||||||
|  | 					continue | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if(!did_handling){ | ||||||
|  | 			// key is unhandled by any modes...
 | ||||||
|  | 			return unhandled(key) | ||||||
|  | 		} else { | ||||||
|  | 			// XXX should we handle multiple hits???
 | ||||||
|  | 			return KEYBOARD_HANDLER_PROPAGATE&&res?true:false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // helper...
 | ||||||
|  | function doc(text, func){ | ||||||
|  | 	func.doc = text | ||||||
|  | 	return func | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Build structure ready for conversion to HTML help. | ||||||
|  | * Structure: | ||||||
|  | * 	{ | ||||||
|  | * 		<section-title>: { | ||||||
|  | * 			doc: ... | ||||||
|  | * | ||||||
|  | * 			<handler-doc>: <keys-spec> | ||||||
|  | * 			... | ||||||
|  | * 		} | ||||||
|  | * 	} | ||||||
|  | * | ||||||
|  | * 	<keys-spec> 	- list of key names. | ||||||
|  | * | ||||||
|  | */ | ||||||
|  | function getKeyHandler(key, keybindings){ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | function buildKeyindingsHelp(keybindings){ | ||||||
|  | 	var res = {} | ||||||
|  | 
 | ||||||
|  | 	for(var selector in keybindings){ | ||||||
|  | 		var section = keybindings[selector] | ||||||
|  | 		var title = section.title == null ? selector : section.title | ||||||
|  | 		var res_sec = { | ||||||
|  | 			doc: section.doc, | ||||||
|  | 		} | ||||||
|  | 		res.title = res_sec | ||||||
|  | 
 | ||||||
|  | 		for(var k in section){ | ||||||
|  | 			// handler...
 | ||||||
|  | 			var h = section[k] | ||||||
|  | 			var doc | ||||||
|  | 			var key = typeof(k) == typeof(1) ? toKeyName(k) : k | ||||||
|  | 
 | ||||||
|  | 			// an alias...
 | ||||||
|  | 			while(typeof(h) == typeof(1) || typeof(h) == typeof('s')){ | ||||||
|  | 				if(h in section){ | ||||||
|  | 					// XXX need to take care of that we can always be a number or a string...
 | ||||||
|  | 					h = section[h] | ||||||
|  | 				} else if(typeof(h) == typeof(1)) { | ||||||
|  | 					h = section[toKeyName(h)] | ||||||
|  | 				} else { | ||||||
|  | 					h = section[toKeyCode(h)] | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// no handler... 
 | ||||||
|  | 			if(h == null){ | ||||||
|  | 				doc = 'Nothing'				 | ||||||
|  | 
 | ||||||
|  | 			// a handler with doc (array)...
 | ||||||
|  | 			} else if(typeof(h) == typeof([]) && handler.constructor.name == 'Array'){ | ||||||
|  | 				doc = h[1] | ||||||
|  | 
 | ||||||
|  | 			// complex handler (object)...
 | ||||||
|  | 			} else if(typeof(h) == typeof({})){ | ||||||
|  | 				// XXX
 | ||||||
|  | 				 | ||||||
|  | 
 | ||||||
|  | 			// simple handler (function)...
 | ||||||
|  | 			} else { | ||||||
|  | 				doc = h.doc != null ? h.doc : h | ||||||
|  | 
 | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// push the actual data...
 | ||||||
|  | 			if(doc in res_sec){ | ||||||
|  | 				res_sec[doc].push(key) | ||||||
|  | 			} else { | ||||||
|  | 				res_sec[doc] = [key] | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return res | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /********************************************************************** | ||||||
|  | * vim:set ts=4 sw=4 :                                                */ | ||||||
							
								
								
									
										513
									
								
								ui/lib/scroller.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										513
									
								
								ui/lib/scroller.js
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,513 @@ | |||||||
|  | /********************************************************************** | ||||||
|  | *  | ||||||
|  | * General Swipe/Scroll handler lib  | ||||||
|  | * | ||||||
|  | * | ||||||
|  | **********************************************************************/ | ||||||
|  | 
 | ||||||
|  | //var DEBUG = DEBUG != null ? DEBUG : true
 | ||||||
|  | 
 | ||||||
|  | // click threshold in pixels, if the distance between start and end is 
 | ||||||
|  | // less than this, the whole event is considered a click and not a 
 | ||||||
|  | // drag/swipe...
 | ||||||
|  | var CLICK_THRESHOLD = 10 | ||||||
|  | 
 | ||||||
|  | // if the amount of time to wait bteween start and end is greater than
 | ||||||
|  | // this the event is considered a long click.
 | ||||||
|  | // NOTE: this will not auto-fire the event, the user MUST release first.
 | ||||||
|  | var LONG_CLICK_THRESHOLD = 400 | ||||||
|  | 
 | ||||||
|  | // the maximum amount of time between clicks to count them together.
 | ||||||
|  | // NOTE: if multi-clicks are disabled this has no effect.
 | ||||||
|  | // NOTE: this is reset by the timeout explicitly set in the handler...
 | ||||||
|  | // NOTE: this is the timeout between two consecutive clicks and not the 
 | ||||||
|  | // 		total.
 | ||||||
|  | // NOTE: if multiple clicks are enabled this will introduce a lag after
 | ||||||
|  | // 		each click (while we wait for the next), so keep this as small 
 | ||||||
|  | // 		as possible, but not too small as to rush the user too much.
 | ||||||
|  | var MULTI_CLICK_TIMEOUT = 200 | ||||||
|  | 
 | ||||||
|  | // the amount of time between finger releases.
 | ||||||
|  | // NOTE: when this is passed all the fingers released before are ignored.
 | ||||||
|  | var MULTITOUCH_RELEASE_THRESHOLD = 100 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /*********************************************************************/ | ||||||
|  | 
 | ||||||
|  | // Scroll handler
 | ||||||
|  | //
 | ||||||
|  | // This will take two elements a root (container) and a scrolled (first
 | ||||||
|  | // child of the container) and implement drag-scrolling of the scrolled
 | ||||||
|  | // within the root.
 | ||||||
|  | //
 | ||||||
|  | // This calls the following callbacks if they are defined.
 | ||||||
|  | // 	- preCallback (unset)
 | ||||||
|  | // 	- scrollCallback (unset)
 | ||||||
|  | // 	- postCallback (set to postScrollCallback)
 | ||||||
|  | //
 | ||||||
|  | // See scroller.options for configuration.
 | ||||||
|  | //
 | ||||||
|  | //
 | ||||||
|  | // XXX add a resonable cancel scheme...
 | ||||||
|  | // 		... something similar to touch threshold but bigger...
 | ||||||
|  | // XXX setup basic styles for the contained element...
 | ||||||
|  | // XXX revise...
 | ||||||
|  | // XXX test on other devices...
 | ||||||
|  | function makeScrollHandler(root, config){ | ||||||
|  | 	root = $(root) | ||||||
|  | 
 | ||||||
|  | 	// local data...
 | ||||||
|  | 	var ignoring = false | ||||||
|  | 	// XXX this and scroller.state are redundent...
 | ||||||
|  | 	var scrolling = false | ||||||
|  | 	var touch = false | ||||||
|  | 	var touches = 0 | ||||||
|  | 	var max_dx = 0 | ||||||
|  | 	var max_dy = 0 | ||||||
|  | 
 | ||||||
|  | 	var cancelThreshold, scrolled | ||||||
|  | 		// initial state...
 | ||||||
|  | 		, start_x, start_y, start_t | ||||||
|  | 		// previous state...
 | ||||||
|  | 		, prev_x, prev_y, prev_t | ||||||
|  | 		// current state...
 | ||||||
|  | 		, x, y, t | ||||||
|  | 		// state delta...
 | ||||||
|  | 		, dx, dy, dt | ||||||
|  | 
 | ||||||
|  | 		, shift | ||||||
|  | 		, scale | ||||||
|  | 		//, bounds
 | ||||||
|  | 
 | ||||||
|  | 	function startMoveHandler(evt){ | ||||||
|  | 		var options = scroller.options | ||||||
|  | 		// ignore...
 | ||||||
|  | 		if(options.ignoreElements  | ||||||
|  | 				&& $(evt.target).closest(options.ignoreElements).length > 0 | ||||||
|  | 				|| scroller.state == 'paused'){ | ||||||
|  | 			ignoring = true | ||||||
|  | 			return | ||||||
|  | 		} else { | ||||||
|  | 			ignoring = false | ||||||
|  | 		} | ||||||
|  | 		if(event.touches != null){ | ||||||
|  | 			touch = true | ||||||
|  | 		} | ||||||
|  | 		cancelThreshold = options.scrollCancelThreshold | ||||||
|  | 		touches = touch ? event.touches.length : 1 | ||||||
|  | 		// if we are already touching then just skip on this...
 | ||||||
|  | 		// XXX test this...
 | ||||||
|  | 		if(touches > 1){ | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 		prev_t = event.timeStamp || Date.now(); | ||||||
|  | 		start_t = prev_t | ||||||
|  | 		/* | ||||||
|  | 		if(options.autoCancelEvents){ | ||||||
|  | 			bounds = { | ||||||
|  | 				left: options.eventBounds, | ||||||
|  | 				right: root.width() - options.eventBounds, | ||||||
|  | 				top: options.eventBounds, | ||||||
|  | 				bottom: root.height() - options.eventBounds  | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		*/ | ||||||
|  | 		scrolled = $(root.children()[0]) | ||||||
|  | 		setTransitionDuration(scrolled, 0) | ||||||
|  | 		// XXX these two are redundant...
 | ||||||
|  | 		scrolling = true | ||||||
|  | 		scroller.state = 'scrolling' | ||||||
|  | 		// XXX do we need to pass something to this?
 | ||||||
|  | 		options.preCallback && options.preCallback() | ||||||
|  | 
 | ||||||
|  | 		shift = getElementShift(scrolled) | ||||||
|  | 		scale = getElementScale(scrolled) | ||||||
|  | 		// get the user coords...
 | ||||||
|  | 		prev_x = touch ? event.touches[0].pageX : evt.clientX | ||||||
|  | 		start_x = prev_x | ||||||
|  | 		prev_y = touch ? event.touches[0].pageY : evt.clientY | ||||||
|  | 		start_y = prev_y | ||||||
|  | 
 | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// XXX try and make this adaptive to stay ahead of the lags...
 | ||||||
|  | 	// NOTE: this does not support limiting the scroll, might be done in
 | ||||||
|  | 	// 		the future though.
 | ||||||
|  | 	// 		The way to go about this is to track scrolled size in the 
 | ||||||
|  | 	// 		callback...
 | ||||||
|  | 	function moveHandler(evt){ | ||||||
|  | 		if(ignoring){ | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		var options = scroller.options | ||||||
|  | 		evt.preventDefault() | ||||||
|  | 		t = event.timeStamp || Date.now(); | ||||||
|  | 		// get the user coords...
 | ||||||
|  | 		x = touch ? event.touches[0].pageX : evt.clientX | ||||||
|  | 		y = touch ? event.touches[0].pageY : evt.clientY | ||||||
|  | 		touches = touch ? event.touches.length : 1 | ||||||
|  | 
 | ||||||
|  | 		/* | ||||||
|  | 		// XXX needs testing...
 | ||||||
|  | 		// XXX do we need to account for scrollDisabled here???
 | ||||||
|  | 		// check scroll bounds...
 | ||||||
|  | 		if(bounds != null){ | ||||||
|  | 			if(options.hScroll && (x <= bounds.left || x >= bounds.right) | ||||||
|  | 					|| options.vScroll && (y <= bounds.top || y >= bounds.bottom)){ | ||||||
|  | 				// XXX cancel the touch event and trigger the end handler...
 | ||||||
|  | 				return endMoveHandler(evt) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		*/ | ||||||
|  | 
 | ||||||
|  | 		// do the actual scroll...
 | ||||||
|  | 		if(!options.scrollDisabled && scrolling){ | ||||||
|  | 			if(options.hScroll){ | ||||||
|  | 				shift.left += x - prev_x | ||||||
|  | 			} | ||||||
|  | 			if(options.vScroll){ | ||||||
|  | 				shift.top += y - prev_y | ||||||
|  | 			} | ||||||
|  | 			setElementTransform(scrolled, shift, scale) | ||||||
|  | 
 | ||||||
|  | 			// XXX these should be done every time the event is caught or 
 | ||||||
|  | 			// 		just while scrolling?
 | ||||||
|  | 			dx = x - prev_x | ||||||
|  | 			dy = y - prev_y | ||||||
|  | 			max_dx += Math.abs(dx) | ||||||
|  | 			max_dy += Math.abs(dy) | ||||||
|  | 			dt = t - prev_t | ||||||
|  | 			prev_x = x | ||||||
|  | 			prev_y = y | ||||||
|  | 			prev_t = t | ||||||
|  | 
 | ||||||
|  | 			// XXX do we need to pass something to this?
 | ||||||
|  | 			options.scrollCallback && options.scrollCallback() | ||||||
|  | 		} | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	function endMoveHandler(evt){ | ||||||
|  | 		t = event.timeStamp || Date.now(); | ||||||
|  | 		touches = touch ? event.touches.length : 0 | ||||||
|  | 		if(ignoring){ | ||||||
|  | 			if(touches == 0){ | ||||||
|  | 				ignoring = false | ||||||
|  | 			} | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		var options = scroller.options | ||||||
|  | 
 | ||||||
|  | 		// XXX get real transition duration...
 | ||||||
|  | 		scroller.resetTransitions() | ||||||
|  | 
 | ||||||
|  | 		x = touch ? event.changedTouches[0].pageX : evt.clientX | ||||||
|  | 		y = touch ? event.changedTouches[0].pageY : evt.clientY | ||||||
|  | 		// check if we are canceling...
 | ||||||
|  | 		if(cancelThreshold  | ||||||
|  | 				&& Math.abs(start_x-x) < cancelThreshold  | ||||||
|  | 				&& Math.abs(start_y-y) < cancelThreshold | ||||||
|  | 				&& (max_dx > cancelThreshold  | ||||||
|  | 					|| max_dy > cancelThreshold)){ | ||||||
|  | 			scroller.state = 'canceling' | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		var data = { | ||||||
|  | 			orig_event: evt,  | ||||||
|  | 			scroller: scroller,  | ||||||
|  | 			speed: { | ||||||
|  | 				x: dx/dt, | ||||||
|  | 				y: dy/dt | ||||||
|  | 			}, | ||||||
|  | 			distance: { | ||||||
|  | 				x: start_x-x, | ||||||
|  | 				y: start_y-y | ||||||
|  | 			}, | ||||||
|  | 			duration: t-start_t,  | ||||||
|  | 			// current touches...
 | ||||||
|  | 			touches: touches, | ||||||
|  | 			clicks: null, | ||||||
|  | 		} | ||||||
|  | 		// XXX stop only if no fingers are touching or let the callback decide...
 | ||||||
|  | 		if(options.postCallback  | ||||||
|  | 				// XXX revise this....
 | ||||||
|  | 				&& options.postCallback(data) === false  | ||||||
|  | 				|| touches == 0){ | ||||||
|  | 			// cleanup and stop...
 | ||||||
|  | 			touch = false | ||||||
|  | 			scrolling = false | ||||||
|  | 			scroller.state = 'waiting' | ||||||
|  | 			scrolled = null  | ||||||
|  | 			//bounds = null
 | ||||||
|  | 			max_dx = 0 | ||||||
|  | 			max_dy = 0 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	var scroller = { | ||||||
|  | 		options: { | ||||||
|  | 			// if one of these is false, it will restrict scrolling in 
 | ||||||
|  | 			// that direction. hScroll for horizontal and vScroll for 
 | ||||||
|  | 			// vertical.
 | ||||||
|  | 			// NOTE: to disable scroll completely use scrollDisabled, see
 | ||||||
|  | 			// 		below for details.
 | ||||||
|  | 			hScroll: true, | ||||||
|  | 			vScroll: true, | ||||||
|  | 
 | ||||||
|  | 			// this will disable scroll.
 | ||||||
|  | 			// NOTE: this is the same as setting both vScroll and hScroll 
 | ||||||
|  | 			// 		to false, but can be set and reset without affecting 
 | ||||||
|  | 			// 		the actual settings individually...
 | ||||||
|  | 			// NOTE: this takes priority over hScroll/vScroll.
 | ||||||
|  | 			scrollDisabled: false, | ||||||
|  | 
 | ||||||
|  | 			// sets the default transition settings while not scrolling...
 | ||||||
|  | 			transitionDuration: 200, | ||||||
|  | 			transitionEasing: 'ease', | ||||||
|  | 
 | ||||||
|  | 			// items to be ignored by the scroller...
 | ||||||
|  | 			// this is a jQuery compatible selector.
 | ||||||
|  | 			ignoreElements: '.noScroll', | ||||||
|  | 			// this is the side of the rectangle in px, if the user moves
 | ||||||
|  | 			// out of it, and then returns back, the action will get cancelled.
 | ||||||
|  | 			// i.e. the callback will get called with the "cancelling" state.
 | ||||||
|  | 			scrollCancelThreshold: 100, | ||||||
|  | 
 | ||||||
|  | 			/* | ||||||
|  | 			// XXX padding within the target element moving out of which 
 | ||||||
|  | 			// 		will cancell the action...
 | ||||||
|  | 			// XXX needs testing...
 | ||||||
|  | 			autoCancelEvents: false, | ||||||
|  | 			eventBounds: 5, | ||||||
|  | 			*/ | ||||||
|  | 
 | ||||||
|  | 			// callback to be called when the user first touches the screen...
 | ||||||
|  | 			preCallback: null, | ||||||
|  | 			// callback to be called when a scroll step is done...
 | ||||||
|  | 			scrollCallback: null, | ||||||
|  | 			// callback to be called when the user lifts a finger/mouse.
 | ||||||
|  | 			// NOTE: this may happen before the scroll is done, for instance
 | ||||||
|  | 			// 		when one of several fingers participating in the action
 | ||||||
|  | 			// 		gets lifted.
 | ||||||
|  | 			// NOTE: if this returns false explicitly, this will stop scrolling.
 | ||||||
|  | 			postCallback: postScrollCallback, | ||||||
|  | 
 | ||||||
|  | 			// These are used by the default callback...
 | ||||||
|  | 			//
 | ||||||
|  | 			// if true then doubleClick and multiClick events will get 
 | ||||||
|  | 			// triggered.
 | ||||||
|  | 			// NOTE: this will introduce a lag needed to wait for next 
 | ||||||
|  | 			// 		clicks in a group.
 | ||||||
|  | 			// NOTE: when this is false, shortClick is triggered for every 
 | ||||||
|  | 			// 		single click separately.
 | ||||||
|  | 			enableMultiClicks: false, | ||||||
|  | 			// NOTE: if these are null, respective values from the env will
 | ||||||
|  | 			// 		be used.
 | ||||||
|  | 			clickThreshold: null, | ||||||
|  | 			longClickThreshold: null, | ||||||
|  | 			multiClickTimeout: null, | ||||||
|  | 			multitouchTimeout: null, | ||||||
|  | 		}, | ||||||
|  | 		state: 'stopped', | ||||||
|  | 		root: root, | ||||||
|  | 
 | ||||||
|  | 		start: function(){ | ||||||
|  | 			if(this.state == 'paused'){ | ||||||
|  | 				this.state = 'waiting' | ||||||
|  | 			} else { | ||||||
|  | 				this.state = 'waiting' | ||||||
|  | 
 | ||||||
|  | 				// NOTE: if we bind both touch and mouse events, on touch devices they 
 | ||||||
|  | 				//		might start interfering with each other...
 | ||||||
|  | 				if('ontouchmove' in window){ | ||||||
|  | 					root | ||||||
|  | 						.on('touchstart', startMoveHandler) | ||||||
|  | 						.on('touchmove', moveHandler)  | ||||||
|  | 						.on('touchend', endMoveHandler) | ||||||
|  | 						.on('touchcancel', endMoveHandler) | ||||||
|  | 				} else { | ||||||
|  | 					root | ||||||
|  | 						.on('mousedown', startMoveHandler)  | ||||||
|  | 						.on('mousemove', moveHandler)  | ||||||
|  | 						.on('mouseup', endMoveHandler)  | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			return this | ||||||
|  | 		}, | ||||||
|  | 		// XXX test...
 | ||||||
|  | 		pause: function(){ | ||||||
|  | 			this.state = 'paused' | ||||||
|  | 			return this | ||||||
|  | 		}, | ||||||
|  | 		stop: function(){ | ||||||
|  | 			if('ontouchmove' in window){ | ||||||
|  | 				root | ||||||
|  | 					.off('touchstart', startMoveHandler) | ||||||
|  | 					.off('touchmove', moveHandler)  | ||||||
|  | 					.off('touchend', endMoveHandler) | ||||||
|  | 					.off('touchcancel', endMoveHandler) | ||||||
|  | 			} else { | ||||||
|  | 				root | ||||||
|  | 					.off('mousedown', startMoveHandler)  | ||||||
|  | 					.off('mousemove', moveHandler)  | ||||||
|  | 					.off('mouseup', endMoveHandler)  | ||||||
|  | 			} | ||||||
|  | 			this.state = 'stopped' | ||||||
|  | 			return this | ||||||
|  | 		}, | ||||||
|  | 		resetTransitions: function(){ | ||||||
|  | 			var scrolled = this.root.children().first() | ||||||
|  | 			setTransitionDuration(scrolled, this.options.transitionDuration) | ||||||
|  | 			setTransitionEasing(scrolled, this.options.transitionEasing) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// merge the config with the defaults...
 | ||||||
|  | 	if(config != null){ | ||||||
|  | 		$.extend(scroller.options, config) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return scroller | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // default callback...
 | ||||||
|  | //
 | ||||||
|  | // This will provide support for the following events on the scroll root
 | ||||||
|  | // element:
 | ||||||
|  | // 		- scrollCancelled
 | ||||||
|  | //
 | ||||||
|  | // 		- shortClick
 | ||||||
|  | // 		- doubleClick
 | ||||||
|  | // 		- multiClick
 | ||||||
|  | // 			this will store the number of clicks in data.clicks
 | ||||||
|  | // 		- longClick
 | ||||||
|  | //
 | ||||||
|  | // 		- swipeLeft
 | ||||||
|  | // 		- swipeRight
 | ||||||
|  | // 		- swipeUp
 | ||||||
|  | // 		- swipeDown
 | ||||||
|  | //
 | ||||||
|  | // 		- screenReleased
 | ||||||
|  | //
 | ||||||
|  | // NOTE: data.touches passed to the event is the number of touches 
 | ||||||
|  | // 		released within the multitouchTimeout.
 | ||||||
|  | // 		this differs from what postScrollCallback actually gets in the 
 | ||||||
|  | // 		same field when it receives the scroll data object.
 | ||||||
|  | // XXX add generic snap
 | ||||||
|  | // XXX add generic inertial scroll 
 | ||||||
|  | // 		...see jli.js/animateElementTo for a rough implementation
 | ||||||
|  | // XXX test multiple touches...
 | ||||||
|  | function postScrollCallback(data){ | ||||||
|  | 	var scroller = data.scroller | ||||||
|  | 	var options = scroller.options | ||||||
|  | 	var root = scroller.root | ||||||
|  | 	var clickThreshold = options.clickThreshold || CLICK_THRESHOLD | ||||||
|  | 	var longClickThreshold = options.longClickThreshold || LONG_CLICK_THRESHOLD | ||||||
|  | 	var multitouchTimeout = options.multitouchTimeout || MULTITOUCH_RELEASE_THRESHOLD | ||||||
|  | 	var enableMultiClicks = options.enableMultiClicks | ||||||
|  | 	var multiClickTimeout = options.multiClickTimeout || MULTI_CLICK_TIMEOUT | ||||||
|  | 
 | ||||||
|  | 	var now = Date.now(); | ||||||
|  | 
 | ||||||
|  | 	// cancel event...
 | ||||||
|  | 	if(scroller.state == 'canceling'){ | ||||||
|  | 		return root.trigger('scrollCancelled', data) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// handle multiple touches...
 | ||||||
|  | 	if(data.touches > 0){ | ||||||
|  | 		var then = scroller._last_touch_release | ||||||
|  | 		if(then == null || now - then < multitouchTimeout){ | ||||||
|  | 			if(scroller._touches == null){ | ||||||
|  | 				scroller._touches = 1 | ||||||
|  | 			} else { | ||||||
|  | 				scroller._touches += 1 | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			scroller._touches = null | ||||||
|  | 		} | ||||||
|  | 		// wait for the next touch release...
 | ||||||
|  | 		scroller._last_touch_release = now | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	// calculate how many touches did participate... 
 | ||||||
|  | 	} else { | ||||||
|  | 		data.touches = scroller._touches ? scroller._touches + 1 : 1 | ||||||
|  | 		scroller._last_touch_release = null | ||||||
|  | 		scroller._touches = null | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// clicks, double-clicks, multi-clicks and long-clicks...
 | ||||||
|  | 	if(Math.max( | ||||||
|  | 			Math.abs(data.distance.x),  | ||||||
|  | 			Math.abs(data.distance.y)) < clickThreshold){ | ||||||
|  | 		if(data.duration > longClickThreshold){ | ||||||
|  | 			return root.trigger('longClick', data)  | ||||||
|  | 		} | ||||||
|  | 		if(!enableMultiClicks){ | ||||||
|  | 			return root.trigger('shortClick', data) | ||||||
|  | 
 | ||||||
|  | 		} else { | ||||||
|  | 			// count the clicks so far...
 | ||||||
|  | 			if(scroller._clicks == null){ | ||||||
|  | 				scroller._clicks = 1 | ||||||
|  | 			} else { | ||||||
|  | 				scroller._clicks += 1 | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// kill any previous waits...
 | ||||||
|  | 			if(scroller._click_timeout_id != null){ | ||||||
|  | 				clearTimeout(scroller._click_timeout_id) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// wait for the next click...
 | ||||||
|  | 			scroller._click_timeout_id = setTimeout(function(){ | ||||||
|  | 				var clicks = scroller._clicks | ||||||
|  | 				data.clicks = clicks | ||||||
|  | 				if(clicks == 1){ | ||||||
|  | 					root.trigger('shortClick', data) | ||||||
|  | 				} else if(clicks == 2){ | ||||||
|  | 					root.trigger('doubleClick', data) | ||||||
|  | 				} else { | ||||||
|  | 					root.trigger('multiClick', data) | ||||||
|  | 				}					 | ||||||
|  | 				scroller._clicks = null | ||||||
|  | 				scroller._click_timeout_id = null | ||||||
|  | 			}, multiClickTimeout) | ||||||
|  | 
 | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// swipes...
 | ||||||
|  | 	// XXX might be a good idea to chain these with swipe and screenReleased
 | ||||||
|  | 	if(Math.abs(data.distance.x) > Math.abs(data.distance.y)){ | ||||||
|  | 		if(data.distance.x <= -clickThreshold && root.data('events').swipeLeft){ | ||||||
|  | 			return root.trigger('swipeLeft', data) | ||||||
|  | 		} else if(data.distance.x >= clickThreshold && root.data('events').swipeRight){ | ||||||
|  | 			return root.trigger('swipeRight', data) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if(data.distance.y <= -clickThreshold && root.data('events').swipeUp){ | ||||||
|  | 			return root.trigger('swipeUp', data) | ||||||
|  | 		} else if(data.distance.y >= clickThreshold && root.data('events').swipeDown){ | ||||||
|  | 			return root.trigger('swipeDown', data) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// this is triggered if no swipes were handled...
 | ||||||
|  | 	return root.trigger('screenReleased', data) | ||||||
|  | }  | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /********************************************************************** | ||||||
|  | * vim:set ts=4 sw=4 :                                                */ | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user