/*!
* jQuery 2d Transform v0.9.3
* http://wiki.github.com/heygrady/transform/
*
* Copyright 2010, Grady Kuhnline
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Date: Sat Dec 4 15:46:09 2010 -0800
*/
///////////////////////////////////////////////////////
// Transform
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
/**
* @var Regex identify the matrix filter in IE
*/
var rmatrix = /progid:DXImageTransform\.Microsoft\.Matrix\(.*?\)/,
rfxnum = /^([\+\-]=)?([\d+.\-]+)(.*)$/,
rperc = /%/;

// Steal some code from Modernizr
var m = document.createElement( 'modernizr' ),
m_style = m.style;

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;
}
}
}

// 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();

// IE9 public preview 6 requires the DOM names
if (vendorPrefix == '-ms-') {
transformProperty = 'msTransform';
transformOriginProperty = 'msTransformOrigin';
}

/**
* Class for creating cross-browser transformations
* @constructor
*/
$.extend({
transform: function(elem) {
// Cache the transform object on the element itself
elem.transform = this;

/**
* The element we're working with
* @var jQueryCollection
*/
this.$elem = $(elem);

/**
* Remember the matrix we're applying to help the safeOuterLength func
*/
this.applyingMatrix = false;
this.matrix = null;

/**
* Remember the css height and width to save time
* This is only really used in IE
* @var Number
*/
this.height = null;
this.width = null;
this.outerHeight = null;
this.outerWidth = null;

/**
* We need to know the box-sizing in IE for building the outerHeight and outerWidth
* @var string
*/
this.boxSizingValue = null;
this.boxSizingProperty = null;

this.attr = null;
this.transformProperty = transformProperty;
this.transformOriginProperty = transformOriginProperty;
}
});

$.extend($.transform, {
/**
* @var Array list of all valid transform functions
*/
funcs: ['matrix', 'origin', 'reflect', 'reflectX', 'reflectXY', 'reflectY', 'rotate', 'scale', 'scaleX', 'scaleY', 'skew', 'skewX', 'skewY', 'translate', 'translateX', 'translateY']
});

/**
* Create Transform as a jQuery plugin
* @param Object funcs
* @param Object options
*/
$.fn.transform = function(funcs, options) {
return this.each(function() {
var t = this.transform || new $.transform(this);
if (funcs) {
t.exec(funcs, options);
}
});
};

