| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | * 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
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var POOL_SIZE = 64 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var DEFAULT_TRANSITION_DURATION = 200 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX this affects only the innertial part, not setCurrentPage...
 | 
					
						
							|  |  |  | var USE_TRANSITIONS_FOR_ANIMATION = false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var USE_TRANSFORM = true | 
					
						
							|  |  |  | var USE_3D_TRANSFORM = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | // 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) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | var getElementOrigin = makeCSSVendorAttrGetter( | 
					
						
							|  |  |  | 		'transformOrigin', | 
					
						
							|  |  |  | 		{top: 0, left: 0}, | 
					
						
							|  |  |  | 		function(data){ | 
					
						
							| 
									
										
										
										
											2015-11-16 11:31:15 +03:00
										 |  |  | 			var res = /(-?[0-9.]*(px|%)) (-?[0-9.]*(px|%))/.exec(data) | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | 			return { | 
					
						
							|  |  |  | 				left: res[1].slice(-2) == 'px' ? parseFloat(res[1]) : res[1], | 
					
						
							|  |  |  | 				top: res[3].slice(-2) == 'px' ? parseFloat(res[3]) : res[3], | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | // 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]) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | var getElementOffset = makeCSSVendorAttrGetter( | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		'transform', | 
					
						
							|  |  |  | 		{left: 0, top: 0}, | 
					
						
							|  |  |  | 		function(data){ | 
					
						
							| 
									
										
										
										
											2015-11-16 11:31:15 +03:00
										 |  |  | 			var res = /(translate\(|matrix\([^,]*,[^,]*,[^,]*,[^,]*,)([^,]*),([^\)]*)\)/.exec(data) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 			return { | 
					
						
							|  |  |  | 				left: parseFloat(res[2]), | 
					
						
							|  |  |  | 				top: parseFloat(res[3]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var getElementTransitionDuration = makeCSSVendorAttrGetter( | 
					
						
							|  |  |  | 		'transitionDuration',  | 
					
						
							|  |  |  | 		DEFAULT_TRANSITION_DURATION,  | 
					
						
							|  |  |  | 		parseInt) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-17 03:54:29 +04:00
										 |  |  | // Get relative offset...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This is like jQuery.offset() but takes into account:
 | 
					
						
							|  |  |  | //	- scale
 | 
					
						
							|  |  |  | //	- origin
 | 
					
						
							|  |  |  | //	- actual relative offset
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // point can be:
 | 
					
						
							|  |  |  | //	- {
 | 
					
						
							|  |  |  | //		top: <top>, 
 | 
					
						
							|  |  |  | //		left: <left>,
 | 
					
						
							|  |  |  | //		[scale: 'screen'|'elem'|<scale>,]
 | 
					
						
							|  |  |  | //	  }
 | 
					
						
							|  |  |  | //	- 'origin' (default)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This expects: 
 | 
					
						
							|  |  |  | // 	- the block is directly nested in the container
 | 
					
						
							|  |  |  | // 	- the block can be scaled
 | 
					
						
							|  |  |  | // 	- the block has an origin set
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | function getRelativeOffset(container, block, point){ | 
					
						
							|  |  |  | 	point = point == null ? {} : point | 
					
						
							|  |  |  | 	var l = point.left | 
					
						
							|  |  |  | 	var t = point.top | 
					
						
							|  |  |  | 	var scale = point.scale | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// get the input data...
 | 
					
						
							|  |  |  | 	var s = getElementScale(block) | 
					
						
							|  |  |  | 	var o = getElementOrigin(block) | 
					
						
							|  |  |  | 	// get only the value we need...
 | 
					
						
							|  |  |  | 	var W = container.width() | 
					
						
							|  |  |  | 	var H = container.height() | 
					
						
							|  |  |  | 	// we need this to make everything relative to the container...
 | 
					
						
							|  |  |  | 	var co = container.offset() | 
					
						
							|  |  |  | 	var offset = getElementOffset(block) | 
					
						
							|  |  |  | 	var bo = block.offset() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	scale = scale == 'screen' ? 1  | 
					
						
							|  |  |  | 		: scale == 'elem' ? s | 
					
						
							| 
									
										
										
										
											2014-10-17 08:21:58 +04:00
										 |  |  | 		: scale == null ? s | 
					
						
							| 
									
										
										
										
											2014-10-17 03:54:29 +04:00
										 |  |  | 		: scale | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// normalize the l,t to element scale...
 | 
					
						
							|  |  |  | 	if(l != null && t != null){ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// get only the value we need...
 | 
					
						
							|  |  |  | 		// NOTE: width and height are used to calculate the correction
 | 
					
						
							|  |  |  | 		//		due to origin/scale...
 | 
					
						
							|  |  |  | 		var w = block.width() | 
					
						
							|  |  |  | 		var h = block.height() | 
					
						
							|  |  |  | 		o = { | 
					
						
							|  |  |  | 			// target offset scale...
 | 
					
						
							|  |  |  | 			top: t*scale  | 
					
						
							|  |  |  | 				// set origin to top left corner of element (compensate
 | 
					
						
							|  |  |  | 				// for scaling)...
 | 
					
						
							|  |  |  | 				+ (h - h*s) / (h / o.top),  | 
					
						
							|  |  |  | 			left: l*scale  | 
					
						
							|  |  |  | 				+ (w - w*s) / (w / o.left), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return { | 
					
						
							|  |  |  | 		top: offset.top + (H/2 - offset.top) - o.top, | 
					
						
							|  |  |  | 		left: offset.left + (W/2 - offset.left) - o.left, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | // NOTE: at this point this works only on the X axis...
 | 
					
						
							|  |  |  | function setElementTransform(elem, offset, scale, duration){ | 
					
						
							|  |  |  | 	elem = $(elem) | 
					
						
							| 
									
										
										
										
											2014-11-05 03:26:29 +03:00
										 |  |  | 	//var t3d = USE_3D_TRANSFORM ? 'translateZ(0)' : ''
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 	var t3d = USE_3D_TRANSFORM ? 'translate3d(0,0,0)' : '' | 
					
						
							| 
									
										
										
										
											2015-12-19 09:34:22 +03:00
										 |  |  | 	//var translate = USE_3D_TRANSFORM ? 'translate3d' : 'translate'
 | 
					
						
							|  |  |  | 	var translate = 'translate' | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if(offset == null){ | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | 		offset = getElementOffset(elem) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 	// 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){ | 
					
						
							| 
									
										
										
										
											2015-12-19 09:34:22 +03:00
										 |  |  | 		var transform = translate+'('+  | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 				Math.round(offset.left) +'px, '+ | 
					
						
							| 
									
										
										
										
											2015-12-19 09:34:22 +03:00
										 |  |  | 				//Math.round(offset.top) +'px'+ (USE_3D_TRANSFORM && ', 0px' || '') +') '
 | 
					
						
							|  |  |  | 				Math.round(offset.top) +'px) ' | 
					
						
							|  |  |  | 			+'scale('+ scale +') ' | 
					
						
							|  |  |  | 			+ t3d | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		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 { | 
					
						
							| 
									
										
										
										
											2015-12-19 09:34:22 +03:00
										 |  |  | 		//var transform = translate+'(0px, 0px'+ (USE_3D_TRANSFORM && ', 0px' || '') +') '
 | 
					
						
							|  |  |  | 		var transform = translate+'(0px, 0px) ' | 
					
						
							|  |  |  | 			+'scale('+ scale +') ' | 
					
						
							|  |  |  | 			+ t3d | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Run a function controllably in an animation frame
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: we do not need to make this run several callbacks as the 
 | 
					
						
							|  |  |  | // 		browser already does this and will do the loop faster...
 | 
					
						
							|  |  |  | function animationFrameRunner(func){ | 
					
						
							|  |  |  | 	var next | 
					
						
							|  |  |  | 	var _nop = function(){ return this } | 
					
						
							|  |  |  | 	var frame | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if(this === window){ | 
					
						
							|  |  |  | 		self = new animationFrameRunner | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		self = this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	self.func = func | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var _tick = function(){ | 
					
						
							|  |  |  | 		func(Date.now()) | 
					
						
							|  |  |  | 		frame = getAnimationFrame(next) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// main user interface...
 | 
					
						
							|  |  |  | 	var start = function(){ | 
					
						
							|  |  |  | 		next = _tick | 
					
						
							|  |  |  | 		this.start = _nop | 
					
						
							|  |  |  | 		this.stop = stop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// start things up...
 | 
					
						
							|  |  |  | 		// NOTE: we are not calling _tick here directly to avoid stray,
 | 
					
						
							|  |  |  | 		// 		off-frame call to func...
 | 
					
						
							|  |  |  | 		frame = getAnimationFrame(next) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var stop = function(){ | 
					
						
							|  |  |  | 		if(frame != null){ | 
					
						
							|  |  |  | 			cancelAnimationFrame(frame) | 
					
						
							|  |  |  | 			frame = null | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		next = _nop | 
					
						
							|  |  |  | 		this.start = start | 
					
						
							|  |  |  | 		this.stop = _nop  | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// setup the ticker in stopped state...
 | 
					
						
							|  |  |  | 	stop.call(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return self | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX make this a drop-in replacement for setElementTransform...
 | 
					
						
							|  |  |  | // XXX cleanup, still flacky...
 | 
					
						
							|  |  |  | function animateElementTo(elem, to, duration, easing, speed, callback, 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 | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | 		var from = getElementOffset(elem) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		var cur = { | 
					
						
							|  |  |  | 			top: from.top, | 
					
						
							|  |  |  | 			left: from.left | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var dist = { | 
					
						
							|  |  |  | 			top: to.top - from.top, | 
					
						
							|  |  |  | 			left: to.left - from.left, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// XXX are we using this...
 | 
					
						
							|  |  |  | 		elem.animating = true | 
					
						
							|  |  |  | 		elem.next_frame = null | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// remember step start position...
 | 
					
						
							|  |  |  | 		var s_t = cur.top | 
					
						
							|  |  |  | 		var s_l = cur.left | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		function animate(){ | 
					
						
							|  |  |  | 			// prevent running animations till next call of animateElementTo(..)
 | 
					
						
							|  |  |  | 			if(elem.next_frame === false){ | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			var t = Date.now() | 
					
						
							|  |  |  | 			// end of the animation...
 | 
					
						
							|  |  |  | 			if(t >= then){ | 
					
						
							|  |  |  | 				setElementTransform(elem, to) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if(!elem.animating){ | 
					
						
							|  |  |  | 				// XXX jittery...
 | 
					
						
							|  |  |  | 				setElementTransform(elem, cur) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// remember step start position...
 | 
					
						
							|  |  |  | 			s_t = cur.top | 
					
						
							|  |  |  | 			s_l = cur.left | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// animate a step with speed...
 | 
					
						
							|  |  |  | 			if(speed != null){ | 
					
						
							|  |  |  | 				// NOTE: these are almost identical, they are inlined 
 | 
					
						
							|  |  |  | 				// 		for speed...
 | 
					
						
							|  |  |  | 				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 | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				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 | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// liner animate...
 | 
					
						
							|  |  |  | 			} 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) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			callback != null && callback({ | 
					
						
							|  |  |  | 				x: cur.left - s_l, | 
					
						
							|  |  |  | 				y: cur.top - s_t, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// sched next frame...
 | 
					
						
							|  |  |  | 			elem.next_frame = getAnimationFrame(animate) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		animate() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function stopAnimation(elem){ | 
					
						
							|  |  |  | 	if(elem.next_frame){ | 
					
						
							|  |  |  | 		cancelAnimationFrame(elem.next_frame) | 
					
						
							|  |  |  | 		elem.next_frame = false | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX account for other transitions...
 | 
					
						
							| 
									
										
										
										
											2014-10-18 03:36:46 +04:00
										 |  |  | // XXX make a sync version...
 | 
					
						
							| 
									
										
										
										
											2015-12-19 09:34:22 +03:00
										 |  |  | function setElementOffset(elem, l, t, scale){ | 
					
						
							|  |  |  | 	return setElementTransform(elem, [l, t], scale) | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | function setElementScale(elem, scale){ | 
					
						
							|  |  |  | 	return setElementTransform(elem, null, scale) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function setElementOrigin(elem, x, y, z){ | 
					
						
							|  |  |  | 	x = x == null ? '50%' : x | 
					
						
							|  |  |  | 	y = y == null ? '50%' : y | 
					
						
							|  |  |  | 	z = z == null ? '0' : z | 
					
						
							|  |  |  | 	var value = x +' '+ y +' '+ z | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return $(elem).css({ | 
					
						
							|  |  |  | 		'transform-origin': value,  | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | 		'-o-transform-origin':  value, | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		'-ms-transform-origin':  value, | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | 		'-moz-transform-origin':  value, | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		'-webkit-transform-origin':  value, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-18 03:36:46 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | // a sync version of setElementOrigin(..), this will not trigger transforms...
 | 
					
						
							|  |  |  | function setElementOriginSync(elem, x, y, z){ | 
					
						
							|  |  |  | 	x = x == null ? '50%' : x | 
					
						
							|  |  |  | 	y = y == null ? '50%' : y | 
					
						
							|  |  |  | 	z = z == null ? '0' : z | 
					
						
							|  |  |  | 	var value = x +' '+ y +' '+ z | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	elem = $(elem) | 
					
						
							|  |  |  | 	var e = elem[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	e.style.display = 'none' | 
					
						
							|  |  |  | 	// now kick the browser into recognition of our changes NOW ;)
 | 
					
						
							|  |  |  | 	getComputedStyle(e).display | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	e.style['-o-transform-origin'] =  value | 
					
						
							|  |  |  | 	e.style['-ms-transform-origin'] =  value | 
					
						
							|  |  |  | 	e.style['-moz-transform-origin'] =  value | 
					
						
							|  |  |  | 	e.style['-webkit-transform-origin'] =  value | 
					
						
							|  |  |  | 	e.style['transform-origin'] = value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	e.style.display = '' | 
					
						
							|  |  |  | 	getComputedStyle(e).display | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return $(elem) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | // this is like setElementOrigin(..) but will compensate for element 
 | 
					
						
							|  |  |  | // shift when scaled...
 | 
					
						
							|  |  |  | // NOTE: this will work only of translate is used for positioning...
 | 
					
						
							| 
									
										
										
										
											2015-12-25 08:47:27 +03:00
										 |  |  | function shiftOriginTo(elem, l, t, scale){ | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | 	var o = getElementOrigin(elem) | 
					
						
							| 
									
										
										
										
											2015-12-25 08:47:27 +03:00
										 |  |  | 	var scale = scale || getElementScale(elem) | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | 	var offset = getElementOffset(elem) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// calculate the offset change and compensate...
 | 
					
						
							|  |  |  | 	var cl = offset.left + ((o.left - o.left*scale) - (l - l*scale)) | 
					
						
							|  |  |  | 	var ct = offset.top + ((o.top - o.top*scale) - (t - t*scale)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setElementOffset(elem, cl, ct) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-10-18 03:36:46 +04:00
										 |  |  | 	return setElementOriginSync(elem, l+'px', t+'px') | 
					
						
							| 
									
										
										
										
											2014-10-15 09:06:10 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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)) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /************************************************** Deferred utils ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Deferred worker pool...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		makeDeferredPool([size][, paused]) -> pool
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This will create and return a pooled queue of deferred workers.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // The pool can be in one of the folowing states:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	- filling
 | 
					
						
							|  |  |  | // 		This state prevents .depleted() from triggering until the pool 
 | 
					
						
							|  |  |  | // 		exits the filling state.
 | 
					
						
							|  |  |  | // 		This helps us to prevent premature depletion of the pool in 
 | 
					
						
							|  |  |  | // 		cases where the queue is depleted faster than it is being filled.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	- paused
 | 
					
						
							|  |  |  | // 		This state prevents any new queued workers from starting.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Public interface:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.enqueue(obj, func, args) -> deferred
 | 
					
						
							|  |  |  | // 			Add a worker to queue.
 | 
					
						
							|  |  |  | // 			If the pool is not filled and not paused, this will run the
 | 
					
						
							|  |  |  | // 			worker right away.
 | 
					
						
							|  |  |  | // 			If the pool is full the worker is added to queue (FIFO) and
 | 
					
						
							|  |  |  | // 			ran in its turn.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.dropQueue() -> pool
 | 
					
						
							|  |  |  | // 			Drop the queued workers.
 | 
					
						
							|  |  |  | // 			NOTE: this will not stop the already running workers.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //		.filling()
 | 
					
						
							|  |  |  | //			Enter the filling state
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //		.doneFilling()
 | 
					
						
							|  |  |  | //			Exit the filling state
 | 
					
						
							|  |  |  | //			NOTE: this will trigger .depleted() if at the time of call
 | 
					
						
							|  |  |  | //				both the pool and queue are empty.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.pause() -> pool
 | 
					
						
							|  |  |  | // 			Pause the queue.
 | 
					
						
							|  |  |  | // 			NOTE: this also has a second form: .pause(func), see below.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.resume() -> pool
 | 
					
						
							|  |  |  | // 			Restart the queue.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //		.isFilling() -> bool
 | 
					
						
							|  |  |  | //			Test if the pool is being filled -- filling state.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.isRunning() -> bool
 | 
					
						
							|  |  |  | // 			Test if any workers are running in the pool.
 | 
					
						
							|  |  |  | // 			NOTE: this will return false ONLY when the pool is empty.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.isPaused() -> bool
 | 
					
						
							|  |  |  | // 			Test if pool is in a paused state.
 | 
					
						
							|  |  |  | // 			NOTE: some workers may sill be finishing up so if you want
 | 
					
						
							|  |  |  | // 					to test whether any workers are still running use
 | 
					
						
							|  |  |  | // 					.isRunning()
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Event handler/callback registration:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.on(evt, func) -> pool
 | 
					
						
							|  |  |  | // 			Register a handler (func) for an event (evt).
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.off(evt[, func]) -> pool
 | 
					
						
							|  |  |  | // 			Remove a handler (func) form and event (evt).
 | 
					
						
							|  |  |  | // 			NOTE: if func is omitted, remove all handlers from the given
 | 
					
						
							|  |  |  | // 					event...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.progress(func) -> pool
 | 
					
						
							|  |  |  | // 			Register a progress handler.
 | 
					
						
							|  |  |  | // 			The handler is called after each worker is done and will get
 | 
					
						
							|  |  |  | // 			passed:
 | 
					
						
							|  |  |  | // 				- workers done count
 | 
					
						
							|  |  |  | // 				- workers total count
 | 
					
						
							|  |  |  | // 			Short hand for:
 | 
					
						
							|  |  |  | // 				.on('progress', func) -> pool
 | 
					
						
							|  |  |  | // 			NOTE: the total number of workers can change as new workers
 | 
					
						
							|  |  |  | // 					are added or the queue is cleared...
 | 
					
						
							|  |  |  | // 			
 | 
					
						
							|  |  |  | // 		.fail(func) -> pool
 | 
					
						
							|  |  |  | // 			Register a worker fail handler.
 | 
					
						
							|  |  |  | // 			The handler is called when a worker goes into the fail state.
 | 
					
						
							|  |  |  | // 			This will get passed:
 | 
					
						
							|  |  |  | // 				- workers done count
 | 
					
						
							|  |  |  | // 				- workers total count
 | 
					
						
							|  |  |  | // 			Short hand for:
 | 
					
						
							|  |  |  | // 				.on('fail', func) -> pool
 | 
					
						
							|  |  |  | // 			NOTE: this will not stop the execution of other handlers.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.pause(func) -> pool
 | 
					
						
							|  |  |  | // 			Register a pause handler.
 | 
					
						
							|  |  |  | // 			This handler is called after the last worker finishes when 
 | 
					
						
							|  |  |  | // 			the queue is paused.
 | 
					
						
							|  |  |  | // 			Short hand for:
 | 
					
						
							|  |  |  | // 				.on('progress', func) -> pool
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.resume(func) -> pool
 | 
					
						
							|  |  |  | // 			Short hand for:
 | 
					
						
							|  |  |  | // 				.on('resume', func) -> pool
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		.depleted(func) -> pool
 | 
					
						
							|  |  |  | // 			Register a depleted pool handler.
 | 
					
						
							|  |  |  | // 			The handler will get called when the queue and pool are empty
 | 
					
						
							|  |  |  | // 			(depleted) and the last worker is done.
 | 
					
						
							|  |  |  | // 			Short hand for:
 | 
					
						
							|  |  |  | // 				.on('deplete', func) -> pool
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX should this be an object or a factory???
 | 
					
						
							|  |  |  | function makeDeferredPool(size, paused){ | 
					
						
							|  |  |  | 	size = size == null ? POOL_SIZE : size | 
					
						
							|  |  |  | 	size = size < 0 ? 1  | 
					
						
							|  |  |  | 		: size > 512 ? 512 | 
					
						
							|  |  |  | 		: size | 
					
						
							|  |  |  | 	paused = paused == null ? false : paused | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var Pool = { | 
					
						
							|  |  |  | 		pool: [], | 
					
						
							|  |  |  | 		queue: [], | 
					
						
							|  |  |  | 		size: size, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// XXX do we need to hide or expose them and use their API???
 | 
					
						
							|  |  |  | 		_event_handlers: { | 
					
						
							|  |  |  | 			deplete: $.Callbacks(), | 
					
						
							|  |  |  | 			progress: $.Callbacks(), | 
					
						
							|  |  |  | 			pause: $.Callbacks(), | 
					
						
							|  |  |  | 			resume: $.Callbacks(), | 
					
						
							|  |  |  | 			fail: $.Callbacks() | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_paused: paused, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Run a worker...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// This will:
 | 
					
						
							|  |  |  | 	// 	- create and add a worker to the pool, which will:
 | 
					
						
							|  |  |  | 	// 		- run an element from the queue
 | 
					
						
							|  |  |  | 	// 		- remove self from pool
 | 
					
						
							|  |  |  | 	// 		- if the pool is not full, create another worker (call 
 | 
					
						
							|  |  |  | 	// 		  ._run(..)) else exit
 | 
					
						
							|  |  |  | 	// 		- call ._fill() to replenish the pool
 | 
					
						
							|  |  |  | 	Pool._run = function(deferred, func, args){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		var pool = this.pool | 
					
						
							|  |  |  | 		var pool_size = this.size | 
					
						
							|  |  |  | 		var queue = this.queue | 
					
						
							|  |  |  | 		var run = this._run | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// run an element from the queue...
 | 
					
						
							|  |  |  | 		var worker = func.apply(null, args) | 
					
						
							|  |  |  | 		pool.push(worker) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// NOTE: this is explicitly after the pool push to avoid the 
 | 
					
						
							|  |  |  | 		// 		possible race condition of the worker exiting and 
 | 
					
						
							|  |  |  | 		// 		triggering .always(..) before being added to the pool...
 | 
					
						
							|  |  |  | 		worker | 
					
						
							|  |  |  | 			.always(function(){ | 
					
						
							|  |  |  | 				// prepare to remove self from pool...
 | 
					
						
							|  |  |  | 				var i = pool.indexOf(this) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-11-19 22:26:59 +03:00
										 |  |  | 				Pool._event_handlers.progress.fire(pool.length - pool.len, pool.length + queue.length) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// remove self from queue...
 | 
					
						
							|  |  |  | 				delete pool[i] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// shrink the pool if it's overfilled...
 | 
					
						
							|  |  |  | 				// i.e. do not pop another worker and let the "thread" die.
 | 
					
						
							| 
									
										
										
										
											2014-11-19 22:26:59 +03:00
										 |  |  | 				if(pool.len > pool_size){ | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 					// remove self...
 | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// pause the queue -- do not do anything else...
 | 
					
						
							|  |  |  | 				if(that._paused == true){ | 
					
						
							|  |  |  | 					// if pool is empty fire the pause event...
 | 
					
						
							| 
									
										
										
										
											2014-11-19 22:26:59 +03:00
										 |  |  | 					if(pool.len == 0){ | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 						Pool._event_handlers.pause.fire() | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// get the next queued worker...
 | 
					
						
							|  |  |  | 				var next = queue.splice(0, 1)[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// run the next worker if it exists...
 | 
					
						
							|  |  |  | 				if(next != null){ | 
					
						
							|  |  |  | 					run.apply(that, next) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// empty queue AND empty pool mean we are done...
 | 
					
						
							| 
									
										
										
										
											2014-11-19 22:26:59 +03:00
										 |  |  | 				} else if(pool.len == 0){ | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 					var l = pool.length | 
					
						
							|  |  |  | 					// NOTE: potential race condition -- something can be
 | 
					
						
							|  |  |  | 					// 		pushed to pool just before it's "compacted"...
 | 
					
						
							|  |  |  | 					pool.length = 0 | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 					if(!that._filling){ | 
					
						
							|  |  |  | 						that._event_handlers.deplete.fire(l) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// keep the pool full...
 | 
					
						
							|  |  |  | 				that._fill() | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.fail(function(){ | 
					
						
							| 
									
										
										
										
											2014-11-19 22:26:59 +03:00
										 |  |  | 				Pool._event_handlers.fail.fire(pool.length - pool.len, pool.length + queue.length) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 				deferred.reject.apply(deferred, arguments) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.progress(function(){ | 
					
						
							|  |  |  | 				deferred.notify.apply(deferred, arguments) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.done(function(){ | 
					
						
							|  |  |  | 				deferred.resolve.apply(deferred, arguments) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return worker | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Fill the pool...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	Pool._fill = function(){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		var pool_size = this.size | 
					
						
							|  |  |  | 		var run = this._run | 
					
						
							| 
									
										
										
										
											2014-11-19 22:26:59 +03:00
										 |  |  | 		var l = this.pool.len | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if(this._paused != true  | 
					
						
							|  |  |  | 				&& l < pool_size  | 
					
						
							|  |  |  | 				&& this.queue.length > 0){ | 
					
						
							|  |  |  | 			this.queue.splice(0, pool_size - l) | 
					
						
							|  |  |  | 				.forEach(function(e){ | 
					
						
							|  |  |  | 					run.apply(that, e) | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Public methods...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Add a worker to queue...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	Pool.enqueue = function(func){ | 
					
						
							|  |  |  | 		var deferred = $.Deferred() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// add worker to queue...
 | 
					
						
							| 
									
										
										
										
											2018-11-12 23:04:00 +03:00
										 |  |  | 		this.queue.push([deferred, func, [...arguments].slice(1)]) | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// start work if we have not already...
 | 
					
						
							|  |  |  | 		this._fill() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		//return this
 | 
					
						
							|  |  |  | 		return deferred | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Drop the queued workers...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this will not stop the running workers...
 | 
					
						
							|  |  |  | 	// XXX should this return the pool or the dropped queue???
 | 
					
						
							|  |  |  | 	Pool.dropQueue = function(){ | 
					
						
							|  |  |  | 		this.queue.splice(0, this.queue.length) | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Filling state...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// When this mode is set, it will prevent the queue from triggering
 | 
					
						
							|  |  |  | 	// the depleated action until .doneFilling() is called...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// This is to prevent the pool depleting before the queue is filled
 | 
					
						
							|  |  |  | 	// in the case of tasks ending faster than they are added...
 | 
					
						
							|  |  |  | 	Pool.filling = function(){ | 
					
						
							|  |  |  | 		this._filling = true | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	Pool.doneFilling = function(){ | 
					
						
							|  |  |  | 		delete this._filling | 
					
						
							|  |  |  | 		// trigger depleted if we are empty...
 | 
					
						
							| 
									
										
										
										
											2014-11-19 22:26:59 +03:00
										 |  |  | 		if(this.pool.len == 0 && this.queue.length == 0){ | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 			that._event_handlers.deplete.fire(l) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	Pool.isFilling = function(){ | 
					
						
							|  |  |  | 		return this._filling == true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Paused state...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this will not directly cause .isRunning() to return false 
 | 
					
						
							|  |  |  | 	// 		as this will not directly spot all workers, it will just 
 | 
					
						
							|  |  |  | 	// 		pause the queue and the workers that have already started
 | 
					
						
							|  |  |  | 	// 		will keep running until they are done, and only when the 
 | 
					
						
							|  |  |  | 	// 		pool is empty will the .isRunning() return false.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX test...
 | 
					
						
							|  |  |  | 	Pool.pause = function(func){ | 
					
						
							|  |  |  | 		if(func == null){ | 
					
						
							|  |  |  | 			this._paused = true | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this.on('pause', func) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX test...
 | 
					
						
							|  |  |  | 	Pool.resume = function(func){ | 
					
						
							|  |  |  | 		if(func == null){ | 
					
						
							|  |  |  | 			this._paused = false | 
					
						
							|  |  |  | 			this._event_handlers['resume'].forEach(function(f){ f() }) | 
					
						
							|  |  |  | 			this._fill() | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this.on('resume', func) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Pool.isPaused = function(){ | 
					
						
							|  |  |  | 		return this._paused | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	Pool.isRunning = function(){ | 
					
						
							| 
									
										
										
										
											2014-11-19 22:26:59 +03:00
										 |  |  | 		return this.pool.len > 0 | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Generic event handlers...
 | 
					
						
							|  |  |  | 	Pool.on = function(evt, handler){ | 
					
						
							|  |  |  | 		this._event_handlers[evt].add(handler) | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// NOTE: if this is not given a handler, it will clear all handlers 
 | 
					
						
							|  |  |  | 	// 		from the given event...
 | 
					
						
							|  |  |  | 	Pool.off = function(evt, handler){ | 
					
						
							|  |  |  | 		if(handler != null){ | 
					
						
							|  |  |  | 			this._event_handlers[evt].remove(handler) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this._event_handlers[evt].empty() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Register a queue depleted handler...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// This occurs when a populated queue is depleted and the last worker
 | 
					
						
							|  |  |  | 	// is done.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this is similar to jQuery.Deferred().done(..) but differs in
 | 
					
						
							|  |  |  | 	// 		that the pool can fill up and get depleted more than once, 
 | 
					
						
							|  |  |  | 	// 		thus, the handlers may get called more than once per pool 
 | 
					
						
							|  |  |  | 	// 		life...
 | 
					
						
							|  |  |  | 	// NOTE: it is recommended to fill the queue faster than the workers
 | 
					
						
							|  |  |  | 	// 		finish, as this may get called after last worker is done and
 | 
					
						
							|  |  |  | 	// 		the next is queued...
 | 
					
						
							|  |  |  | 	Pool.depleted = function(func){ | 
					
						
							|  |  |  | 		return this.on('deplete', func) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Deferred compatibility...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: the key difference between this and the deferred is that this
 | 
					
						
							|  |  |  | 	// 		does not have memory and can get called multiple times...
 | 
					
						
							|  |  |  | 	// XXX is this correct???
 | 
					
						
							|  |  |  | 	//Pool.done = Pool.depleted
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Register queue progress handler...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// This occurs after each worker is done.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// handler will be passed:
 | 
					
						
							|  |  |  | 	// 	- the pool object
 | 
					
						
							|  |  |  | 	// 	- workers done
 | 
					
						
							|  |  |  | 	// 	- total workers (done + queued)
 | 
					
						
							|  |  |  | 	Pool.progress = function(func){ | 
					
						
							|  |  |  | 		return this.on('progress', func) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Register worker fail handler...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	Pool.fail = function(func){ | 
					
						
							|  |  |  | 		return this.on('fail', func) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return Pool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**************************************************** JS utilities ***/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get screen dpi...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This will calculate the value and save it to screen.dpi
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // if force is true this will re-calculate the value.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this needs the body loaded to work...
 | 
					
						
							|  |  |  | // NOTE: this may depend on page zoom...
 | 
					
						
							|  |  |  | // NOTE: yes, this is a hack, but since we have no other reliable way to
 | 
					
						
							|  |  |  | // 		do this...
 | 
					
						
							|  |  |  | function getDPI(force){ | 
					
						
							|  |  |  | 	if(screen.dpi == null || force){ | 
					
						
							|  |  |  | 		var e = $('<div id="inch">') | 
					
						
							|  |  |  | 			.css({ | 
					
						
							|  |  |  | 				position: 'absolute', | 
					
						
							|  |  |  | 				width: '1in', | 
					
						
							|  |  |  | 				left: '-100%', | 
					
						
							|  |  |  | 				top: '-100%' | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			.appendTo($('body')) | 
					
						
							|  |  |  | 		var res = e.width() | 
					
						
							|  |  |  | 		e.remove() | 
					
						
							|  |  |  | 		screen.dpi = res | 
					
						
							|  |  |  | 		return res	 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		return screen.dpi | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | // XXX is this correct???
 | 
					
						
							|  |  |  | $(getDPI) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // return 1, -1, or 0 depending on sign of x
 | 
					
						
							|  |  |  | function sign(x){ | 
					
						
							|  |  |  | 	return (x > 0) - (x < 0) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var getAnimationFrame = (window.requestAnimationFrame | 
					
						
							|  |  |  | 		|| window.webkitRequestAnimationFrame  | 
					
						
							|  |  |  | 		|| window.mozRequestAnimationFrame | 
					
						
							|  |  |  | 		|| window.oRequestAnimationFrame | 
					
						
							|  |  |  | 		|| window.msRequestAnimationFrame | 
					
						
							|  |  |  | 		|| function(callback){  | 
					
						
							|  |  |  | 			setTimeout(callback, 1000/60)  | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-16 04:49:52 +03:00
										 |  |  | var cancelAnimationFrame = (window.cancelAnimationFrame  | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		|| window.webkitCancelAnimationFrame  | 
					
						
							| 
									
										
										
										
											2016-05-16 04:49:52 +03:00
										 |  |  | 		|| window.mozCancelAnimationFrame | 
					
						
							|  |  |  | 		|| window.oCancelAnimationFrame | 
					
						
							|  |  |  | 		|| window.msCancelAnimationFrame | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 		|| clearTimeout) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function logCalls(func, logger){ | 
					
						
							|  |  |  | 	var that = this | 
					
						
							|  |  |  | 	var _func = function(){ | 
					
						
							|  |  |  | 		logger(func, arguments) | 
					
						
							|  |  |  | 		return func.apply(that, arguments) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	_func.name = func.name | 
					
						
							|  |  |  | 	return _func | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function assyncCall(func){ | 
					
						
							|  |  |  | 	var that = this | 
					
						
							|  |  |  | 	var _func = function(){ | 
					
						
							|  |  |  | 		var res = $.Deferred() | 
					
						
							|  |  |  | 		setTimeout(function(){ | 
					
						
							|  |  |  | 			res.resolve(func.apply(that, arguments)) | 
					
						
							|  |  |  | 		}, 0) | 
					
						
							|  |  |  | 		return res | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	_func.name = func.name | 
					
						
							|  |  |  | 	return _func | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-12 04:29:55 +03:00
										 |  |  | // Quote a string and convert to RegExp to match self literally.
 | 
					
						
							|  |  |  | function quoteRegExp(str){ | 
					
						
							|  |  |  | 	return str.replace(/([\.\\\/\(\)\[\]\$\*\+\-\{\}\@\^\&\?\<\>])/g, '\\$1') | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-07-20 02:25:36 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							|  |  |  | * vim:set ts=4 sw=4 :                                                */ |