1/* 2* ShiftNav.js 3* 4* http://shiftnav.io 5* 6* Copyright Chris Mavricos, SevenSpark http://sevenspark.com 7*/ 8 9;(function ( $, window, document, undefined ) { 10 11 var pluginName = "shiftnav", 12 defaults = { 13 mouseEvents: true, 14 retractors: true, 15 touchOffClose: true, 16 clicktest: false, 17 windowstest: false, 18 debug: false, 19 swipe_tolerance_x: 150, //min distance to swipe 20 swipe_tolerance_y: 30, //max vertical displacement 21 swipe_edge_proximity: 70, //maximum distance from edge 22 open_current: false, 23 collapse_accordions: false 24 }; 25 26 function Plugin ( element, options ) { 27 28 this.element = element; 29 30 this.$shiftnav = $( this.element ); 31 this.$menu = this.$shiftnav.find( 'ul.shiftnav-menu' ); 32 33 this.settings = $.extend( {}, defaults, options ); 34 this._defaults = defaults; 35 this._name = pluginName; 36 37 this.touchenabled = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0); 38 39 if( window.navigator.pointerEnabled ){ 40 this.touchStart = 'pointerdown'; 41 this.touchEnd = 'pointerup'; 42 this.touchMove = 'pointermove'; 43 } 44 else if( window.navigator.msPointerEnabled ){ 45 this.touchStart = 'MSPointerDown'; 46 this.touchEnd = 'MSPointerUp'; 47 this.touchMove = 'MSPointerMove'; 48 } 49 else{ 50 this.touchStart = 'touchstart'; 51 this.touchEnd = 'touchend'; 52 this.touchMove = 'touchmove'; 53 } 54 55 this.toggleevent = this.touchEnd == 'touchend' ? this.touchEnd + ' click' : this.touchEnd; //add click except for IE 56 this.transitionend = 'transitionend.shiftnav webkitTransitionEnd.shiftnav msTransitionEnd.shiftnav oTransitionEnd.shiftnav'; 57 58 //TESTING 59 if( this.settings.clicktest ) this.touchEnd = 'click'; 60 61 this.init(); 62 63 } 64 65 Plugin.prototype = { 66 67 init: function () { 68 69 70 this.$shiftnav.removeClass( 'shiftnav-nojs' ); //We're off and running 71 72 //this.$toggles = $( '.shiftnav-toggle[data-shiftnav-target="'+this.$shiftnav.attr('id')+'"]' ); 73 this.$toggles = $( '.shiftnav-toggle[data-shiftnav-target="'+this.$shiftnav.data('shiftnav-id')+'"]' ); 74 75 //Initialize user interaction events 76 77 this.initializeShiftNav(); 78 79 this.initializeTargets(); 80 this.initializeSubmenuToggleMouseEvents(); 81 this.initializeRetractors(); 82 this.initializeResponsiveToggle(); 83 this.initializeSwipeHandler(); 84 85 //this.initializeTouchoffClose(); //attached on open instead 86 87 }, 88 89 90 91 /* Initalizers */ 92 93 initializeShiftNav: function(){ 94 95 var $body = $('body'), 96 plugin = this; 97 98 //Only enable the site once 99 if( !$body.hasClass( 'shiftnav-enabled' ) ){ 100 101 $body.addClass( 'shiftnav-enabled' ); 102 if( shiftnav_data.lock_body == 'on' ) $body.addClass( 'shiftnav-lock' ); 103 if( shiftnav_data.lock_body_x == 'on' ) $body.addClass( 'shiftnav-lock-x' ); 104 105 if( shiftnav_data.shift_body != 'off' ) $body.wrapInner( '<div class="shiftnav-wrap"></div>' ); //unique 106 else $body.addClass( 'shiftnav-disable-shift-body' ); 107 108 //Move elements outside of shifter 109 $( '#shiftnav-toggle-main, #wpadminbar' ).appendTo( 'body' ); 110 111 var $wrap = $( '.shiftnav-wrap' ); 112 113 //Pad top 114 var toggleHeight = $( '#shiftnav-toggle-main' ).outerHeight(); 115 $wrap.css( 'padding-top' , toggleHeight ); 116 if( shiftnav_data.shift_body == 'off' ) $body.css( 'padding-top' , toggleHeight ); 117 118 //Setup non-transform 119 //Some browsers provide false positives for feature detection, so we have to do browser detection as well, sadly 120 var fpos = false; //falsePositive 121 var ua = navigator.userAgent.toLowerCase(); 122 123 if( /android/.test( ua ) ){ 124 //always ignore old androids 125 if( /android [1-3]/.test( ua ) ) fpos = true; 126 //always allow Chrome 127 else if( /chrome/.test( ua ) ) fpos = false; 128 //Android 4.4+ is okay 129 else if( /android 4.[4-9]/.test( ua ) ) fpos = false; 130 else fpos = true; 131 } 132 133 if( !shift_supports( 'transform' ) || fpos ){ 134 $body.addClass( 'shiftnav-no-transforms' ); 135 } 136 137 138 //Setup swipe open on MAIN SHIFTNAV only 139 if( shiftnav_data.swipe_open == 'on' ){ 140 var wrap_start_y = 0, 141 wrap_start_x = 0, 142 wrap_cur_y = 0, 143 wrap_cur_x = 0, 144 viewport_width = $( window ).width(); 145 146 147 if( shiftnav_data.shift_body == 'off' ) $wrap = $( 'body' ); 148 149 $wrap.on( 'touchstart' , function( e ){ 150 wrap_start_y = e.originalEvent.changedTouches[0].pageY; 151 wrap_start_x = e.originalEvent.changedTouches[0].pageX; 152 //console.log( "START: " + wrap_start_x ); 153 }); 154 155 //Left edge activate on swipe from left 156 if( $( '#shiftnav-main' ).hasClass( 'shiftnav-left-edge' ) ){ 157 $wrap.on( 'touchmove' , function( e ){ 158 wrap_cur_x = e.originalEvent.changedTouches[0].pageX; 159 //console.log( wrap_cur_x ); 160 161 //if close to left edge 162 if( wrap_start_x < plugin.settings.swipe_edge_proximity ){ 163 e.preventDefault(); 164 165 //if swipe more than 150 166 if( wrap_cur_x - wrap_start_x > plugin.settings.swipe_tolerance_x ){ 167 wrap_cur_y = e.originalEvent.changedTouches[0].pageY; 168 if( Math.abs( wrap_cur_y - wrap_start_y ) < plugin.settings.swipe_tolerance_y ){ 169 plugin.openShiftNav(); 170 e.stopPropagation(); 171 } 172 } 173 } 174 }); 175 } 176 //Right edge activate on swipe from right 177 else{ 178 $wrap.on( 'touchmove' , function( e ){ 179 wrap_cur_x = e.originalEvent.changedTouches[0].pageX; 180 181 //if we start from the edge, tell android we've got this covered 182 if( wrap_start_x > ( viewport_width - plugin.settings.swipe_edge_proximity ) ){ 183 e.preventDefault(); 184 185 //if swipe more than 150, open panel 186 if( ( wrap_start_x - wrap_cur_x > plugin.settings.swipe_tolerance_x ) ){ 187 wrap_cur_y = e.originalEvent.changedTouches[0].pageY; 188 if( Math.abs( wrap_cur_y - wrap_start_y ) < plugin.settings.swipe_tolerance_y ){ 189 plugin.openShiftNav(); 190 e.stopPropagation(); 191 } 192 } 193 } 194 195 196 }); 197 } 198 } 199 200 //Handle searchbar toggle 201 $( '.shiftnav-searchbar-toggle' ).on( this.toggleevent , function( e ){ 202 e.stopPropagation(); 203 e.preventDefault(); 204 205 var $drop = $( this ).next( '.shiftnav-searchbar-drop' ); 206 207 //Close 208 if( $drop.hasClass( 'shiftnav-searchbar-drop-open' ) ){ 209 $drop.removeClass( 'shiftnav-searchbar-drop-open' ); 210 $( 'body' ).off( 'click.shiftnav-searchbar-drop' ); 211 } 212 //Open 213 else{ 214 $drop.addClass( 'shiftnav-searchbar-drop-open' ); 215 $drop.find( '.shiftnav-search-input' ).focus(); 216 217 //Close on click-off - can't do this immediately because IE is so damn dumb 218 setTimeout( function(){ 219 $( 'body' ).on( 'click.shiftnav-searchbar-drop' , function( e ){ 220 $( '.shiftnav-searchbar-drop' ).removeClass( 'shiftnav-searchbar-drop-open' ); 221 $( 'body' ).off( 'click.shiftnav-searchbar-drop' ); 222 }); 223 }, 100); 224 } 225 }); 226 $( '.shiftnav-searchbar-drop' ).on( this.toggleevent , function( e ){ 227 e.stopPropagation(); 228 }); 229 $( '.shiftnav-searchbar-drop .shiftnav-search-input').on( 'blur' , function( e ){ 230 if( $( this ).val() == '' && !toggle_clicked ){ 231 $( this ).parents( '.shiftnav-searchbar-drop' ).removeClass( 'shiftnav-searchbar-drop-open' ); 232 } 233 }); 234 var toggle_clicked; 235 $( '.shiftnav-searchbar-toggle' ).on( 'mousedown' , function( e ){ 236 toggle_clicked = true; 237 }); 238 $( '.shiftnav-searchbar-toggle' ).on( 'mouseup' , function( e ){ 239 toggle_clicked = false;
240 }); 241 242 243 } 244 245 this.$shiftnav.appendTo( 'body' ); 246 247 if( this.$shiftnav.hasClass( 'shiftnav-right-edge' ) ){ 248 this.edge = 'right'; 249 } 250 else this.edge = 'left'; 251 252 this.openclass = 'shiftnav-open shiftnav-open-' + this.edge; 253 254 //Set retractor heights 255 this.$shiftnav.find( '.shiftnav-submenu-activation' ).each( function(){ 256 var length = $( this ).outerHeight(); 257 $( this ).css( { 'height' : length , 'width' : length } ); 258 259 //$( this ).css( 'height' , $( this ).parent( '.menu-item' ).height() ); 260 }); 261 262 263 264 //Current open 265 if( plugin.settings.open_current ){ 266 $( '.shiftnav .shiftnav-sub-accordion.current-menu-item, .shiftnav .shiftnav-sub-accordion.current-menu-item-ancestor' ).addClass( 'shiftnav-active' ); 267 } 268 269 }, 270 271 initializeTargets: function(){ 272 273 var plugin = this; 274 275 this.$shiftnav.on( 'click' , '.shiftnav-target' , function( e ){ 276 var scrolltarget = $(this).data( 'shiftnav-scrolltarget' ); 277 if( scrolltarget ){ 278 var $target = $( scrolltarget ).first(); 279 if( $target.size() > 0 ){ 280 $( 'html,body' ).animate({ 281 scrollTop: $target.offset().top 282 }, 1000 , 'swing' , 283 function(){ 284 plugin.closeShiftNav(); //close the menu after a successful scroll 285 }); 286 return false; //don't follow any links if this scroll target is present 287 } 288 //if target isn't present here, redirect with hash 289 else{ 290 var href = $(this).attr( 'href' ); 291 if( href && href.indexOf( '#' ) == -1 ){ //check that hash does not already exist 292 if( scrolltarget.indexOf( '#' ) == -1 ){ //if this is a class, add a hash tag 293 scrolltarget = '#'+scrolltarget; 294 } 295 window.location = href + scrolltarget; //append hash/scroll target to URL and redirect 296 e.preventDefault(); 297 } 298 //No href, no worries 299 } 300 } 301 else if( $( this ).is( 'span' ) ){ 302 var $li = $( this ).parent( '.menu-item' ); 303 if( $li.hasClass( 'shiftnav-active' ) ){ 304 plugin.closeSubmenu( $li , 'disabledLink' , plugin ); 305 } 306 else{ 307 plugin.openSubmenu( $li , 'disabledLink' , plugin ); 308 } 309 } 310 }); 311 312 }, 313 314 initializeSubmenuToggleMouseEvents: function(){ 315 316 //Don't initialize if mouse events are disabled 317 if( !this.settings.mouseEvents ) return; 318 if( this.settings.clicktest ) return; 319 if( this.settings.windowstest ) return; 320 321 322 if( this.settings.debug ) console.log( 'initializeSubmenuToggleMouseEvents' ); 323 324 var plugin = this; 325 326 this.$shiftnav.on( 'mouseup.shift-submenu-toggle' , '.shiftnav-submenu-activation' , function(e){ plugin.handleMouseActivation( e , this , plugin ); } ); 327 //$shiftnav.on( 'mouseout.shift-submenu-toggle' , '.menu-item' , this.handleMouseout ); //now only added on mouseover 328 }, 329 330 disableSubmenuToggleMouseEvents: function(){ 331 if( this.settings.debug ) console.log( 'disableSubmenuToggleMouseEvents' ); 332 $shiftnav.off( 'mouseover.shift-submenu-toggle' ); 333 $shiftnav.off( 'mouseout.shift-submenu-toggle' ); 334 }, 335 336 337 initializeRetractors: function() { 338 339 if( !this.settings.retractors ) return; //Don't initialize if retractors are disabled 340 var plugin = this; 341 342 //set up the retractors 343 this.$shiftnav.on( 'mouseup.shiftnav' , '.shiftnav-retract' , function(e){ plugin.handleSubmenuRetractorEnd( e , this, plugin); } ); 344 }, 345 346 347 initializeResponsiveToggle: function(){ 348 349 var plugin = this; 350 351 //this.$toggles.on( 'click', 'a', function(e){ 352 this.$toggles.on( this.toggleevent, 'a', function(e){ 353 //allow link to be clicked but don't propagate so toggle won't activate 354 e.stopPropagation(); 355 }); 356 357 //this.$toggles.on( 'click', function(e){ 358 this.$toggles.on( this.toggleevent, function(e){ 359 360 e.preventDefault(); 361 e.stopPropagation(); 362 363 //Ignore click events when toggle is disabled to avoid both touch and click events firing 364 if( e.originalEvent.type == 'click' && $(this).data( 'disableToggle' ) ){ 365 return; 366 } 367 368 if( plugin.$shiftnav.hasClass( 'shiftnav-open-target' ) ){ 369 //console.log( 'close shift nav' ); 370 plugin.closeShiftNav(); 371 } 372 else{ 373 //console.log('open shift nav'); 374 plugin.openShiftNav(); 375 } 376 377 //Temporarily disable toggle for click event when touch is fired 378 if( e.originalEvent.type != 'click' ){ 379 $( this ).data( 'disableToggle' , true ); 380 setTimeout( function(){ 381 $( this ).data( 'disableToggle' , false ); 382 }, 1000 ); 383 } 384 385 return false;
386 387 }); 388 389 }, 390 391 initializeSwipeHandler: function(){ 392 393 var $body = $('body'), 394 start_y = 0, 395 start_x = 0, 396 cur_y = 0, 397 cur_x = 0, 398 plugin = this, 399 scrollprevented = false, 400 viewport_height = $(window).height(), 401 $scrollPanel = this.$shiftnav.find( '.shiftnav-inner' ); 402 403 //Track where touches start 404 $scrollPanel.on( 'touchstart' , function( e ){ 405 start_y = e.originalEvent.changedTouches[0].pageY; 406 start_x = e.originalEvent.changedTouches[0].pageX; 407 }); 408 409 //On drag, when at extents of scroll panel, prevent scrolling of background 410 $scrollPanel.on( this.touchMove , function( e ){ 411 412 scrollprevented = false; //keep track to maximize efficiency 413 414 //If the ShiftNav panel is too short to scroll, prevent scrolling entirely 415 //Check here because panel height can change when submenus are expanded 416 if( viewport_height >= $scrollPanel[0].scrollHeight ){ 417 scrollprevented = true; 418 e.preventDefault(); 419 } 420 //If at top of scroll panel, prevent scrolling up 421 else if( e.currentTarget.scrollTop === 0 ){ 422 cur_y = e.originalEvent.changedTouches[0].pageY; 423 if( cur_y > start_y ){ 424 //console.log( 'TOP | scrolling up' ); 425 scrollprevented = true; 426 e.preventDefault(); 427 } 428 } 429 //If at bottom of scroll panel, prevent scrolling down 430 else if( e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight ){ 431 cur_y = e.originalEvent.changedTouches[0].pageY; 432 if( cur_y < start_y ){ 433 //console.log( 'TOP | scrolling up' ); 434 scrollprevented = true; 435 e.preventDefault(); 436 } 437 } 438 439 //If the scroll hasn't already been nuked, but 440 //we're scrolling horizontally instead of vertically, stop that 441 if( !scrollprevented ){ 442 diff_y = Math.abs( start_y - e.originalEvent.changedTouches[0].pageY ); 443 diff_x = Math.abs( start_x - e.originalEvent.changedTouches[0].pageX ); 444 if( diff_y < diff_x ){ 445 e.preventDefault(); 446 } 447 } 448 }); 449 450 //Swipe in the appropriate direction to close the menu 451 if( shiftnav_data.swipe_close == 'on' ){ 452 453 if( this.$shiftnav.hasClass( 'shiftnav-right-edge' ) ){ 454 $scrollPanel.on( 'touchmove' , function( e ){ 455 cur_x = e.originalEvent.changedTouches[0].pageX; 456 //console.log( cur_x ); 457 if( cur_x - start_x > plugin.settings.swipe_tolerance_x ){ 458 if( Math.abs( cur_y - start_y ) < plugin.settings.swipe_tolerance_y ){ 459 plugin.closeShiftNav(); 460 e.preventDefault(); 461 } 462 } 463 }); 464 } 465 else{ 466 $scrollPanel.on( 'touchmove' , function( e ){ 467 cur_x = e.originalEvent.changedTouches[0].pageX; 468 //console.log( cur_x ); 469 if( start_x - cur_x > plugin.settings.swipe_tolerance_x ){ 470 cur_y = e.originalEvent.changedTouches[0].pageY; 471 if( Math.abs( cur_y - start_y ) < plugin.settings.swipe_tolerance_y ){ 472 plugin.closeShiftNav(); 473 e.preventDefault(); 474 } 475 } 476 e.stopPropagation(); 477 }); 478 } 479 } 480 481 482 483 }, 484 485 openShiftNav: function(){ 486 487 var plugin = this; 488 489 $( 'body' ) 490 .removeClass( 'shiftnav-open-right shiftnav-open-left' ) 491 .addClass( this.openclass ) 492 .addClass( 'shiftnav-transitioning' ); 493 494 //console.log( 'close ' + $( '.shiftnav-open-target' ).attr( 'id' ) ); 495 $( '.shiftnav-open-target' ).removeClass( 'shiftnav-open-target' ); 496 this.$shiftnav 497 .addClass( 'shiftnav-open-target' ) 498 .on( plugin.transitionend, function(){ 499 //if( plugin.settings.debug ) console.log( 'finished submenu close transition' ); 500 $( 'body' ).removeClass( 'shiftnav-transitioning' ); 501 $( this ).off( plugin.transitionend ); 502 }); 503 504 this.initializeTouchoffClose(); 505 506 }, 507 508 closeShiftNav: function(){ 509 510 var plugin = this; 511 512 $( 'body' ) 513 .removeClass( this.openclass ) 514 .addClass( 'shiftnav-transitioning' ); 515 516 this.$shiftnav 517 .removeClass( 'shiftnav-open-target' ) 518 .on( plugin.transitionend, function(){ 519 //if( plugin.settings.debug ) console.log( 'finished submenu close transition' ); 520 $( 'body' ).removeClass( 'shiftnav-transitioning' ); 521 $( this ).off( plugin.transitionend ); 522 }); 523 524 this.disableTouchoffClose(); 525 }, 526 527 initializeTouchoffClose: function(){ 528 529 if( !this.settings.touchOffClose ) return; //Don't initialize if touch off close is disabled 530 531 var plugin = this; 532 $( document ).on( 'click.shiftnav ' + this.touchEnd + '.shiftnav' , function( e ){ plugin.handleTouchoffClose( e , this , plugin ); } ); 533 534 }, 535 disableTouchoffClose: function(){ 536 $( document ).off( '.shiftnav' ); 537 }, 538 539 handleMouseActivation: function( e , activator , plugin ){ 540 if( plugin.settings.debug ) console.log( 'handleMouseover, add mouseout', e ); 541 542 var $li = $( activator ).parent(); 543 544 if( $li.hasClass( 'shiftnav-active' ) ){ 545 plugin.closeSubmenu( $li , 'mouseActivate' , plugin ); 546 } 547 else{ 548 plugin.openSubmenu( $li , 'mouseActivate' , plugin ); 549 } 550 551 //Only attach mouseout after mouseover, this way menus opened by touch won't be closed by mouseout 552 //$li.on( 'mouseout.shift-submenu-toggle' , function( e ){ plugin.handleMouseout( e , this , plugin ); } ); 553 }, 554 555 556 handleSubmenuRetractorEnd: function( e , li , plugin ){ 557 e.preventDefault(); 558 e.stopPropagation(); 559 560 var $li = $(li).parent( 'ul' ).parent( 'li' ); 561 plugin.closeSubmenu( $li , 'handleSubmenuRetractor' , plugin ); 562 563 if( plugin.settings.debug ) console.log( 'handleSubmenuRetractorEnd ' + $li.find('> a').text()); 564 565 }, 566 567 568 569 handleTouchoffClose: function( e , _this , plugin ){ 570 571 //Don't fire during transtion 572 if( $( 'body' ).is( '.shiftnav-transitioning' ) ) return; 573 574 if( $(e.target).parents().add( $(e.target) ).filter( '.shiftnav, .shiftnav-toggle, .shiftnav-ignore' ).size() === 0 ){ 575 576 577 if( plugin.settings.debug ) console.log( 'touchoff close ', e ); 578 579 e.preventDefault(); 580 e.stopPropagation(); 581 582 plugin.closeShiftNav(); 583 plugin.disableTouchoffClose(); 584 585 } 586 587 }, 588 589 590 591 592 /* Controllers */ 593 scrollPanel: function( y ){ 594 if( shiftnav_data.scroll_panel == 'off' ) return 0; 595 596 if( typeof y == 'undefined' ){ 597 return this.$shiftnav.find( '.shiftnav-inner' ).scrollTop(); 598 } 599 else{ 600 this.$shiftnav.find( '.shiftnav-inner' ).scrollTop( y ); 601 } 602 }, 603 604 openSubmenu: function( $li , tag , plugin ){ 605 if( !$li.hasClass( 'shiftnav-active' ) ){ 606 607 //plugin.setMinimumHeight( 'open' , $li ); 608 609 if( $li.hasClass( 'shiftnav-sub-shift' ) ){ 610 611 $li.siblings( '.shiftnav-active' ).removeClass( 'shiftnav-active' ); 612 613 //switch to position absolute, then delay activation below due to Firefox browser bug 614 $li.toggleClass( 'shiftnav-caulk' ); 615 616 plugin.$shiftnav.addClass( 'shiftnav-sub-shift-active' ); 617 618 } 619 else{ 620 if( plugin.settings.collapse_accordions ){ 621 $li.siblings( '.shiftnav-active' ).removeClass( 'shiftnav-active' ); 622 } 623 } 624 625 //Active flags 626 $li.parents( 'ul' ).removeClass( 'shiftnav-sub-active-current' ); 627 $li.find( '> ul' ) 628 .addClass( 'shiftnav-sub-active' ) 629 .addClass( 'shiftnav-sub-active-current' ); 630 631 //A dumb timeout hack to fix this FireFox browser bug https://bugzilla.mozilla.org/show_bug.cgi?id=625289 632 setTimeout( function(){ 633 $li.addClass( 'shiftnav-active' ); 634 $li.trigger( 'shiftnav-open-submenu' ); //API 635 $li.removeClass( 'shiftnav-caulk' ); 636 637 //Wait until item has moved before calculating position 638 setTimeout( function(){ 639 //scroll to top of the menu, make note of initial position 640 var y = plugin.scrollPanel(); 641 $li.data( 'scroll-back' , y );
642 var scrollPanelTo = $li.offset().top + y; 643 plugin.scrollPanel( scrollPanelTo ); 644 //plugin.scrollPanel( 0 ); 645 }, 100 ); 646 647 }, 1 ); 648 } 649 }, 650 651 closeSubmenu: function( $li , tag , plugin ){ 652 653 //var plugin = this; 654 655 if( this.settings.debug ) console.log( 'closeSubmenu ' + $li.find( '>a' ).text() + ' [' + tag + ']' ); 656 657 //If this menu is currently active and has a submenu, close it 658 if( $li.hasClass( 'menu-item-has-children' ) && $li.hasClass( 'shiftnav-active' ) ){ 659 660 $li.addClass( 'shiftnav-in-transition' ); //transition class keeps visual flag until transition completes 661 662 $li.each( function(){ 663 var _$li = $(this); 664 var _$ul = _$li.find( '> ul' ); 665 666 //Remove the transition flag once the transition is completed 667 _$ul.on( plugin.transitionend + '_closesubmenu', function(){ 668 if( plugin.settings.debug ) console.log( 'finished submenu close transition' ); 669 _$li.removeClass( 'shiftnav-in-transition' ); 670 _$ul.off( plugin.transitionend + '_closesubmenu' ); 671 }); 672 }); 673 } 674 675 //Actually remove the active class, which causes the submenu to close 676 $li.removeClass( 'shiftnav-active' ); 677 678 //Shift Sub Specific 679 if( $li.hasClass( 'shiftnav-sub-shift' ) ){ 680 if( $li.parents( '.shiftnav-sub-shift' ).size() == 0 ) plugin.$shiftnav.removeClass( 'shiftnav-sub-shift-active' ); 681 682 //return to original position 683 var y = $li.data( 'scroll-back' ); 684 if( y !== 'undefined' ) plugin.scrollPanel( y ); 685 //console.log( 'y = ' + y ); 686 687 } 688 689 //Active flags 690 $li.find( '> ul' ) 691 .removeClass( 'shiftnav-sub-active' ) 692 .removeClass( 'shiftnav-sub-active-current' ); 693 $li.closest( 'ul' ).addClass( 'shiftnav-sub-active-current' ); 694 695 $li.trigger( 'shiftnav-close-submenu' ); //API 696 697 }, 698 699 closeAllSubmenus: function(){ 700 $( this.element ).find( 'li.menu-item-has-children' ).removeClass( 'shiftnav-active' ); 701 }, 702 703 }; 704 705 $.fn[ pluginName ] = function ( options ) { 706 707 var args = arguments; 708 709 if ( options === undefined || typeof options === 'object' ) { 710 return this.each(function() { 711 if ( !$.data( this, "plugin_" + pluginName ) ) { 712 $.data( this, "plugin_" + pluginName, new Plugin( this, options ) ); 713 } 714 }); 715 } 716 else if ( typeof options === 'string' && options[0] !== '_' && options !== 'init') { 717 // Cache the method call to make it possible to return a value 718 var returns; 719 720 this.each(function () { 721 var instance = $.data(this, 'plugin_' + pluginName); 722 723 // Tests that there's already a plugin-instance and checks that the requested public method exists 724 if ( instance instanceof Plugin && typeof instance[options] === 'function') { 725 726 // Call the method of our plugin instance, and pass it the supplied arguments. 727 returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) ); 728 } 729 730 // Allow instances to be destroyed via the 'destroy' method 731 if (options === 'destroy') { 732 $.data(this, 'plugin_' + pluginName, null); 733 } 734 }); 735 736 // If the earlier cached method gives a value back return the value, otherwise return this to preserve chainability. 737 return returns !== undefined ? returns : this; 738 } 739 }; 740 741})( jQuery, window, document ); 742 743jQuery( document ).ready( function($){ 744 745 //Remove Loading Message 746 $( '.shiftnav-loading' ).remove(); 747 748 //Scroll to non-ID "hashes" 749 if( window.location.hash.substring(1,2) == '.' ){ 750 var $scrollTarget = $( window.location.hash.substring(1) ); 751 if( $scrollTarget.size() ) window.scrollTo( 0 , $scrollTarget.offset().top ); 752 } 753 754 //Run ShiftNav 755 jQuery( '.shiftnav' ).shiftnav({ 756 swipe_tolerance_x : parseInt( shiftnav_data.swipe_tolerance_x ), 757 swipe_tolerance_y : parseInt( shiftnav_data.swipe_tolerance_y ), 758 swipe_edge_proximity : parseInt( shiftnav_data.swipe_edge_proximity ), 759 open_current : shiftnav_data.open_current == 'on' ? true : false, 760 collapse_accordions : shiftnav_data.collapse_accordions == 'on' ? true : false 761 //debug: true 762 //mouseEvents: false 763 //clicktest: true 764 }); 765 766}); 767 768 769 770var shift_supports = (function() { 771 var div = document.createElement('div'), 772 vendors = 'Khtml Ms O Moz Webkit'.split(' '); 773 774 775 return function(prop) { 776
777 var len = vendors.length; 778 779 if ( prop in div.style ) return true; 780 781 prop = prop.replace(/^[a-z]/, function(val) { 782 return val.toUpperCase(); 783 }); 784 785 while(len--) { 786 if ( vendors[len] + prop in div.style ) { 787 return true; 788 } 789 } 790 return false; 791 }; 792})();
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.