PageSourceSearch

https://smallstatebighistory.com/wp-content/themes/edition/js/fixto.min.js?ver=0.3.0

js smallstatebighistory.com collected 2026-10-02 10:14:01 UTC 25,077 bytes, 714 lines download raw bytes

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