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