$.transform.prototype = {
/**
* Applies all of the transformations
* @param Object funcs
* @param Object options
* forceMatrix - uses the matrix in all browsers
* preserve - tries to preserve the values from previous runs
*/
exec: function(funcs, options) {
// extend options
options = $.extend(true, {
forceMatrix: false,
preserve: false
}, options);

// preserve the funcs from the previous run
this.attr = null;
if (options.preserve) {
funcs = $.extend(true, this.getAttrs(true, true), funcs);
} else {
funcs = $.extend(true, {}, funcs); // copy the object to prevent weirdness
}

// Record the custom attributes on the element itself
this.setAttrs(funcs);

// apply the funcs
if ($.support.csstransforms && !options.forceMatrix) {
// CSS3 is supported
return this.execFuncs(funcs);
} else if ($.browser.msie || ($.support.csstransforms && options.forceMatrix)) {
// Internet Explorer or Forced matrix
return this.execMatrix(funcs);
}
return false;
},

/**
* Applies all of the transformations as functions
* @param Object funcs
*/
execFuncs: function(funcs) {
var values = [];

// construct a CSS string
for (var func in funcs) {
// handle origin separately
if (func == 'origin') {
this[func].apply(this, $.isArray(funcs[func]) ? funcs[func] : [funcs[func]]);
} else if ($.inArray(func, $.transform.funcs) !== -1) {
values.push(this.createTransformFunc(func, funcs[func]));
}
}
this.$elem.css(transformProperty, values.join(' '));
return true;
},

/**
* Applies all of the transformations as a matrix
* @param Object funcs
*/
execMatrix: function(funcs) {
var matrix,
tempMatrix,
args;

var elem = this.$elem[0],
_this = this;
function normalPixels(val, i) {
if (rperc.test(val)) {
// this really only applies to translation
return parseFloat(val) / 100 * _this['safeOuter' + (i ? 'Height' : 'Width')]();
}
return toPx(elem, val);
}

var rtranslate = /translate[X|Y]?/,
trans = [];

for (var func in funcs) {
switch ($.type(funcs[func])) {
case 'array': args = funcs[func]; break;
case 'string': args = $.map(funcs[func].split(','), $.trim); break;
default: args = [funcs[func]];
}

if ($.matrix[func]) {

if ($.cssAngle[func]) {
// normalize on degrees
args = $.map(args, $.angle.toDegree);
} else if (!$.cssNumber[func]) {
// normalize to pixels
args = $.map(args, normalPixels);
} else {
// strip units
args = $.map(args, stripUnits);
}

tempMatrix = $.matrix[func].apply(this, args);
if (rtranslate.test(func)) {
//defer translation
trans.push(tempMatrix);
} else {
matrix = matrix ? matrix.x(tempMatrix) : tempMatrix;
}
} else if (func == 'origin') {
this[func].apply(this, args);
}
}

// check that we have a matrix
matrix = matrix || $.matrix.identity();

// Apply translation
$.each(trans, function(i, val) { matrix = matrix.x(val); });

// pull out the relevant values
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 ($.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(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + ', ' + ty + ')');
} else if ($.browser.msie) {
// IE requires the special transform Filter

//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 || $.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
this.applyingMatrix = true;
this.matrix = matrix;

// IE can't set the origin or translate directly
this.fixPosition(matrix, tx, ty);

this.applyingMatrix = false;
this.matrix = null;
}
return true;
},

/**
* Sets the transform-origin
* This really needs to be percentages
* @param Number x length
* @param Number y length
*/
origin: function(x, y) {
// 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 = '100%'; break;
case 'center': // no break
case undefined: x = '50%';
}
switch (y) {
case 'top': y = '0'; break;
case 'bottom': y = '100%'; break;
case 'center': // no break
case undefined: y = '50%'; //TODO: does this work?
}

// store mixed values with units, assumed pixels
this.setAttr('origin', [
rperc.test(x) ? x : toPx(this.$elem[0], x) + 'px',
rperc.test(y) ? y : toPx(this.$elem[0], y) + 'px'
]);
//console.log(this.getAttr('origin'));
return true;
},

/**
* Create a function suitable for a CSS value
* @param string func
* @param Mixed value
*/
createTransformFunc: function(func, value) {
if (func.substr(0, 7) === 'reflect') {
// let's fake reflection, false value
// falsey sets an identity matrix
var m = value ? $.matrix[func]() : $.matrix.identity();
return 'matrix(' + m.e(1,1) + ', ' + m.e(2,1) + ', ' + m.e(1,2) + ', ' + m.e(2,2) + ', 0, 0)';
}

//value = _correctUnits(func, value);

if (func == 'matrix') {
if (vendorPrefix === '-moz-') {
value[4] = value[4] ? value[4] + 'px' : 0;
value[5] = value[5] ? value[5] + 'px' : 0;
}
}
return func + '(' + ($.isArray(value) ? value.join(', ') : value) + ')';
},

/**
* @param Matrix matrix
* @param Number tx
* @param Number ty
* @param Number height
* @param Number width
*/
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'); // mixed percentages and px

// translate a 0, 0 origin to the current origin
var offset = calc.originOffset(new $.matrix.V2(
rperc.test(origin[0]) ? parseFloat(origin[0])/100*calc.outerWidth : parseFloat(origin[0]),
rperc.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
//TODO: This seems wrong in the calculations
var sides = calc.sides();

// Protect against an item that is already positioned
var cssPosition = this.$elem.css('position');
if (cssPosition == 'static') {
cssPosition = 'relative';
}

//TODO: if the element is already positioned, we should attempt to respect it (somehow)
//NOTE: we could preserve our offset top and left in an attr on the elem
var pos = {top: 0, left: 0};

// Approximates transform-origin, tx, and ty
var css = {
'position': cssPosition,
'top': (offset.top + ty + sides.top + pos.top) + 'px',
'left': (offset.left + tx + sides.left + pos.left) + 'px',
'zoom': 1
};

this.$elem.css(css);
}
};

