|
0
|
1 |
/*-------------------------------------------------------------------- |
|
|
2 |
Scripts for creating and manipulating custom menus based on standard <ul> markup |
|
|
3 |
Version: 3.0, 03.31.2009 |
|
|
4 |
|
|
|
5 |
By: Maggie Costello Wachs (maggie@filamentgroup.com) and Scott Jehl (scott@filamentgroup.com) |
|
|
6 |
http://www.filamentgroup.com |
|
|
7 |
* reference articles: http://www.filamentgroup.com/lab/jquery_ipod_style_drilldown_menu/ |
|
|
8 |
|
|
|
9 |
Copyright (c) 2009 Filament Group |
|
|
10 |
Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses. |
|
|
11 |
--------------------------------------------------------------------*/ |
|
|
12 |
|
|
|
13 |
|
|
|
14 |
var allUIMenus = []; |
|
|
15 |
|
|
|
16 |
$.fn.menu = function(options){ |
|
|
17 |
var caller = this; |
|
|
18 |
var options = options; |
|
|
19 |
var m = new Menu(caller, options); |
|
|
20 |
allUIMenus.push(m); |
|
|
21 |
|
|
|
22 |
$(this) |
|
|
23 |
.mousedown(function(){ |
|
|
24 |
if (!m.menuOpen) { m.showLoading(); }; |
|
|
25 |
}) |
|
|
26 |
.click(function(){ |
|
|
27 |
if (m.menuOpen == false) { m.showMenu(); } |
|
|
28 |
else { m.kill(); }; |
|
|
29 |
return false; |
|
|
30 |
}); |
|
|
31 |
}; |
|
|
32 |
|
|
|
33 |
function Menu(caller, options){ |
|
|
34 |
var menu = this; |
|
|
35 |
var caller = $(caller); |
|
|
36 |
var container = $('<div class="fg-menu-container ui-widget ui-widget-content ui-corner-all">'+options.content+'</div>'); |
|
|
37 |
|
|
|
38 |
this.menuOpen = false; |
|
|
39 |
this.menuExists = false; |
|
|
40 |
|
|
|
41 |
var options = jQuery.extend({ |
|
|
42 |
content: null, |
|
|
43 |
width: 180, // width of menu container, must be set or passed in to calculate widths of child menus |
|
|
44 |
maxHeight: 180, // max height of menu (if a drilldown: height does not include breadcrumb) |
|
|
45 |
positionOpts: { |
|
|
46 |
posX: 'left', |
|
|
47 |
posY: 'bottom', |
|
|
48 |
offsetX: 0, |
|
|
49 |
offsetY: 0, |
|
|
50 |
directionH: 'right', |
|
|
51 |
directionV: 'down', |
|
|
52 |
detectH: true, // do horizontal collision detection |
|
|
53 |
detectV: true, // do vertical collision detection |
|
|
54 |
linkToFront: false |
|
|
55 |
}, |
|
|
56 |
showSpeed: 200, // show/hide speed in milliseconds |
|
|
57 |
callerOnState: 'ui-state-active', // class to change the appearance of the link/button when the menu is showing |
|
|
58 |
loadingState: 'ui-state-loading', // class added to the link/button while the menu is created |
|
|
59 |
linkHover: 'ui-state-hover', // class for menu option hover state |
|
|
60 |
linkHoverSecondary: 'li-hover', // alternate class, may be used for multi-level menus |
|
|
61 |
// ----- multi-level menu defaults ----- |
|
|
62 |
crossSpeed: 200, // cross-fade speed for multi-level menus |
|
|
63 |
crumbDefaultText: 'Choose an option:', |
|
|
64 |
backLink: true, // in the ipod-style menu: instead of breadcrumbs, show only a 'back' link |
|
|
65 |
backLinkText: 'Back', |
|
|
66 |
flyOut: false, // multi-level menus are ipod-style by default; this parameter overrides to make a flyout instead |
|
|
67 |
flyOutOnState: 'ui-state-default', |
|
|
68 |
nextMenuLink: 'ui-icon-triangle-1-e', // class to style the link (specifically, a span within the link) used in the multi-level menu to show the next level |
|
|
69 |
topLinkText: 'All', |
|
|
70 |
nextCrumbLink: 'ui-icon-carat-1-e' |
|
|
71 |
}, options); |
|
|
72 |
|
|
|
73 |
var killAllMenus = function(){ |
|
|
74 |
$.each(allUIMenus, function(i){ |
|
|
75 |
if (allUIMenus[i].menuOpen) { allUIMenus[i].kill(); }; |
|
|
76 |
}); |
|
|
77 |
}; |
|
|
78 |
|
|
|
79 |
this.kill = function(){ |
|
|
80 |
caller |
|
|
81 |
.removeClass(options.loadingState) |
|
|
82 |
.removeClass('fg-menu-open') |
|
|
83 |
.removeClass(options.callerOnState); |
|
|
84 |
container.find('li').removeClass(options.linkHoverSecondary).find('a').removeClass(options.linkHover); |
|
|
85 |
if (options.flyOutOnState) { container.find('li a').removeClass(options.flyOutOnState); }; |
|
|
86 |
if (options.callerOnState) { caller.removeClass(options.callerOnState); }; |
|
|
87 |
if (container.is('.fg-menu-ipod')) { menu.resetDrilldownMenu(); }; |
|
|
88 |
if (container.is('.fg-menu-flyout')) { menu.resetFlyoutMenu(); }; |
|
|
89 |
container.parent().hide(); |
|
|
90 |
menu.menuOpen = false; |
|
|
91 |
$(document).unbind('click', killAllMenus); |
|
|
92 |
$(document).unbind('keydown'); |
|
|
93 |
}; |
|
|
94 |
|
|
|
95 |
this.showLoading = function(){ |
|
|
96 |
caller.addClass(options.loadingState); |
|
|
97 |
}; |
|
|
98 |
|
|
|
99 |
this.showMenu = function(){ |
|
|
100 |
killAllMenus(); |
|
|
101 |
if (!menu.menuExists) { menu.create() }; |
|
|
102 |
caller |
|
|
103 |
.addClass('fg-menu-open') |
|
|
104 |
.addClass(options.callerOnState); |
|
|
105 |
container.parent().show().click(function(){ menu.kill(); return false; }); |
|
|
106 |
container.hide().slideDown(options.showSpeed).find('.fg-menu:eq(0)'); |
|
|
107 |
menu.menuOpen = true; |
|
|
108 |
caller.removeClass(options.loadingState); |
|
|
109 |
$(document).click(killAllMenus); |
|
|
110 |
|
|
|
111 |
// assign key events |
|
|
112 |
$(document).keydown(function(event){ |
|
|
113 |
var e; |
|
|
114 |
if (event.which !="") { e = event.which; } |
|
|
115 |
else if (event.charCode != "") { e = event.charCode; } |
|
|
116 |
else if (event.keyCode != "") { e = event.keyCode; } |
|
|
117 |
|
|
|
118 |
var menuType = ($(event.target).parents('div').is('.fg-menu-flyout')) ? 'flyout' : 'ipod' ; |
|
|
119 |
|
|
|
120 |
switch(e) { |
|
|
121 |
case 37: // left arrow |
|
|
122 |
if (menuType == 'flyout') { |
|
|
123 |
$(event.target).trigger('mouseout'); |
|
|
124 |
if ($('.'+options.flyOutOnState).size() > 0) { $('.'+options.flyOutOnState).trigger('mouseover'); }; |
|
|
125 |
}; |
|
|
126 |
|
|
|
127 |
if (menuType == 'ipod') { |
|
|
128 |
$(event.target).trigger('mouseout'); |
|
|
129 |
if ($('.fg-menu-footer').find('a').size() > 0) { $('.fg-menu-footer').find('a').trigger('click'); }; |
|
|
130 |
if ($('.fg-menu-header').find('a').size() > 0) { $('.fg-menu-current-crumb').prev().find('a').trigger('click'); }; |
|
|
131 |
if ($('.fg-menu-current').prev().is('.fg-menu-indicator')) { |
|
|
132 |
$('.fg-menu-current').prev().trigger('mouseover'); |
|
|
133 |
}; |
|
|
134 |
}; |
|
|
135 |
return false; |
|
|
136 |
break; |
|
|
137 |
|
|
|
138 |
case 38: // up arrow |
|
|
139 |
if ($(event.target).is('.' + options.linkHover)) { |
|
|
140 |
var prevLink = $(event.target).parent().prev().find('a:eq(0)'); |
|
|
141 |
if (prevLink.size() > 0) { |
|
|
142 |
$(event.target).trigger('mouseout'); |
|
|
143 |
prevLink.trigger('mouseover'); |
|
|
144 |
}; |
|
|
145 |
} |
|
|
146 |
else { container.find('a:eq(0)').trigger('mouseover'); } |
|
|
147 |
return false; |
|
|
148 |
break; |
|
|
149 |
|
|
|
150 |
case 39: // right arrow |
|
|
151 |
if ($(event.target).is('.fg-menu-indicator')) { |
|
|
152 |
if (menuType == 'flyout') { |
|
|
153 |
$(event.target).next().find('a:eq(0)').trigger('mouseover'); |
|
|
154 |
} |
|
|
155 |
else if (menuType == 'ipod') { |
|
|
156 |
$(event.target).trigger('click'); |
|
|
157 |
setTimeout(function(){ |
|
|
158 |
$(event.target).next().find('a:eq(0)').trigger('mouseover'); |
|
|
159 |
}, options.crossSpeed); |
|
|
160 |
}; |
|
|
161 |
}; |
|
|
162 |
return false; |
|
|
163 |
break; |
|
|
164 |
|
|
|
165 |
case 40: // down arrow |
|
|
166 |
if ($(event.target).is('.' + options.linkHover)) { |
|
|
167 |
var nextLink = $(event.target).parent().next().find('a:eq(0)'); |
|
|
168 |
if (nextLink.size() > 0) { |
|
|
169 |
$(event.target).trigger('mouseout'); |
|
|
170 |
nextLink.trigger('mouseover'); |
|
|
171 |
}; |
|
|
172 |
} |
|
|
173 |
else { container.find('a:eq(0)').trigger('mouseover'); } |
|
|
174 |
return false; |
|
|
175 |
break; |
|
|
176 |
|
|
|
177 |
case 27: // escape |
|
|
178 |
killAllMenus(); |
|
|
179 |
break; |
|
|
180 |
|
|
|
181 |
case 13: // enter |
|
|
182 |
if ($(event.target).is('.fg-menu-indicator') && menuType == 'ipod') { |
|
|
183 |
$(event.target).trigger('click'); |
|
|
184 |
setTimeout(function(){ |
|
|
185 |
$(event.target).next().find('a:eq(0)').trigger('mouseover'); |
|
|
186 |
}, options.crossSpeed); |
|
|
187 |
}; |
|
|
188 |
break; |
|
|
189 |
}; |
|
|
190 |
}); |
|
|
191 |
}; |
|
|
192 |
|
|
|
193 |
this.create = function(){ |
|
|
194 |
container.css({ width: options.width }).appendTo('body').find('ul:first').not('.fg-menu-breadcrumb').addClass('fg-menu'); |
|
|
195 |
container.find('ul, li a').addClass('ui-corner-all'); |
|
|
196 |
|
|
|
197 |
// aria roles & attributes |
|
|
198 |
container.find('ul').attr('role', 'menu').eq(0).attr('aria-activedescendant','active-menuitem').attr('aria-labelledby', caller.attr('id')); |
|
|
199 |
container.find('li').attr('role', 'menuitem'); |
|
|
200 |
container.find('li:has(ul)').attr('aria-haspopup', 'true').find('ul').attr('aria-expanded', 'false'); |
|
|
201 |
container.find('a').attr('tabindex', '-1'); |
|
|
202 |
|
|
|
203 |
// when there are multiple levels of hierarchy, create flyout or drilldown menu |
|
|
204 |
if (container.find('ul').size() > 1) { |
|
|
205 |
if (options.flyOut) { menu.flyout(container, options); } |
|
|
206 |
else { menu.drilldown(container, options); } |
|
|
207 |
} |
|
|
208 |
else { |
|
|
209 |
container.find('a').click(function(){ |
|
|
210 |
menu.chooseItem(this); |
|
|
211 |
return false; |
|
|
212 |
}); |
|
|
213 |
}; |
|
|
214 |
|
|
|
215 |
if (options.linkHover) { |
|
|
216 |
var allLinks = container.find('.fg-menu li a'); |
|
|
217 |
allLinks.hover( |
|
|
218 |
function(){ |
|
|
219 |
var menuitem = $(this); |
|
|
220 |
$('.'+options.linkHover).removeClass(options.linkHover).blur().parent().removeAttr('id'); |
|
|
221 |
$(this).addClass(options.linkHover).focus().parent().attr('id','active-menuitem'); |
|
|
222 |
}, |
|
|
223 |
function(){ |
|
|
224 |
$(this).removeClass(options.linkHover).blur().parent().removeAttr('id'); |
|
|
225 |
} |
|
|
226 |
); |
|
|
227 |
}; |
|
|
228 |
|
|
|
229 |
if (options.linkHoverSecondary) { |
|
|
230 |
container.find('.fg-menu li').hover( |
|
|
231 |
function(){ |
|
|
232 |
$(this).siblings('li').removeClass(options.linkHoverSecondary); |
|
|
233 |
if (options.flyOutOnState) { $(this).siblings('li').find('a').removeClass(options.flyOutOnState); } |
|
|
234 |
$(this).addClass(options.linkHoverSecondary); |
|
|
235 |
}, |
|
|
236 |
function(){ $(this).removeClass(options.linkHoverSecondary); } |
|
|
237 |
); |
|
|
238 |
}; |
|
|
239 |
|
|
|
240 |
menu.setPosition(container, caller, options); |
|
|
241 |
menu.menuExists = true; |
|
|
242 |
}; |
|
|
243 |
|
|
|
244 |
this.chooseItem = function(item){ |
|
|
245 |
// RBA menu.kill(); |
|
|
246 |
// edit this for your own custom function/callback: |
|
|
247 |
$('#menuSelection').text($(item).text()); |
|
|
248 |
location.href = $(item).attr('href'); // RBA |
|
|
249 |
}; |
|
|
250 |
}; |
|
|
251 |
|
|
|
252 |
Menu.prototype.flyout = function(container, options) { |
|
|
253 |
var menu = this; |
|
|
254 |
|
|
|
255 |
this.resetFlyoutMenu = function(){ |
|
|
256 |
var allLists = container.find('ul ul'); |
|
|
257 |
allLists.removeClass('ui-widget-content').hide(); |
|
|
258 |
}; |
|
|
259 |
|
|
|
260 |
container.addClass('fg-menu-flyout').find('li:has(ul)').each(function(){ |
|
|
261 |
var linkWidth = container.width(); |
|
|
262 |
var showTimer, hideTimer; |
|
|
263 |
var allSubLists = $(this).find('ul'); |
|
|
264 |
|
|
|
265 |
allSubLists.css({ left: linkWidth, width: linkWidth }).hide(); |
|
|
266 |
|
|
|
267 |
$(this).find('a:eq(0)').addClass('fg-menu-indicator').html('<span>' + $(this).find('a:eq(0)').text() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>').hover( |
|
|
268 |
function(){ |
|
|
269 |
clearTimeout(hideTimer); |
|
|
270 |
var subList = $(this).next(); |
|
|
271 |
if (!fitVertical(subList, $(this).offset().top)) { subList.css({ top: 'auto', bottom: 0 }); }; |
|
|
272 |
if (!fitHorizontal(subList, $(this).offset().left + 100)) { subList.css({ left: 'auto', right: linkWidth, 'z-index': 999 }); }; |
|
|
273 |
showTimer = setTimeout(function(){ |
|
|
274 |
subList.addClass('ui-widget-content').show(options.showSpeed).attr('aria-expanded', 'true'); |
|
|
275 |
}, 300); |
|
|
276 |
}, |
|
|
277 |
function(){ |
|
|
278 |
clearTimeout(showTimer); |
|
|
279 |
var subList = $(this).next(); |
|
|
280 |
hideTimer = setTimeout(function(){ |
|
|
281 |
subList.removeClass('ui-widget-content').hide(options.showSpeed).attr('aria-expanded', 'false'); |
|
|
282 |
}, 400); |
|
|
283 |
} |
|
|
284 |
); |
|
|
285 |
|
|
|
286 |
$(this).find('ul a').hover( |
|
|
287 |
function(){ |
|
|
288 |
clearTimeout(hideTimer); |
|
|
289 |
if ($(this).parents('ul').prev().is('a.fg-menu-indicator')) { |
|
|
290 |
$(this).parents('ul').prev().addClass(options.flyOutOnState); |
|
|
291 |
} |
|
|
292 |
}, |
|
|
293 |
function(){ |
|
|
294 |
hideTimer = setTimeout(function(){ |
|
|
295 |
allSubLists.hide(options.showSpeed); |
|
|
296 |
container.find(options.flyOutOnState).removeClass(options.flyOutOnState); |
|
|
297 |
}, 500); |
|
|
298 |
} |
|
|
299 |
); |
|
|
300 |
}); |
|
|
301 |
|
|
|
302 |
container.find('a').click(function(){ |
|
|
303 |
menu.chooseItem(this); |
|
|
304 |
return false; |
|
|
305 |
}); |
|
|
306 |
}; |
|
|
307 |
|
|
|
308 |
|
|
|
309 |
Menu.prototype.drilldown = function(container, options) { |
|
|
310 |
var menu = this; |
|
|
311 |
var topList = container.find('.fg-menu'); |
|
|
312 |
var breadcrumb = $('<ul class="fg-menu-breadcrumb ui-widget-header ui-corner-all ui-helper-clearfix"></ul>'); |
|
|
313 |
var crumbDefaultHeader = $('<li class="fg-menu-breadcrumb-text">'+options.crumbDefaultText+'</li>'); |
|
|
314 |
var firstCrumbText = (options.backLink) ? options.backLinkText : options.topLinkText; |
|
|
315 |
var firstCrumbClass = (options.backLink) ? 'fg-menu-prev-list' : 'fg-menu-all-lists'; |
|
|
316 |
var firstCrumbLinkClass = (options.backLink) ? 'ui-state-default ui-corner-all' : ''; |
|
|
317 |
var firstCrumbIcon = (options.backLink) ? '<span class="ui-icon ui-icon-triangle-1-w"></span>' : ''; |
|
|
318 |
var firstCrumb = $('<li class="'+firstCrumbClass+'"><a href="#" class="'+firstCrumbLinkClass+'">'+firstCrumbIcon+firstCrumbText+'</a></li>'); |
|
|
319 |
|
|
|
320 |
container.addClass('fg-menu-ipod'); |
|
|
321 |
|
|
|
322 |
if (options.backLink) { breadcrumb.addClass('fg-menu-footer').appendTo(container).hide(); } |
|
|
323 |
else { breadcrumb.addClass('fg-menu-header').prependTo(container); }; |
|
|
324 |
breadcrumb.append(crumbDefaultHeader); |
|
|
325 |
|
|
|
326 |
var checkMenuHeight = function(el){ |
|
|
327 |
if (el.height() > options.maxHeight) { el.addClass('fg-menu-scroll') }; |
|
|
328 |
el.css({ height: options.maxHeight }); |
|
|
329 |
}; |
|
|
330 |
|
|
|
331 |
var resetChildMenu = function(el){ el.removeClass('fg-menu-scroll').removeClass('fg-menu-current').height('auto'); }; |
|
|
332 |
|
|
|
333 |
this.resetDrilldownMenu = function(){ |
|
|
334 |
$('.fg-menu-current').removeClass('fg-menu-current'); |
|
|
335 |
topList.animate({ left: 0 }, options.crossSpeed, function(){ |
|
|
336 |
$(this).find('ul').each(function(){ |
|
|
337 |
$(this).hide(); |
|
|
338 |
resetChildMenu($(this)); |
|
|
339 |
}); |
|
|
340 |
topList.addClass('fg-menu-current'); |
|
|
341 |
}); |
|
|
342 |
$('.fg-menu-all-lists').find('span').remove(); |
|
|
343 |
breadcrumb.empty().append(crumbDefaultHeader); |
|
|
344 |
$('.fg-menu-footer').empty().hide(); |
|
|
345 |
checkMenuHeight(topList); |
|
|
346 |
}; |
|
|
347 |
|
|
|
348 |
topList |
|
|
349 |
.addClass('fg-menu-content fg-menu-current ui-widget-content ui-helper-clearfix') |
|
|
350 |
.css({ width: container.width() }) |
|
|
351 |
.find('ul') |
|
|
352 |
.css({ width: container.width(), left: container.width() }) |
|
|
353 |
.addClass('ui-widget-content') |
|
|
354 |
.hide(); |
|
|
355 |
checkMenuHeight(topList); |
|
|
356 |
|
|
|
357 |
topList.find('a').each(function(){ |
|
|
358 |
// if the link opens a child menu: |
|
|
359 |
if ($(this).next().is('ul')) { |
|
|
360 |
$(this) |
|
|
361 |
.addClass('fg-menu-indicator') |
|
|
362 |
.each(function(){ $(this).html('<span>' + $(this).text() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>'); }) |
|
|
363 |
.click(function(){ // ----- show the next menu |
|
|
364 |
var nextList = $(this).next(); |
|
|
365 |
var parentUl = $(this).parents('ul:eq(0)'); |
|
|
366 |
var parentLeft = (parentUl.is('.fg-menu-content')) ? 0 : parseFloat(topList.css('left')); |
|
|
367 |
var nextLeftVal = Math.round(parentLeft - parseFloat(container.width())); |
|
|
368 |
var footer = $('.fg-menu-footer'); |
|
|
369 |
|
|
|
370 |
// show next menu |
|
|
371 |
resetChildMenu(parentUl); |
|
|
372 |
checkMenuHeight(nextList); |
|
|
373 |
topList.animate({ left: nextLeftVal }, options.crossSpeed); |
|
|
374 |
nextList.show().addClass('fg-menu-current').attr('aria-expanded', 'true'); |
|
|
375 |
|
|
|
376 |
var setPrevMenu = function(backlink){ |
|
|
377 |
var b = backlink; |
|
|
378 |
var c = $('.fg-menu-current'); |
|
|
379 |
var prevList = c.parents('ul:eq(0)'); |
|
|
380 |
c.hide().attr('aria-expanded', 'false'); |
|
|
381 |
resetChildMenu(c); |
|
|
382 |
checkMenuHeight(prevList); |
|
|
383 |
prevList.addClass('fg-menu-current').attr('aria-expanded', 'true'); |
|
|
384 |
if (prevList.hasClass('fg-menu-content')) { b.remove(); footer.hide(); }; |
|
|
385 |
}; |
|
|
386 |
|
|
|
387 |
// initialize "back" link |
|
|
388 |
if (options.backLink) { |
|
|
389 |
if (footer.find('a').size() == 0) { |
|
|
390 |
footer.show(); |
|
|
391 |
$('<a href="#"><span class="ui-icon ui-icon-triangle-1-w"></span> <span>Back</span></a>') |
|
|
392 |
.appendTo(footer) |
|
|
393 |
.click(function(){ // ----- show the previous menu |
|
|
394 |
var b = $(this); |
|
|
395 |
var prevLeftVal = parseFloat(topList.css('left')) + container.width(); |
|
|
396 |
topList.animate({ left: prevLeftVal }, options.crossSpeed, function(){ |
|
|
397 |
setPrevMenu(b); |
|
|
398 |
}); |
|
|
399 |
return false; |
|
|
400 |
}); |
|
|
401 |
} |
|
|
402 |
} |
|
|
403 |
// or initialize top breadcrumb |
|
|
404 |
else { |
|
|
405 |
if (breadcrumb.find('li').size() == 1){ |
|
|
406 |
breadcrumb.empty().append(firstCrumb); |
|
|
407 |
firstCrumb.find('a').click(function(){ |
|
|
408 |
menu.resetDrilldownMenu(); |
|
|
409 |
return false; |
|
|
410 |
}); |
|
|
411 |
} |
|
|
412 |
$('.fg-menu-current-crumb').removeClass('fg-menu-current-crumb'); |
|
|
413 |
var crumbText = $(this).find('span:eq(0)').text(); |
|
|
414 |
var newCrumb = $('<li class="fg-menu-current-crumb"><a href="javascript://" class="fg-menu-crumb">'+crumbText+'</a></li>'); |
|
|
415 |
newCrumb |
|
|
416 |
.appendTo(breadcrumb) |
|
|
417 |
.find('a').click(function(){ |
|
|
418 |
if ($(this).parent().is('.fg-menu-current-crumb')){ |
|
|
419 |
menu.chooseItem(this); |
|
|
420 |
} |
|
|
421 |
else { |
|
|
422 |
var newLeftVal = - ($('.fg-menu-current').parents('ul').size() - 1) * 180; |
|
|
423 |
topList.animate({ left: newLeftVal }, options.crossSpeed, function(){ |
|
|
424 |
setPrevMenu(); |
|
|
425 |
}); |
|
|
426 |
|
|
|
427 |
// make this the current crumb, delete all breadcrumbs after this one, and navigate to the relevant menu |
|
|
428 |
$(this).parent().addClass('fg-menu-current-crumb').find('span').remove(); |
|
|
429 |
$(this).parent().nextAll().remove(); |
|
|
430 |
}; |
|
|
431 |
return false; |
|
|
432 |
}); |
|
|
433 |
newCrumb.prev().append(' <span class="ui-icon '+options.nextCrumbLink+'"></span>'); |
|
|
434 |
}; |
|
|
435 |
return false; |
|
|
436 |
}); |
|
|
437 |
} |
|
|
438 |
// if the link is a leaf node (doesn't open a child menu) |
|
|
439 |
else { |
|
|
440 |
$(this).click(function(){ |
|
|
441 |
menu.chooseItem(this); |
|
|
442 |
return false; |
|
|
443 |
}); |
|
|
444 |
}; |
|
|
445 |
}); |
|
|
446 |
}; |
|
|
447 |
|
|
|
448 |
|
|
|
449 |
/* Menu.prototype.setPosition parameters (defaults noted with *): |
|
|
450 |
referrer = the link (or other element) used to show the overlaid object |
|
|
451 |
settings = can override the defaults: |
|
|
452 |
- posX/Y: where the top left corner of the object should be positioned in relation to its referrer. |
|
|
453 |
X: left*, center, right |
|
|
454 |
Y: top, center, bottom* |
|
|
455 |
- offsetX/Y: the number of pixels to be offset from the x or y position. Can be a positive or negative number. |
|
|
456 |
- directionH/V: where the entire menu should appear in relation to its referrer. |
|
|
457 |
Horizontal: left*, right |
|
|
458 |
Vertical: up, down* |
|
|
459 |
- detectH/V: detect the viewport horizontally / vertically |
|
|
460 |
- linkToFront: copy the menu link and place it on top of the menu (visual effect to make it look like it overlaps the object) */ |
|
|
461 |
|
|
|
462 |
Menu.prototype.setPosition = function(widget, caller, options) { |
|
|
463 |
var el = widget; |
|
|
464 |
var referrer = caller; |
|
|
465 |
var dims = { |
|
|
466 |
refX: referrer.offset().left, |
|
|
467 |
refY: referrer.offset().top, |
|
|
468 |
refW: referrer.getTotalWidth(), |
|
|
469 |
refH: referrer.getTotalHeight() |
|
|
470 |
}; |
|
|
471 |
var options = options; |
|
|
472 |
var xVal, yVal; |
|
|
473 |
|
|
|
474 |
var helper = $('<div class="positionHelper"></div>'); |
|
|
475 |
helper.css({ position: 'absolute', left: dims.refX, top: dims.refY, width: dims.refW, height: dims.refH }); |
|
|
476 |
el.wrap(helper); |
|
|
477 |
|
|
|
478 |
// get X pos |
|
|
479 |
switch(options.positionOpts.posX) { |
|
|
480 |
case 'left': xVal = 0; |
|
|
481 |
break; |
|
|
482 |
case 'center': xVal = dims.refW / 2; |
|
|
483 |
break; |
|
|
484 |
case 'right': xVal = dims.refW; |
|
|
485 |
break; |
|
|
486 |
}; |
|
|
487 |
|
|
|
488 |
// get Y pos |
|
|
489 |
switch(options.positionOpts.posY) { |
|
|
490 |
case 'top': yVal = 0; |
|
|
491 |
break; |
|
|
492 |
case 'center': yVal = dims.refH / 2; |
|
|
493 |
break; |
|
|
494 |
case 'bottom': yVal = dims.refH; |
|
|
495 |
break; |
|
|
496 |
}; |
|
|
497 |
|
|
|
498 |
// add the offsets (zero by default) |
|
|
499 |
xVal += options.positionOpts.offsetX; |
|
|
500 |
yVal += options.positionOpts.offsetY; |
|
|
501 |
|
|
|
502 |
// position the object vertically |
|
|
503 |
if (options.positionOpts.directionV == 'up') { |
|
|
504 |
el.css({ top: 'auto', bottom: yVal }); |
|
|
505 |
if (options.positionOpts.detectV && !fitVertical(el)) { |
|
|
506 |
el.css({ bottom: 'auto', top: yVal }); |
|
|
507 |
} |
|
|
508 |
} |
|
|
509 |
else { |
|
|
510 |
el.css({ bottom: 'auto', top: yVal }); |
|
|
511 |
if (options.positionOpts.detectV && !fitVertical(el)) { |
|
|
512 |
el.css({ top: 'auto', bottom: yVal }); |
|
|
513 |
} |
|
|
514 |
}; |
|
|
515 |
|
|
|
516 |
// and horizontally |
|
|
517 |
if (options.positionOpts.directionH == 'left') { |
|
|
518 |
el.css({ left: 'auto', right: xVal }); |
|
|
519 |
if (options.positionOpts.detectH && !fitHorizontal(el)) { |
|
|
520 |
el.css({ right: 'auto', left: xVal }); |
|
|
521 |
} |
|
|
522 |
} |
|
|
523 |
else { |
|
|
524 |
el.css({ right: 'auto', left: xVal }); |
|
|
525 |
if (options.positionOpts.detectH && !fitHorizontal(el)) { |
|
|
526 |
el.css({ left: 'auto', right: xVal }); |
|
|
527 |
} |
|
|
528 |
}; |
|
|
529 |
|
|
|
530 |
// if specified, clone the referring element and position it so that it appears on top of the menu |
|
|
531 |
if (options.positionOpts.linkToFront) { |
|
|
532 |
referrer.clone().addClass('linkClone').css({ |
|
|
533 |
position: 'absolute', |
|
|
534 |
top: 0, |
|
|
535 |
right: 'auto', |
|
|
536 |
bottom: 'auto', |
|
|
537 |
left: 0, |
|
|
538 |
width: referrer.width(), |
|
|
539 |
height: referrer.height() |
|
|
540 |
}).insertAfter(el); |
|
|
541 |
}; |
|
|
542 |
}; |
|
|
543 |
|
|
|
544 |
|
|
|
545 |
/* Utilities to sort and find viewport dimensions */ |
|
|
546 |
|
|
|
547 |
function sortBigToSmall(a, b) { return b - a; }; |
|
|
548 |
|
|
|
549 |
jQuery.fn.getTotalWidth = function(){ |
|
|
550 |
return $(this).width() + parseInt($(this).css('paddingRight')) + parseInt($(this).css('paddingLeft')) + parseInt($(this).css('borderRightWidth')) + parseInt($(this).css('borderLeftWidth')); |
|
|
551 |
}; |
|
|
552 |
|
|
|
553 |
jQuery.fn.getTotalHeight = function(){ |
|
|
554 |
return $(this).height() + parseInt($(this).css('paddingTop')) + parseInt($(this).css('paddingBottom')) + parseInt($(this).css('borderTopWidth')) + parseInt($(this).css('borderBottomWidth')); |
|
|
555 |
}; |
|
|
556 |
|
|
|
557 |
function getScrollTop(){ |
|
|
558 |
return self.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; |
|
|
559 |
}; |
|
|
560 |
|
|
|
561 |
function getScrollLeft(){ |
|
|
562 |
return self.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; |
|
|
563 |
}; |
|
|
564 |
|
|
|
565 |
function getWindowHeight(){ |
|
|
566 |
var de = document.documentElement; |
|
|
567 |
return self.innerHeight || (de && de.clientHeight) || document.body.clientHeight; |
|
|
568 |
}; |
|
|
569 |
|
|
|
570 |
function getWindowWidth(){ |
|
|
571 |
var de = document.documentElement; |
|
|
572 |
return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth; |
|
|
573 |
}; |
|
|
574 |
|
|
|
575 |
/* Utilities to test whether an element will fit in the viewport |
|
|
576 |
Parameters: |
|
|
577 |
el = element to position, required |
|
|
578 |
leftOffset / topOffset = optional parameter if the offset cannot be calculated (i.e., if the object is in the DOM but is set to display: 'none') */ |
|
|
579 |
|
|
|
580 |
function fitHorizontal(el, leftOffset){ |
|
|
581 |
var leftVal = parseInt(leftOffset) || $(el).offset().left; |
|
|
582 |
return (leftVal + $(el).width() <= getWindowWidth() + getScrollLeft() && leftVal - getScrollLeft() >= 0); |
|
|
583 |
}; |
|
|
584 |
|
|
|
585 |
function fitVertical(el, topOffset){ |
|
|
586 |
var topVal = parseInt(topOffset) || $(el).offset().top; |
|
|
587 |
return (topVal + $(el).height() <= getWindowHeight() + getScrollTop() && topVal - getScrollTop() >= 0); |
|
|
588 |
}; |
|
|
589 |
|
|
|
590 |
/*-------------------------------------------------------------------- |
|
|
591 |
* javascript method: "pxToEm" |
|
|
592 |
* by: |
|
|
593 |
Scott Jehl (scott@filamentgroup.com) |
|
|
594 |
Maggie Wachs (maggie@filamentgroup.com) |
|
|
595 |
http://www.filamentgroup.com |
|
|
596 |
* |
|
|
597 |
* Copyright (c) 2008 Filament Group |
|
|
598 |
* Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses. |
|
|
599 |
* |
|
|
600 |
* Description: Extends the native Number and String objects with pxToEm method. pxToEm converts a pixel value to ems depending on inherited font size. |
|
|
601 |
* Article: http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/ |
|
|
602 |
* Demo: http://www.filamentgroup.com/examples/pxToEm/ |
|
|
603 |
* |
|
|
604 |
* Options: |
|
|
605 |
scope: string or jQuery selector for font-size scoping |
|
|
606 |
reverse: Boolean, true reverses the conversion to em-px |
|
|
607 |
* Dependencies: jQuery library |
|
|
608 |
* Usage Example: myPixelValue.pxToEm(); or myPixelValue.pxToEm({'scope':'#navigation', reverse: true}); |
|
|
609 |
* |
|
|
610 |
* Version: 2.0, 08.01.2008 |
|
|
611 |
* Changelog: |
|
|
612 |
* 08.02.2007 initial Version 1.0 |
|
|
613 |
* 08.01.2008 - fixed font-size calculation for IE |
|
|
614 |
--------------------------------------------------------------------*/ |
|
|
615 |
|
|
|
616 |
Number.prototype.pxToEm = String.prototype.pxToEm = function(settings){ |
|
|
617 |
//set defaults |
|
|
618 |
settings = jQuery.extend({ |
|
|
619 |
scope: 'body', |
|
|
620 |
reverse: false |
|
|
621 |
}, settings); |
|
|
622 |
|
|
|
623 |
var pxVal = (this == '') ? 0 : parseFloat(this); |
|
|
624 |
var scopeVal; |
|
|
625 |
var getWindowWidth = function(){ |
|
|
626 |
var de = document.documentElement; |
|
|
627 |
return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth; |
|
|
628 |
}; |
|
|
629 |
|
|
|
630 |
/* When a percentage-based font-size is set on the body, IE returns that percent of the window width as the font-size. |
|
|
631 |
For example, if the body font-size is 62.5% and the window width is 1000px, IE will return 625px as the font-size. |
|
|
632 |
When this happens, we calculate the correct body font-size (%) and multiply it by 16 (the standard browser font size) |
|
|
633 |
to get an accurate em value. */ |
|
|
634 |
|
|
|
635 |
if (settings.scope == 'body' && $.browser.msie && (parseFloat($('body').css('font-size')) / getWindowWidth()).toFixed(1) > 0.0) { |
|
|
636 |
var calcFontSize = function(){ |
|
|
637 |
return (parseFloat($('body').css('font-size'))/getWindowWidth()).toFixed(3) * 16; |
|
|
638 |
}; |
|
|
639 |
scopeVal = calcFontSize(); |
|
|
640 |
} |
|
|
641 |
else { scopeVal = parseFloat(jQuery(settings.scope).css("font-size")); }; |
|
|
642 |
|
|
|
643 |
var result = (settings.reverse == true) ? (pxVal * scopeVal).toFixed(2) + 'px' : (pxVal / scopeVal).toFixed(2) + 'em'; |
|
|
644 |
return result; |
|
|
645 |
}; |