Skip to content
Browse files

splitting out into separate files to make it easier to maintain.

  • Loading branch information...
1 parent 79caf7b commit 976397080ab240379c2717dc9de30856c7599c01 @heygrady committed Oct 6, 2010
View
108 src/jquery.angle.js
@@ -0,0 +1,108 @@
+///////////////////////////////////////////////////////
+// Angle
+///////////////////////////////////////////////////////
+(function($, window, document, undefined) {
+ /**
+ * Converting a radian to a degree
+ * @const
+ */
+ var RAD_DEG = 180/Math.PI;
+
+ /**
+ * Converting a radian to a grad
+ * @const
+ */
+ var RAD_GRAD = 200/Math.PI;
+
+ /**
+ * Converting a degree to a radian
+ * @const
+ */
+ var DEG_RAD = Math.PI/180;
+
+ /**
+ * Converting a degree to a grad
+ * @const
+ */
+ var DEG_GRAD = 2/1.8;
+
+ /**
+ * Converting a grad to a degree
+ * @const
+ */
+ var GRAD_DEG = 0.9;
+
+ /**
+ * Converting a grad to a radian
+ * @const
+ */
+ var GRAD_RAD = Math.PI/200;
+
+ /**
+ * Functions for converting angles
+ * @var Object
+ */
+ $.extend({
+ angle: {
+ /**
+ * available units for an angle
+ * @var Regex
+ */
+ runit: /(deg|g?rad)/,
+
+ /**
+ * Convert a radian into a degree
+ * @param Number rad
+ * @return Number
+ */
+ radianToDegree: function(rad) {
+ return rad * RAD_DEG;
+ },
+
+ /**
+ * Convert a radian into a degree
+ * @param Number rad
+ * @return Number
+ */
+ radianToGrad: function(rad) {
+ return rad * RAD_GRAD;
+ },
+
+ /**
+ * Convert a degree into a radian
+ * @param Number deg
+ * @return Number
+ */
+ degreeToRadian: function(deg) {
+ return deg * DEG_RAD;
+ },
+
+ /**
+ * Convert a degree into a radian
+ * @param Number deg
+ * @return Number
+ */
+ degreeToGrad: function(deg) {
+ return deg * DEG_GRAD;
+ },
+
+ /**
+ * Convert a grad into a degree
+ * @param Number grad
+ * @return Number
+ */
+ gradToDegree: function(grad) {
+ return grad * GRAD_DEG;
+ },
+
+ /**
+ * Convert a grad into a radian
+ * @param Number grad
+ * @return Number
+ */
+ gradToRadian: function(grad) {
+ return grad * GRAD_RAD;
+ }
+ }
+ });
+})(jQuery, this, this.document);
View
192 src/jquery.matrix.calculations.js
@@ -0,0 +1,192 @@
+///////////////////////////////////////////////////////
+// Matrix Calculations
+///////////////////////////////////////////////////////
+(function($, window, document, undefined) {
+ /**
+ * Matrix object for creating matrices relevant for 2d Transformations
+ * @var Object
+ */
+ if (typeof($.matrix) == 'undefined') {
+ $.extend({
+ matrix: {}
+ });
+ }
+
+ $.extend( $.matrix, {
+ /**
+ * Class for calculating coordinates on a matrix
+ * @param Matrix matrix
+ * @param Number outerHeight
+ * @param Number outerWidth
+ * @constructor
+ */
+ calc: function(matrix, outerHeight, outerWidth) {
+ /**
+ * @var Matrix
+ */
+ this.matrix = matrix;
+
+ /**
+ * @var Number
+ */
+ this.outerHeight = outerHeight;
+
+ /**
+ * @var Number
+ */
+ this.outerWidth = outerWidth;
+ }
+ });
+
+ $.matrix.calc.prototype = {
+ /**
+ * Calculate a coord on the new object
+ * @return Object
+ */
+ coord: function(x, y, z) {
+ //default z and w
+ z = typeof(z) !== 'undefined' ? z : 0;
+
+ var matrix = this.matrix,
+ vector;
+
+ switch (matrix.rows) {
+ case 2:
+ vector = matrix.x(new $.matrix.V2(x, y));
+ break;
+ case 3:
+ vector = matrix.x(new $.matrix.V3(x, y, z));
+ break;
+ }
+
+ return vector;
+ },
+
+ /**
+ * Calculate the corners of the new object
+ * @return Object
+ */
+ corners: function(x, y) {
+ // Try to save the corners if this is called a lot
+ var save = !(typeof(x) !=='undefined' || typeof(y) !=='undefined'),
+ c;
+ if (!this.c || !save) {
+ y = y || this.outerHeight;
+ x = x || this.outerWidth;
+
+ c = {
+ tl: this.coord(0, 0),
+ bl: this.coord(0, y),
+ tr: this.coord(x, 0),
+ br: this.coord(x, y)
+ };
+ } else {
+ c = this.c;
+ }
+
+ if (save) {
+ this.c = c;
+ }
+ return c;
+ },
+
+ /**
+ * Calculate the sides of the new object
+ * @return Object
+ */
+ sides: function(corners) {
+ // The corners of the box
+ var c = corners || this.corners();
+
+ return {
+ top: Math.min(c.tl.e(2), c.tr.e(2), c.br.e(2), c.bl.e(2)),
+ bottom: Math.max(c.tl.e(2), c.tr.e(2), c.br.e(2), c.bl.e(2)),
+ left: Math.min(c.tl.e(1), c.tr.e(1), c.br.e(1), c.bl.e(1)),
+ right: Math.max(c.tl.e(1), c.tr.e(1), c.br.e(1), c.bl.e(1))
+ };
+ },
+
+ /**
+ * Calculate the offset of the new object
+ * @return Object
+ */
+ offset: function(corners) {
+ // The corners of the box
+ var s = this.sides(corners);
+
+ // return size
+ return {
+ height: Math.abs(s.bottom - s.top),
+ width: Math.abs(s.right - s.left)
+ };
+ },
+
+ /**
+ * Calculate the area of the new object
+ * @return Number
+ * @link http://en.wikipedia.org/wiki/Quadrilateral#Area_of_a_convex_quadrilateral
+ */
+ area: function(corners) {
+ // The corners of the box
+ var c = corners || this.corners();
+
+ // calculate the two diagonal vectors
+ var v1 = {
+ x: c.tr.e(1) - c.tl.e(1) + c.br.e(1) - c.bl.e(1),
+ y: c.tr.e(2) - c.tl.e(2) + c.br.e(2) - c.bl.e(2)
+ },
+ v2 = {
+ x: c.bl.e(1) - c.tl.e(1) + c.br.e(1) - c.tr.e(1),
+ y: c.bl.e(2) - c.tl.e(2) + c.br.e(2) - c.tr.e(2)
+ };
+
+ return 0.25 * Math.abs(v1.e(1) * v2.e(2) - v1.e(2) * v2.e(1));
+ },
+
+ /**
+ * Calculate the non-affinity of the new object
+ * @return Number
+ */
+ nonAffinity: function() {
+ // The corners of the box
+ var sides = this.sides(),
+ xDiff = sides.top - sides.bottom,
+ yDiff = sides.left - sides.right;
+
+ return parseFloat(parseFloat(Math.abs(
+ (Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) /
+ (sides.top * sides.bottom + sides.left * sides.right)
+ )).toFixed(8));
+ },
+
+ /**
+ * Calculate a proper top and left for IE
+ * @param Object toOrigin
+ * @param Object fromOrigin
+ * @return Object
+ */
+ originOffset: function(toOrigin, fromOrigin) {
+ // the origin to translate to
+ toOrigin = toOrigin ? toOrigin : new $.matrix.V2(
+ this.outerWidth * 0.5,
+ this.outerHeight * 0.5
+ );
+
+ // the origin to translate from (IE has a fixed origin of 0, 0)
+ fromOrigin = fromOrigin ? fromOrigin : new $.matrix.V2(
+ 0,
+ 0
+ );
+
+ // transform the origins
+ var toCenter = this.coord(toOrigin.e(1), toOrigin.e(2));
+ var fromCenter = this.coord(fromOrigin.e(1), fromOrigin.e(2));
+
+ // return the offset
+ return {
+ top: (fromCenter.e(2) - fromOrigin.e(2)) - (toCenter.e(2) - toOrigin.e(2)),
+ left: (fromCenter.e(1) - fromOrigin.e(1)) - (toCenter.e(1) - toOrigin.e(1))
+ };
+ }
+ };
+})(jQuery, this, this.document);
View
219 src/jquery.matrix.functions.js
@@ -0,0 +1,219 @@
+///////////////////////////////////////////////////////
+// 2d Matrix Functions
+///////////////////////////////////////////////////////
+(function($, window, document, undefined) {
+ /**
+ * Matrix object for creating matrices relevant for 2d Transformations
+ * @var Object
+ */
+ if (typeof($.matrix) == 'undefined') {
+ $.extend({
+ matrix: {}
+ });
+ }
+
+ $.extend( $.matrix, {
+ /**
+ * Identity matrix
+ * @param Number size
+ * @return Matrix
+ */
+ identity: function(size) {
+ size = size || 2;
+ var length = size * size,
+ elements = new Array(length),
+ mod = size + 1;
+ for (var i = 0; i < length; i++) {
+ elements[i] = (i % mod) === 0 ? 1 : 0;
+ }
+ return new $.matrix['M'+size+'x'+size](elements);
+ },
+
+ /**
+ * Matrix
+ * @return Matrix
+ */
+ matrix: function() {
+ var args = Array.prototype.slice.call(arguments);
+ // arguments are in column-major order
+ switch (arguments.length) {
+ case 4:
+ return new $.matrix.M2x2(
+ args[0], args[2],
+ args[1], args[3]
+ );
+ break;
+ case 6:
+ return new $.matrix.M3x3(
+ args[0], args[2], args[4],
+ args[1], args[3], args[5],
+ 0, 0, 1
+ );
+ break;
+ }
+ },
+
+ /**
+ * Reflect (same as rotate(180))
+ * @return Matrix
+ */
+ reflect: function() {
+ return new $.matrix.M2x2(
+ -1, 0,
+ 0, -1
+ );
+ },
+
+ /**
+ * Reflect across the x-axis (mirrored upside down)
+ * @return Matrix
+ */
+ reflectX: function() {
+ return new $.matrix.M2x2(
+ 1, 0,
+ 0, -1
+ );
+ },
+
+ /**
+ * Reflect by swapping x an y (same as reflectX + rotate(-90))
+ * @return Matrix
+ */
+ reflectXY: function() {
+ return new $.matrix.M2x2(
+ 0, 1,
+ 1, 0
+ );
+ },
+
+ /**
+ * Reflect across the y-axis (mirrored)
+ * @return Matrix
+ */
+ reflectY: function() {
+ return new $.matrix.M2x2(
+ -1, 0,
+ 0, 1
+ );
+ },
+
+ /**
+ * Rotates around the origin
+ * @param Number deg
+ * @return Matrix
+ * @link http://www.w3.org/TR/SVG/coords.html#RotationDefined
+ */
+ rotate: function(deg) {
+ //TODO: detect units
+ var rad = $.angle.degreeToRadian(deg),
+ costheta = Math.cos(rad),
+ sintheta = Math.sin(rad);
+
+ var a = costheta,
+ b = sintheta,
+ c = -sintheta,
+ d = costheta;
+
+ return new $.matrix.M2x2(
+ a, c,
+ b, d
+ );
+
+ },
+
+ /**
+ * Scale
+ * @param Number sx
+ * @param Number sy
+ * @return Matrix
+ * @link http://www.w3.org/TR/SVG/coords.html#ScalingDefined
+ */
+ scale: function (sx, sy) {
+ sx = sx || sx === 0 ? sx : 1;
+ sy = sy || sy === 0 ? sy : sx;
+
+ return new $.matrix.M2x2(
+ sx, 0,
+ 0, sy
+ );
+ },
+
+ /**
+ * Scale on the X-axis
+ * @param Number sx
+ * @return Matrix
+ */
+ scaleX: function (sx) {
+ return $.matrix.scale(sx, 1);
+ },
+
+ /**
+ * Scale on the Y-axis
+ * @param Number sy
+ * @return Matrix
+ */
+ scaleY: function (sy) {
+ return $.matrix.scale(1, sy);
+ },
+
+ /**
+ * Skews on the X-axis and Y-axis
+ * @param Number degX
+ * @param Number degY
+ * @return Matrix
+ */
+ skew: function (degX, degY) {
+ degX = degX || 0;
+ degY = degY || 0;
+
+ //TODO: detect units
+ var radX = $.angle.degreeToRadian(degX),
+ radY = $.angle.degreeToRadian(degY),
+ x = Math.tan(radX),
+ y = Math.tan(radY);
+
+ return new $.matrix.M2x2(
+ 1, x,
+ y, 1
+ );
+ },
+
+ /**
+ * Skews on the X-axis
+ * @param Number degX
+ * @return Matrix
+ * @link http://www.w3.org/TR/SVG/coords.html#SkewXDefined
+ */
+ skewX: function (degX) {
+ return $.matrix.skew(degX);
+ },
+
+ /**
+ * Skews on the Y-axis
+ * @param Number degY
+ * @return Matrix
+ * @link http://www.w3.org/TR/SVG/coords.html#SkewYDefined
+ */
+ skewY: function (degY) {
+ return $.matrix.skew(0, degY);
+ },
+
+ /**
+ * Translate
+ * @param Number tx
+ * @param Number ty
+ * @return Matrix
+ * @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
+ */
+ translate: function (tx, ty) {
+ tx = tx || 0;
+ ty = ty || 0;
+
+ return new $.matrix.M3x3(
+ 1, 0, tx,
+ 0, 1, ty,
+ 0, 0, 1
+ );
+ }
+ });
+})(jQuery, this, this.document);
View
266 src/jquery.matrix.js
@@ -0,0 +1,266 @@
+///////////////////////////////////////////////////////
+// Matrix
+///////////////////////////////////////////////////////
+(function($, window, document, undefined) {
+ /**
+ * Matrix object for creating matrices relevant for 2d Transformations
+ * @var Object
+ */
+ if (typeof($.matrix) == 'undefined') {
+ $.extend({
+ matrix: {}
+ });
+ }
+
+ $.extend( $.matrix, {
+ /**
+ * A 2-value vector
+ * @param Number x
+ * @param Number y
+ * @constructor
+ */
+ V2: function(x, y){
+ if ($.isArray(arguments[0])) {
+ this.elements = arguments[0].slice(0, 2);
+ } else {
+ this.elements = [x, y];
+ }
+ this.length = 2;
+ },
+
+ /**
+ * A 2-value vector
+ * @param Number x
+ * @param Number y
+ * @param Number z
+ * @constructor
+ */
+ V3: function(x, y, z){
+ if ($.isArray(arguments[0])) {
+ this.elements = arguments[0].slice(0, 3);
+ } else {
+ this.elements = [x, y, z];
+ }
+ this.length = 3;
+ },
+
+ /**
+ * A 2x2 Matrix, useful for 2D-transformations without translations
+ * @param Number mn
+ * @constructor
+ */
+ M2x2: function(m11, m12, m21, m22) {
+ if ($.isArray(arguments[0])) {
+ this.elements = arguments[0].slice(0, 4);
+ } else {
+ this.elements = Array.prototype.slice.call(arguments).slice(0, 4);
+ }
+ this.rows = 2;
+ this.cols = 2;
+ },
+
+ /**
+ * A 3x3 Matrix, useful for 3D-transformations without translations
+ * @param Number mn
+ * @constructor
+ */
+ M3x3: function(m11, m12, m13, m21, m22, m23, m31, m32, m33) {
+ if ($.isArray(arguments[0])) {
+ this.elements = arguments[0].slice(0, 9);
+ } else {
+ this.elements = Array.prototype.slice.call(arguments).slice(0, 9);
+ }
+ this.rows = 3;
+ this.cols = 3;
+ }
+ });
+
+ /** generic matrix prototype */
+ var Matrix = {
+ /**
+ * Return a specific element from the matrix
+ * @param Number row where 1 is the 0th row
+ * @param Number col where 1 is the 0th column
+ * @return Number
+ */
+ e: function(row, col) {
+ var rows = this.rows,
+ cols = this.cols;
+
+ // return 0 on nonsense rows and columns
+ if (row > rows || col > rows || row < 1 || col < 1) {
+ return 0;
+ }
+
+ return this.elements[(row - 1) * cols + col - 1];
+ }
+ };
+
+ /** Extend all of the matrix types with the same prototype */
+ $.extend($.matrix.M2x2.prototype, Matrix, {
+ toM3x3: function() {
+ var a = this.elements;
+ return new $.matrix.M3x3(
+ a[0], a[1], 0,
+ a[2], a[3], 0,
+ 0, 0, 1
+ );
+ }
+
+ /**
+ * Multiply a 2x2 matrix by a similar matrix or a vector
+ * @param M2x2 | V2 matrix
+ * @return M2x2 | V2
+ */
+ x: function(matrix) {
+ var isVector = typeof(matrix.rows) === 'undefined';
+
+ // Ensure the right-sized matrix
+ if (!isVector && matrix.rows == 3) {
+ return this.toM3x3().x(matrix);
+ }
+
+ var a = this.elements,
+ b = matrix.elements;
+
+ if (isVector && b.length == 2) {
+ // b is actually a vector
+ return new $.matrix.V2(
+ a[0] * b[0] + a[1] * b[1],
+ a[2] * b[0] + a[3] * b[1]
+ );
+ } else if (b.length == a.length) {
+ // b is a 2x2 matrix
+ return new $.matrix.M2x2(
+ a[0] * b[0] + a[1] * b[2],
+ a[0] * b[1] + a[1] * b[3],
+
+ a[2] * b[0] + a[3] * b[2],
+ a[2] * b[1] + a[3] * b[3]
+ );
+ }
+ return false; // fail
+ },
+
+ /**
+ * Generates an inverse of the current matrix
+ * @param void
+ * @return M2x2
+ * @link http://www.dr-lex.be/random/matrix_inv.html
+ */
+ inverse: function() {
+ var d = 1/this.determinant(),
+ a = this.elements;
+ return new $.matrix.M2x2(
+ d * a[3], d * -a[1],
+ d * -a[2], d * a[0]
+ );
+ },
+
+ /**
+ * Calculates the determinant of the current matrix
+ * @param void
+ * @return Number
+ * @link http://www.dr-lex.be/random/matrix_inv.html
+ */
+ determinant: function() {
+ var a = this.elements;
+ return a[0] * a[3] - a[1] * a[2];
+ }
+ });
+
+ $.extend($.matrix.M3x3.prototype, Matrix, {
+ /**
+ * Multiply a 3x3 matrix by a similar matrix or a vector
+ * @param M3x3 | V3 matrix
+ * @return M3x3 | V3
+ */
+ x: function(matrix) {
+ var isVector = typeof(matrix.rows) === 'undefined';
+
+ // Ensure the right-sized matrix
+ if (!isVector && matrix.rows < 3) {
+ matrix = matrix.toM3x3();
+ }
+
+ var a = this.elements,
+ b = matrix.elements;
+
+ if (isVector && b.length == 3) {
+ // b is actually a vector
+ return new $.matrix.V3(
+ a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
+ a[3] * b[0] + a[4] * b[1] + a[5] * b[2],
+ a[6] * b[0] + a[7] * b[1] + a[8] * b[2]
+ );
+ } else if (b.length == a.length) {
+ // b is a 3x3 matrix
+ return new $.matrix.M3x3(
+ a[0] * b[0] + a[1] * b[3] + a[2] * b[6],
+ a[0] * b[1] + a[1] * b[4] + a[2] * b[7],
+ a[0] * b[2] + a[1] * b[5] + a[2] * b[8],
+
+ a[3] * b[0] + a[4] * b[3] + a[5] * b[6],
+ a[3] * b[1] + a[4] * b[4] + a[5] * b[7],
+ a[3] * b[2] + a[4] * b[5] + a[5] * b[8],
+
+ a[6] * b[0] + a[7] * b[3] + a[8] * b[6],
+ a[6] * b[1] + a[7] * b[4] + a[8] * b[7],
+ a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
+ );
+ }
+ return false; // fail
+ },
+
+ /**
+ * Generates an inverse of the current matrix
+ * @param void
+ * @return M3x3
+ * @link http://www.dr-lex.be/random/matrix_inv.html
+ */
+ inverse: function() {
+ var d = 1/this.determinant(),
+ a = this.elements;
+ return new $.matrix.M3x3(
+ d * ( a[8] * a[4] - a[7] * a[5]),
+ d * (-(a[8] * a[1] - a[7] * a[2])),
+ d * ( a[5] * a[1] - a[4] * a[2]),
+
+ d * (-(a[8] * a[3] - a[6] * a[5])),
+ d * ( a[8] * a[0] - a[6] * a[2]),
+ d * (-(a[5] * a[0] - a[3] * a[2])),
+
+ d * ( a[7] * a[3] - a[6] * a[4]),
+ d * (-(a[7] * a[0] - a[6] * a[1])),
+ d * ( a[4] * a[0] - a[3] * a[1])
+ );
+ },
+
+ /**
+ * Calculates the determinant of the current matrix
+ * @param void
+ * @return Number
+ * @link http://www.dr-lex.be/random/matrix_inv.html
+ */
+ determinant: function() {
+ var a = this.elements;
+ return a[0] * (a[8] * a[4] - a[7] * a[5]) - a[3] * (a[8] * a[1] - a[7] * a[2]) + a[6] * (a[5] * a[1] - a[4] * a[2]);
+ }
+ });
+
+ /** generic vector prototype */
+ var Vector = {
+ /**
+ * Return a specific element from the vector
+ * @param Number i where 1 is the 0th value
+ * @return Number
+ */
+ e: function(i) {
+ return this.elements[i - 1];
+ }
+ };
+
+ /** Extend all of the vector types with the same prototype */
+ $.extend($.matrix.V2.prototype, Vector);
+ $.extend($.matrix.V3.prototype, Vector);
+})(jQuery, this, this.document);
View
297 src/jquery.transform.animate.js
@@ -0,0 +1,297 @@
+///////////////////////////////////////////////////////
+// Animation
+///////////////////////////////////////////////////////
+(function($, window, document, undefined) {
+ // Extend the jQuery animation to handle transform functions
+ /**
+ * @var Regex looks for units on a string
+ */
+ var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
+
+ /**
+ * @var Regex identify if additional values are hidden in the unit
+ */
+ var rfxmultinum = /^(.*?)\s+([+\-]=)?([\d+.\-]+)(.*)$/;
+
+ /**
+ * Doctors prop values in the event that they contain spaces
+ * @param Object prop
+ * @param String speed
+ * @param String easing
+ * @param Function callback
+ * @return bool
+ */
+ var _animate = $.fn.animate;
+ $.fn.animate = function( prop, speed, easing, callback ) {
+ //NOTE: The $.fn.animate() function is a big jerk and requires
+ // you to attempt to convert the values passed into pixels.
+ // So we have to doctor the values passed in here to make
+ // sure $.fn.animate() won't think there's units an ruin
+ // our fun.
+ if (prop && !jQuery.isEmptyObject(prop)) {
+ var $elem = this;
+ jQuery.each( prop, function( name, val ) {
+ // Clean up the numbers for space-sperated prop values
+ if ($.inArray(name, $.transform.funcs) != -1) {
+ // allow for reflection animation
+ if ($.transform.rfunc.reflect.test(name)) {
+ var m = val ? $.matrix[name]() : $.matrix.identity(),
+ e = m.elements;
+ val = [e[0], e[1], e[2], e[3]];
+ }
+
+ var parts = rfxnum.exec(val);
+
+ if ((parts && parts[3]) || $.isArray(val)) {
+ // Either a unit was found or an array was passed
+ var end, unit, values = [];
+
+ if ($.isArray(val)) {
+ // An array was passed
+ $.each(val, function(i) {
+ parts = rfxnum.exec(this);
+ end = parseFloat(parts[2]);
+ unit = parts[3] || "px";
+
+ // Remember value
+ values.push({
+ end: (parts[1] ? parts[1] : '') + end,
+ unit: unit
+ });
+ });
+ } else {
+ // A unit was found
+ end = parseFloat( parts[2] );
+ unit = parts[3] || "px";
+
+ // Remember the first value
+ values.push({
+ end: (parts[1] ? parts[1] : '') + end,
+ unit: unit
+ });
+
+ // Detect additional values hidden in the unit
+ var i = 0;
+ while (parts = rfxmultinum.exec(unit)) {
+ // Fix the previous unit
+ values[i].unit = parts[1];
+
+ // Remember this value
+ values.push({
+ end: (parts[2] ? parts[2] : '') + parseFloat(parts[3]),
+ unit: parts[4]
+ });
+ unit = parts[4];
+ i++;
+ }
+ }
+
+ // Save the values and truncate the value to make it safe to animate
+ $elem.data('data-animate-' + name, values);
+ prop[name] = values[0].end; // NOTE: this propegates into the arguments object
+ }
+ }
+ });
+ }
+ //NOTE: we edit prop above
+ return _animate.apply(this, arguments);
+ };
+
+ /**
+ * Returns appropriate start value for transform props
+ * @param Boolean force
+ * @return Number
+ */
+ var _cur = $.fx.prototype.cur;
+ $.fx.prototype.cur = function(force) {
+ //NOTE: The cur function tries to look things up on the element
+ // itself as a native property first instead of as a style
+ // property. However, the animate function is a big jerk
+ // and it's extremely easy to poison the element.style
+ // with a random property and ruin all of the fun. So, it's
+ // easier to just look it up ourselves.
+ if ($.inArray(this.prop, $.transform.funcs) != -1) {
+ this.transform = this.transform || this.elem.transform || new $.transform(this.elem);
+ var r = $.transform.rfunc;
+
+ // return a single unitless number and animation will play nice
+ var value = this.transform.getAttr(this.prop),
+ parts = rfxnum.exec($.isArray(value) ? value[0] : value);
+ if (value === null || parts === null) {
+ value = r.scale.test(this.prop) || r.reflect.test(this.prop) ? 1 : 0;
+ parts = [null, null, value];
+ }
+ return parseFloat(parts[2]);
+ }
+ return _cur.apply(this, arguments);
+ };
+
+ /**
+ * Detects the existence of a space separated value
+ * @param Object fx
+ * @return null
+ */
+ $.fx.multivalueInit = function(fx) {
+ var $elem = $(fx.elem),
+ values = fx.transform.getAttr(fx.prop), // existing values
+ initValues = $elem.data('data-animate-' + fx.prop); // new values passed into animate
+
+ if (initValues) {
+ $elem.removeData('data-animate-' + fx.prop); // unremember the saved property
+ }
+
+ if ($.transform.rfunc.reflect.test(fx.prop)) {
+ values = fx.transform.getAttr('matrix');
+ }
+
+ fx.values = [];
+
+ // If we found a previous array but we're only setting one value, we need to set both
+ if ($.isArray(values) && !$.isArray(initValues)) {
+ initValues = [
+ {
+ end: parseFloat(fx.end),
+ unit: fx.unit
+ },
+ {
+ end: $.transform.rfunc.scale.test(fx.prop) ? 1 : 0,
+ unit: fx.unit
+ }
+ ];
+ }
+
+ // If we altered the values before
+ // This happens in the doctored animate function when we pass a unit or multiple values
+ if (initValues) {
+ var start,
+ rscalefunc = $.transform.rfunc.scale,
+ parts;
+ $.each(initValues, function(i, val) {
+ // pull out the start value
+ if ($.isArray(values)) {
+ start = values[i];
+ } else if (i > 0) {
+ // scale duplicates the values for x and y
+ start = rscalefunc.test(fx.prop) ? values : null;
+ } else {
+ start = values;
+ }
+
+ // if we didn't find a start value
+ if (!start && start !== 0) {
+ start = rscalefunc.test(fx.prop) ? 1 : 0;
+ }
+
+ // ensure a number
+ start = parseFloat(start);
+
+ // handle the existence of += and -= prefixes
+ parts = rfxnum.exec(val.end);
+ if (parts && parts[1]) {
+ val.end = ((parts[1] === "-=" ? -1 : 1) * parseFloat(parts[2])) + start;
+ }
+
+ // Save the values
+ fx.values.push({
+ start: parseFloat(start),
+ end: parseFloat(val.end),
+ unit: val.unit
+ });
+ });
+ } else {
+ // Save the known value
+ fx.values.push({
+ start: parseFloat(fx.start),
+ end: parseFloat(fx.end), // force a Number
+ unit: fx.unit
+ });
+ }
+ };
+
+ /**
+ * Animates a multi value attribute
+ * @param Object fx
+ * @return null
+ */
+ $.fx.multivalueStep = {
+ _default: function(fx) {
+ $.each(fx.values, function(i, val) {
+ fx.values[i].now = val.start + ((val.end - val.start) * fx.pos);
+ });
+ }
+ };
+
+ /**
+ * Step for animating tranformations
+ */
+ $.each($.transform.funcs, function(i, func) {
+ $.fx.step[func] = function(fx) {
+ // Initialize the transformation
+ if (!fx.transformInit) {
+ fx.transform = fx.transform || fx.elem.transform || new $.transform(fx.elem);
+
+ // Handle multiple values
+ $.fx.multivalueInit(fx);
+ if (fx.values.length > 1) {
+ fx.multiple = true;
+ }
+
+ // Force degrees for angles, Remove units for unitless
+ var r = $.transform.rfunc;
+ if (r.angle.test(fx.prop)) {
+ //TODO: we should convert from other rational units
+ fx.unit = 'deg';
+ } else if (r.scale.test(fx.prop)) {
+ fx.unit = '';
+ } else if (r.reflect.test(fx.prop)) {
+ //TODO: for animation purposes, this is a matrix and can be animated (although it looks silly)
+ fx.unit = ''; //this is a boolean func
+ } else if (fx.prop == 'matrix') {
+ fx.unit = '';
+ }
+ //TODO: I guess we already foced length units earlier
+
+ // Force all units on multiple values to be the same
+ //TODO: we should convert from other rational units
+ $.each(fx.values, function(i) {fx.values[i].unit = fx.unit;});
+
+ fx.transformInit = true;
+ }
+
+
+ // Increment all of the values
+ if (fx.multiple) {
+ ($.fx.multivalueStep[fx.prop] || $.fx.multivalueStep._default)(fx);
+ } else {
+ fx.values[0].now = fx.now;
+ }
+
+ var values = [];
+
+ // Do some value correction and join the values
+ $.each(fx.values, function(i, value) {
+ // Keep angles below 360 in either direction.
+ if (value.unit == 'deg') {
+ while (value.now >= 360 ) {
+ value.now -= 360;
+ }
+ while (value.now <= -360 ) {
+ value.now += 360;
+ }
+ }
+ // TODO: handle reflection matrices here
+
+ //Pretty up the final value (use the double parseFloat
+ // to correct super small decimals)
+ values.push(parseFloat(parseFloat(value.now).toFixed(8)) + value.unit);
+ });
+
+ // Apply the transformation
+ var funcs = {},
+ prop = $.transform.rfunc.reflect.test(fx.prop) ? 'matrix' : fx.prop;
+
+ funcs[prop] = fx.multiple ? values : values[0];
+ fx.transform.exec(funcs, {preserve: true});
+ };
+ });
+})(jQuery, this, this.document);
View
122 src/jquery.transform.attributes.js
@@ -0,0 +1,122 @@
+///////////////////////////////////////////////////////
+// Attr
+///////////////////////////////////////////////////////
+(function($, window, document, undefined) {
+ var rfuncvalue = /([\w-]*?)\((.*?)\)/g, // with values
+ attr = 'data-transform',
+ rspace = /\s/,
+ rcspace = /,\s/;
+
+ $.extend($.transform.prototype, {
+ /**
+ * This overrides all of the attributes
+ * @param Object funcs a list of transform functions to store on this element
+ * @return void
+ */
+ setAttrs: function(funcs) {
+ var string = '',
+ value;
+ for (var func in funcs) {
+ value = funcs[func];
+ if ($.isArray(value)) {
+ value = value.join(', ');
+ }
+ string += ' ' + func + '(' + value + ')';
+ }
+ this.attr = $.trim(string);
+ this.$elem.attr(attr, this.attr);
+ },
+
+ /**
+ * This sets only a specific atribute
+ * @param string func name of a transform function
+ * @param mixed value with proper units
+ * @return void
+ */
+ setAttr: function(func, value) {
+ // stringify the value
+ if ($.isArray(value)) {
+ value = value.join(', ');
+ }
+ value = $.trim(value+'');
+
+ // pull from a local variable to look it up
+ var transform = this.attr || this.$elem.attr(attr);
+
+ if (!transform || transform.indexOf(func) > -1) {
+ // We don't have any existing values, save it
+ // we don't have this function yet, save it
+ this.attr = $.trim(transform + ' ' + func + '(' + value + ')');
+ this.$elem.attr(attr, this.attr);
+ } else {
+ // replace the existing value
+ var funcs = [], parts;
+
+ // regex split
+ rfuncvalue.lastIndex = 0; // reset the regex pointer
+ while ((result = rfuncvalue.exec(transform)) !== null) {
+ if (func == parts[1]) {
+ funcs.push(func + '(' + value + ')');
+ } else {
+ funcs.push(parts[0]);
+ }
+ }
+ this.attr = funcs.join(' ');
+ this.$elem.attr(attr, this.attr);
+ }
+ },
+
+ /**
+ * @return Object
+ */
+ getAttrs: function() {
+ var transform = this.attr || this.$elem.attr(attr);
+ if (!transform) {
+ // We don't have any existing values, return empty object
+ return {};
+ }
+
+ // replace the existing value
+ var attrs = {},
+ result, parts, value;
+
+ rfuncvalue.lastIndex = 0; // reset the regex pointer
+ while ((parts = rfuncvalue.exec(transform)) !== null) {
+ if (parts) {
+ value = parts[2].split(rcspace);
+ attrs[parts[1]] = value.length == 1 ? value[0] : value;
+ }
+ }
+ return attrs;
+ },
+
+ /**
+ * @param String func
+ * @return mixed
+ */
+ getAttr: function(func) {
+ var attrs = this.getAttrs();
+
+ if (typeof attrs[func] !== 'undefined') {
+ return attrs[func];
+ }
+
+ // animate needs sensible defaults for some props
+ switch (func) {
+ case 'scale': return [1, 1]; break;
+ case 'scaleX': // no break;
+ case 'scaleY': return 1; break;
+ case 'matrix': return [1, 0, 0, 1, 0, 0]; break;
+ case 'origin':
+ if ($.support.csstransforms) {
+ // supported browsers return percentages always
+ return this.$elem.css(this.transformOriginProperty).split(rspace);
+ } else {
+ // just force IE to also return a percentage
+ return ['50%', '50%'];
+ }
+ }
+ return null;
+ }
+ });
+})(jQuery, this, this.document);
View
1,254 src/jquery.transform.js
@@ -7,115 +7,6 @@
* http://jquery.org/license
*/
///////////////////////////////////////////////////////
-// Angle
-///////////////////////////////////////////////////////
-(function($, window, document, undefined) {
- /**
- * Converting a radian to a degree
- * @const
- */
- var RAD_DEG = 180/Math.PI;
-
- /**
- * Converting a radian to a grad
- * @const
- */
- var RAD_GRAD = 200/Math.PI;
-
- /**
- * Converting a degree to a radian
- * @const
- */
- var DEG_RAD = Math.PI/180;
-
- /**
- * Converting a degree to a grad
- * @const
- */
- var DEG_GRAD = 2/1.8;
-
- /**
- * Converting a grad to a degree
- * @const
- */
- var GRAD_DEG = 0.9;
-
- /**
- * Converting a grad to a radian
- * @const
- */
- var GRAD_RAD = Math.PI/200;
-
- /**
- * Functions for converting angles
- * @var Object
- */
- $.extend({
- angle: {
- /**
- * available units for an angle
- * @var Regex
- */
- runit: /(deg|g?rad)/,
-
- /**
- * Convert a radian into a degree
- * @param Number rad
- * @return Number
- */
- radianToDegree: function(rad) {
- return rad * RAD_DEG;
- },
-
- /**
- * Convert a radian into a degree
- * @param Number rad
- * @return Number
- */
- radianToGrad: function(rad) {
- return rad * RAD_GRAD;
- },
-
- /**
- * Convert a degree into a radian
- * @param Number deg
- * @return Number
- */
- degreeToRadian: function(deg) {
- return deg * DEG_RAD;
- },
-
- /**
- * Convert a degree into a radian
- * @param Number deg
- * @return Number
- */
- degreeToGrad: function(deg) {
- return deg * DEG_GRAD;
- },
-
- /**
- * Convert a grad into a degree
- * @param Number grad
- * @return Number
- */
- gradToDegree: function(grad) {
- return grad * GRAD_DEG;
- },
-
- /**
- * Convert a grad into a radian
- * @param Number grad
- * @return Number
- */
- gradToRadian: function(grad) {
- return grad * GRAD_RAD;
- }
- }
- });
-})(jQuery, this, this.document);
-
-///////////////////////////////////////////////////////
// Transform
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
@@ -124,6 +15,19 @@
*/
var rmatrix = /progid:DXImageTransform\.Microsoft\.Matrix\(.*?\)/;
+ // Steal some code from Modernizr
+ var m = document.createElement( 'modernizr' ),
+ m_style = m.style,
+ docElement = document.documentElement;
+
+ // Capture some basic properties
+ var vendorPrefix = getVendorPrefix(),
+ transformProperty = vendorPrefix !== null ? vendorPrefix + 'transform' : false,
+ transformOriginProperty = vendorPrefix !== null ? vendorPrefix + 'transform-origin' : false;
+
+ // store support in the jQuery Support object
+ $.support.csstransforms = supportCssTransforms();
+
/**
* Class for creating cross-browser transformations
* @constructor
@@ -138,14 +42,7 @@
* @var jQueryCollection
*/
this.$elem = $(elem);
-
- /**
- * Remember the transform property so we don't have to keep
- * looking it up
- * @var string
- */
- this.transformProperty = this.getTransformProperty();
-
+
/**
* Remember the matrix we're applying to help the safeOuterLength func
*/
@@ -170,6 +67,8 @@
this.boxSizingProperty = null;
this.attr = null;
+ this.transformProperty = transformProperty;
+ this.transformOriginProperty = transformOriginProperty;
}
});
@@ -225,9 +124,6 @@
* preserve - tries to preserve the values from previous runs
*/
exec: function(funcs, options) {
- // determine if the CSS property is known
- var property = this.transformProperty;
-
// extend options
options = $.extend(true, {
forceMatrix: false,
@@ -247,10 +143,10 @@
this.setAttrs(funcs);
// apply the funcs
- if (property && !options.forceMatrix) {
+ if ($.support.csstransforms && !options.forceMatrix) {
// CSS3 is supported
return this.execFuncs(funcs);
- } else if ($.browser.msie || (property && options.forceMatrix)) {
+ } else if ($.browser.msie || ($.support.csstransforms && options.forceMatrix)) {
// Internet Explorer or Forced matrix
return this.execMatrix(funcs);
}
@@ -259,11 +155,10 @@
/**
* Applies all of the transformations as functions
- * var Object funcs
+ * @param Object funcs
*/
execFuncs: function(funcs) {
var values = [];
- var property = this.transformProperty;
// construct a CSS string
for (var func in funcs) {
@@ -274,105 +169,68 @@
values.push(this.createTransformFunc(func, funcs[func]));
}
}
- this.$elem.css(property, values.join(' '));
+ this.$elem.css(transformProperty, values.join(' '));
return true;
},
/**
* Applies all of the transformations as a matrix
- * var Object options
+ * @param Object funcs
*/
execMatrix: function(funcs) {
var matrix,
- property = this.transformProperty,
+ tempMatrix,
args;
- // collect all the matrices
- var strip = function(i, arg) {
- args[i] = parseFloat(arg);
- };
-
for (var func in funcs) {
- if ($.matrix[func] || func == 'matrix') {
+ if ($.matrix[func]) {
args = $.isArray(funcs[func]) ? funcs[func] : [funcs[func]];
// strip the units
// TODO: should probably convert the units properly instead of just stripping them
- $.each(args, strip);
-
- var m;
- if (func == 'matrix') {
- m = new $.matrix.M2x2(args[0], args[1], args[2], args[3]);
- if (args[4]) {
- this.setAttr('translateX', args[4]);
- }
- if (args[5]) {
- this.setAttr('translateY', args[5]);
- }
- } else {
- m = $.matrix[func].apply(this, args);
- }
+ args = $.map(args, stripUnits);
- if (!matrix) {
- matrix = m;
- } else {
- matrix = matrix.x(m);
- }
+ // TODO: translation and origin should be applied last
+ // TODO: should hold translations until the extreme end
+ tempMatrix = $.matrix[func].apply(this, args);
+ matrix = matrix ? matrix.x(tempMatrix) : tempMatrix;
} else if (func == 'origin') {
+ //TODO: this is a dumb way to handle the origin for a matrix
args = $.isArray(funcs[func]) ? funcs[func] : [funcs[func]];
this[func].apply(this, args);
}
- }
-
- // calculate translation
- // NOTE: Translate is additive
- var translate = this.getAttr('translate') || 0,
- translateX = this.getAttr('translateX') || 0,
- translateY = this.getAttr('translateY') || 0;
-
- if (!$.isArray(translate)) {
- translate = [translate, 0];
}
// check that we have a matrix
- if (!matrix) {
- // TODO: This will result in a filter being needlessly set in IE
- matrix = new $.matrix.M2x2(1, 0, 0, 1);
- }
-
+ // TODO: This will result in a filter being needlessly set in IE
+ matrix = matrix || $.matrix.identity();
+
// pull out the relevant values
- var a = parseFloat(parseFloat(matrix.e(1,1)).toFixed(8)),
- b = parseFloat(parseFloat(matrix.e(2,1)).toFixed(8)),
- c = parseFloat(parseFloat(matrix.e(1,2)).toFixed(8)),
- d = parseFloat(parseFloat(matrix.e(2,2)).toFixed(8)),
- tx = 0,
- ty = 0;
-
-
- // only run the translate matrix if we need to
- if (translate[0] || translate[1] || translateX || translateY) {
- var tvector = matrix.x(new $.matrix.V2(
- parseFloat(translate[0]) + parseFloat(translateX),
- parseFloat(translate[1]) + parseFloat(translateY)
- ));
- tx = parseFloat(parseFloat(tvector.e(1)).toFixed(8));
- ty = parseFloat(parseFloat(tvector.e(2)).toFixed(8));
- }
+ var a = parseFloat(matrix.e(1,1).toFixed(6)),
+ b = parseFloat(matrix.e(2,1).toFixed(6)),
+ c = parseFloat(matrix.e(1,2).toFixed(6)),
+ d = parseFloat(matrix.e(2,2).toFixed(6)),
+ tx = matrix.rows === 3 ? parseFloat(matrix.e(1,3).toFixed(6)) : 0,
+ ty = matrix.rows === 3 ? parseFloat(matrix.e(2,3).toFixed(6)) : 0;
//apply the transform to the element
- if (property && property.substr(0, 4) == '-moz') { // -moz
- this.$elem.css(property, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + 'px, ' + ty + 'px)');
- } else if (property) { // -webkit, -o, w3c
+ if ($.support.csstransforms && vendorPrefix === '-moz-') {
+ // -moz-
+ this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + 'px, ' + ty + 'px)');
+ } else if ($.support.csstransforms) {
+ // -webkit, -o-, w3c
// NOTE: WebKit and Opera don't allow units on the translate variables
- this.$elem.css(property, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + ', ' + ty + ')');
- } else if (jQuery.browser.msie) { // IE
+ this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + ', ' + ty + ')');
+ } else if ($.browser.msie) {
// IE requires the special transform Filter
- var filterType = ', filterType=\'nearest neighbor\''; //bilinear
+
+ //TODO: Use Nearest Neighbor during animation FilterType=\'nearest neighbor\'
+ var filterType = ', FilterType=\'nearest neighbor\''; //bilinear
var style = this.$elem[0].style;
var matrixFilter = 'progid:DXImageTransform.Microsoft.Matrix(' +
'M11=' + a + ', M12=' + c + ', M21=' + b + ', M22=' + d +
', sizingMethod=\'auto expand\'' + filterType + ')';
- var filter = style.filter || jQuery.curCSS( this.$elem[0], "filter" ) || "";var filter = style.filter || jQuery.curCSS( this.$elem[0], "filter" ) || "";
+ var filter = style.filter || jQuery.curCSS( this.$elem[0], "filter" ) || "";
style.filter = rmatrix.test(filter) ? filter.replace(rmatrix, matrixFilter) : filter ? filter + ' ' + matrixFilter : matrixFilter;
// Let's know that we're applying post matrix fixes and the height/width will be static for a bit
@@ -390,89 +248,59 @@
/**
* Sets the transform-origin
+ * This really needs to be percentages
* @param Number x length
* @param Number y length
*/
origin: function(x, y) {
- var property = this.transformProperty,
- height = this.safeOuterHeight(),
- width = this.safeOuterWidth();
-
- // correct for word lengths
+ // use CSS in supported browsers
+ if ($.support.csstransforms) {
+ if (typeof y === 'undefined') {
+ this.$elem.css(transformOriginProperty, x);
+ } else {
+ this.$elem.css(transformOriginProperty, x + ' ' + y);
+ }
+ return true;
+ }
+
+ // correct for keyword lengths
switch (x) {
case 'left': x = '0'; break;
- case 'right': x = width; break;
- case 'center': x = width * 0.5; break;
+ case 'right': x = '100%'; break;
+ case 'center': // no break
+ case undefined: x = '50%';
}
switch (y) {
case 'top': y = '0'; break;
- case 'bottom': y = height; break;
+ case 'bottom': y = '100%'; break;
case 'center': // no break
- case undefined: y = height * 0.5; break;
- }
-
- // assume all length units are px
- //TODO: handle unit conversion better
- x = /%/.test(x) ? width * parseFloat(x) /100 : parseFloat(x);
- if (typeof(y) !== 'undefined') {
- y = /%/.test(y) ? height * parseFloat(y) /100 : parseFloat(y);
- }
-
- // Apply the property
- if (property) { //Moz, WebKit, Opera
- if (!y && y !== 0) {
- this.$elem.css(property + '-origin', x + 'px');
- } else {
- this.$elem.css(property + '-origin', x + 'px ' + y + 'px');
- }
+ case undefined: y = '50%'; //TODO: does this work?
}
- // remember the transform origin
- // TODO: setting in px isn't an entirely accurate way to do this
- if (!y && y !== 0) {
- this.setAttr('origin', x);
- } else {
+ // store percentages directly
+ if (/%/.test(x) && /%/.test(y)) {
this.setAttr('origin', [x, y]);
+ return true;
}
+
+ // store mixed values with units, assumed pixels
+ this.setAttr('origin', [
+ /%/.test(x) ? x : parseFloat(x) + 'px',
+ /%/.test(y) ? y : parseFloat(y) + 'px'
+ ]);
return true;
},
/**
- * Try to determine which browser we're in by the existence of a
- * custom transform property
- * @param void
- * @return String
- */
- getTransformProperty: function() {
- if (this.transformProperty) {
- return this.transformProperty;
- }
- var elem = document.body;
- var property = {
- transform : 'transform',
- MozTransform : '-moz-transform',
- WebkitTransform : '-webkit-transform',
- OTransform : '-o-transform'
- };
- for (var p in property) {
- if (typeof elem.style[p] != 'undefined') {
- this.transformProperty = property[p];
- return property[p];
- }
- }
- // Default to transform also
- return null;
- },
-
- /**
* Create a function suitable for a CSS value
* @param string func
* @param Mixed value
*/
createTransformFunc: function(func, value) {
if ($.transform.rfunc.reflect.test(func)) {
// let's fake reflection
- var matrix = value ? $.matrix[func]() : $.matrix.empty(),
+ // TODO: why would value be false?
+ var matrix = value ? $.matrix[func]() : $.matrix.identity(),
a = matrix.e(1,1),
b = matrix.e(2,1),
c = matrix.e(1,2),
@@ -485,6 +313,12 @@
if (!$.isArray(value)) {
return func + '(' + value + ')';
} else if (func == 'matrix') {
+ if (vendorPrefix === '-moz-' && value[4]) {
+ value[4] = value[4] +'px';
+ }
+ if (vendorPrefix === '-moz-' && value[5]) {
+ value[5] = value[5] +'px';
+ }
return 'matrix(' + value[0] + ', ' + value[1] + ', ' + value[2] + ', ' + value[3] + ', ' + (value[4] || 0) + ', ' + (value[5] || 0) + ')';
} else {
return func + '(' + value[0] + ', ' + value[1] + ')';
@@ -501,16 +335,18 @@
fixPosition: function(matrix, tx, ty, height, width) {
// now we need to fix it!
var calc = new $.matrix.calc(matrix, this.safeOuterHeight(), this.safeOuterWidth()),
- origin = this.getAttr('origin');
+ origin = this.getAttr('origin'); // mixed percentages and px
// translate a 0, 0 origin to the current origin
- var offset = calc.originOffset({
- x: parseFloat(origin[0]),
- y: parseFloat(origin[1])
- });
+ var offset = calc.originOffset(new $.matrix.V2(
+ /%/.test(origin[0]) ? parseFloat(origin[0])/100*calc.outerWidth : parseFloat(origin[0]),
+ /%/.test(origin[1]) ? parseFloat(origin[1])/100*calc.outerHeight : parseFloat(origin[1])
+ ));
// IE glues the top-most and left-most pixels of the transformed object to top/left of the original object
- var sides = calc.sides();
+ //TODO: This seems wrong in the calculations
+ var sides = calc.sides(),
+ size = calc.offset();
// Protect against an item that is already positioned
var cssPosition = this.$elem.css('position');
@@ -534,6 +370,42 @@
}
};
+ function stripUnits(arg) {
+ return parseFloat(arg);
+ }
+
+ /**
+ * Find the prefix that this browser uses
+ */
+ function getVendorPrefix() {
+ var property = {
+ transformProperty : '',
+ MozTransform : '-moz-',
+ WebkitTransform : '-webkit-',
+ OTransform : '-o-',
+ msTransform : '-ms-'
+ };
+ for (var p in property) {
+ if (typeof m_style[p] != 'undefined') {
+ return property[p];
+ }
+ }
+ return null;
+ }
+
+ function supportCssTransforms() {
+ if (typeof(window.Modernizr) !== 'undefined') {
+ return Modernizr.csstransforms;
+ }
+
+ var props = [ 'transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform' ];
+ for ( var i in props ) {
+ if ( m_style[ props[i] ] !== undefined ) {
+ return true;
+ }
+ }
+ }
+
/**
* Ensure that values have the appropriate units on them
* @param string func
@@ -614,7 +486,7 @@
if (this.applyingMatrix && !this[funcName] && this.matrix) {
// calculate and return the correct size
var calc = new $.matrix.calc(this.matrix, 1, 1),
- ratio = calc.size(),
+ ratio = calc.offset(),
length = this.$elem[funcName]() / ratio[dim];
this[funcName] = length;
@@ -632,7 +504,7 @@
// setup some variables
var elem = this.$elem[0],
- outerLen = parseFloat($.curCSS(elem, dim, true)),
+ outerLen = parseFloat($.curCSS(elem, dim, true)), //TODO: this can be cached on animations that do not animate height/width
boxSizingProp = this.boxSizingProperty,
boxSizingValue = this.boxSizingValue;
@@ -696,871 +568,3 @@
}
})(jQuery, this, this.document);
-
-///////////////////////////////////////////////////////
-// Attr
-///////////////////////////////////////////////////////
-(function($, window, document, undefined) {
- var rfuncvalue = /(origin|matrix|reflect(X|XY|Y)?|rotate|scale[XY]?|skew[XY]?|translate[XY]?)\((.*?)\)/g, // with values
- rfuncname = /^origin|matrix|reflect(XY|[XY])?|rotate|scale[XY]?|skew[XY]?|translate[XY]?$/, // just funcname
- attr = 'data-transform',
- rspace = /\s/,
- rcspace = /,\s/;
-
- $.extend($.transform.prototype, {
- /**
- * This overrides all of the attributes
- * @param Object funcs a list of transform functions to store on this element
- * @return void
- */
- setAttrs: function(funcs) {
- var string = '',
- value;
- for (var func in funcs) {
- if (rfuncname.test(func)) {
- value = funcs[func];
- if ($.isArray(value)) {
- value = value.join(', ');
- }
- string += ' ' + func + '(' + value + ')';
- }
- }
- this.attr = $.trim(string);
- this.$elem.attr(attr, this.attr);
- },
-
- /**
- * This sets only a specific atribute
- * @param string func name of a transform function
- * @param mixed value with proper units
- * @return void
- */
- setAttr: function(func, value) {
- // not a valid function
- if (!rfuncname.test(func)) {
- return;
- }
-
- // stringify the value
- if ($.isArray(value)) {
- value = value.join(', ');
- }
- value = $.trim(value);
-
- var transform = this.attr || this.$elem.attr(attr);
- if (!transform) {
- // We don't have any existing values, save it
- this.attr = func + '(' + value + ')';
- this.$elem.attr(attr, this.attr);
- } else if (transform.indexOf(func) > -1) {
- // we don't have this function yet, save it
- this.attr = transform + ' ' + func + '(' + value + ')';
- this.$elem.attr(attr, this.attr);
- }
-
- // replace the existing value
- var values = [],
- result, parts;
-
- rfuncvalue.lastIndex = 0; //reset the regex pointer
- while ((result = rfuncvalue.exec(transform)) !== null) {
- values.push(result[0]);
- }
-
- for (var i = 0, l = values.length; i < l; i++) {
- rfuncvalue.lastIndex = 0; //reset the regex pointer
- parts = rfuncvalue.exec(values[i]);
-
- if (func == parts[1]) {
- values[i] = func + '(' + value + ')';
- break;
- }
- }
- this.attr = values.join(' ');
- this.$elem.attr(attr, this.attr);
- },
-
- /**
- * @return Object values with proper units
- */
- getAttrs: function() {
- var transform = this.attr || this.$elem.attr(attr);
- if (!transform) {
- // We don't have any existing values, return empty object
- return {};
- }
-
- // replace the existing value
- var values = [],
- attrs = {},
- result, parts, value;
-
- rfuncvalue.lastIndex = 0; //reset the regex pointer
- while ((result = rfuncvalue.exec(transform)) !== null) {
- values.push(result[0]);
- }
-
- for (var i = 0, l = values.length; i < l; i++) {
- rfuncvalue.lastIndex = 0; //reset the regex pointer
- parts = rfuncvalue.exec(values[i]);
-
- if (parts && rfuncname.test(parts[1])) {
- value = parts[3].split(rcspace);
- attrs[parts[1]] = value.length == 1 ? value[0] : value;
- }
- }
- return attrs;
- },
-
- /**
- * @param String func
- * @param Bool split splits space separated values into an array
- * @return value with proper units
- */
- getAttr: function(func) {
- // not a valid function
- if (!rfuncname.test(func)) {
- return null;
- }
-
- var transform = this.attr || this.$elem.attr(attr);
- var rscalefunc = $.transform.rfunc.scale;
- if (func != 'origin' && func != 'matrix' && (!transform || transform.indexOf(func) === -1)) {
- // We don't have any existing values, return null
- return rscalefunc.test(func) ? 1 : null;
- }
-
- // return the existing value
- var values = [],
- result, parts, value = null;
- rfuncvalue.lastIndex = 0; //reset the regex pointer
- while ((result = rfuncvalue.exec(transform)) !== null) {
- values.push(result[0]);
- }
-
- for (var i = 0, l = values.length; i < l; i++) {
- rfuncvalue.lastIndex = 0; //reset the regex pointer
- parts = rfuncvalue.exec(values[i]);
-
- if (func == parts[1]) {
- value = parts[3].split(rcspace);
- return value.length == 1 ? value[0] : value;
- }
- }
-
- // maybe look it up?
- //NOTE: Moz and WebKit always return the value of transform
- // as a matrix and obscures the individual functions. So
- // it's impossible to know what was set in the CSS.
- if (func == 'origin') {
- var rperc = /%/;
-
- // we can look up the origin in CSS
- value = this.transformProperty ?
- this.$elem.css(this.transformProperty + '-origin') :
- [this.safeOuterWidth() * 0.5, this.safeOuterHeight() * 0.5]; // for IE
- value = $.isArray(value) ? value : value.split(rspace);
-
- //Moz reports the value in % if there hasn't been a transformation yet
- if (rperc.test(value[0])) {
- if (rperc.test(value[0])) {
- value[0] = this.safeOuterWidth() * (parseFloat(value[0])/100);
- }
- if (rperc.test(value[1])) {
- value[1] = this.safeOuterHeight() * (parseFloat(value[1])/100);
- }
- }
- } else if (func == 'matrix') {
- value = [1, 0, 0, 1, 0, 0];
- } else if (rscalefunc.test(func)) {
- // force scale to be 1
- value = 1;
- }
-
- return $.isArray(value) && value.length == 1 ? value[0] : value;
- }
- });
-})(jQuery, this, this.document);
-
-
-///////////////////////////////////////////////////////
-// Matrix
-///////////////////////////////////////////////////////
-(function($, window, document, undefined) {
- /**
- * Matrix object for creating matrices relevant for 2d Transformations
- * @var Object
- */
- $.extend({
- matrix: {}
- });
-
- $.extend( $.matrix, {
- /**
- * Class for calculating coordinates on a matrix
- * @param Matrix matrix
- * @param Number outerHeight
- * @param Number outerWidth
- * @constructor
- */
- calc: function(matrix, outerHeight, outerWidth) {
- /**
- * @var Matrix
- */
- this.matrix = matrix;
-
- /**
- * @var Number
- */
- this.outerHeight = outerHeight;
-
- /**
- * @var Number
- */
- this.outerWidth = outerWidth;
- },
-
- /**
- * A 2-value vector
- * @param Number x
- * @param Number y
- * @constructor
- */
- V2: function(x, y){
- this.elements = [x, y];
- },
-
- /**
- * A 2x2 Matrix, useful for 2D-transformations without translations
- * @param Number mn
- * @constructor
- */
- M2x2: function(m11, m12, m21, m22) {
- this.elements = [m11, m12, m21, m22];
- },
-
- /**
- * Empty matrix
- * @return Matrix
- */
- empty: function() {
- return new $.matrix.M2x2(
- 1, 0,
- 0, 1
- );
- },
-
- /**
- * Reflect (same as rotate(180))
- * @return Matrix
- */
- reflect: function() {
- return new $.matrix.M2x2(
- -1, 0,
- 0, -1
- );
- },
-
- /**
- * Reflect across the x-axis (mirrored upside down)
- * @return Matrix
- */
- reflectX: function() {
- return new $.matrix.M2x2(
- 1, 0,
- 0, -1
- );
- },
-
- /**
- * Reflect by swapping x an y (same as reflectX + rotate(-90))
- * @return Matrix
- */
- reflectXY: function() {
- return new $.matrix.M2x2(
- 0, 1,
- 1, 0
- );
- },
-
- /**
- * Reflect across the y-axis (mirrored)
- * @return Matrix
- */
- reflectY: function() {
- return new $.matrix.M2x2(
- -1, 0,
- 0, 1
- );
- },
-
- /**
- * Rotates around the origin
- * @param Number deg
- * @return Matrix
- */
- rotate: function(deg) {
- //TODO: detect units
- var rad = $.angle.degreeToRadian(deg),
- costheta = Math.cos(rad),
- sintheta = Math.sin(rad);
-
- var a = costheta,
- b = sintheta,
- c = -sintheta,
- d = costheta;
-
- return new $.matrix.M2x2(
- a, c,
- b, d
- );
-
- },
-
- /**
- * Scale
- * @param Number sx
- * @param Number sy
- * @return Matrix
- */
- scale: function (sx, sy) {
- sx = sx || sx === 0 ? sx : 1;
- sy = sy || sy === 0 ? sy : sx;
-
- return new $.matrix.M2x2(
- sx, 0,
- 0, sy
- );
- },
-
- /**
- * Scale on the X-axis
- * @param Number sx
- * @return Matrix
- */
- scaleX: function (sx) {
- return $.matrix.scale(sx, 1);
- },
-
- /**
- * Scale on the Y-axis
- * @param Number sy
- * @return Matrix
- */
- scaleY: function (sy) {
- return $.matrix.scale(1, sy);
- },
-
- /**
- * Skews on the X-axis and Y-axis
- * @param Number degX
- * @param Number degY
- * @return Matrix
- */
- skew: function (degX, degY) {
- degX = degX || 0;
- degY = degY || 0;
-
- //TODO: detect units
- var radX = $.angle.degreeToRadian(degX),
- radY = $.angle.degreeToRadian(degY),
- x = Math.tan(radX),
- y = Math.tan(radY);
-
- return new $.matrix.M2x2(
- 1, x,
- y, 1
- );
- },
-
- /**
- * Skews on the X-axis
- * @param Number degX
- * @return Matrix
- */
- skewX: function (degX) {
- return $.matrix.skew(degX);
- },
-
- /**
- * Skews on the Y-axis
- * @param Number degY
- * @return Matrix
- */
- skewY: function (degY) {
- return $.matrix.skew(0, degY);
- }
- });
-
- $.matrix.calc.prototype = {
- /**
- * Calculate a coord on the new object
- * @return Object
- */
- coord: function(x, y) {
- var matrix = this.matrix,
- vector = matrix.x(new $.matrix.V2(x, y));
-
- return {
- x: vector.e(1),
- y: vector.e(2)
- };
- },
-
- /**
- * Calculate the corners of the new object
- * @return Object
- */
- corners: function() {
- var y = this.outerHeight,
- x = this.outerWidth;
-
- return {
- tl: this.coord(0, 0),
- bl: this.coord(0, y),
- tr: this.coord(x, 0),
- br: this.coord(x, y)
- };
- },
-
- /**
- * Calculate the dimensions of the new object
- * @return Object
- */
- sides: function() {
- // The corners of the box
- var corners = this.corners();
-
- // create empty dimensions
- var sides = {
- top: 0,
- bottom: 0,
- left: 0,
- right: 0
- }, x, y;
-
- // Find the extreme corners
- for (var pos in corners) {
- // Transform the coords
- x = corners[pos].x;
- y = corners[pos].y;
-
- // Record the extreme corners
- if (y < sides.top) {
- sides.top = y;
- }
- if (y > sides.bottom) {
- sides.bottom = y;
- }
- if (x < sides.left) {
- sides.left = x;
- }
- if (x > sides.right) {
- sides.right = x;
- }
- }
-
- return sides;
- },
-
- /**
- * Calculate the size of the new object
- * @return Object
- */
- size: function() {
- var sides = this.sides();
-
- // return size
- return {
- height: Math.abs(sides.bottom - sides.top),
- width: Math.abs(sides.right - sides.left)
- };
- },
-
- /**
- * Calculate a proper top and left for IE
- * @param Object toOrigin
- * @param Object fromOrigin
- * @return Object
- */
- originOffset: function(toOrigin, fromOrigin) {
- // the origin to translate to
- toOrigin = toOrigin ? toOrigin : {
- x: this.outerWidth * 0.5,
- y: this.outerHeight * 0.5
- };
-
- // the origin to translate from (IE has a fixed origin of 0, 0)
- fromOrigin = fromOrigin ? fromOrigin : {
- x: 0,
- y: 0
- };
-
- // transform the origins
- var toCenter = this.coord(toOrigin.x, toOrigin.y);
- var fromCenter = this.coord(fromOrigin.x, fromOrigin.y);
-
- // return the offset
- return {
- top: (fromCenter.y - fromOrigin.y) - (toCenter.y - toOrigin.y),
- left: (fromCenter.x - fromOrigin.x) - (toCenter.x - toOrigin.x)
- };
- }
- };
-
- $.matrix.M2x2.prototype = {
- /**
- * Multiply a 2x2 matrix by a similar matrix or a vector
- * @param M2x2 | V2 matrix
- * @return M2x2 | V2
- */
- x: function(matrix) {
- var a = this.elements,
- b = matrix.elements;
-
- if (b.length == 2) {
- // b is actually a vector
- return new $.matrix.V2(
- a[0] * b[0] + a[1] * b[1],
- a[2] * b[0] + a[3] * b[1]
- );
- } else if (b.length == 4) {
- // b is a 2x2 matrix
- return new $.matrix.M2x2(
- a[0] * b[0] + a[1] * b[2],
- a[0] * b[1] + a[1] * b[3],
-
- a[2] * b[0] + a[3] * b[2],
- a[2]