1 2/*! 3 * skrollr core 4 * 5 * Alexander Prinzhorn - https://github.com/Prinzhorn/skrollr 6 * 7 * Free to use under terms of MIT license 8 */ 9(function(window, document, undefined) { 10 'use strict'; 11 12 /* 13 * Global api. 14 */ 15 var skrollr = { 16 get: function() { 17 return _instance; 18 }, 19 //Main entry point. 20 init: function(options) { 21 return _instance || new Skrollr(options); 22 }, 23 VERSION: '0.6.22' 24 }; 25 26 //Minify optimization. 27 var hasProp = Object.prototype.hasOwnProperty; 28 var Math = window.Math; 29 var getStyle = window.getComputedStyle; 30 31 //They will be filled when skrollr gets initialized. 32 var documentElement; 33 var body; 34 35 var EVENT_TOUCHSTART = 'touchstart'; 36 var EVENT_TOUCHMOVE = 'touchmove'; 37 var EVENT_TOUCHCANCEL = 'touchcancel'; 38 var EVENT_TOUCHEND = 'touchend'; 39 40 var SKROLLABLE_CLASS = 'skrollable'; 41 var SKROLLABLE_BEFORE_CLASS = SKROLLABLE_CLASS + '-before'; 42 var SKROLLABLE_BETWEEN_CLASS = SKROLLABLE_CLASS + '-between'; 43 var SKROLLABLE_AFTER_CLASS = SKROLLABLE_CLASS + '-after'; 44 45 var SKROLLR_CLASS = 'skrollr'; 46 var NO_SKROLLR_CLASS = 'no-' + SKROLLR_CLASS; 47 var SKROLLR_DESKTOP_CLASS = SKROLLR_CLASS + '-desktop'; 48 var SKROLLR_MOBILE_CLASS = SKROLLR_CLASS + '-mobile'; 49 50 var DEFAULT_EASING = 'linear'; 51 var DEFAULT_DURATION = 1000;//ms 52 var DEFAULT_MOBILE_DECELERATION = 0.004;//pixel/ms² 53 54 var DEFAULT_SMOOTH_SCROLLING_DURATION = 200;//ms 55 56 var ANCHOR_START = 'start'; 57 var ANCHOR_END = 'end'; 58 var ANCHOR_CENTER = 'center'; 59 var ANCHOR_BOTTOM = 'bottom'; 60 61 //The property which will be added to the DOM element to hold the ID of the skrollable. 62 var SKROLLABLE_ID_DOM_PROPERTY = '___skrollable_id'; 63 64 var rxTouchIgnoreTags = /^(?:input|textarea|button|select)$/i; 65 66 var rxTrim = /^\s+|\s+$/g; 67 68 //Find all data-attributes. data-[_constant]-[offset]-[anchor]-[anchor]. 69 var rxKeyframeAttribute = /^data(?:-(_\w+))?(?:-?(-?\d*\.?\d+p?))?(?:-?(start|end|top|center|bottom))?(?:-?(top|center|bottom))?$/; 70 71 var rxPropValue = /\s*([\w\-\[\]]+)\s*:\s*(.+?)\s*(?:;|$)/gi; 72 73 //Easing function names follow the property in square brackets. 74 var rxPropEasing = /^([a-z\-]+)\[(\w+)\]$/; 75 76 var rxCamelCase = /-([a-z])/g; 77 var rxCamelCaseFn = function(str, letter) { 78 return letter.toUpperCase(); 79 }; 80 81 //Numeric values with optional sign.
82 var rxNumericValue = /[\-+]?[\d]*\.?[\d]+/g; 83 84 //Used to replace occurences of {?} with a number. 85 var rxInterpolateString = /\{\?\}/g; 86 87 //Finds rgb(a) colors, which don't use the percentage notation. 88 var rxRGBAIntegerColor = /rgba?\(\s*-?\d+\s*,\s*-?\d+\s*,\s*-?\d+/g; 89 90 //Finds all gradients. 91 var rxGradient = /[a-z\-]+-gradient/g; 92 93 //Vendor prefix. Will be set once skrollr gets initialized. 94 var theCSSPrefix = ''; 95 var theDashedCSSPrefix = ''; 96 97 //Will be called once (when skrollr gets initialized). 98 var detectCSSPrefix = function() { 99 //Only relevant prefixes. May be extended. 100 //Could be dangerous if there will ever be a CSS property which actually starts with "ms". Don't hope so. 101 var rxPrefixes = /^(?:O|Moz|webkit|ms)|(?:-(?:o|moz|webkit|ms)-)/; 102 103 //Detect prefix for current browser by finding the first property using a prefix. 104 if(!getStyle) { 105 return; 106 } 107 108 var style = getStyle(body, null); 109 110 for(var k in style) { 111 //We check the key and if the key is a number, we check the value as well, because safari's getComputedStyle returns some weird array-like thingy. 112 theCSSPrefix = (k.match(rxPrefixes) || (+k == k && style[k].match(rxPrefixes))); 113 114 if(theCSSPrefix) { 115 break; 116 } 117 } 118 119 //Did we even detect a prefix? 120 if(!theCSSPrefix) { 121 theCSSPrefix = theDashedCSSPrefix = ''; 122 123 return; 124 } 125 126 theCSSPrefix = theCSSPrefix[0]; 127 128 //We could have detected either a dashed prefix or this camelCaseish-inconsistent stuff. 129 if(theCSSPrefix.slice(0,1) === '-') { 130 theDashedCSSPrefix = theCSSPrefix; 131 132 //There's no logic behind these. Need a look up. 133 theCSSPrefix = ({ 134 '-webkit-': 'webkit', 135 '-moz-': 'Moz', 136 '-ms-': 'ms', 137 '-o-': 'O' 138 })[theCSSPrefix]; 139 } else { 140 theDashedCSSPrefix = '-' + theCSSPrefix.toLowerCase() + '-'; 141 } 142 }; 143 144 var polyfillRAF = function() { 145 var requestAnimFrame = window.requestAnimationFrame || window[theCSSPrefix.toLowerCase() + 'RequestAnimationFrame']; 146 147 var lastTime = _now(); 148 149 if(_isMobile || !requestAnimFrame) { 150 requestAnimFrame = function(callback) { 151 //How long did it take to render? 152 var deltaTime = _now() - lastTime; 153 var delay = Math.max(0, 1000 / 60 - deltaTime); 154 155 return window.setTimeout(function() { 156 lastTime = _now(); 157 callback(); 158 }, delay); 159 }; 160 } 161 162 return requestAnimFrame; 163 }; 164 165 var polyfillCAF = function() { 166 var cancelAnimFrame = window.cancelAnimationFrame || window[theCSSPrefix.toLowerCase() + 'CancelAnimationFrame']; 167 168 if(_isMobile || !cancelAnimFrame) { 169 cancelAnimFrame = function(timeout) { 170 return window.clearTimeout(timeout); 171 }; 172 } 173 174 return cancelAnimFrame; 175 }; 176 177 //Built-in easing functions. 178 var easings = { 179 begin: function() { 180 return 0; 181 }, 182 end: function() { 183 return 1; 184 }, 185 linear: function(p) { 186 return p; 187 }, 188 quadratic: function(p) { 189 return p * p; 190 }, 191 cubic: function(p) { 192 return p * p * p; 193 }, 194 swing: function(p) { 195 return (-Math.cos(p * Math.PI) / 2) + 0.5; 196 }, 197 sqrt: function(p) { 198 return Math.sqrt(p); 199 }, 200 outCubic: function(p) { 201 return (Math.pow((p - 1), 3) + 1); 202 }, 203 //see https://www.desmos.com/calculator/tbr20s8vd2 for how I did this 204 bounce: function(p) { 205 var a; 206 207 if(p <= 0.5083) { 208 a = 3; 209 } else if(p <= 0.8489) { 210 a = 9; 211 } else if(p <= 0.96208) { 212 a = 27; 213 } else if(p <= 0.99981) { 214 a = 91; 215 } else { 216 return 1; 217 } 218 219 return 1 - Math.abs(3 * Math.cos(p * a * 1.028) / a); 220 } 221 }; 222 223 /** 224 * Constructor. 225 */ 226 function Skrollr(options) { 227 documentElement = document.documentElement; 228 body = document.body; 229 230 detectCSSPrefix(); 231 232 _instance = this; 233 234 options = options || {}; 235 236 _constants = options.constants || {}; 237
238 //We allow defining custom easings or overwrite existing. 239 if(options.easing) { 240 for(var e in options.easing) { 241 easings[e] = options.easing[e]; 242 } 243 } 244 245 _edgeStrategy = options.edgeStrategy || 'set'; 246 247 _listeners = { 248 //Function to be called right before rendering. 249 beforerender: options.beforerender, 250 251 //Function to be called right after finishing rendering. 252 render: options.render 253 }; 254 255 //forceHeight is true by default 256 _forceHeight = options.forceHeight !== false; 257 258 if(_forceHeight) { 259 _scale = options.scale || 1; 260 } 261 262 _mobileDeceleration = options.mobileDeceleration || DEFAULT_MOBILE_DECELERATION; 263 264 _smoothScrollingEnabled = options.smoothScrolling !== false; 265 _smoothScrollingDuration = options.smoothScrollingDuration || DEFAULT_SMOOTH_SCROLLING_DURATION; 266 267 //Dummy object. Will be overwritten in the _render method when smooth scrolling is calculated. 268 _smoothScrolling = { 269 targetTop: _instance.getScrollTop() 270 }; 271 272 //A custom check function may be passed. 273 _isMobile = ((options.mobileCheck || function() { 274 return (/Android|iPhone|iPad|iPod|BlackBerry/i).test(navigator.userAgent || navigator.vendor || window.opera); 275 })()); 276 277 if(_isMobile) { 278 _skrollrBody = document.getElementById('skrollr-body'); 279 280 //Detect 3d transform if there's a skrollr-body (only needed for #skrollr-body). 281 if(_skrollrBody) { 282 _detect3DTransforms(); 283 } 284 285 _initMobile(); 286 _updateClass(documentElement, [SKROLLR_CLASS, SKROLLR_MOBILE_CLASS], [NO_SKROLLR_CLASS]); 287 } else { 288 _updateClass(documentElement, [SKROLLR_CLASS, SKROLLR_DESKTOP_CLASS], [NO_SKROLLR_CLASS]); 289 } 290 291 //Triggers parsing of elements and a first reflow. 292 _instance.refresh(); 293 294 _addEvent(window, 'resize orientationchange', function() { 295 var width = documentElement.clientWidth; 296 var height = documentElement.clientHeight; 297 298 //Only reflow if the size actually changed (#271). 299 if(height !== _lastViewportHeight || width !== _lastViewportWidth) { 300 _lastViewportHeight = height; 301 _lastViewportWidth = width; 302 303 _requestReflow = true; 304 } 305 }); 306 307 var requestAnimFrame = polyfillRAF(); 308 309 //Let's go. 310 (function animloop(){ 311 _render(); 312 _animFrame = requestAnimFrame(animloop); 313 }()); 314 315 return _instance; 316 } 317 318 /** 319 * (Re)parses some or all elements. 320 */ 321 Skrollr.prototype.refresh = function(elements) { 322 var elementIndex; 323 var elementsLength; 324 var ignoreID = false; 325 326 //Completely reparse anything without argument. 327 if(elements === undefined) { 328 //Ignore that some elements may already have a skrollable ID. 329 ignoreID = true; 330 331 _skrollables = []; 332 _skrollableIdCounter = 0; 333 334 elements = document.getElementsByTagName('*'); 335 } else if(elements.length === undefined) { 336 //We also accept a single element as parameter. 337 elements = [elements]; 338 } 339 340 elementIndex = 0; 341 elementsLength = elements.length; 342 343 for(; elementIndex < elementsLength; elementIndex++) { 344 var el = elements[elementIndex]; 345 var anchorTarget = el; 346 var keyFrames = []; 347 348 //If this particular element should be smooth scrolled. 349 var smoothScrollThis = _smoothScrollingEnabled; 350 351 //The edge strategy for this particular element. 352 var edgeStrategy = _edgeStrategy; 353 354 if(!el.attributes) { 355 continue; 356 } 357 358 //Iterate over all attributes and search for key frame attributes. 359 var attributeIndex = 0; 360 var attributesLength = el.attributes.length; 361 362 for (; attributeIndex < attributesLength; attributeIndex++) { 363 var attr = el.attributes[attributeIndex]; 364 365 if(attr.name === 'data-anchor-target') { 366 anchorTarget = document.querySelector(attr.value); 367 368 if(anchorTarget === null) { 369 throw 'Unable to find anchor target "' + attr.value + '"'; 370 } 371 372 continue; 373 } 374 375 //Global smooth scrolling can be overridden by the element attribute. 376 if(attr.name === 'data-smooth-scrolling') { 377 smoothScrollThis = attr.value !== 'off'; 378 379 continue; 380 } 381 382 //Global edge strategy can be overridden by the element attribute. 383 if(attr.name === 'data-edge-strategy') { 384 edgeStrategy = attr.value; 385 386 continue; 387 } 388 389 var match = attr.name.match(rxKeyframeAttribute); 390 391 if(match === null) { 392 continue; 393 } 394 395 var kf = { 396 props: attr.value, 397 //Point back to the element as well. 398 element: el 399 }; 400 401 keyFrames.push(kf); 402 403 var constant = match[1]; 404 405 if(constant) { 406 //Strip the underscore prefix. 407 kf.constant = constant.substr(1); 408 } 409 410 //Get the key frame offset. 411 var offset = match[2]; 412 413 //Is it a percentage offset? 414 if(/p$/.test(offset)) { 415 kf.isPercentage = true; 416 kf.offset = (offset.slice(0, -1) | 0) / 100; 417 } else { 418 kf.offset = (offset | 0); 419 } 420 421 var anchor1 = match[3]; 422 423 //If second anchor is not set, the first will be taken for both. 424 var anchor2 = match[4] || anchor1; 425 426 //"absolute" (or "classic") mode, where numbers mean absolute scroll offset. 427 if(!anchor1 || anchor1 === ANCHOR_START || anchor1 === ANCHOR_END) { 428 kf.mode = 'absolute'; 429 430 //data-end needs to be calculated after all key frames are known. 431 if(anchor1 === ANCHOR_END) { 432 kf.isEnd = true; 433 } else if(!kf.isPercentage) { 434 //For data-start we can already set the key frame w/
434o calculations. 435 //#59: "scale" options should only affect absolute mode. 436 kf.offset = kf.offset * _scale; 437 } 438 } 439 //"relative" mode, where numbers are relative to anchors. 440 else { 441 kf.mode = 'relative'; 442 kf.anchors = [anchor1, anchor2]; 443 } 444 } 445 446 //Does this element have key frames? 447 if(!keyFrames.length) { 448 continue; 449 } 450 451 //Will hold the original style and class attributes before we controlled the element (see #80). 452 var styleAttr, classAttr; 453 454 var id; 455 456 if(!ignoreID && SKROLLABLE_ID_DOM_PROPERTY in el) { 457 //We already have this element under control. Grab the corresponding skrollable id. 458 id = el[SKROLLABLE_ID_DOM_PROPERTY]; 459 styleAttr = _skrollables[id].styleAttr; 460 classAttr = _skrollables[id].classAttr; 461 } else { 462 //It's an unknown element. Asign it a new skrollable id. 463 id = (el[SKROLLABLE_ID_DOM_PROPERTY] = _skrollableIdCounter++); 464 styleAttr = el.style.cssText; 465 classAttr = _getClass(el); 466 } 467 468 _skrollables[id] = { 469 element: el, 470 styleAttr: styleAttr, 471 classAttr: classAttr, 472 anchorTarget: anchorTarget, 473 keyFrames: keyFrames, 474 smoothScrolling: smoothScrollThis, 475 edgeStrategy: edgeStrategy 476 }; 477 478 _updateClass(el, [SKROLLABLE_CLASS], []); 479 } 480 481 //Reflow for the first time. 482 _reflow(); 483 484 //Now that we got all key frame numbers right, actually parse the properties. 485 elementIndex = 0; 486 elementsLength = elements.length; 487 488 for(; elementIndex < elementsLength; elementIndex++) { 489 var sk = _skrollables[elements[elementIndex][SKROLLABLE_ID_DOM_PROPERTY]]; 490 491 if(sk === undefined) { 492 continue; 493 } 494 495 //Parse the property string to objects 496 _parseProps(sk); 497 498 //Fill key frames with missing properties from left and right 499 _fillProps(sk); 500 } 501 502 return _instance; 503 }; 504 505 /** 506 * Transform "relative" mode to "absolute" mode. 507 * That is, calculate anchor position and offset of element. 508 */ 509 Skrollr.prototype.relativeToAbsolute = function(element, viewportAnchor, elementAnchor) { 510 var viewportHeight = documentElement.clientHeight; 511 var box = element.getBoundingClientRect(); 512 var absolute = box.top; 513 514 //#100: IE doesn't supply "height" with getBoundingClientRect. 515 var boxHeight = box.bottom - box.top; 516 517 if(viewportAnchor === ANCHOR_BOTTOM) { 518 absolute -= viewportHeight; 519 } else if(viewportAnchor === ANCHOR_CENTER) { 520 absolute -= viewportHeight / 2; 521 } 522 523 if(elementAnchor === ANCHOR_BOTTOM) { 524 absolute += boxHeight; 525 } else if(elementAnchor === ANCHOR_CENTER) { 526 absolute += boxHeight / 2; 527 } 528 529 //Compensate scrolling since getBoundingClientRect is relative to viewport. 530 absolute += _instance.getScrollTop(); 531 532 return (absolute + 0.5) | 0; 533 }; 534 535 /** 536 * Animates scroll top to new position. 537 */ 538 Skrollr.prototype.animateTo = function(top, options) { 539 options = options || {}; 540 541 var now = _now(); 542 var scrollTop = _instance.getScrollTop(); 543 544 //Setting this to a new value will automatically cause the current animation to stop, if any. 545 _scrollAnimation = { 546 startTop: scrollTop, 547 topDiff: top - scrollTop, 548 targetTop: top, 549 duration: options.duration || DEFAULT_DURATION, 550 startTime: now, 551 endTime: now + (options.duration || DEFAULT_DURATION), 552 easing: easings[options.easing || DEFAULT_EASING], 553 done: options.done 554 }; 555 556 //Don't queue the animation if there's nothing to animate. 557 if(!_scrollAnimation.topDiff) { 558 if(_scrollAnimation.done) { 559 _scrollAnimation.done.call(_instance, false); 560 } 561 562 _scrollAnimation = undefined; 563 } 564 565 return _instance; 566 }; 567 568 /** 569 * Stops animateTo animation. 570 */ 571 Skrollr.prototype.stopAnimateTo = function() { 572 if(_scrollAnimation && _scrollAnimation.done) { 573 _scrollAnimation.done.call(_instance, true); 574 } 575 576 _scrollAnimation = undefined; 577 }; 578 579 /** 580 * Returns if an animation caused by animateTo is currently running. 581 */ 582 Skrollr.prototype.isAnimatingTo = function() { 583 return !!_scrollAnimation; 584 }; 585 586 Skrollr.prototype.setScrollTop = function(top, force) { 587 _forceRender = (force === true); 588 589 if(_isMobile) { 590 _mobileOffset = Math.min(Math.max(top, 0), _maxKeyFrame); 591 } else { 592 window.scrollTo(0, top); 593 } 594 595 return _instance; 596 }; 597 598 Skrollr.prototype.getScrollTop = function() { 599 if(_isMobile) { 600 return _mobileOffset; 601 } else { 602 return window.pageYOffset || documentElement.scrollTop || body.scrollTop || 0; 603 } 604 }; 605 606 Skrollr.prototype.getMaxScrollTop = function() { 607 return _maxKeyFrame; 608 }; 609 610 Skrollr.prototype.on = function(name, fn) { 611 _listeners[name] = fn; 612 613 return _instance; 614 }; 615 616 Skrollr.prototype.off = function(name) { 617 delete _listeners[name]; 618 619 return _instance; 620 }; 621 622 Skrollr.prototype.destroy = function() { 623 var cancelAnimFrame = polyfillCAF();
624 cancelAnimFrame(_animFrame); 625 _removeAllEvents(); 626 627 _updateClass(documentElement, [NO_SKROLLR_CLASS], [SKROLLR_CLASS, SKROLLR_DESKTOP_CLASS, SKROLLR_MOBILE_CLASS]); 628 629 var skrollableIndex = 0; 630 var skrollablesLength = _skrollables.length; 631 632 for(; skrollableIndex < skrollablesLength; skrollableIndex++) { 633 _reset(_skrollables[skrollableIndex].element); 634 } 635 636 documentElement.style.overflow = body.style.overflow = 'auto'; 637 documentElement.style.height = body.style.height = 'auto'; 638 639 if(_skrollrBody) { 640 skrollr.setStyle(_skrollrBody, 'transform', 'none'); 641 } 642 643 _instance = undefined; 644 _skrollrBody = undefined; 645 _listeners = undefined; 646 _forceHeight = undefined; 647 _maxKeyFrame = 0; 648 _scale = 1; 649 _constants = undefined; 650 _mobileDeceleration = undefined; 651 _direction = 'down'; 652 _lastTop = -1; 653 _lastViewportWidth = 0; 654 _lastViewportHeight = 0; 655 _requestReflow = false; 656 _scrollAnimation = undefined; 657 _smoothScrollingEnabled = undefined; 658 _smoothScrollingDuration = undefined; 659 _smoothScrolling = undefined; 660 _forceRender = undefined; 661 _skrollableIdCounter = 0; 662 _edgeStrategy = undefined; 663 _isMobile = false; 664 _mobileOffset = 0; 665 _translateZ = undefined; 666 }; 667 668 /* 669 Private methods. 670 */ 671 672 var _initMobile = function() { 673 var initialElement; 674 var initialTouchY; 675 var initialTouchX; 676 var currentElement; 677 var currentTouchY; 678 var currentTouchX; 679 var lastTouchY; 680 var deltaY; 681 682 var initialTouchTime; 683 var currentTouchTime; 684 var lastTouchTime; 685 var deltaTime; 686 687 _addEvent(documentElement, [EVENT_TOUCHSTART, EVENT_TOUCHMOVE, EVENT_TOUCHCANCEL, EVENT_TOUCHEND].join(' '), function(e) { 688 var touch = e.changedTouches[0]; 689 690 currentElement = e.target; 691 692 //We don't want text nodes. 693 while(currentElement.nodeType === 3) { 694 currentElement = currentElement.parentNode; 695 } 696 697 currentTouchY = touch.clientY; 698 currentTouchX = touch.clientX; 699 currentTouchTime = e.timeStamp; 700 701 if(!rxTouchIgnoreTags.test(currentElement.tagName)) { 702 e.preventDefault(); 703 } 704 705 switch(e.type) { 706 case EVENT_TOUCHSTART: 707 //The last element we tapped on. 708 if(initialElement) { 709 initialElement.blur(); 710 } 711 712 _instance.stopAnimateTo(); 713 714 initialElement = currentElement; 715 716 initialTouchY = lastTouchY = currentTouchY; 717 initialTouchX = currentTouchX; 718 initialTouchTime = currentTouchTime; 719 720 break; 721 case EVENT_TOUCHMOVE: 722 //Prevent default event on touchIgnore elements in case they don't have focus yet. 723 if(rxTouchIgnoreTags.test(currentElement.tagName) && document.activeElement !== currentElement) { 724 e.preventDefault(); 725 } 726 727 deltaY = currentTouchY - lastTouchY; 728 deltaTime = currentTouchTime - lastTouchTime; 729 730 _instance.setScrollTop(_mobileOffset - deltaY, true); 731 732 lastTouchY = currentTouchY; 733 lastTouchTime = currentTouchTime; 734 break; 735 default: 736 case EVENT_TOUCHCANCEL: 737 case EVENT_TOUCHEND: 738 var distanceY = initialTouchY - currentTouchY; 739 var distanceX = initialTouchX - currentTouchX; 740 var distance2 = distanceX * distanceX + distanceY * distanceY; 741 742 //Check if it was more like a tap (moved less than 7px). 743 if(distance2 < 49) { 744 if(!rxTouchIgnoreTags.test(initialElement.tagName)) { 745 initialElement.focus(); 746 747 //It was a tap, click the element. 748 var clickEvent = document.createEvent('MouseEvents'); 749 clickEvent.initMouseEvent('click', true, true, e.view, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 0, null); 750 initialElement.dispatchEvent(clickEvent); 751 } 752 753 return; 754 } 755 756 initialElement = undefined; 757 758 var speed = deltaY / deltaTime; 759 760 //Cap speed at 3 pixel/ms. 761 speed = Math.max(Math.min(speed, 3), -3); 762 763 var duration = Math.abs(speed / _mobileDeceleration); 764 var targetOffset = speed * duration + 0.5 * _mobileDeceleration * duration * duration; 765 var targetTop = _instance.getScrollTop() - targetOffset; 766 767 //Relative duration change for when scrolling above bounds. 768 var targetRatio = 0; 769 770 //Change duration proportionally when scrolling would leave bounds. 771 if(targetTop > _maxKeyFrame) { 772 targetRatio = (_maxKeyFrame - targetTop) / targetOffset; 773 774 targetTop = _maxKeyFrame; 775 } else if(targetTop < 0) { 776 targetRatio = -targetTop / targetOffset; 777 778 targetTop = 0; 779 } 780 781 duration = duration * (1 - targetRatio); 782 783 _instance.animateTo((targetTop + 0.5) | 0, {easing: 'outCubic', duration: duration}); 784 break; 785 } 786 }); 787 788 //Just in case there has already been some native scrolling, reset it. 789 window.scrollTo(0, 0); 790 documentElement.style.overflow = body.style.overflow = 'hidden'; 791 }; 792 793 /**
794 * Updates key frames which depend on others / need to be updated on resize. 795 * That is "end" in "absolute" mode and all key frames in "relative" mode. 796 * Also handles constants, because they may change on resize. 797 */ 798 var _updateDependentKeyFrames = function() { 799 var viewportHeight = documentElement.clientHeight; 800 var processedConstants = _processConstants(); 801 var skrollable; 802 var element; 803 var anchorTarget; 804 var keyFrames; 805 var keyFrameIndex; 806 var keyFramesLength; 807 var kf; 808 var skrollableIndex; 809 var skrollablesLength; 810 var offset; 811 var constantValue; 812 813 //First process all relative-mode elements and find the max key frame. 814 skrollableIndex = 0; 815 skrollablesLength = _skrollables.length; 816 817 for(; skrollableIndex < skrollablesLength; skrollableIndex++) { 818 skrollable = _skrollables[skrollableIndex]; 819 element = skrollable.element; 820 anchorTarget = skrollable.anchorTarget; 821 keyFrames = skrollable.keyFrames; 822 823 keyFrameIndex = 0; 824 keyFramesLength = keyFrames.length; 825 826 for(; keyFrameIndex < keyFramesLength; keyFrameIndex++) { 827 kf = keyFrames[keyFrameIndex]; 828 829 offset = kf.offset; 830 constantValue = processedConstants[kf.constant] || 0; 831 832 kf.frame = offset; 833 834 if(kf.isPercentage) { 835 //Convert the offset to percentage of the viewport height. 836 offset = offset * viewportHeight; 837 838 //Absolute + percentage mode. 839 kf.frame = offset; 840 } 841 842 if(kf.mode === 'relative') { 843 _reset(element); 844 845 kf.frame = _instance.relativeToAbsolute(anchorTarget, kf.anchors[0], kf.anchors[1]) - offset; 846 847 _reset(element, true); 848 } 849 850 kf.frame += constantValue; 851 852 //Only search for max key frame when forceHeight is enabled. 853 if(_forceHeight) { 854 //Find the max key frame, but don't use one of the data-end ones for comparison. 855 if(!kf.isEnd && kf.frame > _maxKeyFrame) { 856 _maxKeyFrame = kf.frame; 857 } 858 } 859 } 860 } 861 862 //#133: The document can be larger than the maxKeyFrame we found. 863 _maxKeyFrame = Math.max(_maxKeyFrame, _getDocumentHeight()); 864 865 //Now process all data-end keyframes. 866 skrollableIndex = 0; 867 skrollablesLength = _skrollables.length; 868 869 for(; skrollableIndex < skrollablesLength; skrollableIndex++) { 870 skrollable = _skrollables[skrollableIndex]; 871 keyFrames = skrollable.keyFrames; 872 873 keyFrameIndex = 0; 874 keyFramesLength = keyFrames.length; 875 876 for(; keyFrameIndex < keyFramesLength; keyFrameIndex++) { 877 kf = keyFrames[keyFrameIndex]; 878 879 constantValue = processedConstants[kf.constant] || 0; 880 881 if(kf.isEnd) { 882 kf.frame = _maxKeyFrame - kf.offset + constantValue; 883 } 884 } 885 886 skrollable.keyFrames.sort(_keyFrameComparator); 887 } 888 }; 889 890 /** 891 * Calculates and sets the style properties for the element at the given frame. 892 * @param fakeFrame The frame to render at when smooth scrolling is enabled. 893 * @param actualFrame The actual frame we are at. 894 */ 895 var _calcSteps = function(fakeFrame, actualFrame) { 896 //Iterate over all skrollables. 897 var skrollableIndex = 0; 898 var skrollablesLength = _skrollables.length; 899 900 for(; skrollableIndex < skrollablesLength; skrollableIndex++) { 901 var skrollable = _skrollables[skrollableIndex]; 902 var element = skrollable.element; 903 var frame = skrollable.smoothScrolling ? fakeFrame : actualFrame; 904 var frames = skrollable.keyFrames; 905 var firstFrame = frames[0].frame; 906 var lastFrame = frames[frames.length - 1].frame; 907 var beforeFirst = frame < firstFrame; 908 var afterLast = frame > lastFrame; 909 var firstOrLastFrame = frames[beforeFirst ? 0 : frames.length - 1]; 910 var key; 911 var value; 912 913 //If we are before/after the first/last frame, set the styles according to the given edge strategy. 914 if(beforeFirst || afterLast) {
915 //Check if we already handled this edge case last time. 916 //Note: using setScrollTop it's possible that we jumped from one edge to the other. 917 if(beforeFirst && skrollable.edge === -1 || afterLast && skrollable.edge === 1) { 918 continue; 919 } 920 921 //Add the skrollr-before or -after class. 922 _updateClass(element, [beforeFirst ? SKROLLABLE_BEFORE_CLASS : SKROLLABLE_AFTER_CLASS], [SKROLLABLE_BEFORE_CLASS, SKROLLABLE_BETWEEN_CLASS, SKROLLABLE_AFTER_CLASS]); 923 924 //Remember that we handled the edge case (before/after the first/last keyframe). 925 skrollable.edge = beforeFirst ? -1 : 1; 926 927 switch(skrollable.edgeStrategy) { 928 case 'reset': 929 _reset(element); 930 continue; 931 case 'ease': 932 //Handle this case like it would be exactly at first/last keyframe and just pass it on. 933 frame = firstOrLastFrame.frame; 934 break; 935 default: 936 case 'set': 937 var props = firstOrLastFrame.props; 938 939 for(key in props) { 940 if(hasProp.call(props, key)) { 941 value = _interpolateString(props[key].value); 942 943 skrollr.setStyle(element, key, value); 944 } 945 } 946 947 continue; 948 } 949 } else { 950 //Did we handle an edge last time? 951 if(skrollable.edge !== 0) { 952 _updateClass(element, [SKROLLABLE_CLASS, SKROLLABLE_BETWEEN_CLASS], [SKROLLABLE_BEFORE_CLASS, SKROLLABLE_AFTER_CLASS]); 953 skrollable.edge = 0; 954 } 955 } 956 957 //Find out between which two key frames we are right now. 958 var keyFrameIndex = 0; 959 var framesLength = frames.length - 1; 960 961 for(; keyFrameIndex < framesLength; keyFrameIndex++) { 962 if(frame >= frames[keyFrameIndex].frame && frame <= frames[keyFrameIndex + 1].frame) { 963 var left = frames[keyFrameIndex]; 964 var right = frames[keyFrameIndex + 1]; 965 966 for(key in left.props) { 967 if(hasProp.call(left.props, key)) { 968 var progress = (frame - left.frame) / (right.frame - left.frame); 969 970 //Transform the current progress using the given easing function. 971 progress = left.props[key].easing(progress); 972 973 //Interpolate between the two values 974 value = _calcInterpolation(left.props[key].value, right.props[key].value, progress); 975 976 value = _interpolateString(value); 977 978 skrollr.setStyle(element, key, value); 979 } 980 } 981 982 break; 983 } 984 } 985 } 986 }; 987 988 /** 989 * Renders all elements. 990 */ 991 var _render = function() { 992 if(_requestReflow) { 993 _requestReflow = false; 994 _reflow(); 995 } 996 997 //We may render something else than the actual scrollbar position. 998 var renderTop = _instance.getScrollTop(); 999 1000 //If there's an animation, which ends in current render call, call the callback after rendering. 1001 var afterAnimationCallback; 1002 var now = _now(); 1003 var progress; 1004 1005 //Before actually rendering handle the scroll animation, if any. 1006 if(_scrollAnimation) { 1007 //It's over 1008 if(now >= _scrollAnimation.endTime) { 1009 renderTop = _scrollAnimation.targetTop; 1010 afterAnimationCallback = _scrollAnimation.done; 1011 _scrollAnimation = undefined; 1012 } else { 1013 //Map the current progress to the new progress using given easing function. 1014 progress = _scrollAnimation.easing((now - _scrollAnimation.startTime) / _scrollAnimation.duration); 1015 1016 renderTop = (_scrollAnimation.startTop + progress * _scrollAnimation.topDiff) | 0; 1017 } 1018 1019 _instance.setScrollTop(renderTop, true); 1020 } 1021 //Smooth scrolling only if there's no animation running and if we're not forcing the rendering. 1022 else if(!_forceRender) { 1023 var smoothScrollingDiff = _smoothScrolling.targetTop - renderTop; 1024 1025 //The user scrolled, start new smooth scrolling. 1026 if(smoothScrollingDiff) { 1027 _smoothScrolling = { 1028 startTop: _lastTop, 1029 topDiff: renderTop - _lastTop, 1030 targetTop: renderTop, 1031 startTime: _lastRenderCall, 1032 endTime: _lastRenderCall + _smoothScrollingDuration 1033 }; 1034 } 1035 1036 //Interpolate the internal scroll position (not the actual scrollbar). 1037 if(now <= _smoothScrolling.endTime) { 1038 //Map the current progress to the new progress using easing function. 1039 progress = easings.sqrt((now - _smoothScrolling.startTime) / _smoothScrollingDuration); 1040 1041 renderTop = (_smoothScrolling.startTop + progress * _smoothScrolling.to
1041pDiff) | 0; 1042 } 1043 } 1044 1045 //That's were we actually "scroll" on mobile. 1046 if(_isMobile && _skrollrBody) { 1047 //Set the transform ("scroll it"). 1048 skrollr.setStyle(_skrollrBody, 'transform', 'translate(0, ' + -(_mobileOffset) + 'px) ' + _translateZ); 1049 } 1050 1051 //Did the scroll position even change? 1052 if(_forceRender || _lastTop !== renderTop) { 1053 //Remember in which direction are we scrolling? 1054 _direction = (renderTop > _lastTop) ? 'down' : (renderTop < _lastTop ? 'up' : _direction); 1055 1056 _forceRender = false; 1057 1058 var listenerParams = { 1059 curTop: renderTop, 1060 lastTop: _lastTop, 1061 maxTop: _maxKeyFrame, 1062 direction: _direction 1063 }; 1064 1065 //Tell the listener we are about to render. 1066 var continueRendering = _listeners.beforerender && _listeners.beforerender.call(_instance, listenerParams); 1067 1068 //The beforerender listener function is able the cancel rendering. 1069 if(continueRendering !== false) { 1070 //Now actually interpolate all the styles. 1071 _calcSteps(renderTop, _instance.getScrollTop()); 1072 1073 //Remember when we last rendered. 1074 _lastTop = renderTop; 1075 1076 if(_listeners.render) { 1077 _listeners.render.call(_instance, listenerParams); 1078 } 1079 } 1080 1081 if(afterAnimationCallback) { 1082 afterAnimationCallback.call(_instance, false); 1083 } 1084 } 1085 1086 _lastRenderCall = now; 1087 }; 1088 1089 /** 1090 * Parses the properties for each key frame of the given skrollable. 1091 */ 1092 var _parseProps = function(skrollable) { 1093 //Iterate over all key frames 1094 var keyFrameIndex = 0; 1095 var keyFramesLength = skrollable.keyFrames.length; 1096 1097 for(; keyFrameIndex < keyFramesLength; keyFrameIndex++) { 1098 var frame = skrollable.keyFrames[keyFrameIndex]; 1099 var easing; 1100 var value; 1101 var prop; 1102 var props = {}; 1103 1104 var match; 1105 1106 while((match = rxPropValue.exec(frame.props)) !== null) { 1107 prop = match[1]; 1108 value = match[2]; 1109 1110 easing = prop.match(rxPropEasing); 1111 1112 //Is there an easing specified for this prop? 1113 if(easing !== null) { 1114 prop = easing[1]; 1115 easing = easing[2]; 1116 } else { 1117 easing = DEFAULT_EASING; 1118 } 1119 1120 //Exclamation point at first position forces the value to be taken literal. 1121 value = value.indexOf('!') ? _parseProp(value) : [value.slice(1)]; 1122 1123 //Save the prop for this key frame with his value and easing function 1124 props[prop] = { 1125 value: value, 1126 easing: easings[easing] 1127 }; 1128 } 1129 1130 frame.props = props; 1131 } 1132 }; 1133 1134 /** 1135 * Parses a value extracting numeric values and generating a format string 1136 * for later interpolation of the new values in old string. 1137 * 1138 * @param val The CSS value to be parsed. 1139 * @return Something like ["rgba(?%,?%, ?%,?)", 100, 50, 0, .7] 1140 * where the first element is the format string later used 1141 * and all following elements are the numeric value. 1142 */ 1143 var _parseProp = function(val) { 1144 var numbers = []; 1145 1146 //One special case, where floats don't work. 1147 //We replace all occurences of rgba colors 1148 //which don't use percentage notation with the percentage notation. 1149 rxRGBAIntegerColor.lastIndex = 0; 1150 val = val.replace(rxRGBAIntegerColor, function(rgba) { 1151 return rgba.replace(rxNumericValue, function(n) { 1152 return n / 255 * 100 + '%'; 1153 }); 1154 }); 1155 1156 //Handle prefixing of "gradient" values. 1157 //For now only the prefixed value will be set. Unprefixed isn't supported anyway. 1158 if(theDashedCSSPrefix) { 1159 rxGradient.lastIndex = 0; 1160 val = val.replace(rxGradient, function(s) { 1161 return theDashedCSSPrefix + s; 1162 }); 1163 } 1164 1165 //Now parse ANY number inside this string and create a format str
1165ing. 1166 val = val.replace(rxNumericValue, function(n) { 1167 numbers.push(+n); 1168 return '{?}'; 1169 }); 1170 1171 //Add the formatstring as first value. 1172 numbers.unshift(val); 1173 1174 return numbers; 1175 }; 1176 1177 /** 1178 * Fills the key frames with missing left and right hand properties. 1179 * If key frame 1 has property X and key frame 2 is missing X, 1180 * but key frame 3 has X again, then we need to assign X to key frame 2 too. 1181 * 1182 * @param sk A skrollable. 1183 */ 1184 var _fillProps = function(sk) { 1185 //Will collect the properties key frame by key frame 1186 var propList = {}; 1187 var keyFrameIndex; 1188 var keyFramesLength; 1189 1190 //Iterate over all key frames from left to right 1191 keyFrameIndex = 0; 1192 keyFramesLength = sk.keyFrames.length; 1193 1194 for(; keyFrameIndex < keyFramesLength; keyFrameIndex++) { 1195 _fillPropForFrame(sk.keyFrames[keyFrameIndex], propList); 1196 } 1197 1198 //Now do the same from right to fill the last gaps 1199 1200 propList = {}; 1201 1202 //Iterate over all key frames from right to left 1203 keyFrameIndex = sk.keyFrames.length - 1; 1204 1205 for(; keyFrameIndex >= 0; keyFrameIndex--) { 1206 _fillPropForFrame(sk.keyFrames[keyFrameIndex], propList); 1207 } 1208 }; 1209 1210 var _fillPropForFrame = function(frame, propList) { 1211 var key; 1212 1213 //For each key frame iterate over all right hand properties and assign them, 1214 //but only if the current key frame doesn't have the property by itself 1215 for(key in propList) { 1216 //The current frame misses this property, so assign it. 1217 if(!hasProp.call(frame.props, key)) { 1218 frame.props[key] = propList[key]; 1219 } 1220 } 1221 1222 //Iterate over all props of the current frame and collect them 1223 for(key in frame.props) { 1224 propList[key] = frame.props[key]; 1225 } 1226 }; 1227 1228 /** 1229 * Calculates the new values for two given values array. 1230 */ 1231 var _calcInterpolation = function(val1, val2, progress) { 1232 var valueIndex; 1233 var val1Length = val1.length; 1234 1235 //They both need to have the same length 1236 if(val1Length !== val2.length) { 1237 throw 'Can\'t interpolate between "' + val1[0] + '" and "' + val2[0] + '"'; 1238 } 1239 1240 //Add the format string as first element. 1241 var interpolated = [val1[0]]; 1242 1243 valueIndex = 1; 1244 1245 for(; valueIndex < val1Length; valueIndex++) { 1246 //That's the line where the two numbers are actually interpolated. 1247 interpolated[valueIndex] = val1[valueIndex] + ((val2[valueIndex] - val1[valueIndex]) * progress); 1248 } 1249 1250 return interpolated; 1251 }; 1252 1253 /** 1254 * Interpolates the numeric values into the format string. 1255 */ 1256 var _interpolateString = function(val) { 1257 var valueIndex = 1; 1258 1259 rxInterpolateString.lastIndex = 0; 1260 1261 return val[0].replace(rxInterpolateString, function() { 1262 return val[valueIndex++]; 1263 }); 1264 }; 1265 1266 /** 1267 * Resets the class and style attribute to what it was before skrollr manipulated the element. 1268 * Also remembers the values it had before reseting, in order to undo the reset. 1269 */ 1270 var _reset = function(elements, undo) { 1271 //We accept a single element or an array of elements. 1272 elements = [].concat(elements); 1273 1274 var skrollable; 1275 var element; 1276 var elementsIndex = 0; 1277 var elementsLength = elements.length; 1278 1279 for(; elementsIndex < elementsLength; elementsIndex++) { 1280 element = elements[elementsIndex]; 1281 skrollable = _skrollables[element[SKROLLABLE_ID_DOM_PROPERTY]]; 1282 1283 //Couldn't find the skrollable for this DOM element. 1284 if(!skrollable) { 1285 continue; 1286 } 1287 1288 if(undo) { 1289 //Reset class and style to the "dirty" (set by skrollr) values. 1290 element.style.cssText = skrollable.dirtyStyleAttr; 1291 _updateClass(element, skrollable.dirtyClassAttr); 1292 } else { 1293 //Remember the "dirty" (set by skrollr) class and style. 1294 skrollable.dirtyStyleAttr = element.style.cssText; 1295 skrollable.dirtyClassAttr = _getClass(element); 1296 1297 //Reset class and style to what it originally was. 1298 element.style.cssText = skrollable.styleAttr; 1299 _updateClass(element, skrollable.classAttr); 1300 } 1301 } 1302 }; 1303 1304 /** 1305 * Detects support for 3d transforms by applying it to the skrollr-body. 1306 */ 1307 var _detect3DTransforms = function() { 1308 _translateZ = 'translateZ(0)'; 1309 skrollr.setStyle(_skrollrBody, 'transform', _translateZ); 1310 1311 var computedStyle = getStyle(_skrollrBody); 1312 var computedTransform = computedStyle.getPropertyValue('transform'); 1313 var computedTransformWithPrefix = computedStyle.getPropertyValue(theDashedCSSPrefix + 'transform'); 1314 var has3D = (computedTransform && computedTransform !== 'none') || (computedTransformWithPrefix && computedTransformWithPrefix !== 'none'); 1315 1316 if(!has3D) { 1317 _translateZ = ''; 1318 } 1319 }; 1320 1321 /** 1322 * Set the CSS property on the given element. Sets prefixed properties as well. 1323 */ 1324 skrollr.setStyle = function(el, prop, val) { 1325 var style = el.style; 1326 1327 //Camel case. 1328 prop = prop.replace(rxCamelCase, rxCamelCaseFn).replace('-', ''); 1329 1330 //Make sure z-index gets a <integer>. 1331 //This is the only <integer> case we need to handle. 1332 if(prop === 'zIndex') { 1333 if(isNaN(val)) { 1334 //If it's not a number, don't touch it. 1335 //It could for example be "auto" (#351). 1336 style[prop] = val; 1337 } else { 1338 //Floor the number. 1339 style[prop] = '' + (val | 0); 1340 } 1341 } 1342 //#64: "float" can't be set across browsers. Needs to use "cssFloat" for all except IE. 1343 else if(prop === 'float') { 1344 style.styleFloat = style.cssFloat = val; 1345 } 1346 else { 1347 //Need try-catch for old IE. 1348 try { 1349 //Set prefixed property if there's a prefix. 1350 if(theCSSPrefix) { 1351 style[theCSSPrefix + prop.slice(0,1).toUpperCase() + prop.slice(1)] = val; 1352 } 1353 1354 //Set unprefixed. 1355 style[prop] = val; 1356 } catch(ignore) {} 1357 } 1358 }; 1359 1360 /** 1361 * Cross browser event handling. 1362 */ 1363 var _addEvent = skrollr.addEvent = function(element, names, callback) { 1364 var intermediate = function(e) { 1365 //Normalize IE event stuff. 1366 e = e || window.event; 1367 1368 if(!e.target) { 1369 e.target = e.srcElement; 1370 } 1371 1372 if(!e.preventDefault) { 1373 e.preventDefault = function() { 1374 e.returnValue = false;
1375 }; 1376 } 1377 1378 return callback.call(this, e); 1379 }; 1380 1381 names = names.split(' '); 1382 1383 var name; 1384 var nameCounter = 0; 1385 var namesLength = names.length; 1386 1387 for(; nameCounter < namesLength; nameCounter++) { 1388 name = names[nameCounter]; 1389 1390 if(element.addEventListener) { 1391 element.addEventListener(name, callback, false); 1392 } else { 1393 element.attachEvent('on' + name, intermediate); 1394 } 1395 1396 //Remember the events to be able to flush them later. 1397 _registeredEvents.push({ 1398 element: element, 1399 name: name, 1400 listener: callback 1401 }); 1402 } 1403 }; 1404 1405 var _removeEvent = skrollr.removeEvent = function(element, names, callback) { 1406 names = names.split(' '); 1407 1408 var nameCounter = 0; 1409 var namesLength = names.length; 1410 1411 for(; nameCounter < namesLength; nameCounter++) { 1412 if(element.removeEventListener) { 1413 element.removeEventListener(names[nameCounter], callback, false); 1414 } else { 1415 element.detachEvent('on' + names[nameCounter], callback); 1416 } 1417 } 1418 }; 1419 1420 var _removeAllEvents = function() { 1421 var eventData; 1422 var eventCounter = 0; 1423 var eventsLength = _registeredEvents.length; 1424 1425 for(; eventCounter < eventsLength; eventCounter++) { 1426 eventData = _registeredEvents[eventCounter]; 1427 1428 _removeEvent(eventData.element, eventData.name, eventData.listener); 1429 } 1430 1431 _registeredEvents = []; 1432 }; 1433 1434 var _reflow = function() { 1435 var pos = _instance.getScrollTop(); 1436 1437 //Will be recalculated by _updateDependentKeyFrames. 1438 _maxKeyFrame = 0; 1439 1440 if(_forceHeight && !_isMobile) { 1441 //un-"force" the height to not mess with the calculations in _updateDependentKeyFrames (#216). 1442 body.style.height = 'auto'; 1443 } 1444 1445 _updateDependentKeyFrames(); 1446 1447 if(_forceHeight && !_isMobile) { 1448 //"force" the height. 1449 body.style.height = (_maxKeyFrame + documentElement.clientHeight) + 'px'; 1450 } 1451 1452 //The scroll offset may now be larger than needed (on desktop the browser/os prevents scrolling farther than the bottom). 1453 if(_isMobile) { 1454 _instance.setScrollTop(Math.min(_instance.getScrollTop(), _maxKeyFrame)); 1455 } else { 1456 //Remember and reset the scroll pos (#217). 1457 _instance.setScrollTop(pos, true); 1458 } 1459 1460 _forceRender = true; 1461 }; 1462 1463 /* 1464 * Returns a copy of the constants object where all functions and strings have been evaluated. 1465 */ 1466 var _processConstants = function() { 1467 var viewportHeight = documentElement.clientHeight; 1468 var copy = {}; 1469 var prop; 1470 var value; 1471 1472 for(prop in _constants) { 1473 value = _constants[prop]; 1474 1475 if(typeof value === 'function') { 1476 value = value.call(_instance); 1477 } 1478 //Percentage offset. 1479 else if((/p$/).test(value)) { 1480 value = (value.slice(0, -1) / 100) * viewportHeight; 1481 } 1482 1483 copy[prop] = value; 1484 } 1485 1486 return copy; 1487 }; 1488 1489 /* 1490 * Returns the height of the document. 1491 */ 1492 var _getDocumentHeight = function() { 1493 var skrollrBodyHeight = (_skrollrBody && _skrollrBody.offsetHeight || 0); 1494 var bodyHeight = Math.max(skrollrBodyHeight, body.scrollHeight, body.offsetHeight, documentElement.scrollHeight, documentElement.offsetHeight, documentElement.clientHeight); 1495 1496 return bodyHeight - documentElement.clientHeight; 1497 }; 1498 1499 /** 1500 * Returns a string of space separated classnames for the current element. 1501 * Works with SVG as well. 1502 */ 1503 var _getClass = function(element) { 1504 var prop = 'className'; 1505 1506 //SVG support by using className.baseVal instead of just className. 1507 if(window.SVGElement && element instanceof window.SVGElement) { 1508 element = element[prop]; 1509 prop = 'baseVal'; 1510 } 1511 1512 return element[prop]; 1513 }; 1514 1515 /** 1516 * Adds and removes a CSS classes. 1517 * Works with SVG as well.
1518 * add and remove are arrays of strings, 1519 * or if remove is ommited add is a string and overwrites all classes. 1520 */ 1521 var _updateClass = function(element, add, remove) { 1522 var prop = 'className'; 1523 1524 //SVG support by using className.baseVal instead of just className. 1525 if(window.SVGElement && element instanceof window.SVGElement) { 1526 element = element[prop]; 1527 prop = 'baseVal'; 1528 } 1529 1530 //When remove is ommited, we want to overwrite/set the classes. 1531 if(remove === undefined) { 1532 element[prop] = add; 1533 return; 1534 } 1535 1536 //Cache current classes. We will work on a string before passing back to DOM. 1537 var val = element[prop]; 1538 1539 //All classes to be removed. 1540 var classRemoveIndex = 0; 1541 var removeLength = remove.length; 1542 1543 for(; classRemoveIndex < removeLength; classRemoveIndex++) { 1544 val = _untrim(val).replace(_untrim(remove[classRemoveIndex]), ' '); 1545 } 1546 1547 val = _trim(val); 1548 1549 //All classes to be added. 1550 var classAddIndex = 0; 1551 var addLength = add.length; 1552 1553 for(; classAddIndex < addLength; classAddIndex++) { 1554 //Only add if el not already has class. 1555 if(_untrim(val).indexOf(_untrim(add[classAddIndex])) === -1) { 1556 val += ' ' + add[classAddIndex]; 1557 } 1558 } 1559 1560 element[prop] = _trim(val); 1561 }; 1562 1563 var _trim = function(a) { 1564 return a.replace(rxTrim, ''); 1565 }; 1566 1567 /** 1568 * Adds a space before and after the string. 1569 */ 1570 var _untrim = function(a) { 1571 return ' ' + a + ' '; 1572 }; 1573 1574 var _now = Date.now || function() { 1575 return +new Date(); 1576 }; 1577 1578 var _keyFrameComparator = function(a, b) { 1579 return a.frame - b.frame; 1580 }; 1581 1582 /* 1583 * Private variables. 1584 */ 1585 1586 //Singleton 1587 var _instance; 1588 1589 /* 1590 A list of all elements which should be animated associated with their the metadata. 1591 Exmaple skrollable with two key frames animating from 100px width to 20px: 1592 1593 skrollable = { 1594 element: <the DOM element>, 1595 styleAttr: <style attribute of the element before skrollr>, 1596 classAttr: <class attribute of the element before skrollr>, 1597 keyFrames: [ 1598 { 1599 frame: 100, 1600 props: { 1601 width: { 1602 value: ['{?}px', 100], 1603 easing: <reference to easing function> 1604 } 1605 }, 1606 mode: "absolute" 1607 }, 1608 { 1609 frame: 200, 1610 props: { 1611 width: { 1612 value: ['{?}px', 20], 1613 easing: <reference to easing function> 1614 } 1615 }, 1616 mode: "absolute" 1617 } 1618 ] 1619 }; 1620 */ 1621 var _skrollables; 1622 1623 var _skrollrBody; 1624 1625 var _listeners; 1626 var _forceHeight; 1627 var _maxKeyFrame = 0; 1628 1629 var _scale = 1; 1630 var _constants; 1631 1632 var _mobileDeceleration; 1633 1634 //Current direction (up/down). 1635 var _direction = 'down'; 1636 1637 //The last top offset value. Needed to determine direction. 1638 var _lastTop = -1; 1639 1640 //The last time we called the render method (doesn't mean we rendered!). 1641 var _lastRenderCall = _now(); 1642 1643 //For detecting if it actually resized (#271). 1644 var _lastViewportWidth = 0; 1645 var _lastViewportHeight = 0; 1646 1647 var _requestReflow = false; 1648 1649 //Will contain data about a running scrollbar animation, if any. 1650 var _scrollAnimation; 1651 1652 var _smoothScrollingEnabled; 1653 1654 var _smoothScrollingDuration; 1655 1656 //Will contain settins for smooth scrolling if enabled. 1657 var _smoothScrolling; 1658 1659 //Can be set by any operation/event to force rendering even if the scrollbar didn't move. 1660 var _forceRender; 1661 1662 //Each skrollable gets an unique ID incremented for each skrollable. 1663 //The ID is the index in the _skrollables array. 1664 var _skrollableIdCounter = 0; 1665 1666 var _edgeStrategy; 1667 1668 1669 //Mobile specific vars. Will be stripped by UglifyJS when not in use. 1670 var _isMobile = false;
1671 1672 //The virtual scroll offset when using mobile scrolling. 1673 var _mobileOffset = 0; 1674 1675 //If the browser supports 3d transforms, this will be filled with 'translateZ(0)' (empty string otherwise). 1676 var _translateZ; 1677 1678 //Will contain data about registered events by skrollr. 1679 var _registeredEvents = []; 1680 1681 //Animation frame id returned by RequestAnimationFrame (or timeout when RAF is not supported). 1682 var _animFrame; 1683 1684 //Expose skrollr as either a global variable or a require.js module 1685 if(typeof define === 'function' && define.amd) { 1686 define('skrollr', function () { 1687 return skrollr; 1688 }); 1689 } else { 1690 window.skrollr = skrollr; 1691 } 1692 1693}(window, document)); 1694 1695 1696 1697 1698 1699 1700 1701 1702// Magnific Popup v0.9.9 by Dmitry Semenov 1703// http://bit.ly/magnific-popup#build=inline+image+gallery+retina+imagezoom+fastclick 1704(function(a){var b="Close",c="BeforeClose",d="AfterClose",e="BeforeAppend",f="MarkupParse",g="Open",h="Change",i="mfp",j="."+i,k="mfp-ready",l="mfp-removing",m="mfp-prevent-close",n,o=function(){},p=!!window.jQuery,q,r=a(window),s,t,u,v,w,x=function(a,b){n.ev.on(i+a+j,b)},y=function(b,c,d,e){var f=document.createElement("div");return f.className="mfp-"+b,d&&(f.innerHTML=d),e?c&&c.appendChild(f):(f=a(f),c&&f.appendTo(c)),f},z=function(b,c){n.ev.triggerHandler(i+b,c),n.st.callbacks&&(b=b.charAt(0).toLowerCase()+b.slice(1),n.st.callbacks[b]&&n.st.callbacks[b].apply(n,a.isArray(c)?c:[c]))},A=function(b){if(b!==w||!n.currTemplate.closeBtn)n.currTemplate.closeBtn=a(n.st.closeMarkup.replace("%title%",n.st.tClose)),w=b;return n.currTemplate.closeBtn},B=function(){a.magnificPopup.instance||(n=new o,n.init(),a.magnificPopup.instance=n)},C=function(){var a=document.createElement("p").style,b=["ms","O","Moz","Webkit"];if(a.transition!==undefined)return!0;while(b.length)if(b.pop()+"Transition"in a)return!0;return!1};o.prototype={constructor:o,init:function(){var b=navigator.appVersion;n.isIE7=b.indexOf("MSIE 7.")!==-1,n.isIE8=b.indexOf("MSIE 8.")!==-1,n.isLowIE=n.isIE7||n.isIE8,n.isAndroid=/android/gi.test(b),n.isIOS=/iphone|ipad|ipod/gi.test(b),n.supportsTransition=C(),n.probablyMobile=n.isAndroid||n.isIOS||/(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent),t=a(document),n.popupsCache={}},open:function(b){s||(s=a(document.body));var c;if(b.isObj===!1){n.items=b.items.toArray(),n.index=0;var d=b.items,e;for(c=0;c<d.length;c++){e=d[c],e.parsed&&(e=e.el[0]);if(e===b.el[0]){n.index=c;break}}}else n.items=a.isArray(b.items)?b.items:[b.items],n.index=b.index||0;if(n.isOpen){n.updateItemHTML();return}n.types=[],v="",b.mainEl&&b.mainEl.length?n.ev=b.mainEl.eq(0):n.ev=t,b.key?(n.popupsCache[b.key]||(n.popupsCache[b.key]={}),n.currTemplate=n.popupsCache[b.key]):n.currTemplate={},n.st=a.extend(!0,{},a.magnificPopup.defaults,b),n.fixedContentPos=n.st.fixedContentPos==="auto"?!n.probablyMobile:n.st.fixedContentPos,n.st.modal&&(n.st.closeOnContentClick=!1,n.st.closeOnBgClick=!1,n.st.showCloseBtn=!1,n.st.enableEscapeKey=!1),n.bgOverlay||(n.bgOverlay=y("bg").on("click"+j,function(){n.close()}),n.wrap=y("wrap").attr("tabindex",-1).on("click"+j,function(a){n._checkIfClose(a.target)&&n.close()}),n.container=y("container",n.wrap)),n.contentContainer=y("content"),n.st.preloader&&(n.preloader=y("preloader",n.container,n.st.tLoading));var h=a.magnificPopup.modules;for(c=0;c<h.length;c++){var i=h[c];i=i.charAt(0).toUpperCase()+i.slice(1),n["init"+i].call(n)}z("BeforeOpen"),n.st.showCloseBtn&&(n.st.closeBtnInside?(x(f,function(a,b,c,d){c.close_replaceWith=A(d.type)}),v+=" mfp-close-btn-in"):n.wrap.append(A())),n.st.alignTop&&(v+=" mfp-align-top"),n.fixedContentPos?n.wrap.css({overflow:n.st.overflowY,overflowX:"hidden",overflowY:n.st.overflowY}):n.wrap.css({top:r.scrollTop(),position:"absolute"}),(n.st.fixedBgPos===!1||n.st.fixedBgPos==="auto"&&!n.fixedContentPos)&&n.bgOverlay.css({height:t.height(),position:"absolute"}),n.st.enableEscapeKey&&t.on("keyup"+j,function(a){a.keyCode===27&&n.close()}),r.on("resize"+j,function(){n.updateSize()}),n.st.closeOnContentClick||(v+=" mfp-auto-cursor"),v&&n.wrap.addClass(v);var l=n.wH=r.height(),m={};if(n.fixedContentPos&&n._hasScrollBar(l)){var o=n._getScrollbarSize();o&&(m.marginRight=o)}n.fixedContentPos&&(n.isIE7?a("body, html").css("overflow","hidden"):m.overflow="hidden");var p=n.st.mainClass;return n.isIE7&&(p+=" mfp-ie7"),p&&n._addClassToMFP(p),n.updateItemHTML(),z("BuildControls"),a("html").css(m),n.bgOverlay.add(n.wrap).prependTo(n.st.prepe
1704ndTo||s),n._lastFocusedEl=document.activeElement,setTimeout(function(){n.content?(n._addClassToMFP(k),n._setFocus()):n.bgOverlay.addClass(k),t.on("focusin"+j,n._onFocusIn)},16),n.isOpen=!0,n.updateSize(l),z(g),b},close:function(){if(!n.isOpen)return;z(c),n.isOpen=!1,n.st.removalDelay&&!n.isLowIE&&n.supportsTransition?(n._addClassToMFP(l),setTimeout(function(){n._close()},n.st.removalDelay)):n._close()},_close:function(){z(b);var c=l+" "+k+" ";n.bgOverlay.detach(),n.wrap.detach(),n.container.empty(),n.st.mainClass&&(c+=n.st.mainClass+" "),n._removeClassFromMFP(c);if(n.fixedContentPos){var e={marginRight:""};n.isIE7?a("body, html").css("overflow",""):e.overflow="",a("html").css(e)}t.off("keyup"+j+" focusin"+j),n.ev.off(j),n.wrap.attr("class","mfp-wrap").removeAttr("style"),n.bgOverlay.attr("class","mfp-bg"),n.container.attr("class","mfp-container"),n.st.showCloseBtn&&(!n.st.closeBtnInside||n.currTemplate[n.currItem.type]===!0)&&n.currTemplate.closeBtn&&n.currTemplate.closeBtn.detach(),n._lastFocusedEl&&a(n._lastFocusedEl).focus(),n.currItem=null,n.content=null,n.currTemplate=null,n.prevHeight=0,z(d)},updateSize:function(a){if(n.isIOS){var b=document.documentElement.clientWidth/window.innerWidth,c=window.innerHeight*b;n.wrap.css("height",c),n.wH=c}else n.wH=a||r.height();n.fixedContentPos||n.wrap.css("height",n.wH),z("Resize")},updateItemHTML:function(){var b=n.items[n.index];n.contentContainer.detach(),n.content&&n.content.detach(),b.parsed||(b=n.parseEl(n.index));var c=b.type;z("BeforeChange",[n.currItem?n.currItem.type:"",c]),n.currItem=b;if(!n.currTemplate[c]){var d=n.st[c]?n.st[c].markup:!1;z("FirstMarkupParse",d),d?n.currTemplate[c]=a(d):n.currTemplate[c]=!0}u&&u!==b.type&&n.container.removeClass("mfp-"+u+"-holder");var e=n["get"+c.charAt(0).toUpperCase()+c.slice(1)](b,n.currTemplate[c]);n.appendContent(e,c),b.preloaded=!0,z(h,b),u=b.type,n.container.prepend(n.contentContainer),z("AfterChange")},appendContent:function(a,b){n.content=a,a?n.st.showCloseBtn&&n.st.closeBtnInside&&n.currTemplate[b]===!0?n.content.find(".mfp-close").length||n.content.append(A()):n.content=a:n.content="",z(e),n.container.addClass("mfp-"+b+"-holder"),n.contentContainer.append(n.content)},parseEl:function(b){var c=n.items[b],d;c.tagName?c={el:a(c)}:(d=c.type,c={data:c,src:c.src});if(c.el){var e=n.types;for(var f=0;f<e.length;f++)if(c.el.hasClass("mfp-"+e[f])){d=e[f];break}c.src=c.el.attr("data-mfp-src"),c.src||(c.src=c.el.attr("href"))}return c.type=d||n.st.type||"inline",c.index=b,c.parsed=!0,n.items[b]=c,z("ElementParse",c),n.items[b]},addGroup:function(a,b){var c=function(c){c.mfpEl=this,n._openClick(c,a,b)};b||(b={});var d="click.magnificPopup";b.mainEl=a,b.items?(b.isObj=!0,a.off(d).on(d,c)):(b.isObj=!1,b.delegate?a.off(d).on(d,b.delegate,c):(b.items=a,a.off(d).on(d,c)))},_openClick:function(b,c,d){var e=d.midClick!==undefined?d.midClick:a.magnificPopup.defaults.midClick;if(!e&&(b.which===2||b.ctrlKey||b.metaKey))return;var f=d.disableOn!==undefined?d.disableOn:a.magnificPopup.defaults.disableOn;if(f)if(a.isFunction(f)){if(!f.call(n))return!0}else if(r.width()<f)return!0;b.type&&(b.preventDefault(),n.isOpen&&b.stopPropagation()),d.el=a(b.mfpEl),d.delegate&&(d.items=c.find(d.delegate)),n.open(d)},updateStatus:function(a,b){if(n.preloader){q!==a&&n.container.removeClass("mfp-s-"+q),!b&&a==="loading"&&(b=n.st.tLoading);var c={status:a,text:b};z("UpdateStatus",c),a=c.status,b=c.text,n.preloader.html(b),n.preloader.find("a").on("click",function(a){a.stopImmediatePropagation()}),n.container.addClass("mfp-s-"+a),q=a}},_checkIfClose:function(b){if(a(b).hasClass(m))return;var c=n.st.closeOnContentClick,d=n.st.closeOnBgClick;if(c&&d)return!0;if(!n.content||a(b).hasClass("mfp-close")||n.preloader&&b===n.preloader[0])return!0;if(b!==n.content[0]&&!a.contains(n.content[0],b)){if(d&&a.contains(document,b))return!0}else if(c)return!0;return!1},_addClassToMFP:function(a){n.bgOverlay.addClass(a),n.wrap.addClass(a)},_removeClassFromMFP:function(a){this.bgOverlay.removeClass(a),n.wrap.removeClass(a)},_hasScrollBar:function(a){return(n.isIE7?t.height():document.body.scrollHeight)>
1704(a||r.height())},_setFocus:function(){(n.st.focus?n.content.find(n.st.focus).eq(0):n.wrap).focus()},_onFocusIn:function(b){if(b.target!==n.wrap[0]&&!a.contains(n.wrap[0],b.target))return n._setFocus(),!1},_parseMarkup:function(b,c,d){var e;d.data&&(c=a.extend(d.data,c)),z(f,[b,c,d]),a.each(c,function(a,c){if(c===undefined||c===!1)return!0;e=a.split("_");if(e.length>1){var d=b.find(j+"-"+e[0]);if(d.length>0){var f=e[1];f==="replaceWith"?d[0]!==c[0]&&d.replaceWith(c):f==="img"?d.is("img")?d.attr("src",c):d.replaceWith('<img src="'+c+'" class="'+d.attr("class")+'" />'):d.attr(e[1],c)}}else b.find(j+"-"+a).html(c)})},_getScrollbarSize:function(){if(n.scrollbarSize===undefined){var a=document.createElement("div");a.id="mfp-sbm",a.style.cssText="width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;",document.body.appendChild(a),n.scrollbarSize=a.offsetWidth-a.clientWidth,document.body.removeChild(a)}return n.scrollbarSize}},a.magnificPopup={instance:null,proto:o.prototype,modules:[],open:function(b,c){return B(),b?b=a.extend(!0,{},b):b={},b.isObj=!0,b.index=c||0,this.instance.open(b)},close:function(){return a.magnificPopup.instance&&a.magnificPopup.instance.close()},registerModule:function(b,c){c.options&&(a.magnificPopup.defaults[b]=c.options),a.extend(this.proto,c.proto),this.modules.push(b)},defaults:{disableOn:0,key:null,midClick:!1,mainClass:"",preloader:!0,focus:"",closeOnContentClick:!1,closeOnBgClick:!0,closeBtnInside:!0,showCloseBtn:!0,enableEscapeKey:!0,modal:!1,alignTop:!1,removalDelay:0,prependTo:null,fixedContentPos:"auto",fixedBgPos:"auto",overflowY:"auto",closeMarkup:'<button title="%title%" type="button" class="mfp-close">×</button>',tClose:"Close (Esc)",tLoading:"Loading..."}},a.fn.magnificPopup=function(b){B();var c=a(this);if(typeof b=="string")if(b==="open"){var d,e=p?c.data("magnificPopup"):c[0].magnificPopup,f=parseInt(arguments[1],10)||0;e.items?d=e.items[f]:(d=c,e.delegate&&(d=d.find(e.delegate)),d=d.eq(f)),n._openClick({mfpEl:d},c,e)}else n.isOpen&&n[b].apply(n,Array.prototype.slice.call(arguments,1));else b=a.extend(!0,{},b),p?c.data("magnificPopup",b):c[0].magnificPopup=b,n.addGroup(c,b);return c};var D="inline",E,F,G,H=function(){G&&(F.after(G.addClass(E)).detach(),G=null)};a.magnificPopup.registerModule(D,{options:{hiddenClass:"hide",markup:"",tNotFound:"Content not found"},proto:{initInline:function(){n.types.push(D),x(b+"."+D,function(){H()})},getInline:function(b,c){H();if(b.src){var d=n.st.inline,e=a(b.src);if(e.length){var f=e[0].parentNode;f&&f.tagName&&(F||(E=d.hiddenClass,F=y(E),E="mfp-"+E),G=e.after(F).detach().removeClass(E)),n.updateStatus("ready")}else n.updateStatus("error",d.tNotFound),e=a("<div>");return b.inlineElement=e,e}return n.updateStatus("ready"),n._parseMarkup(c,{},b),c}}});var I,J=function(b){if(b.data&&b.data.title!==undefined)return b.data.title;var c=n.st.image.titleSrc;if(c){if(a.isFunction(c))return c.call(n,b);if(b.el)return b.el.attr(c)||""}return""};a.magnificPopup.registerModule("image",{options:{markup:'<div class="mfp-figure"><div class="mfp-close"></div><figure><div class="mfp-img"></div><figcaption><div class="mfp-bottom-bar"><div class="mfp-title"></div><div class="mfp-counter"></div></div></figcaption></figure></div>',cursor:"mfp-zoom-out-cur",titleSrc:"title",verticalFit:!0,tError:'<a href="%url%">The image</a> could not be loaded.'},proto:{initImage:function(){var a=n.st.image,c=".image";n.types.push("image"),x(g+c,function(){n.currItem.type==="image"&&a.cursor&&s.addClass(a.cursor)}),x(b+c,function(){a.cursor&&s.removeClass(a.cursor),r.off("resize"+j)}),x("Resize"+c,n.resizeImage),n.isLowIE&&x("AfterChange",n.resizeImage)},resizeImage:function(){var a=n.currItem;if(!a||!a.img)return;if(n.st.image.verticalFit){var b=0;n.isLowIE&&(b=parseInt(a.img.css("padding-top"),10)+parseInt(a.img.css("padding-bottom"),10)),a.img.css("max-height",n.wH-b)}},_onImageHasSize:function(a){a.img&&(a.hasSize=!0,I&&clearInterval(I),a.isCheckingImgSize=!1,z("ImageHasSize",a),a.imgHidden&&(n.content&&n.content.removeClass("mfp-loading"),a.imgHidden=!1))},findImageSize:function(a){var b=0,c=a.img[0],d=function(e){I&&clearInterval(I),I=setInterval(function(){if(c.naturalWi
1704dth>0){n._onImageHasSize(a);return}b>200&&clearInterval(I),b++,b===3?d(10):b===40?d(50):b===100&&d(500)},e)};d(1)},getImage:function(b,c){var d=0,e=function(){b&&(b.img[0].complete?(b.img.off(".mfploader"),b===n.currItem&&(n._onImageHasSize(b),n.updateStatus("ready")),b.hasSize=!0,b.loaded=!0,z("ImageLoadComplete")):(d++,d<200?setTimeout(e,100):f()))},f=function(){b&&(b.img.off(".mfploader"),b===n.currItem&&(n._onImageHasSize(b),n.updateStatus("error",g.tError.replace("%url%",b.src))),b.hasSize=!0,b.loaded=!0,b.loadError=!0)},g=n.st.image,h=c.find(".mfp-img");if(h.length){var i=document.createElement("img");i.className="mfp-img",b.img=a(i).on("load.mfploader",e).on("error.mfploader",f),i.src=b.src,h.is("img")&&(b.img=b.img.clone()),i=b.img[0],i.naturalWidth>0?b.hasSize=!0:i.width||(b.hasSize=!1)}return n._parseMarkup(c,{title:J(b),img_replaceWith:b.img},b),n.resizeImage(),b.hasSize?(I&&clearInterval(I),b.loadError?(c.addClass("mfp-loading"),n.updateStatus("error",g.tError.replace("%url%",b.src))):(c.removeClass("mfp-loading"),n.updateStatus("ready")),c):(n.updateStatus("loading"),b.loading=!0,b.hasSize||(b.imgHidden=!0,c.addClass("mfp-loading"),n.findImageSize(b)),c)}}});var K,L=function(){return K===undefined&&(K=document.createElement("p").style.MozTransform!==undefined),K};a.magnificPopup.registerModule("zoom",{options:{enabled:!1,easing:"ease-in-out",duration:300,opener:function(a){return a.is("img")?a:a.find("img")}},proto:{initZoom:function(){var a=n.st.zoom,d=".zoom",e;if(!a.enabled||!n.supportsTransition)return;var f=a.duration,g=function(b){var c=b.clone().removeAttr("style").removeAttr("class").addClass("mfp-animated-image"),d="all "+a.duration/1e3+"s "+a.easing,e={position:"fixed",zIndex:9999,left:0,top:0,"-webkit-backface-visibility":"hidden"},f="transition";return e["-webkit-"+f]=e["-moz-"+f]=e["-o-"+f]=e[f]=d,c.css(e),c},h=function(){n.content.css("visibility","visible")},i,j;x("BuildControls"+d,function(){if(n._allowZoom()){clearTimeout(i),n.content.css("visibility","hidden"),e=n._getItemToZoom();if(!e){h();return}j=g(e),j.css(n._getOffset()),n.wrap.append(j),i=setTimeout(function(){j.css(n._getOffset(!0)),i=setTimeout(function(){h(),setTimeout(function(){j.remove(),e=j=null,z("ZoomAnimationEnded")},16)},f)},16)}}),x(c+d,function(){if(n._allowZoom()){clearTimeout(i),n.st.removalDelay=f;if(!e){e=n._getItemToZoom();if(!e)return;j=g(e)}j.css(n._getOffset(!0)),n.wrap.append(j),n.content.css("visibility","hidden"),setTimeout(function(){j.css(n._getOffset())},16)}}),x(b+d,function(){n._allowZoom()&&(h(),j&&j.remove(),e=null)})},_allowZoom:function(){return n.currItem.type==="image"},_getItemToZoom:function(){return n.currItem.hasSize?n.currItem.img:!1},_getOffset:function(b){var c;b?c=n.currItem.img:c=n.st.zoom.opener(n.currItem.el||n.currItem);var d=c.offset(),e=parseInt(c.css("padding-top"),10),f=parseInt(c.css("padding-bottom"),10);d.top-=a(window).scrollTop()-e;var g={width:c.width(),height:(p?c.innerHeight():c[0].offsetHeight)-f-e};return L()?g["-moz-transform"]=g.transform="translate("+d.left+"px,"+d.top+"px)":(g.left=d.left,g.top=d.top),g}}});var M=function(a){var b=n.items.length;return a>b-1?a-b:a<0?b+a:a},N=function(a,b,c){return a.replace(/%curr%/gi,b+1).replace(/%total%/gi,c)};a.magnificPopup.registerModule("gallery",{options:{enabled:!1,arrowMarkup:'<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',preload:[0,2],navigateByImgClick:!0,arrows:!0,tPrev:"Previous (Left arrow key)",tNext:"Next (Right arrow key)",tCounter:"%curr% of %total%"},proto:{initGallery:function(){var c=n.st.gallery,d=".mfp-gallery",e=Boolean(a.fn.mfpFastClick);n.direction=!0;if(!c||!c.enabled)return!1;v+=" mfp-gallery",x(g+d,function(){c.navigateByImgClick&&n.wrap.on("click"+d,".mfp-img",function(){if(n.items.length>1)return n.next(),!1}),t.on("keydown"+d,function(a){a.keyCode===37?n.prev():a.keyCode===39&&n.next()})}),x("UpdateStatus"+d,function(a,b){b.text&&(b.text=N(b.text,n.currItem.index,n.items.length))}),x(f+d,function(a,b,d,e){var f=n.items.length;d.counter=f>1?N(c.tCounter,e.index,f):""}),x("BuildControls"+d,function(){if(n.items.length>1&&c.arrows&&!n.arrowLeft){var b=c.arrowMarkup,d=n.arrowLeft=a(b.replace(/%title%/gi,c.tPrev).replace(/%dir%/gi,"left")).addClass(m),f=n.arrowRight=a(b.replace(/%title%/gi,c.tNext).replace(/%dir%/gi,"right")).addClass(m),g=e?"mfpFastClick":"click";d[g](function(){n.prev()}),f[g](function(){n.next()}),n.isIE7&&(y("b",d[0],!1,!0),y("a",d[0],!1,!0),y("b",f[0],!1,!0),y("a",f[0],!1,!0)),n.container.append(d.add(f))}}),x(h+d,function(){n._preloadTimeout&&clearTimeout(n._preloadTimeout),n._preloadTimeout=setTimeout(function(){n.preloadNearbyImages(),n._preloadTimeout=null},16)}
1704),x(b+d,function(){t.off(d),n.wrap.off("click"+d),n.arrowLeft&&e&&n.arrowLeft.add(n.arrowRight).destroyMfpFastClick(),n.arrowRight=n.arrowLeft=null})},next:function(){n.direction=!0,n.index=M(n.index+1),n.updateItemHTML()},prev:function(){n.direction=!1,n.index=M(n.index-1),n.updateItemHTML()},goTo:function(a){n.direction=a>=n.index,n.index=a,n.updateItemHTML()},preloadNearbyImages:function(){var a=n.st.gallery.preload,b=Math.min(a[0],n.items.length),c=Math.min(a[1],n.items.length),d;for(d=1;d<=(n.direction?c:b);d++)n._preloadItem(n.index+d);for(d=1;d<=(n.direction?b:c);d++)n._preloadItem(n.index-d)},_preloadItem:function(b){b=M(b);if(n.items[b].preloaded)return;var c=n.items[b];c.parsed||(c=n.parseEl(b)),z("LazyLoad",c),c.type==="image"&&(c.img=a('<img class="mfp-img" />').on("load.mfploader",function(){c.hasSize=!0}).on("error.mfploader",function(){c.hasSize=!0,c.loadError=!0,z("LazyLoadError",c)}).attr("src",c.src)),c.preloaded=!0}}});var O="retina";a.magnificPopup.registerModule(O,{options:{replaceSrc:function(a){return a.src.replace(/\.\w+$/,function(a){return"@2x"+a})},ratio:1},proto:{initRetina:function(){if(window.devicePixelRatio>1){var a=n.st.retina,b=a.ratio;b=isNaN(b)?b():b,b>1&&(x("ImageHasSize."+O,function(a,c){c.img.css({"max-width":c.img[0].naturalWidth/b,width:"100%"})}),x("ElementParse."+O,function(c,d){d.src=a.replaceSrc(d,b)}))}}}}),function(){var b=1e3,c="ontouchstart"in window,d=function(){r.off("touchmove"+f+" touchend"+f)},e="mfpFastClick",f="."+e;a.fn.mfpFastClick=function(e){return a(this).each(function(){var g=a(this),h;if(c){var i,j,k,l,m,n;g.on("touchstart"+f,function(a){l=!1,n=1,m=a.originalEvent?a.originalEvent.touches[0]:a.touches[0],j=m.clientX,k=m.clientY,r.on("touchmove"+f,function(a){m=a.originalEvent?a.originalEvent.touches:a.touches,n=m.length,m=m[0];if(Math.abs(m.clientX-j)>10||Math.abs(m.clientY-k)>10)l=!0,d()}).on("touchend"+f,function(a){d();if(l||n>1)return;h=!0,a.preventDefault(),clearTimeout(i),i=setTimeout(function(){h=!1},b),e()})})}g.on("click"+f,function(){h||e()})})},a.fn.destroyMfpFastClick=function(){a(this).off("touchstart"+f+" click"+f),c&&r.off("touchmove"+f+" touchend"+f)}}(),B()})(window.jQuery||window.Zepto) 1705 1706 1707 1708 1709/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */ 1710 1711/* Begin Elixir theme specific javascript */ 1712 1713/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */ 1714 1715// 'elixir' is the global object for the elixir RapidWeaver theme 1716var elixir = {}; 1717 1718// reduce potential conflicts with other scripts on the page 1719elixir.jQuery = jQuery.noConflict(true); 1720var $elixir = elixir.jQuery; 1721 1722// Create unique object and namespace for theme functions 1723elixir.themeFunctions = {}; 1724 1725// Define a closure 1726elixir.themeFunctions = (function() { 1727 // When jQuery is used it will be available as $ and jQuery but only 1728 // inside the closure. 1729 var jQuery = elixir.jQuery; 1730 var $ = jQuery; 1731 var $elixir = jQuery.noConflict(); 1732 1733 1734 1735 /////////////////////////////// 1736 // Mobile Detection 1737 /////////////////////////////// 1738 1739 function isMobile(){ 1740 return ( 1741 (navigator.userAgent.match(/Android/i)) || 1742 (navigator.userAgent.match(/webOS/i)) || 1743 (navigator.userAgent.match(/iPhone/i)) || 1744 (navigator.userAgent.match(/iPod/i)) || 1745 (navigator.userAgent.match(/iPad/i)) || 1746 (navigator.userAgent.match(/BlackBerry/)) 1747 ); 1748 } 1749 1750 1751 function extraContent() { 1752 /* 1753 # ExtraContent (jQuery) # 1754 1755 AUTHOR: Adam Merrifield <http://adam.merrifield.ca> 1756 Giuseppe Caruso <http://www.bonsai-studio.net/> 1757 VERSION: r1.4.2 1758 DATE: 12-16-10 09:40 1759 1760 USAGE: 1761 - be sure to include a copy of the jQuery libraray in the <head> 1762 before the inclusion of this function 1763 - noConflict mode is optional but highly recommended 1764 - call this script in the <head> 1765 - change the value of ecValue to match the number of ExtraContent 1766 areas in your theme 1767 */ 1768 $elixir(document).ready(function(){ 1769 var extraContent = (function() { 1770 // change ecValue to suit your theme 1771 var ecValue = 4; 1772 for (i=1;i<=ecValue;i++) 1773 { 1774 $elixir('#myExtraContent'+i+' script').remove(); 1775 $elixir('#myExtraContent'+i).appendTo('#extraContainer'+i).show(); 1776 } 1777 })(); 1778 }); 1779 1780 // Automatically show ExtraContent areas if they contain content. 1781 1782 if ($elixir('#myExtraContent1').length > 0) { 1783 $elixir('#extraContent1').show(); 1784 } 1785 1786 if ($elixir('#myExtraContent2').length > 0) { 1787 $elixir('#extraContent2').show(); 1788 } 1789 1790 if ($elixir('#myExtraContent3').length > 0) { 1791 $elixir('#extraContent3').show(); 1792 } 1793 1794 if ($elixir('#myExtraContent4').length > 0) { 1795 $elixir('#extraContent4').show(); 1796 $elixir('#inner_footer').css({'margin-top' : '20px'}) 1797 } 1798 } 1799 1800 function blogEntryTopper() { 1801 /* 1802 1803 -=-= BLOG ENTRY TOPPER =-=- 1804 1805 DESCRIPTION: Handles the blog entry topper images. Finds image in blog entries with an ALT tag 1806 of 'blogEntryTopper' and then moves image to the top of the entry. The theme's 1807 CSS autosizes the image to fit the blog entry's width. 1808 1809 AUTHOR: Adam Shiver / Elixir Graphics 1810 VERSION: 1.0b 1811 DATE: May 9, 2011 1812 1813 */ 1814 1815 blogEntryTopperImg = $elixir('.blog-entry img[alt^=blogEntryTopper]'); 1816 blogEntryTopperImg.hide(); 1817 blogEntryTopperImg.each(function() { 1818 /* Applies special class to the image */ 1819 $elixir(this).addClass('blogEntryTopperImg'); 1820 /* Moves topper image to the top of the blog entry and wraps it in a div with a class of blogEntryTopper_wrapper */ 1821 $elixir(this).prependTo($elixir(this).parent().parent()).show().wrap('<div class="blogEntryTopper_wrapper" />'); 1822 }); 1823 } 1824 1825 1826 function generalThemeFunctions() { 1827 // 1828 // Add class to last blog entry on a page 1829 // 1830 $elixir('.blog-entry').last().addClass('lastBlogChild'); 1831 1832 // 1833 // Display the Breadcrumb trail only if it is enabled and has content 1834 // 1835 if ( $elixir.trim( $elixir('#breadcrumb').html() ).length ) {
1836 $elixir('#breadcrumb').show(); 1837 } 1838 1839 1840 // 1841 // Move social badges to their appropriate locations. 1842 // 1843 $elixir('a.myBadge').appendTo('#social_badges'); 1844 1845 } 1846 1847 function navigation() { 1848 1849 if ($elixir('nav#secondary_navigation ul ul li').length > 0) { 1850 $elixir('nav#secondary_navigation ul ul').show(); 1851 } 1852 1853 $elixir('nav#main_navigation li, nav#mobile_navigation li').has('ul').addClass('hasChild'); 1854 1855 } 1856 1857 1858 function photoAlbum() { 1859 // 1860 // Handles lightbox for Photo Album page 1861 // 1862 $elixir('.thumbnail-frame').each(function(){ 1863 var thumbnailCaption = $elixir('.thumbnail-caption', this).text(); 1864 $elixir('a',this).attr({ 1865 'href' : $elixir('a img',this).attr('src').replace(/thumb/i,'full'), 1866 'title' : thumbnailCaption, 1867 }); 1868 }); 1869 1870 $elixir('.thumbnail-frame').magnificPopup({ 1871 delegate: 'a', // child items selector, by clicking on it popup will open 1872 type: 'image', 1873 titleSrc: 'title', 1874 gallery:{ 1875 enabled: true, 1876 navigateByImgClick: false, 1877 preload: [0,2] 1878 }, 1879 1880 closeOnContentClick: true, 1881 1882 mainClass: 'mfp-with-zoom', // this class is for CSS animation below 1883 1884 zoom: { 1885 enabled: true, // By default it's false, so don't forget to enable it 1886 1887 duration: 300, // duration of the effect, in milliseconds 1888 easing: 'ease-in-out', // CSS transition easing function 1889 1890 // The "opener" function should return the element from which popup will be zoomed in 1891 // and to which popup will be scaled down 1892 // By defailt it looks for an image tag: 1893 opener: function(openerElement) { 1894 // openerElement is the element on which popup was initialized, in this case its <a> tag 1895 // you don't need to add "opener" option if this code matches your needs, it's defailt one. 1896 return openerElement.is('img') ? openerElement : openerElement.find('img'); 1897 } 1898 } 1899 1900 }); 1901 1902 // 1903 // Hanldes caption spacing when image captions are turned on for thumbnails. 1904 // 1905 if ($elixir('.thumbnail-wrap .thumbnail-caption').length > 0) { 1906 $elixir('.thumbnail-wrap').css({'margin-bottom' : '80px'}); 1907 } 1908 } 1909 1910 $elixir(document).ready(function() { 1911 generalThemeFunctions(); 1912 navigation(); 1913 photoAlbum(); 1914 extraContent(); 1915 blogEntryTopper(); 1916 }); 1917 1918 $elixir(window).load(function() { 1919 banner_height = $elixir('#banner img').height(); 1920 $elixir('#alignment_wrapper .items li .outerContainer').css({'height':banner_height}); 1921 }); 1922 1923 $elixir(window).resize(function() { 1924 banner_height = $elixir('#banner img').height(); 1925 $elixir('#alignment_wrapper .items li .outerContainer').css({'height':banner_height}); 1926 }); 1927 1928/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */ 1929 1930/* End Elixir theme specific javascript */ 1931 1932/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */ 1933 1934 1935 1936 1937 1938/* 1939 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 1940 * 1941 * Uses the built in easing capabilities added In jQuery 1.1 1942 * to offer multiple easing options 1943 * 1944 * TERMS OF USE - jQuery Easing 1945 * 1946 * Open source under the BSD License. 1947 * 1948 * Copyright é 2008 George McGinley Smith 1949 * All rights reserved. 1950 * 1951 * Redistribution and use in source and binary forms, with or without modification, 1952 * are permitted provided that the following conditions are met: 1953 * 1954 * Redistributions of source code must retain the above copyright notice, this list of 1955 * conditions and the following disclaimer. 1956 * Redistributions in binary form must reproduce the above copyright notice, this list 1957 * of conditions and the following disclaimer in the documentation and/or other materials 1958 * provided with the distribution. 1959 * 1960 * Neither the name of the author nor the names of contributors may be used to endorse 1961 * or promote products derived from this software without specific prior written permission. 1962 * 1963 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 1964 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1965 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 1966 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
vendor: 5,087 bytes, lines 1967-2112
1967 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 1968 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 1969 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 1970 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 1971 * OF THE POSSIBILITY OF SUCH DAMAGE. 1972 * 1973*/ 1974 1975// t: current time, b: begInnIng value, c: change In value, d: duration 1976jQuery.easing['jswing'] = jQuery.easing['swing']; 1977 1978jQuery.extend( jQuery.easing, 1979{ 1980 def: 'easeOutQuad', 1981 swing: function (x, t, b, c, d) { 1982 //alert(jQuery.easing.default); 1983 return jQuery.easing[jQuery.easing.def](x, t, b, c, d); 1984 }, 1985 easeInQuad: function (x, t, b, c, d) { 1986 return c*(t/=d)*t + b; 1987 }, 1988 easeOutQuad: function (x, t, b, c, d) { 1989 return -c *(t/=d)*(t-2) + b; 1990 }, 1991 easeInOutQuad: function (x, t, b, c, d) { 1992 if ((t/=d/2) < 1) return c/2*t*t + b; 1993 return -c/2 * ((--t)*(t-2) - 1) + b; 1994 }, 1995 easeInCubic: function (x, t, b, c, d) { 1996 return c*(t/=d)*t*t + b; 1997 }, 1998 easeOutCubic: function (x, t, b, c, d) { 1999 return c*((t=t/d-1)*t*t + 1) + b; 2000 }, 2001 easeInOutCubic: function (x, t, b, c, d) { 2002 if ((t/=d/2) < 1) return c/2*t*t*t + b; 2003 return c/2*((t-=2)*t*t + 2) + b; 2004 }, 2005 easeInQuart: function (x, t, b, c, d) { 2006 return c*(t/=d)*t*t*t + b; 2007 }, 2008 easeOutQuart: function (x, t, b, c, d) { 2009 return -c * ((t=t/d-1)*t*t*t - 1) + b; 2010 }, 2011 easeInOutQuart: function (x, t, b, c, d) { 2012 if ((t/=d/2) < 1) return c/2*t*t*t*t + b; 2013 return -c/2 * ((t-=2)*t*t*t - 2) + b; 2014 }, 2015 easeInQuint: function (x, t, b, c, d) { 2016 return c*(t/=d)*t*t*t*t + b; 2017 }, 2018 easeOutQuint: function (x, t, b, c, d) { 2019 return c*((t=t/d-1)*t*t*t*t + 1) + b; 2020 }, 2021 easeInOutQuint: function (x, t, b, c, d) { 2022 if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; 2023 return c/2*((t-=2)*t*t*t*t + 2) + b; 2024 }, 2025 easeInSine: function (x, t, b, c, d) { 2026 return -c * Math.cos(t/d * (Math.PI/2)) + c + b; 2027 }, 2028 easeOutSine: function (x, t, b, c, d) { 2029 return c * Math.sin(t/d * (Math.PI/2)) + b; 2030 }, 2031 easeInOutSine: function (x, t, b, c, d) { 2032 return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; 2033 }, 2034 easeInExpo: function (x, t, b, c, d) { 2035 return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; 2036 }, 2037 easeOutExpo: function (x, t, b, c, d) { 2038 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; 2039 }, 2040 easeInOutExpo: function (x, t, b, c, d) { 2041 if (t==0) return b; 2042 if (t==d) return b+c; 2043 if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; 2044 return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; 2045 }, 2046 easeInCirc: function (x, t, b, c, d) { 2047 return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; 2048 }, 2049 easeOutCirc: function (x, t, b, c, d) { 2050 return c * Math.sqrt(1 - (t=t/d-1)*t) + b; 2051 }, 2052 easeInOutCirc: function (x, t, b, c, d) { 2053 if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; 2054 return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; 2055 }, 2056 easeInElastic: function (x, t, b, c, d) { 2057 var s=1.70158;var p=0;var a=c; 2058 if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 2059 if (a < Math.abs(c)) { a=c; var s=p/4; } 2060 else var s = p/(2*Math.PI) * Math.asin (c/a); 2061 return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 2062 }, 2063 easeOutElastic: function (x, t, b, c, d) { 2064 var s=1.70158;var p=0;var a=c; 2065 if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 2066 if (a < Math.abs(c)) { a=c; var s=p/4; } 2067 else var s = p/(2*Math.PI) * Math.asin (c/a); 2068 return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; 2069 }, 2070 easeInOutElastic: function (x, t, b, c, d) { 2071 var s=1.70158;var p=0;var a=c; 2072 if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); 2073 if (a < Math.abs(c)) { a=c; var s=p/4; } 2074 else var s = p/(2*Math.PI) * Math.asin (c/a); 2075 if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 2076 return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; 2077 }, 2078 easeInBack: function (x, t, b, c, d, s) { 2079 if (s == undefined) s = 1.70158; 2080 return c*(t/=d)*t*((s+1)*t - s) + b; 2081 }, 2082 easeOutBack: function (x, t, b, c, d, s) { 2083 if (s == undefined) s = 1.70158; 2084 return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; 2085 }, 2086 easeInOutBack: function (x, t, b, c, d, s) { 2087 if (s == undefined) s = 1.70158; 2088 if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; 2089 return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; 2090 }, 2091 easeInBounce: function (x, t, b, c, d) { 2092 return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; 2093 }, 2094 easeOutBounce: function (x, t, b, c, d) { 2095 if ((t/=d) < (1/2.75)) { 2096 return c*(7.5625*t*t) + b; 2097 } else if (t < (2/2.75)) { 2098 return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; 2099 } else if (t < (2.5/2.75)) { 2100 return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; 2101 } else { 2102 return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; 2103 } 2104 }, 2105 easeInOutBounce: function (x, t, b, c, d) { 2106 if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; 2107 return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; 2108 } 2109}); 2110 2111/* 2112 *
2113 * TERMS OF USE - EASING EQUATIONS 2114 * 2115 * Open source under the BSD License. 2116 * 2117 * Copyright é 2001 Robert Penner 2118 * All rights reserved. 2119 * 2120 * Redistribution and use in source and binary forms, with or without modification, 2121 * are permitted provided that the following conditions are met: 2122 * 2123 * Redistributions of source code must retain the above copyright notice, this list of 2124 * conditions and the following disclaimer. 2125 * Redistributions in binary form must reproduce the above copyright notice, this list 2126 * of conditions and the following disclaimer in the documentation and/or other materials 2127 * provided with the distribution. 2128 * 2129 * Neither the name of the author nor the names of contributors may be used to endorse 2130 * or promote products derived from this software without specific prior written permission. 2131 * 2132 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 2133 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 2134 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2135 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 2136 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 2137 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2138 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 2139 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 2140 * OF THE POSSIBILITY OF SUCH DAMAGE. 2141 * 2142 */ 2143 2144 2145/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */ 2146 2147 2148 2149/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */ 2150 2151 2152 2153 2154})(elixir.themeFunctions);
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.