1/** 2 * Creates and initialises the stickymenu. 3 * 4 * @class 5 * @extends Responsiville.Debug 6 * @extends Responsiville.Events 7 * 8 * @author Nevma, [email protected] 9 * @license http://www.gnu.org/licenses/gpl-3.0.en.html GPLv3 10 * 11 * @fires Responsiville.Stickymenu#init 12 * 13 * @classdesc A responsive stickymenu for the Responsiville framework. It makes 14 * a menu, or any HTML element for that matter, stay fixed on top of 15 * the screen as the user scrolls down the page. Pretty useful in 16 * responsive web design and in long web pages in general. 17 * 18 * @param {Object} options The initialisation options of the module. 19 * 20 * @property {Object} options The options that define the object 21 * behaviour. 22 * @property {boolean} options.debug Whether to print debug messages in 23 * the browser console. 24 * @property {boolean} options.slug A special codename for the instance 25 * of the element, to be used as a 26 * class on its container and as a key 27 * in arrays where it is grouped with 28 * other elements of its kind. 29 * @property {string} options.element Selector for the element that 30 * actually is the element to be made 31 * sticky. 32 * @property {string} options.enabled The class added to the element when 33 * the stickymenu is enabled. Note 34 * that the stickymenu might be there 35 * without being enabled. Enabled does 36 * not mean active. Enabled means 37 * having the readiness to become 38 * active upon the desired page 39 * scroll. 40 * @property {string} options.active The class added to the stickymenu 41 * whenit becomes active. 42 * @property {string} options.activeBody The class added to the body element 43 * when the stickymenu becomes active. 44 * @property {string} options.offsetTop The extra space to wait for the user 45 * to scroll down to, before the sticky 46 * menu us activated. 47 * @property {string} options.offsetBottom The reverse of offsetTop, the extra 48 * space before the top of the page 49 * where the stickymenu will be 50 * deactivated. 51 * @property {string} options.enter Comma separated list of breakpoints 52 * in which the module enters, wihch 53 * means it is enabled (not activated). 54 * @property {string} options.leave Comma separated list of breakpoints 55 * in which the module leaves, which 56 * means it is disabled. 57 */ 58Responsiville.Stickymenu = function( options ) { 59 60 // Responsiville must be initialised. 61 if( ! Responsiville.Main.getInstance() ) { 62 throw new Error( 'Responsiville instantiation error: the framework has not been initialised, call new Responsiville::Main() and then Responsiville::init().' ); 63 } 64 65 66 67 // General settings setup. 68 this.options = {}; 69 this.options = jQuery.extend( this.options, Responsiville.Stickymenu.defaults, options ); 70 this.codeName = 'responsiville.stickymenu'; 71 this.responsiville = Responsiville.Main.getInstance(); 72 73 74 75 // Cache important DOM elements. 76 this.$body = this.responsiville.$body; 77 this.$document = this.responsiville.$document; 78 this.$window = this.responsiville.$window; 79 this.$element = jQuery( this.options.element ); 80 81 82 83 // Check for developer settings inside main element's data attributes. 84 for( var key in this.options ) { 85 86 var value = this.$element.data( 'responsiville-stickymenu-' + key.toLowerCase() ); 87 if( typeof value !== 'undefined' ) { 88 this.options[key] = value; 89 } 90 91 } 92 93 // Uniqueue element slug. 94 if( this.options.slug == '' ) { 95 Responsiville.Stickymenu.elementsCounter = Responsiville.Stickymenu.elementsCounter !== undefined ? ++Responsiville.Stickymenu.elementsCounter : 0;
96 this.options.slug = this.codeName.replace( '.', '-' ) + '-' + Responsiville.Stickymenu.elementsCounter; 97 } 98 99 // Take special care for enter/leave breakpoints possibly given as HTML data attributes. 100 var htmlBreakpoints = Responsiville.determineEnableDisableBreakpoints({ 101 enter: this.$element.data( 'responsiville-stickymenu-enter' ), 102 leave: this.$element.data( 'responsiville-stickymenu-leave' ) 103 }); 104 105 if( htmlBreakpoints !== null ) { 106 this.options.enter = htmlBreakpoints.enter; 107 this.options.leave = htmlBreakpoints.leave; 108 } 109 110 111 112 // If no menu found raise an error. 113 if( this.$element.length === 0 ) { 114 115 this.log( 'Responsiville.Stickymenu instantiation error: no menus found (' + this.options.menu + ').' ); 116 return; 117 118 } 119 120 121 122 // Add this object as a main element's data attribute for API usage. 123 this.$element.data( 'responsiville-api', this ); 124 this.$element.data( 'responsiville-stickymenu-api', this ); 125 126 127 128 // Initialises the stickymenu element. 129 this.enabled = false; 130 this.activated = false; 131 this.setupEvents(); 132 133 this.log( 'stickymenu initialised' ); 134 135 136 137 /** 138 * Called after the stickymenu has been created. 139 * @event Responsiville.Stickymenu#created 140 */ 141 this.fireEvent( 'created' ); 142 143}; 144 145 146 147// Extend with logging functions. 148Responsiville.extend( Responsiville.Stickymenu, Responsiville.Debug.Extensions ); 149 150// Extend with event handling mechanism. 151Responsiville.extend( Responsiville.Stickymenu, Responsiville.Events ); 152 153 154 155/** 156 * @property {boolean} AUTO_RUN Controls whether this module should run by 157 * default, without the developer calling it, using 158 * its default settings. Defaults to true. 159 */ 160Responsiville.Stickymenu.AUTO_RUN = typeof RESPONSIVILLE_AUTO_INIT !== 'undefined' ? RESPONSIVILLE_AUTO_INIT : true; 161 162 163 164/** 165 * @property {Object} defaults Default values for this module settings. 166 */ 167Responsiville.Stickymenu.defaults = { 168 debug : false, 169 slug : '', 170 element : '.responsiville-stickymenu', 171 enabled : 'responsiville-stickymenu-enabled', 172 active : 'responsiville-stickymenu-active', 173 activeBody : 'responsiville-stickymenu-active-body', 174 offsetTop : 0, 175 offsetBottom : 0, 176 enter : 'laptop, desktop, large, xlarge', 177 leave : 'small, mobile, tablet' 178}; 179 180 181 182/** 183 * Runs through the page and searches for elements that apply to the current 184 * module in order to apply it to them automatically. Useful for automatically 185 * creating elements with this module's behaviour just by setting up the 186 * predefined classes and data attributes in HTML elements of the page. 187 * 188 * @function 189 * @static 190 * 191 * @return {void} 192 */ 193Responsiville.Stickymenu.autoRun = function() { 194 195 jQuery( Responsiville.Stickymenu.defaults.element ).each( function() { 196 197 new Responsiville.Stickymenu({ 198 debug : Responsiville.Main.getInstance().options.debug, 199 element : this 200 }); 201 202 }); 203 204}; 205 206 207 208/** 209 * Sets up event handlers necessary for the object to function properly. 210 * 211 * @return {void} 212 */ 213Responsiville.Stickymenu.prototype.setupEvents = function() { 214 215 var k, length; 216 217 // Register to be enabled on the required breakpoints. 218 var breakpointsEnter = Responsiville.splitAndTrim( this.options.enter ); 219 220 for( k=0, length=breakpointsEnter.length; k<length; k++ ) { 221 this.responsiville.on( 'enter.' + breakpointsEnter[k], this.getBoundFunction( this.enable ) ); 222 } 223 224 // Register to be disabled on the required breakpoints. 225 var breakpointsLeave = Responsiville.splitAndTrim( this.options.leave ); 226 227 for( k=0, length=breakpointsLeave.length; k<length; k++ ) { 228 this.responsiville.on( 'enter.' + breakpointsLeave[k], this.getBoundFunction( this.disable ) ); 229 } 230 231 // Enable right away if necessary. 232 if( this.responsiville.is( this.options.enter ) ) { 233 this.enable(); 234 } 235 236 // Add window scroll and resize handler 237 var scrollThrottled = this.getBoundFunction( Responsiville.throttle( this.scroll ) ); 238 239 this.$window.on( 'scroll', scrollThrottled ); 240 this.$window.on( 'resize', scrollThrottled ); 241 242}; 243 244 245 246/** 247 * Enables the stickymenu. It does not activate it. It simply enables it, so 248 * when the necessary trigger is fired (ie the user scrolls down enough) then 249 * the element is activated (which means it is fixed at the top of the screen). 250 * Useful in responsive web design. 251 * 252 * @fires Responsiville.Stickymenu#enabled 253 * 254 * @return {void} 255 */ 256Responsiville.Stickymenu.prototype.enable = function() { 257 258 this.log( 'enable stickymenu' ); 259 this.$element.addClass( this.options.enabled ); 260 this.enabled = true; 261 this.scroll(); 262 263 /** 264 * Called after the stickymenu has been enabled. 265 * @event Responsiville.Stickymenu#enabled 266 */ 267 this.fireEvent( 'enabled' ); 268 269}; 270 271 272 273/** 274 * Disables the stickymenu. It does not deactivate it. It simply disables it, 275 * so it does not function any more when the necessary trigger is fired. Useful 276 * in responsive web design. 277 * 278 * @fires Responsiville.Stickymenu#disabled 279 * 280 * @return {void} 281 */ 282Responsiville.Stickymenu.prototype.disable = function() { 283 284 this.log( 'disable stickymenu' ); 285 this.deactivate(); 286 this.enabled = false;
287 this.$element.removeClass( this.options.enabled ); 288 289 /** 290 * Called after the stickymenu has been disabled. 291 * @event Responsiville.Stickymenu#disabled 292 */ 293 this.fireEvent( 'disabled' ); 294 295}; 296 297 298 299/** 300 * Activates the stickymenu, which means fixes it on top of the screen as the 301 * user keeps scrolling down. 302 * 303 * @fires Responsiville.Stickymenu#activating 304 * @fires Responsiville.Stickymenu#activated 305 * 306 * @return {void} 307 */ 308Responsiville.Stickymenu.prototype.activate = function() { 309 310 if( ! this.enabled ) { 311 return; 312 } 313 314 if( this.activated ) { 315 return; 316 } 317 318 this.log( 'activate stickymenu' ); 319 320 /** 321 * Called before the stickymenu has been activated. 322 * @event Responsiville.Stickymenu#activating 323 */ 324 this.fireEvent( 'activating' ); 325 326 this.$body.addClass( this.options.activeBody ); 327 this.$element.addClass( this.options.active ); 328 this.activated = true; 329 330 /** 331 * Called after the stickymenu has been activated. 332 * @event Responsiville.Stickymenu#activated 333 */ 334 this.fireEvent( 'activated' ); 335 336}; 337 338 339 340/** 341 * Deactivates the stickymenu, which means restoring it to its original state. 342 * 343 * @fires Responsiville.Sticky#deactivating 344 * @fires Responsiville.Sticky#deactivated 345 * 346 * @return {void} 347 */ 348Responsiville.Stickymenu.prototype.deactivate = function() { 349 350 if( ! this.enabled ) { 351 return; 352 } 353 354 if( ! this.activated ) { 355 return; 356 } 357 358 this.log( 'deactivate stickymenu' ); 359 360 /** 361 * Called before the stickymenu has been deactivated. 362 * @event Responsiville.Stickymenu#deactivating 363 */ 364 this.fireEvent( 'deactivating' ); 365 366 this.$body.removeClass( this.options.activeBody ); 367 this.$element.removeClass( this.options.active ); 368 this.activated = false; 369 370 /** 371 * Called after the stickymenu has been deactivated. 372 * @event Responsiville.Stickymenu#deactivated 373 */ 374 this.fireEvent( 'deactivated' ); 375 376}; 377 378 379 380/** 381 * Window scroll event handler which checks when the stickymenu should be 382 * activated and deactivated. Can also be called on demand, if necessary. 383 * 384 * @return {void} 385 */ 386Responsiville.Stickymenu.prototype.scroll = function() { 387 388 if( ! this.enabled ) { 389 return; 390 } 391 392 this.log( 'stickymenu scroll' ); 393 394 var scrollY = window.pageYOffset !== undefined ? window.pageYOffset : this.$window.get( 0 ).scrollY; 395 var threshold = this.options.offsetTop - this.options.offsetBottom; 396 397 if( scrollY > threshold ) { 398 this.activate(); 399 } else if( scrollY <= threshold ) { 400 this.deactivate(); 401 } 402 403};
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.