diff --git a/src/Animator.js b/src/Animator.js
index 631db7cba..8431a48ec 100644
--- a/src/Animator.js
+++ b/src/Animator.js
@@ -8,6 +8,7 @@ require('enyo');
var
kind = require('./kind'),
utils = require('./utils'),
+ easing = require('./easing'),
animation = require('./animation');
var
@@ -145,7 +146,7 @@ module.exports = kind(
* @default module:enyo/easing~easing.cubicOut
* @public
*/
- easingFunction: animation.easing.cubicOut
+ easingFunction: easing.cubicOut
},
/*
diff --git a/src/SceneSupport.js b/src/SceneSupport.js
new file mode 100644
index 000000000..870bb7905
--- /dev/null
+++ b/src/SceneSupport.js
@@ -0,0 +1,22 @@
+var
+ kind = require('./kind'),
+ utils = require('./utils'),
+ scene = require('./scene');
+
+var SceneSupport = {
+
+ create: kind.inherit(function(sup) {
+ var sctor;
+ return function() {
+ sup.apply(this, arguments);
+ sctor = this.scene;
+ if (sctor) {
+ sctor = scene(this, sctor);
+ utils.mixin(sctor, this.sceneOptions);
+ this.scene = sctor;
+ }
+ };
+ })
+};
+
+module.exports = SceneSupport;
diff --git a/src/animation.js b/src/animation.js
index 2176e080e..791414e48 100644
--- a/src/animation.js
+++ b/src/animation.js
@@ -15,7 +15,9 @@ var ms = Math.round(1000/60),
cRAF = 'cancelRequestAnimationFrame',
cAF = 'cancelAnimationFrame',
i, pl, p, wcRAF, wrAF, wcAF,
- _requestFrame, _cancelFrame, cancelFrame;
+ _requestFrame, _cancelFrame, cancelFrame,
+ core = { ts: 0, obs: {}};
+
/*
* Fallback on setTimeout
@@ -98,63 +100,36 @@ exports.cancelRequestAnimationFrame = function(id) {
exports.cancelAnimationFrame = function(id) {
return _cancelFrame(id);
};
-
/**
-* A set of interpolation functions for animations, similar in function to CSS3
-* transitions.
+* Subcribes for animation frame ticks.
*
-* These are intended for use with {@link module:enyo/animation#easedLerp}. Each easing function
-* accepts one (1) [Number]{@glossary Number} parameter and returns one (1)
-* [Number]{@glossary Number} value.
+* @param {Object} ctx - The context on which callback is registered.
+* @param {Function} callback - A [callback]{@glossary callback} to be executed on tick.
+* @public
+*/
+exports.subscribe = function(ctx,callback) {
+ var id = utils.uid("rAF");
+ core.obs[id] = utils.bindSafely(ctx, callback);
+ return id;
+};
+/**
+* Unsubcribes for animation frame ticks.
*
+* @param {Object} node - The context on which callback is registered.
+* @param {Function} callback - A [callback]{@glossary callback} to be executed on tick.
* @public
*/
-exports.easing = /** @lends module:enyo/animation~easing.prototype */ {
- /**
- * cubicIn
- *
- * @public
- */
- cubicIn: function(n) {
- return Math.pow(n, 3);
- },
- /**
- * cubicOut
- *
- * @public
- */
- cubicOut: function(n) {
- return Math.pow(n - 1, 3) + 1;
- },
- /**
- * expoOut
- *
- * @public
- */
- expoOut: function(n) {
- return (n == 1) ? 1 : (-1 * Math.pow(2, -10 * n) + 1);
- },
- /**
- * quadInOut
- *
- * @public
- */
- quadInOut: function(n) {
- n = n * 2;
- if (n < 1) {
- return Math.pow(n, 2) / 2;
- }
- return -1 * ((--n) * (n - 2) - 1) / 2;
- },
- /**
- * linear
- *
- * @public
- */
- linear: function(n) {
- return n;
- }
+exports.unsubscribe = function(id) {
+ delete core.obs[id];
+};
+
+var startrAF = function(){
+ _requestFrame(function (time) {
+ startrAF();
+ core.ts = time;
+ }.bind(this));
};
+startrAF();
/**
* Gives an interpolation of an animated transition's distance from 0 to 1.
@@ -206,3 +181,19 @@ exports.easedComplexLerp = function(t0, duration, easing, reverse, time, startVa
return easing(lerp, time, startValue, valueChange, duration);
}
};
+
+
+//TODO: A temporary implementation for rAF with observers.
+Object.defineProperty(core, 'ts', {
+
+ get: function() {
+ return this.value;
+ },
+
+ set: function(newValue) {
+ for(var i in this.obs){
+ this.obs[i](this.value, newValue);
+ }
+ this.value = newValue;
+ }
+});
\ No newline at end of file
diff --git a/src/easing.js b/src/easing.js
new file mode 100644
index 000000000..080decc92
--- /dev/null
+++ b/src/easing.js
@@ -0,0 +1,281 @@
+/**
+* Contains set of interpolation functions for animations, similar in function to CSS3 transitions.
+* @module enyo/easing
+*/
+
+var easing = module.exports = {
+ /**
+ * Linear ease with no acceleration
+ * @public
+ */
+ linear: function(n) {
+ return n;
+ },
+ /**
+ * Accelerating with second-degree polynomial.
+ * @public
+ */
+ quadIn: function(t) {
+ return t * t;
+ },
+ /**
+ * Deaccelerating with second-degree polynomial.
+ * @public
+ */
+ quadOut: function(t) {
+ return -1 * t * (t - 2);
+ },
+ /**
+ * Halfway accelerating and then deaccelerating with second-degree polynomial.
+ * @public
+ */
+ quadInOut: function(n) {
+ n = n * 2;
+ if (n < 1) {
+ return Math.pow(n, 2) / 2;
+ }
+ return -1 * ((--n) * (n - 2) - 1) / 2;
+ },
+ /**
+ * Accelerating with third-degree polynomial.
+ * @public
+ */
+ cubicIn: function(n) {
+ return Math.pow(n, 3);
+ },
+ /**
+ * Deaccelerating with third-degree polynomial.
+ * @public
+ */
+ cubicOut: function(n) {
+ return Math.pow(n - 1, 3) + 1;
+ },
+ /**
+ * Halfway accelerating and then deaccelerating with third-degree polynomial.
+ * @public
+ */
+ cubicInOut: function(t) {
+ if ((t *= 2) < 1) return 0.5 * t * t * t;
+ return 0.5 * ((t -= 2) * t * t + 2);
+ },
+ /**
+ * Accelerating with fourth-degree polynomial
+ * @public
+ */
+ quartIn: function(t) {
+ return t * t * t * t;
+ },
+ /**
+ * Deaccelerating with fourth-degree polynomial
+ * @public
+ */
+ quartOut: function(t) {
+ return -1 * (--t * t * t * t - 1);
+ },
+ /**
+ * Halfway accelerating and then deaccelerating with fourth-degree polynomial
+ * @public
+ */
+ quartInOut: function(t) {
+ if ((t *= 2) < 1) return 0.5 * t * t * t * t;
+ return -0.5 * ((t -= 2) * t * t * t - 2);
+ },
+ /**
+ * Accelerating with fifth-degree polynomial
+ * @public
+ */
+ quintIn: function(t) {
+ return t * t * t * t * t;
+ },
+ /**
+ * Deaccelerating with fifth-degree polynomial
+ * @public
+ */
+ quintOut: function(t) {
+ return --t * t * t * t * t + 1;
+ },
+ /**
+ * Halfway accelerating and then deaccelerating with fifth-degree polynomial
+ * @public
+ */
+ quintInOut: function(t, d) {
+ if ((t *= 2) < 1) return 0.5 * t * t * t * t * t;
+ return 0.5 * ((t -= 2) * t * t * t * t + 2);
+ },
+ /**
+ * Accelerating using a sine formula
+ * @public
+ */
+ sineIn: function(t) {
+ return -1 * Math.cos(t * (Math.PI / 2)) + 1;
+ },
+ /**
+ * Deaccelerating using a sine formula
+ * @public
+ */
+ sineOut: function(t) {
+ return Math.sin(t * (Math.PI / 2));
+ },
+ /**
+ * Halfway accelerating and then deaccelerating using a sine formula
+ * @public
+ */
+ sineInOut: function(t) {
+ return -0.5 * (Math.cos(Math.PI * t) - 1);
+ },
+ /**
+ * Accelerating using an exponential formula
+ * @public
+ */
+ expoIn: function(t) {
+ return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
+ },
+ /**
+ * Deaccelerating using an exponential formula
+ * @public
+ */
+ expoOut: function(n) {
+ return (n == 1) ? 1 : (-1 * Math.pow(2, -10 * n) + 1);
+ },
+ /**
+ * Halfway accelerating and then deaccelerating using an exponential formula
+ * @public
+ */
+ expoInOut: function(t) {
+ if (t === 0) return 0;
+ if (t === 1) return 1;
+ if ((t *= 2) < 1) return 0.5 * Math.pow(2, 10 * (t - 1));
+ return 0.5 * (-Math.pow(2, -10 * --t) + 2);
+ },
+ /**
+ * Accelerating using a circular function
+ * @public
+ */
+ circIn: function(t) {
+ return -1 * (Math.sqrt(1 - t * t) - 1);
+ },
+ /**
+ * Deaccelerating using a circular function
+ * @public
+ */
+ circOut: function(t) {
+ return Math.sqrt(1 - (--t * t));
+ },
+ /**
+ * Halfway accelerating and then deaccelerating using a circular function
+ * @public
+ */
+ circInOut: function(t) {
+ if ((t *= 2) < 1) return -0.5 * (Math.sqrt(1 - t * t) - 1);
+ return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
+ },
+ /**
+ * Accelerating with a bouncing effect
+ * @public
+ */
+ bounceIn: function(t) {
+ return 1 - easing.bounceOut(1 - t);
+ },
+ /**
+ * Deaccelerating with a bouncing effect
+ * @public
+ */
+ bounceOut: function(t) {
+ if (t < 0.363636) {
+ return 7.5625 * t * t;
+ } else if (t < 0.727272) {
+ return 7.5625 * (t -= 0.545454) * t + 0.75;
+ } else if (t < (2.5 / 2.75)) {
+ return 7.5625 * (t -= 0.818182) * t + 0.9375;
+ } else {
+ return 7.5625 * (t -= 0.954545) * t + 0.984375;
+ }
+ },
+ /**
+ * Halfway accelerating and then deaccelerating with a bouncing effect
+ * @public
+ */
+ bounceInOut: function(t) {
+ if (t < 0.5) return easing.bounceIn(t * 2) * 0.5;
+ return easing.bounceOut(t * 2 - 1) * 0.5 + 0.5;
+ },
+ /**
+ * Accelerating as a spring oscillating back and forth until it comes to rest
+ * @public
+ */
+ elasticIn: function(t, d) {
+ var a = 1,
+ p = 0,
+ s = 1.70158;
+ if (t === 0) return 0;
+ if (t === 1) return 1;
+ if (!p) p = d * 0.3;
+ if (a < 1) {
+ a = 1;
+ s = p / 4;
+ } else s = p / (2 * Math.PI) * Math.asin(1 / a);
+ return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p));
+ },
+ /**
+ * Deaccelerating as a spring oscillating back and forth until it comes to rest
+ * @public
+ */
+ elasticOut: function(t, d) {
+ var a = 1,
+ p = 0,
+ s = 1.70158;
+ if (t === 0) return 0;
+ if (t === 1) return 1;
+ if (!p) p = d * 0.3;
+ if (a < 1) {
+ a = 1;
+ s = p / 4;
+ } else s = p / (2 * Math.PI) * Math.asin(1 / a);
+ return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + 1;
+ },
+ /**
+ * Halfway accelerating and then deaccelerating as a spring
+ * oscillating back and forth until it comes to rest
+ * @public
+ */
+ elasticInOut: function(t, d) {
+ var a = 1,
+ p = 0,
+ s = 1.70158;
+ if (t === 0) return 0;
+ if ((t *= 2) === 2) return 1;
+ if (!p) p = d * (0.3 * 1.5);
+ if (a < 1) {
+ a = 1;
+ s = p / 4;
+ } else s = p / (2 * Math.PI) * Math.asin(1 / a);
+ if (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p));
+ return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + 1;
+ },
+ /**
+ * Accelerating while retracting slightly before it begins to animate in the path indicated
+ * @public
+ */
+ backIn: function(t, d, s) {
+ if (!s) s = 1.70158;
+ return t * t * ((s + 1) * t - s);
+ },
+ /**
+ * Deaccelerating while retracting slightly before it begins to animate in the path indicated
+ * @public
+ */
+ backOut: function(t, d, s) {
+ if (!s) s = 1.70158;
+ return --t * t * ((s + 1) * t + s) + 1;
+ },
+ /**
+ * Halfway accelerating and then deaccelerating while retracting
+ * slightly before it begins to animate in the path indicated
+ * @public
+ */
+ backInOut: function(t, d, s) {
+ if (!s) s = 1.70158;
+ if ((t *= 2) < 1) return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
+ return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
+ }
+};
\ No newline at end of file
diff --git a/src/kind.js b/src/kind.js
index 7a479c75c..e8f3ea66a 100644
--- a/src/kind.js
+++ b/src/kind.js
@@ -1,8 +1,9 @@
require('enyo');
var
- logger = require('./logger'),
- utils = require('./utils');
+ logger = require('./logger'),
+ scene = require('./scene'),
+ utils = require('./utils');
var defaultCtor = null;
@@ -240,30 +241,31 @@ kind.inherited = function (originals, replacements) {
var target = originals.callee;
var fn = target._inherited;
- // regardless of how we got here, just ensure we actually
- // have a function to call or else we throw a console
- // warning to notify developers they are calling a
- // super method that doesn't exist
- if ('function' === typeof fn) {
- var args = originals;
- if (replacements) {
- // combine the two arrays, with the replacements taking the first
- // set of arguments, and originals filling up the rest.
- args = [];
- var i = 0, l = replacements.length;
- for (; i < l; ++i) {
- args[i] = replacements[i];
- }
- l = originals.length;
- for (; i < l; ++i) {
- args[i] = originals[i];
- }
- }
- return fn.apply(this, args);
- } else {
- logger.warn('enyo.kind.inherited: unable to find requested ' +
- 'super-method from -> ' + originals.callee.displayName + ' in ' + this.kindName);
- }
+ // regardless of how we got here, just ensure we actually
+ // have a function to call or else we throw a console
+ // warning to notify developers they are calling a
+ // super method that doesn't exist
+ if ('function' === typeof fn) {
+ var args = originals;
+ if (replacements) {
+ // combine the two arrays, with the replacements taking the first
+ // set of arguments, and originals filling up the rest.
+ args = [];
+ var i = 0,
+ l = replacements.length;
+ for (; i < l; ++i) {
+ args[i] = replacements[i];
+ }
+ l = originals.length;
+ for (; i < l; ++i) {
+ args[i] = originals[i];
+ }
+ }
+ return fn.apply(this, args);
+ } else {
+ logger.warn('enyo.kind.inherited: unable to find requested ' +
+ 'super-method from -> ' + originals.callee.displayName + ' in ' + this.kindName);
+ }
};
// dcl inspired super-inheritance
@@ -350,47 +352,48 @@ kind.statics = {
*/
subclass: function (ctor, props) {},
- /**
- * Allows for extension of the current [kind]{@glossary kind} without
- * creating a new kind. This method is available on all
- * [constructors]{@glossary constructor}, although calling it on a
- * [deferred]{@glossary deferred} constructor will force it to be
- * resolved at that time. This method does not re-run the
- * {@link module:enyo/kind.features} against the constructor or instance.
- *
- * @name module:enyo/kind.extend
- * @method
- * @param {Object|Object[]} props A [hash]{@glossary Object} or [array]{@glossary Array}
- * of [hashes]{@glossary Object}. Properties will override
- * [prototype]{@glossary Object.prototype} properties. If a
- * method that is being added already exists, the new method will
- * supersede the existing one. The method may call
- * `this.inherited()` or be wrapped with `kind.inherit()` to call
- * the original method (this chains multiple methods tied to a
- * single [kind]{@glossary kind}).
- * @param {Object} [target] - The instance to be extended. If this is not specified, then the
- * [constructor]{@glossary constructor} of the
- * [object]{@glossary Object} this method is being called on will
- * be extended.
- * @returns {Object} The constructor of the class, or specific
- * instance, that has been extended.
- * @public
- */
- extend: function (props, target) {
- var ctor = this
- , exts = utils.isArray(props)? props: [props]
- , proto, fn;
+ /**
+ * Allows for extension of the current [kind]{@glossary kind} without
+ * creating a new kind. This method is available on all
+ * [constructors]{@glossary constructor}, although calling it on a
+ * [deferred]{@glossary deferred} constructor will force it to be
+ * resolved at that time. This method does not re-run the
+ * {@link module:enyo/kind.features} against the constructor or instance.
+ *
+ * @name module:enyo/kind.extend
+ * @method
+ * @param {Object|Object[]} props A [hash]{@glossary Object} or [array]{@glossary Array}
+ * of [hashes]{@glossary Object}. Properties will override
+ * [prototype]{@glossary Object.prototype} properties. If a
+ * method that is being added already exists, the new method will
+ * supersede the existing one. The method may call
+ * `this.inherited()` or be wrapped with `kind.inherit()` to call
+ * the original method (this chains multiple methods tied to a
+ * single [kind]{@glossary kind}).
+ * @param {Object} [target] - The instance to be extended. If this is not specified, then the
+ * [constructor]{@glossary constructor} of the
+ * [object]{@glossary Object} this method is being called on will
+ * be extended.
+ * @returns {Object} The constructor of the class, or specific
+ * instance, that has been extended.
+ * @public
+ */
+ extend: function(props, target) {
+ var ctor = this,
+ exts = utils.isArray(props) ? props : [props],
+ proto, fn;
fn = function (key, value) {
return !(typeof value == 'function' || isInherited(value)) && concatenated.indexOf(key) === -1;
};
- proto = target || ctor.prototype;
- for (var i=0, ext; (ext=exts[i]); ++i) {
- kind.concatHandler(proto, ext, true);
- kind.extendMethods(proto, ext, true);
- utils.mixin(proto, ext, {filter: fn});
- }
+ proto = target || ctor.prototype;
+ for (var i = 0, ext;
+ (ext = exts[i]); ++i) {
+ kind.concatHandler(proto, ext, true);
+ kind.extendMethods(proto, ext, true);
+ utils.mixin(proto, ext, { filter: fn });
+ }
return target || ctor;
},
@@ -416,17 +419,18 @@ kind.statics = {
};
/**
-* @method
-* @private
-*/
-exports.concatHandler = function (ctor, props, instance) {
- var proto = ctor.prototype || ctor
- , base = proto.ctor;
-
- while (base) {
- if (base.concat) base.concat(ctor, props, instance);
- base = base.prototype.base;
- }
+ * @method
+ * @private
+ */
+exports.concatHandler = function(ctor, props, instance) {
+
+ var proto = ctor.prototype || ctor,
+ base = proto.ctor;
+
+ while (base) {
+ if (base.concat) base.concat(ctor, props, instance);
+ base = base.prototype.base;
+ }
};
var kindCtors =
@@ -440,33 +444,32 @@ var kindCtors =
exports._kindCtors = {};
/**
-* @method
-* @private
-*/
-var constructorForKind = exports.constructorForKind = function (kind) {
- if (kind === null) {
- return kind;
- } else if (kind === undefined) {
- return getDefaultCtor();
- }
- else if (utils.isFunction(kind)) {
- return kind;
- }
- logger.warn('Creating instances by name is deprecated. Name used:', kind);
- // use memoized constructor if available...
- var ctor = kindCtors[kind];
- if (ctor) {
- return ctor;
- }
- // otherwise look it up and memoize what we find
- //
- // if kind is an object in enyo, say "Control", then ctor = enyo["Control"]
- // if kind is a path under enyo, say "Heritage.Button", then ctor = enyo["Heritage.Button"] || enyo.Heritage.Button
- // if kind is a fully qualified path, say "enyo.Heritage.Button", then ctor = enyo["enyo.Heritage.Button"] || enyo.enyo.Heritage.Button || enyo.Heritage.Button
- //
- // Note that kind "Foo" will resolve to enyo.Foo before resolving to global "Foo".
- // This is important so "Image" will map to built-in Image object, instead of enyo.Image control.
- ctor = Theme[kind] || (global.enyo && global.enyo[kind]) || utils.getPath.call(global, 'enyo.' + kind) || global[kind] || utils.getPath.call(global, kind);
+ * @method
+ * @private
+ */
+var constructorForKind = exports.constructorForKind = function(kind) {
+ if (kind === null) {
+ return kind;
+ } else if (kind === undefined) {
+ return getDefaultCtor();
+ } else if (utils.isFunction(kind)) {
+ return kind;
+ }
+ logger.warn('Creating instances by name is deprecated. Name used:', kind);
+ // use memoized constructor if available...
+ var ctor = kindCtors[kind];
+ if (ctor) {
+ return ctor;
+ }
+ // otherwise look it up and memoize what we find
+ //
+ // if kind is an object in enyo, say "Control", then ctor = enyo["Control"]
+ // if kind is a path under enyo, say "Heritage.Button", then ctor = enyo["Heritage.Button"] || enyo.Heritage.Button
+ // if kind is a fully qualified path, say "enyo.Heritage.Button", then ctor = enyo["enyo.Heritage.Button"] || enyo.enyo.Heritage.Button || enyo.Heritage.Button
+ //
+ // Note that kind "Foo" will resolve to enyo.Foo before resolving to global "Foo".
+ // This is important so "Image" will map to built-in Image object, instead of enyo.Image control.
+ ctor = Theme[kind] || (global.enyo && global.enyo[kind]) || utils.getPath.call(global, 'enyo.' + kind) || global[kind] || utils.getPath.call(global, kind);
// If what we found at this namespace isn't a function, it's definitely not a kind constructor
if (!utils.isFunction(ctor)) {
diff --git a/src/scene.js b/src/scene.js
new file mode 100644
index 000000000..5ec9270d8
--- /dev/null
+++ b/src/scene.js
@@ -0,0 +1,555 @@
+var
+ tween = require('./tween'),
+ utils = require('./utils'),
+ animation = require('./animation');
+/**
+ * Contains the declaration for the {@link module:enyo/scene~scene} of an animation.
+ * @module enyo/scene
+ */
+
+var _ts, _framerate = 16.6;
+
+var AnimationSupport = {
+ /**
+ * Reiterates the animation applied to the component.
+ * It could be;
+ * true, for infinite iteration
+ * [Number], for how many times animation should iterate.
+ * false, for no repetition
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ repeat: false,
+ /**
+ * Reduces GPU layers when the animation is completed.
+ * As most of the transform animations happens on
+ * GPU layer, ans stays there even after the animations
+ * is completed. However, need to be carefull while using this
+ * feature as if the component tends to animate regularly, this
+ * feature would be an overhead.
+ * when true, GPU memory is freed
+ * false, layer remain intact.
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ handleLayers: false,
+ /**
+ * Indentifies whether the animation is in progress or not.
+ * when true, animation is in progress
+ * false, otherwise.
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ animating: false,
+ /**
+ * Specifies the rate at which animation should be played.
+ * When 0, animation is still
+ * 1, animation plays with normal speed
+ * 2, animation plays with 2X fast speed
+ * 0.5, animation plays with slow speed at half of normal speed.
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ direction: 0,
+ /**
+ * Specifies the rate at which animation should be played.
+ * When 0, animation is still
+ * 1, animation plays with normal speed
+ * 2, animation plays with 2X fast speed
+ * 0.5, animation plays with slow speed at half of normal speed.
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ speed: 0,
+ /**
+ * Moves animation to a particular point within the span of
+ * an animation. Its value could lie between 0 and total duration
+ * of the animation.
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ seekInterval: 0,
+ /**
+ * Plays animation in sequence when set to true else
+ * its a parallal animation. This could be applied for
+ * animation properties as well as for scenes within a
+ * scene.
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ isSequence: true,
+ /**
+ * Starts animation when scene is initialized,
+ * when this property is set to true. When false scene instance has
+ * to be explicity played using 'play' api.
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ autoPlay: true,
+ /**
+ * The limit for an animation, which could be an instance
+ * of time as well as distance.
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ span: 0,
+ /**
+ * The current time state of animation. This represents the
+ * time at which animation is progressed upon. As this property
+ * directly impacts the state of animation, updating this value
+ * have direct effect on animation unless its animation is halted.
+ * The range lies between 0 to overall span of animation.
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ timeline: 0,
+ /**
+ * Starts the animation of scene.
+ * @public
+ * @memberOf module:enyo/scene
+ */
+ play: function() {
+ this.direction = this.speed = 1;
+ if (isNaN(this.timeline) || !this.timeline) {
+ this.timeline = 0;
+ }
+ this.animating = true;
+ return this;
+ },
+
+ /**
+ * Resumes the paused animation of scene.
+ * @memberOf module:enyo/scene
+ * @public
+ */
+ resume: function() {
+ this.direction = 1;
+ return this;
+ },
+
+ /**
+ * Pauses the animation of scene.
+ * @memberOf module:enyo/scene
+ * @public
+ */
+ pause: function() {
+ this.direction = 0;
+ return this;
+ },
+
+ /**
+ * Reverses the animation of scene.
+ * @memberOf module:enyo/scene
+ * @public
+ */
+ reverse: function() {
+ this.direction = -1;
+ },
+
+ /**
+ * Stops the animation of scene.
+ * @memberOf module:enyo/scene
+ * @public
+ */
+ stop: function() {
+ this.speed = 0;
+ this.timeline = 0;
+ },
+
+ /**
+ * Seeks the animation to the position provided in seek
+ * The value of seek
should be between 0 to duration
of the animation.
+ * @param {Number} seek where the animation has to be seeked
+ * @memberOf module:enyo/scene
+ * @public
+ */
+ seek: function(seek) {
+ this.timeline = seek;
+ },
+
+ /**
+ * Seeks the animation to the position provided in seek
with animation
+ * The value of seek
should be between 0 to duration
of the animation.
+ * @param {Number} seek where the animation has to be seeked
+ * @memberOf module:enyo/scene
+ * @public
+ */
+ seekAnimate: function(seek) {
+ if (seek >= 0) {
+ if (!this.animating)
+ this.play();
+ this.speed = 1;
+ } else {
+ this.speed = -1;
+ }
+ this.seekInterval = this.timeline + seek;
+ if (this.seekInterval < 0) {
+ this.speed = 0;
+ this.seekInterval = 0;
+ }
+ },
+
+ //TODO: Move these events to Event Delegator
+ /**
+ * Event to identify when the scene has done animating.
+ * @memberOf module:enyo/scene
+ * @public
+ */
+ completed: function() {},
+
+ /**
+ * Event to identify when the scene has done a step(rAF updatation of time) in the animation.
+ * @memberOf module:enyo/scene
+ * @public
+ */
+ step: function() {}
+};
+
+/**
+ * Interface which accepts the animation details and returns a scene object
+ * @param {Object} actor component which has to be animated
+ * @param {Object} props properties of the component
+ * @param {Object} opts additional options
+ * @return {Object} A scene object
+ */
+module.exports = function (proto, properties, opts) {
+ var i, ctor, ps, s;
+
+ if (!utils.isArray(proto)) {
+ ps = new scene(proto, properties, opts);
+ } else {
+ ps = new scene();
+ if (opts) utils.mixin(ps, opts);
+ for (i = 0;
+ (ctor = proto[i]); i++) {
+ s = new scene(ctor, properties);
+ ps.addScene(s);
+ }
+ }
+
+ ps.autoPlay && ps.play();
+ return ps;
+};
+
+/**
+ * {@link module:enyo/Scene~Scene}
+ *
+ * @class Scene
+ * @extends module:enyo/Scene~Scene
+ * @param {Object} actor component which has to be animated
+ * @param {Object} props properties of the component
+ * @param {Object} opts additional options
+ * @public
+ */
+function scene(actor, props, opts) {
+ this.id = utils.uid("@");
+ this.poses = [];
+ this.rolePlays = [];
+ this.actor = actor;
+
+ this.rAFId = animation.subscribe(this, loop);
+ utils.mixin(this, AnimationSupport);
+ if (opts) utils.mixin(this, opts);
+
+ if (props) {
+ var anims = utils.isArray(props) ? props : [props];
+ for (var i = 0, anim;
+ (anim = anims[i]); i++) {
+ if (anim.delay) this.addAnimation({}, anim.delay, actor.duration);
+ this.addAnimation(anim, anim.duration || 0, actor.duration);
+ }
+ }
+}
+
+/**
+ * Checks whether the scene is in a active state, which then can be animated
+ * @return {Boolean} generated value in true or false. true in case of the parent scene
+ * @memberOf module:enyo/scene
+ * @public
+ */
+scene.prototype.isActive = function() {
+ if (this.actor)
+ return this.actor.generated && !this.actor.destroyed;
+
+ // making sure parent scenes are always active.
+ return true;
+};
+
+/**
+ * Sets the new animations with the properties passed.
+ * @param {Object} properties Animation properties
+ * @memberOf module:enyo/scene
+ * @public
+ */
+scene.prototype.setAnimation = function(properties) {
+ var currentPose = findScene(this.poses, "poses");
+ setScene(currentPose, this.timeline, properties);
+};
+
+/**
+ * Gets the current animation pose.
+ * @param {Number} index animation index
+ * @return {Object} pose pose with the passed index
+ * @memberOf module:enyo/scene
+ * @public
+ */
+scene.prototype.getAnimation = function(index) {
+ return index < 0 || this.poses[index];
+};
+
+/**
+ * addAnimation is used for adding new animation with the passed properties at run time.
+ * @param {Object} newProp animation properties for new animation
+ * @param {Number} span span between the animation
+ * @param {Number} dur duration for the new animation
+ * @memberOf module:enyo/scene
+ * @public
+ */
+scene.prototype.addAnimation = function(newProp, span, dur) {
+ dur = dur || this.span;
+ var l = this.poses.length,
+ old = 0,
+ spanCache = span.toString().match(/%$/) ? (span.replace(/%$/, '') * dur / 100) : span,
+ newSpan = newProp instanceof this.constructor ? newProp.span : spanCache;
+
+ if (l > 0 && this.isSequence) {
+ old = this.poses[l - 1].span;
+ newSpan += old;
+ }
+ this.poses.push({
+ animate: newProp,
+ span: newSpan,
+ begin: old
+ });
+ this.span = newSpan;
+};
+
+/**
+ * Add a new animation scene for the animation.
+ * @param {Object} sc scene which has to be added
+ * @memberOf module:enyo/scene
+ * @public
+ */
+scene.prototype.addScene = function(sc) {
+ var l = this.poses.length,
+ old = 0,
+ newSpan = sc instanceof this.constructor ? sc.span : 0;
+
+ if (l > 0 && this.isSequence) {
+ old = this.poses[l - 1].span;
+ newSpan += old;
+ sc.span = newSpan;
+ sc.begin = old;
+ }
+
+ this.poses.push(sc);
+ this.span = newSpan;
+};
+
+/**
+ * @private
+ */
+function action(ts, pose) {
+ var tm, i, poses,
+ dur = this.span,
+ actor = this.actor;
+
+ if (this.isActive()) {
+ tm = rolePlay(ts, this);
+ if (isNaN(tm) || tm < 0) return pose;
+
+ poses = posesAtTime(this.poses, tm > dur ? dur : tm);
+ for (i = 0, pose;
+ (pose = poses[i]); i++) {
+ if (pose instanceof this.constructor) {
+ pose.speed = this.speed;
+ pose.direction = this.direction;
+ pose.handleLayers = this.handleLayers;
+ action.call(pose, ts);
+ } else {
+ update(pose, actor, (tm - pose.begin), (pose.span - pose.begin));
+ }
+ }
+ this.step && this.step(actor);
+
+ if (tm > dur) cut.call(this, actor);
+ }
+ return pose;
+}
+
+/**
+ * @private
+ */
+function cut(actor) {
+ this.repeat = REPEAT[this.repeat] || this.repeat;
+ this.timeline = --this.repeat ? 0 : this.span;
+
+ if (this.repeat > 0) {
+ applyInitialStyle(this);
+ return;
+ } else {
+ if (FILLMODE[this.fillmode]) {
+ applyInitialStyle(this);
+ }
+ }
+
+ if (this.handleLayers) {
+ this.speed = 0;
+ if (this.active) {
+ this.active = false;
+ tween.halt(actor);
+ }
+ }
+ this.animating = false;
+ this.completed && this.completed(actor);
+}
+
+/**
+ * @private
+ */
+function loop(was, is) {
+ if (this.animating) {
+ _ts = is - (was || 0);
+ _ts = (_ts > _framerate) ? _framerate : _ts;
+ action.call(this, _ts);
+ } else if (this.actor && this.actor.destroyed) {
+ animation.unsubscribe(this.rAFId);
+ }
+}
+
+/**
+ * @private
+ */
+function update(pose, actor, since, dur) {
+ var t;
+ if (!pose._startAnim) tween.init(actor, pose);
+ if (since < 0) since = 0;
+ if (since <= dur && dur !== 0) {
+ t = since / dur;
+ tween.step(actor, pose, t, dur);
+ } else {
+ tween.step(actor, pose, 1, dur);
+ }
+}
+
+/**
+ * rolePlay
updated the timeline of the actor which is currently animating.
+ * @param {Number} t Elapsed time since the animation of this pose has started (ratio in factor of 1)
+ * @param {@link module:enyo/Component~Component} actor The component which is animating
+ * @return {Number} Returns the updated timeline of the actor
+ * @private
+ */
+function rolePlay(t, actor) {
+ actor = actor || this;
+ t = t * actor.speed * actor.direction;
+
+ actor.timeline += t;
+ if (actor.seekInterval !== 0) {
+ if ((actor.seekInterval - actor.timeline) * actor.speed < 0) {
+ actor.seekInterval = 0;
+ actor.speed = 0;
+ }
+ }
+
+ if (actor.timeline === undefined || actor.timeline < 0)
+ actor.timeline = 0;
+ return actor.timeline;
+}
+
+/**
+ * Returns animation pose index for a particular
+ * instance of time from the list of
+ * animations added to the scene.
+ * @param {number} span - Time span from the animation timeline
+ * @return {number} - index of the animation
+ * @private
+ */
+function posesAtTime(anims, span) {
+ function doFilter(val, idx, ar) {
+ return span > (val.begin || 0) && span <= val.span;
+ }
+ return anims.filter(doFilter);
+}
+
+/**
+ * @private
+ */
+function modify(pose, currentTm) {
+ pose.span = currentTm;
+ delete pose._endAnim;
+ pose._endAnim = pose.currentState;
+ return pose;
+}
+/**
+ * @private
+ */
+function setScene(poseArr, tm, properties) {
+ var currentTime = tm;
+ for (var i = 0; i < poseArr.length; i++) {
+
+ if (poseArr[i].begin <= currentTime && poseArr[i].span >= currentTime) { // check the current Pose
+ modify(poseArr[i], currentTime);
+ poseArr.splice((i + 1), poseArr.length - (i + 1));
+ poseArr[(i + 1)] = {
+ animate: properties,
+ begin: currentTime,
+ span: currentTime + properties.duration
+ };
+ break;
+ }
+ }
+}
+/**
+ * @private
+ */
+function hasScene(poseArr, propCheck) {
+ var bool;
+ if (!utils.isArray(poseArr)) {
+ bool = poseArr[propCheck] ? true : false;
+ } else {
+ for (var i = 0; i < poseArr.length; i++) {
+ bool = poseArr[i][propCheck] ? true : false;
+ if (bool) break;
+ }
+ }
+ return bool;
+}
+/**
+ * @private
+ */
+function findScene(poseArr, propCheck) {
+ var parentNode, currNode = poseArr;
+ if (hasScene(poseArr, propCheck)) {
+ if (utils.isArray(poseArr)) {
+ for (var i = 0; i < poseArr.length; i++) {
+ parentNode = currNode[i];
+ currNode = findScene(currNode[i].poses, propCheck);
+ }
+ } else {
+ parentNode = currNode;
+ currNode = findScene(currNode.poses, propCheck);
+ }
+ }
+ return parentNode ? parentNode[propCheck] : parentNode;
+}
+
+/**
+ * @private
+ */
+function applyInitialStyle(node) {
+ node = node.actor ? node : findScene(node.poses, "actor");
+ node = node.actor || node;
+ node.addStyles(node.initialState);
+}
+
+var
+ REPEAT = {
+ 'true': Infinity,
+ 'false': 0
+ },
+ FILLMODE = {
+ 'backwards': true,
+ 'forwards': false,
+ 'default': false,
+ 'none': false
+ };
\ No newline at end of file
diff --git a/src/transform.js b/src/transform.js
new file mode 100644
index 000000000..b9ba52ea9
--- /dev/null
+++ b/src/transform.js
@@ -0,0 +1,735 @@
+/**
+* Contains the declaration for the {@link module:enyo/transform~transform} kind.
+* @module enyo/transform
+*/
+require('enyo');
+
+/**
+ * To create a Typed_array
+ * @param {Number} size The size of the buffer required
+ * @return {Number[]} Typed_array
+ */
+function typedArray (size) {
+ return new Float32Array(new ArrayBuffer(size));
+}
+
+/**
+ * To input the specified indices with value 1
+ * @param {Number[]} matrix typedArray sent
+ * @param {Number[]} numberMat indices where value has to be 1
+ */
+function inputValues (matrix, numberMat) {
+ for (var i = 0; i < numberMat.length; i++) {
+ matrix[numberMat[i]] = 1;
+ }
+}
+
+
+/**
+ * To translate in any dimension based on co-ordinates.
+ * @public
+ * @param {Number} x Translate value in X axis
+ * @param {Number} y Translate value in Y axis
+ * @param {Number} z Translate value in Z axis
+ * @return {Number[]} Matrix3d
+ */
+exports.translate = function(x, y, z) {
+ var translateMat, modifiedMat;
+ translateMat = typedArray(64);
+ modifiedMat = inputValues(translateMat, new Uint8Array([0, 5, 10, 15]));
+ translateMat[12] = x;
+ translateMat[13] = y ? y : 0;
+ translateMat[14] = z ? z : 0;
+ return translateMat;
+};
+
+/**
+ * To translate in x dimension
+ * @public
+ * @param {Number} x Translate value in X axis
+ * @return {Number[]} Matrix3d
+ */
+exports.translateX = function(x) {
+ var translateX, modifiedMat;
+ translateX = typedArray(64);
+ modifiedMat = inputValues(translateX, new Uint8Array([0, 5, 10, 15]));
+ translateX[12] = x ? x : 0;
+ return translateX;
+};
+
+/**
+ * To translate in y dimension
+ * @public
+ * @param {Number} y Translate value in Y axis
+ * @return {Number[]} Matrix3d
+ */
+exports.translateY = function(y) {
+ var translateY, modifiedMat;
+ translateY = typedArray(64);
+ modifiedMat = inputValues(translateY, new Uint8Array([0, 5, 10, 15]));
+ translateY[13] = y ? y : 0;
+ return translateY;
+};
+
+/**
+ * To translate in z dimension
+ * @public
+ * @param {Number} z Translate value in Z axis
+ * @return {Number[]} Matrix3d
+ */
+exports.translateZ = function(z) {
+ var translateZ, modifiedMat;
+ translateZ = typedArray(64);
+ modifiedMat = inputValues(translateZ, new Uint8Array([0, 5, 10, 15]));
+ translateZ[14] = z ? z : 0;
+ return translateZ;
+};
+
+/**
+ * To scale in any dimension
+ * @public
+ * @param {Number} x Scale value in X axis
+ * @param {Number} y Scale value in Y axis
+ * @param {Number} z Scale value in Z axis
+ * @return {Number[]} Matrix3d
+ */
+exports.scale = function(x, y, z) {
+ var scaleMat = typedArray(64);
+ scaleMat[0] = x;
+ scaleMat[5] = y ? y : 1;
+ scaleMat[10] = z ? z : 1;
+ scaleMat[15] = 1;
+ return scaleMat;
+};
+
+/**
+ * To skew in any dimension (skew can only happen in 2d)
+ * @public
+ * @param {Number} a Skew value in X axis
+ * @param {Number} b Skew value in Y axis
+ * @return {Number[]} Matrix3d
+ */
+exports.skew =function(a, b) {
+ var skewMat, modifiedMat;
+ a = a ? Math.tan(a * Math.PI / 180) : 0;
+ b = b ? Math.tan(b * Math.PI / 180) : 0;
+
+ skewMat = typedArray(64);
+ modifiedMat = inputValues(skewMat, new Uint8Array([0, 5, 10, 15]));
+ skewMat[1] = b;
+ skewMat[4] = a;
+ return skewMat;
+};
+
+/**
+ * To rotate in x-axis
+ * @public
+ * @param {Number} a Rotate value in X axis
+ * @return {Number[]} Matrix3d
+ */
+exports.rotateX = function(a) {
+ var cosa, sina, rotateXMat, modifiedMat;
+ a = a ? a * Math.PI / 180 : 0;
+ cosa = Math.cos(a);
+ sina = Math.sin(a);
+
+ rotateXMat = typedArray(64);
+ modifiedMat = inputValues(rotateXMat, new Uint8Array([0, 15]));
+ rotateXMat[5] = cosa;
+ rotateXMat[6] = -sina;
+ rotateXMat[9] = sina;
+ rotateXMat[10] = cosa;
+ return rotateXMat;
+};
+
+/**
+ * To rotate in y-axis
+ * @public
+ * @param {Number} b Rotate value in Y axis
+ * @return {Number[]} Matrix3d
+ */
+exports.rotateY = function(b) {
+ var cosb, sinb, rotateYMat, modifiedMat;
+ b = b ? b * Math.PI / 180 : 0;
+ cosb = Math.cos(b);
+ sinb = Math.sin(b);
+
+ rotateYMat = typedArray(64);
+ modifiedMat = inputValues(rotateYMat, new Uint8Array([5, 15]));
+ rotateYMat[0] = cosb;
+ rotateYMat[2] = sinb;
+ rotateYMat[8] = -sinb;
+ rotateYMat[10] = cosb;
+ return rotateYMat;
+};
+
+/**
+ * To rotate in z-axis
+ * @public
+ * @param {Number} g Rotate value in Z axis
+ * @return {Number[]} Matrix3d
+ */
+exports.rotateZ = function(g) {
+ var cosg, sing, rotateZMat;
+ g = g ? g * Math.PI / 180 : 0;
+ cosg = Math.cos(g);
+ sing = Math.sin(g);
+
+ rotateZMat = typedArray(64);
+ rotateZMat[0] = cosg;
+ rotateZMat[1] = -sing;
+ rotateZMat[4] = sing;
+ rotateZMat[5] = cosg;
+ rotateZMat[15] = 1;
+ return rotateZMat;
+};
+
+/**
+ * To rotate in any dimension
+ * @public
+ * @param {Number} a Rotate value in X axis
+ * @param {Number} b Rotate value in Y axis
+ * @param {Number} g Rotate value in Z axis
+ * @return {Number[]} Matrix3d
+ */
+exports.rotate = function(a, b, g) {
+ var ca, sa, cb, sb, cg, sg, rotateMat;
+ a = a ? a * Math.PI / 180 : 0;
+ b = b ? b * Math.PI / 180 : 0;
+ g = g ? g * Math.PI / 180 : 0;
+ ca = Math.cos(a);
+ sa = Math.sin(a);
+ cb = Math.cos(b);
+ sb = Math.sin(b);
+ cg = Math.cos(g);
+ sg = Math.sin(g);
+
+ rotateMat = typedArray(64);
+ rotateMat[0] = cb * cg;
+ rotateMat[1] = ca * sg + sa * sb * cg;
+ rotateMat[2] = sa * sg - ca * sb * cg;
+ rotateMat[4] = -cb * sg;
+ rotateMat[5] = ca * cg - sa * sb * sg;
+ rotateMat[6] = sa * cg + ca * sb * sg;
+ rotateMat[8] = sb;
+ rotateMat[9] = -sa * cb;
+ rotateMat[10] = ca * cb;
+ rotateMat[15] = 1;
+ return rotateMat;
+};
+
+exports.Matrix = {
+
+ /**
+ * To create Identity Matrix3d as array.
+ * @public
+ * @return {Number[]} Identity Matrix3d
+ */
+ identity: function() {
+ var identityMatrix, modifiedMat;
+ identityMatrix = typedArray(64);
+ modifiedMat = inputValues(identityMatrix, new Uint8Array([0, 5, 10, 15]));
+ return identityMatrix;
+ },
+
+ /**
+ * To create Identity Matrix2d as array.
+ * @public
+ * @return {Number[]} Identity Matrix2d as array
+ */
+ identity2D: function() {
+ var identity2D, modifiedMat;
+ identity2D = typedArray(36);
+ modifiedMat = inputValues(identity2D, new Uint8Array([0, 4, 8]));
+ return identity2D;
+ },
+
+
+ /**
+ * To create Identity Matrix (NXN order).
+ * @public
+ * @param {Number} N Order of Identity Matrix
+ * @return {Number[][]} Identity Matrix of order N
+ */
+ identityMatrix: function(N) {
+ var i, j, row, result = [];
+ for (i = 0; i < N; i++) {
+ row = [];
+ for (j = 0; j < N; j++) {
+ if (i === j) {
+ row.push(1);
+ } else {
+ row.push(0);
+ }
+ }
+ result.push(row);
+ }
+ return result;
+ },
+ /**
+ * To multiply 2 Martix3d (4x4 order)
+ * @public
+ * @param {Number[]} m1 1st Matrix3d
+ * @param {Number[]} m2 2nd Matrix3d
+ * @return {Number[]} Resultant Matrix3d
+ */
+ multiply: function(m1, m2) {
+ if (m1.length !== 16 || m2.length !== 16) return;
+ var multiplyMat = typedArray(64);
+ multiplyMat[0] = m1[0] * m2[0] + m1[4] * m2[1] + m1[8] * m2[2];
+ multiplyMat[1] = m1[1] * m2[0] + m1[5] * m2[1] + m1[9] * m2[2];
+ multiplyMat[2] = m1[2] * m2[0] + m1[6] * m2[1] + m1[10] * m2[2];
+ multiplyMat[4] = m1[0] * m2[4] + m1[4] * m2[5] + m1[8] * m2[6];
+ multiplyMat[5] = m1[1] * m2[4] + m1[5] * m2[5] + m1[9] * m2[6];
+ multiplyMat[6] = m1[2] * m2[4] + m1[6] * m2[5] + m1[10] * m2[6];
+ multiplyMat[8] = m1[0] * m2[8] + m1[4] * m2[9] + m1[8] * m2[10];
+ multiplyMat[9] = m1[1] * m2[8] + m1[5] * m2[9] + m1[9] * m2[10];
+ multiplyMat[10] = m1[2] * m2[8] + m1[6] * m2[9] + m1[10] * m2[10];
+ multiplyMat[12] = m1[0] * m2[12] + m1[4] * m2[13] + m1[8] * m2[14] + m1[12];
+ multiplyMat[13] = m1[1] * m2[12] + m1[5] * m2[13] + m1[9] * m2[14] + m1[13];
+ multiplyMat[14] = m1[2] * m2[12] + m1[6] * m2[13] + m1[10] * m2[14] + m1[14];
+ multiplyMat[15] = 1;
+ return multiplyMat;
+ },
+
+ /**
+ * To multiply 2 Martix3d (n*n order)
+ * @param {Number[]} m1 1st Matrix3d
+ * @param {Number[]} m2 2nd Matrix3d
+ * @return {Number[]} Resultant Matrix3d
+ */
+ multiplyN: function(m1, m2) {
+ var i, j, sum,
+ m = [],
+ l1 = m1.length,
+ l2 = m2.length;
+
+ for (i = 0; i < l1; i++) {
+ sum = 0;
+ for (j = 0; j < l2; j++) {
+ sum += m1[i][j] * m2[j];
+ }
+ m.push(sum);
+ }
+ return m;
+ },
+
+ /**
+ * To inverse matrix of order N
+ * @public
+ * @param {Number[]} matrix Matrix (NxN order)
+ * @param {Number} n Order of the matrix
+ * @return {Number[]} Inverted Matrix
+ */
+ inverseN: function(matrix, n) {
+ var i, j, k, r, t,
+ result = this.identityMatrix(n);
+
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < n; j++) {
+ if (i != j) {
+ r = matrix[j][i] / matrix[i][i];
+ for (k = 0; k < n; k++) {
+ matrix[j][k] -= r * matrix[i][k];
+ result[j][k] -= r * result[i][k];
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < n; i++) {
+ t = matrix[i][i];
+ for (j = 0; j < n; j++) {
+ matrix[i][j] = matrix[i][j] / t;
+ result[i][j] = result[i][j] / t;
+ }
+ }
+
+ return result;
+ },
+
+ /**
+ * Calculate matrix3d of a frame based on transformation vectors.
+ * @public
+ * @param {Number[]} trns Translate vector
+ * @param {Number[]} rot Rotate quaternion vector
+ * @param {Number[]} sc Scale vector
+ * @param {Number[]} sq Skew vector
+ * @param {Number[]} per Perspective vector
+ * @return {Number[]} Final Matrix3d for particular frame
+ */
+ recompose: function(trns, rot, sc, sq, per) {
+ var i,
+ x = rot[0],
+ y = rot[1],
+ z = rot[2],
+ w = rot[3],
+ m = this.identity(),
+ sM = this.identity(),
+ rM = this.identity();
+
+
+ // apply perspective
+ if (per) {
+ m[3] = per[0];
+ m[7] = per[1];
+ m[11] = per[2];
+ m[15] = per[3];
+ }
+
+ m[12] = trns[0];
+ m[13] = trns[1];
+ m[14] = trns[2];
+
+ // apply rotate
+ rM[0] = 1 - 2 * (y * y + z * z);
+ rM[1] = 2 * (x * y - z * w);
+ rM[2] = 2 * (x * z + y * w);
+ rM[4] = 2 * (x * y + z * w);
+ rM[5] = 1 - 2 * (x * x + z * z);
+ rM[6] = 2 * (y * z - x * w);
+ rM[8] = 2 * (x * z - y * w);
+ rM[9] = 2 * (y * z + x * w);
+ rM[10] = 1 - 2 * (x * x + y * y);
+
+ m = this.multiply(m, rM);
+
+ // apply skew
+ if (sq[2]) {
+ sM[9] = sq[2];
+ m = this.multiply(m, sM);
+ }
+
+ if (sq[1]) {
+ sM[9] = 0;
+ sM[8] = sq[1];
+ m = this.multiply(m, sM);
+ }
+
+ if (sq[0]) {
+ sM[8] = 0;
+ sM[4] = sq[0];
+ m = this.multiply(m, sM);
+ }
+
+ // apply scale
+ for (i = 0; i < 12; i += 4) {
+ m[0 + i] *= sc[0];
+ m[1 + i] *= sc[1];
+ m[2 + i] *= sc[2];
+ }
+ return m;
+ },
+
+ /**
+ * Decompose transformation vectors into various properties out of matrix3d.
+ * @public
+ * @param {Number[]} matrix Matrix3d
+ * @param {Object} ret To store various transformation properties like translate, rotate, scale, skew and perspective.
+ * @return {Boolean} true, if matrix exists else false.
+ */
+ decompose: function(matrix, ret) {
+ if (matrix[15] === 0) return false;
+ var i,
+ tV = [],
+ rV = [],
+ pV = [],
+ skV = [],
+ scV = [],
+ row = [],
+ pdum3 = [];
+
+ for (i = 0; i < 16; i++)
+ matrix[i] /= matrix[15];
+
+ //TODO: decompose perspective
+ pV = [0, 0, 0, 0];
+
+ for (i = 0; i < 3; i++)
+ tV[i] = matrix[12 + i];
+
+ for (i = 0; i < 12; i += 4) {
+ row.push([
+ matrix[0 + i],
+ matrix[1 + i],
+ matrix[2 + i]
+ ]);
+ }
+
+ scV[0] = vector.len(row[0]);
+ row[0] = quaternion.normalize(row[0]);
+ skV[0] = vector.dot(row[0], row[1]);
+ row[1] = vector.combine(row[1], row[0], 1.0, -skV[0]);
+
+ scV[1] = vector.len(row[1]);
+ row[1] = quaternion.normalize(row[1]);
+ skV[0] /= scV[1];
+
+ // Compute XZ and YZ shears, orthogonalized 3rd row
+ skV[1] = vector.dot(row[0], row[2]);
+ row[2] = vector.combine(row[2], row[0], 1.0, -skV[1]);
+ skV[2] = vector.dot(row[1], row[2]);
+ row[2] = vector.combine(row[2], row[1], 1.0, -skV[2]);
+
+ // Next, get Z scale and normalize 3rd row.
+ scV[2] = vector.len(row[2]);
+ row[2] = quaternion.normalize(row[2]);
+ skV[1] /= scV[2];
+ skV[2] /= scV[2];
+
+ pdum3 = vector.cross(row[1], row[2]);
+ if (vector.dot(row[0], pdum3) < 0) {
+ for (i = 0; i < 3; i++) {
+ scV[i] *= -1;
+ row[i][0] *= -1;
+ row[i][1] *= -1;
+ row[i][2] *= -1;
+ }
+ }
+
+ rV[0] = 0.5 * Math.sqrt(Math.max(1 + row[0][0] - row[1][1] - row[2][2], 0));
+ rV[1] = 0.5 * Math.sqrt(Math.max(1 - row[0][0] + row[1][1] - row[2][2], 0));
+ rV[2] = 0.5 * Math.sqrt(Math.max(1 - row[0][0] - row[1][1] + row[2][2], 0));
+ rV[3] = 0.5 * Math.sqrt(Math.max(1 + row[0][0] + row[1][1] + row[2][2], 0));
+
+ if (row[2][1] > row[1][2]) rV[0] = -rV[0];
+ if (row[0][2] > row[2][0]) rV[1] = -rV[1];
+ if (row[1][0] > row[0][1]) rV[2] = -rV[2];
+
+ ret.translate = tV;
+ ret.rotate = rV;
+ ret.scale = scV;
+ ret.skew = skV;
+ ret.perspective = pV;
+ return true;
+ },
+
+ /**
+ * Decompose transformation matrix2d from matrix3d.
+ * @public
+ * @param {Number[]} matrix Matrix3d
+ * @param {Object} ret To store various transformation properties like translate, angle and matrix.
+ * @return {Boolean} ret To store various transformation properties like translate, angle and matrix.
+ */
+ decompose2D: function(m, ret) {
+ var scale = [],
+ matrix = [],
+ row0x = m[0],
+ row0y = m[1],
+ row1x = m[4],
+ row1y = m[5],
+ det, angle, sn, cs,
+ m11, m12, m21, m22;
+
+ ret = ret || {};
+ scale = [
+ Math.sqrt(row0x * row0x + row0y * row0y),
+ Math.sqrt(row1x * row1x + row1y * row1y)
+ ];
+
+ // If determinant is negative, one axis was flipped.
+ det = row0x * row1y - row0y * row1x;
+ if (det < 0)
+ // Flip axis with minimum unit vector dot product.
+ if (row0x < row1y)
+ scale[0] = -scale[0];
+ else
+ scale[1] = -scale[1];
+
+ // Renormalize matrix to remove scale.
+ if (scale[0]) {
+ row0x *= 1 / scale[0];
+ row0y *= 1 / scale[0];
+ }
+
+ if (scale[1]) {
+ row1x *= 1 / scale[1];
+ row1y *= 1 / scale[1];
+ }
+ ret.scale = scale;
+
+
+ // Compute rotation and renormalize matrix.
+ angle = Math.atan2(row0y, row0x);
+
+ if (angle) {
+ sn = -row0y;
+ cs = row0x;
+ m11 = row0x;
+ m12 = row0y;
+ m21 = row1x;
+ m22 = row1y;
+ row0x = cs * m11 + sn * m21;
+ row0y = cs * m12 + sn * m22;
+ row1x = -sn * m11 + cs * m21;
+ row1y = -sn * m12 + cs * m22;
+ }
+
+ // Rotate(-angle) = [cos(angle), sin(angle), -sin(angle), cos(angle)]
+ // = [row0x, -row0y, row0y, row0x]
+ // Thanks to the normalization above.
+ matrix[0] = row0x;
+ matrix[1] = row0y;
+ matrix[2] = row1x;
+ matrix[3] = row1y;
+ matrix[4] = m[12];
+ matrix[5] = m[13];
+ ret.matrix2D = matrix;
+
+ // Convert into degrees because our rotation functions expect it.
+ ret.angle = angle * 180 / Math.PI;
+
+ return ret;
+ },
+
+ /**
+ * Convert Matrix3d array to Matrix3d String
+ * @public
+ * @param {Number[]} m Matrix3d Array
+ * @return {String} Matrix3d String
+ */
+ toString: function(m) {
+ var i, ms = m.length > 10 ? 'matrix3d(' : 'matrix(';
+ for (i = 0; i < m.length - 1; i++) {
+ ms += (m[i] < 0.000001 && m[i] > -0.000001) ? '0,' : m[i] + ',';
+ }
+ ms += m[m.length - 1] + ')';
+ return ms;
+ }
+};
+
+var vector = exports.Vector = {
+ /**
+ * Length of a vector
+ * @param {Number[]} v - vector
+ * @return {Number} resultant length
+ * @public
+ */
+ len: function(v) {
+ return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ },
+
+ /**
+ * Divides vector with a scalar value.
+ * @param {Number[]} v - vector
+ * @param {Number} s - scalar value to divide
+ * @return {Number[]} resultant vector
+ * @public
+ */
+ divide: function(v, s) {
+ var divideVector = new Float32Array([v[0] / s, v[1] / s, v[2] / s]);
+ return divideVector;
+ },
+
+ /**
+ * Dot product of 3D vectors
+ * @param {Number[]} v1 - vector
+ * @param {Number[]} v2 - vector
+ * @return {Number} resultant dot product
+ * @public
+ */
+ dot: function(v1, v2) {
+ return (v1[0] * v2[0]) + (v1[1] * v2[1]) + (v1[2] * v2[2]) + (v1[3] !== undefined && v2[3] !== undefined ? (v1[3] * v2[3]) : 0);
+ },
+
+ /**
+ * Cross product of two vectors
+ * @param {Number[]} v1 - vector
+ * @param {Number[]} v2 - vector
+ * @return {Number[]} resultant cross product
+ * @public
+ */
+ cross: function(v1, v2) {
+ var crossProdMat = new Float32Array([v1[1] * v2[2] - v1[2] * v2[1], v1[2] * v2[0] - v1[0] * v2[2], v1[0] * v2[1] - v1[1] * v2[0]]);
+ return crossProdMat;
+ },
+
+ /**
+ * Combine scalar values with two vectors.
+ * Required during parsing scaler values matrix.
+ * @param {Number[]} a - first vector
+ * @param {Number[]} b - second vector
+ * @param {Number[]} ascl - first vector scalar
+ * @param {Number[]} bscl - second vector scalar
+ * @return {Number[]} resultant vector
+ * @public
+ */
+ combine: function(a, b, ascl, bscl) {
+ var combineMat = new Float32Array([(ascl * a[0]) + (bscl * b[0]), (ascl * a[1]) + (bscl * b[1]), (ascl * a[2]) + (bscl * b[2])]);
+ return combineMat;
+
+ }
+};
+
+var quaternion = exports.Quaternion = {
+ /**
+ * Gives the direction of motion from one vector to other.
+ * Returns true if moving towards positive direction.
+ * @param {Number[]} q1 - quant
+ * @param {Number[]} q2 - quant
+ * @return {boolean} true if positive, false otherwise.
+ * @public
+ */
+ direction: function(q1, q2) {
+ return (q1[0] - q2[0]) < 0 || (q1[1] - q2[1]) < 0 || (q1[2] - q2[2]) < 0;
+ },
+
+ /**
+ * Dot product of 3D quanterion
+ * @param {Number[]} q1 - quanterion
+ * @param {Number[]} q2 - quanterion
+ * @return {Number} resultant dot product
+ * @public
+ */
+ quantDot: function(q1, q2) {
+ return (q1[0] * q2[0]) + (q1[1] * q2[1]) + (q1[2] * q2[2]) + (q1[3] * q2[3]);
+ },
+
+ multiplication: function(q1, q2) {
+ return [q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2] - q2[3] * q1[3],
+ q2[0] * q1[1] + q2[1] * q1[0] - q2[2] * q1[3] + q2[3] * q1[2],
+ q2[0] * q1[2] + q2[1] * q1[3] + q2[2] * q1[0] - q2[3] * q1[1],
+ q2[0] * q1[3] - q2[1] * q1[2] + q2[2] * q1[1] + q2[3] * q1[0]];
+ },
+
+ /**
+ * Normalizing a vector is obtaining another unit vector in the same direction.
+ * To normalize a vector, divide the vector by its magnitude.
+ * @param {Number[]} q1 - quanterion
+ * @return {Number[]} resultant quanterion
+ * @public
+ */
+ normalize: function(q) {
+ return vector.divide(q, vector.len(q));
+ },
+
+ /**
+ * Converts a rotation vector to a quaternion vector.
+ * @param {Number[]} v - vector
+ * @return {Number[]} resultant quaternion
+ * @public
+ */
+ toQuant: function(v) {
+ if (!v) v = [];
+ var p = parseFloat(v[1] || 0) * Math.PI / 360,
+ y = parseFloat(v[2] || 0) * Math.PI / 360,
+ r = parseFloat(v[0] || 0) * Math.PI / 360,
+ c1 = Math.cos(p),
+ c2 = Math.cos(y),
+ c3 = Math.cos(r),
+ s1 = Math.sin(p),
+ s2 = Math.sin(y),
+ s3 = Math.sin(r),
+ q;
+
+ q = new Float32Array([
+ Math.round((s1 * s2 * c3 + c1 * c2 * s3) * 100000) / 100000,
+ Math.round((s1 * c2 * c3 + c1 * s2 * s3) * 100000) / 100000,
+ Math.round((c1 * s2 * c3 - s1 * c2 * s3) * 100000) / 100000,
+ Math.round((c1 * c2 * c3 - s1 * s2 * s3) * 100000) / 100000
+ ]);
+
+ return q;
+ }
+ //TODO: Acheive the same fucntionality for other 11 choices XYX, XZX, XZY, YXY, YXZ, YZX, YZY, ZXY, ZXZ, ZYX, ZYZ
+};
\ No newline at end of file
diff --git a/src/tween.js b/src/tween.js
new file mode 100644
index 000000000..5c6d975ef
--- /dev/null
+++ b/src/tween.js
@@ -0,0 +1,748 @@
+require('enyo');
+
+var
+ dom = require('./dom'),
+ utils = require('./utils'),
+ transform = require('./transform');
+
+
+var fn, state, ease, points, path, oldState, newState, node, matrix, cState = [],
+ domCSS = {};
+/**
+ * Tween is a module responsible for creating intermediate frames for an animation.
+ * The responsibilities of this module is to;
+ * - Interpolating current state of character.
+ * - Update DOM based on current state, using matrix for tranform and styles for others.
+ *
+ * @module enyo/tween
+ */
+module.exports = {
+
+ /**
+ * Indentifies initial pose of an element.
+ * @param {Object} actor - Element to be animated
+ * @param {Object} pose - Current behavior in the animation (at a given time)
+ * @param {Object} pose - Current behavior in the animation (at a given time)
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ init: function(actor, pose, initial) {
+ var k;
+ actor.initialState = actor.initialState || {};
+ if (!(actor && pose && pose.animate)) return;
+ node = actor.hasNode();
+ utils.mixin(pose, getAnimatedProperty(node, pose.animate, initial));
+ actor.currentState = pose.currentState;
+ for (k in pose.initialState) {
+ actor.initialState[k] = actor.initialState[k] || pose.initialState[k];
+ }
+ return pose;
+ },
+
+ /**
+ * Step represents state of the actor at any point of time in the animation.
+ * @param {Object} actor - Element to be animated
+ * @param {Object} pose - Current behavior in the animation (at a given time)
+ * @param {Number} t - Fraction which represents the animation (between 0 to 1)
+ * @param {Number} d - Duration of the current pose
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ step: function(actor, pose, t, d) {
+ if (!(actor && pose && pose.animate)) return;
+ t = t < 0 ? 0 : t;
+ t = t > 1 ? 1 : t;
+
+ var k;
+ domCSS = {};
+ node = actor.hasNode();
+ state = utils.clone(pose.currentState || pose._startAnim);
+ points = pose.controlPoints = pose.controlPoints || {};
+ ease = pose.animate && pose.animate.ease ? pose.animate.ease : this.ease;
+ path = pose.animate && pose.animate.path;
+
+ if (pose.props) {
+ for (k in pose.props) {
+ if (!pose._endAnim[k] || k === 'duration' || k === 'ease' || k === 'path') {
+ continue;
+ }
+
+ cState = utils.clone(state[k] || []);
+ newState = pose._endAnim[k].slice();
+ oldState = pose._startAnim[k].slice();
+
+ if (ease && (typeof ease !== 'function')) {
+ if (k == 'rotate') {
+ points[k] = points[k] ||
+ this.bezierSPoints(ease, oldState, newState, pose.props[k], points[k]);
+ fn = this.bezierSpline;
+ } else {
+ points[k] = points[k] ||
+ this.bezierPoints(ease, oldState, newState, points[k]);
+ fn = this.bezier;
+ }
+ cState = fn.call(this, t, points[k], cState);
+ } else {
+ fn = (k === 'rotate') ? this.slerp : this.lerp;
+ cState = fn.call(this, oldState, newState, ease(t, d), cState);
+ }
+
+ if (!isTransform(k)) {
+ domCSS = toPropertyValue(k, cState, domCSS);
+ }
+ state[k] = cState;
+ }
+ } else {
+ utils.mixin(state, oldState);
+ }
+
+ //TODO: Support for properties other than translate
+ if (path) {
+ this.traversePath(t, path, state.translate);
+ }
+
+ matrix = transform.Matrix.recompose(
+ state.translate,
+ state.rotate,
+ state.scale,
+ state.skew,
+ state.perspective
+ );
+ state.matrix = matrix;
+ pose.currentState = state;
+ domCSS = toTransformValue(matrix, domCSS);
+
+ actor.addStyles(domCSS);
+ },
+
+ /**
+ * This causes the stopped animation to be removed from GPU layer.
+ * @param {Object} actor - Element to be animated
+ * @param {Object} pose - Current behavior in the animation (at a given time)
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ halt: function(actor, pose) {
+ var matrix = pose.currentState && pose.currentState.matrix;
+
+ pose = transform.Matrix.decompose2D(matrix);
+ domCSS = toTransformValue(pose.matrix2D);
+ actor.addStyles(domCSS);
+ },
+
+ /**
+ * Overridden function for applying the default ease.
+ * @param {Number} t - Fraction which represents the animation (between 0 to 1)
+ * @return {Number} t
+ * @memberOf module:enyo/tween
+ * @public
+ * @override
+ */
+ ease: function(t) {
+ return t;
+ },
+
+ /**
+ * Draws linear interpolation between two values.
+ * @param {Number[]} vA - origin vector
+ * @param {Number[]} vB - Destination vector
+ * @param {Number} t - Fraction which represents the animation (between 0 to 1)
+ * @param {Number[]} vR - Resultant vector
+ * @return {Number[]} vR
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ lerp: function(vA, vB, t, vR) {
+ if (!vA) return;
+ if (!vR) vR = [];
+ var i, l = vA.length;
+
+ for (i = 0; i < l; i++) {
+ vR[i] = (1 - t) * vA[i] + t * vB[i];
+ }
+ return vR;
+ },
+
+ /**
+ * Draws sperical linear interpolation between two values.
+ * @param {Number[]} qA Quaternion origin
+ * @param {Number[]} qB - Quaternion destination
+ * @param {Number} t - Fraction which represents the animation (between 0 to 1)
+ * @param {Number[]} qR - Resultant quaternion
+ * @return {Number[]} qR
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ slerp: function(qA, qB, t, qR) {
+ if (!qA) return;
+ if (!qR) qR = [];
+ var a,
+ b,
+ theta,
+ dot = transform.Quaternion.quantDot(qA, qB),
+ l = qA.length;
+
+ dot = Math.min(Math.max(dot, -1.0), 1.0);
+ if (dot == 1.0) {
+ qR = utils.cloneArray(qA);
+ return qR;
+ }
+ theta = Math.acos(dot);
+ for (var i = 0; i < l; i++) {
+ a = (Math.sin((1 - t) * theta) / Math.sin(theta)) * qA[i];
+ b = (Math.sin(t * theta) / Math.sin(theta)) * qB[i];
+ qR[i] = a + b;
+ }
+ return qR;
+ },
+
+ /**
+ * Creates bezier curve path for animation.
+ * @param {Number} t - Fraction which represents the animation (between 0 to 1)
+ * @param {Number[]} points - knot and control points
+ * @param {Number[]} vR - Resulting points
+ * @return {Number[]} vR
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ bezier: function(t, points, vR) {
+ if (!points) return;
+ if (!vR) vR = [];
+
+ var i, j,
+ c = points.length,
+ l = points[0].length,
+ lastIndex = (c - 1),
+ startPoint = points[0],
+ endPoint = points[lastIndex],
+ values = this.getBezierValues(t, lastIndex);
+
+ for (i = 0; i < l; i++) {
+ vR[i] = 0;
+ for (j = 0; j < c; j++) {
+ if ((j > 0) && (j < (c - 1))) {
+ vR[i] = vR[i] + ((startPoint[i] + (points[j][i] * (endPoint[i] - startPoint[i]))) * values[j]);
+ } else {
+ vR[i] = vR[i] + (points[j][i] * values[j]);
+ }
+ }
+ }
+ return vR;
+ },
+
+ /**
+ * Returns the control points for bezier curve.
+ * @param {Object} easeObj- The easing object with values.
+ * @param {Number[]} startPoint - Starting point of the curve
+ * @param {Number[]} endPoint - End point of the curve
+ * @param {Number[]} points - control points
+ * @return {Number[]} points
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ bezierPoints: function(easeObj, startPoint, endPoint, points) {
+ if (!easeObj) return;
+ var order = (easeObj && Object.keys(easeObj).length) ? (Object.keys(easeObj).length + 1) : 0;
+ var bValues = [],
+ m1 = [],
+ m2 = [],
+ m3 = [],
+ m4 = [],
+ l = 0;
+ points = [startPoint];
+
+ var t, a;
+ for (var key in easeObj) {
+ t = parseFloat(key) / 100;
+ a = parseFloat(easeObj[key]) / 100;
+ bValues = this.getBezierValues(t, order);
+ bValues.shift();
+ m1.push(a - bValues.pop());
+ m2.push(bValues);
+ }
+
+ m3 = transform.Matrix.inverseN(m2, bValues.length);
+ m4 = transform.Matrix.multiplyN(m3, m1);
+ l = m4.length;
+ for (var i = 0; i < l; i++) {
+ var pValues = [];
+ for (var j = 0; j < endPoint.length; j++) {
+ pValues.push(m4[i]);
+ }
+ points.push(pValues);
+ }
+
+ points.push(endPoint);
+ return points;
+ },
+
+ /**
+ * Traverses the path of the animation
+ * @param {Number} t - Fraction which represents the animation (between 0 to 1)
+ * @param {Number[]} path - Array of points
+ * @param {Number[]} vR Resulatant Array
+ * @return {Number[]} vR
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ traversePath: function(t, path, vR) {
+ if (!path) return;
+ if (!vR) vR = [];
+
+ var i, j,
+ c = path.length,
+ l = path[0].length,
+ lastIndex = (c - 1),
+ values = this.getBezierValues(t, lastIndex);
+
+ for (i = 0; i < l; i++) {
+ vR[i] = 0;
+ for (j = 0; j < c; j++) {
+ vR[i] = vR[i] + (path[j][i] * values[j]);
+ }
+ }
+ return vR;
+ },
+
+ /**
+ * Returns the control points for bezier spline.
+ * @param {Object} ease- The easing object with values.
+ * @param {Number[]} startQuat - Quaternion origin
+ * @param {Number[]} endQuat - Quaternion destination
+ * @param {Number[]} endPoint - Final Destination point
+ * @param {Number[]} splinePoints - spline control points
+ * @return {Number[]} splinePoints
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ bezierSPoints: function(ease, startQuat, endQuat, endPoint, splinePoints) {
+ if (!ease) return;
+ var time = [0],
+ quats = [startQuat];
+
+ var a, n, _a, aI, bN, eP, key, i, j,
+ eD = formatCSSValues(endPoint);
+
+ splinePoints = splinePoints || {};
+ if (Object.keys(ease).length > 0) {
+ for (key in ease) {
+ eP = utils.clone(eD);
+ a = parseFloat(ease[key]) / 100;
+ for (i in eP) {
+ eP[i] *= a;
+ }
+ quats.push(transform.Quaternion.toQuant(eP));
+ time.push(parseFloat(key) / 100);
+ }
+ quats.push(endQuat);
+ time.push(1);
+ n = quats.length - 1;
+ aI = this.slerp(startQuat, endQuat, 0);
+ splinePoints[0] = [quats[0], aI, aI, quats[1]];
+ for (i = 0, j = 1; i < n; i++, j++) {
+ if (i === 0) {
+ aI = this.slerp(quats[0], this.slerp(quats[2], quats[1], 2.0), 1.0 / 3);
+ } else {
+ _a = this.slerp(this.slerp(quats[i - 1], quats[i], 2.0), quats[i + 1], 0.5);
+ aI = this.slerp(quats[j], _a, 1.0 / 3);
+ }
+ if (j === n) {
+ bN = this.slerp(quats[j], this.slerp(quats[j - 2], quats[j - 1], 2.0), 1.0 / 3);
+ } else {
+ _a = this.slerp(this.slerp(quats[j - 1], quats[j], 2.0), quats[j + 1], 0.5);
+ bN = this.slerp(quats[j], _a, -1.0 / 3);
+ }
+ splinePoints[time[j]] = [quats[i], aI, bN, quats[i + 1]];
+ }
+ }
+ return splinePoints;
+ },
+
+ /**
+ * Creates bezier spline path for animation.
+ * @param {Number} t - Fraction which represents the animation (between 0 to 1)
+ * @param {Number[]} points - knot and control points
+ * @param {Number[]} vR - Resulting points
+ * @return {Number[]} vR
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ bezierSpline: function(t, points, vR) {
+ if (!points) return;
+ if (!vR) vR = [];
+ var Q0, Q1, Q2, R0, R1,
+ p, key, pts;
+ for (p in points) {
+ if (p >= t) {
+ key = p;
+ break;
+ }
+ }
+ pts = points[key];
+
+ if (pts.length >= 4) {
+ Q0 = this.slerp(pts[0], pts[1], t);
+ Q1 = this.slerp(pts[1], pts[2], t);
+ Q2 = this.slerp(pts[2], pts[3], t);
+ R0 = this.slerp(Q0, Q1, t);
+ R1 = this.slerp(Q1, Q2, t);
+ vR = this.slerp(R0, R1, t);
+ } else
+ vR = this.slerp(pts[0], pts[1], t);
+ return vR;
+ },
+
+ /**
+ * Function to get the bezier coeffients based on the time and order
+ * @param {number} t - time
+ * @param {number} n - order
+ * @return {object} - bezier coefficients
+ * @memberOf module:enyo/tween
+ * @public
+ */
+ getBezierValues: function(t, n) {
+ t = parseFloat(t, 10),
+ n = parseInt(n, 10);
+
+ if (isNaN(t) || isNaN(n))
+ return void 0;
+ if ((t < 0) || (n < 0))
+ return void 0;
+ if (t > 1)
+ return void 0;
+
+ var c,
+ values = [],
+
+ x = (1 - t),
+ y = t;
+ //
+ // Binomial theorem to expand (x+y)^n
+ //
+ for (var k = 0; k <= n; k++) {
+ c = getCoeff(n, k) * Math.pow(x, (n - k)) * Math.pow(y, k);
+ values.push(c);
+ }
+
+ return values;
+ }
+};
+
+/**
+ * This function returns the coefficents based on the order and the current position
+ * @param {number} n - order
+ * @param {number} k - current position
+ * @return {object} - coefficients
+ * @private
+ */
+function getCoeff (n, k) {
+ n = parseInt(n, 10);
+ k = parseInt(k, 10);
+ // Credits
+ // https://math.stackexchange.com/questions/202554/how-do-i-compute-binomial-coefficients-efficiently#answer-927064
+ if (isNaN(n) || isNaN(k))
+ return void 0;
+ if ((n < 0) || (k < 0))
+ return void 0;
+ if (k > n)
+ return void 0;
+ if (k === 0)
+ return 1;
+ if (k === n)
+ return 1;
+ if (k > n / 2)
+ return getCoeff(n, n - k);
+
+ return n * getCoeff(n - 1, k - 1) / k;
+}
+
+/**
+ * Get DOM node animation properties.
+ * @param {HTMLElement} node DOM node
+ * @param {Object} props Properties to fetch from DOM.
+ * @param {Object} initial Default properties to be applied.
+ * @return {Object} Object with various animation properties.
+ * @private
+ */
+function getAnimatedProperty(node, props, initial) {
+ if (!node) return;
+
+ var
+ diff,
+ eP = {},
+ sP = initial ? utils.mixin({}, initial) : {},
+ tP = {},
+ dP = {},
+ iP = {},
+ m, k, v, t,
+ s = initial ? undefined : dom.getComputedStyle(node);
+
+ for (k in props) {
+ v = sP[k];
+ if (!isTransform(k)) {
+ v = v || getStyleValue(s || dom.getComputedStyle(node), k);
+ iP[k] = v;
+ sP[k] = formatCSSValues(v, k);
+ eP[k] = formatCSSValues(props[k], k);
+ if (sP[k].length) {
+ diff = sP[k].length - eP[k].length;
+ if(diff > 0) {
+ eP[k] = eP[k].concat(Array(diff).fill(0));
+ } else {
+ sP[k] = sP[k].concat(Array(-1 * diff).fill(0));
+ }
+ }
+ } else {
+ v = formatTransformValues(props[k], k);
+ if (k.match(/rotate/)) {
+ v = transform.Quaternion.toQuant(v);
+ tP.rotate = tP.rotate ? transform.Quaternion.multiplication(tP.rotate, v) : v;
+ } else {
+ t = k.replace(/[XYZ]$/, '');
+ tP[t] = tP[t] ? merge(tP[t], v) : v;
+ }
+ if (k.match(/[XYZ]$/)) {
+ t = k.replace(/[XYZ]$/, '');
+ props[t] = tP[t].join();
+ delete props[k];
+ }
+ }
+ }
+
+ if (initial) {
+ dP.translate = initial.translate;
+ dP.rotate = initial.rotate.length < 4 ? transform.Quaternion.toQuant(initial.rotate) : initial.rotate;
+ dP.scale = initial.scale;
+ dP.skew = initial.skew;
+ dP.perspective = initial.perspective;
+ } else {
+ m = getStyleValue(s || dom.getComputedStyle(node), dom.getCssTransformProp());
+ m = formatTransformValues(m, 'matrix');
+ transform.Matrix.decompose(m, dP);
+ iP.transform = transform.Matrix.toString(m);
+ }
+
+ for (k in dP) {
+ sP[k] = dP[k];
+ eP[k] = tP[k] || dP[k];
+ }
+ return {
+ _startAnim: sP,
+ _endAnim: eP,
+ _transform: dP,
+ currentState: dP,
+ initialState: iP,
+ matrix: m,
+ props: props
+ };
+}
+
+/**
+* @private
+*/
+function merge (ar1, ar2) {
+ ar1.map(function(num, id) {
+ return num + ar2[id];
+ });
+ return ar1;
+}
+
+
+/**
+ * Converts comma separated values to array.
+ * @private
+ * @param {String} val Value of required animation in any property.
+ * @param {String} prop Property name of which value has been provided.
+ * @return {Number[]} Create array from val.
+ */
+function formatCSSValues(val, prop) {
+ if (typeof val === 'function') {
+ return val;
+ }
+ if (prop === 'duration' || prop === 'delay') {
+ val = 0;
+ }
+ if (SHADOW[prop] || COLOR[prop]) {
+ if (val === 'none') {
+ val = Array(7).fill(0);
+ } else if (val.indexOf('rgb') === 0) {
+ val = val.split(')')[0].replace(/^\w*\(/, '').concat(val.split(')')[1].split(' ').join());
+ } else {
+ val = val.split('rgb(')[1].replace(')',',').concat(val.split('rgb(')[0]).replace(/, $/,'');
+ }
+ }
+ return stringToMatrix(val);
+}
+
+/**
+* @private
+*/
+function formatTransformValues(val, prop) {
+ var
+ res,
+ X, Y, Z,
+ last = prop.match(/[XYZ]$/),
+ defalt = prop.match(/scale/) ? 1 : 0;
+
+ if (prop === 'matrix') {
+ res = transform.Matrix.identity();
+ val = stringToMatrix(val.replace(/^\w*\(/, '').replace(')', ''));
+ if (val.length <= 6) {
+ res[0] = val[0];
+ res[1] = val[1];
+ res[4] = val[2];
+ res[5] = val[3];
+ res[12] = val[4];
+ res[13] = val[5];
+ }
+ if (val.length == 16) {
+ res = val;
+ }
+ } else if (last) {
+ val = parseFloat(val, 10);
+ X = (last[0] === 'X') ? val : defalt;
+ Y = (last[0] === 'Y') ? val : defalt;
+ Z = (last[0] === 'Z') ? val : defalt;
+ res = [X, Y, Z];
+ } else {
+ res = stringToMatrix(val);
+ }
+ return res;
+}
+
+/**
+ * Validates if property is a transform property.
+ * @public
+ * @param {String} transform Any transform property, for which we want to identify whether or not the property is transform.
+ * @return {Number} Value of the required transform property.
+ */
+function isTransform(transform) {
+ return TRANSFORM[transform];
+}
+
+/**
+* @private
+*/
+function stringToMatrix(val) {
+ if (!val || val === "auto" || val === 'none') {
+ return 0;
+ }
+ return val.toString().split(",").map(function(v) {
+ return parseFloat(v, 10);
+ });
+}
+
+/**
+* @private
+*/
+function toPropertyValue(prop, val, ret) {
+ if (!val) return;
+ ret = ret || {};
+ if (COLOR[prop]) {
+ val = val.map(function(v) {
+ return parseInt(v, 10);
+ });
+ val = 'rgb(' + val + ')';
+ } else if (INT_UNIT[prop]) {
+ val = parseInt(val[0], 10);
+ } else if (BORDER[prop]) {
+ val = val.map(function(v) {
+ return v + '%';
+ }).join(' ');
+ } else if (OPACITY[prop]) {
+ val = val[0].toFixed(6);
+ val = (val <= 0) ? '0.000001' : val;
+ } else if (SHADOW[prop]) {
+ val = 'rgb(' + val.slice(0, 3).map(function(v) {
+ return parseInt(v, 10);
+ }) + ') ' + val.slice(3).map(function(v) {
+ return v + 'px';
+ }).join(' ');
+ } else {
+ val = val[0] + 'px';
+ }
+
+ ret[prop] = val;
+ return ret;
+}
+
+
+/**
+* @private
+*/
+function toTransformValue (matrix, ret) {
+ var mat = transform.Matrix.toString(matrix),
+ key = dom.getStyleTransformProp();
+
+ ret = ret || {};
+ ret[key] = mat;
+ return ret;
+}
+
+
+/**
+ * Gets a style property applied from the DOM element.
+ * @private
+ * @param {HTMLElement} style Computed style of a DOM.
+ * @param {String} key Property name for which style has to be fetched.
+ * @return {Number|HTMLElement}
+ */
+function getStyleValue(style, key) {
+ return style.getPropertyValue(key) || style[key];
+}
+
+var
+ BORDER = {
+ 'border-radius': 1,
+ 'border-image-slice': 1,
+ 'border-top-left-radius': 1,
+ 'border-top-right-radius': 1,
+ 'border-bottom-left-radius': 1,
+ 'border-bottom-right-radius': 1
+ },
+ COLOR = {
+ 'color': 1,
+ 'fill': 1,
+ 'stop-color': 1,
+ 'flood-color': 1,
+ 'border-color': 1,
+ 'outline-color': 1,
+ 'lighting-color': 1,
+ 'border-top-color': 1,
+ 'background-color': 1,
+ 'border-left-color': 1,
+ 'border-right-color': 1,
+ 'border-bottom-color': 1
+ },
+ INT_UNIT = {
+ 'z-index': 1
+ },
+ SHADOW = {
+ 'box-shadow': 1,
+ 'text-shadow': 1
+ },
+ OPACITY = {
+ 'opacity': 1,
+ 'flood-opacity': 1,
+ 'stop-opacity': 1,
+ 'fill-opacity': 1,
+ 'stroke-opacity': 1
+ },
+ TRANSFORM = {
+ translate: 1,
+ translateX: 1,
+ translateY: 1,
+ translateZ: 1,
+ rotate: 1,
+ rotateX: 1,
+ rotateY: 1,
+ rotateZ: 1,
+ skew: 1,
+ skewX: 1,
+ skewY: 1,
+ scale: 1,
+ scaleX: 1,
+ scaleY: 1,
+ scaleZ: 1,
+ perspective: 1
+ };
\ No newline at end of file