1/*! 2 * jQuery Transit - CSS3 transitions and transformations 3 * (c) 2011-2014 Rico Sta. Cruz 4 * MIT Licensed. 5 * 6 * http://ricostacruz.com/jquery.transit 7 * http://github.com/rstacruz/jquery.transit 8 */ 9 10/* jshint expr: true */ 11 12;(function (root, factory) { 13 14 if (typeof define === 'function' && define.amd) { 15 define(['jquery'], factory); 16 } else if (typeof exports === 'object') { 17 module.exports = factory(require('jquery')); 18 } else { 19 factory(root.jQuery); 20 } 21 22}(this, function($) { 23 24 $.transit = { 25 version: "0.9.12", 26 27 // Map of $.css() keys to values for 'transitionProperty'. 28 // See https://developer.mozilla.org/en/CSS/CSS_transitions#Properties_that_can_be_animated 29 propertyMap: { 30 marginLeft : 'margin', 31 marginRight : 'margin', 32 marginBottom : 'margin', 33 marginTop : 'margin', 34 paddingLeft : 'padding', 35 paddingRight : 'padding', 36 paddingBottom : 'padding', 37 paddingTop : 'padding' 38 }, 39 40 // Will simply transition "instantly" if false 41 enabled: true, 42 43 // Set this to false if you don't want to use the transition end property. 44 useTransitionEnd: false 45 }; 46 47 var div = document.createElement('div'); 48 var support = {}; 49 50 // Helper function to get the proper vendor property name. 51 // (`transition` => `WebkitTransition`) 52 function getVendorPropertyName(prop) { 53 // Handle unprefixed versions (FF16+, for example) 54 if (prop in div.style) return prop; 55 56 var prefixes = ['Moz', 'Webkit', 'O', 'ms']; 57 var prop_ = prop.charAt(0).toUpperCase() + prop.substr(1); 58 59 for (var i=0; i<prefixes.length; ++i) { 60 var vendorProp = prefixes[i] + prop_; 61 if (vendorProp in div.style) { return vendorProp; } 62 } 63 } 64 65 // Helper function to check if transform3D is supported. 66 // Should return true for Webkits and Firefox 10+. 67 function checkTransform3dSupport() { 68 div.style[support.transform] = ''; 69 div.style[support.transform] = 'rotateY(90deg)'; 70 return div.style[support.transform] !== ''; 71 } 72 73 var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; 74 75 // Check for the browser's transitions support. 76 support.transition = getVendorPropertyName('transition'); 77 support.transitionDelay = getVendorPropertyName('transitionDelay'); 78 support.transform = getVendorPropertyName('transform'); 79 support.transformOrigin = getVendorPropertyName('transformOrigin'); 80 support.filter = getVendorPropertyName('Filter'); 81 support.transform3d = checkTransform3dSupport(); 82 83 var eventNames = { 84 'transition': 'transitionend', 85 'MozTransition': 'transitionend', 86 'OTransition': 'oTransitionEnd', 87 'WebkitTransition': 'webkitTransitionEnd', 88 'msTransition': 'MSTransitionEnd' 89 }; 90 91 // Detect the 'transitionend' event needed. 92 var transitionEnd = support.transitionEnd = eventNames[support.transition] || null; 93 94 // Populate jQuery's `$.support` with the vendor prefixes we know. 95 // As per [jQuery's cssHooks documentation](http://api.jquery.com/jQuery.cssHooks/), 96 // we set $.support.transition to a string of the actual property name used. 97 for (var key in support) { 98 if (support.hasOwnProperty(key) && typeof $.support[key] === 'undefined') { 99 $.support[key] = support[key]; 100 } 101 } 102 103 // Avoid memory leak in IE. 104 div = null; 105 106 // ## $.cssEase 107 // List of easing aliases that you can use with `$.fn.transition`. 108 $.cssEase = { 109 '_default': 'ease', 110 'in': 'ease-in', 111 'out': 'ease-out', 112 'in-out': 'ease-in-out', 113 'snap': 'cubic-bezier(0,1,.5,1)', 114 // Penner equations 115 'easeInCubic': 'cubic-bezier(.550,.055,.675,.190)', 116 'easeOutCubic': 'cubic-bezier(.215,.61,.355,1)', 117 'easeInOutCubic': 'cubic-bezier(.645,.045,.355,1)', 118 'easeInCirc': 'cubic-bezier(.6,.04,.98,.335)', 119 'easeOutCirc': 'cubic-bezier(.075,.82,.165,1)', 120 'easeInOutCirc': 'cubic-bezier(.785,.135,.15,.86)', 121 'easeInExpo': 'cubic-bezier(.95,.05,.795,.035)', 122 'easeOutExpo': 'cubic-bezier(.19,1,.22,1)', 123 'easeInOutExpo': 'cubic-bezier(1,0,0,1)', 124 'easeInQuad': 'cubic-bezier(.55,.085,.68,.53)', 125 'easeOutQuad': 'cubic-bezier(.25,.46,.45,.94)', 126 'easeInOutQuad': 'cubic-bezier(.455,.03,.515,.955)', 127 'easeInQuart': 'cubic-bezier(.895,.03,.685,.22)', 128 'easeOutQuart': 'cubic-bezier(.165,.84,.44,1)',
129 'easeInOutQuart': 'cubic-bezier(.77,0,.175,1)', 130 'easeInQuint': 'cubic-bezier(.755,.05,.855,.06)', 131 'easeOutQuint': 'cubic-bezier(.23,1,.32,1)', 132 'easeInOutQuint': 'cubic-bezier(.86,0,.07,1)', 133 'easeInSine': 'cubic-bezier(.47,0,.745,.715)', 134 'easeOutSine': 'cubic-bezier(.39,.575,.565,1)', 135 'easeInOutSine': 'cubic-bezier(.445,.05,.55,.95)', 136 'easeInBack': 'cubic-bezier(.6,-.28,.735,.045)', 137 'easeOutBack': 'cubic-bezier(.175, .885,.32,1.275)', 138 'easeInOutBack': 'cubic-bezier(.68,-.55,.265,1.55)' 139 }; 140 141 // ## 'transform' CSS hook 142 // Allows you to use the `transform` property in CSS. 143 // 144 // $("#hello").css({ transform: "rotate(90deg)" }); 145 // 146 // $("#hello").css('transform'); 147 // //=> { rotate: '90deg' } 148 // 149 $.cssHooks['transit:transform'] = { 150 // The getter returns a `Transform` object. 151 get: function(elem) { 152 return $(elem).data('transform') || new Transform(); 153 }, 154 155 // The setter accepts a `Transform` object or a string. 156 set: function(elem, v) { 157 var value = v; 158 159 if (!(value instanceof Transform)) { 160 value = new Transform(value); 161 } 162 163 // We've seen the 3D version of Scale() not work in Chrome when the 164 // element being scaled extends outside of the viewport. Thus, we're 165 // forcing Chrome to not use the 3d transforms as well. Not sure if 166 // translate is affectede, but not risking it. Detection code from 167 // http://davidwalsh.name/detecting-google-chrome-javascript 168 if (support.transform === 'WebkitTransform' && !isChrome) { 169 elem.style[support.transform] = value.toString(true); 170 } else { 171 elem.style[support.transform] = value.toString(); 172 } 173 174 $(elem).data('transform', value); 175 } 176 }; 177 178 // Add a CSS hook for `.css({ transform: '...' })`. 179 // In jQuery 1.8+, this will intentionally override the default `transform` 180 // CSS hook so it'll play well with Transit. (see issue #62) 181 $.cssHooks.transform = { 182 set: $.cssHooks['transit:transform'].set 183 }; 184 185 // ## 'filter' CSS hook 186 // Allows you to use the `filter` property in CSS. 187 // 188 // $("#hello").css({ filter: 'blur(10px)' }); 189 // 190 $.cssHooks.filter = { 191 get: function(elem) { 192 return elem.style[support.filter]; 193 }, 194 set: function(elem, value) { 195 elem.style[support.filter] = value; 196 } 197 }; 198 199 // jQuery 1.8+ supports prefix-free transitions, so these polyfills will not 200 // be necessary. 201 if ($.fn.jquery < "1.8") { 202 // ## 'transformOrigin' CSS hook 203 // Allows the use for `transformOrigin` to define where scaling and rotation 204 // is pivoted. 205 // 206 // $("#hello").css({ transformOrigin: '0 0' }); 207 // 208 $.cssHooks.transformOrigin = { 209 get: function(elem) { 210 return elem.style[support.transformOrigin]; 211 }, 212 set: function(elem, value) { 213 elem.style[support.transformOrigin] = value; 214 } 215 }; 216 217 // ## 'transition' CSS hook 218 // Allows you to use the `transition` property in CSS. 219 // 220 // $("#hello").css({ transition: 'all 0 ease 0' }); 221 // 222 $.cssHooks.transition = { 223 get: function(elem) { 224 return elem.style[support.transition]; 225 }, 226 set: function(elem, value) { 227 elem.style[support.transition] = value; 228 } 229 }; 230 } 231 232 // ## Other CSS hooks 233 // Allows you to rotate, scale and translate. 234 registerCssHook('scale'); 235 registerCssHook('scaleX'); 236 registerCssHook('scaleY'); 237 registerCssHook('translate'); 238 registerCssHook('rotate'); 239 registerCssHook('rotateX'); 240 registerCssHook('rotateY'); 241 registerCssHook('rotate3d'); 242 registerCssHook('perspective'); 243 registerCssHook('skewX'); 244 registerCssHook('skewY'); 245 registerCssHook('x', true); 246 registerCssHook('y', true); 247 248 // ## Transform class 249 // This is the main class of a transformation property that powers 250 // `$.fn.css({ transform: '...' })`. 251 // 252 // This is, in essence, a dictionary object with key/values as `-transform` 253 // properties. 254 // 255 // var t = new Transform("rotate(90) scale(4)"); 256 // 257 // t.rotate //=> "90deg" 258 // t.scale //=> "4,4" 259 // 260 // Setters are accounted for. 261 // 262 // t.set('rotate', 4) 263 // t.rotate //=> "4deg" 264 // 265 // Convert it to a CSS string using the `toString()` and `toString(true)` (for WebKit) 266 // functions. 267 // 268 // t.toString() //=> "rotate(90deg) scale(4,4)" 269 // t.toString(true) //=> "rotate(90deg) scale3d(4,4,0)" (WebKit version) 270 // 271 function Transform(str) { 272 if (typeof str === 'string') { this.parse(str); } 273 return this; 274 } 275 276 Transform.prototype = { 277 // ### setFromString() 278 // Sets a property from a string. 279 // 280 // t.setFromString('scale', '2,4'); 281 // // Same as set('scale', '2', '4'); 282 // 283 setFromString: function(prop, val) { 284 var args = 285 (typeof val === 'string') ? val.split(',') : 286 (val.constructor === Array) ? val : 287 [ val ]; 288 289 args.unshift(prop); 290 291 Transform.prototype.set.apply(this, args); 292 }, 293 294 // ### set() 295 // Sets a property. 296 // 297 // t.set('scale', 2, 4); 298 // 299 set: function(prop) { 300 var args = Array.prototype.slice.apply(arguments, [1]); 301 if (this.setter[prop]) { 302 this.setter[prop].apply(this, args); 303 } else { 304 this[prop] = args.join(','); 305 } 306 }, 307 308 get: function(prop) { 309 if (this.getter[prop]) { 310 return this.getter[prop].apply(this); 311 } else { 312 return this[prop] || 0; 313 } 314 }, 315 316 setter: { 317 // ### rotate 318 // 319 // .css({ rotate: 30 }) 320 // .css({ rotate: "30" }) 321 // .css({ rotate: "30deg" }) 322 // .css({ rotate: "30deg" }) 323 // 324 rotate: function(theta) { 325 this.rotate = unit(theta, 'deg'); 326 }, 327 328 rotateX: function(theta) { 329 this.rotateX = unit(theta, 'deg'); 330 }, 331 332 rotateY: function(theta) { 333 this.rotateY = unit(theta, 'deg'); 334 }, 335 336 // ### scale 337 // 338 // .css({ scale: 9 }) //=> "scale(9,9)" 339 // .css({ scale: '3,2' }) //=> "scale(3,2)" 340 // 341 scale: function(x, y) { 342 if (y === undefined) { y = x; } 343 this.scale = x + "," + y; 344 }, 345 346 // ### skewX + skewY 347 skewX: function(x) { 348 this.skewX = unit(x, 'deg'); 349 }, 350 351 skewY: function(y) { 352 this.skewY = unit(y, 'deg'); 353 }, 354 355 // ### perspectvie 356 perspective: function(dist) { 357 this.perspective = unit(dist, 'px'); 358 }, 359 360 // ### x / y 361 // Translations. Notice how this keeps the other value. 362 // 363 // .css({ x: 4 }) //=> "translate(4px, 0)" 364 // .css({ y: 10 }) //=> "translate(4px, 10px)" 365 // 366 x: function(x) { 367 this.set('translate', x, null); 368 }, 369 370 y: function(y) { 371 this.set('translate', null, y); 372 }, 373 374 // ### translate 375 // Notice how this keeps the other value. 376 // 377 // .css({ translate: '2, 5' }) //=> "translate(2px, 5px)" 378 // 379 translate: function(x, y) { 380 if (this._translateX === undefined) { this._translateX = 0; } 381 if (this._translateY === undefined) { this._translateY = 0; } 382 383 if (x !== null && x !== undefined) { this._translateX = unit(x, 'px'); } 384 if (y !== null && y !== undefined) { this._translateY = unit(y, 'px'); } 385 386 this.translate = this._translateX + "," + this._translateY; 387 } 388 }, 389 390 getter: { 391 x: function() { 392 return this._translateX || 0; 393 }, 394 395 y: function() { 396 return this._translateY || 0; 397 }, 398 399 scale: function() { 400 var s = (this.scale || "1,1").split(','); 401 if (s[0]) { s[0] = parseFloat(s[0]); } 402 if (s[1]) { s[1] = parseFloat(s[1]); } 403 404 // "2.5,2.5" => 2.5 405 // "2.5,1" => [2.5,1] 406 return (s[0] === s[1]) ? s[0] : s; 407 }, 408 409 rotate3d: function() { 410 var s = (this.rotate3d || "0,0,0,0deg").split(','); 411 for (var i=0; i<=3; ++i) { 412 if (s[i]) { s[i] = parseFloat(s[i]); } 413 } 414 if (s[3]) { s[3] = unit(s[3], 'deg'); } 415 416 return s; 417 } 418 }, 419 420 // ### parse() 421 // Parses from a string. Called on constructor. 422 parse: function(str) { 423 var self = this; 424 str.replace(/([a-zA-Z0-9]+)\((.*?)\)/g, function(x, prop, val) { 425 self.setFromString(prop, val); 426 }); 427 }, 428 429 // ### toString() 430 // Converts to a `transition` CSS property string. If `use3d` is given, 431 // it converts to a `-webkit-transition` CSS property string instead. 432 toString: function(use3d) { 433 var re = []; 434 435 for (var i in this) { 436 if (this.hasOwnProperty(i)) { 437 // Don't use 3D transformations if the browser can't support it. 438 if ((!support.transform3d) && ( 439 (i === 'rotateX') || 440 (i === 'rotateY') || 441 (i === 'perspective') || 442 (i === 'transformOrigin'))) { continue; } 443 444 if (i[0] !== '_') { 445 if (use3d && (i === 'scale')) { 446 re.push(i + "3d(" + this[i] + ",1)"); 447 } else if (use3d && (i === 'translate')) { 448 re.push(i + "3d(" + this[i] + ",0)"); 449 } else { 450 re.push(i + "(" + this[i] + ")"); 451 } 452 } 453 } 454 } 455 456 return re.join(" "); 457 } 458 }; 459 460 function callOrQueue(self, queue, fn) { 461 if (queue === true) { 462 self.queue(fn); 463 } else if (queue) { 464 self.queue(queue, fn); 465 } else { 466 self.each(function () { 467 fn.call(this); 468 }); 469 } 470 } 471 472 // ### getProperties(dict) 473 // Returns properties (for `transition-property`) for dictionary `props`. The 474 // value of `props` is what you would expect in `$.css(...)`. 475 function getProperties(props) { 476 var re = []; 477 478 $.each(props, function(key) { 479 key = $.camelCase(key); // Convert "text-align" => "textAlign" 480 key = $.transit.propertyMap[key] || $.cssProps[key] || key; 481 key = uncamel(key); // Convert back to dasherized 482 483 // Get vendor specify propertie 484 if (support[key]) 485 key = uncamel(support[key]); 486 487 if ($.inArray(key, re) === -1) { re.push(key); } 488 }); 489 490 return re; 491 } 492 493 // ### getTransition() 494 // Returns the transition string to be used for the `transition` CSS property. 495 // 496 // Example: 497 // 498 // getTransition({ opacity: 1, rotate: 30 }, 500, 'ease'); 499 // //=> 'opacity 500ms ease, -webkit-transform 500ms ease' 500 // 501 function getTransition(properties, duration, easing, delay) { 502 // Get the CSS properties needed. 503 var props = getProperties(properties); 504 505 // Account for aliases (`in` => `ease-in`). 506 if ($.cssEase[easing]) { easing = $.cssEase[easing]; } 507 508 // Build the duration/easing/delay attributes for it. 509 var attribs = '' + toMS(duration) + ' ' + easing; 510 if (parseInt(delay, 10) > 0) { attribs += ' ' + toMS(delay); } 511 512 // For more properties, add them this way: 513 // "margin 200ms ease, padding 200ms ease, ..." 514 var transitions = []; 515 $.each(props, function(i, name) { 516 transitions.push(name + ' ' + attribs); 517 }); 518 519 return transitions.join(', '); 520 } 521 522 // ## $.fn.transition 523 // Works like $.fn.animate(), but uses CSS transitions. 524 // 525 // $("...").transition({ opacity: 0.1, scale: 0.3 }); 526 // 527 // // Specific duration 528 // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500); 529 //
530 // // With duration and easing 531 // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500, 'in'); 532 // 533 // // With callback 534 // $("...").transition({ opacity: 0.1, scale: 0.3 }, function() { ... }); 535 // 536 // // With everything 537 // $("...").transition({ opacity: 0.1, scale: 0.3 }, 500, 'in', function() { ... }); 538 // 539 // // Alternate syntax 540 // $("...").transition({ 541 // opacity: 0.1, 542 // duration: 200, 543 // delay: 40, 544 // easing: 'in', 545 // complete: function() { /* ... */ } 546 // }); 547 // 548 $.fn.transition = $.fn.transit = function(properties, duration, easing, callback) { 549 var self = this; 550 var delay = 0; 551 var queue = true; 552 553 var theseProperties = $.extend(true, {}, properties); 554 555 // Account for `.transition(properties, callback)`. 556 if (typeof duration === 'function') { 557 callback = duration; 558 duration = undefined; 559 } 560 561 // Account for `.transition(properties, options)`. 562 if (typeof duration === 'object') { 563 easing = duration.easing; 564 delay = duration.delay || 0; 565 queue = typeof duration.queue === "undefined" ? true : duration.queue; 566 callback = duration.complete; 567 duration = duration.duration; 568 } 569 570 // Account for `.transition(properties, duration, callback)`. 571 if (typeof easing === 'function') { 572 callback = easing; 573 easing = undefined; 574 } 575 576 // Alternate syntax. 577 if (typeof theseProperties.easing !== 'undefined') { 578 easing = theseProperties.easing; 579 delete theseProperties.easing; 580 } 581 582 if (typeof theseProperties.duration !== 'undefined') { 583 duration = theseProperties.duration; 584 delete theseProperties.duration; 585 } 586 587 if (typeof theseProperties.complete !== 'undefined') { 588 callback = theseProperties.complete; 589 delete theseProperties.complete; 590 } 591 592 if (typeof theseProperties.queue !== 'undefined') { 593 queue = theseProperties.queue; 594 delete theseProperties.queue; 595 } 596 597 if (typeof theseProperties.delay !== 'undefined') { 598 delay = theseProperties.delay; 599 delete theseProperties.delay; 600 } 601 602 // Set defaults. (`400` duration, `ease` easing) 603 if (typeof duration === 'undefined') { duration = $.fx.speeds._default; } 604 if (typeof easing === 'undefined') { easing = $.cssEase._default; } 605 606 duration = toMS(duration); 607 608 // Build the `transition` property. 609 var transitionValue = getTransition(theseProperties, duration, easing, delay); 610 611 // Compute delay until callback. 612 // If this becomes 0, don't bother setting the transition property. 613 var work = $.transit.enabled && support.transition; 614 var i = work ? (parseInt(duration, 10) + parseInt(delay, 10)) : 0; 615 616 // If there's nothing to do... 617 if (i === 0) { 618 var fn = function(next) { 619 self.css(theseProperties); 620 if (callback) { callback.apply(self); } 621 if (next) { next(); } 622 }; 623 624 callOrQueue(self, queue, fn); 625 return self; 626 } 627 628 // Save the old transitions of each element so we can restore it later. 629 var oldTransitions = {}; 630 631 var run = function(nextCall) { 632 var bound = false; 633 634 // Prepare the callback. 635 var cb = function() { 636 if (bound) { self.unbind(transitionEnd, cb); } 637 638 if (i > 0) { 639 self.each(function() { 640 this.style[support.transition] = (oldTransitions[this] || null); 641 }); 642 } 643 644 if (typeof callback === 'function') { callback.apply(self); } 645 if (typeof nextCall === 'function') { nextCall(); } 646 }; 647 648 if ((i > 0) && (transitionEnd) && ($.transit.useTransitionEnd)) { 649 // Use the 'transitionend' event if it's available. 650 bound = true; 651 self.bind(transitionEnd, cb); 652 } else { 653 // Fallback to timers if the 'transitionend' event isn't supported. 654 window.setTimeout(cb, i); 655 } 656 657 // Apply transitions. 658 self.each(function() { 659 if (i > 0) { 660 this.style[support.transition] = transitionValue; 661 } 662 $(this).css(theseProperties); 663 }); 664 }; 665
666 // Defer running. This allows the browser to paint any pending CSS it hasn't 667 // painted yet before doing the transitions. 668 var deferredRun = function(next) { 669 this.offsetWidth = this.offsetWidth; // force a repaint 670 run(next); 671 }; 672 673 // Use jQuery's fx queue. 674 callOrQueue(self, queue, deferredRun); 675 676 // Chainability. 677 return this; 678 }; 679 680 function registerCssHook(prop, isPixels) { 681 // For certain properties, the 'px' should not be implied. 682 if (!isPixels) { $.cssNumber[prop] = true; } 683 684 $.transit.propertyMap[prop] = support.transform; 685 686 $.cssHooks[prop] = { 687 get: function(elem) { 688 var t = $(elem).css('transit:transform'); 689 return t.get(prop); 690 }, 691 692 set: function(elem, value) { 693 var t = $(elem).css('transit:transform'); 694 t.setFromString(prop, value); 695 696 $(elem).css({ 'transit:transform': t }); 697 } 698 }; 699 700 } 701 702 // ### uncamel(str) 703 // Converts a camelcase string to a dasherized string. 704 // (`marginLeft` => `margin-left`) 705 function uncamel(str) { 706 return str.replace(/([A-Z])/g, function(letter) { return '-' + letter.toLowerCase(); }); 707 } 708 709 // ### unit(number, unit) 710 // Ensures that number `number` has a unit. If no unit is found, assume the 711 // default is `unit`. 712 // 713 // unit(2, 'px') //=> "2px" 714 // unit("30deg", 'rad') //=> "30deg" 715 // 716 function unit(i, units) { 717 if ((typeof i === "string") && (!i.match(/^[\-0-9\.]+$/))) { 718 return i; 719 } else { 720 return "" + i + units; 721 } 722 } 723 724 // ### toMS(duration) 725 // Converts given `duration` to a millisecond string. 726 // 727 // toMS('fast') => $.fx.speeds[i] => "200ms" 728 // toMS('normal') //=> $.fx.speeds._default => "400ms" 729 // toMS(10) //=> '10ms' 730 // toMS('100ms') //=> '100ms' 731 // 732 function toMS(duration) { 733 var i = duration; 734 735 // Allow string durations like 'fast' and 'slow', without overriding numeric values. 736 if (typeof i === 'string' && (!i.match(/^[\-0-9\.]+/))) { i = $.fx.speeds[i] || $.fx.speeds._default; } 737 738 return unit(i, 'ms'); 739 } 740 741 // Export some functions for testable-ness. 742 $.transit.getTransitionValue = getTransition; 743 744 return $; 745}));
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.