1 2var fixto = (function ($, window, document) { 3 4 // Start Computed Style. Please do not modify this module here. Modify it from its own repo. See address below. 5 6 /*! Computed Style - v0.1.0 - 2012-07-19 7 * https://github.com/bbarakaci/computed-style 8 * Copyright (c) 2012 Burak Barakaci; Licensed MIT */ 9 var computedStyle = (function() { 10 var computedStyle = { 11 getAll : function(element){ 12 return document.defaultView.getComputedStyle(element); 13 }, 14 get : function(element, name){ 15 return this.getAll(element)[name]; 16 }, 17 toFloat : function(value){ 18 return parseFloat(value, 10) || 0; 19 }, 20 getFloat : function(element,name){ 21 return this.toFloat(this.get(element, name)); 22 }, 23 _getAllCurrentStyle : function(element) { 24 return element.currentStyle; 25 } 26 }; 27 28 if (document.documentElement.currentStyle) { 29 computedStyle.getAll = computedStyle._getAllCurrentStyle; 30 } 31 32 return computedStyle; 33 34 }()); 35 36 // End Computed Style. Modify whatever you want to. 37 38 var mimicNode = (function(){ 39 /* 40 Class Mimic Node 41 Dependency : Computed Style 42 Tries to mimick a dom node taking his styles, dimensions. May go to his repo if gets mature. 43 */ 44 45 function MimicNode(element) { 46 this.element = element; 47 this.replacer = document.createElement('div'); 48 this.replacer.style.visibility = 'hidden'; 49 this.hide(); 50 element.parentNode.insertBefore(this.replacer, element); 51 } 52 53 MimicNode.prototype = { 54 replace : function(){ 55 var rst = this.replacer.style; 56 var styles = computedStyle.getAll(this.element); 57 58 // rst.width = computedStyle.width(this.element) + 'px'; 59 // rst.height = this.element.offsetHeight + 'px'; 60 61 // Setting offsetWidth 62 rst.width = this._width(); 63 rst.height = this._height(); 64 65 // Adopt margins 66 rst.marginTop = styles.marginTop; 67 rst.marginBottom = styles.marginBottom; 68 rst.marginLeft = styles.marginLeft; 69 rst.marginRight = styles.marginRight; 70 71 // Adopt positioning 72 rst.cssFloat = styles.cssFloat; 73 rst.styleFloat = styles.styleFloat; //ie8; 74 rst.position = styles.position; 75 rst.top = styles.top; 76 rst.right = styles.right; 77 rst.bottom = styles.bottom; 78 rst.left = styles.left; 79 // rst.borderStyle = styles.borderStyle; 80 81 rst.display = styles.display; 82 83 }, 84 85 hide: function () { 86 this.replacer.style.display = 'none'; 87 }, 88 89 _width : function(){ 90 return this.element.getBoundingClientRect().width + 'px'; 91 }, 92 93 _widthOffset : function(){ 94 return this.element.offsetWidth + 'px'; 95 }, 96 97 _height : function(){ 98 return this.element.getBoundingClientRect().height + 'px'; 99 }, 100 101 _heightOffset : function(){ 102 return this.element.offsetHeight + 'px'; 103 }, 104 105 destroy: function () { 106 $(this.replacer).remove(); 107 108 // set properties to null to break references 109 for (var prop in this) { 110 if (this.hasOwnProperty(prop)) { 111 this[prop] = null; 112 } 113 } 114 } 115 }; 116 117 var bcr = document.documentElement.getBoundingClientRect(); 118 if(!bcr.width){ 119 MimicNode.prototype._width = MimicNode.prototype._widthOffset; 120 MimicNode.prototype._height = MimicNode.prototype._heightOffset; 121 } 122 123 return { 124 MimicNode:MimicNode, 125 computedStyle:computedStyle 126 }; 127 }()); 128 129 // Class handles vendor prefixes 130 function Prefix() { 131 // Cached vendor will be stored when it is detected 132 this._vendor = null; 133 134 //this._dummy = document.createElement('div'); 135 } 136 137 Prefix.prototype = { 138 139 _vendors: { 140 webkit: { cssPrefix: '-webkit-', jsPrefix: 'Webkit'}, 141 moz: { cssPrefix: '-moz-', jsPrefix: 'Moz'}, 142 ms: { cssPrefix: '-ms-', jsPrefix: 'ms'}, 143 opera: { cssPrefix: '-o-', jsPrefix: 'O'} 144 }, 145 146 _prefixJsProperty: function(vendor, prop) { 147 return vendor.jsPrefix + prop[0].toUpperCase() + prop.substr(1); 148 }, 149 150 _prefixValue: function(vendor, value) { 151 return vendor.cssPrefix + value; 152 }, 153 154 _valueSupported: function(prop, value, dummy) { 155 // IE8 will throw Illegal Argument when you attempt to set a not supported value. 156 try { 157 dummy.style[prop] = value; 158 return dummy.style[prop] === value; 159 } 160 catch(er) { 161 return false;
162 } 163 }, 164 165 /** 166 * Returns true if the property is supported 167 * @param {string} prop Property name 168 * @returns {boolean} 169 */ 170 propertySupported: function(prop) { 171 // Supported property will return either inine style value or an empty string. 172 // Undefined means property is not supported. 173 return document.documentElement.style[prop] !== undefined; 174 }, 175 176 /** 177 * Returns prefixed property name for js usage 178 * @param {string} prop Property name 179 * @returns {string|null} 180 */ 181 getJsProperty: function(prop) { 182 // Try native property name first. 183 if(this.propertySupported(prop)) { 184 return prop; 185 } 186 187 // Prefix it if we know the vendor already 188 if(this._vendor) { 189 return this._prefixJsProperty(this._vendor, prop); 190 } 191 192 // We don't know the vendor, try all the possibilities 193 var prefixed; 194 for(var vendor in this._vendors) { 195 prefixed = this._prefixJsProperty(this._vendors[vendor], prop); 196 if(this.propertySupported(prefixed)) { 197 // Vendor detected. Cache it. 198 this._vendor = this._vendors[vendor]; 199 return prefixed; 200 } 201 } 202 203 // Nothing worked 204 return null; 205 }, 206 207 /** 208 * Returns supported css value for css property. Could be used to check support or get prefixed value string. 209 * @param {string} prop Property 210 * @param {string} value Value name 211 * @returns {string|null} 212 */ 213 getCssValue: function(prop, value) { 214 // Create dummy element to test value 215 var dummy = document.createElement('div'); 216 217 // Get supported property name 218 var jsProperty = this.getJsProperty(prop); 219 220 // Try unprefixed value 221 if(this._valueSupported(jsProperty, value, dummy)) { 222 return value; 223 } 224 225 var prefixedValue; 226 227 // If we know the vendor already try prefixed value 228 if(this._vendor) { 229 prefixedValue = this._prefixValue(this._vendor, value); 230 if(this._valueSupported(jsProperty, prefixedValue, dummy)) { 231 return prefixedValue; 232 } 233 } 234 235 // Try all vendors 236 for(var vendor in this._vendors) { 237 prefixedValue = this._prefixValue(this._vendors[vendor], value); 238 if(this._valueSupported(jsProperty, prefixedValue, dummy)) { 239 // Vendor detected. Cache it. 240 this._vendor = this._vendors[vendor]; 241 return prefixedValue; 242 } 243 } 244 // No support for value 245 return null; 246 } 247 }; 248 249 var prefix = new Prefix(); 250 251 // We will need this frequently. Lets have it as a global until we encapsulate properly. 252 var transformJsProperty = prefix.getJsProperty('transform'); 253 254 // Will hold if browser creates a positioning context for fixed elements. 255 var fixedPositioningContext; 256 257 // Checks if browser creates a positioning context for fixed elements. 258 // Transform rule will create a positioning context on browsers who follow the spec. 259 // Ie for example will fix it according to documentElement 260 // TODO: Other css rules also effects. perspective creates at chrome but not in firefox. transform-style preserve3d effects. 261 function checkFixedPositioningContextSupport() { 262 var support = false; 263 var parent = document.createElement('div'); 264 var child = document.createElement('div'); 265 parent.appendChild(child); 266 parent.style[transformJsProperty] = 'translate(0)'; 267 // Make sure there is space on top of parent 268 parent.style.marginTop = '10px'; 269 parent.style.visibility = 'hidden'; 270 child.style.position = 'fixed'; 271 child.style.top = 0; 272 document.body.appendChild(parent); 273 var rect = child.getBoundingClientRect(); 274 // If offset top is greater than 0 meand transformed element created a positioning context. 275 if(rect.top > 0) { 276 support = true; 277 } 278 // Remove dummy content 279 document.body.removeChild(parent); 280 return support; 281 } 282 283 // It will return null if position sticky is not supported 284 var nativeStickyValue = null; // prefix.getCssValue('position', 'sticky'); 285 286 // It will return null if position fixed is not supported 287 var fixedPositionValue = prefix.getCssValue('position', 'fixed'); 288 289 // Dirty business 290 var ie = navigator.appName === 'Microsoft Internet Explorer'; 291 var ieversion; 292 293 if(ie){ 294 ieversion = parseFloat(navigator.appVersion.split("MSIE")[1]); 295 } 296 297 function FixTo(child, parent, options) { 298 this.child = child; 299 this._$child = $(child); 300 this.parent = parent; 301 this.options = { 302 className: 'fixto-fixed', 303 top: 0 304 }; 305 this._setOptions(options); 306 } 307 308 FixTo.prototype = { 309 // Returns the total outerHeight of the elements passed to mind option. Will return 0 if none. 310 _mindtop: function () { 311 var top = 0; 312 if(this._$mind) { 313 var el; 314 var rect; 315 var height; 316 for(var i=0, l=this._$mind.length; i<l; i++) { 317 el = this._$mind[i]; 318 rect = el.getBoundingClientRect(); 319 if(rect.height) { 320 top += rect.height; 321 } 322 else { 323 var styles = computedStyle.getAll(el); 324 top += el.offsetHeight + computedStyle.toFloat(styles.marginTop) + computedStyle.toFloat(styles.marginBottom); 325 } 326 } 327 } 328 return top; 329 }, 330 331 // Public method to stop the behaviour of this instance. 332 stop: function () { 333 this._stop(); 334 this._running = false;
335 }, 336 337 // Public method starts the behaviour of this instance. 338 start: function () { 339 340 // Start only if it is not running not to attach event listeners multiple times. 341 if(!this._running) { 342 this._start(); 343 this._running = true; 344 } 345 }, 346 347 //Public method to destroy fixto behaviour 348 destroy: function () { 349 this.stop(); 350 351 this._destroy(); 352 353 // Remove jquery data from the element 354 this._$child.removeData('fixto-instance'); 355 356 // set properties to null to break references 357 for (var prop in this) { 358 if (this.hasOwnProperty(prop)) { 359 this[prop] = null; 360 } 361 } 362 }, 363 364 _setOptions: function(options) { 365 $.extend(this.options, options); 366 if(this.options.mind) { 367 this._$mind = $(this.options.mind); 368 } 369 if(this.options.zIndex) { 370 this.child.style.zIndex = this.options.zIndex; 371 } 372 }, 373 374 setOptions: function(options) { 375 this._setOptions(options); 376 this.refresh(); 377 }, 378 379 // Methods could be implemented by subclasses 380 381 _stop: function() { 382 383 }, 384 385 _start: function() { 386 387 }, 388 389 _destroy: function() { 390 391 }, 392 393 refresh: function() { 394 395 } 396 }; 397 398 // Class FixToContainer 399 function FixToContainer(child, parent, options) { 400 FixTo.call(this, child, parent, options); 401 this._replacer = new mimicNode.MimicNode(child); 402 this._ghostNode = this._replacer.replacer; 403 404 this._saveStyles(); 405
406 this._saveViewportHeight(); 407 408 // Create anonymous functions and keep references to register and unregister events. 409 this._proxied_onscroll = this._bind(this._onscroll, this); 410 this._proxied_onresize = this._bind(this._onresize, this); 411 412 this.start(); 413 } 414 415 FixToContainer.prototype = new FixTo(); 416 417 $.extend(FixToContainer.prototype, { 418 419 // Returns an anonymous function that will call the given function in the given context 420 _bind : function (fn, context) { 421 return function () { 422 return fn.call(context); 423 }; 424 }, 425 426 // at ie8 maybe only in vm window resize event fires everytime an element is resized. 427 _toresize : ieversion===8 ? document.documentElement : window, 428 429 _onscroll: function _onscroll() { 430 this._scrollTop = document.documentElement.scrollTop || document.body.scrollTop; 431 this._parentBottom = (this.parent.offsetHeight + this._fullOffset('offsetTop', this.parent)); 432 433 if (this.options.mindBottomPadding !== false) { 434 this._parentBottom -= computedStyle.getFloat(this.parent, 'paddingBottom'); 435 } 436 437 if (!this.fixed) { 438 439 var childStyles = computedStyle.getAll(this.child); 440 441 if ( 442 this._scrollTop < this._parentBottom && 443 this._scrollTop > (this._fullOffset('offsetTop', this.child) - this.options.top - this._mindtop()) && 444 this._viewportHeight > (this.child.offsetHeight + computedStyle.toFloat(childStyles.marginTop) + computedStyle.toFloat(childStyles.marginBottom)) 445 ) { 446 447 this._fix(); 448 this._adjust(); 449 } 450 } else { 451 if (this._scrollTop > this._parentBottom || this._scrollTop < (this._fullOffset('offsetTop', this._ghostNode) - this.options.top - this._mindtop())) { 452 this._unfix(); 453 return; 454 } 455 this._adjust(); 456 } 457 }, 458 459 _adjust: function _adjust() { 460 var top = 0; 461 var mindTop = this._mindtop(); 462 var diff = 0; 463 var childStyles = computedStyle.getAll(this.child); 464 var context = null; 465 466 if(fixedPositioningContext) { 467 // Get positioning context. 468 context = this._getContext(); 469 if(context) { 470 // There is a positioning context. Top should be according to the context. 471 top = Math.abs(context.getBoundingClientRect().top); 472 } 473 } 474 475 diff = (this._parentBottom - this._scrollTop) - (this.child.offsetHeight + computedStyle.toFloat(childStyles.marginBottom) + mindTop + this.options.top); 476 477 if(diff>0) { 478 diff = 0; 479 } 480 481 this.child.style.top = (diff + mindTop + top + this.options.top) - computedStyle.toFloat(childStyles.marginTop) + 'px'; 482 }, 483 484 // Calculate cumulative offset of the element. 485 // Optionally according to context 486 _fullOffset: function _fullOffset(offsetName, elm, context) { 487 var offset = elm[offsetName]; 488 var offsetParent = elm.offsetParent; 489 490 // Add offset of the ascendent tree until we reach to the document root or to the given context 491 while (offsetParent !== null && offsetParent !== context) { 492 offset = offset + offsetParent[offsetName]; 493 offsetParent = offsetParent.offsetParent; 494 } 495 496 return offset; 497 }, 498 499 // Get positioning context of the element. 500 // We know that the closest parent that a transform rule applied will create a positioning context. 501 _getContext: function() { 502 var parent; 503 var element = this.child; 504 var context = null; 505 var styles; 506 507 // Climb up the treee until reaching the context 508 while(!context) { 509 parent = element.parentNode; 510 if(parent === document.documentElement) { 511 return null; 512 } 513 514 styles = computedStyle.getAll(parent); 515 // Element has a transform rule
516 if(styles[transformJsProperty] !== 'none') { 517 context = parent; 518 break; 519 } 520 element = parent; 521 } 522 return context; 523 }, 524 525 _fix: function _fix() { 526 var child = this.child; 527 var childStyle = child.style; 528 var childStyles = computedStyle.getAll(child); 529 var left = child.getBoundingClientRect().left; 530 var width = childStyles.width; 531 532 this._saveStyles(); 533 534 if(document.documentElement.currentStyle){ 535 // Function for ie<9. When hasLayout is not triggered in ie7, he will report currentStyle as auto, clientWidth as 0. Thus using offsetWidth. 536 // Opera also falls here 537 width = (child.offsetWidth) - (computedStyle.toFloat(childStyles.paddingLeft) + computedStyle.toFloat(childStyles.paddingRight) + computedStyle.toFloat(childStyles.borderLeftWidth) + computedStyle.toFloat(childStyles.borderRightWidth)) + 'px'; 538 } 539 540 // Ie still fixes the container according to the viewport. 541 if(fixedPositioningContext) { 542 var context = this._getContext(); 543 if(context) { 544 // There is a positioning context. Left should be according to the context. 545 left = child.getBoundingClientRect().left - context.getBoundingClientRect().left; 546 } 547 } 548 549 this._replacer.replace(); 550 551 childStyle.left = (left - computedStyle.toFloat(childStyles.marginLeft)) + 'px'; 552 childStyle.width = width; 553 554 childStyle.position = 'fixed'; 555 childStyle.top = this._mindtop() + this.options.top - computedStyle.toFloat(childStyles.marginTop) + 'px'; 556 this._$child.addClass(this.options.className); 557 this.fixed = true; 558 }, 559 560 _unfix: function _unfix() { 561 var childStyle = this.child.style; 562 this._replacer.hide(); 563 childStyle.position = this._childOriginalPosition; 564 childStyle.top = this._childOriginalTop; 565 childStyle.width = this._childOriginalWidth; 566 childStyle.left = this._childOriginalLeft; 567 this._$child.removeClass(this.options.className); 568 this.fixed = false; 569 }, 570 571 _saveStyles: function(){ 572 var childStyle = this.child.style; 573 this._childOriginalPosition = childStyle.position; 574 this._childOriginalTop = childStyle.top; 575 this._childOriginalWidth = childStyle.width; 576 this._childOriginalLeft = childStyle.left; 577 }, 578 579 _onresize: function () { 580 this.refresh(); 581 }, 582
583 _saveViewportHeight: function () { 584 // ie8 doesn't support innerHeight 585 this._viewportHeight = window.innerHeight || document.documentElement.clientHeight; 586 }, 587 588 _stop: function() { 589 // Unfix the container immediately. 590 this._unfix(); 591 // remove event listeners 592 $(window).unbind('scroll', this._proxied_onscroll); 593 $(this._toresize).unbind('resize', this._proxied_onresize); 594 }, 595 596 _start: function() { 597 // Trigger onscroll to have the effect immediately. 598 this._onscroll(); 599 600 // Attach event listeners 601 $(window).bind('scroll', this._proxied_onscroll); 602 $(this._toresize).bind('resize', this._proxied_onresize); 603 }, 604 605 _destroy: function() { 606 // Destroy mimic node instance 607 this._replacer.destroy(); 608 }, 609 610 refresh: function() { 611 this._saveViewportHeight(); 612 this._unfix(); 613 this._onscroll(); 614 } 615 }); 616 617 function NativeSticky(child, parent, options) { 618 FixTo.call(this, child, parent, options); 619 this.start(); 620 } 621 622 NativeSticky.prototype = new FixTo(); 623 624 $.extend(NativeSticky.prototype, { 625 _start: function() { 626 627 var childStyles = computedStyle.getAll(this.child); 628 629 this._childOriginalPosition = childStyles.position; 630 this._childOriginalTop = childStyles.top; 631 632 this.child.style.position = nativeStickyValue; 633 this.refresh(); 634 }, 635 636 _stop: function() { 637 this.child.style.position = this._childOriginalPosition; 638 this.child.style.top = this._childOriginalTop; 639 }, 640 641 refresh: function() { 642 this.child.style.top = this._mindtop() + this.options.top + 'px'; 643 } 644 }); 645 646 647 648 var fixTo = function fixTo(childElement, parentElement, options) { 649 if((nativeStickyValue && !options) || (nativeStickyValue && options && options.useNativeSticky !== false)) { 650 // Position sticky supported and user did not disabled the usage of it. 651 return new NativeSticky(childElement, parentElement, options); 652 } 653 else if(fixedPositionValue) { 654 // Position fixed supported 655 656 if(fixedPositioningContext===undefined) { 657 // We don't know yet if browser creates fixed positioning contexts. Check it. 658 fixedPositioningContext = checkFixedPositioningContextSupport(); 659 } 660 661 return new FixToContainer(childElement, parentElement, options); 662 } 663 else { 664 return 'Neither fixed nor sticky positioning supported'; 665 } 666 }; 667 668 /* 669 No support for ie lt 8 670 */ 671 672 if(ieversion<8){ 673 fixTo = function(){ 674 return 'not supported'; 675 }; 676 } 677 678 // Let it be a jQuery Plugin 679 $.fn.fixTo = function (targetSelector, options) { 680 681 var $targets = $(targetSelector); 682 683 var i = 0; 684 return this.each(function () { 685 686 // Check the data of the element. 687 var instance = $(this).data('fixto-instance'); 688 689 // If the element is not bound to an instance, create the instance and save it to elements data. 690 if(!instance) { 691 $(this).data('fixto-instance', fixTo(this, $targets[i], options)); 692 } 693 else { 694 // If we already have the instance here, expect that targetSelector parameter will be a string 695 // equal to a public methods name. Run the method on the instance without checking if 696 // it exists or it is a public method or not. Cause nasty errors when necessary. 697 var method = targetSelector; 698 instance[method].call(instance, options); 699 } 700 i++; 701 }); 702 }; 703 704 /* 705 Expose 706 */ 707 708 return { 709 FixToContainer: FixToContainer, 710 fixTo: fixTo, 711 computedStyle:computedStyle, 712 mimicNode:mimicNode 713 }; 714 715 716}(window.jQuery, window, document));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.