PageSourceSearch

https://www.steinbock-immo.at/fileadmin/templates/scripts/libs/fixto.js

js steinbock-immo.at collected 2026-10-04 01:32:22 UTC 24,518 bytes, 716 lines download raw bytes

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.