jquery.menu.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. /**
  2. * menu - jQuery EasyUI
  3. *
  4. * Copyright (c) 2009-2013 www.jeasyui.com. All rights reserved.
  5. *
  6. * Licensed under the GPL or commercial licenses
  7. * To use it on other terms please contact us: info@jeasyui.com
  8. * http://www.gnu.org/licenses/gpl.txt
  9. * http://www.jeasyui.com/license_commercial.php
  10. */
  11. (function($){
  12. /**
  13. * initialize the target menu, the function can be invoked only once
  14. */
  15. function init(target){
  16. $(target).appendTo('body');
  17. $(target).addClass('menu-top'); // the top menu
  18. $(document).unbind('.menu').bind('mousedown.menu', function(e){
  19. var allMenu = $('body>div.menu:visible');
  20. var m = $(e.target).closest('div.menu', allMenu);
  21. if (m.length){return}
  22. $('body>div.menu-top:visible').menu('hide');
  23. });
  24. var menus = splitMenu($(target));
  25. for(var i=0; i<menus.length; i++){
  26. createMenu(menus[i]);
  27. }
  28. function splitMenu(menu){
  29. var menus = [];
  30. menu.addClass('menu');
  31. menus.push(menu);
  32. if (!menu.hasClass('menu-content')){
  33. menu.children('div').each(function(){
  34. var submenu = $(this).children('div');
  35. if (submenu.length){
  36. submenu.insertAfter(target);
  37. this.submenu = submenu; // point to the sub menu
  38. var mm = splitMenu(submenu);
  39. menus = menus.concat(mm);
  40. }
  41. });
  42. }
  43. return menus;
  44. }
  45. function createMenu(menu){
  46. var width = $.parser.parseOptions(menu[0], ['width']).width;
  47. if (menu.hasClass('menu-content')){
  48. menu[0].originalWidth = width || menu._outerWidth();
  49. } else {
  50. menu[0].originalWidth = width || 0;
  51. menu.children('div').each(function(){
  52. var item = $(this);
  53. if (item.hasClass('menu-sep')){
  54. // item.html('&nbsp;');
  55. } else {
  56. var itemOpts = $.extend({}, $.parser.parseOptions(this,['name','iconCls','href']), {
  57. disabled: (item.attr('disabled') ? true : undefined)
  58. });
  59. item[0].itemName = itemOpts.name || '';
  60. item[0].itemHref = itemOpts.href || '';
  61. // item.attr('name',itemOpts.name || '').attr('href',itemOpts.href || '');
  62. var text = item.addClass('menu-item').html();
  63. item.empty().append($('<div class="menu-text"></div>').html(text));
  64. if (itemOpts.iconCls){
  65. $('<div class="menu-icon"></div>').addClass(itemOpts.iconCls).appendTo(item);
  66. }
  67. if (itemOpts.disabled){
  68. setDisabled(target, item[0], true);
  69. }
  70. if (item[0].submenu){
  71. $('<div class="menu-rightarrow"></div>').appendTo(item); // has sub menu
  72. }
  73. bindMenuItemEvent(target, item);
  74. }
  75. });
  76. $('<div class="menu-line"></div>').prependTo(menu);
  77. }
  78. setMenuWidth(target, menu);
  79. menu.hide();
  80. bindMenuEvent(target, menu);
  81. }
  82. }
  83. function setMenuWidth(target, menu){
  84. var opts = $.data(target, 'menu').options;
  85. var d = menu.css('display');
  86. menu.css({
  87. display: 'block',
  88. left:-10000
  89. });
  90. // menu.find('div.menu-item')._outerHeight(22);
  91. var width = 0;
  92. menu.find('div.menu-text').each(function(){
  93. if (width < $(this)._outerWidth()){
  94. width = $(this)._outerWidth();
  95. }
  96. $(this).closest('div.menu-item')._outerHeight($(this)._outerHeight()+2);
  97. });
  98. width += 65;
  99. menu._outerWidth(Math.max((menu[0].originalWidth || 0), width, opts.minWidth));
  100. menu.css('display', d);
  101. }
  102. /**
  103. * bind menu event
  104. */
  105. function bindMenuEvent(target, menu){
  106. var state = $.data(target, 'menu');
  107. menu.unbind('.menu').bind('mouseenter.menu', function(){
  108. if (state.timer){
  109. clearTimeout(state.timer);
  110. state.timer = null;
  111. }
  112. }).bind('mouseleave.menu', function(){
  113. state.timer = setTimeout(function(){
  114. hideAll(target);
  115. }, 100);
  116. });
  117. }
  118. /**
  119. * bind menu item event
  120. */
  121. function bindMenuItemEvent(target, item){
  122. item.unbind('.menu');
  123. item.bind('click.menu', function(){
  124. if ($(this).hasClass('menu-item-disabled')){
  125. return;
  126. }
  127. // only the sub menu clicked can hide all menus
  128. if (!this.submenu){
  129. hideAll(target);
  130. var href = $(this).attr('href');
  131. if (href){
  132. location.href = href;
  133. }
  134. }
  135. var item = $(target).menu('getItem', this);
  136. $.data(target, 'menu').options.onClick.call(target, item);
  137. }).bind('mouseenter.menu', function(e){
  138. // hide other menu
  139. item.siblings().each(function(){
  140. if (this.submenu){
  141. hideMenu(this.submenu);
  142. }
  143. $(this).removeClass('menu-active');
  144. });
  145. // show this menu
  146. item.addClass('menu-active');
  147. if ($(this).hasClass('menu-item-disabled')){
  148. item.addClass('menu-active-disabled');
  149. return;
  150. }
  151. var submenu = item[0].submenu;
  152. if (submenu){
  153. $(target).menu('show', {
  154. menu: submenu,
  155. parent: item
  156. });
  157. }
  158. }).bind('mouseleave.menu', function(e){
  159. item.removeClass('menu-active menu-active-disabled');
  160. var submenu = item[0].submenu;
  161. if (submenu){
  162. if (e.pageX>=parseInt(submenu.css('left'))){
  163. item.addClass('menu-active');
  164. } else {
  165. hideMenu(submenu);
  166. }
  167. } else {
  168. item.removeClass('menu-active');
  169. }
  170. });
  171. }
  172. /**
  173. * hide top menu and it's all sub menus
  174. */
  175. function hideAll(target){
  176. var state = $.data(target, 'menu');
  177. if (state){
  178. if ($(target).is(':visible')){
  179. hideMenu($(target));
  180. state.options.onHide.call(target);
  181. }
  182. }
  183. return false;
  184. }
  185. /**
  186. * show the menu, the 'param' object has one or more properties:
  187. * left: the left position to display
  188. * top: the top position to display
  189. * menu: the menu to display, if not defined, the 'target menu' is used
  190. * parent: the parent menu item to align to
  191. * alignTo: the element object to align to
  192. */
  193. function showMenu(target, param){
  194. var left,top;
  195. param = param || {};
  196. var menu = $(param.menu || target);
  197. if (menu.hasClass('menu-top')){
  198. var opts = $.data(target, 'menu').options;
  199. $.extend(opts, param);
  200. left = opts.left;
  201. top = opts.top;
  202. if (opts.alignTo){
  203. var at = $(opts.alignTo);
  204. left = at.offset().left;
  205. top = at.offset().top + at._outerHeight();
  206. }
  207. // if (param.left != undefined){left = param.left}
  208. // if (param.top != undefined){top = param.top}
  209. if (left + menu.outerWidth() > $(window)._outerWidth() + $(document)._scrollLeft()){
  210. left = $(window)._outerWidth() + $(document).scrollLeft() - menu.outerWidth() - 5;
  211. }
  212. if (top + menu.outerHeight() > $(window)._outerHeight() + $(document).scrollTop()){
  213. top -= menu.outerHeight();
  214. }
  215. } else {
  216. var parent = param.parent; // the parent menu item
  217. left = parent.offset().left + parent.outerWidth() - 2;
  218. if (left + menu.outerWidth() + 5 > $(window)._outerWidth() + $(document).scrollLeft()){
  219. left = parent.offset().left - menu.outerWidth() + 2;
  220. }
  221. var top = parent.offset().top - 3;
  222. if (top + menu.outerHeight() > $(window)._outerHeight() + $(document).scrollTop()){
  223. top = $(window)._outerHeight() + $(document).scrollTop() - menu.outerHeight() - 5;
  224. }
  225. }
  226. menu.css({left:left,top:top});
  227. menu.show(0, function(){
  228. if (!menu[0].shadow){
  229. menu[0].shadow = $('<div class="menu-shadow"></div>').insertAfter(menu);
  230. }
  231. menu[0].shadow.css({
  232. display:'block',
  233. zIndex:$.fn.menu.defaults.zIndex++,
  234. left:menu.css('left'),
  235. top:menu.css('top'),
  236. width:menu.outerWidth(),
  237. height:menu.outerHeight()
  238. });
  239. menu.css('z-index', $.fn.menu.defaults.zIndex++);
  240. if (menu.hasClass('menu-top')){
  241. $.data(menu[0], 'menu').options.onShow.call(menu[0]);
  242. }
  243. });
  244. }
  245. function hideMenu(menu){
  246. if (!menu) return;
  247. hideit(menu);
  248. menu.find('div.menu-item').each(function(){
  249. if (this.submenu){
  250. hideMenu(this.submenu);
  251. }
  252. $(this).removeClass('menu-active');
  253. });
  254. function hideit(m){
  255. m.stop(true,true);
  256. if (m[0].shadow){
  257. m[0].shadow.hide();
  258. }
  259. m.hide();
  260. }
  261. }
  262. function findItem(target, text){
  263. var result = null;
  264. var tmp = $('<div></div>');
  265. function find(menu){
  266. menu.children('div.menu-item').each(function(){
  267. var item = $(target).menu('getItem', this);
  268. var s = tmp.empty().html(item.text).text();
  269. if (text == $.trim(s)) {
  270. result = item;
  271. } else if (this.submenu && !result){
  272. find(this.submenu);
  273. }
  274. });
  275. }
  276. find($(target));
  277. tmp.remove();
  278. return result;
  279. }
  280. function setDisabled(target, itemEl, disabled){
  281. var t = $(itemEl);
  282. if (disabled){
  283. t.addClass('menu-item-disabled');
  284. if (itemEl.onclick){
  285. itemEl.onclick1 = itemEl.onclick;
  286. itemEl.onclick = null;
  287. }
  288. } else {
  289. t.removeClass('menu-item-disabled');
  290. if (itemEl.onclick1){
  291. itemEl.onclick = itemEl.onclick1;
  292. itemEl.onclick1 = null;
  293. }
  294. }
  295. }
  296. function appendItem(target, param){
  297. var menu = $(target);
  298. if (param.parent){
  299. if (!param.parent.submenu){
  300. var submenu = $('<div class="menu"><div class="menu-line"></div></div>').appendTo('body');
  301. submenu.hide();
  302. param.parent.submenu = submenu;
  303. $('<div class="menu-rightarrow"></div>').appendTo(param.parent);
  304. }
  305. menu = param.parent.submenu;
  306. }
  307. var item = $('<div class="menu-item"></div>').appendTo(menu);
  308. $('<div class="menu-text"></div>').html(param.text).appendTo(item);
  309. if (param.iconCls) $('<div class="menu-icon"></div>').addClass(param.iconCls).appendTo(item);
  310. if (param.id) item.attr('id', param.id);
  311. // if (param.href) item.attr('href', param.href);
  312. // if (param.name) item.attr('name', param.name);
  313. if (param.name){item[0].itemName = param.name}
  314. if (param.href){item[0].itemHref = param.href}
  315. if (param.onclick){
  316. if (typeof param.onclick == 'string'){
  317. item.attr('onclick', param.onclick);
  318. } else {
  319. item[0].onclick = eval(param.onclick);
  320. }
  321. }
  322. if (param.handler) item[0].onclick = eval(param.handler);
  323. bindMenuItemEvent(target, item);
  324. if (param.disabled){
  325. setDisabled(target, item[0], true);
  326. }
  327. bindMenuEvent(target, menu);
  328. setMenuWidth(target, menu);
  329. }
  330. function removeItem(target, itemEl){
  331. function removeit(el){
  332. if (el.submenu){
  333. el.submenu.children('div.menu-item').each(function(){
  334. removeit(this);
  335. });
  336. var shadow = el.submenu[0].shadow;
  337. if (shadow) shadow.remove();
  338. el.submenu.remove();
  339. }
  340. $(el).remove();
  341. }
  342. removeit(itemEl);
  343. }
  344. function destroyMenu(target){
  345. $(target).children('div.menu-item').each(function(){
  346. removeItem(target, this);
  347. });
  348. if (target.shadow) target.shadow.remove();
  349. $(target).remove();
  350. }
  351. $.fn.menu = function(options, param){
  352. if (typeof options == 'string'){
  353. return $.fn.menu.methods[options](this, param);
  354. }
  355. options = options || {};
  356. return this.each(function(){
  357. var state = $.data(this, 'menu');
  358. if (state){
  359. $.extend(state.options, options);
  360. } else {
  361. state = $.data(this, 'menu', {
  362. options: $.extend({}, $.fn.menu.defaults, $.fn.menu.parseOptions(this), options)
  363. });
  364. init(this);
  365. }
  366. $(this).css({
  367. left: state.options.left,
  368. top: state.options.top
  369. });
  370. });
  371. };
  372. $.fn.menu.methods = {
  373. options: function(jq){
  374. return $.data(jq[0], 'menu').options;
  375. },
  376. show: function(jq, pos){
  377. return jq.each(function(){
  378. showMenu(this, pos);
  379. });
  380. },
  381. hide: function(jq){
  382. return jq.each(function(){
  383. hideAll(this);
  384. });
  385. },
  386. destroy: function(jq){
  387. return jq.each(function(){
  388. destroyMenu(this);
  389. });
  390. },
  391. /**
  392. * set the menu item text
  393. * param: {
  394. * target: DOM object, indicate the menu item
  395. * text: string, the new text
  396. * }
  397. */
  398. setText: function(jq, param){
  399. return jq.each(function(){
  400. $(param.target).children('div.menu-text').html(param.text);
  401. });
  402. },
  403. /**
  404. * set the menu icon class
  405. * param: {
  406. * target: DOM object, indicate the menu item
  407. * iconCls: the menu item icon class
  408. * }
  409. */
  410. setIcon: function(jq, param){
  411. return jq.each(function(){
  412. var item = $(this).menu('getItem', param.target);
  413. if (item.iconCls){
  414. $(item.target).children('div.menu-icon').removeClass(item.iconCls).addClass(param.iconCls);
  415. } else {
  416. $('<div class="menu-icon"></div>').addClass(param.iconCls).appendTo(param.target);
  417. }
  418. });
  419. },
  420. /**
  421. * get the menu item data that contains the following property:
  422. * {
  423. * target: DOM object, the menu item
  424. * id: the menu id
  425. * text: the menu item text
  426. * iconCls: the icon class
  427. * href: a remote address to redirect to
  428. * onclick: a function to be called when the item is clicked
  429. * }
  430. */
  431. getItem: function(jq, itemEl){
  432. var t = $(itemEl);
  433. var item = {
  434. target: itemEl,
  435. id: t.attr('id'),
  436. text: $.trim(t.children('div.menu-text').html()),
  437. disabled: t.hasClass('menu-item-disabled'),
  438. // href: t.attr('href'),
  439. // name: t.attr('name'),
  440. name: itemEl.itemName,
  441. href: itemEl.itemHref,
  442. onclick: itemEl.onclick
  443. }
  444. var icon = t.children('div.menu-icon');
  445. if (icon.length){
  446. var cc = [];
  447. var aa = icon.attr('class').split(' ');
  448. for(var i=0; i<aa.length; i++){
  449. if (aa[i] != 'menu-icon'){
  450. cc.push(aa[i]);
  451. }
  452. }
  453. item.iconCls = cc.join(' ');
  454. }
  455. return item;
  456. },
  457. findItem: function(jq, text){
  458. return findItem(jq[0], text);
  459. },
  460. /**
  461. * append menu item, the param contains following properties:
  462. * parent,id,text,iconCls,href,onclick
  463. * when parent property is assigned, append menu item to it
  464. */
  465. appendItem: function(jq, param){
  466. return jq.each(function(){
  467. appendItem(this, param);
  468. });
  469. },
  470. removeItem: function(jq, itemEl){
  471. return jq.each(function(){
  472. removeItem(this, itemEl);
  473. });
  474. },
  475. enableItem: function(jq, itemEl){
  476. return jq.each(function(){
  477. setDisabled(this, itemEl, false);
  478. });
  479. },
  480. disableItem: function(jq, itemEl){
  481. return jq.each(function(){
  482. setDisabled(this, itemEl, true);
  483. });
  484. }
  485. };
  486. $.fn.menu.parseOptions = function(target){
  487. return $.extend({}, $.parser.parseOptions(target, ['left','top',{minWidth:'number'}]));
  488. };
  489. $.fn.menu.defaults = {
  490. zIndex:110000,
  491. left: 0,
  492. top: 0,
  493. minWidth: 120,
  494. onShow: function(){},
  495. onHide: function(){},
  496. onClick: function(item){}
  497. };
  498. })(jQuery);