/**
* Ensure that values have the appropriate units on them
* @param string func
* @param Mixed value
*/
function toPx(elem, val) {
var parts = rfxnum.exec($.trim(val));

if (parts[3] && parts[3] !== 'px') {
var prop = 'paddingBottom',
orig = $.style( elem, prop );

$.style( elem, prop, val );
val = cur( elem, prop );
$.style( elem, prop, orig );
return val;
}
return parseFloat( val );
}

function cur(elem, prop) {
if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
return elem[ prop ];
}

var r = parseFloat( $.css( elem, prop ) );
return r && r > -10000 ? r : 0;
}
})(jQuery, this, this.document);


///////////////////////////////////////////////////////
// Safe Outer Length
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
$.extend($.transform.prototype, {
/**
* @param void
* @return Number
*/
safeOuterHeight: function() {
return this.safeOuterLength('height');
},

/**
* @param void
* @return Number
*/
safeOuterWidth: function() {
return this.safeOuterLength('width');
},

/**
* Returns reliable outer dimensions for an object that may have been transformed.
* Only use this if the matrix isn't handy
* @param String dim height or width
* @return Number
*/
safeOuterLength: function(dim) {
var funcName = 'outer' + (dim == 'width' ? 'Width' : 'Height');

if (!$.support.csstransforms && $.browser.msie) {
// make the variables more generic
dim = dim == 'width' ? 'width' : 'height';

// if we're transforming and have a matrix; we can shortcut.
// the true outerHeight is the transformed outerHeight divided by the ratio.
// the ratio is equal to the height of a 1px by 1px box that has been transformed by the same matrix.
if (this.applyingMatrix && !this[funcName] && this.matrix) {
// calculate and return the correct size
var calc = new $.matrix.calc(this.matrix, 1, 1),
ratio = calc.offset(),
length = this.$elem[funcName]() / ratio[dim];
this[funcName] = length;

return length;
} else if (this.applyingMatrix && this[funcName]) {
// return the cached calculation
return this[funcName];
}

// map dimensions to box sides
var side = {
height: ['top', 'bottom'],
width: ['left', 'right']
};

// setup some variables
var elem = this.$elem[0],
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;

// IE6 && IE7 will never have a box-sizing property, so fake it
if (!this.boxSizingProperty) {
boxSizingProp = this.boxSizingProperty = _findBoxSizingProperty() || 'box-sizing';
boxSizingValue = this.boxSizingValue = this.$elem.css(boxSizingProp) || 'content-box';
}

// return it immediately if we already know it
if (this[funcName] && this[dim] == outerLen) {
return this[funcName];
} else {
this[dim] = outerLen;
}

// add in the padding and border
if (boxSizingProp && (boxSizingValue == 'padding-box' || boxSizingValue == 'content-box')) {
outerLen += parseFloat($.curCSS(elem, 'padding-' + side[dim][0], true)) || 0 +
parseFloat($.curCSS(elem, 'padding-' + side[dim][1], true)) || 0;
}
if (boxSizingProp && boxSizingValue == 'content-box') {
outerLen += parseFloat($.curCSS(elem, 'border-' + side[dim][0] + '-width', true)) || 0 +
parseFloat($.curCSS(elem, 'border-' + side[dim][1] + '-width', true)) || 0;
}

// remember and return the outerHeight
this[funcName] = outerLen;
return outerLen;
}
return this.$elem[funcName]();
}
});

/**
* Determine the correct property for checking the box-sizing property
* @param void
* @return string
*/
var _boxSizingProperty = null;
function _findBoxSizingProperty () {
if (_boxSizingProperty) {
return _boxSizingProperty;
}

var property = {
boxSizing : 'box-sizing',
MozBoxSizing : '-moz-box-sizing',
WebkitBoxSizing : '-webkit-box-sizing',
OBoxSizing : '-o-box-sizing'
},
elem = document.body;

for (var p in property) {
if (typeof elem.style[p] != 'undefined') {
_boxSizingProperty = property[p];
return _boxSizingProperty;
}
}
return null;
}
})(jQuery, this, this.document);


