├── version.txt
├── test
├── i
│ ├── arrow-up.png
│ ├── elephant.png
│ ├── background.jpg
│ ├── example-all.png
│ ├── example-center.png
│ ├── example-default.png
│ ├── example-skewx-25.png
│ ├── example-skewy-25.png
│ ├── example-top-left.png
│ ├── example-rotate-45.png
│ ├── example-scalex-0.5.png
│ ├── example-scaley-0.5.png
│ ├── example-skew-25-25.png
│ ├── example-scale-0.5-0.5.png
│ ├── example-translatex-25.png
│ ├── example-translatey-25.png
│ ├── example-center-default.png
│ ├── example-translate-25-25.png
│ ├── example-rotate-45-skewx-25.png
│ ├── example-skewx-25-rotate-45.png
│ └── example-skewx-25-skewy-25.png
├── css
│ ├── transform.css
│ ├── style.css
│ └── global.css
├── js
│ ├── form.js
│ └── attr.js
├── _test.html
├── transform.html
├── test.html
└── test3.html
├── Rakefile
├── README.md
├── src
├── jquery.angle.js
├── jquery.matrix.calculations.js
├── jquery.matrix.functions.js
├── jquery.transform.attributes.js
├── jquery.matrix.js
├── jquery.transform.animate.js
└── jquery.transform.js
└── dist
├── jquery.transform-0.9.3.min.js
└── jquery.transform.js
/version.txt:
--------------------------------------------------------------------------------
1 | 0.9.3
--------------------------------------------------------------------------------
/test/i/arrow-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/arrow-up.png
--------------------------------------------------------------------------------
/test/i/elephant.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/elephant.png
--------------------------------------------------------------------------------
/test/i/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/background.jpg
--------------------------------------------------------------------------------
/test/i/example-all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-all.png
--------------------------------------------------------------------------------
/test/i/example-center.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-center.png
--------------------------------------------------------------------------------
/test/i/example-default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-default.png
--------------------------------------------------------------------------------
/test/i/example-skewx-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-skewx-25.png
--------------------------------------------------------------------------------
/test/i/example-skewy-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-skewy-25.png
--------------------------------------------------------------------------------
/test/i/example-top-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-top-left.png
--------------------------------------------------------------------------------
/test/i/example-rotate-45.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-rotate-45.png
--------------------------------------------------------------------------------
/test/i/example-scalex-0.5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-scalex-0.5.png
--------------------------------------------------------------------------------
/test/i/example-scaley-0.5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-scaley-0.5.png
--------------------------------------------------------------------------------
/test/i/example-skew-25-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-skew-25-25.png
--------------------------------------------------------------------------------
/test/i/example-scale-0.5-0.5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-scale-0.5-0.5.png
--------------------------------------------------------------------------------
/test/i/example-translatex-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-translatex-25.png
--------------------------------------------------------------------------------
/test/i/example-translatey-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-translatey-25.png
--------------------------------------------------------------------------------
/test/i/example-center-default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-center-default.png
--------------------------------------------------------------------------------
/test/i/example-translate-25-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-translate-25-25.png
--------------------------------------------------------------------------------
/test/i/example-rotate-45-skewx-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-rotate-45-skewx-25.png
--------------------------------------------------------------------------------
/test/i/example-skewx-25-rotate-45.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-skewx-25-rotate-45.png
--------------------------------------------------------------------------------
/test/i/example-skewx-25-skewy-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/heygrady/transform/HEAD/test/i/example-skewx-25-skewy-25.png
--------------------------------------------------------------------------------
/test/css/transform.css:
--------------------------------------------------------------------------------
1 | form .line {
2 | margin-bottom: 10px;
3 | }
4 | .right-col {
5 | background-repeat: no-repeat;
6 | background-position: 23px 58px;
7 | }
8 | .holder {
9 | position: relative;
10 | overflow: visible;
11 | height: 130px;
12 | width: 130px;
13 | background-color: #cccccc;
14 | margin: 50px;
15 | }
16 |
17 | .transform {
18 | height: 100px;
19 | width: 100px;
20 | background-color: deeppink;
21 | border: 5px solid black;
22 | padding: 10px;
23 | }
24 |
--------------------------------------------------------------------------------
/test/js/form.js:
--------------------------------------------------------------------------------
1 | (function($, window, document, undefined) {
2 | // capture form clicks
3 | $('#transform').submit(function(e) {
4 | e.preventDefault();
5 | processForm();
6 | });
7 | $('#go').click(function(e) {
8 | e.preventDefault();
9 | processForm();
10 | var $el = $(this);
11 | $el.animate({rotate: '+=360deg'});
12 | });
13 |
14 | /**
15 | * cache the form fields
16 | * @var Object
17 | */
18 | var fields = {
19 | reflect: $('#reflect'),
20 | reflectX: $('#reflectX'),
21 | reflectY: $('#reflectY'),
22 | reflectXY: $('#reflectXY'),
23 | rotate: $('#rotate'),
24 | scale: {
25 | x: $('#scale-x'),
26 | y: $('#scale-y')
27 | },
28 | scaleX: $('#scaleX'),
29 | scaleY: $('#scaleY'),
30 | skew: {
31 | x: $('#skew-x'),
32 | y: $('#skew-y')
33 | },
34 | skewX: $('#skewX'),
35 | skewY: $('#skewY'),
36 | translate: {
37 | x: $('#translate-x'),
38 | y: $('#translate-y')
39 | },
40 | translateX: $('#translateX'),
41 | translateY: $('#translateY'),
42 | origin: {
43 | x: $('#origin-x'),
44 | y: $('#origin-y')
45 | }
46 | };
47 |
48 | /**
49 | * Process the form
50 | */
51 | function processForm() {
52 | var funcs = {};
53 |
54 | for (var key in fields) {
55 | var field = fields[key];
56 | var val = null;
57 | if (field.x) {
58 | var x = field.x.val();
59 | var y = field.y.val();
60 | if (x && y) {
61 | val = [x, y];
62 | } else if (x) {
63 | val = x;
64 | }
65 | } else if ($.transform.rfunc.reflect.test(key)) {
66 | val = field.is(':checked');
67 | } else {
68 | var x = field.val();
69 | if (x) {
70 | val = x;
71 | }
72 | }
73 | if (val) {
74 | funcs[key] = val;
75 | }
76 | }
77 | if ($('#animate').is(':checked')) {
78 | $.each(funcs, function(func, val) {
79 | if ($.isArray(val)) {
80 | funcs[func] = val.join(' ');
81 | }
82 | });
83 | $('#target').transform({}).animate(funcs);
84 | $('#target2').transform({origin: [0, 0]}).animate(funcs);
85 | } else {
86 | $('#target').transform(funcs);
87 | funcs['origin'] = [0, 0];
88 | $('#target2').transform(funcs, {forceMatrix: true});
89 | }
90 | }
91 | })(jQuery, this, this.document);
--------------------------------------------------------------------------------
/test/_test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Untitled Document
6 |
24 |
25 |
26 |
27 |
28 |
29 |
Transform This
Matrix
30 |
31 |
32 |
Transform This
Straight
33 |
34 |
35 |
Transform This
Decomposed
36 |
37 |
38 |
39 |
Transform This
Matrix
40 |
41 |
42 |
Transform This
Straight
43 |
44 |
45 |
Transform This
Decomposed
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
79 |
80 |
--------------------------------------------------------------------------------
/test/css/style.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | margin: 0;
3 | padding: 0;
4 | height: 100%;
5 | }
6 |
7 | h2 {
8 | font-size: 28px;
9 | margin-bottom: 20px;
10 | }
11 |
12 | #container {
13 | width: 960px;
14 | margin: 10px auto;
15 | }
16 |
17 | #body {
18 | padding: 10px;
19 | background-image: url(../i/background.jpg);
20 | border: 4px solid #000000;
21 | }
22 |
23 | .box {
24 | float: left;
25 | display: inline;
26 | margin-bottom: 10px;
27 | margin-right: 10px;
28 | }
29 | .outer {
30 | position: relative;
31 | width: 150px;
32 |
33 | background-color: #000000;
34 | background-color: rgba(0, 0, 0, 0.5); /* FF3+, Saf3+, Opera 10.10+, Chrome */
35 | }
36 | .lt-ie8 .outer {
37 | background-color: transparent;
38 | filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#7F000000',EndColorStr='#7F000000'); /* IE6,IE7 */
39 | }
40 | .ie8 .outer {
41 | background-color: transparent;
42 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#7F000000',EndColorStr='#7F000000')"; /* IE8 */
43 | }
44 | #footer {
45 | padding-top: 25px;
46 | }
47 | .animate,
48 | .transition,
49 | .transform {
50 | height: 150px;
51 | width: 150px;
52 | }
53 |
54 | .plain .animate,
55 | .plain .transition,
56 | .plain .transform,
57 | .plain .transform2 {
58 | border: 5px solid #000000;
59 | background-color: deeppink;
60 | padding: 5px;
61 |
62 | height: 150px;
63 | width: 150px;
64 |
65 | box-sizing: border-box;
66 | -moz-box-sizing: border-box;
67 | -webkit-box-sizing: border-box;
68 |
69 | -moz-transform: translate(13px, 13px) translateX(13px);
70 | }
71 | .plain .transform2 {
72 | position: absolute;
73 | height: 180px;
74 | width: 180px;
75 | top: -15px;
76 | left: -15px;
77 | }
78 | .transition {
79 | -moz-transition-property: -moz-transform;
80 | -moz-transition-duration: .4s;
81 | -moz-transition-timing-function: ease-in-out;
82 |
83 | -webkit-transition-property: -webkit-transform;
84 | -webkit-transition-duration: .4s;
85 | -webkit-transition-timing-function: ease-in-out;
86 |
87 | -o-transition-property: -o-transform;
88 | -o-transition-duration: .4s;
89 | -o-transition-timing-function: ease-in-out;
90 | }
91 | #body:after,
92 | .clearfix:after {
93 | content: ".";
94 | display: block;
95 | height: 0;
96 | clear: both;
97 | visibility: hidden;
98 | }
99 | .ie #body,
100 | .ie .clearfix {
101 | zoom: 1; /* triggers hasLayout */
102 | display: block; /* resets display for IE/Win */
103 | }
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | prefix = File.dirname( __FILE__ )
2 |
3 | # Directory variables
4 | src_dir = File.join( prefix, 'src' )
5 | build_dir = File.join( prefix, 'build' )
6 | test_dir = File.join( prefix, 'test' )
7 |
8 | # A different destination directory can be set by
9 | # setting DIST_DIR before calling rake
10 | dist_dir = ENV['DIST_DIR'] || File.join( prefix, 'dist' )
11 |
12 | base_files = %w{transform transform.attributes transform.animate angle matrix matrix.calculations matrix.functions}.map { |js| File.join( src_dir, "jquery.#{js}.js" ) }
13 |
14 | # General Variables
15 | date = `git log -1`[/^Date:\s+(.+)$/, 1]
16 | version = File.read( File.join( prefix, 'version.txt' ) ).strip
17 |
18 | # jQuery files/dirs
19 | jq = File.join( dist_dir, "jquery.transform-#{version}.js" )
20 | jq_min = File.join( dist_dir, "jquery.transform-#{version}.min.js" )
21 | jq_test = File.join( dist_dir, "jquery.transform.js" )
22 |
23 |
24 | # Build tools
25 | rhino = "java -jar \"#{build_dir}/js.jar\""
26 | minfier = "java -jar \"#{build_dir}/yuicompressor-2.4.2.jar\""
27 |
28 | # Turn off output other than needed from `sh` and file commands
29 | verbose(false)
30 |
31 | # Tasks
32 | task :default => "all"
33 |
34 | desc "Builds jQuery; Tests with JSLint; Minifies jQuery"
35 | task :all => [:dist, :jquery, :lint, :min] do
36 | puts "jQuery build complete."
37 | end
38 |
39 | desc "Builds jQuery Transform: jquery.transform.js (Default task)"
40 | task :jquery => [jq, jq_test]
41 |
42 | desc "Builds a minified version of jQuery Transform: jquery.transform.min.js"
43 | task :min => jq_min
44 |
45 |
46 | task :init => [] do
47 | end
48 |
49 | desc "Removes dist folder"
50 | task :dist do
51 | puts "Removing Distribution directory: #{dist_dir}..."
52 | rm_rf dist_dir
53 | end
54 |
55 | desc "Tests built jquery.transform.js against JSLint"
56 | task :lint => jq do
57 | puts "Checking jQuery against JSLint..."
58 | sh "#{rhino} \"" + File.join(build_dir, 'jslint-check.js') + "\""
59 | end
60 |
61 |
62 | # File and Directory Dependencies
63 | directory dist_dir
64 |
65 | file jq => [dist_dir, base_files].flatten do
66 | puts "Building jquery.transform.js..."
67 |
68 | File.open(jq, 'w') do |f|
69 | f.write cat(base_files).gsub(/(Date:.)/, "\\1#{date}" ).gsub(/@VERSION/, version)
70 | end
71 | end
72 |
73 | file jq_min => jq do
74 | puts "Building jquery.transform.min.js..."
75 | sh "#{minfier} -o \"#{jq_min}\" \"#{jq}\""
76 | end
77 |
78 | file jq_test => jq do
79 | puts "Building a test version of Transform 3d..."
80 | cp jq, jq_test
81 | end
82 |
83 | def cat( files )
84 | files.map do |file|
85 | File.read(file)
86 | end.join('')
87 | end
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NO LONGER MAINTAINED
2 | As a note, I'm no longer maintaining this library. There was a much simpler library by Louis Remi. I forked it a while back and ported over the portions of my library that were missing from his. Louis' approach is much simpler and more performant.
3 |
4 | My fork is [here](https://github.com/heygrady/jquery.transform.js)
5 |
6 | Louis Remi's is [here](https://github.com/louisremi/jquery.transform.js)
7 |
8 |
9 |
10 |
11 | # 2D Transformations
12 | This library uses native CSS3 transformations in supported browsers and relies on the matrix filter in Internet Explorer 8 and below.
13 |
14 | NOTE: In Internet Explorer 8 and below, the transform-origin and the translate functions are simulated using relative positioning. Because of this, in Internet Explorer 8 and below, the top and left position of an element will be incorrect after it has been transformed. The [solution](https://github.com/heygrady/transform/issues#issue/6) is to wrap the element that is to be transformed and position that wrapper instead.
15 |
16 | * Since 0.9.0, proper units are required
17 | * Since 0.9.0, jQuery 1.4.3 or above is required
18 |
19 | ## Supported Browsers
20 | * Native CSS3 Support
21 | * FireFox 3.5+
22 | * Safari 3.1+
23 | * Chrome
24 | * Opera 10.5+
25 | * Internet Explorer 9+
26 | * Matrix Filter Support
27 | * Internet Explorer 5.5 - 8
28 |
29 | ## Usage
30 | // Rotate 30 Degrees
31 | $('#example').transform({rotate: '30deg'});
32 |
33 | // Use CSS Hooks to Rotate
34 | $('#example').css({rotate: '30deg'});
35 |
36 | // Animate the rotation
37 | $('#example').animate({rotate: '30deg'});
38 |
39 | // Go Crazy
40 | $('#example').transform({
41 | matrix: [1, 0, 0, 1, 0, 0], //applies a matrix
42 | reflect: true, //same as rotate(180deg)
43 | reflectX: true, //mirrored upside down
44 | reflectXY: true, //same as reflectX + rotate(-90deg)
45 | reflectY: true, //mirrored
46 | rotate: '45deg', //rotates 45 degrees
47 | skew: ['10deg', '10deg'], //skews 10 degrees on the x and y axis
48 | skewX: '10deg', //skews 10 degrees on the x axis
49 | skewY: '10deg', //skews 10 degrees on the y axis
50 | scale: [1.5, 1.5], //scales by 1.5 on the x and y axis
51 | scaleX: 1.5, //scales by 1.5 on the x axis
52 | scaleY: 1.5, //scales by 1.5 on the y axis
53 | translate: ['20px', '20px'], //moves the transformation 20px on the x and y axis
54 | translateX: 20px', //moves the transformation 20px on the x axis
55 | translateY: '20px', //moves the transformation 20px on the y axis
56 | origin: ['20%', '20%'] //changes the transformation origin
57 | });
58 |
59 | // Properties can be strings or arrays
60 | $('#example').css({skew: ['10deg', '10deg']});
61 | $('#example').css({skew: '10deg, 10deg'});
62 |
63 | // For animation, arrays should be nested because of jQuery's per-property easing support
64 | $('#example').animate({skew: ['10deg', '10deg']}); // technically this defines nonsense easing of 10deg
65 | $('#example').animate({skew: [['10deg', '10deg']]}); // this is a friendlier way of supporting this
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/jquery.angle.js:
--------------------------------------------------------------------------------
1 |
2 | ///////////////////////////////////////////////////////
3 | // Angle
4 | ///////////////////////////////////////////////////////
5 | (function($, window, document, undefined) {
6 | /**
7 | * Converting a radian to a degree
8 | * @const
9 | */
10 | var RAD_DEG = 180/Math.PI;
11 |
12 | /**
13 | * Converting a radian to a grad
14 | * @const
15 | */
16 | var RAD_GRAD = 200/Math.PI;
17 |
18 | /**
19 | * Converting a degree to a radian
20 | * @const
21 | */
22 | var DEG_RAD = Math.PI/180;
23 |
24 | /**
25 | * Converting a degree to a grad
26 | * @const
27 | */
28 | var DEG_GRAD = 2/1.8;
29 |
30 | /**
31 | * Converting a grad to a degree
32 | * @const
33 | */
34 | var GRAD_DEG = 0.9;
35 |
36 | /**
37 | * Converting a grad to a radian
38 | * @const
39 | */
40 | var GRAD_RAD = Math.PI/200;
41 |
42 |
43 | var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
44 |
45 | /**
46 | * Functions for converting angles
47 | * @var Object
48 | */
49 | $.extend({
50 | angle: {
51 | /**
52 | * available units for an angle
53 | * @var Regex
54 | */
55 | runit: /(deg|g?rad)/,
56 |
57 | /**
58 | * Convert a radian into a degree
59 | * @param Number rad
60 | * @return Number
61 | */
62 | radianToDegree: function(rad) {
63 | return rad * RAD_DEG;
64 | },
65 |
66 | /**
67 | * Convert a radian into a degree
68 | * @param Number rad
69 | * @return Number
70 | */
71 | radianToGrad: function(rad) {
72 | return rad * RAD_GRAD;
73 | },
74 |
75 | /**
76 | * Convert a degree into a radian
77 | * @param Number deg
78 | * @return Number
79 | */
80 | degreeToRadian: function(deg) {
81 | return deg * DEG_RAD;
82 | },
83 |
84 | /**
85 | * Convert a degree into a radian
86 | * @param Number deg
87 | * @return Number
88 | */
89 | degreeToGrad: function(deg) {
90 | return deg * DEG_GRAD;
91 | },
92 |
93 | /**
94 | * Convert a grad into a degree
95 | * @param Number grad
96 | * @return Number
97 | */
98 | gradToDegree: function(grad) {
99 | return grad * GRAD_DEG;
100 | },
101 |
102 | /**
103 | * Convert a grad into a radian
104 | * @param Number grad
105 | * @return Number
106 | */
107 | gradToRadian: function(grad) {
108 | return grad * GRAD_RAD;
109 | },
110 |
111 | /**
112 | * Convert an angle with a unit to a degree
113 | * @param String val angle with a unit
114 | * @return Number
115 | */
116 | toDegree: function (val) {
117 | var parts = rfxnum.exec(val);
118 | if (parts) {
119 | val = parseFloat( parts[2] );
120 | switch (parts[3] || 'deg') {
121 | case 'grad':
122 | val = $.angle.gradToDegree(val);
123 | break;
124 | case 'rad':
125 | val = $.angle.radianToDegree(val);
126 | break;
127 | }
128 | return val;
129 | }
130 | return 0;
131 | }
132 | }
133 | });
134 | })(jQuery, this, this.document);
--------------------------------------------------------------------------------
/src/jquery.matrix.calculations.js:
--------------------------------------------------------------------------------
1 |
2 | ///////////////////////////////////////////////////////
3 | // Matrix Calculations
4 | ///////////////////////////////////////////////////////
5 | (function($, window, document, undefined) {
6 | /**
7 | * Matrix object for creating matrices relevant for 2d Transformations
8 | * @var Object
9 | */
10 | if (typeof($.matrix) == 'undefined') {
11 | $.extend({
12 | matrix: {}
13 | });
14 | }
15 |
16 | $.extend( $.matrix, {
17 | /**
18 | * Class for calculating coordinates on a matrix
19 | * @param Matrix matrix
20 | * @param Number outerHeight
21 | * @param Number outerWidth
22 | * @constructor
23 | */
24 | calc: function(matrix, outerHeight, outerWidth) {
25 | /**
26 | * @var Matrix
27 | */
28 | this.matrix = matrix;
29 |
30 | /**
31 | * @var Number
32 | */
33 | this.outerHeight = outerHeight;
34 |
35 | /**
36 | * @var Number
37 | */
38 | this.outerWidth = outerWidth;
39 | }
40 | });
41 |
42 | $.matrix.calc.prototype = {
43 | /**
44 | * Calculate a coord on the new object
45 | * @return Object
46 | */
47 | coord: function(x, y, z) {
48 | //default z and w
49 | z = typeof(z) !== 'undefined' ? z : 0;
50 |
51 | var matrix = this.matrix,
52 | vector;
53 |
54 | switch (matrix.rows) {
55 | case 2:
56 | vector = matrix.x(new $.matrix.V2(x, y));
57 | break;
58 | case 3:
59 | vector = matrix.x(new $.matrix.V3(x, y, z));
60 | break;
61 | }
62 |
63 | return vector;
64 | },
65 |
66 | /**
67 | * Calculate the corners of the new object
68 | * @return Object
69 | */
70 | corners: function(x, y) {
71 | // Try to save the corners if this is called a lot
72 | var save = !(typeof(x) !=='undefined' || typeof(y) !=='undefined'),
73 | c;
74 | if (!this.c || !save) {
75 | y = y || this.outerHeight;
76 | x = x || this.outerWidth;
77 |
78 | c = {
79 | tl: this.coord(0, 0),
80 | bl: this.coord(0, y),
81 | tr: this.coord(x, 0),
82 | br: this.coord(x, y)
83 | };
84 | } else {
85 | c = this.c;
86 | }
87 |
88 | if (save) {
89 | this.c = c;
90 | }
91 | return c;
92 | },
93 |
94 | /**
95 | * Calculate the sides of the new object
96 | * @return Object
97 | */
98 | sides: function(corners) {
99 | // The corners of the box
100 | var c = corners || this.corners();
101 |
102 | return {
103 | top: Math.min(c.tl.e(2), c.tr.e(2), c.br.e(2), c.bl.e(2)),
104 | bottom: Math.max(c.tl.e(2), c.tr.e(2), c.br.e(2), c.bl.e(2)),
105 | left: Math.min(c.tl.e(1), c.tr.e(1), c.br.e(1), c.bl.e(1)),
106 | right: Math.max(c.tl.e(1), c.tr.e(1), c.br.e(1), c.bl.e(1))
107 | };
108 | },
109 |
110 | /**
111 | * Calculate the offset of the new object
112 | * @return Object
113 | */
114 | offset: function(corners) {
115 | // The corners of the box
116 | var s = this.sides(corners);
117 |
118 | // return size
119 | return {
120 | height: Math.abs(s.bottom - s.top),
121 | width: Math.abs(s.right - s.left)
122 | };
123 | },
124 |
125 | /**
126 | * Calculate the area of the new object
127 | * @return Number
128 | * @link http://en.wikipedia.org/wiki/Quadrilateral#Area_of_a_convex_quadrilateral
129 | */
130 | area: function(corners) {
131 | // The corners of the box
132 | var c = corners || this.corners();
133 |
134 | // calculate the two diagonal vectors
135 | var v1 = {
136 | x: c.tr.e(1) - c.tl.e(1) + c.br.e(1) - c.bl.e(1),
137 | y: c.tr.e(2) - c.tl.e(2) + c.br.e(2) - c.bl.e(2)
138 | },
139 | v2 = {
140 | x: c.bl.e(1) - c.tl.e(1) + c.br.e(1) - c.tr.e(1),
141 | y: c.bl.e(2) - c.tl.e(2) + c.br.e(2) - c.tr.e(2)
142 | };
143 |
144 | return 0.25 * Math.abs(v1.e(1) * v2.e(2) - v1.e(2) * v2.e(1));
145 | },
146 |
147 | /**
148 | * Calculate the non-affinity of the new object
149 | * @return Number
150 | */
151 | nonAffinity: function() {
152 | // The corners of the box
153 | var sides = this.sides(),
154 | xDiff = sides.top - sides.bottom,
155 | yDiff = sides.left - sides.right;
156 |
157 | return parseFloat(parseFloat(Math.abs(
158 | (Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) /
159 | (sides.top * sides.bottom + sides.left * sides.right)
160 | )).toFixed(8));
161 | },
162 |
163 | /**
164 | * Calculate a proper top and left for IE
165 | * @param Object toOrigin
166 | * @param Object fromOrigin
167 | * @return Object
168 | */
169 | originOffset: function(toOrigin, fromOrigin) {
170 | // the origin to translate to
171 | toOrigin = toOrigin ? toOrigin : new $.matrix.V2(
172 | this.outerWidth * 0.5,
173 | this.outerHeight * 0.5
174 | );
175 |
176 | // the origin to translate from (IE has a fixed origin of 0, 0)
177 | fromOrigin = fromOrigin ? fromOrigin : new $.matrix.V2(
178 | 0,
179 | 0
180 | );
181 |
182 | // transform the origins
183 | var toCenter = this.coord(toOrigin.e(1), toOrigin.e(2));
184 | var fromCenter = this.coord(fromOrigin.e(1), fromOrigin.e(2));
185 |
186 | // return the offset
187 | return {
188 | top: (fromCenter.e(2) - fromOrigin.e(2)) - (toCenter.e(2) - toOrigin.e(2)),
189 | left: (fromCenter.e(1) - fromOrigin.e(1)) - (toCenter.e(1) - toOrigin.e(1))
190 | };
191 | }
192 | };
193 | })(jQuery, this, this.document);
--------------------------------------------------------------------------------
/src/jquery.matrix.functions.js:
--------------------------------------------------------------------------------
1 |
2 | ///////////////////////////////////////////////////////
3 | // 2d Matrix Functions
4 | ///////////////////////////////////////////////////////
5 | (function($, window, document, undefined) {
6 | /**
7 | * Matrix object for creating matrices relevant for 2d Transformations
8 | * @var Object
9 | */
10 | if (typeof($.matrix) == 'undefined') {
11 | $.extend({
12 | matrix: {}
13 | });
14 | }
15 | var $m = $.matrix,
16 | $m2x2 = $m.M2x2,
17 | $m3x3 = $m.M3x3;
18 |
19 | $.extend( $m, {
20 | /**
21 | * Identity matrix
22 | * @param Number size
23 | * @return Matrix
24 | */
25 | identity: function(size) {
26 | size = size || 2;
27 | var length = size * size,
28 | elements = new Array(length),
29 | mod = size + 1;
30 | for (var i = 0; i < length; i++) {
31 | elements[i] = (i % mod) === 0 ? 1 : 0;
32 | }
33 | return new $m['M'+size+'x'+size](elements);
34 | },
35 |
36 | /**
37 | * Matrix
38 | * @return Matrix
39 | */
40 | matrix: function() {
41 | var args = Array.prototype.slice.call(arguments);
42 | // arguments are in column-major order
43 | switch (arguments.length) {
44 | case 4:
45 | return new $m2x2(
46 | args[0], args[2],
47 | args[1], args[3]
48 | );
49 | case 6:
50 | return new $m3x3(
51 | args[0], args[2], args[4],
52 | args[1], args[3], args[5],
53 | 0, 0, 1
54 | );
55 | }
56 | },
57 |
58 | /**
59 | * Reflect (same as rotate(180))
60 | * @return Matrix
61 | */
62 | reflect: function() {
63 | return new $m2x2(
64 | -1, 0,
65 | 0, -1
66 | );
67 | },
68 |
69 | /**
70 | * Reflect across the x-axis (mirrored upside down)
71 | * @return Matrix
72 | */
73 | reflectX: function() {
74 | return new $m2x2(
75 | 1, 0,
76 | 0, -1
77 | );
78 | },
79 |
80 | /**
81 | * Reflect by swapping x an y (same as reflectX + rotate(-90))
82 | * @return Matrix
83 | */
84 | reflectXY: function() {
85 | return new $m2x2(
86 | 0, 1,
87 | 1, 0
88 | );
89 | },
90 |
91 | /**
92 | * Reflect across the y-axis (mirrored)
93 | * @return Matrix
94 | */
95 | reflectY: function() {
96 | return new $m2x2(
97 | -1, 0,
98 | 0, 1
99 | );
100 | },
101 |
102 | /**
103 | * Rotates around the origin
104 | * @param Number deg
105 | * @return Matrix
106 | * @link http://www.w3.org/TR/SVG/coords.html#RotationDefined
107 | */
108 | rotate: function(deg) {
109 | //TODO: detect units
110 | var rad = $.angle.degreeToRadian(deg),
111 | costheta = Math.cos(rad),
112 | sintheta = Math.sin(rad);
113 |
114 | var a = costheta,
115 | b = sintheta,
116 | c = -sintheta,
117 | d = costheta;
118 |
119 | return new $m2x2(
120 | a, c,
121 | b, d
122 | );
123 | },
124 |
125 | /**
126 | * Scale
127 | * @param Number sx
128 | * @param Number sy
129 | * @return Matrix
130 | * @link http://www.w3.org/TR/SVG/coords.html#ScalingDefined
131 | */
132 | scale: function (sx, sy) {
133 | sx = sx || sx === 0 ? sx : 1;
134 | sy = sy || sy === 0 ? sy : sx;
135 |
136 | return new $m2x2(
137 | sx, 0,
138 | 0, sy
139 | );
140 | },
141 |
142 | /**
143 | * Scale on the X-axis
144 | * @param Number sx
145 | * @return Matrix
146 | */
147 | scaleX: function (sx) {
148 | return $m.scale(sx, 1);
149 | },
150 |
151 | /**
152 | * Scale on the Y-axis
153 | * @param Number sy
154 | * @return Matrix
155 | */
156 | scaleY: function (sy) {
157 | return $m.scale(1, sy);
158 | },
159 |
160 | /**
161 | * Skews on the X-axis and Y-axis
162 | * @param Number degX
163 | * @param Number degY
164 | * @return Matrix
165 | */
166 | skew: function (degX, degY) {
167 | degX = degX || 0;
168 | degY = degY || 0;
169 |
170 | //TODO: detect units
171 | var radX = $.angle.degreeToRadian(degX),
172 | radY = $.angle.degreeToRadian(degY),
173 | x = Math.tan(radX),
174 | y = Math.tan(radY);
175 |
176 | return new $m2x2(
177 | 1, x,
178 | y, 1
179 | );
180 | },
181 |
182 | /**
183 | * Skews on the X-axis
184 | * @param Number degX
185 | * @return Matrix
186 | * @link http://www.w3.org/TR/SVG/coords.html#SkewXDefined
187 | */
188 | skewX: function (degX) {
189 | return $m.skew(degX);
190 | },
191 |
192 | /**
193 | * Skews on the Y-axis
194 | * @param Number degY
195 | * @return Matrix
196 | * @link http://www.w3.org/TR/SVG/coords.html#SkewYDefined
197 | */
198 | skewY: function (degY) {
199 | return $m.skew(0, degY);
200 | },
201 |
202 | /**
203 | * Translate
204 | * @param Number tx
205 | * @param Number ty
206 | * @return Matrix
207 | * @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
208 | */
209 | translate: function (tx, ty) {
210 | tx = tx || 0;
211 | ty = ty || 0;
212 |
213 | return new $m3x3(
214 | 1, 0, tx,
215 | 0, 1, ty,
216 | 0, 0, 1
217 | );
218 | },
219 |
220 | /**
221 | * Translate on the X-axis
222 | * @param Number tx
223 | * @return Matrix
224 | * @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
225 | */
226 | translateX: function (tx) {
227 | return $m.translate(tx);
228 | },
229 |
230 | /**
231 | * Translate on the Y-axis
232 | * @param Number ty
233 | * @return Matrix
234 | * @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
235 | */
236 | translateY: function (ty) {
237 | return $m.translate(0, ty);
238 | }
239 | });
240 | })(jQuery, this, this.document);
--------------------------------------------------------------------------------
/src/jquery.transform.attributes.js:
--------------------------------------------------------------------------------
1 |
2 | ///////////////////////////////////////////////////////
3 | // Attr
4 | ///////////////////////////////////////////////////////
5 | (function($, window, document, undefined) {
6 | var rfuncvalue = /([\w\-]*?)\((.*?)\)/g, // with values
7 | attr = 'data-transform',
8 | rspace = /\s/,
9 | rcspace = /,\s?/;
10 |
11 | $.extend($.transform.prototype, {
12 | /**
13 | * This overrides all of the attributes
14 | * @param Object funcs a list of transform functions to store on this element
15 | * @return void
16 | */
17 | setAttrs: function(funcs) {
18 | var string = '',
19 | value;
20 | for (var func in funcs) {
21 | value = funcs[func];
22 | if ($.isArray(value)) {
23 | value = value.join(', ');
24 | }
25 | string += ' ' + func + '(' + value + ')';
26 | }
27 | this.attr = $.trim(string);
28 | this.$elem.attr(attr, this.attr);
29 | },
30 |
31 | /**
32 | * This sets only a specific atribute
33 | * @param string func name of a transform function
34 | * @param mixed value with proper units
35 | * @return void
36 | */
37 | setAttr: function(func, value) {
38 | // stringify the value
39 | if ($.isArray(value)) {
40 | value = value.join(', ');
41 | }
42 |
43 | // pull from a local variable to look it up
44 | var transform = this.attr || this.$elem.attr(attr);
45 | if (!transform || transform.indexOf(func) == -1) {
46 | // we don't have any existing values, save it
47 | // we don't have this function yet, save it
48 | this.attr = $.trim(transform + ' ' + func + '(' + value + ')');
49 | this.$elem.attr(attr, this.attr);
50 | } else {
51 | // replace the existing value
52 | var funcs = [], parts;
53 |
54 | // regex split
55 | rfuncvalue.lastIndex = 0; // reset the regex pointer
56 | while (parts = rfuncvalue.exec(transform)) {
57 | if (func == parts[1]) {
58 | funcs.push(func + '(' + value + ')');
59 | } else {
60 | funcs.push(parts[0]);
61 | }
62 | }
63 | this.attr = funcs.join(' ');
64 | this.$elem.attr(attr, this.attr);
65 | }
66 | },
67 |
68 | /**
69 | * @return Object
70 | */
71 | getAttrs: function() {
72 | var transform = this.attr || this.$elem.attr(attr);
73 | if (!transform) {
74 | // We don't have any existing values, return empty object
75 | return {};
76 | }
77 |
78 | // replace the existing value
79 | var attrs = {}, parts, value;
80 |
81 | rfuncvalue.lastIndex = 0; // reset the regex pointer
82 | while ((parts = rfuncvalue.exec(transform)) !== null) {
83 | if (parts) {
84 | value = parts[2].split(rcspace);
85 | attrs[parts[1]] = value.length == 1 ? value[0] : value;
86 | }
87 | }
88 | return attrs;
89 | },
90 |
91 | /**
92 | * @param String func
93 | * @return mixed
94 | */
95 | getAttr: function(func) {
96 | var attrs = this.getAttrs();
97 | if (typeof attrs[func] !== 'undefined') {
98 | return attrs[func];
99 | }
100 |
101 | //TODO: move the origin to a function
102 | if (func === 'origin' && $.support.csstransforms) {
103 | // supported browsers return percentages always
104 | return this.$elem.css(this.transformOriginProperty).split(rspace);
105 | } else if (func === 'origin') {
106 | // just force IE to also return a percentage
107 | return ['50%', '50%'];
108 | }
109 |
110 | return $.cssDefault[func] || 0;
111 | }
112 | });
113 |
114 | // Define
115 | if (typeof($.cssAngle) == 'undefined') {
116 | $.cssAngle = {};
117 | }
118 | $.extend($.cssAngle, {
119 | rotate: true,
120 | skew: true,
121 | skewX: true,
122 | skewY: true
123 | });
124 |
125 | // Define default values
126 | if (typeof($.cssDefault) == 'undefined') {
127 | $.cssDefault = {};
128 | }
129 |
130 | $.extend($.cssDefault, {
131 | scale: [1, 1],
132 | scaleX: 1,
133 | scaleY: 1,
134 | matrix: [1, 0, 0, 1, 0, 0],
135 | origin: ['50%', '50%'], // TODO: allow this to be a function, like get
136 | reflect: [1, 0, 0, 1, 0, 0],
137 | reflectX: [1, 0, 0, 1, 0, 0],
138 | reflectXY: [1, 0, 0, 1, 0, 0],
139 | reflectY: [1, 0, 0, 1, 0, 0]
140 | });
141 |
142 | // Define functons with multiple values
143 | if (typeof($.cssMultipleValues) == 'undefined') {
144 | $.cssMultipleValues = {};
145 | }
146 | $.extend($.cssMultipleValues, {
147 | matrix: 6,
148 | origin: {
149 | length: 2,
150 | duplicate: true
151 | },
152 | reflect: 6,
153 | reflectX: 6,
154 | reflectXY: 6,
155 | reflectY: 6,
156 | scale: {
157 | length: 2,
158 | duplicate: true
159 | },
160 | skew: 2,
161 | translate: 2
162 | });
163 |
164 | // specify unitless funcs
165 | $.extend($.cssNumber, {
166 | matrix: true,
167 | reflect: true,
168 | reflectX: true,
169 | reflectXY: true,
170 | reflectY: true,
171 | scale: true,
172 | scaleX: true,
173 | scaleY: true
174 | });
175 |
176 | // override all of the css functions
177 | $.each($.transform.funcs, function(i, func) {
178 | $.cssHooks[func] = {
179 | set: function(elem, value) {
180 | var transform = elem.transform || new $.transform(elem),
181 | funcs = {};
182 | funcs[func] = value;
183 | transform.exec(funcs, {preserve: true});
184 | },
185 | get: function(elem, computed) {
186 | var transform = elem.transform || new $.transform(elem);
187 | return transform.getAttr(func);
188 | }
189 | };
190 | });
191 |
192 | // Support Reflection animation better by returning a matrix
193 | $.each(['reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
194 | $.cssHooks[func].get = function(elem, computed) {
195 | var transform = elem.transform || new $.transform(elem);
196 | return transform.getAttr('matrix') || $.cssDefault[func];
197 | };
198 | });
199 | })(jQuery, this, this.document);
--------------------------------------------------------------------------------
/test/js/attr.js:
--------------------------------------------------------------------------------
1 | var rfunc = /(matrix|reflect(XY|X|Y)?|rotate|scale[X|Y]?|skew[X|Y]?|translate[X|Y]?)\((*?)\)/g,
2 | rspace = /\s+/,
3 | rcspace = /,\s+/;
4 |
5 | $.extend($.transform.prototype, {
6 | /**
7 | * @param String func with associated value rotate(13deg)
8 | */
9 | addTransform: function( func ) {
10 | // split the function into component parts
11 | var parts = rfunc.exec(func);
12 | if (parts) {
13 | var $elem = this.$elem,
14 | transform = $elem.attr('data-transform');
15 |
16 | if (!transform) {
17 | // it's empty, just save
18 | $elem.attr('data-transform', func);
19 | } else if (transform.indexOf(parts[1]) > -1) {
20 | // if we've already got it, replace it
21 | var result = null,
22 | string = '';
23 |
24 | // loop the funcs to replace
25 | while ((result = rfunc.exec(transform)) != null) {
26 | if (parts[1] == result[1]) {
27 | string += ' ' + func;
28 | } else {
29 | string += ' ' + result[0];
30 | }
31 | }
32 | $elem.attr('data-transform', jQuery.trim(string));
33 | } else {
34 | // this is a new one, just save
35 | $elem.attr('data-transform', transform + ' ' + func);
36 | }
37 | }
38 | },
39 |
40 | /**
41 | * @param String func
42 | */
43 | removeTransform: function( func ) {
44 | if (func) {
45 | var $elem = this.$elem,
46 | transform = $elem.attr('data-transform'),
47 | result = null,
48 | string = '';
49 |
50 | if (transform && transform.indexOf(func) > -1) {
51 | // loop the funcs to remove
52 | while ((result = rfunc.exec(transform)) != null) {
53 | if (func != result[1]) {
54 | string += ' ' + result[0];
55 | }
56 | }
57 |
58 | string = jQuery.trim(string);
59 | if (!string) {
60 | $elem.removeAttr('data-transform');
61 | } else {
62 | $elem.attr('data-transform', jQuery.trim(string));
63 | }
64 | }
65 | } else {
66 | this.$elem.removeAttr('data-transform');
67 | }
68 |
69 | },
70 |
71 | /**
72 | * @param String func
73 | * @return bool
74 | */
75 | hasTransform: function( func ) {
76 | var transform = this.$elem.attr('data-transform');
77 |
78 | if (!transform) {
79 | return false;
80 | } else if (func && transform.indexOf(func) > -1) {
81 | return true;
82 | }
83 | return false;
84 | },
85 |
86 | /**
87 | * @param String func
88 | * @param Array
89 | */
90 | getTransform: function( func ) {
91 | var transform = this.$elem.attr('data-transform');
92 |
93 | if (!transform) {
94 | // console.log('getTransform(' + func + ') =>', 'nothing');
95 | return null;
96 | }
97 |
98 | var result = null;
99 | if (func && transform.indexOf(func) > -1) {
100 | // return a specific value
101 | var value;
102 | while ((result = rfunc.exec(transform)) != null) {
103 | if (func == result[1]) {
104 | value = result[4].split(rcspace);
105 | //console.log(func, value);
106 | return value.length == 1 ? value[0] : value;
107 | }
108 | }
109 | } else if (!func) {
110 | // return all values
111 | var values = {}, value;
112 | while ((result = rfunc.exec(transform)) != null) {
113 | value = result[4].split(rcspace);
114 | values[result[2]] = value.length == 1 ? value[0] : value;
115 | }
116 | return values;
117 | }
118 | console.log(transform);
119 | return null;
120 | },
121 |
122 | /**
123 | * Clears out all of the custom attributed
124 | * @param void
125 | */
126 | clearAttrs: function() {
127 | this.$elem.attr('data-transform', '');
128 | },
129 |
130 | /**
131 | * @param Object funcs a list of transform functions to store on this element
132 | * @return void
133 | */
134 | setAttrs: function(funcs) {
135 | var string = '',
136 | value;
137 | for (var func in funcs) {
138 | value = funcs[func];
139 |
140 | // TODO: normalize values on px, deg, etc
141 | if ($.isArray(value)) {
142 | value = value.join(', ');
143 | }
144 |
145 | string += ' ' + func + '(' + value + ')';
146 | }
147 | this.$elem.attr('data-transform', jQuery.trim(string));
148 | },
149 |
150 | /**
151 | * @param string func name of a transform function
152 | * @param mixed value with proper units
153 | * @return void
154 | */
155 | setAttr: function(func, value) {
156 | // TODO: normalize values on px, deg, etc
157 | if ($.isArray(value)) {
158 | value = value.join(', ');
159 | }
160 |
161 | this.addTransform(func + '(' + value + ')'); //should be unitless
162 | },
163 |
164 | /**
165 | * @return Object values with proper units
166 | */
167 | getAttrs: function() {
168 | return this.getTransform();
169 | },
170 |
171 | /**
172 | * @param String func
173 | * @return Array of values
174 | */
175 | getAttr: function(func) {
176 | //NOTE: Moz and WebKit always return the value of transform
177 | // as a matrix and obscures the individual functions. So
178 | // it's impossible to know what was set in the CSS.
179 | var value = this.getTransform(func);
180 | var rspace = /\s/;
181 | var rperc = /%/;
182 |
183 | if (func == 'origin' && !value && value !== 0) {
184 | // we can look up the origin in CSS
185 | value = this.$elem.css(this.transformProperty + '-origin');
186 |
187 | //Moz reports the value in % if there hasn't been a transformation yet
188 | if (rperc.test(value)) {
189 | value = value.split(rspace);
190 | if (rperc.test(value[0])) {
191 | value[0] = this.safeOuterWidth() * (parseFloat(value[0])/100);
192 | }
193 | if (rperc.test(value[1])) {
194 | value[1] = this.safeOuterHeight() * (parseFloat(value[1])/100);
195 | }
196 | } else if (!$.isArray(value)) {
197 | value = value.split(rspace);
198 | }
199 | } else if (!value && value !== 0 && $.transform.rfunc.scale.test(func)) {
200 | //scale functions return 1 by default
201 | //value = 1;
202 | }
203 | //console.log('getAttr(' + func + ') =>', value);
204 | return value;
205 | }
206 | });
--------------------------------------------------------------------------------
/test/transform.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CSS3 Transform Matrix Calculator
7 |
8 |
9 |
10 |
11 |
12 |
13 |
24 |
25 |
26 |
27 |
28 |
29 | CSS3 Transform Matrix Calculator
30 | Create transformations in all browsers
31 |
32 |
33 |
34 |
45 |
142 |
143 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
158 |
159 |
160 |
--------------------------------------------------------------------------------
/src/jquery.matrix.js:
--------------------------------------------------------------------------------
1 |
2 | ///////////////////////////////////////////////////////
3 | // Matrix
4 | ///////////////////////////////////////////////////////
5 | (function($, window, document, undefined) {
6 | /**
7 | * Matrix object for creating matrices relevant for 2d Transformations
8 | * @var Object
9 | */
10 | if (typeof($.matrix) == 'undefined') {
11 | $.extend({
12 | matrix: {}
13 | });
14 | }
15 | var $m = $.matrix;
16 |
17 | $.extend( $m, {
18 | /**
19 | * A 2-value vector
20 | * @param Number x
21 | * @param Number y
22 | * @constructor
23 | */
24 | V2: function(x, y){
25 | if ($.isArray(arguments[0])) {
26 | this.elements = arguments[0].slice(0, 2);
27 | } else {
28 | this.elements = [x, y];
29 | }
30 | this.length = 2;
31 | },
32 |
33 | /**
34 | * A 2-value vector
35 | * @param Number x
36 | * @param Number y
37 | * @param Number z
38 | * @constructor
39 | */
40 | V3: function(x, y, z){
41 | if ($.isArray(arguments[0])) {
42 | this.elements = arguments[0].slice(0, 3);
43 | } else {
44 | this.elements = [x, y, z];
45 | }
46 | this.length = 3;
47 | },
48 |
49 | /**
50 | * A 2x2 Matrix, useful for 2D-transformations without translations
51 | * @param Number mn
52 | * @constructor
53 | */
54 | M2x2: function(m11, m12, m21, m22) {
55 | if ($.isArray(arguments[0])) {
56 | this.elements = arguments[0].slice(0, 4);
57 | } else {
58 | this.elements = Array.prototype.slice.call(arguments).slice(0, 4);
59 | }
60 | this.rows = 2;
61 | this.cols = 2;
62 | },
63 |
64 | /**
65 | * A 3x3 Matrix, useful for 3D-transformations without translations
66 | * @param Number mn
67 | * @constructor
68 | */
69 | M3x3: function(m11, m12, m13, m21, m22, m23, m31, m32, m33) {
70 | if ($.isArray(arguments[0])) {
71 | this.elements = arguments[0].slice(0, 9);
72 | } else {
73 | this.elements = Array.prototype.slice.call(arguments).slice(0, 9);
74 | }
75 | this.rows = 3;
76 | this.cols = 3;
77 | }
78 | });
79 |
80 | /** generic matrix prototype */
81 | var Matrix = {
82 | /**
83 | * Return a specific element from the matrix
84 | * @param Number row where 1 is the 0th row
85 | * @param Number col where 1 is the 0th column
86 | * @return Number
87 | */
88 | e: function(row, col) {
89 | var rows = this.rows,
90 | cols = this.cols;
91 |
92 | // return 0 on nonsense rows and columns
93 | if (row > rows || col > rows || row < 1 || col < 1) {
94 | return 0;
95 | }
96 |
97 | return this.elements[(row - 1) * cols + col - 1];
98 | },
99 |
100 | /**
101 | * Taken from Zoomooz
102 | * https://github.com/jaukia/zoomooz/blob/c7a37b9a65a06ba730bd66391bbd6fe8e55d3a49/js/jquery.zoomooz.js
103 | */
104 | decompose: function() {
105 | var a = this.e(1, 1),
106 | b = this.e(2, 1),
107 | c = this.e(1, 2),
108 | d = this.e(2, 2),
109 | e = this.e(1, 3),
110 | f = this.e(2, 3);
111 |
112 | // In case the matrix can't be decomposed
113 | if (Math.abs(a * d - b * c) < 0.01) {
114 | return {
115 | rotate: 0 + 'deg',
116 | skewX: 0 + 'deg',
117 | scaleX: 1,
118 | scaleY: 1,
119 | translateX: 0 + 'px',
120 | translateY: 0 + 'px'
121 | };
122 | }
123 |
124 | // Translate is easy
125 | var tx = e, ty = f;
126 |
127 | // factor out the X scale
128 | var sx = Math.sqrt(a * a + b * b);
129 | a = a/sx;
130 | b = b/sx;
131 |
132 | // factor out the skew
133 | var k = a * c + b * d;
134 | c -= a * k;
135 | d -= b * k;
136 |
137 | // factor out the Y scale
138 | var sy = Math.sqrt(c * c + d * d);
139 | c = c / sy;
140 | d = d / sy;
141 | k = k / sy;
142 |
143 | // account for negative scale
144 | if ((a * d - b * c) < 0.0) {
145 | a = -a;
146 | b = -b;
147 | //c = -c; // accomplishes nothing to negate it
148 | //d = -d; // accomplishes nothing to negate it
149 | sx = -sx;
150 | //sy = -sy //Scale Y shouldn't ever be negated
151 | }
152 |
153 | // calculate the rotation angle and skew angle
154 | var rad2deg = $.angle.radianToDegree;
155 | var r = rad2deg(Math.atan2(b, a));
156 | k = rad2deg(Math.atan(k));
157 |
158 | return {
159 | rotate: r + 'deg',
160 | skewX: k + 'deg',
161 | scaleX: sx,
162 | scaleY: sy,
163 | translateX: tx + 'px',
164 | translateY: ty + 'px'
165 | };
166 | }
167 | };
168 |
169 | /** Extend all of the matrix types with the same prototype */
170 | $.extend($m.M2x2.prototype, Matrix, {
171 | toM3x3: function() {
172 | var a = this.elements;
173 | return new $m.M3x3(
174 | a[0], a[1], 0,
175 | a[2], a[3], 0,
176 | 0, 0, 1
177 | );
178 | },
179 |
180 | /**
181 | * Multiply a 2x2 matrix by a similar matrix or a vector
182 | * @param M2x2 | V2 matrix
183 | * @return M2x2 | V2
184 | */
185 | x: function(matrix) {
186 | var isVector = typeof(matrix.rows) === 'undefined';
187 |
188 | // Ensure the right-sized matrix
189 | if (!isVector && matrix.rows == 3) {
190 | return this.toM3x3().x(matrix);
191 | }
192 |
193 | var a = this.elements,
194 | b = matrix.elements;
195 |
196 | if (isVector && b.length == 2) {
197 | // b is actually a vector
198 | return new $m.V2(
199 | a[0] * b[0] + a[1] * b[1],
200 | a[2] * b[0] + a[3] * b[1]
201 | );
202 | } else if (b.length == a.length) {
203 | // b is a 2x2 matrix
204 | return new $m.M2x2(
205 | a[0] * b[0] + a[1] * b[2],
206 | a[0] * b[1] + a[1] * b[3],
207 |
208 | a[2] * b[0] + a[3] * b[2],
209 | a[2] * b[1] + a[3] * b[3]
210 | );
211 | }
212 | return false; // fail
213 | },
214 |
215 | /**
216 | * Generates an inverse of the current matrix
217 | * @param void
218 | * @return M2x2
219 | * @link http://www.dr-lex.be/random/matrix_inv.html
220 | */
221 | inverse: function() {
222 | var d = 1/this.determinant(),
223 | a = this.elements;
224 | return new $m.M2x2(
225 | d * a[3], d * -a[1],
226 | d * -a[2], d * a[0]
227 | );
228 | },
229 |
230 | /**
231 | * Calculates the determinant of the current matrix
232 | * @param void
233 | * @return Number
234 | * @link http://www.dr-lex.be/random/matrix_inv.html
235 | */
236 | determinant: function() {
237 | var a = this.elements;
238 | return a[0] * a[3] - a[1] * a[2];
239 | }
240 | });
241 |
242 | $.extend($m.M3x3.prototype, Matrix, {
243 | /**
244 | * Multiply a 3x3 matrix by a similar matrix or a vector
245 | * @param M3x3 | V3 matrix
246 | * @return M3x3 | V3
247 | */
248 | x: function(matrix) {
249 | var isVector = typeof(matrix.rows) === 'undefined';
250 |
251 | // Ensure the right-sized matrix
252 | if (!isVector && matrix.rows < 3) {
253 | matrix = matrix.toM3x3();
254 | }
255 |
256 | var a = this.elements,
257 | b = matrix.elements;
258 |
259 | if (isVector && b.length == 3) {
260 | // b is actually a vector
261 | return new $m.V3(
262 | a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
263 | a[3] * b[0] + a[4] * b[1] + a[5] * b[2],
264 | a[6] * b[0] + a[7] * b[1] + a[8] * b[2]
265 | );
266 | } else if (b.length == a.length) {
267 | // b is a 3x3 matrix
268 | return new $m.M3x3(
269 | a[0] * b[0] + a[1] * b[3] + a[2] * b[6],
270 | a[0] * b[1] + a[1] * b[4] + a[2] * b[7],
271 | a[0] * b[2] + a[1] * b[5] + a[2] * b[8],
272 |
273 | a[3] * b[0] + a[4] * b[3] + a[5] * b[6],
274 | a[3] * b[1] + a[4] * b[4] + a[5] * b[7],
275 | a[3] * b[2] + a[4] * b[5] + a[5] * b[8],
276 |
277 | a[6] * b[0] + a[7] * b[3] + a[8] * b[6],
278 | a[6] * b[1] + a[7] * b[4] + a[8] * b[7],
279 | a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
280 | );
281 | }
282 | return false; // fail
283 | },
284 |
285 | /**
286 | * Generates an inverse of the current matrix
287 | * @param void
288 | * @return M3x3
289 | * @link http://www.dr-lex.be/random/matrix_inv.html
290 | */
291 | inverse: function() {
292 | var d = 1/this.determinant(),
293 | a = this.elements;
294 | return new $m.M3x3(
295 | d * ( a[8] * a[4] - a[7] * a[5]),
296 | d * (-(a[8] * a[1] - a[7] * a[2])),
297 | d * ( a[5] * a[1] - a[4] * a[2]),
298 |
299 | d * (-(a[8] * a[3] - a[6] * a[5])),
300 | d * ( a[8] * a[0] - a[6] * a[2]),
301 | d * (-(a[5] * a[0] - a[3] * a[2])),
302 |
303 | d * ( a[7] * a[3] - a[6] * a[4]),
304 | d * (-(a[7] * a[0] - a[6] * a[1])),
305 | d * ( a[4] * a[0] - a[3] * a[1])
306 | );
307 | },
308 |
309 | /**
310 | * Calculates the determinant of the current matrix
311 | * @param void
312 | * @return Number
313 | * @link http://www.dr-lex.be/random/matrix_inv.html
314 | */
315 | determinant: function() {
316 | var a = this.elements;
317 | 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]);
318 | }
319 | });
320 |
321 | /** generic vector prototype */
322 | var Vector = {
323 | /**
324 | * Return a specific element from the vector
325 | * @param Number i where 1 is the 0th value
326 | * @return Number
327 | */
328 | e: function(i) {
329 | return this.elements[i - 1];
330 | }
331 | };
332 |
333 | /** Extend all of the vector types with the same prototype */
334 | $.extend($m.V2.prototype, Vector);
335 | $.extend($m.V3.prototype, Vector);
336 | })(jQuery, this, this.document);
--------------------------------------------------------------------------------
/test/css/global.css:
--------------------------------------------------------------------------------
1 | /* ====== reset ====== */
2 | html{color:#000;background:#FFF;}
3 | body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}
4 | table{border-collapse:collapse;border-spacing:0;}
5 | fieldset,img{border:0;}
6 | address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}
7 | li{list-style:none;}
8 | caption,th{text-align:left;}
9 | h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}
10 | q:before,q:after{content:'';}
11 | abbr,acronym{border:0;font-variant:normal;}
12 | sup{vertical-align:text-top;}
13 | sub{vertical-align:text-bottom;}
14 | input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}
15 | input,textarea,select{*font-size:100%;}
16 | legend{color:#000;}
17 |
18 | /* html5 elements */
19 | article,aside,footer,header,hgroup,menu,nav,section{display:block;}
20 | datalist{display:none;}
21 |
22 |
23 | /* ====== fonts ====== */
24 | body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}
25 |
26 |
27 | /* ====== templates ====== */
28 | body{_text-align:center;}/* IE5.5 */
29 | .main{display:table-cell;*display:block;width:auto;}
30 | .body,.main{*zoom:1;}
31 | .body:after,.main:after{clear:both;display:block;visibility:hidden;overflow:hidden;height:0 !important;line-height:0;font-size:xx-large;content:" x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x ";}
32 | .page{margin:0 auto;width:960px;_text-align:left;}/* wraps other template elems to set width */ /* text-align IE5.5 */
33 | /* .liquid can extend page to allow for a liquid page width */
34 | .liquid{width:auto;margin:0;}
35 |
36 | /* columns */
37 | .left-col{float:left;width:175px;_margin-right:-3px;}
38 | .right-col{float:right;width:320px;_margin-left:-3px;}
39 |
40 |
41 | /* ====== grids ====== */
42 | .line:after,.lastUnit:after{clear:both;display:block;visibility:hidden;overflow:hidden;height:0 !important;line-height:0;font-size:xx-large;content:" x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x ";}
43 | .line{*zoom:1;}
44 | .unit{float:left;}
45 | .size1of1{float:none;}
46 | .size1of2{width:50%;}
47 | .size1of3{width:33.33333%;}
48 | .size2of3{width:66.66666%;}
49 | .size1of4{width:25%;}
50 | .size3of4{width:75%;}
51 | .size1of5{width:20%;}
52 | .size2of5{width:40%;}
53 | .size3of5{width:60%;}
54 | .size4of5{width:80%;}
55 | .lastUnit{display:table-cell;float:none;width:auto;*display:block;*zoom:1;_position:relative;_left:-3px;_margin-right:-3px;}
56 |
57 |
58 | /* ====== modules ====== */
59 | /* mod */
60 | .mod{margin:10px;}
61 | .mod .inner{/*background:url(skin/transparent.png) repeat left top;*/}
62 | .hd,.bd,.ft{overflow:hidden;_overflow:visible;_zoom:1;}
63 | .inner{position:relative;}
64 | b{display:block;background-repeat:no-repeat;font-size:1%;position:relative;z-index:10;}
65 | .tl, .tr, .bl, .br{height:10px;width:10px;float:left;}
66 | .tl{background-position:left top;}
67 | .tr{background-position:right top;}
68 | .bl{background-position:left bottom;}
69 | .br{background-position:right bottom;}
70 | .br,.tr{float:right;}
71 | .tr, .tl{overflow:hidden;margin-bottom:-32000px;}/* margin bottom needs to be < -9px */
72 | .bl,.br{margin-top:-10px;}
73 | .top{background-position:center top;}
74 | .bottom{background-position:center bottom;_zoom:1;}/* this zoom required for IE5.5 only*/
75 |
76 | /* complex */
77 | .complex{overflow:hidden;*position:relative;*zoom:1;}/* position/zoom required for IE7, 6, 5.5 */
78 | .complex .tl, .complex .tr{height:32000px;margin-bottom:-32000px;width:10px;}
79 | .complex .bl, .complex .br{/*margin-top:0;*/}
80 | .complex .top{height:5px;}
81 | .complex .bottom{height:5px;/*margin-top:-10px;*/}
82 |
83 | /* pop */
84 | .pop{overflow:visible;margin:10px 20px 20px 10px;background-position:left top;}
85 | .pop .inner{right:-10px;bottom:-10px;background-position:right bottom;padding:0 10px 10px 0;}
86 | .pop .tl, .pop .br{display:none;}
87 | .pop .bl{bottom:-10px;}
88 | .pop .tr{right:-10px;}
89 |
90 |
91 | /* ====== module skins ====== */
92 |
93 | /* ----- noted (extends mod) ----- */
94 | .noted,.noted .inner{border:1px solid #c2c2c2;/*-moz-border-radius: 5px;-webkit-border-radius: 5px;border-radius: 5px;*/}
95 | .noted .inner{border-color:#eded68;}
96 | .noted b{background-image:url(../i/noted.png);}
97 | .noted .tl{left:-1px;top:-1px;}
98 | .noted .tr{right:-1px;top:-1px;}
99 | .noted .bl{left:-1px;bottom:-1px;}
100 | .noted .br{right:-1px;bottom:-1px;}
101 |
102 |
103 | /* ====== default spacing ====== */
104 | h1, h2, h3, h4, h5, h6, ul, ol,dl, p,blockquote{padding:10px;}
105 | h1, h2, h3, h4, h5, h6,img{padding-bottom:0;}
106 | pre{margin:10px;}
107 | table h1,table h2,table h3, table h4, table h5, table h6, table p, table ul, table ol, table dl{padding:0;}
108 |
109 |
110 | /* ====== Elements ====== */
111 | img{display:block;}
112 | em{font-style:italic;}
113 | strong{font-weight:bold;}
114 | hr{border:2px solid #e2e2e2;border-width:0 0 2px 0;margin:20px 10px 10px 10px;}
115 | code{color:#0B8C8F;}
116 |
117 |
118 | /* ====== headings ====== */
119 | /* .h1-.h6 classes should be used to maintain the semantically appropriate heading levels - NOT for use on non-headings */
120 | h1, .h1{font-size:196%;font-weight:normal;font-style:normal;color:#212121;}
121 | h2, .h2{font-size:167%;font-weight:normal;font-style:normal;color:#212121;}
122 | h3, .h3{font-size:146.5%;font-weight:normal;font-style:normal;color:#212121;}
123 | h4, .h4{font-size:123.1%;font-weight:normal;font-style:normal;color:#212121;}
124 | h5, .h5{font-size:108%;font-weight:bold;font-style:normal;color:#212121;}
125 | h6, .h6{font-size:108%;font-weight:normal;font-style:italic;color:#212121;}
126 | /* if additional headings are needed they should be created via additional classes, never via location dependant styling */
127 |
128 |
129 | /* ====== links ======*/
130 | a{text-decoration:none;}
131 | a:focus, a:hover{text-decoration:underline;}
132 |
133 |
134 | /* ====== lists ======*/
135 | /* numbered */
136 | ol.simple-list li{list-style-type:decimal;margin-left:40px;}
137 |
138 | /* standard */
139 | ul.simple-list li{list-style-type:disc;margin-left:40px;}
140 |
141 | /* horizontal */
142 | .horizontal-list li{float:left;_display:inline;margin-left:20px;}
143 | .horizontal-list li.first{margin-left:0;}
144 |
145 | /* dropdown */
146 | .dropdown-list{position:relative;}
147 | /*
148 | TODO change to dropdown-sub
149 | */
150 | .dropdown-list ul.sub-list{display:none;position:absolute;left: 0;}
151 | .dropdown-list li:hover ul.sub-list{display:block;}
152 |
153 | /* ====== tables ====== */
154 | .data{padding:20px;position:relative;zoom:1;vertical-align:top;border-right:solid 1px transparent;/* border fixes a FF2 bug which causes the data table to overlay its borders*/}
155 | .data table{width:100%;border:1px solid #AE0345;}
156 | th, td{vertical-align:top;border:1px solid #AE0345;}
157 |
158 | .txt-c, .data .txt-c td, .data .txt-c th{text-align:center;}
159 | .txt-l, .data .txt-l td, .data .txt-l th{text-align:left;}
160 | .txt-r, .data .txt-r td, .data .txt-r th{text-align:right;}
161 | .txt-t, .data .txt-t td, .data .txt-t th{vertical-align:top;}
162 | .txt-b, .data .txt-b td, .data .txt-b th{vertical-align:bottom;}
163 | .txt-m, .data .txt-m td, .data .txt-m th{vertical-align:middle;}
164 |
165 | .data th,.data td{padding:3px 20px}
166 | .data thead tr{background-color:#fff0f8;}
167 | .data th{color:#000;font-weight:bold}
168 |
169 | /* specification table - extends data table */
170 | .spec{padding:10px;}
171 | .spec table{border-top:1px solid #e2e2e2;border-bottom-color:#fff;border-left:none;border-right:none;}
172 | .spec th, .spec td{border:1px solid #e2e2e2;border-width:1px 0;padding-left:0;}
173 | .spec .odd, .spec .even{background-color:#fff;}
174 |
175 |
176 | /* ====== blockquote ====== */
177 | cite{display:block;text-align:right;padding-top:10px;}
178 |
179 |
180 | /* ====== callout ====== */
181 | .callout{font-size:189%;color:#999;font-style:italic;}
182 | .callout cite{display:block;text-align:right;padding-top:30px;font-size:69.25%;}
183 | .callout span.quot{font-size:200%;vertical-align:sub;color:#e2e2e2;line-height:15px;font-weight:bold;}
184 | .callout span.quotLast{vertical-align:middle;}
185 |
186 |
187 | /* ====== media ====== */
188 | .media{zoom:1;margin:10px;}
189 | .media .img{float:left;margin-right:10px;}
190 | .media .img img{display:block;}
191 | .media .imgExt{float:right;margin-left:10px;}
192 |
193 |
194 | /* ====== background image classes ====== */
195 |
196 |
197 | /* ====== special formatting classes ====== */
198 |
199 |
200 | /* ====== utility ====== */
201 | .hide-offscreen{position:absolute;left:-9999em;top:-9999em;}
202 | .hide-textindent{text-indent:-9999em;display:block;overflow:hidden;}
203 |
204 |
205 | /* ====== specific layout styling ====== */
206 | #logo{float:right;margin:10px;}
207 | #logo img {
208 | /*
209 | TODO does ie7+ even support AlphaImageLoader? Can remove _ if they don't.
210 | */
211 | _filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../i/logo-website.png', sizingMethod='image');
212 | }
213 |
214 |
215 | /* ====== clearfix ====== */
216 | .page:after,.head:after,.body:after,.foot:after,.horizontal-list:after{content:".";display:block;height:0;clear:both;visibility:hidden;}
217 | .page,.head,.body,.foot,.horizontal-list{*zoom:1;}
218 |
219 | /* ====== Font size chart ====== */
220 | /*
221 | PX %%
222 | ----------
223 | 10 77
224 | 11 85
225 | 12 93
226 | 13 100
227 | 14 108
228 | 15 116
229 | 16 123.1
230 | 17 131
231 | 18 138.5
232 | 19 146.5
233 | 20 153.9
234 | 21 161.6
235 | 22 167
236 | 23 174
237 | 24 182
238 | 25 189
239 | 26 197
240 | */
--------------------------------------------------------------------------------
/src/jquery.transform.animate.js:
--------------------------------------------------------------------------------
1 |
2 | ///////////////////////////////////////////////////////
3 | // Animation
4 | ///////////////////////////////////////////////////////
5 | (function($, window, document, undefined) {
6 | /**
7 | * @var Regex looks for units on a string
8 | */
9 | var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
10 |
11 | /**
12 | * Doctors prop values in the event that they contain spaces
13 | * @param Object prop
14 | * @param String speed
15 | * @param String easing
16 | * @param Function callback
17 | * @return bool
18 | */
19 | var _animate = $.fn.animate;
20 | $.fn.animate = function( prop, speed, easing, callback ) {
21 | var optall = $.speed(speed, easing, callback),
22 | mv = $.cssMultipleValues;
23 |
24 | // Speed always creates a complete function that must be reset
25 | optall.complete = optall.old;
26 |
27 | // Capture multiple values
28 | if (!$.isEmptyObject(prop)) {
29 | if (typeof optall.original === 'undefined') {
30 | optall.original = {};
31 | }
32 | $.each( prop, function( name, val ) {
33 | if (mv[name]
34 | || $.cssAngle[name]
35 | || (!$.cssNumber[name] && $.inArray(name, $.transform.funcs) !== -1)) {
36 |
37 | // Handle special easing
38 | var specialEasing = null;
39 | if (jQuery.isArray(prop[name])) {
40 | var mvlen = 1, len = val.length;
41 | if (mv[name]) {
42 | mvlen = (typeof mv[name].length === 'undefined' ? mv[name] : mv[name].length);
43 | }
44 | if ( len > mvlen
45 | || (len < mvlen && len == 2)
46 | || (len == 2 && mvlen == 2 && isNaN(parseFloat(val[len - 1])))) {
47 |
48 | specialEasing = val[len - 1];
49 | val.splice(len - 1, 1);
50 | }
51 | }
52 |
53 | // Store the original values onto the optall
54 | optall.original[name] = val.toString();
55 |
56 | // reduce to a unitless number (to trick animate)
57 | prop[name] = parseFloat(val);
58 | }
59 | } );
60 | }
61 |
62 | //NOTE: we edited prop above to trick animate
63 | //NOTE: we pre-convert to an optall so we can doctor it
64 | return _animate.apply(this, [arguments[0], optall]);
65 | };
66 |
67 | var prop = 'paddingBottom';
68 | function cur(elem, prop) {
69 | if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
70 | //return elem[ prop ];
71 | }
72 |
73 | var r = parseFloat( $.css( elem, prop ) );
74 | return r && r > -10000 ? r : 0;
75 | }
76 |
77 | var _custom = $.fx.prototype.custom;
78 | $.fx.prototype.custom = function(from, to, unit) {
79 | var multiple = $.cssMultipleValues[this.prop],
80 | angle = $.cssAngle[this.prop];
81 |
82 | //TODO: simply check for the existence of CSS Hooks?
83 | if (multiple || (!$.cssNumber[this.prop] && $.inArray(this.prop, $.transform.funcs) !== -1)) {
84 | this.values = [];
85 |
86 | if (!multiple) {
87 | multiple = 1;
88 | }
89 |
90 | // Pull out the known values
91 | var values = this.options.original[this.prop],
92 | currentValues = $(this.elem).css(this.prop),
93 | defaultValues = $.cssDefault[this.prop] || 0;
94 |
95 | // make sure the current css value is an array
96 | if (!$.isArray(currentValues)) {
97 | currentValues = [currentValues];
98 | }
99 |
100 | // make sure the new values are an array
101 | if (!$.isArray(values)) {
102 | if ($.type(values) === 'string') {
103 | values = values.split(',');
104 | } else {
105 | values = [values];
106 | }
107 | }
108 |
109 | // make sure we have enough new values
110 | var length = multiple.length || multiple, i = 0;
111 | while (values.length < length) {
112 | values.push(multiple.duplicate ? values[0] : defaultValues[i] || 0);
113 | i++;
114 | }
115 |
116 | // calculate a start, end and unit for each new value
117 | var start, parts, end, //unit,
118 | fx = this,
119 | transform = fx.elem.transform;
120 | orig = $.style(fx.elem, prop);
121 |
122 | $.each(values, function(i, val) {
123 | // find a sensible start value
124 | if (currentValues[i]) {
125 | start = currentValues[i];
126 | } else if (defaultValues[i] && !multiple.duplicate) {
127 | start = defaultValues[i];
128 | } else if (multiple.duplicate) {
129 | start = currentValues[0];
130 | } else {
131 | start = 0;
132 | }
133 |
134 | // Force the correct unit on the start
135 | if (angle) {
136 | start = $.angle.toDegree(start);
137 | } else if (!$.cssNumber[fx.prop]) {
138 | parts = rfxnum.exec($.trim(start));
139 | if (parts[3] && parts[3] !== 'px') {
140 | if (parts[3] === '%') {
141 | start = parseFloat( parts[2] ) / 100 * transform['safeOuter' + (i ? 'Height' : 'Width')]();
142 | } else {
143 | $.style( fx.elem, prop, start);
144 | start = cur(fx.elem, prop);
145 | $.style( fx.elem, prop, orig);
146 | }
147 | }
148 | }
149 | start = parseFloat(start);
150 |
151 | // parse the value with a regex
152 | parts = rfxnum.exec($.trim(val));
153 |
154 | if (parts) {
155 | // we found a sensible value and unit
156 | end = parseFloat( parts[2] );
157 | unit = parts[3] || "px"; //TODO: change to an appropriate default unit
158 |
159 | if (angle) {
160 | end = $.angle.toDegree(end + unit);
161 | unit = 'deg';
162 | } else if (!$.cssNumber[fx.prop] && unit === '%') {
163 | start = (start / transform['safeOuter' + (i ? 'Height' : 'Width')]()) * 100;
164 | } else if (!$.cssNumber[fx.prop] && unit !== 'px') {
165 | $.style( fx.elem, prop, (end || 1) + unit);
166 | start = ((end || 1) / cur(fx.elem, prop)) * start;
167 | $.style( fx.elem, prop, orig);
168 | }
169 |
170 | // If a +=/-= token was provided, we're doing a relative animation
171 | if (parts[1]) {
172 | end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
173 | }
174 | } else {
175 | // I don't know when this would happen
176 | end = val;
177 | unit = '';
178 | }
179 |
180 | // Save the values
181 | fx.values.push({
182 | start: start,
183 | end: end,
184 | unit: unit
185 | });
186 | });
187 | }
188 | return _custom.apply(this, arguments);
189 | };
190 |
191 | /**
192 | * Animates a multi value attribute
193 | * @param Object fx
194 | * @return null
195 | */
196 | $.fx.multipleValueStep = {
197 | _default: function(fx) {
198 | $.each(fx.values, function(i, val) {
199 | fx.values[i].now = val.start + ((val.end - val.start) * fx.pos);
200 | });
201 | }
202 | };
203 | $.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
204 | $.fx.multipleValueStep[func] = function(fx) {
205 | var d = fx.decomposed,
206 | $m = $.matrix;
207 | m = $m.identity();
208 |
209 | d.now = {};
210 |
211 | // increment each part of the decomposition and recompose it
212 | $.each(d.start, function(k) {
213 | // calculate the current value
214 | d.now[k] = parseFloat(d.start[k]) + ((parseFloat(d.end[k]) - parseFloat(d.start[k])) * fx.pos);
215 |
216 | // skip functions that won't affect the transform
217 | if (((k === 'scaleX' || k === 'scaleY') && d.now[k] === 1) ||
218 | (k !== 'scaleX' && k !== 'scaleY' && d.now[k] === 0)) {
219 | return true;
220 | }
221 |
222 | // calculating
223 | m = m.x($m[k](d.now[k]));
224 | });
225 |
226 | // save the correct matrix values for the value of now
227 | var val;
228 | $.each(fx.values, function(i) {
229 | switch (i) {
230 | case 0: val = parseFloat(m.e(1, 1).toFixed(6)); break;
231 | case 1: val = parseFloat(m.e(2, 1).toFixed(6)); break;
232 | case 2: val = parseFloat(m.e(1, 2).toFixed(6)); break;
233 | case 3: val = parseFloat(m.e(2, 2).toFixed(6)); break;
234 | case 4: val = parseFloat(m.e(1, 3).toFixed(6)); break;
235 | case 5: val = parseFloat(m.e(2, 3).toFixed(6)); break;
236 | }
237 | fx.values[i].now = val;
238 | });
239 | };
240 | });
241 | /**
242 | * Step for animating tranformations
243 | */
244 | $.each($.transform.funcs, function(i, func) {
245 | $.fx.step[func] = function(fx) {
246 | var transform = fx.elem.transform || new $.transform(fx.elem),
247 | funcs = {};
248 |
249 | if ($.cssMultipleValues[func] || (!$.cssNumber[func] && $.inArray(func, $.transform.funcs) !== -1)) {
250 | ($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
251 | funcs[fx.prop] = [];
252 | $.each(fx.values, function(i, val) {
253 | funcs[fx.prop].push(val.now + ($.cssNumber[fx.prop] ? '' : val.unit));
254 | });
255 | } else {
256 | funcs[fx.prop] = fx.now + ($.cssNumber[fx.prop] ? '' : fx.unit);
257 | }
258 |
259 | transform.exec(funcs, {preserve: true});
260 | };
261 | });
262 |
263 | // Support matrix animation
264 | $.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
265 | $.fx.step[func] = function(fx) {
266 | var transform = fx.elem.transform || new $.transform(fx.elem),
267 | funcs = {};
268 |
269 | if (!fx.initialized) {
270 | fx.initialized = true;
271 |
272 | // Reflections need a sensible end value set
273 | if (func !== 'matrix') {
274 | var values = $.matrix[func]().elements;
275 | var val;
276 | $.each(fx.values, function(i) {
277 | switch (i) {
278 | case 0: val = values[0]; break;
279 | case 1: val = values[2]; break;
280 | case 2: val = values[1]; break;
281 | case 3: val = values[3]; break;
282 | default: val = 0;
283 | }
284 | fx.values[i].end = val;
285 | });
286 | }
287 |
288 | // Decompose the start and end
289 | fx.decomposed = {};
290 | var v = fx.values;
291 |
292 | fx.decomposed.start = $.matrix.matrix(v[0].start, v[1].start, v[2].start, v[3].start, v[4].start, v[5].start).decompose();
293 | fx.decomposed.end = $.matrix.matrix(v[0].end, v[1].end, v[2].end, v[3].end, v[4].end, v[5].end).decompose();
294 | }
295 |
296 | ($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
297 | funcs.matrix = [];
298 | $.each(fx.values, function(i, val) {
299 | funcs.matrix.push(val.now);
300 | });
301 |
302 | transform.exec(funcs, {preserve: true});
303 | };
304 | });
305 | })(jQuery, this, this.document);
--------------------------------------------------------------------------------
/test/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
51 |
52 |
53 |
76 |
77 |
78 |
79 |
80 |

81 |
82 |
83 |
84 |
85 |
86 |
87 |

88 |
89 |
90 |
91 |
98 |
99 |
100 |
101 |
102 |
103 |

104 |
105 |
106 |
107 |
108 |
109 |
110 |

111 |
112 |
113 |
114 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
135 |
136 |
397 |
398 |
399 |
--------------------------------------------------------------------------------
/src/jquery.transform.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery 2d Transform v@VERSION
3 | * http://wiki.github.com/heygrady/transform/
4 | *
5 | * Copyright 2010, Grady Kuhnline
6 | * Dual licensed under the MIT or GPL Version 2 licenses.
7 | * http://jquery.org/license
8 | *
9 | * Date:
10 | */
11 | ///////////////////////////////////////////////////////
12 | // Transform
13 | ///////////////////////////////////////////////////////
14 | (function($, window, document, undefined) {
15 | /**
16 | * @var Regex identify the matrix filter in IE
17 | */
18 | var rmatrix = /progid:DXImageTransform\.Microsoft\.Matrix\(.*?\)/,
19 | rfxnum = /^([\+\-]=)?([\d+.\-]+)(.*)$/,
20 | rperc = /%/;
21 |
22 | // Steal some code from Modernizr
23 | var m = document.createElement( 'modernizr' ),
24 | m_style = m.style;
25 |
26 | function stripUnits(arg) {
27 | return parseFloat(arg);
28 | }
29 |
30 | /**
31 | * Find the prefix that this browser uses
32 | */
33 | function getVendorPrefix() {
34 | var property = {
35 | transformProperty : '',
36 | MozTransform : '-moz-',
37 | WebkitTransform : '-webkit-',
38 | OTransform : '-o-',
39 | msTransform : '-ms-'
40 | };
41 | for (var p in property) {
42 | if (typeof m_style[p] != 'undefined') {
43 | return property[p];
44 | }
45 | }
46 | return null;
47 | }
48 |
49 | function supportCssTransforms() {
50 | if (typeof(window.Modernizr) !== 'undefined') {
51 | return Modernizr.csstransforms;
52 | }
53 |
54 | var props = [ 'transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform' ];
55 | for ( var i in props ) {
56 | if ( m_style[ props[i] ] !== undefined ) {
57 | return true;
58 | }
59 | }
60 | }
61 |
62 | // Capture some basic properties
63 | var vendorPrefix = getVendorPrefix(),
64 | transformProperty = vendorPrefix !== null ? vendorPrefix + 'transform' : false,
65 | transformOriginProperty = vendorPrefix !== null ? vendorPrefix + 'transform-origin' : false;
66 |
67 | // store support in the jQuery Support object
68 | $.support.csstransforms = supportCssTransforms();
69 |
70 | // IE9 public preview 6 requires the DOM names
71 | if (vendorPrefix == '-ms-') {
72 | transformProperty = 'msTransform';
73 | transformOriginProperty = 'msTransformOrigin';
74 | }
75 |
76 | /**
77 | * Class for creating cross-browser transformations
78 | * @constructor
79 | */
80 | $.extend({
81 | transform: function(elem) {
82 | // Cache the transform object on the element itself
83 | elem.transform = this;
84 |
85 | /**
86 | * The element we're working with
87 | * @var jQueryCollection
88 | */
89 | this.$elem = $(elem);
90 |
91 | /**
92 | * Remember the matrix we're applying to help the safeOuterLength func
93 | */
94 | this.applyingMatrix = false;
95 | this.matrix = null;
96 |
97 | /**
98 | * Remember the css height and width to save time
99 | * This is only really used in IE
100 | * @var Number
101 | */
102 | this.height = null;
103 | this.width = null;
104 | this.outerHeight = null;
105 | this.outerWidth = null;
106 |
107 | /**
108 | * We need to know the box-sizing in IE for building the outerHeight and outerWidth
109 | * @var string
110 | */
111 | this.boxSizingValue = null;
112 | this.boxSizingProperty = null;
113 |
114 | this.attr = null;
115 | this.transformProperty = transformProperty;
116 | this.transformOriginProperty = transformOriginProperty;
117 | }
118 | });
119 |
120 | $.extend($.transform, {
121 | /**
122 | * @var Array list of all valid transform functions
123 | */
124 | funcs: ['matrix', 'origin', 'reflect', 'reflectX', 'reflectXY', 'reflectY', 'rotate', 'scale', 'scaleX', 'scaleY', 'skew', 'skewX', 'skewY', 'translate', 'translateX', 'translateY']
125 | });
126 |
127 | /**
128 | * Create Transform as a jQuery plugin
129 | * @param Object funcs
130 | * @param Object options
131 | */
132 | $.fn.transform = function(funcs, options) {
133 | return this.each(function() {
134 | var t = this.transform || new $.transform(this);
135 | if (funcs) {
136 | t.exec(funcs, options);
137 | }
138 | });
139 | };
140 |
141 | $.transform.prototype = {
142 | /**
143 | * Applies all of the transformations
144 | * @param Object funcs
145 | * @param Object options
146 | * forceMatrix - uses the matrix in all browsers
147 | * preserve - tries to preserve the values from previous runs
148 | */
149 | exec: function(funcs, options) {
150 | // extend options
151 | options = $.extend(true, {
152 | forceMatrix: false,
153 | preserve: false
154 | }, options);
155 |
156 | // preserve the funcs from the previous run
157 | this.attr = null;
158 | if (options.preserve) {
159 | funcs = $.extend(true, this.getAttrs(true, true), funcs);
160 | } else {
161 | funcs = $.extend(true, {}, funcs); // copy the object to prevent weirdness
162 | }
163 |
164 | // Record the custom attributes on the element itself
165 | this.setAttrs(funcs);
166 |
167 | // apply the funcs
168 | if ($.support.csstransforms && !options.forceMatrix) {
169 | // CSS3 is supported
170 | return this.execFuncs(funcs);
171 | } else if ($.browser.msie || ($.support.csstransforms && options.forceMatrix)) {
172 | // Internet Explorer or Forced matrix
173 | return this.execMatrix(funcs);
174 | }
175 | return false;
176 | },
177 |
178 | /**
179 | * Applies all of the transformations as functions
180 | * @param Object funcs
181 | */
182 | execFuncs: function(funcs) {
183 | var values = [];
184 |
185 | // construct a CSS string
186 | for (var func in funcs) {
187 | // handle origin separately
188 | if (func == 'origin') {
189 | this[func].apply(this, $.isArray(funcs[func]) ? funcs[func] : [funcs[func]]);
190 | } else if ($.inArray(func, $.transform.funcs) !== -1) {
191 | values.push(this.createTransformFunc(func, funcs[func]));
192 | }
193 | }
194 | this.$elem.css(transformProperty, values.join(' '));
195 | return true;
196 | },
197 |
198 | /**
199 | * Applies all of the transformations as a matrix
200 | * @param Object funcs
201 | */
202 | execMatrix: function(funcs) {
203 | var matrix,
204 | tempMatrix,
205 | args;
206 |
207 | var elem = this.$elem[0],
208 | _this = this;
209 | function normalPixels(val, i) {
210 | if (rperc.test(val)) {
211 | // this really only applies to translation
212 | return parseFloat(val) / 100 * _this['safeOuter' + (i ? 'Height' : 'Width')]();
213 | }
214 | return toPx(elem, val);
215 | }
216 |
217 | var rtranslate = /translate[X|Y]?/,
218 | trans = [];
219 |
220 | for (var func in funcs) {
221 | switch ($.type(funcs[func])) {
222 | case 'array': args = funcs[func]; break;
223 | case 'string': args = $.map(funcs[func].split(','), $.trim); break;
224 | default: args = [funcs[func]];
225 | }
226 |
227 | if ($.matrix[func]) {
228 |
229 | if ($.cssAngle[func]) {
230 | // normalize on degrees
231 | args = $.map(args, $.angle.toDegree);
232 | } else if (!$.cssNumber[func]) {
233 | // normalize to pixels
234 | args = $.map(args, normalPixels);
235 | } else {
236 | // strip units
237 | args = $.map(args, stripUnits);
238 | }
239 |
240 | tempMatrix = $.matrix[func].apply(this, args);
241 | if (rtranslate.test(func)) {
242 | //defer translation
243 | trans.push(tempMatrix);
244 | } else {
245 | matrix = matrix ? matrix.x(tempMatrix) : tempMatrix;
246 | }
247 | } else if (func == 'origin') {
248 | this[func].apply(this, args);
249 | }
250 | }
251 |
252 | // check that we have a matrix
253 | matrix = matrix || $.matrix.identity();
254 |
255 | // Apply translation
256 | $.each(trans, function(i, val) { matrix = matrix.x(val); });
257 |
258 | // pull out the relevant values
259 | var a = parseFloat(matrix.e(1,1).toFixed(6)),
260 | b = parseFloat(matrix.e(2,1).toFixed(6)),
261 | c = parseFloat(matrix.e(1,2).toFixed(6)),
262 | d = parseFloat(matrix.e(2,2).toFixed(6)),
263 | tx = matrix.rows === 3 ? parseFloat(matrix.e(1,3).toFixed(6)) : 0,
264 | ty = matrix.rows === 3 ? parseFloat(matrix.e(2,3).toFixed(6)) : 0;
265 |
266 | //apply the transform to the element
267 | if ($.support.csstransforms && vendorPrefix === '-moz-') {
268 | // -moz-
269 | this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + 'px, ' + ty + 'px)');
270 | } else if ($.support.csstransforms) {
271 | // -webkit, -o-, w3c
272 | // NOTE: WebKit and Opera don't allow units on the translate variables
273 | this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + ', ' + ty + ')');
274 | } else if ($.browser.msie) {
275 | // IE requires the special transform Filter
276 |
277 | //TODO: Use Nearest Neighbor during animation FilterType=\'nearest neighbor\'
278 | var filterType = ', FilterType=\'nearest neighbor\''; //bilinear
279 | var style = this.$elem[0].style;
280 | var matrixFilter = 'progid:DXImageTransform.Microsoft.Matrix(' +
281 | 'M11=' + a + ', M12=' + c + ', M21=' + b + ', M22=' + d +
282 | ', sizingMethod=\'auto expand\'' + filterType + ')';
283 | var filter = style.filter || $.curCSS( this.$elem[0], "filter" ) || "";
284 | style.filter = rmatrix.test(filter) ? filter.replace(rmatrix, matrixFilter) : filter ? filter + ' ' + matrixFilter : matrixFilter;
285 |
286 | // Let's know that we're applying post matrix fixes and the height/width will be static for a bit
287 | this.applyingMatrix = true;
288 | this.matrix = matrix;
289 |
290 | // IE can't set the origin or translate directly
291 | this.fixPosition(matrix, tx, ty);
292 |
293 | this.applyingMatrix = false;
294 | this.matrix = null;
295 | }
296 | return true;
297 | },
298 |
299 | /**
300 | * Sets the transform-origin
301 | * This really needs to be percentages
302 | * @param Number x length
303 | * @param Number y length
304 | */
305 | origin: function(x, y) {
306 | // use CSS in supported browsers
307 | if ($.support.csstransforms) {
308 | if (typeof y === 'undefined') {
309 | this.$elem.css(transformOriginProperty, x);
310 | } else {
311 | this.$elem.css(transformOriginProperty, x + ' ' + y);
312 | }
313 | return true;
314 | }
315 |
316 | // correct for keyword lengths
317 | switch (x) {
318 | case 'left': x = '0'; break;
319 | case 'right': x = '100%'; break;
320 | case 'center': // no break
321 | case undefined: x = '50%';
322 | }
323 | switch (y) {
324 | case 'top': y = '0'; break;
325 | case 'bottom': y = '100%'; break;
326 | case 'center': // no break
327 | case undefined: y = '50%'; //TODO: does this work?
328 | }
329 |
330 | // store mixed values with units, assumed pixels
331 | this.setAttr('origin', [
332 | rperc.test(x) ? x : toPx(this.$elem[0], x) + 'px',
333 | rperc.test(y) ? y : toPx(this.$elem[0], y) + 'px'
334 | ]);
335 | //console.log(this.getAttr('origin'));
336 | return true;
337 | },
338 |
339 | /**
340 | * Create a function suitable for a CSS value
341 | * @param string func
342 | * @param Mixed value
343 | */
344 | createTransformFunc: function(func, value) {
345 | if (func.substr(0, 7) === 'reflect') {
346 | // let's fake reflection, false value
347 | // falsey sets an identity matrix
348 | var m = value ? $.matrix[func]() : $.matrix.identity();
349 | return 'matrix(' + m.e(1,1) + ', ' + m.e(2,1) + ', ' + m.e(1,2) + ', ' + m.e(2,2) + ', 0, 0)';
350 | }
351 |
352 | //value = _correctUnits(func, value);
353 |
354 | if (func == 'matrix') {
355 | if (vendorPrefix === '-moz-') {
356 | value[4] = value[4] ? value[4] + 'px' : 0;
357 | value[5] = value[5] ? value[5] + 'px' : 0;
358 | }
359 | }
360 | return func + '(' + ($.isArray(value) ? value.join(', ') : value) + ')';
361 | },
362 |
363 | /**
364 | * @param Matrix matrix
365 | * @param Number tx
366 | * @param Number ty
367 | * @param Number height
368 | * @param Number width
369 | */
370 | fixPosition: function(matrix, tx, ty, height, width) {
371 | // now we need to fix it!
372 | var calc = new $.matrix.calc(matrix, this.safeOuterHeight(), this.safeOuterWidth()),
373 | origin = this.getAttr('origin'); // mixed percentages and px
374 |
375 | // translate a 0, 0 origin to the current origin
376 | var offset = calc.originOffset(new $.matrix.V2(
377 | rperc.test(origin[0]) ? parseFloat(origin[0])/100*calc.outerWidth : parseFloat(origin[0]),
378 | rperc.test(origin[1]) ? parseFloat(origin[1])/100*calc.outerHeight : parseFloat(origin[1])
379 | ));
380 |
381 | // IE glues the top-most and left-most pixels of the transformed object to top/left of the original object
382 | //TODO: This seems wrong in the calculations
383 | var sides = calc.sides();
384 |
385 | // Protect against an item that is already positioned
386 | var cssPosition = this.$elem.css('position');
387 | if (cssPosition == 'static') {
388 | cssPosition = 'relative';
389 | }
390 |
391 | //TODO: if the element is already positioned, we should attempt to respect it (somehow)
392 | //NOTE: we could preserve our offset top and left in an attr on the elem
393 | var pos = {top: 0, left: 0};
394 |
395 | // Approximates transform-origin, tx, and ty
396 | var css = {
397 | 'position': cssPosition,
398 | 'top': (offset.top + ty + sides.top + pos.top) + 'px',
399 | 'left': (offset.left + tx + sides.left + pos.left) + 'px',
400 | 'zoom': 1
401 | };
402 |
403 | this.$elem.css(css);
404 | }
405 | };
406 |
407 | /**
408 | * Ensure that values have the appropriate units on them
409 | * @param string func
410 | * @param Mixed value
411 | */
412 | function toPx(elem, val) {
413 | var parts = rfxnum.exec($.trim(val));
414 |
415 | if (parts[3] && parts[3] !== 'px') {
416 | var prop = 'paddingBottom',
417 | orig = $.style( elem, prop );
418 |
419 | $.style( elem, prop, val );
420 | val = cur( elem, prop );
421 | $.style( elem, prop, orig );
422 | return val;
423 | }
424 | return parseFloat( val );
425 | }
426 |
427 | function cur(elem, prop) {
428 | if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
429 | return elem[ prop ];
430 | }
431 |
432 | var r = parseFloat( $.css( elem, prop ) );
433 | return r && r > -10000 ? r : 0;
434 | }
435 | })(jQuery, this, this.document);
436 |
437 |
438 | ///////////////////////////////////////////////////////
439 | // Safe Outer Length
440 | ///////////////////////////////////////////////////////
441 | (function($, window, document, undefined) {
442 | $.extend($.transform.prototype, {
443 | /**
444 | * @param void
445 | * @return Number
446 | */
447 | safeOuterHeight: function() {
448 | return this.safeOuterLength('height');
449 | },
450 |
451 | /**
452 | * @param void
453 | * @return Number
454 | */
455 | safeOuterWidth: function() {
456 | return this.safeOuterLength('width');
457 | },
458 |
459 | /**
460 | * Returns reliable outer dimensions for an object that may have been transformed.
461 | * Only use this if the matrix isn't handy
462 | * @param String dim height or width
463 | * @return Number
464 | */
465 | safeOuterLength: function(dim) {
466 | var funcName = 'outer' + (dim == 'width' ? 'Width' : 'Height');
467 |
468 | if (!$.support.csstransforms && $.browser.msie) {
469 | // make the variables more generic
470 | dim = dim == 'width' ? 'width' : 'height';
471 |
472 | // if we're transforming and have a matrix; we can shortcut.
473 | // the true outerHeight is the transformed outerHeight divided by the ratio.
474 | // the ratio is equal to the height of a 1px by 1px box that has been transformed by the same matrix.
475 | if (this.applyingMatrix && !this[funcName] && this.matrix) {
476 | // calculate and return the correct size
477 | var calc = new $.matrix.calc(this.matrix, 1, 1),
478 | ratio = calc.offset(),
479 | length = this.$elem[funcName]() / ratio[dim];
480 | this[funcName] = length;
481 |
482 | return length;
483 | } else if (this.applyingMatrix && this[funcName]) {
484 | // return the cached calculation
485 | return this[funcName];
486 | }
487 |
488 | // map dimensions to box sides
489 | var side = {
490 | height: ['top', 'bottom'],
491 | width: ['left', 'right']
492 | };
493 |
494 | // setup some variables
495 | var elem = this.$elem[0],
496 | outerLen = parseFloat($.curCSS(elem, dim, true)), //TODO: this can be cached on animations that do not animate height/width
497 | boxSizingProp = this.boxSizingProperty,
498 | boxSizingValue = this.boxSizingValue;
499 |
500 | // IE6 && IE7 will never have a box-sizing property, so fake it
501 | if (!this.boxSizingProperty) {
502 | boxSizingProp = this.boxSizingProperty = _findBoxSizingProperty() || 'box-sizing';
503 | boxSizingValue = this.boxSizingValue = this.$elem.css(boxSizingProp) || 'content-box';
504 | }
505 |
506 | // return it immediately if we already know it
507 | if (this[funcName] && this[dim] == outerLen) {
508 | return this[funcName];
509 | } else {
510 | this[dim] = outerLen;
511 | }
512 |
513 | // add in the padding and border
514 | if (boxSizingProp && (boxSizingValue == 'padding-box' || boxSizingValue == 'content-box')) {
515 | outerLen += parseFloat($.curCSS(elem, 'padding-' + side[dim][0], true)) || 0 +
516 | parseFloat($.curCSS(elem, 'padding-' + side[dim][1], true)) || 0;
517 | }
518 | if (boxSizingProp && boxSizingValue == 'content-box') {
519 | outerLen += parseFloat($.curCSS(elem, 'border-' + side[dim][0] + '-width', true)) || 0 +
520 | parseFloat($.curCSS(elem, 'border-' + side[dim][1] + '-width', true)) || 0;
521 | }
522 |
523 | // remember and return the outerHeight
524 | this[funcName] = outerLen;
525 | return outerLen;
526 | }
527 | return this.$elem[funcName]();
528 | }
529 | });
530 |
531 | /**
532 | * Determine the correct property for checking the box-sizing property
533 | * @param void
534 | * @return string
535 | */
536 | var _boxSizingProperty = null;
537 | function _findBoxSizingProperty () {
538 | if (_boxSizingProperty) {
539 | return _boxSizingProperty;
540 | }
541 |
542 | var property = {
543 | boxSizing : 'box-sizing',
544 | MozBoxSizing : '-moz-box-sizing',
545 | WebkitBoxSizing : '-webkit-box-sizing',
546 | OBoxSizing : '-o-box-sizing'
547 | },
548 | elem = document.body;
549 |
550 | for (var p in property) {
551 | if (typeof elem.style[p] != 'undefined') {
552 | _boxSizingProperty = property[p];
553 | return _boxSizingProperty;
554 | }
555 | }
556 | return null;
557 | }
558 | })(jQuery, this, this.document);
559 |
560 |
--------------------------------------------------------------------------------
/dist/jquery.transform-0.9.3.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery 2d Transform v0.9.3
3 | * http://wiki.github.com/heygrady/transform/
4 | *
5 | * Copyright 2010, Grady Kuhnline
6 | * Dual licensed under the MIT or GPL Version 2 licenses.
7 | * http://jquery.org/license
8 | *
9 | * Date: Sat Dec 4 15:46:09 2010 -0800
10 | */
11 | (function(f,g,j,b){var h=/progid:DXImageTransform\.Microsoft\.Matrix\(.*?\)/,c=/^([\+\-]=)?([\d+.\-]+)(.*)$/,q=/%/;var d=j.createElement("modernizr"),e=d.style;function n(s){return parseFloat(s)}function l(){var s={transformProperty:"",MozTransform:"-moz-",WebkitTransform:"-webkit-",OTransform:"-o-",msTransform:"-ms-"};for(var t in s){if(typeof e[t]!="undefined"){return s[t]}}return null}function r(){if(typeof(g.Modernizr)!=="undefined"){return Modernizr.csstransforms}var t=["transformProperty","WebkitTransform","MozTransform","OTransform","msTransform"];for(var s in t){if(e[t[s]]!==b){return true}}}var a=l(),i=a!==null?a+"transform":false,k=a!==null?a+"transform-origin":false;f.support.csstransforms=r();if(a=="-ms-"){i="msTransform";k="msTransformOrigin"}f.extend({transform:function(s){s.transform=this;this.$elem=f(s);this.applyingMatrix=false;this.matrix=null;this.height=null;this.width=null;this.outerHeight=null;this.outerWidth=null;this.boxSizingValue=null;this.boxSizingProperty=null;this.attr=null;this.transformProperty=i;this.transformOriginProperty=k}});f.extend(f.transform,{funcs:["matrix","origin","reflect","reflectX","reflectXY","reflectY","rotate","scale","scaleX","scaleY","skew","skewX","skewY","translate","translateX","translateY"]});f.fn.transform=function(s,t){return this.each(function(){var u=this.transform||new f.transform(this);if(s){u.exec(s,t)}})};f.transform.prototype={exec:function(s,t){t=f.extend(true,{forceMatrix:false,preserve:false},t);this.attr=null;if(t.preserve){s=f.extend(true,this.getAttrs(true,true),s)}else{s=f.extend(true,{},s)}this.setAttrs(s);if(f.support.csstransforms&&!t.forceMatrix){return this.execFuncs(s)}else{if(f.browser.msie||(f.support.csstransforms&&t.forceMatrix)){return this.execMatrix(s)}}return false},execFuncs:function(t){var s=[];for(var u in t){if(u=="origin"){this[u].apply(this,f.isArray(t[u])?t[u]:[t[u]])}else{if(f.inArray(u,f.transform.funcs)!==-1){s.push(this.createTransformFunc(u,t[u]))}}}this.$elem.css(i,s.join(" "));return true},execMatrix:function(z){var C,x,t;var F=this.$elem[0],B=this;function A(N,M){if(q.test(N)){return parseFloat(N)/100*B["safeOuter"+(M?"Height":"Width")]()}return o(F,N)}var s=/translate[X|Y]?/,u=[];for(var v in z){switch(f.type(z[v])){case"array":t=z[v];break;case"string":t=f.map(z[v].split(","),f.trim);break;default:t=[z[v]]}if(f.matrix[v]){if(f.cssAngle[v]){t=f.map(t,f.angle.toDegree)}else{if(!f.cssNumber[v]){t=f.map(t,A)}else{t=f.map(t,n)}}x=f.matrix[v].apply(this,t);if(s.test(v)){u.push(x)}else{C=C?C.x(x):x}}else{if(v=="origin"){this[v].apply(this,t)}}}C=C||f.matrix.identity();f.each(u,function(M,N){C=C.x(N)});var K=parseFloat(C.e(1,1).toFixed(6)),I=parseFloat(C.e(2,1).toFixed(6)),H=parseFloat(C.e(1,2).toFixed(6)),G=parseFloat(C.e(2,2).toFixed(6)),L=C.rows===3?parseFloat(C.e(1,3).toFixed(6)):0,J=C.rows===3?parseFloat(C.e(2,3).toFixed(6)):0;if(f.support.csstransforms&&a==="-moz-"){this.$elem.css(i,"matrix("+K+", "+I+", "+H+", "+G+", "+L+"px, "+J+"px)")}else{if(f.support.csstransforms){this.$elem.css(i,"matrix("+K+", "+I+", "+H+", "+G+", "+L+", "+J+")")}else{if(f.browser.msie){var w=", FilterType='nearest neighbor'";var D=this.$elem[0].style;var E="progid:DXImageTransform.Microsoft.Matrix(M11="+K+", M12="+H+", M21="+I+", M22="+G+", sizingMethod='auto expand'"+w+")";var y=D.filter||f.curCSS(this.$elem[0],"filter")||"";D.filter=h.test(y)?y.replace(h,E):y?y+" "+E:E;this.applyingMatrix=true;this.matrix=C;this.fixPosition(C,L,J);this.applyingMatrix=false;this.matrix=null}}}return true},origin:function(s,t){if(f.support.csstransforms){if(typeof t==="undefined"){this.$elem.css(k,s)}else{this.$elem.css(k,s+" "+t)}return true}switch(s){case"left":s="0";break;case"right":s="100%";break;case"center":case b:s="50%"}switch(t){case"top":t="0";break;case"bottom":t="100%";break;case"center":case b:t="50%"}this.setAttr("origin",[q.test(s)?s:o(this.$elem[0],s)+"px",q.test(t)?t:o(this.$elem[0],t)+"px"]);return true},createTransformFunc:function(t,u){if(t.substr(0,7)==="reflect"){var s=u?f.matrix[t]():f.matrix.identity();return"matrix("+s.e(1,1)+", "+s.e(2,1)+", "+s.e(1,2)+", "+s.e(2,2)+", 0, 0)"}if(t=="matrix"){if(a==="-moz-"){u[4]=u[4]?u[4]+"px":0;u[5]=u[5]?u[5]+"px":0}}return t+"("+(f.isArray(u)?u.join(", "):u)+")"},fixPosition:function(B,y,x,D,s){var w=new f.matrix.calc(B,this.safeOuterHeight(),this.safeOuterWidth()),C=this.getAttr("origin");var v=w.originOffset(new f.matrix.V2(q.test(C[0])?parseFloat(C[0])/100*w.outerWidth:parseFloat(C[0]),q.test(C[1])?parseFloat(C[1])/100*w.outerHeight:parseFloat(C[1])));var t=w.sides();var u=this.$elem.css("position");if(u=="static"){u="relative"}var A={top:0,left:0};var z={position:u,top:(v.top+x+t.top+A.top)+"px",left:(v.left+y+t.left+A.left)+"px",zoom:1};this.$elem.css(z)}};function o(s,u){var t=c.exec(f.trim(u));if(t[3]&&t[3]!=="px"){var w="paddingBottom",v=f.style(s,w);f.style(s,w,u);u=p(s,w);f.style(s,w,v);return u}return parseFloat(u)}function p(t,u){if(t[u]!=null&&(!t.style||t.style[u]==null)){return t[u]}var s=parseFloat(f.css(t,u));return s&&s>-10000?s:0}})(jQuery,this,this.document);(function(d,c,a,f){d.extend(d.transform.prototype,{safeOuterHeight:function(){return this.safeOuterLength("height")},safeOuterWidth:function(){return this.safeOuterLength("width")},safeOuterLength:function(l){var p="outer"+(l=="width"?"Width":"Height");if(!d.support.csstransforms&&d.browser.msie){l=l=="width"?"width":"height";if(this.applyingMatrix&&!this[p]&&this.matrix){var k=new d.matrix.calc(this.matrix,1,1),n=k.offset(),g=this.$elem[p]()/n[l];this[p]=g;return g}else{if(this.applyingMatrix&&this[p]){return this[p]}}var o={height:["top","bottom"],width:["left","right"]};var h=this.$elem[0],j=parseFloat(d.curCSS(h,l,true)),q=this.boxSizingProperty,i=this.boxSizingValue;if(!this.boxSizingProperty){q=this.boxSizingProperty=e()||"box-sizing";i=this.boxSizingValue=this.$elem.css(q)||"content-box"}if(this[p]&&this[l]==j){return this[p]}else{this[l]=j}if(q&&(i=="padding-box"||i=="content-box")){j+=parseFloat(d.curCSS(h,"padding-"+o[l][0],true))||0+parseFloat(d.curCSS(h,"padding-"+o[l][1],true))||0}if(q&&i=="content-box"){j+=parseFloat(d.curCSS(h,"border-"+o[l][0]+"-width",true))||0+parseFloat(d.curCSS(h,"border-"+o[l][1]+"-width",true))||0}this[p]=j;return j}return this.$elem[p]()}});var b=null;function e(){if(b){return b}var h={boxSizing:"box-sizing",MozBoxSizing:"-moz-box-sizing",WebkitBoxSizing:"-webkit-box-sizing",OBoxSizing:"-o-box-sizing"},g=a.body;for(var i in h){if(typeof g.style[i]!="undefined"){b=h[i];return b}}return null}})(jQuery,this,this.document);(function(g,f,b,h){var d=/([\w\-]*?)\((.*?)\)/g,a="data-transform",e=/\s/,c=/,\s?/;g.extend(g.transform.prototype,{setAttrs:function(i){var j="",l;for(var k in i){l=i[k];if(g.isArray(l)){l=l.join(", ")}j+=" "+k+"("+l+")"}this.attr=g.trim(j);this.$elem.attr(a,this.attr)},setAttr:function(k,l){if(g.isArray(l)){l=l.join(", ")}var j=this.attr||this.$elem.attr(a);if(!j||j.indexOf(k)==-1){this.attr=g.trim(j+" "+k+"("+l+")");this.$elem.attr(a,this.attr)}else{var i=[],n;d.lastIndex=0;while(n=d.exec(j)){if(k==n[1]){i.push(k+"("+l+")")}else{i.push(n[0])}}this.attr=i.join(" ");this.$elem.attr(a,this.attr)}},getAttrs:function(){var j=this.attr||this.$elem.attr(a);if(!j){return{}}var i={},l,k;d.lastIndex=0;while((l=d.exec(j))!==null){if(l){k=l[2].split(c);i[l[1]]=k.length==1?k[0]:k}}return i},getAttr:function(j){var i=this.getAttrs();if(typeof i[j]!=="undefined"){return i[j]}if(j==="origin"&&g.support.csstransforms){return this.$elem.css(this.transformOriginProperty).split(e)}else{if(j==="origin"){return["50%","50%"]}}return g.cssDefault[j]||0}});if(typeof(g.cssAngle)=="undefined"){g.cssAngle={}}g.extend(g.cssAngle,{rotate:true,skew:true,skewX:true,skewY:true});if(typeof(g.cssDefault)=="undefined"){g.cssDefault={}}g.extend(g.cssDefault,{scale:[1,1],scaleX:1,scaleY:1,matrix:[1,0,0,1,0,0],origin:["50%","50%"],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]});if(typeof(g.cssMultipleValues)=="undefined"){g.cssMultipleValues={}}g.extend(g.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});g.extend(g.cssNumber,{matrix:true,reflect:true,reflectX:true,reflectXY:true,reflectY:true,scale:true,scaleX:true,scaleY:true});g.each(g.transform.funcs,function(j,k){g.cssHooks[k]={set:function(n,o){var l=n.transform||new g.transform(n),i={};i[k]=o;l.exec(i,{preserve:true})},get:function(n,l){var i=n.transform||new g.transform(n);return i.getAttr(k)}}});g.each(["reflect","reflectX","reflectXY","reflectY"],function(j,k){g.cssHooks[k].get=function(n,l){var i=n.transform||new g.transform(n);return i.getAttr("matrix")||g.cssDefault[k]}})})(jQuery,this,this.document);(function(e,g,h,c){var d=/^([+\-]=)?([\d+.\-]+)(.*)$/;var a=e.fn.animate;e.fn.animate=function(p,l,o,n){var k=e.speed(l,o,n),j=e.cssMultipleValues;k.complete=k.old;if(!e.isEmptyObject(p)){if(typeof k.original==="undefined"){k.original={}}e.each(p,function(s,u){if(j[s]||e.cssAngle[s]||(!e.cssNumber[s]&&e.inArray(s,e.transform.funcs)!==-1)){var t=null;if(jQuery.isArray(p[s])){var r=1,q=u.length;if(j[s]){r=(typeof j[s].length==="undefined"?j[s]:j[s].length)}if(q>r||(q-10000?j:0}var f=e.fx.prototype.custom;e.fx.prototype.custom=function(u,v,w){var y=e.cssMultipleValues[this.prop],p=e.cssAngle[this.prop];if(y||(!e.cssNumber[this.prop]&&e.inArray(this.prop,e.transform.funcs)!==-1)){this.values=[];if(!y){y=1}var x=this.options.original[this.prop],t=e(this.elem).css(this.prop),j=e.cssDefault[this.prop]||0;if(!e.isArray(t)){t=[t]}if(!e.isArray(x)){if(e.type(x)==="string"){x=x.split(",")}else{x=[x]}}var l=y.length||y,s=0;while(x.lengthi||h>i||k<1||h<1){return 0}return this.elements[(k-1)*j+h-1]},decompose:function(){var v=this.e(1,1),t=this.e(2,1),q=this.e(1,2),p=this.e(2,2),o=this.e(1,3),n=this.e(2,3);if(Math.abs(v*p-t*q)<0.01){return{rotate:0+"deg",skewX:0+"deg",scaleX:1,scaleY:1,translateX:0+"px",translateY:0+"px"}}var l=o,j=n;var u=Math.sqrt(v*v+t*t);v=v/u;t=t/u;var i=v*q+t*p;q-=v*i;p-=t*i;var s=Math.sqrt(q*q+p*p);q=q/s;p=p/s;i=i/s;if((v*p-t*q)<0){v=-v;t=-t;u=-u}var w=f.angle.radianToDegree;var h=w(Math.atan2(t,v));i=w(Math.atan(i));return{rotate:h+"deg",skewX:i+"deg",scaleX:u,scaleY:s,translateX:l+"px",translateY:j+"px"}}};f.extend(d.M2x2.prototype,c,{toM3x3:function(){var h=this.elements;return new d.M3x3(h[0],h[1],0,h[2],h[3],0,0,0,1)},x:function(j){var k=typeof(j.rows)==="undefined";if(!k&&j.rows==3){return this.toM3x3().x(j)}var i=this.elements,h=j.elements;if(k&&h.length==2){return new d.V2(i[0]*h[0]+i[1]*h[1],i[2]*h[0]+i[3]*h[1])}else{if(h.length==i.length){return new d.M2x2(i[0]*h[0]+i[1]*h[2],i[0]*h[1]+i[1]*h[3],i[2]*h[0]+i[3]*h[2],i[2]*h[1]+i[3]*h[3])}}return false},inverse:function(){var i=1/this.determinant(),h=this.elements;return new d.M2x2(i*h[3],i*-h[1],i*-h[2],i*h[0])},determinant:function(){var h=this.elements;return h[0]*h[3]-h[1]*h[2]}});f.extend(d.M3x3.prototype,c,{x:function(j){var k=typeof(j.rows)==="undefined";if(!k&&j.rows<3){j=j.toM3x3()}var i=this.elements,h=j.elements;if(k&&h.length==3){return new d.V3(i[0]*h[0]+i[1]*h[1]+i[2]*h[2],i[3]*h[0]+i[4]*h[1]+i[5]*h[2],i[6]*h[0]+i[7]*h[1]+i[8]*h[2])}else{if(h.length==i.length){return new d.M3x3(i[0]*h[0]+i[1]*h[3]+i[2]*h[6],i[0]*h[1]+i[1]*h[4]+i[2]*h[7],i[0]*h[2]+i[1]*h[5]+i[2]*h[8],i[3]*h[0]+i[4]*h[3]+i[5]*h[6],i[3]*h[1]+i[4]*h[4]+i[5]*h[7],i[3]*h[2]+i[4]*h[5]+i[5]*h[8],i[6]*h[0]+i[7]*h[3]+i[8]*h[6],i[6]*h[1]+i[7]*h[4]+i[8]*h[7],i[6]*h[2]+i[7]*h[5]+i[8]*h[8])}}return false},inverse:function(){var i=1/this.determinant(),h=this.elements;return new d.M3x3(i*(h[8]*h[4]-h[7]*h[5]),i*(-(h[8]*h[1]-h[7]*h[2])),i*(h[5]*h[1]-h[4]*h[2]),i*(-(h[8]*h[3]-h[6]*h[5])),i*(h[8]*h[0]-h[6]*h[2]),i*(-(h[5]*h[0]-h[3]*h[2])),i*(h[7]*h[3]-h[6]*h[4]),i*(-(h[7]*h[0]-h[6]*h[1])),i*(h[4]*h[0]-h[3]*h[1]))},determinant:function(){var h=this.elements;return h[0]*(h[8]*h[4]-h[7]*h[5])-h[3]*(h[8]*h[1]-h[7]*h[2])+h[6]*(h[5]*h[1]-h[4]*h[2])}});var a={e:function(h){return this.elements[h-1]}};f.extend(d.V2.prototype,a);f.extend(d.V3.prototype,a)})(jQuery,this,this.document);(function(c,b,a,d){if(typeof(c.matrix)=="undefined"){c.extend({matrix:{}})}c.extend(c.matrix,{calc:function(e,f,g){this.matrix=e;this.outerHeight=f;this.outerWidth=g}});c.matrix.calc.prototype={coord:function(e,i,h){h=typeof(h)!=="undefined"?h:0;var g=this.matrix,f;switch(g.rows){case 2:f=g.x(new c.matrix.V2(e,i));break;case 3:f=g.x(new c.matrix.V3(e,i,h));break}return f},corners:function(e,h){var f=!(typeof(e)!=="undefined"||typeof(h)!=="undefined"),g;if(!this.c||!f){h=h||this.outerHeight;e=e||this.outerWidth;g={tl:this.coord(0,0),bl:this.coord(0,h),tr:this.coord(e,0),br:this.coord(e,h)}}else{g=this.c}if(f){this.c=g}return g},sides:function(e){var f=e||this.corners();return{top:Math.min(f.tl.e(2),f.tr.e(2),f.br.e(2),f.bl.e(2)),bottom:Math.max(f.tl.e(2),f.tr.e(2),f.br.e(2),f.bl.e(2)),left:Math.min(f.tl.e(1),f.tr.e(1),f.br.e(1),f.bl.e(1)),right:Math.max(f.tl.e(1),f.tr.e(1),f.br.e(1),f.bl.e(1))}},offset:function(e){var f=this.sides(e);return{height:Math.abs(f.bottom-f.top),width:Math.abs(f.right-f.left)}},area:function(e){var h=e||this.corners();var g={x:h.tr.e(1)-h.tl.e(1)+h.br.e(1)-h.bl.e(1),y:h.tr.e(2)-h.tl.e(2)+h.br.e(2)-h.bl.e(2)},f={x:h.bl.e(1)-h.tl.e(1)+h.br.e(1)-h.tr.e(1),y:h.bl.e(2)-h.tl.e(2)+h.br.e(2)-h.tr.e(2)};return 0.25*Math.abs(g.e(1)*f.e(2)-g.e(2)*f.e(1))},nonAffinity:function(){var f=this.sides(),g=f.top-f.bottom,e=f.left-f.right;return parseFloat(parseFloat(Math.abs((Math.pow(g,2)+Math.pow(e,2))/(f.top*f.bottom+f.left*f.right))).toFixed(8))},originOffset:function(h,g){h=h?h:new c.matrix.V2(this.outerWidth*0.5,this.outerHeight*0.5);g=g?g:new c.matrix.V2(0,0);var e=this.coord(h.e(1),h.e(2));var f=this.coord(g.e(1),g.e(2));return{top:(f.e(2)-g.e(2))-(e.e(2)-h.e(2)),left:(f.e(1)-g.e(1))-(e.e(1)-h.e(1))}}}})(jQuery,this,this.document);(function(e,d,a,f){if(typeof(e.matrix)=="undefined"){e.extend({matrix:{}})}var c=e.matrix,g=c.M2x2,b=c.M3x3;e.extend(c,{identity:function(k){k=k||2;var l=k*k,n=new Array(l),j=k+1;for(var h=0;h
2 |
3 |
4 |
5 |
6 |
8 |
9 |
10 | test3
11 |
12 |
13 |
14 |
37 |
38 |
39 |
40 |
41 |
Testing
42 |
43 |
44 |
: |
45 |
46 |
47 |
Transform
48 |
49 |
50 |
51 |
52 |
Transform
53 |
54 |
55 |
56 |
57 |
Transform
58 |
59 |
60 |
61 |
62 |

63 |
64 |
65 |

66 |
67 |
68 |

69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
715 |
716 |
--------------------------------------------------------------------------------
/dist/jquery.transform.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery 2d Transform v0.9.3
3 | * http://wiki.github.com/heygrady/transform/
4 | *
5 | * Copyright 2010, Grady Kuhnline
6 | * Dual licensed under the MIT or GPL Version 2 licenses.
7 | * http://jquery.org/license
8 | *
9 | * Date: Sat Dec 4 15:46:09 2010 -0800
10 | */
11 | ///////////////////////////////////////////////////////
12 | // Transform
13 | ///////////////////////////////////////////////////////
14 | (function($, window, document, undefined) {
15 | /**
16 | * @var Regex identify the matrix filter in IE
17 | */
18 | var rmatrix = /progid:DXImageTransform\.Microsoft\.Matrix\(.*?\)/,
19 | rfxnum = /^([\+\-]=)?([\d+.\-]+)(.*)$/,
20 | rperc = /%/;
21 |
22 | // Steal some code from Modernizr
23 | var m = document.createElement( 'modernizr' ),
24 | m_style = m.style;
25 |
26 | function stripUnits(arg) {
27 | return parseFloat(arg);
28 | }
29 |
30 | /**
31 | * Find the prefix that this browser uses
32 | */
33 | function getVendorPrefix() {
34 | var property = {
35 | transformProperty : '',
36 | MozTransform : '-moz-',
37 | WebkitTransform : '-webkit-',
38 | OTransform : '-o-',
39 | msTransform : '-ms-'
40 | };
41 | for (var p in property) {
42 | if (typeof m_style[p] != 'undefined') {
43 | return property[p];
44 | }
45 | }
46 | return null;
47 | }
48 |
49 | function supportCssTransforms() {
50 | if (typeof(window.Modernizr) !== 'undefined') {
51 | return Modernizr.csstransforms;
52 | }
53 |
54 | var props = [ 'transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform' ];
55 | for ( var i in props ) {
56 | if ( m_style[ props[i] ] !== undefined ) {
57 | return true;
58 | }
59 | }
60 | }
61 |
62 | // Capture some basic properties
63 | var vendorPrefix = getVendorPrefix(),
64 | transformProperty = vendorPrefix !== null ? vendorPrefix + 'transform' : false,
65 | transformOriginProperty = vendorPrefix !== null ? vendorPrefix + 'transform-origin' : false;
66 |
67 | // store support in the jQuery Support object
68 | $.support.csstransforms = supportCssTransforms();
69 |
70 | // IE9 public preview 6 requires the DOM names
71 | if (vendorPrefix == '-ms-') {
72 | transformProperty = 'msTransform';
73 | transformOriginProperty = 'msTransformOrigin';
74 | }
75 |
76 | /**
77 | * Class for creating cross-browser transformations
78 | * @constructor
79 | */
80 | $.extend({
81 | transform: function(elem) {
82 | // Cache the transform object on the element itself
83 | elem.transform = this;
84 |
85 | /**
86 | * The element we're working with
87 | * @var jQueryCollection
88 | */
89 | this.$elem = $(elem);
90 |
91 | /**
92 | * Remember the matrix we're applying to help the safeOuterLength func
93 | */
94 | this.applyingMatrix = false;
95 | this.matrix = null;
96 |
97 | /**
98 | * Remember the css height and width to save time
99 | * This is only really used in IE
100 | * @var Number
101 | */
102 | this.height = null;
103 | this.width = null;
104 | this.outerHeight = null;
105 | this.outerWidth = null;
106 |
107 | /**
108 | * We need to know the box-sizing in IE for building the outerHeight and outerWidth
109 | * @var string
110 | */
111 | this.boxSizingValue = null;
112 | this.boxSizingProperty = null;
113 |
114 | this.attr = null;
115 | this.transformProperty = transformProperty;
116 | this.transformOriginProperty = transformOriginProperty;
117 | }
118 | });
119 |
120 | $.extend($.transform, {
121 | /**
122 | * @var Array list of all valid transform functions
123 | */
124 | funcs: ['matrix', 'origin', 'reflect', 'reflectX', 'reflectXY', 'reflectY', 'rotate', 'scale', 'scaleX', 'scaleY', 'skew', 'skewX', 'skewY', 'translate', 'translateX', 'translateY']
125 | });
126 |
127 | /**
128 | * Create Transform as a jQuery plugin
129 | * @param Object funcs
130 | * @param Object options
131 | */
132 | $.fn.transform = function(funcs, options) {
133 | return this.each(function() {
134 | var t = this.transform || new $.transform(this);
135 | if (funcs) {
136 | t.exec(funcs, options);
137 | }
138 | });
139 | };
140 |
141 | $.transform.prototype = {
142 | /**
143 | * Applies all of the transformations
144 | * @param Object funcs
145 | * @param Object options
146 | * forceMatrix - uses the matrix in all browsers
147 | * preserve - tries to preserve the values from previous runs
148 | */
149 | exec: function(funcs, options) {
150 | // extend options
151 | options = $.extend(true, {
152 | forceMatrix: false,
153 | preserve: false
154 | }, options);
155 |
156 | // preserve the funcs from the previous run
157 | this.attr = null;
158 | if (options.preserve) {
159 | funcs = $.extend(true, this.getAttrs(true, true), funcs);
160 | } else {
161 | funcs = $.extend(true, {}, funcs); // copy the object to prevent weirdness
162 | }
163 |
164 | // Record the custom attributes on the element itself
165 | this.setAttrs(funcs);
166 |
167 | // apply the funcs
168 | if ($.support.csstransforms && !options.forceMatrix) {
169 | // CSS3 is supported
170 | return this.execFuncs(funcs);
171 | } else if ($.browser.msie || ($.support.csstransforms && options.forceMatrix)) {
172 | // Internet Explorer or Forced matrix
173 | return this.execMatrix(funcs);
174 | }
175 | return false;
176 | },
177 |
178 | /**
179 | * Applies all of the transformations as functions
180 | * @param Object funcs
181 | */
182 | execFuncs: function(funcs) {
183 | var values = [];
184 |
185 | // construct a CSS string
186 | for (var func in funcs) {
187 | // handle origin separately
188 | if (func == 'origin') {
189 | this[func].apply(this, $.isArray(funcs[func]) ? funcs[func] : [funcs[func]]);
190 | } else if ($.inArray(func, $.transform.funcs) !== -1) {
191 | values.push(this.createTransformFunc(func, funcs[func]));
192 | }
193 | }
194 | this.$elem.css(transformProperty, values.join(' '));
195 | return true;
196 | },
197 |
198 | /**
199 | * Applies all of the transformations as a matrix
200 | * @param Object funcs
201 | */
202 | execMatrix: function(funcs) {
203 | var matrix,
204 | tempMatrix,
205 | args;
206 |
207 | var elem = this.$elem[0],
208 | _this = this;
209 | function normalPixels(val, i) {
210 | if (rperc.test(val)) {
211 | // this really only applies to translation
212 | return parseFloat(val) / 100 * _this['safeOuter' + (i ? 'Height' : 'Width')]();
213 | }
214 | return toPx(elem, val);
215 | }
216 |
217 | var rtranslate = /translate[X|Y]?/,
218 | trans = [];
219 |
220 | for (var func in funcs) {
221 | switch ($.type(funcs[func])) {
222 | case 'array': args = funcs[func]; break;
223 | case 'string': args = $.map(funcs[func].split(','), $.trim); break;
224 | default: args = [funcs[func]];
225 | }
226 |
227 | if ($.matrix[func]) {
228 |
229 | if ($.cssAngle[func]) {
230 | // normalize on degrees
231 | args = $.map(args, $.angle.toDegree);
232 | } else if (!$.cssNumber[func]) {
233 | // normalize to pixels
234 | args = $.map(args, normalPixels);
235 | } else {
236 | // strip units
237 | args = $.map(args, stripUnits);
238 | }
239 |
240 | tempMatrix = $.matrix[func].apply(this, args);
241 | if (rtranslate.test(func)) {
242 | //defer translation
243 | trans.push(tempMatrix);
244 | } else {
245 | matrix = matrix ? matrix.x(tempMatrix) : tempMatrix;
246 | }
247 | } else if (func == 'origin') {
248 | this[func].apply(this, args);
249 | }
250 | }
251 |
252 | // check that we have a matrix
253 | matrix = matrix || $.matrix.identity();
254 |
255 | // Apply translation
256 | $.each(trans, function(i, val) { matrix = matrix.x(val); });
257 |
258 | // pull out the relevant values
259 | var a = parseFloat(matrix.e(1,1).toFixed(6)),
260 | b = parseFloat(matrix.e(2,1).toFixed(6)),
261 | c = parseFloat(matrix.e(1,2).toFixed(6)),
262 | d = parseFloat(matrix.e(2,2).toFixed(6)),
263 | tx = matrix.rows === 3 ? parseFloat(matrix.e(1,3).toFixed(6)) : 0,
264 | ty = matrix.rows === 3 ? parseFloat(matrix.e(2,3).toFixed(6)) : 0;
265 |
266 | //apply the transform to the element
267 | if ($.support.csstransforms && vendorPrefix === '-moz-') {
268 | // -moz-
269 | this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + 'px, ' + ty + 'px)');
270 | } else if ($.support.csstransforms) {
271 | // -webkit, -o-, w3c
272 | // NOTE: WebKit and Opera don't allow units on the translate variables
273 | this.$elem.css(transformProperty, 'matrix(' + a + ', ' + b + ', ' + c + ', ' + d + ', ' + tx + ', ' + ty + ')');
274 | } else if ($.browser.msie) {
275 | // IE requires the special transform Filter
276 |
277 | //TODO: Use Nearest Neighbor during animation FilterType=\'nearest neighbor\'
278 | var filterType = ', FilterType=\'nearest neighbor\''; //bilinear
279 | var style = this.$elem[0].style;
280 | var matrixFilter = 'progid:DXImageTransform.Microsoft.Matrix(' +
281 | 'M11=' + a + ', M12=' + c + ', M21=' + b + ', M22=' + d +
282 | ', sizingMethod=\'auto expand\'' + filterType + ')';
283 | var filter = style.filter || $.curCSS( this.$elem[0], "filter" ) || "";
284 | style.filter = rmatrix.test(filter) ? filter.replace(rmatrix, matrixFilter) : filter ? filter + ' ' + matrixFilter : matrixFilter;
285 |
286 | // Let's know that we're applying post matrix fixes and the height/width will be static for a bit
287 | this.applyingMatrix = true;
288 | this.matrix = matrix;
289 |
290 | // IE can't set the origin or translate directly
291 | this.fixPosition(matrix, tx, ty);
292 |
293 | this.applyingMatrix = false;
294 | this.matrix = null;
295 | }
296 | return true;
297 | },
298 |
299 | /**
300 | * Sets the transform-origin
301 | * This really needs to be percentages
302 | * @param Number x length
303 | * @param Number y length
304 | */
305 | origin: function(x, y) {
306 | // use CSS in supported browsers
307 | if ($.support.csstransforms) {
308 | if (typeof y === 'undefined') {
309 | this.$elem.css(transformOriginProperty, x);
310 | } else {
311 | this.$elem.css(transformOriginProperty, x + ' ' + y);
312 | }
313 | return true;
314 | }
315 |
316 | // correct for keyword lengths
317 | switch (x) {
318 | case 'left': x = '0'; break;
319 | case 'right': x = '100%'; break;
320 | case 'center': // no break
321 | case undefined: x = '50%';
322 | }
323 | switch (y) {
324 | case 'top': y = '0'; break;
325 | case 'bottom': y = '100%'; break;
326 | case 'center': // no break
327 | case undefined: y = '50%'; //TODO: does this work?
328 | }
329 |
330 | // store mixed values with units, assumed pixels
331 | this.setAttr('origin', [
332 | rperc.test(x) ? x : toPx(this.$elem[0], x) + 'px',
333 | rperc.test(y) ? y : toPx(this.$elem[0], y) + 'px'
334 | ]);
335 | //console.log(this.getAttr('origin'));
336 | return true;
337 | },
338 |
339 | /**
340 | * Create a function suitable for a CSS value
341 | * @param string func
342 | * @param Mixed value
343 | */
344 | createTransformFunc: function(func, value) {
345 | if (func.substr(0, 7) === 'reflect') {
346 | // let's fake reflection, false value
347 | // falsey sets an identity matrix
348 | var m = value ? $.matrix[func]() : $.matrix.identity();
349 | return 'matrix(' + m.e(1,1) + ', ' + m.e(2,1) + ', ' + m.e(1,2) + ', ' + m.e(2,2) + ', 0, 0)';
350 | }
351 |
352 | //value = _correctUnits(func, value);
353 |
354 | if (func == 'matrix') {
355 | if (vendorPrefix === '-moz-') {
356 | value[4] = value[4] ? value[4] + 'px' : 0;
357 | value[5] = value[5] ? value[5] + 'px' : 0;
358 | }
359 | }
360 | return func + '(' + ($.isArray(value) ? value.join(', ') : value) + ')';
361 | },
362 |
363 | /**
364 | * @param Matrix matrix
365 | * @param Number tx
366 | * @param Number ty
367 | * @param Number height
368 | * @param Number width
369 | */
370 | fixPosition: function(matrix, tx, ty, height, width) {
371 | // now we need to fix it!
372 | var calc = new $.matrix.calc(matrix, this.safeOuterHeight(), this.safeOuterWidth()),
373 | origin = this.getAttr('origin'); // mixed percentages and px
374 |
375 | // translate a 0, 0 origin to the current origin
376 | var offset = calc.originOffset(new $.matrix.V2(
377 | rperc.test(origin[0]) ? parseFloat(origin[0])/100*calc.outerWidth : parseFloat(origin[0]),
378 | rperc.test(origin[1]) ? parseFloat(origin[1])/100*calc.outerHeight : parseFloat(origin[1])
379 | ));
380 |
381 | // IE glues the top-most and left-most pixels of the transformed object to top/left of the original object
382 | //TODO: This seems wrong in the calculations
383 | var sides = calc.sides();
384 |
385 | // Protect against an item that is already positioned
386 | var cssPosition = this.$elem.css('position');
387 | if (cssPosition == 'static') {
388 | cssPosition = 'relative';
389 | }
390 |
391 | //TODO: if the element is already positioned, we should attempt to respect it (somehow)
392 | //NOTE: we could preserve our offset top and left in an attr on the elem
393 | var pos = {top: 0, left: 0};
394 |
395 | // Approximates transform-origin, tx, and ty
396 | var css = {
397 | 'position': cssPosition,
398 | 'top': (offset.top + ty + sides.top + pos.top) + 'px',
399 | 'left': (offset.left + tx + sides.left + pos.left) + 'px',
400 | 'zoom': 1
401 | };
402 |
403 | this.$elem.css(css);
404 | }
405 | };
406 |
407 | /**
408 | * Ensure that values have the appropriate units on them
409 | * @param string func
410 | * @param Mixed value
411 | */
412 | function toPx(elem, val) {
413 | var parts = rfxnum.exec($.trim(val));
414 |
415 | if (parts[3] && parts[3] !== 'px') {
416 | var prop = 'paddingBottom',
417 | orig = $.style( elem, prop );
418 |
419 | $.style( elem, prop, val );
420 | val = cur( elem, prop );
421 | $.style( elem, prop, orig );
422 | return val;
423 | }
424 | return parseFloat( val );
425 | }
426 |
427 | function cur(elem, prop) {
428 | if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
429 | return elem[ prop ];
430 | }
431 |
432 | var r = parseFloat( $.css( elem, prop ) );
433 | return r && r > -10000 ? r : 0;
434 | }
435 | })(jQuery, this, this.document);
436 |
437 |
438 | ///////////////////////////////////////////////////////
439 | // Safe Outer Length
440 | ///////////////////////////////////////////////////////
441 | (function($, window, document, undefined) {
442 | $.extend($.transform.prototype, {
443 | /**
444 | * @param void
445 | * @return Number
446 | */
447 | safeOuterHeight: function() {
448 | return this.safeOuterLength('height');
449 | },
450 |
451 | /**
452 | * @param void
453 | * @return Number
454 | */
455 | safeOuterWidth: function() {
456 | return this.safeOuterLength('width');
457 | },
458 |
459 | /**
460 | * Returns reliable outer dimensions for an object that may have been transformed.
461 | * Only use this if the matrix isn't handy
462 | * @param String dim height or width
463 | * @return Number
464 | */
465 | safeOuterLength: function(dim) {
466 | var funcName = 'outer' + (dim == 'width' ? 'Width' : 'Height');
467 |
468 | if (!$.support.csstransforms && $.browser.msie) {
469 | // make the variables more generic
470 | dim = dim == 'width' ? 'width' : 'height';
471 |
472 | // if we're transforming and have a matrix; we can shortcut.
473 | // the true outerHeight is the transformed outerHeight divided by the ratio.
474 | // the ratio is equal to the height of a 1px by 1px box that has been transformed by the same matrix.
475 | if (this.applyingMatrix && !this[funcName] && this.matrix) {
476 | // calculate and return the correct size
477 | var calc = new $.matrix.calc(this.matrix, 1, 1),
478 | ratio = calc.offset(),
479 | length = this.$elem[funcName]() / ratio[dim];
480 | this[funcName] = length;
481 |
482 | return length;
483 | } else if (this.applyingMatrix && this[funcName]) {
484 | // return the cached calculation
485 | return this[funcName];
486 | }
487 |
488 | // map dimensions to box sides
489 | var side = {
490 | height: ['top', 'bottom'],
491 | width: ['left', 'right']
492 | };
493 |
494 | // setup some variables
495 | var elem = this.$elem[0],
496 | outerLen = parseFloat($.curCSS(elem, dim, true)), //TODO: this can be cached on animations that do not animate height/width
497 | boxSizingProp = this.boxSizingProperty,
498 | boxSizingValue = this.boxSizingValue;
499 |
500 | // IE6 && IE7 will never have a box-sizing property, so fake it
501 | if (!this.boxSizingProperty) {
502 | boxSizingProp = this.boxSizingProperty = _findBoxSizingProperty() || 'box-sizing';
503 | boxSizingValue = this.boxSizingValue = this.$elem.css(boxSizingProp) || 'content-box';
504 | }
505 |
506 | // return it immediately if we already know it
507 | if (this[funcName] && this[dim] == outerLen) {
508 | return this[funcName];
509 | } else {
510 | this[dim] = outerLen;
511 | }
512 |
513 | // add in the padding and border
514 | if (boxSizingProp && (boxSizingValue == 'padding-box' || boxSizingValue == 'content-box')) {
515 | outerLen += parseFloat($.curCSS(elem, 'padding-' + side[dim][0], true)) || 0 +
516 | parseFloat($.curCSS(elem, 'padding-' + side[dim][1], true)) || 0;
517 | }
518 | if (boxSizingProp && boxSizingValue == 'content-box') {
519 | outerLen += parseFloat($.curCSS(elem, 'border-' + side[dim][0] + '-width', true)) || 0 +
520 | parseFloat($.curCSS(elem, 'border-' + side[dim][1] + '-width', true)) || 0;
521 | }
522 |
523 | // remember and return the outerHeight
524 | this[funcName] = outerLen;
525 | return outerLen;
526 | }
527 | return this.$elem[funcName]();
528 | }
529 | });
530 |
531 | /**
532 | * Determine the correct property for checking the box-sizing property
533 | * @param void
534 | * @return string
535 | */
536 | var _boxSizingProperty = null;
537 | function _findBoxSizingProperty () {
538 | if (_boxSizingProperty) {
539 | return _boxSizingProperty;
540 | }
541 |
542 | var property = {
543 | boxSizing : 'box-sizing',
544 | MozBoxSizing : '-moz-box-sizing',
545 | WebkitBoxSizing : '-webkit-box-sizing',
546 | OBoxSizing : '-o-box-sizing'
547 | },
548 | elem = document.body;
549 |
550 | for (var p in property) {
551 | if (typeof elem.style[p] != 'undefined') {
552 | _boxSizingProperty = property[p];
553 | return _boxSizingProperty;
554 | }
555 | }
556 | return null;
557 | }
558 | })(jQuery, this, this.document);
559 |
560 |
561 | ///////////////////////////////////////////////////////
562 | // Attr
563 | ///////////////////////////////////////////////////////
564 | (function($, window, document, undefined) {
565 | var rfuncvalue = /([\w\-]*?)\((.*?)\)/g, // with values
566 | attr = 'data-transform',
567 | rspace = /\s/,
568 | rcspace = /,\s?/;
569 |
570 | $.extend($.transform.prototype, {
571 | /**
572 | * This overrides all of the attributes
573 | * @param Object funcs a list of transform functions to store on this element
574 | * @return void
575 | */
576 | setAttrs: function(funcs) {
577 | var string = '',
578 | value;
579 | for (var func in funcs) {
580 | value = funcs[func];
581 | if ($.isArray(value)) {
582 | value = value.join(', ');
583 | }
584 | string += ' ' + func + '(' + value + ')';
585 | }
586 | this.attr = $.trim(string);
587 | this.$elem.attr(attr, this.attr);
588 | },
589 |
590 | /**
591 | * This sets only a specific atribute
592 | * @param string func name of a transform function
593 | * @param mixed value with proper units
594 | * @return void
595 | */
596 | setAttr: function(func, value) {
597 | // stringify the value
598 | if ($.isArray(value)) {
599 | value = value.join(', ');
600 | }
601 |
602 | // pull from a local variable to look it up
603 | var transform = this.attr || this.$elem.attr(attr);
604 | if (!transform || transform.indexOf(func) == -1) {
605 | // we don't have any existing values, save it
606 | // we don't have this function yet, save it
607 | this.attr = $.trim(transform + ' ' + func + '(' + value + ')');
608 | this.$elem.attr(attr, this.attr);
609 | } else {
610 | // replace the existing value
611 | var funcs = [], parts;
612 |
613 | // regex split
614 | rfuncvalue.lastIndex = 0; // reset the regex pointer
615 | while (parts = rfuncvalue.exec(transform)) {
616 | if (func == parts[1]) {
617 | funcs.push(func + '(' + value + ')');
618 | } else {
619 | funcs.push(parts[0]);
620 | }
621 | }
622 | this.attr = funcs.join(' ');
623 | this.$elem.attr(attr, this.attr);
624 | }
625 | },
626 |
627 | /**
628 | * @return Object
629 | */
630 | getAttrs: function() {
631 | var transform = this.attr || this.$elem.attr(attr);
632 | if (!transform) {
633 | // We don't have any existing values, return empty object
634 | return {};
635 | }
636 |
637 | // replace the existing value
638 | var attrs = {}, parts, value;
639 |
640 | rfuncvalue.lastIndex = 0; // reset the regex pointer
641 | while ((parts = rfuncvalue.exec(transform)) !== null) {
642 | if (parts) {
643 | value = parts[2].split(rcspace);
644 | attrs[parts[1]] = value.length == 1 ? value[0] : value;
645 | }
646 | }
647 | return attrs;
648 | },
649 |
650 | /**
651 | * @param String func
652 | * @return mixed
653 | */
654 | getAttr: function(func) {
655 | var attrs = this.getAttrs();
656 | if (typeof attrs[func] !== 'undefined') {
657 | return attrs[func];
658 | }
659 |
660 | //TODO: move the origin to a function
661 | if (func === 'origin' && $.support.csstransforms) {
662 | // supported browsers return percentages always
663 | return this.$elem.css(this.transformOriginProperty).split(rspace);
664 | } else if (func === 'origin') {
665 | // just force IE to also return a percentage
666 | return ['50%', '50%'];
667 | }
668 |
669 | return $.cssDefault[func] || 0;
670 | }
671 | });
672 |
673 | // Define
674 | if (typeof($.cssAngle) == 'undefined') {
675 | $.cssAngle = {};
676 | }
677 | $.extend($.cssAngle, {
678 | rotate: true,
679 | skew: true,
680 | skewX: true,
681 | skewY: true
682 | });
683 |
684 | // Define default values
685 | if (typeof($.cssDefault) == 'undefined') {
686 | $.cssDefault = {};
687 | }
688 |
689 | $.extend($.cssDefault, {
690 | scale: [1, 1],
691 | scaleX: 1,
692 | scaleY: 1,
693 | matrix: [1, 0, 0, 1, 0, 0],
694 | origin: ['50%', '50%'], // TODO: allow this to be a function, like get
695 | reflect: [1, 0, 0, 1, 0, 0],
696 | reflectX: [1, 0, 0, 1, 0, 0],
697 | reflectXY: [1, 0, 0, 1, 0, 0],
698 | reflectY: [1, 0, 0, 1, 0, 0]
699 | });
700 |
701 | // Define functons with multiple values
702 | if (typeof($.cssMultipleValues) == 'undefined') {
703 | $.cssMultipleValues = {};
704 | }
705 | $.extend($.cssMultipleValues, {
706 | matrix: 6,
707 | origin: {
708 | length: 2,
709 | duplicate: true
710 | },
711 | reflect: 6,
712 | reflectX: 6,
713 | reflectXY: 6,
714 | reflectY: 6,
715 | scale: {
716 | length: 2,
717 | duplicate: true
718 | },
719 | skew: 2,
720 | translate: 2
721 | });
722 |
723 | // specify unitless funcs
724 | $.extend($.cssNumber, {
725 | matrix: true,
726 | reflect: true,
727 | reflectX: true,
728 | reflectXY: true,
729 | reflectY: true,
730 | scale: true,
731 | scaleX: true,
732 | scaleY: true
733 | });
734 |
735 | // override all of the css functions
736 | $.each($.transform.funcs, function(i, func) {
737 | $.cssHooks[func] = {
738 | set: function(elem, value) {
739 | var transform = elem.transform || new $.transform(elem),
740 | funcs = {};
741 | funcs[func] = value;
742 | transform.exec(funcs, {preserve: true});
743 | },
744 | get: function(elem, computed) {
745 | var transform = elem.transform || new $.transform(elem);
746 | return transform.getAttr(func);
747 | }
748 | };
749 | });
750 |
751 | // Support Reflection animation better by returning a matrix
752 | $.each(['reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
753 | $.cssHooks[func].get = function(elem, computed) {
754 | var transform = elem.transform || new $.transform(elem);
755 | return transform.getAttr('matrix') || $.cssDefault[func];
756 | };
757 | });
758 | })(jQuery, this, this.document);
759 | ///////////////////////////////////////////////////////
760 | // Animation
761 | ///////////////////////////////////////////////////////
762 | (function($, window, document, undefined) {
763 | /**
764 | * @var Regex looks for units on a string
765 | */
766 | var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
767 |
768 | /**
769 | * Doctors prop values in the event that they contain spaces
770 | * @param Object prop
771 | * @param String speed
772 | * @param String easing
773 | * @param Function callback
774 | * @return bool
775 | */
776 | var _animate = $.fn.animate;
777 | $.fn.animate = function( prop, speed, easing, callback ) {
778 | var optall = $.speed(speed, easing, callback),
779 | mv = $.cssMultipleValues;
780 |
781 | // Speed always creates a complete function that must be reset
782 | optall.complete = optall.old;
783 |
784 | // Capture multiple values
785 | if (!$.isEmptyObject(prop)) {
786 | if (typeof optall.original === 'undefined') {
787 | optall.original = {};
788 | }
789 | $.each( prop, function( name, val ) {
790 | if (mv[name]
791 | || $.cssAngle[name]
792 | || (!$.cssNumber[name] && $.inArray(name, $.transform.funcs) !== -1)) {
793 |
794 | // Handle special easing
795 | var specialEasing = null;
796 | if (jQuery.isArray(prop[name])) {
797 | var mvlen = 1, len = val.length;
798 | if (mv[name]) {
799 | mvlen = (typeof mv[name].length === 'undefined' ? mv[name] : mv[name].length);
800 | }
801 | if ( len > mvlen
802 | || (len < mvlen && len == 2)
803 | || (len == 2 && mvlen == 2 && isNaN(parseFloat(val[len - 1])))) {
804 |
805 | specialEasing = val[len - 1];
806 | val.splice(len - 1, 1);
807 | }
808 | }
809 |
810 | // Store the original values onto the optall
811 | optall.original[name] = val.toString();
812 |
813 | // reduce to a unitless number (to trick animate)
814 | prop[name] = parseFloat(val);
815 | }
816 | } );
817 | }
818 |
819 | //NOTE: we edited prop above to trick animate
820 | //NOTE: we pre-convert to an optall so we can doctor it
821 | return _animate.apply(this, [arguments[0], optall]);
822 | };
823 |
824 | var prop = 'paddingBottom';
825 | function cur(elem, prop) {
826 | if ( elem[prop] != null && (!elem.style || elem.style[prop] == null) ) {
827 | //return elem[ prop ];
828 | }
829 |
830 | var r = parseFloat( $.css( elem, prop ) );
831 | return r && r > -10000 ? r : 0;
832 | }
833 |
834 | var _custom = $.fx.prototype.custom;
835 | $.fx.prototype.custom = function(from, to, unit) {
836 | var multiple = $.cssMultipleValues[this.prop],
837 | angle = $.cssAngle[this.prop];
838 |
839 | //TODO: simply check for the existence of CSS Hooks?
840 | if (multiple || (!$.cssNumber[this.prop] && $.inArray(this.prop, $.transform.funcs) !== -1)) {
841 | this.values = [];
842 |
843 | if (!multiple) {
844 | multiple = 1;
845 | }
846 |
847 | // Pull out the known values
848 | var values = this.options.original[this.prop],
849 | currentValues = $(this.elem).css(this.prop),
850 | defaultValues = $.cssDefault[this.prop] || 0;
851 |
852 | // make sure the current css value is an array
853 | if (!$.isArray(currentValues)) {
854 | currentValues = [currentValues];
855 | }
856 |
857 | // make sure the new values are an array
858 | if (!$.isArray(values)) {
859 | if ($.type(values) === 'string') {
860 | values = values.split(',');
861 | } else {
862 | values = [values];
863 | }
864 | }
865 |
866 | // make sure we have enough new values
867 | var length = multiple.length || multiple, i = 0;
868 | while (values.length < length) {
869 | values.push(multiple.duplicate ? values[0] : defaultValues[i] || 0);
870 | i++;
871 | }
872 |
873 | // calculate a start, end and unit for each new value
874 | var start, parts, end, //unit,
875 | fx = this,
876 | transform = fx.elem.transform;
877 | orig = $.style(fx.elem, prop);
878 |
879 | $.each(values, function(i, val) {
880 | // find a sensible start value
881 | if (currentValues[i]) {
882 | start = currentValues[i];
883 | } else if (defaultValues[i] && !multiple.duplicate) {
884 | start = defaultValues[i];
885 | } else if (multiple.duplicate) {
886 | start = currentValues[0];
887 | } else {
888 | start = 0;
889 | }
890 |
891 | // Force the correct unit on the start
892 | if (angle) {
893 | start = $.angle.toDegree(start);
894 | } else if (!$.cssNumber[fx.prop]) {
895 | parts = rfxnum.exec($.trim(start));
896 | if (parts[3] && parts[3] !== 'px') {
897 | if (parts[3] === '%') {
898 | start = parseFloat( parts[2] ) / 100 * transform['safeOuter' + (i ? 'Height' : 'Width')]();
899 | } else {
900 | $.style( fx.elem, prop, start);
901 | start = cur(fx.elem, prop);
902 | $.style( fx.elem, prop, orig);
903 | }
904 | }
905 | }
906 | start = parseFloat(start);
907 |
908 | // parse the value with a regex
909 | parts = rfxnum.exec($.trim(val));
910 |
911 | if (parts) {
912 | // we found a sensible value and unit
913 | end = parseFloat( parts[2] );
914 | unit = parts[3] || "px"; //TODO: change to an appropriate default unit
915 |
916 | if (angle) {
917 | end = $.angle.toDegree(end + unit);
918 | unit = 'deg';
919 | } else if (!$.cssNumber[fx.prop] && unit === '%') {
920 | start = (start / transform['safeOuter' + (i ? 'Height' : 'Width')]()) * 100;
921 | } else if (!$.cssNumber[fx.prop] && unit !== 'px') {
922 | $.style( fx.elem, prop, (end || 1) + unit);
923 | start = ((end || 1) / cur(fx.elem, prop)) * start;
924 | $.style( fx.elem, prop, orig);
925 | }
926 |
927 | // If a +=/-= token was provided, we're doing a relative animation
928 | if (parts[1]) {
929 | end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
930 | }
931 | } else {
932 | // I don't know when this would happen
933 | end = val;
934 | unit = '';
935 | }
936 |
937 | // Save the values
938 | fx.values.push({
939 | start: start,
940 | end: end,
941 | unit: unit
942 | });
943 | });
944 | }
945 | return _custom.apply(this, arguments);
946 | };
947 |
948 | /**
949 | * Animates a multi value attribute
950 | * @param Object fx
951 | * @return null
952 | */
953 | $.fx.multipleValueStep = {
954 | _default: function(fx) {
955 | $.each(fx.values, function(i, val) {
956 | fx.values[i].now = val.start + ((val.end - val.start) * fx.pos);
957 | });
958 | }
959 | };
960 | $.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
961 | $.fx.multipleValueStep[func] = function(fx) {
962 | var d = fx.decomposed,
963 | $m = $.matrix;
964 | m = $m.identity();
965 |
966 | d.now = {};
967 |
968 | // increment each part of the decomposition and recompose it
969 | $.each(d.start, function(k) {
970 | // calculate the current value
971 | d.now[k] = parseFloat(d.start[k]) + ((parseFloat(d.end[k]) - parseFloat(d.start[k])) * fx.pos);
972 |
973 | // skip functions that won't affect the transform
974 | if (((k === 'scaleX' || k === 'scaleY') && d.now[k] === 1) ||
975 | (k !== 'scaleX' && k !== 'scaleY' && d.now[k] === 0)) {
976 | return true;
977 | }
978 |
979 | // calculating
980 | m = m.x($m[k](d.now[k]));
981 | });
982 |
983 | // save the correct matrix values for the value of now
984 | var val;
985 | $.each(fx.values, function(i) {
986 | switch (i) {
987 | case 0: val = parseFloat(m.e(1, 1).toFixed(6)); break;
988 | case 1: val = parseFloat(m.e(2, 1).toFixed(6)); break;
989 | case 2: val = parseFloat(m.e(1, 2).toFixed(6)); break;
990 | case 3: val = parseFloat(m.e(2, 2).toFixed(6)); break;
991 | case 4: val = parseFloat(m.e(1, 3).toFixed(6)); break;
992 | case 5: val = parseFloat(m.e(2, 3).toFixed(6)); break;
993 | }
994 | fx.values[i].now = val;
995 | });
996 | };
997 | });
998 | /**
999 | * Step for animating tranformations
1000 | */
1001 | $.each($.transform.funcs, function(i, func) {
1002 | $.fx.step[func] = function(fx) {
1003 | var transform = fx.elem.transform || new $.transform(fx.elem),
1004 | funcs = {};
1005 |
1006 | if ($.cssMultipleValues[func] || (!$.cssNumber[func] && $.inArray(func, $.transform.funcs) !== -1)) {
1007 | ($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
1008 | funcs[fx.prop] = [];
1009 | $.each(fx.values, function(i, val) {
1010 | funcs[fx.prop].push(val.now + ($.cssNumber[fx.prop] ? '' : val.unit));
1011 | });
1012 | } else {
1013 | funcs[fx.prop] = fx.now + ($.cssNumber[fx.prop] ? '' : fx.unit);
1014 | }
1015 |
1016 | transform.exec(funcs, {preserve: true});
1017 | };
1018 | });
1019 |
1020 | // Support matrix animation
1021 | $.each(['matrix', 'reflect', 'reflectX', 'reflectXY', 'reflectY'], function(i, func) {
1022 | $.fx.step[func] = function(fx) {
1023 | var transform = fx.elem.transform || new $.transform(fx.elem),
1024 | funcs = {};
1025 |
1026 | if (!fx.initialized) {
1027 | fx.initialized = true;
1028 |
1029 | // Reflections need a sensible end value set
1030 | if (func !== 'matrix') {
1031 | var values = $.matrix[func]().elements;
1032 | var val;
1033 | $.each(fx.values, function(i) {
1034 | switch (i) {
1035 | case 0: val = values[0]; break;
1036 | case 1: val = values[2]; break;
1037 | case 2: val = values[1]; break;
1038 | case 3: val = values[3]; break;
1039 | default: val = 0;
1040 | }
1041 | fx.values[i].end = val;
1042 | });
1043 | }
1044 |
1045 | // Decompose the start and end
1046 | fx.decomposed = {};
1047 | var v = fx.values;
1048 |
1049 | fx.decomposed.start = $.matrix.matrix(v[0].start, v[1].start, v[2].start, v[3].start, v[4].start, v[5].start).decompose();
1050 | fx.decomposed.end = $.matrix.matrix(v[0].end, v[1].end, v[2].end, v[3].end, v[4].end, v[5].end).decompose();
1051 | }
1052 |
1053 | ($.fx.multipleValueStep[fx.prop] || $.fx.multipleValueStep._default)(fx);
1054 | funcs.matrix = [];
1055 | $.each(fx.values, function(i, val) {
1056 | funcs.matrix.push(val.now);
1057 | });
1058 |
1059 | transform.exec(funcs, {preserve: true});
1060 | };
1061 | });
1062 | })(jQuery, this, this.document);
1063 | ///////////////////////////////////////////////////////
1064 | // Angle
1065 | ///////////////////////////////////////////////////////
1066 | (function($, window, document, undefined) {
1067 | /**
1068 | * Converting a radian to a degree
1069 | * @const
1070 | */
1071 | var RAD_DEG = 180/Math.PI;
1072 |
1073 | /**
1074 | * Converting a radian to a grad
1075 | * @const
1076 | */
1077 | var RAD_GRAD = 200/Math.PI;
1078 |
1079 | /**
1080 | * Converting a degree to a radian
1081 | * @const
1082 | */
1083 | var DEG_RAD = Math.PI/180;
1084 |
1085 | /**
1086 | * Converting a degree to a grad
1087 | * @const
1088 | */
1089 | var DEG_GRAD = 2/1.8;
1090 |
1091 | /**
1092 | * Converting a grad to a degree
1093 | * @const
1094 | */
1095 | var GRAD_DEG = 0.9;
1096 |
1097 | /**
1098 | * Converting a grad to a radian
1099 | * @const
1100 | */
1101 | var GRAD_RAD = Math.PI/200;
1102 |
1103 |
1104 | var rfxnum = /^([+\-]=)?([\d+.\-]+)(.*)$/;
1105 |
1106 | /**
1107 | * Functions for converting angles
1108 | * @var Object
1109 | */
1110 | $.extend({
1111 | angle: {
1112 | /**
1113 | * available units for an angle
1114 | * @var Regex
1115 | */
1116 | runit: /(deg|g?rad)/,
1117 |
1118 | /**
1119 | * Convert a radian into a degree
1120 | * @param Number rad
1121 | * @return Number
1122 | */
1123 | radianToDegree: function(rad) {
1124 | return rad * RAD_DEG;
1125 | },
1126 |
1127 | /**
1128 | * Convert a radian into a degree
1129 | * @param Number rad
1130 | * @return Number
1131 | */
1132 | radianToGrad: function(rad) {
1133 | return rad * RAD_GRAD;
1134 | },
1135 |
1136 | /**
1137 | * Convert a degree into a radian
1138 | * @param Number deg
1139 | * @return Number
1140 | */
1141 | degreeToRadian: function(deg) {
1142 | return deg * DEG_RAD;
1143 | },
1144 |
1145 | /**
1146 | * Convert a degree into a radian
1147 | * @param Number deg
1148 | * @return Number
1149 | */
1150 | degreeToGrad: function(deg) {
1151 | return deg * DEG_GRAD;
1152 | },
1153 |
1154 | /**
1155 | * Convert a grad into a degree
1156 | * @param Number grad
1157 | * @return Number
1158 | */
1159 | gradToDegree: function(grad) {
1160 | return grad * GRAD_DEG;
1161 | },
1162 |
1163 | /**
1164 | * Convert a grad into a radian
1165 | * @param Number grad
1166 | * @return Number
1167 | */
1168 | gradToRadian: function(grad) {
1169 | return grad * GRAD_RAD;
1170 | },
1171 |
1172 | /**
1173 | * Convert an angle with a unit to a degree
1174 | * @param String val angle with a unit
1175 | * @return Number
1176 | */
1177 | toDegree: function (val) {
1178 | var parts = rfxnum.exec(val);
1179 | if (parts) {
1180 | val = parseFloat( parts[2] );
1181 | switch (parts[3] || 'deg') {
1182 | case 'grad':
1183 | val = $.angle.gradToDegree(val);
1184 | break;
1185 | case 'rad':
1186 | val = $.angle.radianToDegree(val);
1187 | break;
1188 | }
1189 | return val;
1190 | }
1191 | return 0;
1192 | }
1193 | }
1194 | });
1195 | })(jQuery, this, this.document);
1196 | ///////////////////////////////////////////////////////
1197 | // Matrix
1198 | ///////////////////////////////////////////////////////
1199 | (function($, window, document, undefined) {
1200 | /**
1201 | * Matrix object for creating matrices relevant for 2d Transformations
1202 | * @var Object
1203 | */
1204 | if (typeof($.matrix) == 'undefined') {
1205 | $.extend({
1206 | matrix: {}
1207 | });
1208 | }
1209 | var $m = $.matrix;
1210 |
1211 | $.extend( $m, {
1212 | /**
1213 | * A 2-value vector
1214 | * @param Number x
1215 | * @param Number y
1216 | * @constructor
1217 | */
1218 | V2: function(x, y){
1219 | if ($.isArray(arguments[0])) {
1220 | this.elements = arguments[0].slice(0, 2);
1221 | } else {
1222 | this.elements = [x, y];
1223 | }
1224 | this.length = 2;
1225 | },
1226 |
1227 | /**
1228 | * A 2-value vector
1229 | * @param Number x
1230 | * @param Number y
1231 | * @param Number z
1232 | * @constructor
1233 | */
1234 | V3: function(x, y, z){
1235 | if ($.isArray(arguments[0])) {
1236 | this.elements = arguments[0].slice(0, 3);
1237 | } else {
1238 | this.elements = [x, y, z];
1239 | }
1240 | this.length = 3;
1241 | },
1242 |
1243 | /**
1244 | * A 2x2 Matrix, useful for 2D-transformations without translations
1245 | * @param Number mn
1246 | * @constructor
1247 | */
1248 | M2x2: function(m11, m12, m21, m22) {
1249 | if ($.isArray(arguments[0])) {
1250 | this.elements = arguments[0].slice(0, 4);
1251 | } else {
1252 | this.elements = Array.prototype.slice.call(arguments).slice(0, 4);
1253 | }
1254 | this.rows = 2;
1255 | this.cols = 2;
1256 | },
1257 |
1258 | /**
1259 | * A 3x3 Matrix, useful for 3D-transformations without translations
1260 | * @param Number mn
1261 | * @constructor
1262 | */
1263 | M3x3: function(m11, m12, m13, m21, m22, m23, m31, m32, m33) {
1264 | if ($.isArray(arguments[0])) {
1265 | this.elements = arguments[0].slice(0, 9);
1266 | } else {
1267 | this.elements = Array.prototype.slice.call(arguments).slice(0, 9);
1268 | }
1269 | this.rows = 3;
1270 | this.cols = 3;
1271 | }
1272 | });
1273 |
1274 | /** generic matrix prototype */
1275 | var Matrix = {
1276 | /**
1277 | * Return a specific element from the matrix
1278 | * @param Number row where 1 is the 0th row
1279 | * @param Number col where 1 is the 0th column
1280 | * @return Number
1281 | */
1282 | e: function(row, col) {
1283 | var rows = this.rows,
1284 | cols = this.cols;
1285 |
1286 | // return 0 on nonsense rows and columns
1287 | if (row > rows || col > rows || row < 1 || col < 1) {
1288 | return 0;
1289 | }
1290 |
1291 | return this.elements[(row - 1) * cols + col - 1];
1292 | },
1293 |
1294 | /**
1295 | * Taken from Zoomooz
1296 | * https://github.com/jaukia/zoomooz/blob/c7a37b9a65a06ba730bd66391bbd6fe8e55d3a49/js/jquery.zoomooz.js
1297 | */
1298 | decompose: function() {
1299 | var a = this.e(1, 1),
1300 | b = this.e(2, 1),
1301 | c = this.e(1, 2),
1302 | d = this.e(2, 2),
1303 | e = this.e(1, 3),
1304 | f = this.e(2, 3);
1305 |
1306 | // In case the matrix can't be decomposed
1307 | if (Math.abs(a * d - b * c) < 0.01) {
1308 | return {
1309 | rotate: 0 + 'deg',
1310 | skewX: 0 + 'deg',
1311 | scaleX: 1,
1312 | scaleY: 1,
1313 | translateX: 0 + 'px',
1314 | translateY: 0 + 'px'
1315 | };
1316 | }
1317 |
1318 | // Translate is easy
1319 | var tx = e, ty = f;
1320 |
1321 | // factor out the X scale
1322 | var sx = Math.sqrt(a * a + b * b);
1323 | a = a/sx;
1324 | b = b/sx;
1325 |
1326 | // factor out the skew
1327 | var k = a * c + b * d;
1328 | c -= a * k;
1329 | d -= b * k;
1330 |
1331 | // factor out the Y scale
1332 | var sy = Math.sqrt(c * c + d * d);
1333 | c = c / sy;
1334 | d = d / sy;
1335 | k = k / sy;
1336 |
1337 | // account for negative scale
1338 | if ((a * d - b * c) < 0.0) {
1339 | a = -a;
1340 | b = -b;
1341 | //c = -c; // accomplishes nothing to negate it
1342 | //d = -d; // accomplishes nothing to negate it
1343 | sx = -sx;
1344 | //sy = -sy //Scale Y shouldn't ever be negated
1345 | }
1346 |
1347 | // calculate the rotation angle and skew angle
1348 | var rad2deg = $.angle.radianToDegree;
1349 | var r = rad2deg(Math.atan2(b, a));
1350 | k = rad2deg(Math.atan(k));
1351 |
1352 | return {
1353 | rotate: r + 'deg',
1354 | skewX: k + 'deg',
1355 | scaleX: sx,
1356 | scaleY: sy,
1357 | translateX: tx + 'px',
1358 | translateY: ty + 'px'
1359 | };
1360 | }
1361 | };
1362 |
1363 | /** Extend all of the matrix types with the same prototype */
1364 | $.extend($m.M2x2.prototype, Matrix, {
1365 | toM3x3: function() {
1366 | var a = this.elements;
1367 | return new $m.M3x3(
1368 | a[0], a[1], 0,
1369 | a[2], a[3], 0,
1370 | 0, 0, 1
1371 | );
1372 | },
1373 |
1374 | /**
1375 | * Multiply a 2x2 matrix by a similar matrix or a vector
1376 | * @param M2x2 | V2 matrix
1377 | * @return M2x2 | V2
1378 | */
1379 | x: function(matrix) {
1380 | var isVector = typeof(matrix.rows) === 'undefined';
1381 |
1382 | // Ensure the right-sized matrix
1383 | if (!isVector && matrix.rows == 3) {
1384 | return this.toM3x3().x(matrix);
1385 | }
1386 |
1387 | var a = this.elements,
1388 | b = matrix.elements;
1389 |
1390 | if (isVector && b.length == 2) {
1391 | // b is actually a vector
1392 | return new $m.V2(
1393 | a[0] * b[0] + a[1] * b[1],
1394 | a[2] * b[0] + a[3] * b[1]
1395 | );
1396 | } else if (b.length == a.length) {
1397 | // b is a 2x2 matrix
1398 | return new $m.M2x2(
1399 | a[0] * b[0] + a[1] * b[2],
1400 | a[0] * b[1] + a[1] * b[3],
1401 |
1402 | a[2] * b[0] + a[3] * b[2],
1403 | a[2] * b[1] + a[3] * b[3]
1404 | );
1405 | }
1406 | return false; // fail
1407 | },
1408 |
1409 | /**
1410 | * Generates an inverse of the current matrix
1411 | * @param void
1412 | * @return M2x2
1413 | * @link http://www.dr-lex.be/random/matrix_inv.html
1414 | */
1415 | inverse: function() {
1416 | var d = 1/this.determinant(),
1417 | a = this.elements;
1418 | return new $m.M2x2(
1419 | d * a[3], d * -a[1],
1420 | d * -a[2], d * a[0]
1421 | );
1422 | },
1423 |
1424 | /**
1425 | * Calculates the determinant of the current matrix
1426 | * @param void
1427 | * @return Number
1428 | * @link http://www.dr-lex.be/random/matrix_inv.html
1429 | */
1430 | determinant: function() {
1431 | var a = this.elements;
1432 | return a[0] * a[3] - a[1] * a[2];
1433 | }
1434 | });
1435 |
1436 | $.extend($m.M3x3.prototype, Matrix, {
1437 | /**
1438 | * Multiply a 3x3 matrix by a similar matrix or a vector
1439 | * @param M3x3 | V3 matrix
1440 | * @return M3x3 | V3
1441 | */
1442 | x: function(matrix) {
1443 | var isVector = typeof(matrix.rows) === 'undefined';
1444 |
1445 | // Ensure the right-sized matrix
1446 | if (!isVector && matrix.rows < 3) {
1447 | matrix = matrix.toM3x3();
1448 | }
1449 |
1450 | var a = this.elements,
1451 | b = matrix.elements;
1452 |
1453 | if (isVector && b.length == 3) {
1454 | // b is actually a vector
1455 | return new $m.V3(
1456 | a[0] * b[0] + a[1] * b[1] + a[2] * b[2],
1457 | a[3] * b[0] + a[4] * b[1] + a[5] * b[2],
1458 | a[6] * b[0] + a[7] * b[1] + a[8] * b[2]
1459 | );
1460 | } else if (b.length == a.length) {
1461 | // b is a 3x3 matrix
1462 | return new $m.M3x3(
1463 | a[0] * b[0] + a[1] * b[3] + a[2] * b[6],
1464 | a[0] * b[1] + a[1] * b[4] + a[2] * b[7],
1465 | a[0] * b[2] + a[1] * b[5] + a[2] * b[8],
1466 |
1467 | a[3] * b[0] + a[4] * b[3] + a[5] * b[6],
1468 | a[3] * b[1] + a[4] * b[4] + a[5] * b[7],
1469 | a[3] * b[2] + a[4] * b[5] + a[5] * b[8],
1470 |
1471 | a[6] * b[0] + a[7] * b[3] + a[8] * b[6],
1472 | a[6] * b[1] + a[7] * b[4] + a[8] * b[7],
1473 | a[6] * b[2] + a[7] * b[5] + a[8] * b[8]
1474 | );
1475 | }
1476 | return false; // fail
1477 | },
1478 |
1479 | /**
1480 | * Generates an inverse of the current matrix
1481 | * @param void
1482 | * @return M3x3
1483 | * @link http://www.dr-lex.be/random/matrix_inv.html
1484 | */
1485 | inverse: function() {
1486 | var d = 1/this.determinant(),
1487 | a = this.elements;
1488 | return new $m.M3x3(
1489 | d * ( a[8] * a[4] - a[7] * a[5]),
1490 | d * (-(a[8] * a[1] - a[7] * a[2])),
1491 | d * ( a[5] * a[1] - a[4] * a[2]),
1492 |
1493 | d * (-(a[8] * a[3] - a[6] * a[5])),
1494 | d * ( a[8] * a[0] - a[6] * a[2]),
1495 | d * (-(a[5] * a[0] - a[3] * a[2])),
1496 |
1497 | d * ( a[7] * a[3] - a[6] * a[4]),
1498 | d * (-(a[7] * a[0] - a[6] * a[1])),
1499 | d * ( a[4] * a[0] - a[3] * a[1])
1500 | );
1501 | },
1502 |
1503 | /**
1504 | * Calculates the determinant of the current matrix
1505 | * @param void
1506 | * @return Number
1507 | * @link http://www.dr-lex.be/random/matrix_inv.html
1508 | */
1509 | determinant: function() {
1510 | var a = this.elements;
1511 | 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]);
1512 | }
1513 | });
1514 |
1515 | /** generic vector prototype */
1516 | var Vector = {
1517 | /**
1518 | * Return a specific element from the vector
1519 | * @param Number i where 1 is the 0th value
1520 | * @return Number
1521 | */
1522 | e: function(i) {
1523 | return this.elements[i - 1];
1524 | }
1525 | };
1526 |
1527 | /** Extend all of the vector types with the same prototype */
1528 | $.extend($m.V2.prototype, Vector);
1529 | $.extend($m.V3.prototype, Vector);
1530 | })(jQuery, this, this.document);
1531 | ///////////////////////////////////////////////////////
1532 | // Matrix Calculations
1533 | ///////////////////////////////////////////////////////
1534 | (function($, window, document, undefined) {
1535 | /**
1536 | * Matrix object for creating matrices relevant for 2d Transformations
1537 | * @var Object
1538 | */
1539 | if (typeof($.matrix) == 'undefined') {
1540 | $.extend({
1541 | matrix: {}
1542 | });
1543 | }
1544 |
1545 | $.extend( $.matrix, {
1546 | /**
1547 | * Class for calculating coordinates on a matrix
1548 | * @param Matrix matrix
1549 | * @param Number outerHeight
1550 | * @param Number outerWidth
1551 | * @constructor
1552 | */
1553 | calc: function(matrix, outerHeight, outerWidth) {
1554 | /**
1555 | * @var Matrix
1556 | */
1557 | this.matrix = matrix;
1558 |
1559 | /**
1560 | * @var Number
1561 | */
1562 | this.outerHeight = outerHeight;
1563 |
1564 | /**
1565 | * @var Number
1566 | */
1567 | this.outerWidth = outerWidth;
1568 | }
1569 | });
1570 |
1571 | $.matrix.calc.prototype = {
1572 | /**
1573 | * Calculate a coord on the new object
1574 | * @return Object
1575 | */
1576 | coord: function(x, y, z) {
1577 | //default z and w
1578 | z = typeof(z) !== 'undefined' ? z : 0;
1579 |
1580 | var matrix = this.matrix,
1581 | vector;
1582 |
1583 | switch (matrix.rows) {
1584 | case 2:
1585 | vector = matrix.x(new $.matrix.V2(x, y));
1586 | break;
1587 | case 3:
1588 | vector = matrix.x(new $.matrix.V3(x, y, z));
1589 | break;
1590 | }
1591 |
1592 | return vector;
1593 | },
1594 |
1595 | /**
1596 | * Calculate the corners of the new object
1597 | * @return Object
1598 | */
1599 | corners: function(x, y) {
1600 | // Try to save the corners if this is called a lot
1601 | var save = !(typeof(x) !=='undefined' || typeof(y) !=='undefined'),
1602 | c;
1603 | if (!this.c || !save) {
1604 | y = y || this.outerHeight;
1605 | x = x || this.outerWidth;
1606 |
1607 | c = {
1608 | tl: this.coord(0, 0),
1609 | bl: this.coord(0, y),
1610 | tr: this.coord(x, 0),
1611 | br: this.coord(x, y)
1612 | };
1613 | } else {
1614 | c = this.c;
1615 | }
1616 |
1617 | if (save) {
1618 | this.c = c;
1619 | }
1620 | return c;
1621 | },
1622 |
1623 | /**
1624 | * Calculate the sides of the new object
1625 | * @return Object
1626 | */
1627 | sides: function(corners) {
1628 | // The corners of the box
1629 | var c = corners || this.corners();
1630 |
1631 | return {
1632 | top: Math.min(c.tl.e(2), c.tr.e(2), c.br.e(2), c.bl.e(2)),
1633 | bottom: Math.max(c.tl.e(2), c.tr.e(2), c.br.e(2), c.bl.e(2)),
1634 | left: Math.min(c.tl.e(1), c.tr.e(1), c.br.e(1), c.bl.e(1)),
1635 | right: Math.max(c.tl.e(1), c.tr.e(1), c.br.e(1), c.bl.e(1))
1636 | };
1637 | },
1638 |
1639 | /**
1640 | * Calculate the offset of the new object
1641 | * @return Object
1642 | */
1643 | offset: function(corners) {
1644 | // The corners of the box
1645 | var s = this.sides(corners);
1646 |
1647 | // return size
1648 | return {
1649 | height: Math.abs(s.bottom - s.top),
1650 | width: Math.abs(s.right - s.left)
1651 | };
1652 | },
1653 |
1654 | /**
1655 | * Calculate the area of the new object
1656 | * @return Number
1657 | * @link http://en.wikipedia.org/wiki/Quadrilateral#Area_of_a_convex_quadrilateral
1658 | */
1659 | area: function(corners) {
1660 | // The corners of the box
1661 | var c = corners || this.corners();
1662 |
1663 | // calculate the two diagonal vectors
1664 | var v1 = {
1665 | x: c.tr.e(1) - c.tl.e(1) + c.br.e(1) - c.bl.e(1),
1666 | y: c.tr.e(2) - c.tl.e(2) + c.br.e(2) - c.bl.e(2)
1667 | },
1668 | v2 = {
1669 | x: c.bl.e(1) - c.tl.e(1) + c.br.e(1) - c.tr.e(1),
1670 | y: c.bl.e(2) - c.tl.e(2) + c.br.e(2) - c.tr.e(2)
1671 | };
1672 |
1673 | return 0.25 * Math.abs(v1.e(1) * v2.e(2) - v1.e(2) * v2.e(1));
1674 | },
1675 |
1676 | /**
1677 | * Calculate the non-affinity of the new object
1678 | * @return Number
1679 | */
1680 | nonAffinity: function() {
1681 | // The corners of the box
1682 | var sides = this.sides(),
1683 | xDiff = sides.top - sides.bottom,
1684 | yDiff = sides.left - sides.right;
1685 |
1686 | return parseFloat(parseFloat(Math.abs(
1687 | (Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) /
1688 | (sides.top * sides.bottom + sides.left * sides.right)
1689 | )).toFixed(8));
1690 | },
1691 |
1692 | /**
1693 | * Calculate a proper top and left for IE
1694 | * @param Object toOrigin
1695 | * @param Object fromOrigin
1696 | * @return Object
1697 | */
1698 | originOffset: function(toOrigin, fromOrigin) {
1699 | // the origin to translate to
1700 | toOrigin = toOrigin ? toOrigin : new $.matrix.V2(
1701 | this.outerWidth * 0.5,
1702 | this.outerHeight * 0.5
1703 | );
1704 |
1705 | // the origin to translate from (IE has a fixed origin of 0, 0)
1706 | fromOrigin = fromOrigin ? fromOrigin : new $.matrix.V2(
1707 | 0,
1708 | 0
1709 | );
1710 |
1711 | // transform the origins
1712 | var toCenter = this.coord(toOrigin.e(1), toOrigin.e(2));
1713 | var fromCenter = this.coord(fromOrigin.e(1), fromOrigin.e(2));
1714 |
1715 | // return the offset
1716 | return {
1717 | top: (fromCenter.e(2) - fromOrigin.e(2)) - (toCenter.e(2) - toOrigin.e(2)),
1718 | left: (fromCenter.e(1) - fromOrigin.e(1)) - (toCenter.e(1) - toOrigin.e(1))
1719 | };
1720 | }
1721 | };
1722 | })(jQuery, this, this.document);
1723 | ///////////////////////////////////////////////////////
1724 | // 2d Matrix Functions
1725 | ///////////////////////////////////////////////////////
1726 | (function($, window, document, undefined) {
1727 | /**
1728 | * Matrix object for creating matrices relevant for 2d Transformations
1729 | * @var Object
1730 | */
1731 | if (typeof($.matrix) == 'undefined') {
1732 | $.extend({
1733 | matrix: {}
1734 | });
1735 | }
1736 | var $m = $.matrix,
1737 | $m2x2 = $m.M2x2,
1738 | $m3x3 = $m.M3x3;
1739 |
1740 | $.extend( $m, {
1741 | /**
1742 | * Identity matrix
1743 | * @param Number size
1744 | * @return Matrix
1745 | */
1746 | identity: function(size) {
1747 | size = size || 2;
1748 | var length = size * size,
1749 | elements = new Array(length),
1750 | mod = size + 1;
1751 | for (var i = 0; i < length; i++) {
1752 | elements[i] = (i % mod) === 0 ? 1 : 0;
1753 | }
1754 | return new $m['M'+size+'x'+size](elements);
1755 | },
1756 |
1757 | /**
1758 | * Matrix
1759 | * @return Matrix
1760 | */
1761 | matrix: function() {
1762 | var args = Array.prototype.slice.call(arguments);
1763 | // arguments are in column-major order
1764 | switch (arguments.length) {
1765 | case 4:
1766 | return new $m2x2(
1767 | args[0], args[2],
1768 | args[1], args[3]
1769 | );
1770 | case 6:
1771 | return new $m3x3(
1772 | args[0], args[2], args[4],
1773 | args[1], args[3], args[5],
1774 | 0, 0, 1
1775 | );
1776 | }
1777 | },
1778 |
1779 | /**
1780 | * Reflect (same as rotate(180))
1781 | * @return Matrix
1782 | */
1783 | reflect: function() {
1784 | return new $m2x2(
1785 | -1, 0,
1786 | 0, -1
1787 | );
1788 | },
1789 |
1790 | /**
1791 | * Reflect across the x-axis (mirrored upside down)
1792 | * @return Matrix
1793 | */
1794 | reflectX: function() {
1795 | return new $m2x2(
1796 | 1, 0,
1797 | 0, -1
1798 | );
1799 | },
1800 |
1801 | /**
1802 | * Reflect by swapping x an y (same as reflectX + rotate(-90))
1803 | * @return Matrix
1804 | */
1805 | reflectXY: function() {
1806 | return new $m2x2(
1807 | 0, 1,
1808 | 1, 0
1809 | );
1810 | },
1811 |
1812 | /**
1813 | * Reflect across the y-axis (mirrored)
1814 | * @return Matrix
1815 | */
1816 | reflectY: function() {
1817 | return new $m2x2(
1818 | -1, 0,
1819 | 0, 1
1820 | );
1821 | },
1822 |
1823 | /**
1824 | * Rotates around the origin
1825 | * @param Number deg
1826 | * @return Matrix
1827 | * @link http://www.w3.org/TR/SVG/coords.html#RotationDefined
1828 | */
1829 | rotate: function(deg) {
1830 | //TODO: detect units
1831 | var rad = $.angle.degreeToRadian(deg),
1832 | costheta = Math.cos(rad),
1833 | sintheta = Math.sin(rad);
1834 |
1835 | var a = costheta,
1836 | b = sintheta,
1837 | c = -sintheta,
1838 | d = costheta;
1839 |
1840 | return new $m2x2(
1841 | a, c,
1842 | b, d
1843 | );
1844 | },
1845 |
1846 | /**
1847 | * Scale
1848 | * @param Number sx
1849 | * @param Number sy
1850 | * @return Matrix
1851 | * @link http://www.w3.org/TR/SVG/coords.html#ScalingDefined
1852 | */
1853 | scale: function (sx, sy) {
1854 | sx = sx || sx === 0 ? sx : 1;
1855 | sy = sy || sy === 0 ? sy : sx;
1856 |
1857 | return new $m2x2(
1858 | sx, 0,
1859 | 0, sy
1860 | );
1861 | },
1862 |
1863 | /**
1864 | * Scale on the X-axis
1865 | * @param Number sx
1866 | * @return Matrix
1867 | */
1868 | scaleX: function (sx) {
1869 | return $m.scale(sx, 1);
1870 | },
1871 |
1872 | /**
1873 | * Scale on the Y-axis
1874 | * @param Number sy
1875 | * @return Matrix
1876 | */
1877 | scaleY: function (sy) {
1878 | return $m.scale(1, sy);
1879 | },
1880 |
1881 | /**
1882 | * Skews on the X-axis and Y-axis
1883 | * @param Number degX
1884 | * @param Number degY
1885 | * @return Matrix
1886 | */
1887 | skew: function (degX, degY) {
1888 | degX = degX || 0;
1889 | degY = degY || 0;
1890 |
1891 | //TODO: detect units
1892 | var radX = $.angle.degreeToRadian(degX),
1893 | radY = $.angle.degreeToRadian(degY),
1894 | x = Math.tan(radX),
1895 | y = Math.tan(radY);
1896 |
1897 | return new $m2x2(
1898 | 1, x,
1899 | y, 1
1900 | );
1901 | },
1902 |
1903 | /**
1904 | * Skews on the X-axis
1905 | * @param Number degX
1906 | * @return Matrix
1907 | * @link http://www.w3.org/TR/SVG/coords.html#SkewXDefined
1908 | */
1909 | skewX: function (degX) {
1910 | return $m.skew(degX);
1911 | },
1912 |
1913 | /**
1914 | * Skews on the Y-axis
1915 | * @param Number degY
1916 | * @return Matrix
1917 | * @link http://www.w3.org/TR/SVG/coords.html#SkewYDefined
1918 | */
1919 | skewY: function (degY) {
1920 | return $m.skew(0, degY);
1921 | },
1922 |
1923 | /**
1924 | * Translate
1925 | * @param Number tx
1926 | * @param Number ty
1927 | * @return Matrix
1928 | * @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
1929 | */
1930 | translate: function (tx, ty) {
1931 | tx = tx || 0;
1932 | ty = ty || 0;
1933 |
1934 | return new $m3x3(
1935 | 1, 0, tx,
1936 | 0, 1, ty,
1937 | 0, 0, 1
1938 | );
1939 | },
1940 |
1941 | /**
1942 | * Translate on the X-axis
1943 | * @param Number tx
1944 | * @return Matrix
1945 | * @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
1946 | */
1947 | translateX: function (tx) {
1948 | return $m.translate(tx);
1949 | },
1950 |
1951 | /**
1952 | * Translate on the Y-axis
1953 | * @param Number ty
1954 | * @return Matrix
1955 | * @link http://www.w3.org/TR/SVG/coords.html#TranslationDefined
1956 | */
1957 | translateY: function (ty) {
1958 | return $m.translate(0, ty);
1959 | }
1960 | });
1961 | })(jQuery, this, this.document);
--------------------------------------------------------------------------------