1/** 2 * HSScrollNav Component. 3 * 4 * @author Htmlstream 5 * @version 1.0 6 * @requires jQuery 7 * 8 */ 9;(function ($) { 10 'use strict'; 11 12 $.HSCore.components.HSScrollNav = { 13 14 /** 15 * Base configuraion of the component. 16 * 17 * @private 18 */ 19 _baseConfig: { 20 duration: 400, 21 easing: 'linear', 22 over: $(), 23 activeItemClass: 'active', 24 afterShow: function(){}, 25 beforeShow: function(){} 26 }, 27 28 /** 29 * All initialized item on the page. 30 * 31 * @private 32 */ 33 _pageCollection: $(), 34 35 36 /** 37 * Initialization of the component. 38 * 39 * @param {jQuery} collection 40 * @param {Object} config 41 * 42 * @public 43 * @return {jQuery} 44 */ 45 init: function(collection, config) { 46 47 var self = this; 48 49 if( !collection || !collection.length ) return $(); 50 51 collection.each(function(i, el) { 52 53 var $this = $(el), 54 itemConfig = config && $.isPlainObject(config) ? 55 $.extend(true, {}, self._baseConfig, config, $this.data()) : 56 $.extend(true, {}, self._baseConfig, $this.data()); 57 58 if( !$this.data('HSScrollNav') ) { 59 60 $this.data('HSScrollNav', new HSScrollNav($this, itemConfig)); 61 62 self._pageCollection = self._pageCollection.add( $this ); 63 64 } 65 66 }); 67 68 $(window).on('scroll.HSScrollNav', function(){ 69 70 self._pageCollection.each(function(i, el) { 71 72 $(el).data('HSScrollNav').highlight(); 73 74 }); 75 76 }).trigger('scroll.HSScrollNav'); 77 78 return collection; 79 80 } 81 82 } 83 84 85 /** 86 * HSScrollNav. 87 * 88 * @param {jQuery} element 89 * @param {Object} config 90 * 91 * @constructor 92 */ 93 function HSScrollNav(element, config) { 94 95 /** 96 * Current element. 97 * 98 * @public 99 */ 100 this.element = element; 101 102 /** 103 * Configuraion. 104 * 105 * @public 106 */ 107 this.config = config; 108 109 /** 110 * Sections. 111 * 112 * @public 113 */ 114 this._items = $(); 115 116 this._makeItems(); 117 this._bindEvents(); 118 } 119 120 /** 121 * Return collection of sections. 122 * 123 * @private 124 * @return {jQuery} 125 */ 126 HSScrollNav.prototype._makeItems = function() { 127 128 var self = this; 129 130 this.element.find('a[href^="#"]').each(function(i, el) { 131 132 var $this = $(el); 133 134 if( !$this.data('HSScrollNavSection') ) { 135 136 $this.data('HSScrollNavSection', new HSScrollNavSection($this, self.config)); 137 138 self._items = self._items.add( $this ); 139 140 } 141 142 }); 143 144 }; 145 146 /** 147 * Binds necessary events. 148 * 149 * @private 150 * @return {undefined} 151 */ 152 HSScrollNav.prototype._bindEvents = function() { 153 154 var self = this; 155 156 this.element.on('click.HSScrollNav', 'a[href^="#"]', function(e) { 157 158 var link = this; 159 self._lockHightlight = true; 160 if(self.current) self.current.unhighlight(); 161 link.blur(); 162 self.current = $(link).data('HSScrollNavSection'); 163 self.current.highlight(); 164 165 $(this).data('HSScrollNavSection').show( function(){ 166 self._lockHightlight = false; 167 } ); 168 169 e.preventDefault(); 170 171 }); 172 173 }; 174 175 /** 176 * Activates necessary menu item. 177 * 178 * @public 179 */ 180 HSScrollNav.prototype.highlight = function() { 181 182 var self = this, items, currentItem, current, scrollTop; 183 184 if(!this._items.length || this._lockHightlight) return; 185 186 scrollTop = $(window).scrollTop(); 187 188 if(scrollTop + $(window).height() === $(document).height()) { 189 190 this.current = this._items.last().data('HSScrollNavSection'); 191 192 this.unhighlight(); 193 this.current.highlight(); 194 this.current.changeHash(); 195 196 return; 197 } 198 199 this._items.each(function(i, el){ 200 201 var Section = $(el).data('HSScrollNavSection'), 202 $section = Section.section; 203 204 if(scrollTop > Section.offset) { 205 current = Section; 206 } 207 208 }); 209 210 if(current && this.current != current) { 211 212 this.unhighlight(); 213 current.highlight(); 214 if(this.current) current.changeHash(); 215 216 this.current = current; 217 218 } 219 220 }; 221 222 /** 223 * Deactivates all menu items. 224 * 225 * @public 226 */ 227 HSScrollNav.prototype.unhighlight = function() { 228 229 this._items.each(function(i, el){ 230 $(el).data('HSScrollNavSection').unhighlight(); 231 }); 232 233 }; 234 235 /** 236 * HSScrollNavSection. 237 * 238 * @param {jQuery} element 239 * 240 * @constructor 241 */ 242 function HSScrollNavSection(element, config) { 243 244 var self = this; 245 246 /** 247 * Current section. 248 * 249 * @public 250 */ 251 this.element = element; 252 253 /** 254 * Configuration. 255 * 256 * @public 257 */ 258 this.config = config; 259 260 /** 261 * Getter for acces to the section element. 262 * 263 * @public 264 */ 265 Object.defineProperty(this, 'section', { 266 value: $(self.element.attr('href')) 267 }); 268 269 /** 270 * Getter for determinate position of the section relative to document. 271 * 272 * @public 273 */ 274 275 Object.defineProperty(this, 'offset', { 276 get: function() { 277 278 var header = $('.u-header'), 279 headerStyles = getComputedStyle(header.get(0)), 280 headerPosition = headerStyles.position, 281 offset = self.section.offset().top; 282 283 284 285 if(header.length && headerPosition == 'fixed' && parseInt(headerStyles.top) == 0) { 286 offset = offset - header.outerHeight() - parseInt(headerStyles.marginTop); 287 } 288 289 if(self.config.over.length) { 290 offset = offset - self.config.over.outerHeight(); 291 } 292 293 return offset; 294 } 295 }); 296 297 298 } 299 300 /** 301 * Moves to the section. 302 * 303 * @public 304 */ 305 HSScrollNavSection.prototype.show = function(callback) { 306 307 var self = this; 308 309 if( !this.section.length ) return; 310 311 self.config.beforeShow.call(self.section); 312 313 this.changeHash(); 314 315 $('html, body').stop().animate({ 316 scrollTop: self.offset + 3 317 }, {
318 duration: self.config.duration, 319 easing: self.config.easing, 320 complete: function() { 321 $('html, body').stop().animate({ 322 scrollTop: self.offset + 3 323 }, { 324 duration: self.config.duration, 325 easing: self.config.easing, 326 complete: function() { 327 self.config.afterShow.call(self.section); 328 if($.isFunction(callback)) callback(); 329 } 330 }); 331 } 332 }); 333 334 }; 335 336 /** 337 * Changes location's hash. 338 * 339 * @public 340 */ 341 HSScrollNavSection.prototype.changeHash = function() { 342 this.section.attr('id', ''); 343 window.location.hash = this.element.attr('href'); 344 this.section.attr('id', this.element.attr('href').slice(1)); 345 }; 346 347 /** 348 * Activates the menu item. 349 * 350 * @public 351 */ 352 HSScrollNavSection.prototype.highlight = function() { 353 354 var parent = this.element.parent('li'); 355 if(parent.length) parent.addClass(this.config.activeItemClass); 356 357 }; 358 359 /** 360 * Deactivates the menu item. 361 * 362 * @public 363 */ 364 HSScrollNavSection.prototype.unhighlight = function() { 365 366 var parent = this.element.parent('li');
367 if(parent.length) parent.removeClass(this.config.activeItemClass); 368 369 }; 370 371 372 373})(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.