jquery.contextmenu-ui.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /**
  2. * @license ContextMenu - jQuery plugin for right-click context menus
  3. *
  4. * Author: Chris Domigan
  5. * Contributors: Dan G. Switzer, II
  6. * Parts of this plugin are inspired by Joern Zaefferer's Tooltip plugin
  7. *
  8. * Dual licensed under the MIT and GPL licenses:
  9. * http://www.opensource.org/licenses/mit-license.php
  10. * http://www.gnu.org/licenses/gpl.html
  11. *
  12. * Version: r2
  13. * Date: 16 July 2007
  14. *
  15. * For documentation visit http://www.trendskitchens.co.nz/jquery/contextmenu/
  16. *
  17. * Updated: include support jQuery UI CSS classes existing starting with version 1.8
  18. * and the currents modified CSS classes of version jQuery UI 1.9
  19. * by Oleg Kiriljuk, oleg.kiriljuk@ok-soft.gmbh.com
  20. * Date: 24 December 2011
  21. *
  22. * Updated by Oleg Kiriljuk to support jQuery UI 1.10 and 1.11
  23. * Date: 17 March 2015
  24. */
  25. /*global jQuery, define, module, require */
  26. /*jslint devel: true, browser: true, plusplus: true, eqeq: true */
  27. (function (global, factory) {
  28. "use strict";
  29. if (typeof define === "function" && define.amd) {
  30. // AMD. Register as an anonymous module.
  31. define(["jquery"], function ($) {
  32. return factory($, global.document);
  33. });
  34. } else if (typeof module === "object" && module.exports) {
  35. // Node/CommonJS
  36. module.exports = function (root, $) {
  37. if ($ === undefined) {
  38. // require("jquery") returns a factory that requires window to
  39. // build a jQuery instance, we normalize how we use modules
  40. // that require this pattern but the window provided is a noop
  41. // if it's defined (how jquery works)
  42. $ = typeof window !== "undefined" ?
  43. require("jquery") :
  44. require("jquery")(root || window);
  45. }
  46. factory($, root.document);
  47. return $;
  48. };
  49. } else {
  50. // Browser globals
  51. factory(jQuery, global.document);
  52. }
  53. }(typeof window !== "undefined" ? window : this, function ($, document) {
  54. "use strict";
  55. var menu, shadow, content, hash, currentTarget,
  56. versionParts = $.ui != null && typeof $.ui.version === "string" ? /^([0-9]+)\.([0-9]+)\.([0-9]+)$/.exec($.ui.version) : [],
  57. isAncorRequired = versionParts != null && versionParts.length === 4 && versionParts[1] === "1" && versionParts[2] < 11,
  58. defaults = {
  59. menuClasses: "ui-menu ui-widget ui-widget-content ui-corner-all",
  60. menuIconClasses: "ui-menu-icons ui-menu ui-widget ui-widget-content ui-corner-all",
  61. menuDivStyle: {
  62. position: "absolute",
  63. zIndex: "500"
  64. },
  65. menuStyle: {
  66. width: "100%"
  67. },
  68. itemClasses: "ui-menu-item",
  69. itemStyle: {},
  70. itemHoverStyle: {},
  71. itemAnchorClasses: "ui-corner-all",
  72. itemAnchorStyle: {
  73. position: "relative",
  74. paddingRight: "0"
  75. },
  76. itemIconAnchorStyle: {
  77. paddingLeft: "2em"
  78. },
  79. itemIconSpanStyle: {
  80. left: ".2em",
  81. top: ".3em",
  82. marginRight: ".5em",
  83. position: "absolute",
  84. "float": "left"
  85. },
  86. itemHoverAnchorClasses: "ui-state-hover",
  87. eventPosX: "pageX",
  88. eventPosY: "pageY",
  89. shadow: true,
  90. menuShadowClasses: "ui-widget-shadow",
  91. menuShadowStyle: {
  92. position: "absolute",
  93. zIndex: "499",
  94. margin: "0",
  95. padding: "1px 0 0 6px"
  96. },
  97. onContextMenu: null,
  98. onShowMenu: null
  99. };
  100. $.fn.contextMenu = function (id, options) {
  101. hash = hash || [];
  102. hash.push({
  103. id: id,
  104. menuDivStyle: $.extend({}, defaults.menuDivStyle, options.menuDivStyle || {}),
  105. menuStyle: $.extend({}, defaults.menuStyle, options.menuStyle || {}),
  106. menuShadowStyle: $.extend({}, defaults.menuShadowStyle, options.menuShadowStyle || {}),
  107. itemStyle: $.extend({}, defaults.itemStyle, options.itemStyle || {}),
  108. itemHoverStyle: $.extend({}, defaults.itemHoverStyle, options.itemHoverStyle || {}),
  109. menuClasses: options.menuClasses || defaults.menuClasses,
  110. menuIconClasses: options.menuIconClasses || defaults.menuIconClasses,
  111. menuShadowClasses: options.menuShadowClasses || defaults.menuShadowClasses,
  112. itemClasses: options.itemClasses || defaults.itemClasses,
  113. itemAnchorClasses: options.itemAnchorClasses || defaults.itemAnchorClasses,
  114. itemAnchorStyle: $.extend({}, defaults.itemAnchorStyle, options.itemAnchorStyle || {}),
  115. itemIconSpanStyle: $.extend({}, defaults.itemIconSpanStyle, options.itemIconSpanStyle || {}),
  116. itemIconAnchorStyle: $.extend({}, defaults.itemIconAnchorStyle, options.itemIconAnchorStyle || {}),
  117. itemHoverAnchorClasses: options.itemHoverAnchorClasses || defaults.itemHoverAnchorClasses,
  118. bindings: options.bindings || {},
  119. shadow: options.shadow || options.shadow === false ? options.shadow : defaults.shadow,
  120. onContextMenu: options.onContextMenu || defaults.onContextMenu,
  121. onShowMenu: options.onShowMenu || defaults.onShowMenu,
  122. eventPosX: options.eventPosX || defaults.eventPosX,
  123. eventPosY: options.eventPosY || defaults.eventPosY
  124. });
  125. function hide() {
  126. menu.hide().attr("aria-hidden", "true");
  127. shadow.hide().attr("aria-hidden", "true");
  128. }
  129. function display(i, trigger, e) {
  130. var cur = hash[i], items;
  131. content = $("#" + cur.id).find("ul:first").clone(true);
  132. // Send the content to the menu
  133. menu.html(content);
  134. // if there's an onShowMenu, run it now -- must run after content has been added
  135. // if you try to alter the content variable before the menu.html(), IE6 has issues
  136. // updating the content
  137. if (!!cur.onShowMenu) { menu = cur.onShowMenu(e, menu); }
  138. if (cur.menuClasses) {
  139. if (cur.menuIconClasses && content.find(".ui-icon").length > 0) {
  140. content.addClass(cur.menuIconClasses);
  141. } else {
  142. content.addClass(cur.menuClasses);
  143. }
  144. }
  145. if (!$.isEmptyObject(cur.menuStyle)) {
  146. content.css(cur.menuStyle);
  147. }
  148. items = content.attr("role", "menu").find("li");
  149. if (cur.itemClasses) {
  150. items.addClass(cur.itemClasses).attr("role", isAncorRequired ? "presentation" : "menuitem");
  151. }
  152. if (!$.isEmptyObject(cur.itemStyle)) {
  153. items.css(cur.itemStyle);
  154. }
  155. if (cur.itemAnchorClasses) {
  156. if (isAncorRequired) {
  157. items.children("a").addClass(cur.itemAnchorClasses).filter(":not([role])").attr("role", "menuitem");
  158. } else {
  159. items.addClass(cur.itemAnchorClasses);
  160. }
  161. }
  162. if (!$.isEmptyObject(cur.itemAnchorStyle)) {
  163. if (isAncorRequired) {
  164. items.children("a").css(cur.itemAnchorStyle);
  165. } else {
  166. items.css(cur.itemAnchorStyle);
  167. }
  168. }
  169. if (!$.isEmptyObject(cur.itemIconSpanStyle)) {
  170. if (isAncorRequired) {
  171. items.children("a").children("span.ui-icon").css(cur.itemIconSpanStyle).parent("a").css(cur.itemIconAnchorStyle);
  172. } else {
  173. items.children("span.ui-icon").css(cur.itemIconSpanStyle);
  174. }
  175. }
  176. if ($.isEmptyObject(cur.itemHoverStyle)) {
  177. items.hover(
  178. function () {
  179. //$(this).siblings().children(".ui-state-active").removeClass("ui-state-active");
  180. var menuItem = isAncorRequired ? $(this).children("a") : $(this);
  181. menuItem.addClass(cur.itemHoverAnchorClasses);
  182. },
  183. function () {
  184. var menuItem = isAncorRequired ? $(this).children("a") : $(this);
  185. menuItem.removeClass(cur.itemHoverAnchorClasses);
  186. }
  187. );
  188. } else if (!$.isEmptyObject(cur.itemHoverStyle)) {
  189. items.hover(
  190. function () {
  191. $(this).css(cur.itemHoverStyle);
  192. },
  193. function () {
  194. $(this).css(cur.itemStyle);
  195. }
  196. );
  197. }
  198. items.find("img").css({ verticalAlign: "middle", paddingRight: "2px" });
  199. $.each(cur.bindings, function (menuItemId, func) {
  200. $("#" + menuItemId, menu).bind("click", function () {
  201. hide();
  202. func(trigger, currentTarget);
  203. });
  204. });
  205. menu.css({
  206. left: e[cur.eventPosX],
  207. top: e[cur.eventPosY],
  208. "white-space": "pre"
  209. }).show().removeAttr("aria-hidden");
  210. if (cur.shadow) {
  211. shadow.css({
  212. width: menu.width(),
  213. height: menu.height(),
  214. left: e.pageX + 2,
  215. top: e.pageY + 2
  216. }).show().removeAttr("aria-hidden");
  217. }
  218. var of = menu.offset(), topDelta = 0;
  219. if (of.top + menu.height() > $(window).scrollTop() + window.innerHeight) {
  220. topDelta = $(window).scrollTop() - of.top - menu.height() + window.innerHeight;
  221. of.top += topDelta;
  222. menu.offset(of);
  223. of = shadow.offset();
  224. of.top += topDelta;
  225. shadow.offset(of);
  226. }
  227. /*of = menu.offset(), topDelta = 0;
  228. if (of.left + menu.width() > $(window).scrollLeft() + window.innerWidth) {
  229. topDelta = $(window).scrollLeft() - of.top - menu.width() + window.innerWidth;
  230. of.left += topDelta;
  231. menu.offset(of);
  232. of = shadow.offset();
  233. of.left += topDelta;
  234. shadow.offset(of);
  235. }*/
  236. $(document).one("click", hide);
  237. }
  238. var index = hash.length - 1;
  239. if (!menu) { // Create singleton menu
  240. menu = $("<div class=\"jqContextMenu\"></div>")
  241. .hide()
  242. .attr("aria-hidden", "true")
  243. .css(hash[index].menuDivStyle)
  244. .appendTo("body")
  245. .bind("click", function (e) {
  246. e.stopPropagation();
  247. }).mouseleave(function (e) {
  248. if (e.pageX === -1 && e.pageY === -1) {
  249. return; // over tooltip
  250. }
  251. hide();
  252. });
  253. }
  254. if (!shadow) {
  255. shadow = $("<div></div>")
  256. .addClass(hash[index].menuShadowClasses)
  257. .css(hash[index].menuShadowStyle)
  258. .appendTo("body")
  259. .hide().attr("aria-hidden", "true");
  260. }
  261. $(this).bind("contextmenu", function (e) {
  262. // Check if onContextMenu() defined
  263. var bShowContext = (!!hash[index].onContextMenu) ? hash[index].onContextMenu(e) : true;
  264. currentTarget = e.target;
  265. if (bShowContext) {
  266. display(index, this, e, options);
  267. return false;
  268. }
  269. hide();
  270. return true;
  271. });
  272. return this;
  273. };
  274. // Apply defaults
  275. $.contextMenu = {
  276. defaults: function (userDefaults) {
  277. $.each(userDefaults, function (i, val) {
  278. if (typeof val === "object" && defaults[i]) {
  279. $.extend(defaults[i], val);
  280. } else {
  281. defaults[i] = val;
  282. }
  283. });
  284. }
  285. };
  286. $(function () {
  287. $("div.contextMenu").hide().attr("aria-hidden", "true");
  288. });
  289. }));