PageSourceSearch

https://canalhr.com/wp-content/themes/canal-theme/js/jquery.slicknav.js

js canalhr.com collected 2026-09-29 20:00:26 UTC 10,720 bytes, 373 lines download raw bytes

1/*!
2	SlickNav Responsive Mobile Menu
3	(c) 2014 Josh Cope
4	licensed under MIT
5*/
6;(function ($, document, window) {
7	var
8	// default settings object.
9	defaults = {
10		label: 'MENU',
11		duplicate: true,
12		duration: 200,
13		easingOpen: 'swing',
14		easingClose: 'swing',
15		closedSymbol: '►',
16		openedSymbol: '▼',
17		prependTo: 'body',
18		parentTag: 'a',
19		closeOnClick: false,
20		allowParentLinks: false,
21		init: function(){},
22		open: function(){},
23		close: function(){}
24	},
25	mobileMenu = 'slicknav',
26	prefix = 'slicknav';
27	
28	function Plugin( element, options ) {
29		this.element = element;
30
31        // jQuery has an extend method which merges the contents of two or
32        // more objects, storing the result in the first object. The first object
33        // is generally empty as we don't want to alter the default options for
34        // future instances of the plugin
35        this.settings = $.extend( {}, defaults, options) ;
36        
37        this._defaults = defaults;
38        this._name = mobileMenu;
39        
40        this.init();
41	}
42	
43	Plugin.prototype.init = function () {
44        var $this = this;
45		var menu = $(this.element);
46		var settings = this.settings;
47		
48		// clone menu if needed
49		if (settings.duplicate) {
50			$this.mobileNav = menu.clone();
51			//remove ids from clone to prevent css issues
52			$this.mobileNav.removeAttr('id');
53			$this.mobileNav.find('*').each(function(i,e){
54				$(e).removeAttr('id');
55			});
56		}
57		else
58			$this.mobileNav = menu;
59		
60		// styling class for the button
61		var iconClass = prefix+'_icon';
62		
63		if (settings.label == '') {
64			iconClass += ' '+prefix+'_no-text';
65		}
66		
67		if (settings.parentTag == 'a') {
68			settings.parentTag = 'a href="#"';
69		}
70		
71		// create menu bar
72		$this.mobileNav.attr('class', prefix+'_nav');
73		var menuBar = $('<div class="'+prefix+'_menu"></div>');
74		$this.btn = $('<'+settings.parentTag+' aria-haspopup="true" tabindex="0" class="'+prefix+'_btn '+prefix+'_collapsed"><span class="'+prefix+'_menutxt">'+settings.label+'</span><span class="'+iconClass+'"><span class="'+prefix+'_icon-bar"></span><span class="'+prefix+'_icon-bar"></span><span class="'+prefix+'_icon-bar"></span></span></a>');
75		$(menuBar).append($this.btn);		
76		$(settings.prependTo).prepend(menuBar);
77		menuBar.append($this.mobileNav);
78		
79		// iterate over structure adding additional structure
80		var items = $this.mobileNav.find('li');
81		$(items).each(function () {
82			var item = $(this);
83			data = {};
84			data.children = item.children('ul').attr('role','menu');
85			item.data("menu", data);
86			
87			// if a list item has a nested menu
88			if (data.children.length > 0) {
89			
90				// select all text before the child menu
91				var a = item.contents();
92				var nodes = [];
93				$(a).each(function(){
94					if(!$(this).is("ul")) {
95						nodes.push(this);
96					}
97					else {
98						return false;
99					}
100				});
101				
102				// wrap item text with tag and add classes
103				var wrap = $(nodes).wrapAll('<'+settings.parentTag+' role="menuitem" aria-haspopup="true" tabindex="-1" class="'+prefix+'_item"/>').parent();
104				
105				item.addClass(prefix+'_collapsed');
106				item.addClass(prefix+'_parent');
107				
108				// create parent arrow
109				$(nodes).last().after('<span class="'+prefix+'_arrow">'+settings.closedSymbol+'</span>');
110				
111			
112			} else if ( item.children().length == 0) {
113				 item.addClass(prefix+'_txtnode');
114			}
115			
116			// accessibility for links
117			item.children('a').attr('role', 'menuitem').click(function(){
118				//Emulate menu close if set
119				if (settings.closeOnClick)
120					$($this.btn).click();
121			});
122		});
123		
124		// structure is in place, now hide appropriate items
125		$(items).each(function () {
126			var data = $(this).data("menu");
127			$this._visibilityToggle(data.children, false, null, true);
128		});
129		
130		// finally toggle entire menu
131		$this._visibilityToggle($this.mobileNav, false, 'init', true);
132		
133		// accessibility for menu button
134		$this.mobileNav.attr('role','menu');
135		
136		// outline prevention when using mouse
137		$(document).mousedown(function(){
138			$this._outlines(false);
139		});
140		
141		$(document).keyup(function(){
142			$this._outlines(true);
143		});
144		
145		// menu button click
146		$($this.btn).click(function (e) {
147			e.preventDefault();
148			$this._menuToggle();			
149		});
150		
151		// click on menu parent
152		$this.mobileNav.on('click', '.'+prefix+'_item', function(e){
153			e.preventDefault();
154			$this._itemClick($(this));
155		});
156		
157		// check for enter key on menu button and menu parents
158		$($this.btn).keydown(function (e) {
159			var ev = e || event;
160			if(ev.keyCode == 13) {
161				e.preventDefault();
162				$this._menuToggle();
163			}
164		});
165		
166		$this.mobileNav.on('keydown', '.'+prefix+'_item', function(e) {
167			var ev = e || event;
168			if(ev.keyCode == 13) {
169				e.preventDefault();
170				$this._itemClick($(e.target));
171			}
172		});
173		
174		// allow links clickable within parent tags if set
175		if (settings.allowParentLinks) {
176			$('.'+prefix+'_item a').click(function(e){
177					e.stopImmediatePropagation();
178			});
179		}
180    };
181	
182	//toggle menu
183	Plugin.prototype._menuToggle = function(el){
184		var $this = this;
185		var btn = $this.btn;
186		var mobileNav = $this.mobileNav;
187		
188		if (btn.hasClass(prefix+'_collapsed')) {
189			btn.removeClass(prefix+'_collapsed');
190			btn.addClass(prefix+'_open');
191		} else {
192			btn.removeClass(prefix+'_open');
193			btn.addClass(prefix+'_collapsed');
194		}
195		btn.addClass(prefix+'_animating');
196		$this._visibilityToggle(mobileNav, true, btn);
197	}
198	
199	// toggle clicked items
200	Plugin.prototype._itemClick = function(el) {
201		var $this = this;
202		var settings = $this.settings;
203		var data = el.data("menu");
204		if (!data) {
205			data = {};
206			data.arrow = el.children('.'+prefix+'_arrow');
207			data.ul = el.next('ul');
208			data.parent = el.parent();
209			el.data("menu", data);
210		}
211		if (data.parent.hasClass(prefix+'_collapsed')) {
212			data.arrow.html(settings.openedSymbol);
213			data.parent.removeClass(prefix+'_collapsed');
214			data.parent.addClass(prefix+'_open');
215			data.parent.addClass(prefix+'_animating');
216			$this._visibilityToggle(data.ul, true, el);
217		} else {
218			data.arrow.html(settings.closedSymbol);
219			data.parent.addClass(prefix+'_collapsed');
220			data.parent.removeClass(prefix+'_open');
221			data.parent.addClass(prefix+'_animating');
222			$this._visibilityToggle(data.ul, true, el);
223		}
224	}
225
226	// toggle actual visibility and accessibility tags
227	Plugin.prototype._visibilityToggle = function(el, animate, trigger, init) {
228		var $this = this;
229		var settings = $this.settings;
230		var items = $this._getActionItems(el);
231		var duration = 0;
232		if (animate)
233			duration = settings.duration;
234		
235		if (el.hasClass(prefix+'_hidden')) {
236			el.removeClass(prefix+'_hidden');
237			el.slideDown(duration, settings.easingOpen, function(){
238				
239				$(trigger).removeClass(prefix+'_animating');
240				$(trigger).parent().removeClass(prefix+'_animating');
241				
242				//Fire open callback
243				if (!init) {
244					settings.open(trigger);
245				}
246			});
247			el.attr('aria-hidden','false');
248			items.attr('tabindex', '0');
249			$this._setVisAttr(el, false);
250		} else {
251			el.addClass(prefix+'_hidden');
252			el.slideUp(duration, this.settings.easingClose, function() {
253				el.attr('aria-hidden','true');
254				items.attr('tabindex', '-1');
255				$this._setVisAttr(el, true);
256				el.hide(); //jQuery 1.7 bug fix
257				
258				$(trigger).removeClass(prefix+'_animating');
259				$(trigger).parent().removeClass(prefix+'_animating');
260				
261				//Fire init or close callback
262				if (!init)
263					settings.close(trigger);
264				else if (trigger == 'init')
265					settings.init();
266			});
267		}
268	}
269
270	// set attributes of element and children based on visibility
271	Plugin.prototype._setVisAttr = function(el, hidden) {
272		var $this = this;
273		
274		// select all parents that aren't hidden
275		var nonHidden = el.children('li').children('ul').not('.'+prefix+'_hidden');
276		
277		// iterate over all items setting appropriate tags
278		if (!hidden) {
279			nonHidden.each(function(){
280				var ul = $(this);
281				ul.attr('aria-hidden','false');
282				var items = $this._getActionItems(ul);
283				items.attr('tabindex', '0');
284				$this._setVisAttr(ul, hidden);
285			});
286		} else {
287			nonHidden.each(function(){
288				var ul = $(this);
289				ul.attr('aria-hidden','true');
290				var items = $this._getActionItems(ul);
291				items.attr('tabindex', '-1');
292				$this._setVisAttr(ul, hidden);
293			});
294		}
295	}
296
297	// get all 1st level items that are clickable
298	Plugin.prototype._getActionItems = function(el) {
299		var data = el.data("menu");
300		if (!data) {
301			data = {};
302			var items = el.children('li');
303			var anchors = items.children('a');
304			data.links = anchors.add(items.children('.'+prefix+'_item'));
305			el.data("menu", data);
306		}
307		return data.links;
308	}
309
310	Plugin.prototype._outlines = function(state) {
311		if (!state) {
312			$('.'+prefix+'_item, .'+prefix+'_btn').css('outline','none');
313		} else {
314			$('.'+prefix+'_item, .'+prefix+'_btn').css('outline','');
315		}
316	}
317	
318	Plugin.prototype.toggle = function(){
319		$this._menuToggle();
320	}
321	
322	Plugin.prototype.open = function(){
323		$this = this;
324		if ($this.btn.hasClass(prefix+'_collapsed')) {
325			$this._menuToggle();
326		}
327	}
328	
329	Plugin.prototype.close = function(){
330		$this = this;
331		if ($this.btn.hasClass(prefix+'_open')) {
332			$this._menuToggle();
333		}
334	}
335	
336	$.fn[mobileMenu] = function ( options ) {
337		var args = arguments;
338
339		// Is the first parameter an object (options), or was omitted, instantiate a new instance
340		if (options === undefined || typeof options === 'object') {
341			return this.each(function () {
342
343				// Only allow the plugin to be instantiated once due to methods
344				if (!$.data(this, 'plugin_' + mobileMenu)) {
345
346					// if it has no instance, create a new one, pass options to our plugin constructor,
347					// and store the plugin instance in the elements jQuery data object.
348					$.data(this, 'plugin_' + mobileMenu, new Plugin( this, options ));
349				}
350			});
351
352		// If is a string and doesn't start with an underscore or 'init' function, treat this as a call to a public method.
353		} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
354
355			// Cache the method call to make it possible to return a value
356			var returns;
357
358			this.each(function () {
359				var instance = $.data(this, 'plugin_' + mobileMenu);
360
361				// Tests that there's already a plugin-instance and checks that the requested public method exists
362				if (instance instanceof Plugin && typeof instance[options] === 'function') {
363
364					// Call the method of our plugin instance, and pass it the supplied arguments.
365					returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
366				}
367			});
368
369			// If the earlier cached method gives a value back return the value, otherwise return this to preserve chainability.
370			return returns !== undefined ? returns : this;
371		}
372	};
373}(jQuery, document, window));

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.