///////////////////////////////////////////////////////
// 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(', ');
}

// 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 (parts = rfuncvalue.exec(transform)) {
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 = {}, 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];
}

//TODO: move the origin to a function
if (func === 'origin' && $.support.csstransforms) {
// supported browsers return percentages always
return this.$elem.css(this.transformOriginProperty).split(rspace);
} else if (func === 'origin') {
// just force IE to also return a percentage
return ['50%', '50%'];
}

return $.cssDefault[func] || 0;
}
});

// Define
if (typeof($.cssAngle) == 'undefined') {
$.cssAngle = {};
}
$.extend($.cssAngle, {
rotate: true,
skew: true,
skewX: true,
skewY: true
});

// Define default values
if (typeof($.cssDefault) == 'undefined') {
$.cssDefault = {};
}

$.extend($.cssDefault, {
scale: [1, 1],
scaleX: 1,
scaleY: 1,
matrix: [1, 0, 0, 1, 0, 0],
origin: ['50%', '50%'], // TODO: allow this to be a function, like get
reflect: [1, 0, 0, 1, 0, 0],
reflectX: [1, 0, 0, 1, 0, 0],
reflectXY: [1, 0, 0, 1, 0, 0],
reflectY: [1, 0, 0, 1, 0, 0]
});

// Define functons with multiple values
if (typeof($.cssMultipleValues) == 'undefined') {
$.cssMultipleValues = {};
}
$.extend($.cssMultipleValues, {
matrix: 6,
origin: {
length: 2,
duplicate: true
},
reflect: 6,
reflectX: 6,
reflectXY: 6,
reflectY: 6,
scale: {
length: 2,
duplicate: true
},
skew: 2,
translate: 2
});

// specify unitless funcs
$.extend($.cssNumber, {
matrix: true,
reflect: true,
reflectX: true,
reflectXY: true,
reflectY: true,
scale: true,
scaleX: true,
scaleY: true
});

// override all of the css functions
$.each($.transform.funcs, function(i, func) {
$.cssHooks[func] = {
set: function(elem, value) {
var transform = elem.transform || new $.transform(elem),
funcs = {};
funcs[func] = value;
transform.exec(funcs, {preserve: true});
},
get: function(elem, computed) {
var transform = elem.transform || new $.transform(elem);
return transform.getAttr(func);
}
};
});

// Support Reflection animation better by returning a matrix
$.each(['reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
$.cssHooks[func].get = function(elem, computed) {
var transform = elem.transform || new $.transform(elem);
return transform.getAttr('matrix') || $.cssDefault[func];
};
});
})(jQuery, this, this.document);
///////////////////////////////////////////////////////
// Animation
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
/**
* @var Regex looks for units on a string
*/
var rfxnum = /^([+\-]=)?([\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 ) {
var optall = $.speed(speed, easing, callback),
mv = $.cssMultipleValues;

// Speed always creates a complete function that must be reset
optall.complete = optall.old;

// Capture multiple values
if (!$.isEmptyObject(prop)) {
if (typeof optall.original === 'undefined') {
optall.original = {};
}
$.each( prop, function( name, val ) {
if (mv[name]
|| $.cssAngle[name]
|| (!$.cssNumber[name] && $.inArray(name, $.transform.funcs) !== -1)) {

// Handle special easing
var specialEasing = null;
if (jQuery.isArray(prop[name])) {
var mvlen = 1, len = val.length;
if (mv[name]) {
mvlen = (typeof mv[name].length === 'undefined' ? mv[name] : mv[name].length);
}
if ( len > mvlen
|| (len < mvlen && len == 2)
|| (len == 2 && mvlen == 2 && isNaN(parseFloat(val[len - 1])))) {

specialEasing = val[len - 1];
val.splice(len - 1, 1);
}
}

// Store the original values onto the optall
optall.original[name] = val.toString();

// reduce to a unitless number (to trick animate)
prop[name] = parseFloat(val);
}
} );
}

//NOTE: we edited prop above to trick animate
//NOTE: we pre-convert to an optall so we can doctor it
return _animate.apply(this, [arguments[0], optall]);
};

var prop = 'paddingBottom';
function cur(elem, prop) {
if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
//return elem[ prop ];
}

var r = parseFloat( $.css( elem, prop ) );
return r && r > -10000 ? r : 0;
}

