jquery.menu.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. /**
  2. * EasyUI for jQuery 1.5.5.6
  3. *
  4. * Copyright (c) 2009-2018 www.jeasyui.com. All rights reserved.
  5. *
  6. * Licensed under the freeware license: http://www.jeasyui.com/license_freeware.php
  7. * To use it on other terms please contact us: info@jeasyui.com
  8. *
  9. */
  10. /**
  11. * menu - EasyUI for jQuery
  12. *
  13. */
  14. (function($){
  15. $(function(){
  16. $(document).unbind('.menu').bind('mousedown.menu', function(e){
  17. var m = $(e.target).closest('div.menu,div.combo-p');
  18. if (m.length){return}
  19. $('body>div.menu-top:visible').not('.menu-inline').menu('hide');
  20. hideMenu($('body>div.menu:visible').not('.menu-inline'));
  21. });
  22. });
  23. /**
  24. * initialize the target menu, the function can be invoked only once
  25. */
  26. function init(target){
  27. var opts = $.data(target, 'menu').options;
  28. $(target).addClass('menu-top'); // the top menu
  29. opts.inline ? $(target).addClass('menu-inline') : $(target).appendTo('body');
  30. $(target).bind('_resize', function(e, force){
  31. if ($(this).hasClass('easyui-fluid') || force){
  32. $(target).menu('resize', target);
  33. }
  34. return false;
  35. });
  36. var menus = splitMenu($(target));
  37. for(var i=0; i<menus.length; i++){
  38. createMenu(target, menus[i]);
  39. }
  40. function splitMenu(menu){
  41. var menus = [];
  42. menu.addClass('menu');
  43. menus.push(menu);
  44. if (!menu.hasClass('menu-content')){
  45. menu.children('div').each(function(){
  46. var submenu = $(this).children('div');
  47. if (submenu.length){
  48. submenu.appendTo('body');
  49. this.submenu = submenu; // point to the sub menu
  50. var mm = splitMenu(submenu);
  51. menus = menus.concat(mm);
  52. }
  53. });
  54. }
  55. return menus;
  56. }
  57. }
  58. function createMenu(target, div){
  59. var menu = $(div).addClass('menu');
  60. if (!menu.data('menu')){
  61. menu.data('menu', {
  62. options: $.parser.parseOptions(menu[0], ['width','height'])
  63. });
  64. }
  65. if (!menu.hasClass('menu-content')){
  66. menu.children('div').each(function(){
  67. createItem(target, this);
  68. });
  69. $('<div class="menu-line"></div>').prependTo(menu);
  70. }
  71. setMenuSize(target, menu);
  72. if (!menu.hasClass('menu-inline')){
  73. menu.hide();
  74. }
  75. bindMenuEvent(target, menu);
  76. }
  77. /**
  78. * create the menu item
  79. */
  80. function createItem(target, div, options){
  81. var item = $(div);
  82. var itemOpts = $.extend({}, $.parser.parseOptions(item[0], ['id','name','iconCls','href',{separator:'boolean'}]), {
  83. disabled: (item.attr('disabled') ? true : undefined),
  84. text: $.trim(item.html()),
  85. onclick: item[0].onclick
  86. }, options||{});
  87. itemOpts.onclick = itemOpts.onclick || itemOpts.handler || null;
  88. item.data('menuitem', {
  89. options: itemOpts
  90. });
  91. if (itemOpts.separator){
  92. item.addClass('menu-sep');
  93. }
  94. if (!item.hasClass('menu-sep')){
  95. item.addClass('menu-item');
  96. item.empty().append($('<div class="menu-text"></div>').html(itemOpts.text));
  97. if (itemOpts.iconCls){
  98. $('<div class="menu-icon"></div>').addClass(itemOpts.iconCls).appendTo(item);
  99. }
  100. if (itemOpts.id){
  101. item.attr('id', itemOpts.id);
  102. }
  103. if (itemOpts.onclick){
  104. if (typeof itemOpts.onclick == 'string'){
  105. item.attr('onclick', itemOpts.onclick);
  106. } else {
  107. item[0].onclick = eval(itemOpts.onclick);
  108. }
  109. }
  110. if (itemOpts.disabled){
  111. setDisabled(target, item[0], true);
  112. }
  113. if (item[0].submenu){
  114. $('<div class="menu-rightarrow"></div>').appendTo(item); // has sub menu
  115. }
  116. }
  117. }
  118. function setMenuSize(target, menu){
  119. var opts = $.data(target, 'menu').options;
  120. var style = menu.attr('style') || '';
  121. var isVisible = menu.is(':visible');
  122. menu.css({
  123. display: 'block',
  124. left: -10000,
  125. height: 'auto',
  126. overflow: 'hidden'
  127. });
  128. menu.find('.menu-item').each(function(){
  129. $(this)._outerHeight(opts.itemHeight);
  130. $(this).find('.menu-text').css({
  131. height: (opts.itemHeight-2)+'px',
  132. lineHeight: (opts.itemHeight-2)+'px'
  133. });
  134. });
  135. menu.removeClass('menu-noline').addClass(opts.noline?'menu-noline':'');
  136. var mopts = menu.data('menu').options;
  137. var width = mopts.width;
  138. var height = mopts.height;
  139. if (isNaN(parseInt(width))){
  140. width = 0;
  141. menu.find('div.menu-text').each(function(){
  142. if (width < $(this).outerWidth()){
  143. width = $(this).outerWidth();
  144. }
  145. });
  146. // width += 40;
  147. width = width ? width+40 : '';
  148. }
  149. var autoHeight = menu.outerHeight();
  150. if (isNaN(parseInt(height))){
  151. height = autoHeight;
  152. if (menu.hasClass('menu-top') && opts.alignTo){
  153. var at = $(opts.alignTo);
  154. var h1 = at.offset().top - $(document).scrollTop();
  155. var h2 = $(window)._outerHeight() + $(document).scrollTop() - at.offset().top - at._outerHeight();
  156. height = Math.min(height, Math.max(h1, h2));
  157. } else if (height > $(window)._outerHeight()){
  158. height = $(window).height();
  159. }
  160. }
  161. menu.attr('style', style); // restore the original style
  162. menu.show();
  163. menu._size($.extend({}, mopts, {
  164. width: width,
  165. height: height,
  166. minWidth: mopts.minWidth || opts.minWidth,
  167. maxWidth: mopts.maxWidth || opts.maxWidth
  168. }));
  169. menu.find('.easyui-fluid').triggerHandler('_resize', [true]);
  170. menu.css('overflow', menu.outerHeight() < autoHeight ? 'auto' : 'hidden');
  171. menu.children('div.menu-line')._outerHeight(autoHeight-2);
  172. if (!isVisible){
  173. menu.hide();
  174. }
  175. }
  176. /**
  177. * bind menu event
  178. */
  179. function bindMenuEvent(target, menu){
  180. var state = $.data(target, 'menu');
  181. var opts = state.options;
  182. menu.unbind('.menu');
  183. for(var event in opts.events){
  184. menu.bind(event+'.menu', {target:target}, opts.events[event]);
  185. }
  186. }
  187. function mouseenterHandler(e){
  188. var target = e.data.target;
  189. var state = $.data(target, 'menu');
  190. if (state.timer){
  191. clearTimeout(state.timer);
  192. state.timer = null;
  193. }
  194. }
  195. function mouseleaveHandler(e){
  196. var target = e.data.target;
  197. var state = $.data(target, 'menu');
  198. if (state.options.hideOnUnhover){
  199. state.timer = setTimeout(function(){
  200. hideAll(target, $(target).hasClass('menu-inline'));
  201. }, state.options.duration);
  202. }
  203. }
  204. function mouseoverHandler(e){
  205. var target = e.data.target;
  206. var item = $(e.target).closest('.menu-item');
  207. if (item.length){
  208. item.siblings().each(function(){
  209. if (this.submenu){
  210. hideMenu(this.submenu);
  211. }
  212. $(this).removeClass('menu-active');
  213. });
  214. // show this menu
  215. item.addClass('menu-active');
  216. if (item.hasClass('menu-item-disabled')){
  217. item.addClass('menu-active-disabled');
  218. return;
  219. }
  220. var submenu = item[0].submenu;
  221. if (submenu){
  222. $(target).menu('show', {
  223. menu: submenu,
  224. parent: item
  225. });
  226. }
  227. }
  228. }
  229. function mouseoutHandler(e){
  230. var item = $(e.target).closest('.menu-item');
  231. if (item.length){
  232. item.removeClass('menu-active menu-active-disabled');
  233. var submenu = item[0].submenu;
  234. if (submenu){
  235. if (e.pageX>=parseInt(submenu.css('left'))){
  236. item.addClass('menu-active');
  237. } else {
  238. hideMenu(submenu);
  239. }
  240. } else {
  241. item.removeClass('menu-active');
  242. }
  243. }
  244. }
  245. function clickHandler(e){
  246. var target = e.data.target;
  247. var item = $(e.target).closest('.menu-item');
  248. if (item.length){
  249. var opts = $(target).data('menu').options;
  250. var itemOpts = item.data('menuitem').options;
  251. if (itemOpts.disabled){return;}
  252. if (!item[0].submenu){
  253. hideAll(target, opts.inline);
  254. if (itemOpts.href){
  255. location.href = itemOpts.href;
  256. }
  257. }
  258. item.trigger('mouseenter');
  259. opts.onClick.call(target, $(target).menu('getItem', item[0]));
  260. }
  261. }
  262. /**
  263. * hide top menu and it's all sub menus
  264. */
  265. function hideAll(target, inline){
  266. var state = $.data(target, 'menu');
  267. if (state){
  268. if ($(target).is(':visible')){
  269. hideMenu($(target));
  270. if (inline){
  271. $(target).show();
  272. } else {
  273. state.options.onHide.call(target);
  274. }
  275. }
  276. }
  277. return false;
  278. }
  279. /**
  280. * show the menu, the 'param' object has one or more properties:
  281. * left: the left position to display
  282. * top: the top position to display
  283. * menu: the menu to display, if not defined, the 'target menu' is used
  284. * parent: the parent menu item to align to
  285. * alignTo: the element object to align to
  286. */
  287. function showMenu(target, param){
  288. param = param || {};
  289. var left,top;
  290. var opts = $.data(target, 'menu').options;
  291. var menu = $(param.menu || target);
  292. $(target).menu('resize', menu[0]);
  293. if (menu.hasClass('menu-top')){
  294. $.extend(opts, param);
  295. left = opts.left;
  296. top = opts.top;
  297. if (opts.alignTo){
  298. var at = $(opts.alignTo);
  299. left = at.offset().left;
  300. top = at.offset().top + at._outerHeight();
  301. if (opts.align == 'right'){
  302. left += at.outerWidth() - menu.outerWidth();
  303. }
  304. }
  305. if (left + menu.outerWidth() > $(window)._outerWidth() + $(document)._scrollLeft()){
  306. left = $(window)._outerWidth() + $(document).scrollLeft() - menu.outerWidth() - 5;
  307. }
  308. if (left < 0){left = 0;}
  309. top = _fixTop(top, opts.alignTo);
  310. } else {
  311. var parent = param.parent; // the parent menu item
  312. left = parent.offset().left + parent.outerWidth() - 2;
  313. if (left + menu.outerWidth() + 5 > $(window)._outerWidth() + $(document).scrollLeft()){
  314. left = parent.offset().left - menu.outerWidth() + 2;
  315. }
  316. top = _fixTop(parent.offset().top - 3);
  317. }
  318. function _fixTop(top, alignTo){
  319. if (top + menu.outerHeight() > $(window)._outerHeight() + $(document).scrollTop()){
  320. if (alignTo){
  321. top = $(alignTo).offset().top - menu._outerHeight();
  322. } else {
  323. top = $(window)._outerHeight() + $(document).scrollTop() - menu.outerHeight();
  324. }
  325. }
  326. if (top < 0){top = 0;}
  327. return top;
  328. }
  329. menu.css(opts.position.call(target, menu[0], left, top));
  330. menu.show(0, function(){
  331. if (!menu[0].shadow){
  332. menu[0].shadow = $('<div class="menu-shadow"></div>').insertAfter(menu);
  333. }
  334. menu[0].shadow.css({
  335. display:(menu.hasClass('menu-inline')?'none':'block'),
  336. zIndex:$.fn.menu.defaults.zIndex++,
  337. left:menu.css('left'),
  338. top:menu.css('top'),
  339. width:menu.outerWidth(),
  340. height:menu.outerHeight()
  341. });
  342. menu.css('z-index', $.fn.menu.defaults.zIndex++);
  343. if (menu.hasClass('menu-top')){
  344. opts.onShow.call(target);
  345. }
  346. });
  347. }
  348. function hideMenu(menu){
  349. if (menu && menu.length){
  350. hideit(menu);
  351. menu.find('div.menu-item').each(function(){
  352. if (this.submenu){
  353. hideMenu(this.submenu);
  354. }
  355. $(this).removeClass('menu-active');
  356. });
  357. }
  358. function hideit(m){
  359. m.stop(true,true);
  360. if (m[0].shadow){
  361. m[0].shadow.hide();
  362. }
  363. m.hide();
  364. }
  365. }
  366. function findItem(target, param){
  367. var result = null;
  368. var fn = $.isFunction(param) ? param : function(item){
  369. for(var p in param){
  370. if (item[p] != param[p]){
  371. return false;;
  372. }
  373. }
  374. return true;
  375. }
  376. function find(menu){
  377. menu.children('div.menu-item').each(function(){
  378. var opts = $(this).data('menuitem').options;
  379. if (fn.call(target, opts) == true){
  380. result = $(target).menu('getItem', this);
  381. } else if (this.submenu && !result){
  382. find(this.submenu);
  383. }
  384. });
  385. }
  386. find($(target));
  387. return result;
  388. }
  389. function setDisabled(target, itemEl, disabled){
  390. var t = $(itemEl);
  391. if (t.hasClass('menu-item')){
  392. var opts = t.data('menuitem').options;
  393. opts.disabled = disabled;
  394. if (disabled){
  395. t.addClass('menu-item-disabled');
  396. t[0].onclick = null;
  397. } else {
  398. t.removeClass('menu-item-disabled');
  399. t[0].onclick = opts.onclick;
  400. }
  401. }
  402. }
  403. function appendItem(target, param){
  404. var opts = $.data(target, 'menu').options;
  405. var menu = $(target);
  406. if (param.parent){
  407. if (!param.parent.submenu){
  408. var submenu = $('<div></div>').appendTo('body');
  409. param.parent.submenu = submenu;
  410. $('<div class="menu-rightarrow"></div>').appendTo(param.parent);
  411. createMenu(target, submenu);
  412. }
  413. menu = param.parent.submenu;
  414. }
  415. var div = $('<div></div>').appendTo(menu);
  416. createItem(target, div, param);
  417. }
  418. function removeItem(target, itemEl){
  419. function removeit(el){
  420. if (el.submenu){
  421. el.submenu.children('div.menu-item').each(function(){
  422. removeit(this);
  423. });
  424. var shadow = el.submenu[0].shadow;
  425. if (shadow) shadow.remove();
  426. el.submenu.remove();
  427. }
  428. $(el).remove();
  429. }
  430. removeit(itemEl);
  431. }
  432. function setVisible(target, itemEl, visible){
  433. var menu = $(itemEl).parent();
  434. if (visible){
  435. $(itemEl).show();
  436. } else {
  437. $(itemEl).hide();
  438. }
  439. setMenuSize(target, menu);
  440. }
  441. function destroyMenu(target){
  442. $(target).children('div.menu-item').each(function(){
  443. removeItem(target, this);
  444. });
  445. if (target.shadow) target.shadow.remove();
  446. $(target).remove();
  447. }
  448. $.fn.menu = function(options, param){
  449. if (typeof options == 'string'){
  450. return $.fn.menu.methods[options](this, param);
  451. }
  452. options = options || {};
  453. return this.each(function(){
  454. var state = $.data(this, 'menu');
  455. if (state){
  456. $.extend(state.options, options);
  457. } else {
  458. state = $.data(this, 'menu', {
  459. options: $.extend({}, $.fn.menu.defaults, $.fn.menu.parseOptions(this), options)
  460. });
  461. init(this);
  462. }
  463. $(this).css({
  464. left: state.options.left,
  465. top: state.options.top
  466. });
  467. });
  468. };
  469. $.fn.menu.methods = {
  470. options: function(jq){
  471. return $.data(jq[0], 'menu').options;
  472. },
  473. show: function(jq, pos){
  474. return jq.each(function(){
  475. showMenu(this, pos);
  476. });
  477. },
  478. hide: function(jq){
  479. return jq.each(function(){
  480. hideAll(this);
  481. });
  482. },
  483. destroy: function(jq){
  484. return jq.each(function(){
  485. destroyMenu(this);
  486. });
  487. },
  488. /**
  489. * set the menu item text
  490. * param: {
  491. * target: DOM object, indicate the menu item
  492. * text: string, the new text
  493. * }
  494. */
  495. setText: function(jq, param){
  496. return jq.each(function(){
  497. var item = $(param.target).data('menuitem').options;
  498. item.text = param.text;
  499. $(param.target).children('div.menu-text').html(param.text);
  500. });
  501. },
  502. /**
  503. * set the menu icon class
  504. * param: {
  505. * target: DOM object, indicate the menu item
  506. * iconCls: the menu item icon class
  507. * }
  508. */
  509. setIcon: function(jq, param){
  510. return jq.each(function(){
  511. var item = $(param.target).data('menuitem').options;
  512. item.iconCls = param.iconCls;
  513. $(param.target).children('div.menu-icon').remove();
  514. if (param.iconCls){
  515. $('<div class="menu-icon"></div>').addClass(param.iconCls).appendTo(param.target);
  516. }
  517. });
  518. },
  519. /**
  520. * get the menu item data that contains the following property:
  521. * {
  522. * target: DOM object, the menu item
  523. * id: the menu id
  524. * text: the menu item text
  525. * iconCls: the icon class
  526. * href: a remote address to redirect to
  527. * onclick: a function to be called when the item is clicked
  528. * }
  529. */
  530. getItem: function(jq, itemEl){
  531. var item = $(itemEl).data('menuitem').options;
  532. return $.extend({}, item, {
  533. target: $(itemEl)[0]
  534. });
  535. },
  536. findItem: function(jq, text){
  537. if (typeof text == 'string'){
  538. return findItem(jq[0], function(item){
  539. return $('<div>'+item.text+'</div>').text() == text;
  540. });
  541. } else {
  542. return findItem(jq[0], text);
  543. }
  544. },
  545. /**
  546. * append menu item, the param contains following properties:
  547. * parent,id,text,iconCls,href,onclick
  548. * when parent property is assigned, append menu item to it
  549. */
  550. appendItem: function(jq, param){
  551. return jq.each(function(){
  552. appendItem(this, param);
  553. });
  554. },
  555. removeItem: function(jq, itemEl){
  556. return jq.each(function(){
  557. removeItem(this, itemEl);
  558. });
  559. },
  560. enableItem: function(jq, itemEl){
  561. return jq.each(function(){
  562. setDisabled(this, itemEl, false);
  563. });
  564. },
  565. disableItem: function(jq, itemEl){
  566. return jq.each(function(){
  567. setDisabled(this, itemEl, true);
  568. });
  569. },
  570. showItem: function(jq, itemEl){
  571. return jq.each(function(){
  572. setVisible(this, itemEl, true);
  573. });
  574. },
  575. hideItem: function(jq, itemEl){
  576. return jq.each(function(){
  577. setVisible(this, itemEl, false);
  578. });
  579. },
  580. resize: function(jq, menuEl){
  581. return jq.each(function(){
  582. setMenuSize(this, menuEl ? $(menuEl) : $(this));
  583. });
  584. }
  585. };
  586. $.fn.menu.parseOptions = function(target){
  587. return $.extend({}, $.parser.parseOptions(target, [
  588. {minWidth:'number',itemHeight:'number',duration:'number',hideOnUnhover:'boolean'},
  589. {fit:'boolean',inline:'boolean',noline:'boolean'}
  590. ]));
  591. };
  592. $.fn.menu.defaults = {
  593. zIndex:110000,
  594. left: 0,
  595. top: 0,
  596. alignTo: null,
  597. align: 'left',
  598. minWidth: 150,
  599. // itemHeight: 22,
  600. itemHeight: 32,
  601. duration: 100, // Defines duration time in milliseconds to hide when the mouse leaves the menu.
  602. hideOnUnhover: true, // Automatically hides the menu when mouse exits it
  603. inline: false, // true to stay inside its parent, false to go on top of all elements
  604. fit: false,
  605. noline: false,
  606. events: {
  607. mouseenter: mouseenterHandler,
  608. mouseleave: mouseleaveHandler,
  609. mouseover: mouseoverHandler,
  610. mouseout: mouseoutHandler,
  611. click: clickHandler
  612. },
  613. position: function(target, left, top){
  614. return {left:left,top:top}
  615. },
  616. onShow: function(){},
  617. onHide: function(){},
  618. onClick: function(item){}
  619. };
  620. })(jQuery);