Timing_JQuery_Util

try to take over the world!

ეს სკრიპტი არ უნდა იყოს პირდაპირ დაინსტალირებული. ეს ბიბლიოთეკაა, სხვა სკრიპტებისთვის უნდა ჩართეთ მეტა-დირექტივაში // @require https://update.greasyfork.org/scripts/404470/812557/Timing_JQuery_Util.js.

/**
 * code from http: //creativecouple.github.io/jquery-timing/jquery-timing.js
 * timing.jquery.js
 * 
 * JavaScript functions for waiting / repeating / stopping jQuery actions.
 * 
 * This code is published under the MIT License (MIT).
 * http://www.opensource.org/licenses/mit-license.php
 * 
 * For examples, reference, and other information see
 * http://creativecouple.github.com/jquery-timing/
 * 
 * @author CreativeCouple
 * @author Peter Liske
 * @copyright (c) 2011 by CreativeCouple
 * @see http://creativecouple.github.com/jquery-timing/
 */

(function(jQuery, window){
	/**
	 * object to store statically invoked threads
	 */
	var THREAD_GROUPS = {},
	
	/**
	 * unique timing identifier for different purposes
	 */
	tuid = 1,

	/**
	 * remember original core function $.each()
	 */
	originalEach = jQuery.fn.each,

	/**
	 * remember original core function $.on() (or $.bind())
	 */
	originalOn = jQuery.fn.on || jQuery.fn.bind,
	
	/**
	 * remember original core function $.off() (or $.unbind())
	 */
	originalOff = jQuery.fn.off || jQuery.fn.unbind,
	
	/**
	 * .until() and .all() have special meanings
	 */
	loopEndMethods = {};
	
	function sameOrNextJQuery(before, after) {
		after = jQuery(after);
		after.prevObject = before;
		var i = before.length;
		if (i !== after.length) {
			return after;
		}
		while (i--) {
			if (before[i] !== after[i]) {
				return after;
			}
		}
		return before;
	}
	
	function loopCounts(loops) {
		var ret = [], i = loops.length;
		while (i--) {
			ret[i] = loops[i]._count;
		}
		return ret;
	}
	
	/**
	 * Initialize a new timed invocation chain.
	 * 
	 * @author CreativeCouple
	 * @author Peter Liske
	 * 
	 * @param context initial context
	 * @param methodStack linked list of methods that has been or will be filled by someone else 
	 * @param ongoingLoops optional arguments for callback parameters
	 * @param onStepCallback function to call on each step
	 * @returns the timed invocation chain method
	 */
	function createTimedInvocationChain(context, methodStack, ongoingLoops, onStepCallback) {
		ongoingLoops = ongoingLoops || [];
		var executionState = {
				_context: context,
				_method: methodStack
		},
		preventRecursion = false,
		method, otherExecutionState, deferred;
		
		function hookupToProxy(state, mockup){
			state._canContinue = false;
			function fire(){
				state._next = sameOrNextJQuery(state._context, state._next);
				state._canContinue = true;
				timedInvocationChain();
			}
			return typeof mockup.promise == "function" ? mockup.promise().then(fire) : mockup.then(fire, true);
		}
		
		/**
		 * Invoke all the methods currently in the timed invocation chain.
		 * 
		 * @author CreativeCouple
		 * @author Peter Liske
		 */
		function timedInvocationChain(deferredReturnValue) {
			while (!preventRecursion) try {
				// keep recursive calls away
				preventRecursion = !preventRecursion;
				// save current context state
				if (typeof onStepCallback == "function") {
					onStepCallback(jQuery.makeArray(executionState._next || executionState._context));
				}
				// leave the chain when waiting for a trigger
				if (executionState._canContinue == false) {
					break;
				}
				// check end of chain
				if (!executionState._method._name) {
					if (deferred && (!ongoingLoops.length || ongoingLoops[0]._allowPromise)) {
						// resolve any waiting promise 
						if (executionState._context && typeof executionState._context.promise == "function") {
							executionState._context.promise().then(deferred.resolve);
						} else {
							deferred.resolveWith(executionState._context);
						}
						deferred = null;
					}
					if (!ongoingLoops.length) {
						/*
						 * We've reached the end of our TIC
						 * and there is nothing left to wait for.
						 * So we can safely return the original jQuery object
						 * hence enabling instant invocation.
						 */
						return executionState._context; 
					}
					/*
					 * Now we have ongoing loops but reached the chain's end.
					 */
					otherExecutionState = ongoingLoops[0]._openEndAction && ongoingLoops[0]._openEndAction(timedInvocationChain, executionState, ongoingLoops);
					if (!otherExecutionState) {
						// if innermost loop can't help us, just leave the chain
						break;
					}
					executionState = otherExecutionState;
					continue;
				}
				// check if user tries to use a non-existing function call
				method = executionState._context && executionState._context[executionState._method._name] || loopEndMethods[executionState._method._name];
				if (!method) {					
					throw 'no such method "'+executionState._method._name+'" on object ('+executionState._context+')';
				}
				// check whether we came here triggered or not
				if (method.timing && !executionState._canContinue) {
					// prevent automatic re-trigger in case of loops
					executionState._canContinue = false;
					// handle timing method
					executionState = method.timing(timedInvocationChain, executionState, ongoingLoops, onStepCallback) || executionState;
				} else {
					if (!method.timing && !executionState._canContinue) {
						// prevent automatic re-trigger in case of loops
						executionState._next = executionState._context[executionState._method._name].apply(executionState._context, executionState._method._arguments);
						if (ongoingLoops.length && executionState._next && executionState._next instanceof PredictingProxy) {
							hookupToProxy(executionState, executionState._next);
							continue;
						}
					}
					// go to next step
					otherExecutionState = {
							_context: executionState._next,
							_method: executionState._method._next
					};
					// prevent automatic re-trigger in case of loops
					executionState._canContinue = false;
					// invoke callback method with given arguments
					if (typeof executionState._callback == "function") {
						executionState._callback.apply(executionState._context, loopCounts(ongoingLoops));
					}
					executionState = otherExecutionState;
				}
			} catch(e) {
				/*
				 * We had a runtime exception.
				 * In plain JavaScript live the chain would break now.
				 * So we do, too.
				 */
				preventRecursion = !preventRecursion;
				throw e;
			} finally {
				preventRecursion = !preventRecursion;				
			}
			return deferredReturnValue;
		};
		if (jQuery.Deferred) {
			// add .promise() method to tic
			timedInvocationChain.promise = function(type, target){
				var ret = (deferred = deferred || jQuery.Deferred()).promise(target);
				timedInvocationChain();
				return ret;
			};
		}
		return timedInvocationChain;
	}
	
	/**
	 * Create a placeholder object to collect chained method calls.
	 * 
	 * @author CreativeCouple
	 * @author Peter Liske
	 *
	 * @param context initial context
	 * @param methodStack a linked list that this placeholder will fill with call parameters 
	 * @return the placeholder object
	 */
	function PredictingProxy(context, methodStack, onStepCallback) {
		this['.methods'] = methodStack;
		this['.callback'] = onStepCallback;
		this.length = 0;
		Array.prototype.push.apply(this, jQuery.makeArray(this._ = context._ = context));
		
		for (var key in context) {
			if (!(key in PredictingProxy.prototype) && typeof context[key] == "function") {
				this[key] = extendMockupPrototype(key);
			}
		}
	}
	
	// enabling jQuery.when(tic);
	if (jQuery.Deferred) {
		PredictingProxy.prototype.promise = function(type, target) {
			if (typeof type == "object") {
				target = type;
				type = null;
			}
			return (this['.callback'] && typeof this['.callback'].promise == "function") ? this['.callback'].promise(type, target) : jQuery.Deferred().resolveWith(this).promise(target);
		};
	}
	
	/**
	 * Create and return a new placeholder function on the prototype of PredictingProxy. 
	 */
	function extendMockupPrototype(name){
		return PredictingProxy.prototype[name] = function(){
			this['.methods']._name = name;
			this['.methods']._arguments = arguments;
			this['.methods'] = this['.methods']._next = {};
			return this['.callback'] ? this['.callback'](this, name, arguments) : this;
		};
	}
	
	
	/**
	 * Create replacement methods for .bind(), .on(), .one(), .live(), and .delegate()
	 * that support chaining instead of giving a callback function.
	 */
	jQuery.each(['bind','on','one','live','delegate'], function(index, name){
		if (jQuery.fn[name]) {
			var original = jQuery.fn[name];
			jQuery.fn[name] = function(){
				var i, methodStack, placeholder, timedInvocationChain, deferred, context = this;
				for(i=0; i<arguments.length; i++) {
					if (typeof arguments[i] == "function" || (arguments[i] && typeof arguments[i] == "object") || arguments[i] === false) {
						if (arguments[i] !== jQuery) {
							// fix for jQuery 1.6 .one() + .unbind()
							if (typeof arguments[i] == "function" && jQuery.guid) {
								arguments[i].guid = arguments[i].guid || jQuery.guid++;
							}
							return original.apply(context, arguments);
						}
						break;
					}
				}
				Array.prototype.splice.call(arguments, i, 1, function(){
					timedInvocationChain = createTimedInvocationChain(context.$(this), methodStack, [{
							_count: jQuery.extend(Array.prototype.shift.apply(arguments), arguments),
							_allowPromise: true
						}], function(elements){
						placeholder.length = 0;
						Array.prototype.push.apply(placeholder, elements);
					});
					if (deferred) {
						timedInvocationChain.promise().then(deferred.resolve);
						deferred = null;
					}
					return timedInvocationChain();
				});
				function fire(){
					return timedInvocationChain ? timedInvocationChain(placeholder) : placeholder;
				}
				if (jQuery.Deferred) {					
					fire.promise = function(type, target){
						if (typeof type == "object") {
							target = type;
							type = null;
						}
						return (timedInvocationChain && !type) ? timedInvocationChain.promise(type, target) : (deferred = deferred || jQuery.Deferred()).promise(target);
					};
				}
				return placeholder = new PredictingProxy(original.apply(context, arguments), methodStack = {}, fire);
			};
		}
	});
	
	/**
	 * Create replacement method for .animate() and .load()
	 * that support chaining if $ is given as callback function.
	 */
	jQuery.each(['animate','load'], function(index, name){
		if (jQuery.fn[name]) {
			var original = jQuery.fn[name];
			jQuery.fn[name] = function(){
				while (arguments.length && arguments[arguments.length-1] == null) {
					Array.prototype.pop.apply(arguments);
				}
				if (this.length && arguments.length > 1 && arguments[arguments.length-1] === jQuery) {
					var event = '_timing'+tuid++;
					arguments[arguments.length-1] = function(){
						jQuery(this).trigger(event);
					};
					return this.each().one(event).all(original.apply(this, arguments));
				}
				return original.apply(this, arguments);
			};
		}
	});
		
	/**
	 * Define new methods .wait(), .repeat(), .join(), .then()
	 * which will always start a new TIC if invoked outside of a TIC.  
	 */
	jQuery.each(['wait','repeat','join','then'], function(index, name){
		jQuery.fn[name] = function(){
			var methodStack = {},
			placeholder = new PredictingProxy(this, methodStack, createTimedInvocationChain(this, methodStack, [], function(elements){
					placeholder.length = 0;
					Array.prototype.push.apply(placeholder, elements);
				}));
			return placeholder[name].apply(placeholder, arguments);
		};
	});
	
	/**
	 * Define to wait for joining all animation queues.
	 * 
	 * @param timedInvocationChain
	 * @param executionState
	 */
	jQuery.fn.join.timing = function(timedInvocationChain, executionState) {
		var queueName,
		promising,
		waitingElements = executionState._context.length;
		
		if (typeof executionState._method._arguments[0] == "string") {
			queueName = executionState._method._arguments[0];
			if (typeof executionState._method._arguments[1] == "function") {
				executionState._callback = executionState._method._arguments[1];
			} else {
				promising = executionState._method._arguments[1];
				executionState._callback = executionState._method._arguments[2];
			}
		} else {
			if (typeof executionState._method._arguments[0] == "function") {
				executionState._callback = executionState._method._arguments[0];
			} else {
				promising = executionState._method._arguments[0];
				executionState._callback = executionState._method._arguments[1];
			}
		}
		
		executionState._next = executionState._context;
		executionState._canContinue = !waitingElements;

		// wait for each element to reach the current end of its queue
		if (promising) {
			executionState._context.promise(queueName == null ? 'fx' : queueName).then(function(){
				executionState._canContinue = true;
				timedInvocationChain();
			});
		} else {
			executionState._context.queue(queueName == null ? 'fx' : queueName, function(next){
				executionState._canContinue = !--waitingElements;
				timedInvocationChain();
				next();
			});
		}
	};

	/**
	 * Define to simply run callback method for .then()
	 * 
	 * @param timedInvocationChain
	 * @param executionState
	 */
	jQuery.fn.then.timing = function(timedInvocationChain, executionState){
		executionState._callback = executionState._method._arguments[0];
		executionState._next = executionState._context;
		executionState._canContinue = true;
		if (executionState._method._arguments[1]) {
			Array.prototype.shift.apply(executionState._method._arguments);
		}
	};
	
	/**
	 * Define timeout or binding to wait for.
	 * 
	 * @param timedInvocationChain
	 * @param executionState
	 */
	jQuery.fn.wait.timing = function(timedInvocationChain, executionState, ongoingLoops) {
		var trigger, event, timeout, context = executionState._context;
		
		trigger = executionState._method._arguments[0];
		executionState._callback = executionState._method._arguments[1];

		function triggerAction() {
			originalOff.call(event ? originalOff.call(context, event, triggerAction) : context, 'unwait', unwaitAction);
			executionState._canContinue = true;
			executionState._next = sameOrNextJQuery(executionState._context, executionState._next);
			timedInvocationChain();
		}
		
		function unwaitAction(evt, skipWait){
			originalOff.call(event ? originalOff.call(jQuery(this), event, triggerAction) : jQuery(this), 'unwait', unwaitAction);
			context = context.not(this);
			if (!skipWait) {
				executionState._next = executionState._next.not(this);
			}
			if (!context.length) {
				executionState._canContinue = executionState._next.length;
				executionState._next = sameOrNextJQuery(executionState._context, executionState._next);
				window.clearTimeout(timeout);
				executionState = { _context: context };
			}
			// just update the snapshot info
			timedInvocationChain();
		}

		originalOn.call(context, 'unwait', unwaitAction);
		executionState._next = context;

		if (trigger == null || trigger == jQuery) {
			trigger = context;
		}	
		if (typeof trigger == "function") {
			trigger = trigger.apply(context, loopCounts(ongoingLoops));
		}
		if (typeof trigger == "string") {

			originalOn.call(context, event = trigger, triggerAction);

		} else if (trigger && typeof trigger.promise == "function") {
			
			trigger.promise().then(triggerAction);
			
		} else if (trigger && typeof trigger.then == "function") {
			
			trigger.then(triggerAction, true);
			
		} else {

			timeout = window.setTimeout(triggerAction, Math.max(0,trigger));

		}
	};

	/**
	 * Define to simply run callback method for .then()
	 * 
	 * @param timedInvocationChain
	 * @param executionState
	 */
	jQuery.fn.each = function(callback){
		if (!callback || callback === jQuery) {
			var methodStack = {},
			placeholder = new PredictingProxy(this, methodStack, createTimedInvocationChain(this, methodStack, [], function(elements){
					placeholder.length = 0;
					Array.prototype.push.apply(placeholder, elements);
				}));
			return placeholder.each(callback);
		}
		return originalEach.apply(this, arguments);
	};
	
	/**
	 * Define interval or binding to repeat.
	 * 
	 * @param timedInvocationChain
	 * @param executionState
	 */
	jQuery.fn.each.timing = function(timedInvocationChain, executionState, ongoingLoops, onStepCallback) {
		if (executionState._method._arguments[0] && executionState._method._arguments[0] !== jQuery) {
			executionState._canContinue = true;
			executionState._next = originalEach.apply(executionState._context, executionState._method._arguments);
			return;
		}
		
		var size = Math.max(executionState._context.length, 1),
		finished = 0,
		key, methodToGoOn, openLoopTimeout,
		innerTICs = [],
		innerElements = [],
		proxyPlaceholder = jQuery.extend({}, executionState._context),
		stepByStep = executionState._method._arguments[0] === jQuery;

		if (stepByStep) {
			window.setTimeout(function(){
				openLoopTimeout = true;
				timedInvocationChain();
			},0);
		}
		
		function spreadAction(){
			if (stepByStep) {
				if (finished < size) {
					(innerTICs[finished])();
				}
			} else {
				for (var i=0; i<size; i++) {
					(innerTICs[i])();
				}
			}
			return proxyPlaceholder;
		}
		
		for (key in PredictingProxy.prototype) {
			proxyPlaceholder[key] = spreadAction;
		}
		proxyPlaceholder.length = size;
		for(key=0; key<size; key++) (function(index){
			var innerLoops = ongoingLoops.slice(),
			context = executionState._context.eq(index);
			innerElements[index] = context.get();
			innerLoops.unshift({
				_count: index,
				_allAction: function(state){
					finished++;
					if (finished == size) {
						methodToGoOn = state._method._next;
					}
					timedInvocationChain();
				},
				_fixOpenLoop: loopEndMethods.all,
				_openEndAction: function(tic, state){
					if (openLoopTimeout) {
						finished++;
						if (finished == size) {
							methodToGoOn = state._method;
						}
						timedInvocationChain();
					}
				}
			});
			innerTICs[index] = createTimedInvocationChain(context, executionState._method._next, innerLoops, function(elements){
				innerElements[index] = elements;
				proxyPlaceholder.length = 0;
				for (var i=0; i<size; i++) {
					Array.prototype.push.apply(proxyPlaceholder, innerElements[i]);
				}
				if (onStepCallback)
					onStepCallback(jQuery.makeArray(proxyPlaceholder));
			});
		})(key);

		executionState._next = proxyPlaceholder;
		executionState._canContinue = true;
		executionState._openEndAction = function(tic, state){			
			if (finished == size) {
				ongoingLoops.shift();
				return {
					_context: sameOrNextJQuery(executionState._context, proxyPlaceholder),
					_method: methodToGoOn
				};
			}
			var finishedBefore = finished;
			spreadAction();
			if (finished != finishedBefore) {
				return state;
			}
		};
		executionState._count = size;
		
		ongoingLoops.unshift(executionState);
	};

	loopEndMethods.all = function(executionState){
		jQuery.extend(executionState._method, {
			_next: jQuery.extend({}, executionState._method),
			_name: 'all',
			_arguments: []
		});
		executionState._canContinue = null;
	};
	loopEndMethods.all.timing = function(timedInvocationChain, executionState, ongoingLoops) {
		if (!ongoingLoops.length || !ongoingLoops[0]._fixOpenLoop) {
			throw '.all() method must be used after .each() only';
		}
		if (!ongoingLoops[0]._allAction) {
			ongoingLoops[0]._fixOpenLoop(executionState);
			return;
		}
		
		ongoingLoops[0]._allAction(executionState);
	};
	
	/**
	 * Define interval or binding to repeat.
	 * 
	 * @param timedInvocationChain
	 * @param executionState
	 */
	jQuery.fn.repeat.timing = function(timedInvocationChain, executionState, ongoingLoops) {
		var trigger,
		firstRunNow,
		openLoopTimeout,
		event,
		interval;

		if (typeof executionState._method._arguments[0] == "function") {
			executionState._callback = executionState._method._arguments[0];
		} else if (typeof executionState._method._arguments[1] == "function") {
			trigger = executionState._method._arguments[0];
			executionState._callback = executionState._method._arguments[1];
		} else {
			trigger = executionState._method._arguments[0];
			firstRunNow = executionState._method._arguments[1];
			executionState._callback = executionState._method._arguments[2];
		}
		
		function triggerAction() {
			executionState._next = executionState._next || executionState._context;
			executionState._canContinue = true;
			timedInvocationChain();
		}
		
		function unrepeatAction(){
			originalOff.call(event ? originalOff.call(jQuery(this), event, triggerAction) : jQuery(this), 'unrepeat', unrepeatAction);
			var context = executionState._context.not(this);
			executionState._next = (executionState._next == executionState._context) ? context : executionState._next;
			executionState._context = context;
			executionState._canContinue = executionState._context.length && executionState._canContinue;
			trigger = executionState._context.length && trigger;
			window.clearInterval(!executionState._context.length && interval);
			// just update the snapshot info
			timedInvocationChain();
		}
		
		executionState._openEndAction = function(tic, state){
			if (executionState._canContinue || openLoopTimeout) {
				executionState._count++;
				executionState._next = executionState._next || executionState._context;
				executionState._canContinue = executionState._canContinue || (trigger && state._context && state._context.length);
				return executionState;
			}
		};

		if (trigger == null) {
			
			firstRunNow = trigger = true;
			window.setTimeout(function(){
				openLoopTimeout = true;
				timedInvocationChain();
			},0);
			
		} else {
			if (typeof trigger == "string") {
				originalOn.call(executionState._context, event = trigger, triggerAction);
			} else {
				interval = window.setInterval(triggerAction, Math.max(0, trigger));				
			}
			trigger = false;
		}

		originalOn.call(executionState._context, 'unrepeat', unrepeatAction);
		
		executionState._next = executionState._context;
		executionState._count = 0;
		executionState._untilAction = function(end){
			if (end) {
				unrepeatAction.apply(executionState._context);
			}
			if (trigger) {
				triggerAction();
			}
		};
		executionState._fixOpenLoop = loopEndMethods.until;

		if (firstRunNow) {
			triggerAction();
		}
		
		ongoingLoops.unshift(executionState);
	};

	/**
	 * Defined to evaluate condition when calling .until()
	 */
	loopEndMethods.until = function(executionState){
		jQuery.extend(executionState._method, {
			_next: jQuery.extend({}, executionState._method),
			_name: 'until',
			_arguments: []
		});
		executionState._canContinue = null;
	};
	loopEndMethods.until.timing = function(timedInvocationChain, executionState, ongoingLoops) {
		if (!ongoingLoops.length || !ongoingLoops[0]._fixOpenLoop) {
			throw '.until() method must be used after .repeat() only';
		}
		if (!ongoingLoops[0]._untilAction) {
			ongoingLoops[0]._fixOpenLoop(executionState);
			return;
		}

		var condition = executionState._method._arguments[0],
		loopContext = executionState._method._arguments[1];
		if (condition === jQuery) {
			condition = null;
			loopContext = executionState._method._arguments.length <= 1 || loopContext;
		}
		if (typeof condition == "function") {
			condition = condition.apply(executionState._context, loopCounts(ongoingLoops));
		}
		if (condition == null) {
			condition = !executionState._context.size();
		}
		if (typeof condition == "object") {
			condition = condition.toString();
		}
		if (typeof condition == "number") {
			condition = ongoingLoops[0]._count >= condition-1;
		}
		if (condition) {
			executionState._canContinue = true;
			executionState._next = executionState._context;
			ongoingLoops.shift()._untilAction(condition);
		} else {
			if (loopContext) {
				ongoingLoops[0]._next = executionState._context;
			}
			executionState = ongoingLoops[0];
			executionState._count++;
			executionState._untilAction(condition);
			return executionState;
		}
	};
	
	// support .until() and .all()
	new PredictingProxy(loopEndMethods);
	
	/**
	 * Define unwait and unrepeat methods.
	 */
	jQuery.each(['unwait','unrepeat'], function(index, name){
		jQuery.fn[name] = function(){
			return this.trigger(name, arguments);
		};
	});
	
	/**
	 * define all static timing methods:
	 *  $.wait, $.repeat ,$.join, $.then, $.unwait, $.unrepeat
	 */
	jQuery.each(['wait','repeat','join','then','unwait','unrepeat'], function(index, name){
		jQuery[name] = function(){
			var group = typeof arguments[0] == "string" ? Array.prototype.shift.apply(arguments) : '';
			return jQuery.fn[name].apply(THREAD_GROUPS[group] = (THREAD_GROUPS[group] || jQuery('<div>').text(group)), arguments);
		};
	});

	/**
	 * X defines deferred variables that can be used in timed invocation chains 
	 * 
	 * @author CreativeCouple
	 * @author Peter Liske
	 */
	function X(compute, Var, calculation){
		if (typeof compute == "string") {
			calculation = new Function('x','return ['+compute+'\n,x]');
			compute = function(x, result){
				result = calculation(x);
				callbackVariable.x = result[1];
				return result[0];
			};
		}
		var hasRelatedVariable = typeof Var == "function",
		hasComputation = typeof compute == "function",
		
		callbackVariable = function(value) {
			if (arguments.length == 1) {
				callbackVariable.x = value;
				if (hasRelatedVariable) {
					Var(value);
				}
			} else {
				return evaluate();
			}
		};
		function evaluate(value){
			value = hasRelatedVariable ? Var() : callbackVariable.x;
			return hasComputation ? compute(value) : value;				
		}
		
		callbackVariable.x = 0;
		callbackVariable._ = { toString: callbackVariable.$ = callbackVariable.toString = evaluate.toString = evaluate };
		callbackVariable.mod = function(val){
			return X(function(x){
				return x % val;
			}, callbackVariable);
		};
		callbackVariable.add = function(val){
			return X(function(x){
				return x + val;
			}, callbackVariable);
		};
		callbackVariable.neg = function(){
			return X('-x', callbackVariable);
		};
		// $$ only for backward compatibility
		callbackVariable.$$ = callbackVariable.X = function(compute){
			return X(compute, callbackVariable);
		};
		jQuery.each(['a','b','c','d','e','f','g','h','i','j'], function(index, character){
			callbackVariable[index] = callbackVariable[character] = function(){
				callbackVariable(arguments[index]);
			};
		});
		
		return callbackVariable;
	};
	
	// $$ only for backward compatibility
	window.$$ = jQuery.$$ = jQuery.X = X;
	
	/**
	 * Define chained version of $().
	 * This allows to use .end() to come back to previous jQuery selection.
	 */
	jQuery.fn.$ = function(){
		var ret = jQuery.apply(window, arguments);
		ret.prevObject = this;
		return ret;
	};
	
})(jQuery, window);