var _custom = $.fx.prototype.custom;
$.fx.prototype.custom = function(from, to, unit) {
var multiple = $.cssMultipleValues[this.prop],
angle = $.cssAngle[this.prop];

//TODO: simply check for the existence of CSS Hooks?
if (multiple || (!$.cssNumber[this.prop] && $.inArray(this.prop, $.transform.funcs) !== -1)) {
this.values = [];

if (!multiple) {
multiple = 1;
}

// Pull out the known values
var values = this.options.original[this.prop],
currentValues = $(this.elem).css(this.prop),
defaultValues = $.cssDefault[this.prop] || 0;

// make sure the current css value is an array
if (!$.isArray(currentValues)) {
currentValues = [currentValues];
}

// make sure the new values are an array
if (!$.isArray(values)) {
if ($.type(values) === 'string') {
values = values.split(',');
} else {
values = [values];
}
}

// make sure we have enough new values
var length = multiple.length || multiple, i = 0;
while (values.length < length) {
values.push(multiple.duplicate ? values[0] : defaultValues[i] || 0);
i++;
}

// calculate a start, end and unit for each new value
var start, parts, end, //unit,
fx = this,
transform = fx.elem.transform;
orig = $.style(fx.elem, prop);

$.each(values, function(i, val) {
// find a sensible start value
if (currentValues[i]) {
start = currentValues[i];
} else if (defaultValues[i] && !multiple.duplicate) {
start = defaultValues[i];
} else if (multiple.duplicate) {
start = currentValues[0];
} else {
start = 0;
}

// Force the correct unit on the start
if (angle) {
start = $.angle.toDegree(start);
} else if (!$.cssNumber[fx.prop]) {
parts = rfxnum.exec($.trim(start));
if (parts[3] && parts[3] !== 'px') {
if (parts[3] === '%') {
start = parseFloat( parts[2] ) / 100 * transform['safeOuter' + (i ? 'Height' : 'Width')]();
} else {
$.style( fx.elem, prop, start);
start = cur(fx.elem, prop);
$.style( fx.elem, prop, orig);
}
}
}
start = parseFloat(start);

// parse the value with a regex
parts = rfxnum.exec($.trim(val));

if (parts) {
// we found a sensible value and unit
end = parseFloat( parts[2] );
unit = parts[3] || "px"; //TODO: change to an appropriate default unit

if (angle) {
end = $.angle.toDegree(end + unit);
unit = 'deg';
} else if (!$.cssNumber[fx.prop] && unit === '%') {
start = (start / transform['safeOuter' + (i ? 'Height' : 'Width')]()) * 100;
} else if (!$.cssNumber[fx.prop] && unit !== 'px') {
$.style( fx.elem, prop, (end || 1) + unit);
start = ((end || 1) / cur(fx.elem, prop)) * start;
$.style( fx.elem, prop, orig);
}

// If a +=/-= token was provided, we're doing a relative animation
if (parts[1]) {
end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
}
} else {
// I don't know when this would happen
end = val;
unit = '';
}

// Save the values
fx.values.push({
start: start,
end: end,
unit: unit
});
});
}
return _custom.apply(this, arguments);
};

