ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

fixto.js (26931B)


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