root / drupal7 / sites / all / modules / adminimal_admin_menu / js / slicknav / jquery.slicknav.js @ 024de6ea
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)); |
374 |
|
375 |
(function($) { |
376 |
|
377 |
// Create the responsive menu using SlickNav.
|
378 |
Drupal.admin.behaviors.responsivemenu = function (context, settings, $adminMenu) { |
379 |
|
380 |
$('#admin-menu-menu-responsive').slicknav({ |
381 |
label: 'Menu', |
382 |
prependTo:'body', |
383 |
closedSymbol: "<i class=\"closed\"></i>", |
384 |
openedSymbol: "<i class=\"open\"></i>", |
385 |
allowParentLinks: true |
386 |
}); |
387 |
|
388 |
}; |
389 |
|
390 |
// Create the responsive shortcuts dropdown.
|
391 |
Drupal.admin.behaviors.responsiveshortcuts = function (context, settings, $adminMenu) { |
392 |
|
393 |
// Check if there are any shortucts to respondify.
|
394 |
if(jQuery("div.toolbar-shortcuts ul.menu li").length){ |
395 |
|
396 |
// Create the dropdown base
|
397 |
$("<select id='responsive-shortcuts-dropdown'/>").appendTo("#admin-menu-shortcuts-responsive div.toolbar-shortcuts"); |
398 |
|
399 |
// Create default option "Select"
|
400 |
$("<option />", { |
401 |
"selected" : "selected", |
402 |
"class" : "hide", |
403 |
"value" : "", |
404 |
"text" : Drupal.t('Shortcuts') |
405 |
}).appendTo("#admin-menu-shortcuts-responsive div.toolbar-shortcuts select");
|
406 |
|
407 |
// Populate dropdown with menu items
|
408 |
$("#admin-menu-shortcuts-responsive div.toolbar-shortcuts a").each(function() { |
409 |
var el = $(this); |
410 |
$("<option />", { |
411 |
"value" : el.attr("href"), |
412 |
"text" : el.text()
|
413 |
}).appendTo("#admin-menu-shortcuts-responsive div.toolbar-shortcuts select");
|
414 |
}); |
415 |
|
416 |
// Redirect the user when selecting an option.
|
417 |
$("#admin-menu-shortcuts-responsive div.toolbar-shortcuts select").change(function() { |
418 |
window.location = $(this).find("option:selected").val(); |
419 |
}); |
420 |
|
421 |
// Clean the mess.
|
422 |
$('#admin-menu-shortcuts-responsive div.toolbar-shortcuts ul').remove(); |
423 |
// Move the select box into the responsive menu.
|
424 |
$("#admin-menu-shortcuts-responsive").prependTo(".slicknav_menu"); |
425 |
|
426 |
} |
427 |
|
428 |
// Remove the edit shortcuts link from the DOM to avoid duble rendering.
|
429 |
$('#admin-menu-shortcuts-responsive #edit-shortcuts').remove(); |
430 |
|
431 |
}; |
432 |
})(jQuery); |