1/* 2 * transform: A jQuery cssHooks adding cross-browser 2d transform capabilities to $.fn.css() and $.fn.animate() 3 * 4 * limitations: 5 * - requires jQuery 1.4.3+ 6 * - Should you use the *translate* property, then your elements need to be absolutely positionned in a relatively positionned wrapper **or it will fail in IE678**. 7 * - transformOrigin is not accessible 8 * 9 * latest version and complete README available on Github: 10 * https://github.com/louisremi/jquery.transform.js 11 * 12 * Copyright 2011 @louis_remi 13 * Licensed under the MIT license. 14 * 15 * This saved you an hour of work? 16 * Send me music http://www.amazon.co.uk/wishlist/HNTU0468LQON 17 * 18 */ 19(function( $ ) { 20 21/* 22 * Feature tests and global variables 23 */ 24var div = document.createElement('div'), 25 divStyle = div.style, 26 propertyName = 'transform', 27 suffix = 'Transform', 28 testProperties = [ 29 'O' + suffix, 30 'ms' + suffix, 31 'Webkit' + suffix, 32 'Moz' + suffix, 33 // prefix-less property 34 propertyName 35 ], 36 i = testProperties.length, 37 supportProperty, 38 supportMatrixFilter, 39 propertyHook, 40 propertyGet,
41 rMatrix = /Matrix([^)]*)/; 42 43// test different vendor prefixes of this property 44while ( i-- ) { 45 if ( testProperties[i] in divStyle ) { 46 $.support[propertyName] = supportProperty = testProperties[i]; 47 continue; 48 } 49} 50// IE678 alternative 51if ( !supportProperty ) { 52 $.support.matrixFilter = supportMatrixFilter = divStyle.filter === ''; 53} 54// prevent IE memory leak 55div = divStyle = null; 56 57// px isn't the default unit of this property 58$.cssNumber[propertyName] = true; 59 60/* 61 * fn.css() hooks 62 */ 63if ( supportProperty && supportProperty != propertyName ) { 64 // Modern browsers can use jQuery.cssProps as a basic hook 65 $.cssProps[propertyName] = supportProperty; 66 67 // Firefox needs a complete hook because it stuffs matrix with 'px' 68 if ( supportProperty == 'Moz' + suffix ) { 69 propertyHook = { 70 get: function( elem, computed ) { 71 return (computed ? 72 // remove 'px' from the computed matrix 73 $.css( elem, supportProperty ).split('px').join(''): 74 elem.style[supportProperty] 75 ) 76 }, 77 set: function( elem, value ) { 78 // remove 'px' from matrices 79 elem.style[supportProperty] = /matrix[^)p]*\)/.test(value) ? 80 value.replace(/matrix((?:[^,]*,){4})([^,]*),([^)]*)/, 'matrix$1$2px,$3px'): 81 value; 82 } 83 } 84 /* Fix two jQuery bugs still present in 1.5.1 85 * - rupper is incompatible with IE9, see http://jqbug.com/8346 86 * - jQuery.css is not really jQuery.cssProps aware, see http://jqbug.com/8402 87 */ 88 } else if ( /^1\.[0-5](?:\.|$)/.test($.fn.jquery) ) { 89 propertyHook = { 90 get: function( elem, computed ) { 91 return (computed ? 92 $.css( elem, supportProperty.replace(/^ms/, 'Ms') ): 93 elem.style[supportProperty] 94 ) 95 } 96 } 97 } 98 /* TODO: leverage hardware acceleration of 3d transform in Webkit only 99 else if ( supportProperty == 'Webkit' + suffix && support3dTransform ) { 100 propertyHook = { 101 set: function( elem, value ) { 102 elem.style[supportProperty] = 103 value.replace(); 104 } 105 } 106 }*/ 107 108} else if ( supportMatrixFilter ) { 109 propertyHook = { 110 get: function( elem, computed ) { 111 var elemStyle = ( computed && elem.currentStyle ? elem.currentStyle : elem.style ), 112 matrix; 113 114 if ( elemStyle && rMatrix.test( elemStyle.filter ) ) { 115 matrix = RegExp.$1.split(','); 116 matrix = [ 117 matrix[0].split('=')[1], 118 matrix[2].split('=')[1], 119 matrix[1].split('=')[1], 120 matrix[3].split('=')[1] 121 ]; 122 } else { 123 matrix = [1,0,0,1]; 124 } 125 matrix[4] = elemStyle ? elemStyle.left : 0; 126 matrix[5] = elemStyle ? elemStyle.top : 0; 127 return "matrix(" + matrix + ")"; 128 }, 129 set: function( elem, value, animate ) { 130 var elemStyle = elem.style, 131 currentStyle, 132 Matrix, 133 filter; 134 135 if ( !animate ) { 136 elemStyle.zoom = 1; 137 } 138 139 value = matrix(value); 140 141 // rotate, scale and skew 142 if ( !animate || animate.M ) { 143 Matrix = [ 144 "Matrix("+ 145 "M11="+value[0], 146 "M12="+value[2], 147 "M21="+value[1], 148 "M22="+value[3], 149 "SizingMethod='auto expand'" 150 ].join(); 151 filter = ( currentStyle = elem.currentStyle ) && currentStyle.filter || elemStyle.filter || ""; 152 153 elemStyle.filter = rMatrix.test(filter) ? 154 filter.replace(rMatrix, Matrix) : 155 filter + " progid:DXImageTransform.Microsoft." + Matrix + ")"; 156 157 // center the transform origin, from pbakaus's Transformie http://github.com/pbakaus/transformie 158 if ( (centerOrigin = $.transform.centerOrigin) ) { 159 elemStyle[centerOrigin == 'margin' ? 'marginLeft' : 'left'] = -(elem.offsetWidth/2) + (elem.clientWidth/2) + 'px'; 160 elemStyle[centerOrigin == 'margin' ? 'marginTop' : 'top'] = -(elem.offsetHeight/2) + (elem.clientHeight/2) + 'px'; 161 } 162 } 163 164 // translate 165 if ( !animate || animate.T ) { 166 // We assume that the elements are absolute positionned inside a relative positionned wrapper 167 elemStyle.left = value[4] + 'px'; 168 elemStyle.top = value[5] + 'px'; 169 } 170 } 171 } 172} 173// populate jQuery.cssHooks with the appropriate hook if necessary 174if ( propertyHook ) { 175 $.cssHooks[propertyName] = propertyHook; 176} 177// we need a unique setter for the animation logic 178propertyGet = propertyHook && propertyHook.get ||Â $.css; 179 180/* 181 * fn.animate() hooks 182 */ 183$.fx.step.transform = function( fx ) { 184 var elem = fx.elem, 185 start = fx.start, 186 end = fx.end, 187 split, 188 pos = fx.pos, 189 transform, 190 translate, 191 rotate, 192 scale, 193 skew, 194 T = false, 195 M = false, 196 prop; 197 translate = rotate = scale = skew = ''; 198 199 // fx.end and fx.start need to be converted to their translate/rotate/scale/skew components
200 // so that we can interpolate them 201 if ( !start || typeof start === "string" ) { 202 // the following block can be commented out with jQuery 1.5.1+, see #7912 203 if (!start) { 204 start = propertyGet( elem, supportProperty ); 205 } 206 207 // force layout only once per animation 208 if ( supportMatrixFilter ) { 209 elem.style.zoom = 1; 210 } 211 212 // if the start computed matrix is in end, we are doing a relative animation 213 split = end.split(start); 214 if ( split.length == 2 ) { 215 // remove the start computed matrix to make animations more accurate 216 end = split.join(''); 217 fx.origin = start; 218 start = 'none'; 219 } 220 221 // start is either 'none' or a matrix(...) that has to be parsed 222 fx.start = start = start == 'none'? 223 { 224 translate: [0,0], 225 rotate: 0, 226 scale: [1,1], 227 skew: [0,0] 228 }: 229 unmatrix( toArray(start) ); 230 231 // fx.end has to be parsed and decomposed 232 fx.end = end = ~end.indexOf('matrix')? 233 // bullet-proof parser 234 unmatrix(matrix(end)): 235 // faster and more precise parser 236 components(end); 237 238 // get rid of properties that do not change 239 for ( prop in start) { 240 if ( prop == 'rotate' ? 241 start[prop] == end[prop]: 242 start[prop][0] == end[prop][0] && start[prop][1] == end[prop][1] 243 ) { 244 delete start[prop]; 245 } 246 } 247 } 248 249 /* 250 * We want a fast interpolation algorithm. 251 * This implies avoiding function calls and sacrifying DRY principle: 252 * - avoid $.each(function(){}) 253 * - round values using bitewise hacks, see http://jsperf.com/math-round-vs-hack/3 254 */ 255 if ( start.translate ) { 256 // round translate to the closest pixel 257 translate = ' translate('+ 258 ((start.translate[0] + (end.translate[0] - start.translate[0]) * pos + .5) | 0) +'px,'+ 259 ((start.translate[1] + (end.translate[1] - start.translate[1]) * pos + .5) | 0) +'px'+ 260 ')'; 261 T = true; 262 } 263 if ( start.rotate != undefined ) { 264 rotate = ' rotate('+ (start.rotate + (end.rotate - start.rotate) * pos) +'rad)'; 265 M = true; 266 } 267 if ( start.scale ) { 268 scale = ' scale('+ 269 (start.scale[0] + (end.scale[0] - start.scale[0]) * pos) +','+ 270 (start.scale[1] + (end.scale[1] - start.scale[1]) * pos) + 271 ')'; 272 M = true; 273 } 274 if ( start.skew ) { 275 skew = ' skew('+ 276 (start.skew[0] + (end.skew[0] - start.skew[0]) * pos) +'rad,'+ 277 (start.skew[1] + (end.skew[1] - start.skew[1]) * pos) +'rad'+ 278 ')'; 279 M = true; 280 } 281 282 // In case of relative animation, restore the origin computed matrix here. 283 transform = fx.origin ? 284 fx.origin + translate + skew + scale + rotate: 285 translate + rotate + scale + skew; 286 287 propertyHook && propertyHook.set ? 288 propertyHook.set( elem, transform, {M: M, T: T} ): 289 elem.style[supportProperty] = transform; 290}; 291 292/* 293 * Utility functions 294 */ 295 296// turns a transform string into its 'matrix(A,B,C,D,X,Y)' form (as an array, though) 297function matrix( transform ) { 298 transform = transform.split(')'); 299 var 300 trim = $.trim 301 // last element of the array is an empty string, get rid of it 302 , i = transform.length -1 303 , split, prop, val 304 , A = 1 305 , B = 0 306 , C = 0 307 , D = 1 308 , A_, B_, C_, D_ 309 , tmp1, tmp2 310 , X = 0 311 , Y = 0 312 ; 313 // Loop through the transform properties, parse and multiply them 314 while (i--) { 315 split = transform[i].split('('); 316 prop = trim(split[0]); 317 val = split[1]; 318 A_ = B_ = C_ = D_ = 0; 319 320 switch (prop) { 321 case 'translateX': 322 X += parseInt(val, 10); 323 continue; 324 325 case 'translateY': 326 Y += parseInt(val, 10); 327 continue; 328 329 case 'translate': 330 val = val.split(','); 331 X += parseInt(val[0], 10); 332 Y += parseInt(val[1] || 0, 10); 333 continue; 334 335 case 'rotate': 336 val = toRadian(val); 337 A_ = Math.cos(val); 338 B_ = Math.sin(val); 339 C_ = -Math.sin(val); 340 D_ = Math.cos(val); 341 break; 342 343 case 'scaleX': 344 A_ = val; 345 D_ = 1; 346 break; 347 348 case 'scaleY': 349 A_ = 1; 350 D_ = val; 351 break; 352 353 case 'scale': 354 val = val.split(','); 355 A_ = val[0]; 356 D_ = val.length>1 ? val[1] : val[0]; 357 break; 358 359 case 'skewX': 360 A_ = D_ = 1; 361 C_ = Math.tan(toRadian(val)); 362 break; 363 364 case 'skewY': 365 A_ = D_ = 1; 366 B_ = Math.tan(toRadian(val)); 367 break; 368 369 case 'skew': 370 A_ = D_ = 1; 371 val = val.split(','); 372 C_ = Math.tan(toRadian(val[0])); 373 B_ = Math.tan(toRadian(val[1] || 0)); 374 break; 375 376 case 'matrix': 377 val = val.split(','); 378 A_ = +val[0]; 379 B_ = +val[1]; 380 C_ = +val[2]; 381 D_ = +val[3]; 382 X += parseInt(val[4], 10); 383 Y += parseInt(val[5], 10); 384 } 385 // Matrix product 386 tmp1 = A * A_ + B * C_; 387 B = A * B_ + B * D_; 388 tmp2 = C * A_ + D * C_; 389 D = C * B_ + D * D_; 390 A = tmp1; 391 C = tmp2; 392 } 393 return [A,B,C,D,X,Y]; 394} 395 396// turns a matrix into its rotate, scale and skew components 397// algorithm from http://hg.mozilla.org/mozilla-central/file/7cb3e9795d04/layout/style/nsStyleAnimation.cpp 398function unmatrix(matrix) { 399 var 400 scaleX 401 , scaleY 402 , skew 403 , A = matrix[0] 404 , B = matrix[1] 405 , C = matrix[2] 406 , D = matrix[3] 407 ; 408 409 // Make sure matrix is not singular 410 if ( A * D - B * C ) { 411 // step (3) 412 scaleX = Math.sqrt( A * A + B * B ); 413 A /= scaleX; 414 B /= scaleX; 415 // step (4) 416 skew = A * C + B * D; 417 C -= A * skew; 418 D -= B * skew; 419 // step (5) 420 scaleY = Math.sqrt( C * C + D * D ); 421 C /= scaleY; 422 D /= scaleY; 423 skew /= scaleY; 424 // step (6) 425 if ( A * D < B * C ) { 426 //scaleY = -scaleY; 427 //skew = -skew; 428 A = -A; 429 B = -B; 430 skew = -skew; 431 scaleX = -scaleX; 432 } 433 434 // matrix is singular and cannot be interpolated 435 } else { 436 rotate = scaleX = scaleY = skew = 0; 437 } 438 439 return { 440 translate: [+matrix[4], +matrix[5]], 441 rotate: Math.atan2(B, A), 442 scale: [scaleX, scaleY], 443 skew: [skew, 0] 444 } 445} 446 447// parse tranform components of a transform string not containing 'matrix(...)' 448function components( transform ) { 449 // split the != transforms 450 transform = transform.split(')'); 451 452 var translate = [0,0], 453 rotate = 0, 454 scale = [1,1], 455 skew = [0,0], 456 i = transform.length -1, 457 trim = $.trim, 458 split, name, value; 459 460 // add components 461 while ( i-- ) { 462 split = transform[i].split('('); 463 name = trim(split[0]); 464 value = split[1]; 465 466 if (name == 'translateX') { 467 translate[0] += parseInt(value, 10); 468 469 } else if (name == 'translateY') { 470 translate[1] += parseInt(value, 10); 471 472 } else if (name == 'translate') { 473 value = value.split(','); 474 translate[0] += parseInt(value[0], 10); 475 translate[1] += parseInt(value[1] || 0, 10); 476 477 } else if (name == 'rotate') { 478 rotate += toRadian(value); 479 480 } else if (name == 'scaleX') { 481 scale[0] *= value; 482 483 } else if (name == 'scaleY') { 484 scale[1] *= value; 485 486 } else if (name == 'scale') { 487 value = value.split(','); 488 scale[0] *= value[0]; 489 scale[1] *= (value.length>1? value[1] : value[0]); 490 491 } else if (name == 'skewX') { 492 skew[0] += toRadian(value); 493 494 } else if (name == 'skewY') { 495 skew[1] += toRadian(value); 496 497 } else if (name == 'skew') { 498 value = value.split(','); 499 skew[0] += toRadian(value[0]); 500 skew[1] += toRadian(value[1] || '0'); 501 } 502 } 503 504 return { 505 translate: translate, 506 rotate: rotate, 507 scale: scale, 508 skew: skew 509 }; 510} 511
512// converts an angle string in any unit to a radian Float 513function toRadian(value) { 514 return ~value.indexOf('deg') ? 515 parseInt(value,10) * (Math.PI * 2 / 360): 516 ~value.indexOf('grad') ? 517 parseInt(value,10) * (Math.PI/200): 518 parseFloat(value); 519} 520 521// Converts 'matrix(A,B,C,D,X,Y)' to [A,B,C,D,X,Y] 522function toArray(matrix) { 523 // Fremove the unit of X and Y for Firefox 524 matrix = /\(([^,]*),([^,]*),([^,]*),([^,]*),([^,p]*)(?:px)?,([^)p]*)(?:px)?/.exec(matrix); 525 return [matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6]]; 526} 527 528$.transform = { 529 centerOrigin: 'margin' 530}; 531 532})( jQuery );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.