1"use_strict"; 2 3var Shira; 4(function (Shira, $) { 5 (function (ScrollFix) { 6 /** 7 * @constructor 8 * 9 * @param {HTMLElement} element DOM element that is going to be fixed 10 * @param {Object} options option map 11 */ 12 ScrollFix.Watcher = function (element, options) { 13 this.element = element; 14 this.options = $.extend({}, ScrollFix.Watcher.defaults, options); 15 }; 16 17 ScrollFix.Watcher.defaults = { 18 fixClass: 'scroll-fix', 19 fixTop: 0, 20 fixOffset: 0, 21 unfixOffset: 0, 22 onUpdateFixed: null, 23 syncSize: true, 24 syncPosition: true, 25 style: true 26 }; 27 28 ScrollFix.Watcher.prototype = { 29 element: null, 30 substitute: null, 31 options: null, 32 fixed: false, 33 attached: false, 34 35 /** 36 * Get absolute X position of the given element 37 * 38 * @param {HTMLElement} elem 39 * @returns {Number} 40 */ 41 getElementX: function (elem) { 42 var x = 0; 43 do x += elem.offsetLeft; 44 while (elem = elem.offsetParent); 45 46 return x; 47 }, 48 49 /** 50 * Get absolute Y position of the given element 51 * 52 * @param {HTMLElement} elem 53 * @returns {Number} 54 */ 55 getElementY: function (elem) { 56 var y = 0; 57 do y += elem.offsetTop; 58 while (elem = elem.offsetParent); 59 60 return y; 61 }, 62 63 /** 64 * Fix the element 65 */ 66 fix: function () { 67 // create the substitute 68 this.substitute = $(this.element.cloneNode(false)) 69 .css('visibility', 'hidden') 70 .height($(this.element).height()) 71 .insertAfter(this.element)[0] 72 ; 73 74 // add class and styles 75 if (this.options.style) { 76 $(this.element) 77 .css('position', 'fixed') 78 .css('top', this.options.fixTop + 'px') 79 ; 80 } 81 $(this.element).addClass(this.options.fixClass); 82 }, 83 84 /** 85 * Update the fixed element 86 */ 87 updateFixed: function () { 88 // size 89 if (this.options.syncSize) { 90 $(this.element) 91 .width($(this.substitute).width()) 92 ; 93 } 94 95 // position 96 if (this.options.syncPosition) { 97 var currentScrollLeft = $(window).scrollLeft(); 98 var substituteLeftOffset = this.getElementX(this.substitute); 99 100 $(this.element).css('left', (substituteLeftOffset - currentScrollLeft) + 'px'); 101 } 102 103 // callback 104 if (null !== this.options.onUpdateFixed) { 105 this.options.onUpdateFixed(this); 106 } 107 }, 108 109 /** 110 * Unfix the element 111 */ 112 unfix: function () { 113 // remove the substitute 114 $(this.substitute).remove(); 115 this.substitute = null; 116 117 // reset applied styles and remove class 118 var cssReset = {}; 119 if (this.options.syncPosition) { 120 cssReset.left = ''; 121 } 122 if (this.options.syncSize) { 123 cssReset.width = ''; 124 } 125 if (this.options.style) { 126 cssReset.position = ''; 127 cssReset.top = ''; 128 } 129 $(this.element) 130 .css(cssReset) 131 .removeClass(this.options.fixClass) 132 ; 133 }, 134 135 /** 136 * Attach the watcher 137 */ 138 attach: function () { 139 if (this.attached) { 140 throw new Error('Already attached'); 141 } 142 143 var that = this; 144 145 this.updateEventHandler = function () { 146 that.pulse(); 147 }; 148 149 $(window) 150 .scroll(this.updateEventHandler) 151 .resize(this.updateEventHandler) 152 ; 153 154 this.attached = true; 155 this.pulse(); 156 }, 157 158 /** 159 * Detach the watcher 160 */ 161 detach: function () { 162 if (!this.attached) { 163 throw new Error('Not attached'); 164 } 165 166 $(window) 167 .unbind('scroll', this.updateEventHandler) 168 .unbind('resize', this.updateEventHandler) 169 ; 170 171 this.attached = false;
172 }, 173 174 /** 175 * Pulse the watcher 176 */ 177 pulse: function () { 178 var currentScroll = $(window).scrollTop(); 179 180 if (this.fixed) { 181 if (currentScroll <= this.getElementY(this.substitute) + this.options.unfixOffset) { 182 this.unfix(); 183 this.fixed = false; 184 } else { 185 this.updateFixed(); 186 } 187 } else { 188 if (currentScroll >= this.getElementY(this.element) + this.options.fixOffset) { 189 this.fix(); 190 this.fixed = true; 191 this.updateFixed(); 192 } 193 } 194 } 195 }; 196 197 // jQuery methods 198 199 /** 200 * Attach a watcher to the matched element 201 * 202 * @param {Object} options watcher option map 203 * @returns {ScrollFix.Watcher|Boolean} false if no element was matched 204 */ 205 $.fn.scrollFix = function (options) { 206 var element = this[0]; 207 208 if (element) { 209 var watcher = new ScrollFix.Watcher(element, options); 210 watcher.attach(); 211 212 return watcher; 213 } 214 215 return false; 216 }; 217 })(Shira.ScrollFix || (Shira.ScrollFix = {})); 218})(Shira || (Shira = {}), jQuery);
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.