fg.menu.js 23 KB

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