text:
(function() {
  var MODULE = 'nia-animation';                    // module name, for errors only. AMD module is defined anonymously.
  var AMD_DEPS = ['require', 'exports', 'nia'];    // list of AMD deps. init() must only use require and exports!!

  // --- AMD/COMMONJS Compatibility Snippet starts
  if (/^u/.test(typeof define) || !define.amd) { // no AMD support available? checks for "undefined"
    if (/^u/.test(typeof module) || !module.exports)  // no CommonJS support?
      throw MODULE+": no AMD or CommonJS support. nia-core not loaded? see http://niajs.com/err/10";
    else
      init(require, module.exports);
  }
  else
    define(AMD_DEPS, init);
  // --- AMD/COMMONJS Compatibility Snippet ends

 function init(require, exports) {
	var NIA = require('nia');
	var $ = NIA.$, $$ = NIA.$$, EE = NIA.EE, _ = NIA._;

	var ANIMATE_STATE = 'niaAnimState';

	/*$
	 * @id parsevelocity
     * @name niaAnim.parseVelocity()
     * @syntax niaAnim.parseVelocity(spec)
     * @syntax niaAnim.parseVelocity(spec, delta, unit)
     * @module ANIMATION
     *
     * Parses a velocity or duration. Given a velocity and a delta, it can also compute the duration of a movement.
     *
     * <var>parseVelocity</var> determines whether it got a velocity by looking for a slash ("/") in the string. If not,
	 * it is interpreted as velocity using ##parseDuration(). If it is a velocity, it expects the number of the velocity,
	 * followed by the delta unit, a slash and the time unit.
	 *
	 * @example Parses some velocities:
	 *
niaAnim.parseVelocity('5px/s'); // returns {velocity: 5, deltaUnit: 'px', timeUnit: 's'}
	 * niaAnim.parseVelocity('5  px / s');  // same result, whitespace will be ignored
	 * niaAnim.parseVelocity('20deg/min');  // returns {velocity: 20, deltaUnit: 'deg', timeUnit: 'min'}
	 * niaAnim.parseVelocity('20/min');     // returns {velocity: 20, deltaUnit: '', timeUnit: 'min'}
	 * niaAnim.parseVelocity('20px^2/s');     // returns {velocity: 20, deltaUnit: 'px^2', timeUnit: 's'}
	 * niaAnim.parseVelocity('20 /min');    // same result, just whitespace
	 * niaAnim.parseVelocity('14s');        // recognized as duration, returns 14000
	 * niaAnim.parseVelocity(200);          // recognized as duration in ms, returns 200
	 * niaAnim.parseVelocity(200);          // recognized as duration in ms, returns 200
	 * niaAnim.parseVelocity('5px/s', 15);  // returns 3000 (at 5px/s, 15px take 3000ms)
	 * niaAnim.parseVelocity('5px/s', 15, 'px');    // returns 3000 (but with unit check)
	 * niaAnim.parseVelocity('5px/s', 15, 'inch');  // returns null (delta units do not match)
	 * niaAnim.parseVelocity('5/s', 10, 'inch');    // returns 2000 (no delta unit given: no check)
	 *

     *
     * @param spec the velocity. Either the a value like "30 px/s", or a duration for ##parseDuration().
     * @param delta optional if given, the duration needed given the velocity is computed. Ignored if <var>spec</var>
	 *                       is already a duration.
     * @param unit optional a unit to check against the delta unit. If they don't match, <var>parseVelocity</var> returns <var>null</var>.
     * @return the duration in ms if a duration was given or <var>delta</var> is set. <var>null</var> on error. If no delta was
	 * given and the <var>spec</var> is really a velocity, an object with the properties
	 *

velocity

number component of the velocity, e.g. 5 in '5px/s'

	 *
deltaUnit

the delta unit in the <var>spec</var>, e.g. 'px' in '5px/s'. Can be an empty string!

	 *
timeUnit

the time unit in the <var>spec</var>, e.g. 'd' in '5px/s'.

     */
	function parseVelocity(spec, delta, unit) {
		if (!/\//.test(spec))
			return _.parseDuration(spec);

		var m = /(-?\d+(?:\.\d*)?)\s*([\w^]*)\s*\/\s*(\w+)/.exec(spec);
		if (!m)
			return null;

		if (delta != null) {
			if (unit && m[2] && m[2]!=unit)
				return null;
			else {
				var tu = _.parseDuration("1"+m[3]);
				return tu ? delta / parseFloat(m[1]) * tu : null;
			}
		}
		else
			return {velocity: parseFloat(m[1]), deltaUnit: m[2], timeUnit: m[3]};
	}

	/*$
     * @id getvelocity
     * @name niaAnim.getVelocity()
     * @syntax niaAnim.getVelocity(interpolator, start, end, t)
     * @module ANIMATION
     *
     * Computes an estimation of the interpolator function's velocity (slope) at the given position.
     *
     * The velocity is defined as the change in value relative to the duration of the full movement (which is 1 - the
     * interpolator does not use units).
     *
     * @param interpolator an interpolation function(start, end, t) that interpolates from t=0 to t=1.
     * @param start the start value to pass to the interpolator.
     * @param end the target value to pass to the interpolator.
	 * @param t the position to get the velocity (slope) from. Usually between 0 and 1.
     * @return the estimated velocity (slope) of the interpolator.
     */
    function getVelocity(interpolator, start, end, t) {
		var prec = 0.00000001;
		var t0 = Math.min(Math.max(t, 0), 1);
		var t1 = Math.max(t - prec, 0);
		var t2 = Math.min(t + prec, 1);
		return (interpolator(start, end, t2) - interpolator(start, end, t1)) / (t2-t1);
	}

    /*$
     * @id velointerpolator
     * @name niaAnim.veloInterpolator()
     * @syntax niaAnim.veloInterpolator(start, end, t)
     * @syntax niaAnim.veloInterpolator(start, end, t, vStart, vEnd)
     * @syntax niaAnim.veloInterpolator(start, end, t, vStart, vEnd, returnVelocity)
     * @module ANIMATION
     *
     * A bilinear interpolation function that will smoothly interpolate from the given start value and velocity
     * to the given target value with the given target velocity at the end of the animation.
     *
     * The velocity is defined as the change in value relative to the duration of the full movement (which is 1 - the
     * interpolator does not use units).
     *
     * @param start the start value
     * @param end the target value
     * @param t the current time. If the value is between 0 and 1, it will be interpolated. For a time smaller 0,
     *             the start value will be returned. Times of 1 and larger return the target value.
     * @param vStart optional the initial velocity (using the units from start and duration), relative to whole duration (0 to 1). Defaults to 0.
     * @param vEnd optional the target velocity (using the units from start and duration), relative to whole duration (0 to 1). Defaults to 0.
     * @param returnVelocity optional if true, the function returns the velocity (slope) instead of the position at the given time.
     * @return the interpolated value or velocity (slope), depending on <var>returnVelocity</var>
     */
	function veloInterpolator(start, end, t, vStart, vEnd, returnVelocity) {
		vStart = vStart || 0;
		vEnd = vEnd || 0;
		if (t >= 1)
			return end;
		else if (t < 0)
			return start;
		else if (returnVelocity)
			return vStart + 2*(3*end-3*start-2*vStart-vEnd)*t + 3*(2*start+vStart+vEnd-2*end)*t*t;
		else
			return start + vStart*t + (3*end-3*start-2*vStart-vEnd)*t*t + (2*start+vStart+vEnd-2*end)*t*t*t;
	}

	// analyses object to find numbers. If it is a number, return array containing it. Otherwise return array containing all numbers
	function extractInterpolatable(s) {
		if (_.isNumber(s))
			return [s];
		var nums = [], m;
        var re = /(-?[0-9]+(?:\.[0-9]+)?)|(?:#\w{3}(?:\w{3})?)|(?:rgb\([^\)]+\))/g;
		while ((m = re.exec(s)))
			nums.push(m[1] ? parseFloat(m[0]) : m[0]);
		return nums;
	}

	// takes an object, as given to extractInterpolatable. If s is a number, the arrays first value is returned. If s is a string, the numbers are replaced with the array values.
	function setInterpolatablesArray(s, numArray) {
		if (_.isNumber(s))
			return numArray[0];
		var i = 0;
		return s.replace(/(?:-?[0-9]+(?:\.[0-9]+)?)|(?:#\w{3}(?:\w{3})?)|(?:rgb\([^\)]+\))/g, function() {
			return numArray[i++];
		});
	}
	 function setInterpolatablesList(s, numlist) {
		if (_.isNumber(s))
			return numlist.first();
		var i = 0;
		return s.replace(/(?:-?[0-9]+(?:\.[0-9]+)?)|(?:#\w{3}(?:\w{3})?)|(?:rgb\([^\)]+\))/g, function() {
			return numlist.at(i++);
		});
	}

	function toArray(l) {
		return _.isArrayLike(l) ? l : [l];
	}

	function extractSingleNumber(v) {
		return parseFloat(v.replace(/^[^\d-]+/, ''));
	}

	function getColorComponent(colorCode, index) {
		return (/^#/.test(colorCode)) ?
			parseInt(colorCode.length > 6 ? colorCode.substr(index*2+1, 2) : ((colorCode=colorCode.charAt(index+1))+colorCode), 16)
		:
			extractSingleNumber(colorCode.split(',')[index]);
	}

	function interpolatePropValue(t, start, end, vStart, vEnd) {
		var i = 0;
		return t<=0?start:t>=1?end:
					(/^#|rgb\(/.test(start)) ? // color in format '#rgb' or '#rrggbb' or 'rgb(r,g,b)'?
								('rgb('+ Math.round(_.minMax(veloInterpolator(getColorComponent(start, i), getColorComponent(end, i++), t, vStart, vEnd), 0, 255)) +
								 ',' + Math.round(_.minMax(veloInterpolator(getColorComponent(start, i), getColorComponent(end, i++), t, vStart, vEnd), 0, 255)) +
								 ',' + Math.round(_.minMax(veloInterpolator(getColorComponent(start, i), getColorComponent(end, i++), t, vStart, vEnd), 0, 255)) +
								 ')')
							:
								veloInterpolator(start, end, t, vStart, vEnd);
	}

	// internal, for use in keyframe timelines
	function singlePropVeloDial(listOrSelector, ctx, propertyName, propertyTemplate, startValues, startVelocity, endValues, endVelocity) {
		var isLiveSelector = _.isString(listOrSelector);
		var mList = $(listOrSelector);
		return function(t, returnVelocity) {
			var valList = _.map(startValues, function(start, valIndex) {
				return interpolatePropValue(t, start, _.at(endValues, valIndex), _.at(startVelocity, valIndex), _.at(endVelocity, valIndex), returnVelocity);
			});
			(isLiveSelector ? $(listOrSelector, ctx) : mList).set(propertyName, setInterpolatablesList(propertyTemplate, valList));
		};
	}

    // internal, for use in keyframe timelines. Basically a simplified version of _.call().
    function createDialListFunc(list) {
		return function(t) {
			_.each(list, function(f) {
				f(t);
			});
		};
    }

    // internal, for use in keyframe timelines. Put here to avoid closures as memory hogs in timeline().
	function createVeloDialListFuncs(itemIterable, ctx) {
		return createDialListFunc(_.map(itemIterable, function(item) {
			return singlePropVeloDial(item.tTarget, ctx, item.tPropName, item.tPropTemplate, item.tFrom, item.tVeloStart, item.tTo, item.tVeloEnd);
		}));
	}

    /*$
     * @id velodial
     * @name list.veloDial()
     * @syntax list.veloDial(properties0, properties1, velocities0, velocities1)
     * @module ANIMATION
     *
     * Creates a ##dial() function that animates the properties of the list elements, using two sets of properties and start/stop velocities.
     *
     * A dial is a function that takes a single argument that specifies the progression of the animation. If you call it will 0 or
     * smaller, it will set the first properties.  Call it with 1 or larger, it will set the second properties. For all values between 0 and 1 it will interpolate the values
     *
     * Unlike the Core module's standard dial,
     * <var>veloDial()</var> allows you to specify velocities for the start and end positions, allowing smooth
     * chained animation, whereas with a regular dial without velocity will always start and stop the animation without
     * velocity.
     *
     * @param properties0 a ##set() compatible map of properties for position 0. Can be CSS styles, attributes or everything else supported by <var>set()</var>.
     * @param properties1 a ##set() compatible map of properties for position 1.
     * @param velocities0 optional a map of properties specifying the initial velocities for each property. Velocities are defined as the change in value
     *             during the duration of the whole animation (0-1). If a velocity for a property is not set, it is 0. For animation of strings containing
     *             several values, you can specify a list of velocity values.
     * @param velocities1 optional a map of properties specifying the target velocities that the properties will have at the end of the animation. Defaults to the value of <var>velocities0</var>.
     * @return a dial function function(newPosition) that will set the state.
	 *

	 *
newPosition

If 0 or lower, set the list members to the first state.
	 *             If 1 or higher, sets them to the second state. For any value betweeen 0 and 1, the list members
	 *             will be set to interpolated values.

	 *

     */
	function veloDial(properties0, properties1, velocities0, velocities1) {
		var self = this;

		function getValueMap(properties) { // returns {}->Array
			return _.mapObj(properties, function(name, start) {
				return extractInterpolatable(start);
			});
		}
		var startValueMap = getValueMap(properties0);
		var endValueMap = getValueMap(properties1);

		function getVelocityMap(vmap) { // returns {}->List
			return _.mapObj(startValueMap, function(name, valueArray) {
				return _.map(valueArray, function(startValue, index) {
					if (vmap && vmap[name] != null) {
						if (_.isIterable(vmap[name]))
							return _.at(vmap[name], index) || 0;
						else if (index == 0)
							return vmap[name];
					}
					else
						return 0;
				});
			});
		}
		var startVelocityMap = getVelocityMap(velocities0);
		var endVelocityMap = getVelocityMap(velocities1 || velocities0);

		return function(t) {
			_.eachObj(startValueMap, function(name, valueArray) {
                self.set(name, setInterpolatablesArray(properties0[name],
                    valueArray.map(function(start, valIndex) {
                    	return interpolatePropValue(t, start, endValueMap[name] ? endValueMap[name][valIndex] : 0, _.at(startVelocityMap[name], valIndex), _.at(endVelocityMap[name], valIndex));
                	})));
			});
		};
	}

    /*$
     * @id animatedial
     * @name niaAnim.animateDial()
     * @syntax niaAnim.animateDial(dial)
     * @syntax niaAnim.animateDial(dial, duration)
     * @syntax niaAnim.animateDial(dial, duration, promiseArguments)
     * @module ANIMATION
     *
     * Animates a ##dial(), by calling it in a ##loop() during the given <var>duration</var> while slowly progressing the
     * argument from 0 to 1.
     * It returns a promise that allows you to track the completion of the animation, as well as stopping it.
     *
     * @param dial the ##dial() to animate
     * @param duration optional the duration of the animation in ms, or any value allowed by ##_.parseDuration(), like '2.5s'. Defaults to 500ms.
     * @return a ##Promise that will be fulfilled when the animation completed. Supports ##stop().
     *              If the running animation is stopped, the promise will be rejected.
     */
    function animateDial(dial, duration, promiseArguments) {
        var durationMs = _.parseDuration(duration) || 500;

        return $.loop(function(timePassedMs) {
            dial(timePassedMs/durationMs);
        }, duration);
    }

    /*$
     * @id veloanimate
     * @name .veloAnimate()
     * @syntax list.veloAnimate(propertiesStart, propertiesEnd, velStart, velEnd, duration)
     * @module ANIMATION
     *
     * Animates the properties of the list elements, with velocity support. Unlike the regular ##animate() function,
     * it always uses bilinear interpolation and allows you to define start and end velocities for all properties.
     *
     * @param propertiesStart a map of start properties in ##set() syntax.
     * @param propertiesEnd a map of end properties in ##set() syntax.
     * @param velStart a map of velocities for all properties at start. Uses ##set() syntax for keys, and
     *   either a number as value or, if the animated property is a multi-value string, a list of values.
     *   The velocity is defined as property value change per millisecond.
     * @param velEnd a map of velocities for all properties at the animation's end. Uses ##set() syntax for keys, and
     *   either a number as value or, if the animated property is a multi-value string, a list of values.
     *   The velocity is defined as property value change per millisecond.
     * @param duration optional the duration of the animation in ms, or any value allowed by ##_.parseDuration(), like '2.5s'. Defaults to 500ms.
     * @return a ##Promise that will be fulfilled when the animation completed. Supports ##stop().
     *              If the running animation is stopped, the promise will be rejected.
     */
    function veloAnimate(propertiesStart, propertiesEnd, velStart, velEnd, duration) {
		var durationMs = _.parseDuration(duration) || 500;
        function convertVelocity(v) {
            return v / durationMs;
        }
        return animateDial(this.veloDial(propertiesStart, propertiesEnd,
            _.mapObj(velStart, convertVelocity), _.mapObj(velEnd, convertVelocity)), durationMs, [this]);
    }

    /*$
     * @id play
     * @name niaAnim.play()
     * @syntax niaAnim.play(timeline)
     * @syntax niaAnim.play(timeline, startPosition)
     * @syntax niaAnim.play(timeline, startPosition, autoLoop)
     * @syntax niaAnim.play(timeline, startPosition, autoLoop, onStop)
     * @syntax niaAnim.play(timeline, startPosition, autoLoop, onStop, onChange)
     * @module ANIMATION
     *
     * Executes a ##timeline() or a similar function by calling the timeline function in an animation loop.
     * <var>play()</var> is similar to ##loop(), but offers more ways of controlling the animation.
     *
     * @example Playing a color-changing timeline forever:
     *
var tl = niaAnim.timeline([
     *      {keyframe: '.colorChanger', props: {$color: #ff0000}, wait: 1500},
     *      {keyframe: '.colorChanger', props: {$color: #ff0000}, wait: 1500},
     *      {keyframe: '.colorChanger', props: {$color: #00ff00}, wait: 1500},
     *      {keyframe: '.colorChanger', props: {$color: #ff0000}}
     * ]);
     * niaAnim.play(tl, 0, true);
     *

     *
     * @param timeline a function(timeMs, stop)</var> function that will be invoked in an animation ##loop() to run
     *                         the animation. <var>timeMs</var> is the number of milliseconds since the animation start.
     *                         <var>stop()</var> is a function to call without arguments when the animation finished.
     *                         Same function signature as in ##loop().
     * @param startPosition optional the start position of the animation, in ms
     * @param autoLoop optional if true, the animation will be restarted from the beginning when it finished.
     * @param onChange optional a function(timeMs, isRunning) that will be called from the event loop
     *             every time the animation progresses or when it stops. <var>isRunning</var> will be false when stopped.
     * @return a promise that is fulfilled when the animation is finished. Supports ##stop() to interrupt a running animation.
     *         <var>stop()</var> returns the current position of the timeline.
     *
     * @see ##loop() is a simpler runner for animations.
     * @see ##playToggle() allows starting/stopping animations with a toggle function.
     */
	function play(timeline, startPosition, autoLoop, onChange) {
		var isRunning = true, lastT, runStartedAt;
		var offset = startPosition || 0;

		var prom = $.loop(function(t, stop) {
			if (runStartedAt == null)
				runStartedAt = t;
			lastT = t - runStartedAt;
			timeline(lastT, function() {
				if (autoLoop)
					runStartedAt = null;
				else
					isRunning = false;
			});
			if (!isRunning) {
					prom.fulfill(lastT); // must be set before stop()! Otherwise promise is already rejected.
					stop();
			}
			if (onChange)
				onChange(lastT, isRunning);
		});
		return prom;
    }

    /*$
     * @id player
     * @name niaAnim.player()
     * @syntax niaAnim.player(timeline)
     * @syntax niaAnim.player(timeline, resetOnCall)
     * @module ANIMATION
     *
     * Creates a function that can execute a timeline animation. When it is called while the animation is still
     * running, depending on the second argument the call will either be ignored, or resets the animation.
     * The purpose of the function is to make sure that the animation can not be started while it is already running.
     *
     * @example A color-changing timeline activated by click:
     *
var tl = niaAnim.timeline([
     *      {keyframe: '.colorChanger', props: {$color: #ff0000}, wait: 1500},
     *      {keyframe: '.colorChanger', props: {$color: #ff0000}, wait: 1500},
     *      {keyframe: '.colorChanger', props: {$color: #00ff00}, wait: 1500},
     *      {keyframe: '.colorChanger', props: {$color: #ff0000}}
     * ]);
     * var player = niaAnim.player(tl);
     * $('#changeAnim').onClick(player);
     *

     *
     * @param timeline a function(timeMs, stop)</var> function that will be invoked in an animation ##loop() to run
     *                         the animation. <var>timeMs</var> is the number of milliseconds since the animation start.
     *                         <var>stop()</var> is a function to call without arguments when the animation finished.
     *                         Same function signature as in ##loop().
     * @param resetOnCall if true, the animation will be reset when the returned player function is call while still running.
     *                      Otherwise invocations while running will be ignored.
     * @return the player function. Call to start the animation. With <var>resetOnCall</var>, a subsequent call before animation
     *                    finished will reset the animation. Otherwise an animation can not be interrupted.
     * @see ##play() controls a timeline animation without toggle.
     */
	function player(timeline, resetOnCall) {
		var prom;
		return function() {
			if (prom && resetOnCall) {
				prom.stop();
				prom = null;
			}
			if (!prom)
				prom = play(timeline).then(function() { prom = 0; });
		};
	}

    /*$
     * @id playtoggle
     * @name niaAnim.playToggle()
     * @syntax niaAnim.playToggle(timeline)
     * @syntax niaAnim.playToggle(timeline, resetAfterStop)
     * @syntax niaAnim.playToggle(timeline, resetAfterStop, autoLoop)
     * @module ANIMATION
     *
     * Creates a toggle function that executes a ##timeline() or a similar function by calling the timeline function in an animation loop.
     * The toggle function stops the animation in 'false' state and starts in 'true' state.
     *
     * @example A color-changing timeline activated by mouse-over:
     *
var tl = niaAnim.timeline([
     *      {keyframe: '.colorChanger', props: {$color: #ff0000}, wait: 1500},
     *      {keyframe: '.colorChanger', props: {$color: #ff0000}, wait: 1500},
     *      {keyframe: '.colorChanger', props: {$color: #00ff00}, wait: 1500},
     *      {keyframe: '.colorChanger', props: {$color: #ff0000}}
     * ]);
     * var toggle = niaAnim.playToggle(tl, false, true);
     * $('.active').onOver(toggle);
     *

     *
     * @param timeline a function(timeMs, stop)</var> function that will be invoked in an animation ##loop() to run
     *                         the animation. <var>timeMs</var> is the number of milliseconds since the animation start.
     *                         <var>stop()</var> is a function to call without arguments when the animation finished.
     *                         Same function signature as in ##loop().
     * @param resetAfterStop if true, the animation will be reset when it stops. Otherwise it will start from the beginning.
     * @param autoLoop optional if true, the animation will loop until the toggle stops it. Otherwise it will only run once.
     * @return the ##toggle() function. Call without arguments to toggle the animation. 'true' starts it, 'false' ends it.
     *
     * @see ##play() controls a timeline animation without toggle.
     */
	function playToggle(timeline, resetAfterStop, autoLoop) {
		var stop, lastT;
		return $.createToggle(function(state) {
			if (state)
				stop = play(timeline, lastT, autoLoop, null, resetAfterStop ? function(t) {lastT = t;} : null).stop0;
			else if (stop)
				stop();
		});
	}

    /*$
     * @id createinterpolator
     * @name niaAnim.createInterpolator()
     * @syntax niaAnim.createInterpolator(keyframeList)
     * @module ANIMATION
     *
     * Creates a timeline-based interpolation function for use in ##animate() or ##dial(). This is mostly intended as backup in case
     * you want to easily create something not supported by ##ease.
     *
     * The timeline of the interpolation is based of one or more points and (optionally)
     * the velocity <var>v</var> at that point. <var>createInterpolator()</var> will use a series of cubic function to interpolate
     * between those points. The coordinates of those points are the time <var>t</var>, which must be between 0 and 1, and the
     * value <var>p</var>, which is also between 0 and 1. The velocity <var>v</var> is optional and defaults to 0.
     *
     * If no keyframe with t=0 has been defined, <var>createInterpolator()</var> inserts a point {t: 0, p: 0, v: 0}
     * at the beginning of the timeline.
     *
     * If no keyframe with t=1 has been defined, <var>createInterpolator()</var> inserts a point {t: 1, p: 1, v: 0}
     * at the end of the timeline.
     *
     * @example Creating a simple 'swinging' easing function. Uses it to animate a moving element.
     *
var inOutSwing = niaAnim.createInterpolator([{t: 0.25, p: -0.1, v: 0}, {t: 0.75, p: 1.1, v: 0}]);
     * $('#movingElement').animate({$left: '1000px'}, 500, inOutSwing);

     *
     * @example Creating an easing function that starts fast and then slows down. Uses it to create a ##dial() that fades in an element:
     *
var softOut =  niaAnim.createInterpolator([{t: 0, p: 0, v: 1}, {t: 0.3, p: 0.6, v: 1.5}]);
     * var faderDial = $('.fading').dial({$$fade: 0}, {$$fade: 1}, softOut);

     *
     * @param keyframeList list of timeline points. Must have the properties <var>t</var> for time (0-1) and
     *      <var>p</var> for the value (usually 0-1, even though other values may be possible). The velocity <var>v</var> is optional.
     *      Must be sorted by <var>t</var>.
     * @return the interpolation function(start, end, t) created by the timeline. It interpolates from
	 * t=0 to t=1.
     *
     * @see ##ease() offers pre-defined easing functions.
     * @see ##animate() can use easing functions.
     * @see ##dial() can use easing functions.
     */
	function createInterpolator(listOfKeyFrames) {
		var keyFrames = _(listOfKeyFrames).array();
		if (!keyFrames.length || keyFrames[0].t != 0)
			keyFrames.unshift({t: 0, p: 0, v: 0});
		if (_.last(keyFrames).t != 1)
			keyFrames.push({t: 1, p: 1, v: 0});
		var fGen =  new Function("start", "d", "t", "subInterpolators",
			"if (t <= 0) return start;\n"+
			"else if (t >= 1) return start+d;\n"+
			_(keyFrames).sub(1).map(function(kf, i) {
				return ' else if (t < '+(kf.t)+') return start+d*subInterpolators['+(i)+'](t);\n';
			}).join('')
		);

		var veloInterpolators = _(keyFrames).sub(1).map(function(kf2, index) {
			var kf1 = keyFrames[index];
			var d = (kf2.t - kf1.t) || 1;
			return function(t) {
				return veloInterpolator(kf1.p, kf2.p, (t - kf1.t) / d, kf1.v*d, kf2.v*d);
			};
		}).array();

		return function(start, end, t) {
			return fGen(start, end - start, t, veloInterpolators);
		};
	}

    var functionEases = _.mapObj({
		sineOut: function(t) {
			return Math.sin(t*Math.PI/2);
		},
		sineIn: function(t) {
			return 1-Math.sin((1-t)*Math.PI/2);
		},
		sineInOut: function(t) {
			return (1+Math.sin((t+1.5)*Math.PI))/2;
		},
		sineSwingUp: function(t) {
			return t+0.25*Math.sin(t*2*Math.PI);
		},
		sineSwingDown: function(t) {
			return t-0.25*Math.sin(t*2*Math.PI);
		}
    }, function(name, f) {
		return function (start, end, t) {
			if (t<=0) return start;
			else if (t >= 1) return end;
			else return start + f(t)*(end-start);
		};
    });
    var constructedEases = _.mapObj({
		linear: [{t: 0, p: 0, v: 1}, {t: 1, p: 1, v: 1}],
		inOut: [],
		in: [{t: 1, p: 1, v: 1}],
		out: [{t: 0, p: 0, v: 1}],
		softInSoftOut: [{t: 0.5, p: 0.5, v: 2.5}],
		softInOut: [{t: 0.7, p: 0.5, v: 2}],
		inSoftOut: [{t: 0.3, p: 0.5, v: 2}],
		softIn: [{t: 0.7, p: 0.4, v: 1.5}, {t: 1, p: 1, v: 1}],
		softOut: [{t: 0, p: 0, v: 1}, {t: 0.3, p: 0.6, v: 1.5}],
		stop: [{t: 0, p: 0, v: 1}, {t: 0.5, p: 0.5, v: 0}, {t: 1, p: 1, v: 1}],
		inSwing: [{t: 0.3, p: -0.1, v: 0}, {t: 1, p: 1, v: 1}],
		outSwing: [{t: 0, p: 0, v: 1}, {t: 0.7, p: 1.1, v: 0}],
		inOutSwing: [{t: 0.25, p: -0.1, v: 0}, {t: 0.75, p: 1.1, v: 0}],
		inElastic: [{t: 0.1, p: -0.05, v: 0}, {t: 0.3, p: 0.15, v: 0}, {t: 0.5, p: -0.25, v: 0}, {t: 1, p: 1, v: 3}],
		outElastic: [{t: 0, p: 0, v: 3}, {t: 0.5, p: 1.25, v: 0}, {t: 0.7, p: 0.85, v: 0}, {t: 0.9, p: 1.05, v: 0}],
		inOutElastic: [{t: 0.1, p: -0.05, v: 0}, {t: 0.2, p: 0.1, v: 0}, {t: 0.3, p: -0.2, v: 0},  {t: 0.7, p: 1.2, v: 0},  {t: 0.8, p: 0.9, v: 0}, {t: 0.9, p: 1.05, v: 0}],
		bounce: [{t: 0, p: 0, v: 0.25},
			{t: 0.125, p: 0.125, v: 0}, {t: 0.249999999, p: 0, v: -0.25},  {t: 0.25, p: 0, v: 0.5},
			{t: 0.375, p: 0.25, v: 0},  {t: 0.499999999, p: 0, v: -0.5},  {t: 0.5, p: 0, v: 1},
			{t: 0.625, p: 0.5, v: 0},   {t: 0.749999999, p: 0, v: -1},  {t: 0.75, p: 0, v: 5}]
    }, function(name, def) { return createInterpolator(def); });

    /*$
     * @id ease
     * @name niaAnim.ease
     * @module ANIMATION
     *
     * A map of interpolation functions that can be used with ##animate() or ##dial() for simple animations.
     * The core module supports only the easing functions <var>linear</var> and <var>inOut</var>, or variations
     * of them. This module adds many different easing effects.
     *
     * This is a table of all interpolation functions:
     *

     *
Name	Demo

     *
linear

     *
inOut

     *
in

     *
out

     *
sineOut

     *
sineIn

     *
sineInOut

     *
sineSwingUp

     *
sineSwingDown

     *
softInSoftOut

     *
softInOut

     *
inSoftOut

     *
softIn

     *
softOut

     *
stop

     *
inSwing

     *
outSwing

     *
inOutSwing

     *
inElastic

     *
outElastic

     *
inOutElastic

     *
bounce

     *

     *
     * @example Using <var>inOutSwing</var> to animate a moving element.
     *
$('#movingElement').animate({$left: '1000px'}, 500, niaAnim.ease.inOutSwing);

     *
     * @see ##createInterpolator() allows you to create your own interpolation functions.
     * @see ##animate() can use the easing functions.
     * @see ##dial() can use the easing functions.
     */
    var ease = _.extend(constructedEases,  functionEases);

    /*$
     * @id timeline
     * @name niaAnim.timeline()
     * @syntax niaAnim.timeline(timelineDescriptor)
     * @syntax niaAnim.timeline(ctx, timelineDescriptor)
     * @module ANIMATION
     *
     * Creates a timeline animation function from the given timeline descriptor's keyframes and other timeline elements. The created timeline function can be
     * executed by ##play() and other functions. Timelines animations can be played forward, backward and support seeking. Usually they are bound
     * to a time-driven animation loop, but it is possible to bind them to other things, like a scroll bar position.
     *
     * A simple timeline definition looks like this:
     *
var tl = niaAnim.timeline([
     *      {keyframe: '#movingBlock', props: {$left: '20px', $top: '20px'}, wait: '1s'},
     *      {keyframe: '#movingBlock', props: {$left: '100px', $top: '25px'}, wait: '0.5s'},
     *      {keyframe: '#movingBlock', props: {$left: '100px', $top: '80px'}},
     * ]);

     * And the easiest way to run it is ##play():
     *
niaAnim.play(tl);

     * When you connect the timeline's execution to events, you should consider ##player() and ##playToggle() as higher level mechanisms to control
     * animations.
     *
     * The timeline descriptor is a list of events in the order that they will be executed. Each list element can be:
     *

an object with one or more of the properties described below

     *
a function(t, forward) that will be called when the timeline passes it. <var>t</var> is the current time as given to the timeline
     * function (but not larger than the duration), and <var>forward</var> is true when the timeline is played forwards, and false when backwards.
     * You can exert more control over the callback's execution when you use the <var>callback</var> option shown below.

     *
a list or array of timeline elements. If you embed a list in a timeline descriptor, all its elements will be executed in parallel and not
     *  in order. The timeline will go to the next element when all list elements have finished.

     *
     * All timeline objects can have the following properties:
     *

duration

The duration of the element, typically either as a number in milliseconds or as a value for ##_.parseDuration(),
	 * like '0.4s' or '1min 14s'. Specifies
	 *  how long the execution will take. This applies only to those elements that can have a duration, e.g. not to a callback.

     *
hide

Hides one or more elements using ##hide(). The property's value can be anything allowed for ##dollar#$() (selector, object or list).
     * Uses the timeline's context for the selector if there is one.

     *
show

Shows one or more elements using ##show(). The property's value can be anything allowed for ##dollar#$() (selector, object or list).
     * Uses the timeline's context for the selector if there is one.

     *
start

Sets the start time of the timeline element, relative to the beginning of the animation. You can either specify
	 * this in milliseconds, or using a value for ##_.parseDuration(). If <var>start</var> is missing,
     *  the start position will be the start time of the previous element plus the value of the previous element's <var>wait</var> property, if it has one.

     *
wait

Specifies the time the timeline should wait before the next element in the descriptor is executed. Either a number of milliseconds
	 * or a value for ##_.parseDuration(). If the object has no <var>duration</var> property, this value will also be used as duration.

     *

     *
     * In addition to that, each timeline object can have an action property that defines what to do. This is not required, and you could also have an
     * object containing only <var>wait</var>, but usually you need one. The following action properties are supported:
     *
     * keyframe

     * A keyframe defines a state that one or more object have at a time. The <var>keyframe</var> property's value defines the objects. The value
     * can be anything supported by ##dollar#$() (selector, object or list of objects).
     * The property values are defined in the <var>props</var> element. It uses the universal ##set() syntax:
     *
niaAnim.timeline([
     *      {keyframe: '.colorChanger', props: {$backgroundColor: '#f00'}, wait: '1s'},
     *      {keyframe: '.colorChanger', props: {$backgroundColor: '#00f'}},
     * ]);

     * It is important to know that the keyframe objects are determined at the time of the timeline's creation. If you add additional objects
     * matching the selector after creating the timeline, they will be ignored!
     *
     * Often it is a bit more convenient to let the timeline find out the start value. The <var>auto</var> property defines a list of properties to get from
     * the objects. The values will also be read at the timeline's creation, not at the time of the timeline execution. This timeline uses <var>auto</var>
     * to read the initial $backgroundColor:
     *
niaAnim.timeline([
     *      {keyframe: '.colorChanger', auto: ['$backgroundColor'], wait: '1s'},
     *      {keyframe: '.colorChanger', props: {$backgroundColor: '#00f'}},
     * ]);

     *
     * By default, the timeline will automatically interpolate between the keyframes to allow smooth transitions.  You can see this effect here:
     *
niaAnim.timeline([
     *      {keyframe: '.block', auto: ['$top', '$left'], wait: '1s'},
     *      {keyframe: '.block', props: {$top: 20,  $left: 160}, wait: '1s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 160}, wait: '1s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 20}}
     * ]);

     *
     * You can change the default interpolation by specifying the velocity for each property, or for all properties of the keyframe. Velocities override the default velocity used by the timeline.
     * The velocity is specified by the numeric value change per time unit. It is specified as a simple number, unless you have several numbers in a property string.
     * In that case you can specify a list.
     *
     * In this first example, the overall velocity for each keyframe is set to 0. This will cause the animation to come to stop at every keyframe, and then accellerate again:
     *
niaAnim.timeline([
     *      {keyframe: '.block', auto: ['$top', '$left'], velocity: 0, wait: '1s'},
     *      {keyframe: '.block', props: {$top: 20,  $left: 160}, velocity: 0, wait: '1s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 160}, velocity: 0, wait: '1s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 20}, velocity: 0}
     * ]);

     *
     * Changing the velocity to non-zero values can alter the path dramatically:
     *
niaAnim.timeline([
     *      {keyframe: '.block', auto: ['$top', '$left'], velocity: {$top: -0.1,  $left: 0.1}, wait: '1.5s'},
     *      {keyframe: '.block', props: {$top: 20,  $left: 160}, velocity: {$top: 0.4,  $left: -0.2}, wait: '1.5s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 160}, velocity: {$top: -0.5,  $left: -0.5}, wait: '1.5s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 20}, velocity: {$top: -0.2,  $left: 0}}
     * ]);

     *
     * You can also specify separate velocities before and after the keyframe has been reached. This can create more abrupt motions:
     *
niaAnim.timeline([
     *      {keyframe: '.block', auto: ['$top', '$left'], velocityAfter: {$top: 0.2,  $left: 0}, wait: '1.5s'},
     *      {keyframe: '.block', props: {$top: 20,  $left: 160}, velocityBefore: {$top: 0,  $left: 0.4}, velocityAfter: {$top: 0.4,  $left: 0}, wait: '1.5s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 160}, velocityBefore: {$top: 0.4,  $left: 0}, velocityAfter: {$top: -0.3,  $left: -0.3}, wait: '1.5s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 20}, velocityBefore: {$top: 0.3,  $left: 0.3}}
     * ]);

     *
     * The <var>linear</var> property allows you to create linear motions, thus with constant velocity. If it is set in a keyframe, the following motion of its properties is linear:
     *
niaAnim.timeline([
     *      {keyframe: '.block', auto: ['$top', '$left'], linear: true, wait: 1000},
     *      {keyframe: '.block', props: {$top: 20,  $left: 160}, linear: true, wait: 1000},
     *      {keyframe: '.block', props: {$top: 160, $left: 160}, linear: true, wait: 1000},
     *      {keyframe: '.block', props: {$top: 160, $left: 20}}
     * ]);

     *
     * keystop

     * If you have a keyframe in a timeline, it will animate a value from the previous position. Sometimes this is not desired, and you want to stop the animation of a property,
     * only start another one from a different starting position at a later time. <var>keystop</var> is a special form of keyframe that will end the motion of a propery afterwards:
     *
niaAnim.timeline([
     *      {keyframe: '.block', auto: ['$top', '$left'], wait: '1s'},
     *      {keystop: '.block', props: {$top: 20,  $left: 160}, wait: '1s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 160}, wait: '1s'}, // new animation starts here!
     *      {keyframe: '.block', props: {$top: 160, $left: 20}}
     * ]);

     *
     * dial

     * The <var>dial</var> property adds a ##dial() function to the timeline. During the given <var>duration</var> the dial will be animated and
     * progresses the dial value from 0 to 1:
     *
niaAnim.timeline([
     *      {dial: $('.rotor').dial({$transform: 'rotate(0deg)'}, {$transform: 'rotate(360deg)'}), duration: '2s', wait: '4s'},
     *      {dial: $('.rotor').dial({$$fade: 1}, {$fade: 0}), duration: '1s'}
     * ]);

     *
     * Dials can have a <var>repeat</var> count to repeat an animation. The <var>duration</var> will specify the combined duration of all repetitions:
     *
niaAnim.timeline([
     *      {dial: $('.rotor').dial({$transform: 'rotate(0deg)'}, {$transform: 'rotate(360deg)'}), repeat: 5, duration: '10s'}
     * ]);

     *
     * toggle

     * <var>toggle</var> adds a ##toggle() function to the timeline that will be called with <var>true</var> when it is entered and will stay in that state for the given <var>duration</var>. When the time is
     * over, the toggle will be called again with <var>false</var>.
     *
niaAnim.timeline([
     *      {keyframe: '.block', auto: ['$top', '$left'], wait: '1s'},
     *      {toggle: $('.block').toggle({$backgroundColor: '#000'}, {$backgroundColor: '#f00'}), duration: '5s'},
     *      {wait: '9s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 160}}
     * ]);

     *
     * loop

     * <var>loop</var> calls a custom animation function using ##loop() during the given time.
     *
niaAnim.timeline([
     *      {loop: function(t) {
     *          var dg = t / 5000 * 2 * Math.PI;
     *          $('.block').set({$left: 100 + Math.cos(dg) * 80, $top: 100 + Math.sin(dg) * 80});
     *       }, duration: '5000ms'}
     * ]);

     *
     * callback

     * <var>callback</var> specifies a function that is called when timeline reaches the callback's position:
     *
niaAnim.timeline([
     *      {keyframe: '.block', auto: ['$top', '$left'], wait: '1s'},
     *      {callback: function(t, fwd) { _.log('Current position: ', t, 'Is forward? ', fwd); }},
     *      {wait: '1s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 20}}
     * ]);

     *
     * You can use the <var>forward</var> and <var>backward</var> flags to call the function only when moving into
     * a specific direction. Otherwise it is called in both directions.
     *
niaAnim.timeline([
     *      {keyframe: '.block', auto: ['$top', '$left'], wait: '1s'},
     *      {callback: function(t) { _.log('Going backward! ', t); }, forward: false, backward: true},
     *      {wait: '1s'},
     *      {keyframe: '.block', props: {$top: 160, $left: 20}}
     * ]);

     *
     * show/hide

     * <var>show</var> and <var>hide</var> are properties that can be added to any timeline item. They take any value accepted by ##dollar#$()
     * as value (selector, object or list), and will call ##show() or ##hide() on the selected elements.
     *
niaAnim.timeline([
     *      {wait: '1s'},
     *      {show: '.extraText', wait: '3s'},
     *      {hide: '.extraText'}
     * ]);

     *
     * set

     * <var>set</var> calls ##set() on the elements defined by its value. Any value accepted by ##dollar#$()
     * can be used (selector, object or list). <var>set</var> requires a second property <var>props</var>
     * to define the properties to set.
     *
niaAnim.timeline([
     *      {wait: '1s'},
     *      {set: '.changingText', props: {$color: 'red'}}
     * ]);

     *
     * timeline

     * <var>timeline</var> allows you to embed a nested timeline. This is mostly useful if you have several independent things
     * going on at the same time in your animation. Often <var>timeline</var> is used with arrays, as arrays allow you to
     * execute things in parallel and then wait for all of them to end.
     *
niaAnim.timeline([
     *      [
     *          {timeline: [{dial: myDial, wait: '2s'}]},
     *          {timeline: [{toggle: someToggle, wait: '3s'}]}
     *      ],
     *      {callback: function() { $('#resultText').text('All timelines finished'); }}
     * ]);

     *
     * Like <var>dial</var>, <var>timeline</var> supports the <var>repeat</var> property:
     *
niaAnim.timeline([
     *      {timeline: [
     *          {keyframe: '#backAndForth', props: {$left: 0}, wait: '2s'}]},
     *          {keyframe: '#backAndForth', props: {$left: 100}}]}
     *      ], repeat: 10, duration: '20s'}
     * ]);

     * Please note that embedded timelines will inherit the parent's context, but they will not have access to the parent's keyframes.
     *
     * @param ctx optional a selector context for all keyframe selectors in the timeline. Any value supported by ##dollar#$() can be used here.
     * @param timelineDescriptor a list of timeline elements. The timeline elements will be described above.
     * @return the timeline function.  When called without arguments as function(), it will return the duration of the timeline.
     *              When called with arguments as function(t, stop)., it will render the animation frame given by <var>t</var>.
     *

t

The time to render. Typially in ms since the beginning of the animation, but theoretically a timeline can
     *                         use any time unit. If <var>t</var> is null or not given, the function returns the timeline duration.

     *
stop

A callback function() without arguments that the timeline will invoke when the animation is completed.
     *                           This happens when you invoke it with a <var>t</var> that's greater than the timeline duration.

     *
(callback return value)

If <var>t</var> was not given, the duration of the timeline.
     *                     Typically in ms,  but the timeline is not aware of the units used, so it may be another unit.<
     *                     If <var>t</var> had a value, the timeline function returns nothing (<var>undefined</var>).

     *
     * @see ##play() is designed to execute timeline functions.
     * @see ##playToggle() lets you control timeline execution with a toggle.
     * @see ##loop() is a barebones way to execute a timeline, but sufficient to execute it once. Please note that <var>loop()</var> is called
     *         "loop" because it executes an animation loop, but it does not loop the animation itself. You can use ##play()
     *         with the <var>autoLoop</var> argument to loop the animation.
     */

     // Creates a flexible timeline of events, toggles and animations. It does no impose any specific definition of time - it is suggested to use
     // it with milliseconds, but you could for example also control the time by the y scroll coordinate (see examples).
     //
     // The timeline is defined in a descriptor like this:
	 // [                 // list of items on the timeline, in the order they have been declared
     //     {wait: 100},                                   // waits for the given amount of time. Blocks timeline (the following items will be executed when finished).
     //     {dial: $('#a').dial({$fade:0},{$fade:1}), wait: 500},  // adds a dial that will be moved from 0 to 500 during the given duration. Blocks timeline.
     //     {dial: $('#b').dial({$fade:0},{$fade:1}), duration: 500},        // the same, but does not block the timeline (the following items will be executed immediately)
     //     {toggle: $('#e').toggle({$fade:0},{$fade:1}, 100), wait: 2000}, // toggle will be called with true on start, and with false when it ends. Blocks timeline.
     //     {toggle: $('#f').toggle({$fade:0},{$fade:1}, 100), duration: 2000},        // the same, but does not block the timeline
     //     {loop: function(t){}, wait: 500},          // callback will be called repeatedly for the duration with t the time in ms since it has been reached
     //     {loop: function(t){}, duration: 500},          // same, but non-blocking
     //     {timeline: timelineDescriptor}            // sub-timeline.
     //     {callback: function(tSinceStart){}} , // simple callback, will be called when reached/passed on timeline.
     //     {callback: function(tSinceStart){}, forward: false, backward: true} , // the same, but only called when passed going backward. Default is forward and backward.
     //     {dial: $('#g').dial({$fade:0},{$fade:1}), wait: 500, duration: 100},  // combines wait and duration. animates for 100, but blocks for 500
     //     {show: '#invisible', hide: '#visible'},                                   // shows/hides elements using show()/hide(). Can be used everywhere.
     //     // execute several items in parallel. Blocks until all blocking items are done.
     //     [{dial: $('#h').dial({$fade:0},{$fade:1}), wait: 500}, {toggle: $('#i').toggle({$fade:0},{$fade:1}, 100), wait: 2000},]: $('#h').dial({$fade:0},{$fade:1}), wait: 500}, {toggle: $('#i').toggle({$fade:0},{$fade:1}, 100), wait: 2000}],
     //     {keyframe: '.elem', props: {'@x': 34}, wait: 50},                      // keyframe animation: animates x to 34. Uses auto-smooth. Next step in 50.
     //     {keyframe: '.elem', props: {'@x': 55}, start: 61, wait: 10},           // keyframe animation: absolute positioning. Next element in 10.
     //     {keyframe: '.elem', props: {'@x': 30}, linear: true, wait: 10},        // animates x to 30. Linear anim. Next step in 10.
     //     {keyframe: '.elem', props: {'@x': 50}, velocity: {'@x': 2}, wait: 50}, // animates x to 50. Has given velocity at this keyframe. Next step in 50.
     //     {keyframe: '.elem', props: {'@x': 10}, velocityBefore: {'@x': -2}, velocityAfter: {'@x': 2}, wait: 50}, // animates x to 10. Velocity changes from -2 to 2 instantly.
     //     {keyframe: '#elem', auto: ['@x'], wait: 50},                           // Uses the initial value for @x as key frame instead of value in props.
     //     {keystop: '.elem', props: {'@x': 100}},                                // same as {keyframe: '#elem', props: {'@x': 10}, velocity: {'@x': 0}},
     // ]
     //
     // Each entry can have only either keyframe, dial, toggle, loop, timeline or callback.
     // show, hide can be combined with any of the elements above and with each other, but will also work stand-alone.
     //
     // Returns a timeline function function(t, stop):
     //  - t is the time passed since the start in ms,
     //  - stop is an optional function that will be called by the timeline function when it was invoked with t>=duration
     // The timeline can be plugged right into $.loop(), or be used on its own.
     // Call the timeline function without arguments to make it return its duration.
	function timeline(ctx0, timelineDescriptor) {
		var ZERO_LIST = _(0);

		if (!timelineDescriptor)
			return timeline(null, ctx0);

		// processes an item in the timelineDescriptor, as given by the user. Returns one or more normalized items with t* properties.
		function processItem(prevBlockingEnd, e) {
			if (_.isIterable(e)) {
				if (_.isEmpty(e))
					return null;
				var blockingEnd = prevBlockingEnd;
				var ll = _.map(e, function(li) {
					var p = processItem(prevBlockingEnd, li);
					blockingEnd = Math.max(blockingEnd, p.tBlockingEnd);
					p.tBlockingEnd = prevBlockingEnd;
					return p;
				});
				ll.last().tBlockingEnd = blockingEnd;
				return ll;
			}
			else if (_.isFunction(e))
				return processItem(prevBlockingEnd, {callback: e});
			else {
				if (e.timeline)
					e.tTimeline = _.isFunction(e.timeline) ? e.timeline : timeline(ctx, e.timeline);
				var tStart = e.start != null ? _.parseDuration(e.start) : prevBlockingEnd;
				var tWait = _.parseDuration(e.wait) || (e.tTimeline && e.tTimeline()) || 0;
				return _.extend({}, e, {
					tStart: tStart,
					tBlockingEnd: tStart+tWait,
					tDuration: (e.loop || e.tTimeline || e.dial || e.toggle) ? (e.duration != null ? _.parseDuration(e.duration) : tWait) : 0,
					tForward: e.forward == null ? true : e.forward,
					tBackward: e.backward == null ? true : e.backward,
					tContent: !!(e.loop || e.timeline || e.dial || e.toggle || e.callback || e.show || e.hide || e.set),
					tKeyFrame: !!(e.keyframe || e.keystop),
					tNoDeactivation: !!e.loop,
					tNoDeactivationBack: !!e.loop
				});
			}
		}

		function processKeyFrameItems(items) {
			var keyframePos = {};        // nodeId -> property -> {kf: keyFrame, prevTime: 0, item: item, values: valArray, preValues: valArray, useCount: 0}
			var targetListByNodeId = {}; // used by setters only
			var setters = {};            // time -> nodeId -> {propName: propValue}

			function addSetter(time, nodeId, targetList, name, value) { // setters are single keyframes without follower
				var sTime, sNode;
				if (!(sTime = setters[time]))
					setters[time] = sTime = {};
				if (!(sNode = setters[time][nodeId]))
					sTime[nodeId] = sNode = {};
				sNode[name] = value;

				if (!targetListByNodeId[nodeId])
					targetListByNodeId[nodeId] = targetList;
			}
			function processKeyFrame(kf) {
				var kfTarget = kf.keyframe || kf.keystop;
				var stopFrame = !!kf.keystop;

				function processProps(targetList) {
					var newItems = [];
					var nodeId = targetList.nodeId();
					var target = keyframePos[nodeId];
					if (!target)
						target = keyframePos[nodeId] = {};

					function processProperty(propName, propValueS) {
						var posEntry = target[propName];
						var newValues = _(extractInterpolatable(propValueS));
						if (posEntry == null) // 1st keyframe for the property
							target[propName] = {kf: kf, nodeId: nodeId, targetList: targetList, value: propValueS, values: newValues, useCount: 0};
						else {
							var readVelocity = function(velocityObj, propName, duration) {
								if (velocityObj != null) {
									if (_.isValue(velocityObj))
										return  _(velocityObj*duration);
									else if (_.isIterable(velocityObj))
										return _.map(velocityObj, function(e) { return e * duration; });
									else
										return readVelocity(velocityObj[propName], propName, duration);
								}
								else
									return null;
							};

							var tDuration = kf.tStart - posEntry.kf.tStart;
							var tLinear = !!posEntry.kf.linear;
							var vv, veloStartUserSet, veloStart = ZERO_LIST, veloEndUserSet, veloEnd = ZERO_LIST;
							if (posEntry.prevLinear) {
								veloStart = posEntry.item.tVeloEnd;
								veloStartUserSet = true;
							}
							if ((vv = readVelocity(posEntry.kf.velocity, propName, tDuration)) != null) {
								veloStart = vv;
								veloStartUserSet = true;
							}
							if ((vv = readVelocity(posEntry.kf.velocityAfter, propName, tDuration)) != null) {
								veloStart = vv;
								veloStartUserSet = true;
							}
							if ((vv = readVelocity(kf.velocity, propName, tDuration)) != null) {
								veloEnd = vv;
								veloEndUserSet = true;
							}
							if ((vv = readVelocity(kf.velocityBefore, propName, tDuration)) != null) {
								veloEnd = vv;
								veloEndUserSet = true;
							}

							if (tLinear) {
								var v = _.map(posEntry.values, function(val, index) {
									return newValues.at(index) - val;
								});
								veloStart = veloEnd = v;
								veloStartUserSet = veloEndUserSet = true;

								if (posEntry.item && !posEntry.item.tVeloEndUserSet)
									posEntry.item.tVeloEnd = v;
							}
							else if (posEntry.preValues != null) { // >= 3rd keyframe: correct velocity of PREVIOUS entry and use as start for this entry
								var d = (kf.tStart - posEntry.prevTime) || 1;
								var v = _.map(posEntry.preValues, function(val, index) {
									return (newValues.at(index) - val) / d;
								});
								if (!posEntry.item.tVeloEndUserSet)
									posEntry.item.tVeloEnd = v.map(function(e) { return e * posEntry.item.tDuration; });
								if (!veloStartUserSet)
									veloStart = v.map(function(e) { return e * tDuration; });
								posEntry.item.tNoDeactivation = true;
							}
							var newItem = {tStart: posEntry.kf.tStart, tDuration: tDuration, tTarget: targetList,
								tPropName: propName, tPropTemplate: propValueS, tFrom: posEntry.values, tTo: newValues,
								tVeloStart: veloStart, tVeloEnd: veloEnd,
								tVeloStartUserSet: veloStartUserSet, tVeloEndUserSet: veloEndUserSet,
								tNoDeactivationBack: posEntry.preValues != null, tLinear: tLinear};
							posEntry.prevTime = posEntry.kf.tStart;
							posEntry.preValues = posEntry.values;
							posEntry.prevLinear = posEntry.kf.linear;
							posEntry.values = newValues;
							posEntry.item = newItem;
							posEntry.kf = kf;
							posEntry.useCount++;
							newItems.push(newItem);

							if (stopFrame) {
								if (!posEntry.useCount)
									addSetter(kf.tStart, nodeId, targetList, propName, propValueS);
								delete target[propName];
							}
						}
					}
					_.each(kf.auto, function(propName) {
						processProperty(propName, targetList.get(propName));
					});
					_.eachObj(kf.props, processProperty);
					return _(newItems);
				}
				return $(kfTarget, ctx).collectPer(processProps);
			}

			if (_.isEmpty(items))
				return;

			var sortedItems = _.sort(items, function(a, b) { return a.tStart - b.tStart; });
			var itemMap = {}; // tStart -> tDuration -> tNoDeactivationBack -> []
			sortedItems.collect(processKeyFrame).each(function addItemToMap(item) {
				var tStart = item.tStart, tDuration = item.tDuration, tNoDeactivationBack = item.tNoDeactivationBack;
				if (!itemMap[tStart])
					itemMap[tStart] = {};
				if (!itemMap[tStart][tDuration])
					itemMap[tStart][tDuration] = {};
				if (!itemMap[tStart][tDuration][tNoDeactivationBack])
					itemMap[tStart][tDuration][tNoDeactivationBack] = [item];
				else
					itemMap[tStart][tDuration][tNoDeactivationBack].push(item);
			});

			var resultItems = [];

			// add dials for animations
			_.eachObj(itemMap, function(tStart, itemMapByStart) {
				_.eachObj(itemMapByStart, function(tDuration, itemMapByDuration) {
					_.eachObj(itemMapByDuration, function(tNoDeactivationBack, itemArray) {
						var it = itemArray[0];
						var newItem = {tStart: it.tStart, tDuration: it.tDuration, tNoDeactivationBack: it.tNoDeactivationBack, // why t.tStart instead of tStart???
								tBlockingEnd: it.tStart, tForward: true, tBackward: true, tContent: true
						};
						if (itemArray.length == 1)
							newItem.dial = singlePropVeloDial(it.tTarget, ctx, it.tPropName, it.tPropTemplate, it.tFrom, it.tVeloStart, it.tTo, it.tVeloEnd);
						else
							newItem.dial = createVeloDialListFuncs(itemArray, ctx);
						resultItems.push(newItem);
					});

				});
			});

			// add items for setting values
			_.eachObj(keyframePos, function(nodeId, lByP) {
				_.eachObj(lByP, function(propName, targetObj) {
					if (!targetObj.useCount)
						addSetter(targetObj.kf.tStart, nodeId, targetObj.targetList, propName, targetObj.value);
				});
			});
			_.eachObj(setters, function(time, nm) {
				_.eachObj(nm, function(nodeId, props) {
					resultItems.push({tStart: time, tDuration: 0, tNoDeactivationBack: false,
								tBlockingEnd: time, tForward: true, tBackward: true, tContent: true,
								set: targetListByNodeId[nodeId], props: props});
				});
			});

			return _(resultItems);
		}

		// make td a flat list of items, with additional t* properties
		var ctx = ctx0 && $(ctx0);
		var endOfTimeline = 0;
		var prevBlockingEnd = 0;
		var keyframeItems = [];
		var td = _.collect(timelineDescriptor, function(e) {
			return _(processItem(prevBlockingEnd, e)).collect(function(r) {
				endOfTimeline = Math.max(endOfTimeline, r.tBlockingEnd, r.tDuration != null ? r.tStart+r.tDuration : 0);
				prevBlockingEnd = r.tBlockingEnd;
				if (r.tKeyFrame) {
					keyframeItems.push(r);
					if (r.show || r.hide)
						return {show: r.show, hide: r.hide, tStart: r.tStart, tDuration: 0, tForward: true, tBackward: true};
					else
						return null;
				}
				return r.tContent ? r : null;
			});
		});
		var extraKfItems = processKeyFrameItems(keyframeItems);
		if (extraKfItems)
			td = _(td, extraKfItems);

        // create a list of all activations and deactivations that is used to activate/deactivate in the right order
		var createTimeEvent = function (forward, e) {
			if (forward ? e.tForward : e.tBackward) {
				if (e.tDuration == null && !e.tNoDeactivation)
					return [{time: e.tStart, forwardActive: forward, item: e}, {time: endOfTimeline, forwardActive: !forward, item: e}];
				else if (e.tDuration > 0 && !e.tNoDeactivation)
					return [{time: e.tStart, forwardActive: forward, item: e}, {time: e.tStart+e.tDuration, forwardActive: !forward, item: e}];
				else
					return {time: e.tStart, forwardActive: true, item: e};
			}
		};

		var eventTimeline = td.collect(_.partial(createTimeEvent, true)).sort(function(a, b) { return a.time - b.time; });
		var reverseTimeline = td.collect(_.partial(createTimeEvent, false)).sort(function(a, b) { return b.time - a.time; });

		var lastT = null;
		return function(t, stop) {
			if (t == null)   // if no arg/no t -> return duration
				return endOfTimeline;
			if (t == lastT) // no time change -> nothing to do
				return;
			if (t >= endOfTimeline && lastT != null && lastT >= endOfTimeline) { // animation already ended
				lastT = t;
				return;
			}

			var tSpanLast = lastT == null ? -1 : lastT;
			var tSpanNow = Math.min(t, endOfTimeline);
			var backward = tSpanLast > tSpanNow;
			lastT = t;
			function isInTimeSpan(t0) {
				return backward ? (t0 < tSpanLast && t0 >= tSpanNow) : (t0 > tSpanLast && t0 <= tSpanNow);
			}

			(backward ? reverseTimeline : eventTimeline).each(function(event) {
				var item = event.item;
				var relT = tSpanNow - item.tStart;

				var itemDuration = item.tDuration != null ? item.tDuration : endOfTimeline - item.tStart;
				var itemEnd = item.tDuration != null ? item.tStart+item.tDuration : endOfTimeline;
				var itemIsRunnable = item.loop || item.dial || item.tTimeline;
				var repeat = item.repeat || 1;
				var itemInProgress = itemIsRunnable && itemDuration > 0 && t >= item.tStart && t < itemEnd;
				var eventInTimeSpan = isInTimeSpan(event.time);
				var jumpedOverEvent = backward ? isInTimeSpan(item.tStart) : isInTimeSpan(itemEnd);
				if (eventInTimeSpan) {
					if (item.show && !backward)
						$(item.show, ctx).show();
					if (item.hide && backward)
						$(item.hide, ctx).show();

					if (!itemInProgress && !(backward ? item.tNoDeactivationBack : item.tNoDeactivation)) {
						if (event.forwardActive) {
							if (item.toggle && !jumpedOverEvent)
								item.toggle(true);
							if (item.callback)
								item.callback(tSpanNow, !backward);
							if (item.set && item.props)
								$(item.set, ctx).set(item.props);
						}
						else {
							if (item.toggle)
								item.toggle(false);
							else if (item.dial)
								item.dial(backward ? 0 : (repeat % 1 == 0 ? 1 : repeat % 1));
							else if (item.tTimeline)
								item.tTimeline(backward ? 0 : (repeat % 1 == 0 ? itemDuration : (repeat % 1) * itemDuration));
						}
					}
				}
				// Regular anim
				if (itemInProgress && event.forwardActive) {
					if (item.dial)
						item.dial(relT == itemDuration ? 1 : (relT * repeat / itemDuration) % 1);
					else if (item.loop)
						item.loop(relT);
					else if (item.tTimeline)
						item.tTimeline(relT == itemDuration ? itemDuration : (relT * repeat) % itemDuration);
				}
				if (eventInTimeSpan) {
					if (item.hide && !backward && event.forwardActive)
						$(item.hide, ctx).hide();
					if (item.show && backward)
						$(item.show, ctx).hide();
				}
			});

			if (t >= endOfTimeline && stop)
				stop();
		};
	}

     /*$
     * @id animateset
     * @name list.animateSet()
     * @syntax list.animateSet(propertyName, value)
     * @syntax list.animateSet(propertyName, value, velocity)
     * @syntax list.animateSet(propertyName, value, velocity, continueAnimation)
     * @syntax list.animateSet(propertyName, value, velocity, continueAnimation, overrideLimits)
     * @syntax list.animateSet(propertyValueMap)
     * @syntax list.animateSet(propertyValueMap, velocityMap)
     * @syntax list.animateSet(propertyValueMap, velocityMap, continueAnimation)
     * @syntax list.animateSet(propertyValueMap, velocityMap, continueAnimation, overrideLimits)
     * @module ANIMATION
     *
     * Sets the value and velocity of the given properties for all list nodes. This function is to be used in conjunction with
	 * ##animateTo(), ##accelerate() and ##decelerate(). By default, setting properties will stop any animation, unless the
	 * <var>continueAnimation</var> parameter is set.
	 * <var>animateSet</var> also respects any limits set using ##animateLimit(), unless the <var>overrideLimis</var>
	 * parameter is set. If you attempt to set a value outside of the limits, it will be set to the closest limit.
     *
     * <var>animateSet</var> must be used instead of the regular ##set() if you need to set any value that's
	 * supposed to be animated using ##animateTo(), ##accelerate() or ##decelerate(). Only HTML nodes in the list are supported, as it uses
	 * ##dataset#data() to manage animated nodes.
	 *
	 * @example A simple animation of a number and a color:
	 *
$('.animated').animateSet({$top: '0px', $backgroundColor: '#333'})
	 *                    .animateTo({$top: '150px', $backgroundColor: '#fff'}, '0.6s');
	 *

	 *
     * @param propertyName the name of the property to set, supporting the full ##set() prefixes, like '$propertyName' for CSS styles and more.
     * @param value optional the value to set for the property. Can be a number or string (no functions or objects). Nulls will be ignored.
     * @param velocity optional if set, the the current velocity of the property in the value's unit per ms. Can be a list of velocities
	 *                 if the value is a string containing several numbers. If omitted,
	 *                 the property's velocity will be set to 0. Please note that setting the velocity
	 *                 by itself does not start any animation. You're only setting the initial velocity of an animation, or change the animation
	 *                 while an animation is running.
     * @param continueAnimation optional if set and true, any running animation will continue. Otherwise the ongoing animation will be stopped
	 *                          immediately.
     * @param overrideLimits optional if set and true, it is possible to set values outside of the boundaries defined using ##animateLimit().
	 * @param propertyValueMap a map of property names as keys and value strings or numbers as values. This allows you to set more than one
	 *        property at a time.
	 * @param velocityMap optional a map of property names as keys and the velocity and the value's velocity in unit per ms as values. All properties
	 *                    that do not appear here will be set to 0 velocity.
     * @return this list
	 * @see ##animateTo() animates using the initial values set with <var>animateSet</var>.
     */
	function animateSet(propertyName, value, velocity, continueAnimation, overrideLimits) {
		if (_.isObject(propertyName))
			_.eachObj(propertyName, function(n, v) {
				this.animateSet(n, v, value ? value[n] : null, velocity, continueAnimation);
			}, this);
		else
			this.per(function(el) {
				var state = getAnimateState(el, propertyName);
				if (value != null) {
					el.set(propertyName, value);
					var interpolatables = extractInterpolatable(value);
					if ((state.maxLimits.length || state.minLimits.length) && !overrideLimits)
						_.each(interpolatable, function(vi, index) {
							var l;
							if ((l = maxLimits[index]) != null && vi > l)
								interpolatable[index] = l;
							if ((l = minLimits[index]) != null && vi < l)
								interpolatable[index] = l;
						});
					state.valueTpl = value;
					state.values = interpolatables;
				}
				state.velocities = _(velocity || []);
				if (state.loopFunc && !continueAnimation) {
					state.accelerations = state.decelerations = [];
					state.loopFunc = null;
				}
			});
		return this;
	}

	/*$
     * @id animatelimit
     * @name list.animateLimit()
     * @syntax list.animateLimit(propertyName, minLimit, maxLimit)
     * @syntax list.animateLimit(propertyName, minLimit, maxLimit, onOutOfLimit)
     * @syntax list.animateLimit(propertyName, minLimit, maxLimit, onOutOfLimit, onInLimit)
     * @syntax list.animateLimgit(propertyName, minLimit, maxLimit)
     * @syntax list.animateLimit(minLimitMap, maxLimitMap)
     * @syntax list.animateLimit(minLimitMap, maxLimitMap, onOutOfLimit)
     * @syntax list.animateLimit(minLimitMap, maxLimitMap, onOutOfLimit, onInLimit)
     * @module ANIMATION
     *
     * Limits the possible values of the given properties for all list nodes to be within the given boundaries. Limits are only respected by the functions
	 * ##animateSet(), ##animateTo(), ##accelerate() and ##decelerate(). The exact effect of the limit depends on the function:
	 *

##animateSet() will not allow target values outside of the bounds, unless the <var>overrideLimis</var> parameter is used.
	 *         Instead the nearest limit will be used as target. <var>animateTo</var> will also not call the limit's callbacks.

	 *
##animateTo() will not allow setting values outside of the bounds, unless the <var>overrideLimis</var> parameter is used.
	 *         If limits are active and it is called with a value outside of the limits, the closest limit will be set as new value.
	 *         <var>animateSet</var> will never call the limit's callbacks.

	 *
##accelerate() and ##decelerate() will, by default, stop an animation when the limit has been reached. You can change the default behaviour
	 *     using the callbacks <var>onOutOfLimit</var> and <var>onInLimit</var> for other effects.

	 *

     *
	 * <var>animateLimit</var> allows you to specify callbacks that will be invoked when a limit has been crossed. Please note that will
	 * only be called when the limit is crossed after it has been established. Calling this function while the value is already out
	 * will not invoke any callbacks nor stop animations.
	 *
	 * @example Setting limits for an animation. When the element touches a border, the animation stops.
	 *
$('.animated').animateLimit({$top: 0, $left: 0}, {$top: 100, $left: 100})
	 *               .animateSet({$top: '50px', $left: '50px'}, {$top: Math.random(), $left: Math.random()})
	 *               .decelerate({$top: 0.25, $left: 0.25});
	 *

	 *
	 * @example Setting limits for an animation. When the element touches a border, the animation bounces back.
	 *
$('.animated').animateLimit({$top: 0, $left: 0}, {$top: 100, $left: 100},
	 *                function(pos, v, a, d) { return {velocity: -v}; })
	 *               .animateSet({$top: '50px', $left: '50px'}, {$top: Math.random(), $left: Math.random()})
	 *               .decelerate({$top: 0.25, $left: 0.25});
	 *

	 *
     * @param propertyName the name of the property to limit, supporting the full ##set() prefixes, like '$propertyName' for CSS styles and more.
     * @param minLimit optional the minimum value for the property. Can be a number, a string or an array of numbers. Arrays are needed when
	 *                 the property contains more than one animated properties. If omitted or null, any minimum limit will be removed.
	 * @param maxLimit optional the maximum value for the property. Can be a number, a string or an array of numbers.
	 *                 If omitted or null, any minimum limit will be removed.
     * @param onOutOfLimit optional an optional callback function(state) that will be called when the value crosses a limit.
	 *                     The state object has the following properties:
	 *

value

the property's current value as a number.

	 *
velocity

the current velocity (in the value's unit per ms).

	 *
acceleration

the current acceleration.

	 *
deceleration

the current deceleration.

	 *
stash

This value will be passed to the next <var>onInLimit</var> or <var>onOutOfLimit</var> invocation.

	 *                     You can modify the state object to change the animation (but only set either acceleration or deceleration to non-zero).
	 *                     If you do not specify a callback, <var>velocity</var>, <var>acceleration</var> and <var>deceleration</var> will be set to 0
	 *                     in order to prevent crossing the boundary.
     * @param onInLimit optional an optional callback function(state) that will be called when the value was outside of the limit and
	 *                  returns back into the limits. Note that this won't be called if a value has been kept from crossing by the
	 *                  <var>onOutOfLimit</var> handler. See <var>onOutOfLimit</var> for a definition of the state.
	 *                  If you do not spcify a callback, all values will be kept unmodified.
	 * @param minLimitMap a map of property names as keys and limit strings, numbers or number arrays as values. This allows you to set more than one
	 *        property at a time.
	 * @param maxLimitMap a map of property names as keys and limit strings, numbers or number arrays as values. This allows you to set more than one
	 *        property at a time.
     * @return this list
	 * @see ##animateTo() respects limits set by <var>animateLimit</var>.
	 * @see ##accelerate() respects limits set by <var>animateLimit</var>.
	 * @see ##decelerate() respects limits set by <var>animateLimit</var>.
     */
	function animateLimit(propertyName, minLimit, maxLimit, onOutOfLimit, onInLimit) {
		if (_.isObject(propertyName))
			_.eachObj(_.extend({}, propertyName, minLimit), function(n) {
				this.animateLimit(n, propertyName[n], minLimit[n], maxLimit, onOutOfLimit);
			}, this);
		else
			this.per(function(el) {
				var state = getAnimateState(el, propertyName);
				state.minLimits = minLimit == null ? [] : _.isIterable(minLimit) ? _.array(minLimit) : extractInterpolatable(minLimit);
				state.maxLimits = maxLimit == null ? [] : _.isIterable(maxLimit) ? _.array(maxLimit) : extractInterpolatable(maxLimit);
				state.outOfLimitCallback = onOutOfLimit;
				state.backInLimitsCallback = onInLimit;
			});
		return this;
	}

	/*$
     * @id animateto
     * @name list.animateTo()
     * @syntax list.animateTo(propertyName, value)
     * @syntax list.animateTo(propertyName, value, duration)
     * @syntax list.animateTo(propertyName, value, duration, interpolator)
     * @syntax list.animateTo(propertyName, value, duration, interpolator, overrideLimits)
     * @syntax list.animateTo(propertyValueMap)
     * @syntax list.animateTo(propertyValueMap, duration)
     * @syntax list.animateTo(propertyValueMap, duration, interpolator)
     * @syntax list.animateTo(propertyValueMap, duration, interpolator, overrideLimits)
     * @module ANIMATION
     *
     * Animates the given properties of the list by transitioning from their current values to the given target values.
	 * It is somewhat similar to the Core Module's ##animate() function, but designed
	 * to allow smooth effects when interacting with the user. <var>animateTo</var> has the following differences:
	 *

You can invoke it on an property that is already animated using <var>animateTo</var>, ##accelerate() or ##decelerate(). The
	 *         running animation will be automatically stopped.

	 *
If you call it on a property that's currently animated, its current velocity will be taken into account
	 *         to allow a smooth transition.

	 *
Instead of a duration, you can specify a velocity for the animation as a whole. <var>animateTo</var> will then compute the duration for
	 *         the property. This is often better than a
	 *         fixed duration, as you can take the value difference (distance) into account.

	 *
It respects limits set by ##animateLimit() and will only animate to the nearest limit.

	 *

	 *
	 * When you use ##animateTo() on a property, you should not modify it with ##set(), but only with ##animateSet(). Values
	 * set by ##set() after the initial value will not be used for animations.
	 *
	 * @example A simple animation of a number and a color:
	 *
$('.animated').animateSet({$top: '0px', $backgroundColor: '#333'})
	 *                    .animateTo({$top: '150px', $backgroundColor: '#fff'}, '0.6s');
	 *

	 *
	 * @example Using a velocity to have the duration depend on the start value:
	 *
$('.animated').animateSet({$top: Math.random() * 100})
	 *                    .animateTo({$top: 150}, '40px/s');
	 *

	 *
     * @param propertyName the name of the property to animate, supporting the full ##set() prefixes, like '$propertyName' for CSS styles and more.
     * @param value the target value of the property. Can be a number or string (no functions or objects). If it is a string, it will
	 *              find all numbers in that string and transition them. Because of that, it's important that the start value has the same unit
	 *              and format as the target value. To be sure that this is the case, set a start value with ##animateSet().
	 *              If you set a CSS property ('$' prefix) and pass a number as target, the unit 'px' will be appended for most values (see ##set()).
     * @param duration optional the duration of the animation. You can specify it as number of milliseconds, as a format for ##parseDuration(), like
	 *                  '5.4ms' or you can specify a velocity for ##parseVelocity() like '2.3px/ms'. In the latter case, <var>animateTo</var>
	 *                  will compute the duration indivually for each property and each list member using the value delta and the given velocity.
	 *                  The largest calculated value will be used for the animation.
	 *                  Defaults to 500ms.
	 * @param interpolator optional an interpolation function(startValue, endValue, t) which will be
	 *             called every time an interpolated value is required:
	 *

	 *
startValue

The start value of the transition.

	 *
endValue

The end value of the transition.

	 *
t

A value between 0 and 1 that specifies the state of the transition.

	 *
(callback return value)

The value at the time <var>t</var>.

	 *

	 *             ##ease#niaAnim.ease## provides a large selection of compatible interpolators.
     * @param overrideLimits optional if set and true, it is possible to set target values outside of the boundaries defined using ##animateLimit().
	 *                       Without <var>overrideLimits</var>, it will animate to the nearest boundary instead.
	 * @param propertyValueMap a map of property names as keys and targets as values. This allows you to set more than one property at a time.
     * @return a promise representing the animation with ##stop() support. Will be fulfilled if the animation ended regularly, and
	 *         rejected if it had been stopped before it was completed. Please note that you can prolong the animation
	 *         by calling ##animateTo() or ##accelerate() before the animation completed.
	 * @see ##animateSet() must be used to set start values. It can also stop animations.
	 * @see ##animateLimit() sets boundaries that will be used by <var>animateTo</var>.
     */
	function animateTo(propertyNames, duration, interpolator, overrideLimits, extra) {
		var durationMs = 0;
		if (!_.isObject(propertyNames)) {
			var names = {};
			names[propertyNames] = duration;
			return this.animateTo(names, interpolator, overrideLimits, extra);
		}
		else
			return joinPromises(_.mapObjToList(propertyNames, function(propertyName, value) {
				this.per(function(el) {
					var state = getAnimateState(el, propertyName);
					runAnimateLoop(state);
					state.accelerations = state.decelerations = [];
					state.valueTpl = value;

					var endValueList = _.map(extractInterpolatable(value), function(v, i) {
						if (overrideLimits)
							return v;
						else {
							var l;
							if ((l = state.maxLimits[i]) != null && v > l)
								return l;
							else if ((l = state.minLimits[i]) != null && v < l)
								return l;
							else
								return v;
						}
					});
					var startValueList = endValueList.map(function(ev, i) { return state.values[i] || 0; });
					var startVelocityList = endValueList.map(function(ev, i) { return state.velocities[i] || 0; });
					var deltaAbs = endValueList.map(function(v, i) { return Math.abs(v - startValueList[i]); });
					var propDurationMs = duration == null ? 500 : parseVelocity(duration, _.call(Math.max, Math, deltaAbs));
					durationMs = Math.max(durationMs, propDurationMs);

					if (propDurationMs) {
						state.loopFunc = function(timestamp) {
							var t = Math.min(timestamp, durationMs) / durationMs;
							var vals = state.values, vels = state.velocities;
							vals.length = vels.length = endValueList.count();
							var animationEnds = timestamp >= durationMs;
							for (var i = 0; i < vals.length; i++) {
								var startValue = startValueList.at(i);
								var endValue = endValueList.at(i);
								var startVelocity = startVelocityList.at(i);

								if (interpolator) {
									vals[i] = interpolator(startValue, endValue, t);
									vels[i] = getVelocity(interpolator, startValue, endValue, t);
								}
								else {
									vals[i] = veloInterpolator(startValue, endValue, t, startVelocity, 0);
									vels[i] = veloInterpolator(startValue, endValue, t, startVelocity, 0, true);
								}

								if (animationEnds)
									vels[i] = 0;
							}
							el.set(propertyName, setInterpolatablesArray(value, vals));
							if (animationEnds)
								state.loopFunc = null;
						};
						state.loopStartTime = state.loopTimestamp;
						return state.promise;
					}
					else
						state.loopFunc = null;
				});
			}, this));
	}

	function computeDuration(startPos, velo, accel, targetPos) {
		if (targetPos == null)
			return null;
		else if (accel) {
			var x = velo*velo + 2*(targetPos-startPos)*accel;
			if (x < 0)
				return null;
			var xs = Math.sqrt(x);
			var t1 = (-velo + xs) / accel;
			var t2 = -(velo + xs) / accel;
			if (t1 <= 0)
				return t2 <= 0 ? null : t2;
			else
				return t2 <= 0 ? t1 : Math.min(t1, t2);
		}
		else if (velo) {
			var t = (targetPos - startPos)/velo;
			return t >= 0 ? t : null;
		}
		else
			return null;
	}

	function sign(n) {
		return n > 0 ? 1 : (n < 0 ? -1 : 0);
	}

 	/*$
     * @id accelerate
     * @name list.accelerate()
     * @syntax list.accelerate(propertyName, acceleration, targetVelocity)
     * @syntax list.accelerate(propertyName, acceleration)
     * @syntax list.accelerate(propertyAccelerationMap)
     * @syntax list.accelerate(propertyAccelerationMap, targetVelocity)
     * @syntax list.accelerate(propertyAccelerationMap, targetVelocityMap)
     * @module ANIMATION
     *
     * Animates the given properties of the list by modifying its numerical values using the given acceleration
	 * The animation will run until for each property until the target velocity has been reached, if you specify one.
	 * Otherwise it runs forever, unless you ##stop() it,
	 * Call ##animateTo() with a duration on all values or interrupt it with ##animateSet().
	 *
	 * When you use ##accelerate() on a property, you should not modify it with ##set(), but only with ##animateSet(). Values
	 * set by ##set() after the initial value will not be used for animations.
	 *
	 * <var>accelerate()</var> supports limits set by ##animateLimit(). If the limit has been reached, the animation can be modified
	 * using a callback.
	 *
	 * @example A simple animation of bouncing element:
	 *
$('.animated').animateSet({$top: '0px'})
	 *                    .animateLimit('$top', 0, 100, function(p, v) { return {velocity: -v}; })
	 *                    .accelerate({$top: '9.81px^2/s');
	 *

	 *
     * @param propertyName the name of the property to animate, supporting the full ##set() prefixes, like '$propertyName' for CSS styles and more.
     * @param acceleration the new acceleration. Must be in the value's unit squared, divided by a time unit. You can omit the units
	 *                     if you specify the acceleration in ms. For multi-value properties you can specify a list or array of acceleration values.
	 * @param targetVelocity optional the velocity that must be reached to end the animation.
     * @param propertyValueMap a map of property names as keys and accelerations as values. This allows you to set more than one property at a time.
	 * @param targetVelocityMap optional a map of velocities for properties that will end the animation if the velocity has been reached.
     * @return a promise representing the animation with ##stop() support. Will be rejected when the animation was interrupted.
	 * @see ##animateSet() must be used to set start values. It can also stop animations.
	 * @see ##animateTo() can be combined with <var>accelerate</var> and allows seamless animation.
	 * @see ##animateLimit() sets boundaries that will be used by <var>accelerate</var>.
	 * @see ##decelerate() will slow down properties until they stop moving.
     */
	function accelerate(propertyName, acceleration, targetVelocity) {
		if (_.isObject(propertyName))
			return joinPromises(_.mapObjToList(propertyName, function(prop, accel) {
				return acDecelerate(this, prop, accel, null, _.isObject(acceleration) ? acceleration[prop] : acceleration);
			}, this));
		else
			return acDecelerate(this, propertyName, acceleration, null, targetVelocity);
	}

 	/*$
     * @id decelerate
     * @name list.decelerate()
     * @syntax list.decelerate(propertyName, acceleration)
     * @syntax list.decelerate(propertyName, acceleration, targetVelocity)
     * @syntax list.decelerate(propertyAccelerationMap)
     * @syntax list.decelerate(propertyAccelerationMap, targetVelocity)
     * @syntax list.decelerate(propertyAccelerationMap, targetVelocityMap)
     * @module ANIMATION
     *
     * Animates the given properties of the list by decelerating their velocity which has been previously set using ##animateSet(), ##animateTo()
	 * or ##accelerate().
	 * The animation will run until either the target velocity has been reached or you ##stop() it explicitly. By default, the target velocity is 0.
	 *
	 * When you use ##decelerate() on a property, you should not modify it with ##set(), but only with ##animateSet(). Values
	 * set by ##set() after the initial value will not be used for animations.
	 *
	 * <var>decelerate()</var> supports limits set by ##animateLimit(). If the limit has been reached, the animation can be modified
	 * using a callback.
	 *
	 * @example A simple animation a moving but decelerating element:
	 *
$('.animated').animateSet({$top: '0px'}, {$top: '4px/s')
	 *                    .decelerate({$top: '2px^2/s');
	 *

	 *
     * @param propertyName the name of the property to animate, supporting the full ##set() prefixes, like '$propertyName' for CSS styles and more.
     * @param deceleration the new deceleration. Must be in the value's unit, squared and divided by a time unit, e.g. "15px^2/s". You can omit units
	 *                     if you specify the acceleration in ms. For multi-value properties you can specify a list or array of acceleration values.
 	 * @param targetVelocity optional the velocity that must be reached to end the animation. If not given, it will be executed until the
	 *        animation stopped.
     * @param propertyValueMap a map of property names as keys and accelerations as values. This allows you to set more than one property at a time.
 	 * @param targetVelocityMap optional a map of velocities for properties that will end the animation if the velocity has been reached. Defaults to
	 *        0 for all properties.
     * @return a promise representing the animation with ##stop() support. Will be rejected when the animation was interrupted.
	 * @see ##animateSet() must be used to set start values. It can also stop animations.
	 * @see ##animateTo() can be combined with <var>accelerate</var> and allows seamless animation.
	 * @see ##animateLimit() sets boundaries that will be used by <var>accelerate</var>.
	 * @see ##accelerate() will accelerate changes in property values using a constant acceleration.
     */
	function decelerate(propertyName, deceleration, targetVelocity) {
		if (_.isObject(propertyName)) {
			return joinPromises(_.mapObjToList(propertyName, function(prop, decel) {
				return acDecelerate(this, prop, 0, decel, (_.isObject(deceleration) ? deceleration[prop] : deceleration) || 0);
			}, this));
		}
		else
			return acDecelerate(this, propertyName, 0, deceleration, targetVelocity || 0);
	}

	function joinPromises(promises) {
		if (_.count(promises) == 1)
			return _.first(promises);
		else {
			var prom = _.promise(promises);
			if (_.isEmpty(promises))
				prom.fulfill(0);
			return prom;
		}
	}

	function acDecelerate(list, propertyName, acceleration, deceleration, targetVelocity) {
		var promises = [];
		var accelList = _(acceleration);
		var decelList = _(deceleration);
		var targetVelocityList = _(targetVelocity);

		list.per(function(el) {
			var state = getAnimateState(el, propertyName);
			runAnimateLoop(state);
			var startTimes = _.map(state.values, function(ev, i) { return 0; }).array();
			var startValues = _.map(state.values, function(ev, i) { return ev || 0; }).array();
			var startVelocities = _.map(startTimes, function(ev, i) { return state.velocities[i] || 0; }).array();
			var accelerations = state.accelerations = _.map(startTimes, function(ev, i) {
				return parseVelocity(accelList.at(i) || 0);
			}).array();
			var decelerations = state.decelerations = _.map(startTimes, function(ev, i) {
				return parseVelocity(decelList.at(i) || 0);
			}).array();
			var targets = _.map(startTimes, function(ev, i) {
				return parseVelocity(targetVelocityList.at(i));
			}).array();

			state.loopStartTime = state.loopTimestamp;
			state.loopFunc = function(tAbs) {
				var activeValNum = 0;
				var vals = state.values, vels = state.velocities, minLimits = state.minLimits, maxLimits = state.maxLimits;
				vals.length = vels.length = startValues.length;

				var moveVal = function moveVal(i, recursionLeft) {
					var tRel = tAbs - startTimes[i];
					var accel = accelerations[i] || -decelerations[i] * sign(startVelocities[i]) || 0;
					var tToTargetV = targets[i] == null ? null : (accel ? (targets[i]-startVelocities[i]) / accel : (startVelocities[i] == targets[i] ? 0 : null));
					var tToMinLimit = computeDuration(startValues[i], startVelocities[i], accel, minLimits[i]);
					var tToMaxLimit = computeDuration(startValues[i], startVelocities[i], accel, maxLimits[i]);

					if (tToTargetV != null && tRel>=tToTargetV && (tToMaxLimit == null || tToMaxLimit>=tToTargetV) && (tToMinLimit == null || tToMinLimit>=tToTargetV)) {
						vals[i] = startValues[i] + tToTargetV * startVelocities[i] + tToTargetV * tToTargetV * accel / 2;
						vels[i] = accelerations[i] = decelerations[i] = 0;
						return;
					}

					var v = vels[i] = startVelocities[i] + tRel * accel;
					var t0 = tRel;
					var limitHit = false;
					if ((v || accel) && tToMinLimit != null && tRel >= tToMinLimit &&
						!(tToMinLimit == 0 && v >= 0) &&
						(tToMaxLimit == null || tToMaxLimit>=tToMinLimit )) {
						t0 = tToMinLimit;
						vals[i] = minLimits[i];
						limitHit = true;
						v = vels[i] = startVelocities[i] + t0 * accel;
					}
					else if ((v || accel) && tToMaxLimit != null && tRel >= tToMaxLimit &&
						!(tToMaxLimit == 0 && v <= 0)) {
						t0 = tToMaxLimit;
						vals[i] = maxLimits[i];
						limitHit = true;
						v = vels[i] = startVelocities[i] + t0 * accel;
					}
					else
						vals[i] = startValues[i] + tRel * startVelocities[i] + tRel * tRel * accel / 2;

					if (limitHit) {
						startTimes[i] += t0;
						var cbState = {value: vals[i], velocity: v, acceleration: accelerations[i], deceleration: decelerations[i], stash: state.stashes[i]};
						if ((t0 == tToMinLimit && v > 0) || (t0 == tToMaxLimit && v < 0)) {
							if (state.backInLimitsCallback)
								state.backInLimitsCallback(cbState);
							else
								cbState = null;
						}
						else if (state.outOfLimitCallback)
							state.outOfLimitCallback(cbState);
						else
							cbState.velocity = cbState.acceleration = cbState.deceleration = 0;

						if (cbState) {
							startValues[i] = vals[i] = cbState.value;
							startVelocities[i] = vels[i] = cbState.velocity;
							accelerations[i] = cbState.acceleration;
							decelerations[i] = cbState.deceleration;
							state.stashes[i] = cbState.stash;
						}

						if ((vels[i] || accelerations[i]) && recursionLeft)
							moveVal(i, recursionLeft-1);
					}
				};

				for (var i = 0; i < startValues.length; i++)
					if (vels[i] || accelerations[i] || decelerations[i]) {
						moveVal(i, 10);
						if (vels[i] || accelerations[i] || decelerations[i])
							activeValNum++;
					}
				el.set(propertyName, setInterpolatablesArray(state.valueTpl, vals));
				if (!activeValNum)
					state.loopFunc = null;
			};
			promises.push(state.promise);
		});
		return joinPromises(promises);
	}

	function getAnimateState(el, property) {
		var a;
		var s = el.data(ANIMATE_STATE);
		if (!s)
			el.data(ANIMATE_STATE, s = {});
		a = s[property];
		if (a)
			return a;
		else {
			var v = el.get(property);
			return (s[property] = a = {
				valueTpl: v,      // current value as template for extractInterpolatable()
				values: extractInterpolatable(v),
				velocities: [], // current velocity per ms
				accelerations: [],  // current acceleration per ms^2
				decelerations: [],  // current deceleration per ms^2
				minLimits: [],
				maxLimits: [],
				stashes: [],
				loopTimestamp: 0        // last loop's timestamp
				//loopStartTime: 0,       // begin of current animation, to allow smooth transition betweeen anims. null if unknwn
				//outOfLimitCallback: null,   // as set by animateLimit()
				//backInLimitsCallback: null  // as set by animateLimit()
				//promise: null               // promise of the current loop, set by runAnimateLoop()
				//loopFunc: null         // loop function(t) of animation, null to stop animation
			});
		}
	}

	function runAnimateLoop(state) {
		if (!state.promise) {
			state.loopTimestamp = 0;
			state.loopStartTime = null;
			state.promise = $.loop(function(t, stop) {
				state.loopTimestamp = t;
				if (state.loopStartTime == null)
					state.loopStartTime = t;
				if (state.loopFunc)
					state.loopFunc(t - state.loopStartTime);
				else {
					stop();
					state.promise = null;
				}
			});
		}
	}

    NIA.List.prototype.veloDial = veloDial;
    NIA.List.prototype.veloAnimate = veloAnimate;
    NIA.List.prototype.animateSet = animateSet;
    NIA.List.prototype.animateLimit = animateLimit;
    NIA.List.prototype.animateTo = animateTo;
    NIA.List.prototype.accelerate = accelerate;
    NIA.List.prototype.decelerate = decelerate;

	var internal = { getAnimateState: getAnimateState };

    _.copyObj({
		parseVelocity: parseVelocity,
		timeline: timeline,
		animateDial: animateDial ,
		veloInterpolator: veloInterpolator,
		createInterpolator: createInterpolator,
		ease: ease,
		play: play,
		player: player,
		playToggle: playToggle,
		internal: internal
    }, exports);
  }
})();


text:
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var fs_1 = __importDefault(require("fs"));
var path_1 = __importDefault(require("path"));
// import { controller, get, use } from './decorator'
// import { get } from './decorator'
var decorator_1 = require("../decorator");
var util_1 = require("../utils/util");
var crowller_1 = __importDefault(require("../utils/crowller"));
var dellAnalyzer_1 = __importDefault(require("../utils/dellAnalyzer"));
// 判断是否登录的中间件
var checkLogin = function (req, res, next) {
    var isLogin = !!(req.session ? req.session.login : undefined);
    console.log('checkLogin middleware');
    if (isLogin) {
        next();
    }
    else {
        // res.send('请先登录')
        res.json(util_1.getResponseData(null, '请先登录'));
    }
};
var test = function (req, res, next) {
    console.log('test middleware');
    next();
};
var CrowllerController = /** @class */ (function () {
    function CrowllerController() {
    }
    CrowllerController.prototype.getData = function (req, res) {
        var secret = 'secretKey';
        var url = "http://www.dell-lee.com/typescript/demo.html?secret=" + secret;
        var analyzer = dellAnalyzer_1.default.getInstance();
        new crowller_1.default(url, analyzer);
        // res.send('getDate Success')
        res.json(util_1.getResponseData(true));
    };
    CrowllerController.prototype.showData = function (req, res) {
        try {
            var position = path_1.default.resolve(__dirname, '../../data/course.json');
            var result = fs_1.default.readFileSync(position, 'utf8');
            res.json(JSON.parse(result));
        }
        catch (e) {
            // res.send('尚未爬取到内容')
            res.json(util_1.getResponseData(false, '尚未爬取到内容'));
        }
    };
    __decorate([
        decorator_1.get('/getData'),
        decorator_1.use(checkLogin),
        decorator_1.use(test),
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Object, Object]),
        __metadata("design:returntype", void 0)
    ], CrowllerController.prototype, "getData", null);
    __decorate([
        decorator_1.get('/showData'),
        decorator_1.use(checkLogin),
        __metadata("design:type", Function),
        __metadata("design:paramtypes", [Object, Object]),
        __metadata("design:returntype", void 0)
    ], CrowllerController.prototype, "showData", null);
    CrowllerController = __decorate([
        decorator_1.controller('/api')
    ], CrowllerController);
    return CrowllerController;
}());
exports.CrowllerController = CrowllerController;

redpajama_set_name: RedPajamaGithub


text:
class Visit < ActiveRecord::Base
  belongs_to :property
  belongs_to :customer_account

  #TODO is this the right way to validate that the visit has a property? and a customer_account?
  validates_presence_of :property,
                        :customer_account,
                        :time,
                        :date
end


text:
ngMessages
==========

A simple ngMessages example.