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 >
1287 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);
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.