PageSourceSearch

https://www.beaucaire.fr/wp-content/plugins/shiftnav-pro/assets/js/shiftnav.js?ver=1.2.0.1

js beaucaire.fr collected 2026-10-02 11:09:39 UTC 24,076 bytes, 792 lines download raw bytes

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.