/**
* Animates a multi value attribute
* @param Object fx
* @return null
*/
$.fx.multipleValueStep = {
_default: function(fx) {
$.each(fx.values, function(i, val) {
fx.values[i].now = val.start + ((val.end - val.start) * fx.pos);
});
}
};
$.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
$.fx.multipleValueStep[func] = function(fx) {
var d = fx.decomposed,
$m = $.matrix;
m = $m.identity();

d.now = {};

// increment each part of the decomposition and recompose it
$.each(d.start, function(k) {
// calculate the current value
d.now[k] = parseFloat(d.start[k]) + ((parseFloat(d.end[k]) - parseFloat(d.start[k])) * fx.pos);

// skip functions that won't affect the transform
if (((k === 'scaleX' || k === 'scaleY') && d.now[k] === 1) ||
(k !== 'scaleX' && k !== 'scaleY' && d.now[k] === 0)) {
return true;
}

// calculating
m = m.x($m[k](d.now[k]));
});

// save the correct matrix values for the value of now
var val;
$.each(fx.values, function(i) {
switch (i) {
case 0: val = parseFloat(m.e(1, 1).toFixed(6)); break;
case 1: val = parseFloat(m.e(2, 1).toFixed(6)); break;
case 2: val = parseFloat(m.e(1, 2).toFixed(6)); break;
case 3: val = parseFloat(m.e(2, 2).toFixed(6)); break;
case 4: val = parseFloat(m.e(1, 3).toFixed(6)); break;
case 5: val = parseFloat(m.e(2, 3).toFixed(6)); break;
}
fx.values[i].now = val;
});
};
});
/**
* Step for animating tranformations
*/
$.each($.transform.funcs, function(i, func) {
$.fx.step[func] = function(fx) {
var transform = fx.elem.transform || new $.transform(fx.elem),
funcs = {};

if ($.cssMultipleValues[func] || (!$.cssNumber[func] && $.inArray(func, $.transform.funcs) !== -1)) {
($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
funcs[fx.prop] = [];
$.each(fx.values, function(i, val) {
funcs[fx.prop].push(val.now + ($.cssNumber[fx.prop] ? '' : val.unit));
});
} else {
funcs[fx.prop] = fx.now + ($.cssNumber[fx.prop] ? '' : fx.unit);
}

transform.exec(funcs, {preserve: true});
};
});

// Support matrix animation
$.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
$.fx.step[func] = function(fx) {
var transform = fx.elem.transform || new $.transform(fx.elem),
funcs = {};

if (!fx.initialized) {
fx.initialized = true;

// Reflections need a sensible end value set
if (func !== 'matrix') {
var values = $.matrix[func]().elements;
var val;
$.each(fx.values, function(i) {
switch (i) {
case 0: val = values[0]; break;
case 1: val = values[2]; break;
case 2: val = values[1]; break;
case 3: val = values[3]; break;
default: val = 0;
}
fx.values[i].end = val;
});
}

// Decompose the start and end
fx.decomposed = {};
var v = fx.values;

fx.decomposed.start = $.matrix.matrix(v[0].start, v[1].start, v[2].start, v[3].start, v[4].start, v[5].start).decompose();
fx.decomposed.end = $.matrix.matrix(v[0].end, v[1].end, v[2].end, v[3].end, v[4].end, v[5].end).decompose();
}

($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
funcs.matrix = [];
$.each(fx.values, function(i, val) {
funcs.matrix.push(val.now);
});

transform.exec(funcs, {preserve: true});
};
});
})(jQuery, this, this.document);
///////////////////////////////////////////////////////
// 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;


var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;

/**
* 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;
},

/**
* Convert an angle with a unit to a degree
* @param String val angle with a unit
* @return Number
*/
toDegree: function (val) {
var parts = rfxnum.exec(val);
if (parts) {
val = parseFloat( parts[2] );
switch (parts[3] || 'deg') {
case 'grad':
val = $.angle.gradToDegree(val);
break;
case 'rad':
val = $.angle.radianToDegree(val);
break;
}
return val;
}
return 0;
}
}
});
})(jQuery, this, this.document);
///////////////////////////////////////////////////////
// Matrix
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
/**
* Matrix object for creating matrices relevant for 2d Transformations
* @var Object
*/
if (typeof($.matrix) == 'undefined') {
$.extend({
matrix: {}
});
}
var $m = $.matrix;

$.extend( $m, {
/**
* 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];
},

/**
* Taken from Zoomooz
* https://github.com/jaukia/zoomooz/blob/c7a37b9a65a06ba730bd66391bbd6fe8e55d3a49/js/jquery.zoomooz.js
*/
decompose: function() {
var a = this.e(1, 1),
b = this.e(2, 1),
c = this.e(1, 2),
d = this.e(2, 2),
e = this.e(1, 3),
f = this.e(2, 3);

// In case the matrix can't be decomposed
if (Math.abs(a * d - b * c) < 0.01) {
return {
rotate: 0 + 'deg',
skewX: 0 + 'deg',
scaleX: 1,
scaleY: 1,
translateX: 0 + 'px',
translateY: 0 + 'px'
};
}

// Translate is easy
var tx = e, ty = f;

// factor out the X scale
var sx = Math.sqrt(a * a + b * b);
a = a/sx;
b = b/sx;

// factor out the skew
var k = a * c + b * d;
c -= a * k;
d -= b * k;

// factor out the Y scale
var sy = Math.sqrt(c * c + d * d);
c = c / sy;
d = d / sy;
k = k / sy;

// account for negative scale
if ((a * d - b * c) < 0.0) {
a = -a;
b = -b;
//c = -c; // accomplishes nothing to negate it
//d = -d; // accomplishes nothing to negate it
sx = -sx;
//sy = -sy //Scale Y shouldn't ever be negated
}

// calculate the rotation angle and skew angle
var rad2deg = $.angle.radianToDegree;
var r = rad2deg(Math.atan2(b, a));
k = rad2deg(Math.atan(k));

return {
rotate: r + 'deg',
skewX: k + 'deg',
scaleX: sx,
scaleY: sy,
translateX: tx + 'px',
translateY: ty + 'px'
};
}
};

/** Extend all of the matrix types with the same prototype */
$.extend($m.M2x2.prototype, Matrix, {
toM3x3: function() {
var a = this.elements;
return new $m.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 $m.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 $m.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 $m.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($m.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 $m.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 $m.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 $m.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($m.V2.prototype, Vector);
$.extend($m.V3.prototype, Vector);
})(jQuery, this, this.document);
///////////////////////////////////////////////////////
// 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);
///////////////////////////////////////////////////////
// 2d Matrix Functions
///////////////////////////////////////////////////////
(function($, window, document, undefined) {
/**
* Matrix object for creating matrices relevant for 2d Transformations
* @var Object
*/
if (typeof($.matrix) == 'undefined') {
$.extend({
matrix: {}
});
}
var $m = $.matrix,
$m2x2 = $m.M2x2,
$m3x3 = $m.M3x3;

$.extend( $m, {
/**
* 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 $m['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 $m2x2(
args[0], args[2],
args[1], args[3]
);
case 6:
return new $m3x3(
args[0], args[2], args[4],
args[1], args[3], args[5],
0, 0, 1
);
}
},

/**
* Reflect (same as rotate(180))
* @return Matrix
*/
reflect: function() {
return new $m2x2(
-1, 0,
0, -1
);
},

/**
* Reflect across the x-axis (mirrored upside down)
* @return Matrix
*/
reflectX: function() {
return new $m2x2(
1, 0,
0, -1
);
},

/**
* Reflect by swapping x an y (same as reflectX + rotate(-90))
* @return Matrix
*/
reflectXY: function() {
return new $m2x2(
0, 1,
1, 0
);
},

/**
* Reflect across the y-axis (mirrored)
* @return Matrix
*/
reflectY: function() {
return new $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 $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 $m2x2(
sx, 0,
0, sy
);
},

/**
* Scale on the X-axis
* @param Number sx
* @return Matrix
*/
scaleX: function (sx) {
return $m.scale(sx, 1);
},

/**
* Scale on the Y-axis
* @param Number sy
* @return Matrix
*/
scaleY: function (sy) {
return $m.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 $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 $m.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 $m.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 $m3x3(
1, 0, tx,
0, 1, ty,
0, 0, 1
);
},

/**
* Translate on the X-axis
* @param Number tx
* @return Matrix
* @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
*/
translateX: function (tx) {
return $m.translate(tx);
},

/**
* Translate on the Y-axis
* @param Number ty
* @return Matrix
* @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
*/
translateY: function (ty) {
return $m.translate(0, ty);
}
});
})(jQuery, this, this.document);
