From 7468f0df26c61caf6dc9ae283049eac946813eaa Mon Sep 17 00:00:00 2001 From: zpc Date: Sun, 7 Dec 2025 12:52:31 +0800 Subject: [PATCH] 321 --- .../2.9.2/context-menu-icons.eot | Bin 0 -> 3528 bytes .../2.9.2/context-menu-icons.ttf | Bin 0 -> 3320 bytes .../2.9.2/context-menu-icons.woff | Bin 0 -> 2180 bytes .../2.9.2/context-menu-icons.woff2 | Bin 0 -> 1684 bytes .../2.9.2/jquery.contextMenu.css | 309 +++ .../2.9.2/jquery.contextMenu.js | 2134 +++++++++++++++++ .../2.9.2/jquery.contextMenu.min.css | 16 + .../2.9.2/jquery.contextMenu.min.css.map | 1 + .../2.9.2/jquery.contextMenu.min.js | 2 + .../2.9.2/jquery.contextMenu.min.js.map | 1 + .../2.9.2/jquery.ui.position.js | 513 ++++ .../2.9.2/jquery.ui.position.min.js | 6 + .../wwwroot/views/sq/sqroompricing/index.html | 84 +- .../Controllers/SQController.cs | 10 +- 14 files changed, 3061 insertions(+), 15 deletions(-) create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/context-menu-icons.eot create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/context-menu-icons.ttf create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/context-menu-icons.woff create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/context-menu-icons.woff2 create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.contextMenu.css create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.contextMenu.js create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.contextMenu.min.css create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.contextMenu.min.css.map create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.contextMenu.min.js create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.contextMenu.min.js.map create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.ui.position.js create mode 100644 server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.ui.position.min.js diff --git a/server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/context-menu-icons.eot b/server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/context-menu-icons.eot new file mode 100644 index 0000000000000000000000000000000000000000..896c5b024602bbfefe65be75909e18c07f3aa8f5 GIT binary patch literal 3528 zcmd^CZ){W76+id=w+)GtIPV3LI4@3~4GXc29otX}VUPyWrlbWL)KLkU#0lvf+e;i4 z=vJ}Oe~5}!p&zDdnzRknR*@!ED|A{bHJ@6Fx_zjsZ5mXX)J@uq$fjwlST~&g?t3o; z%BJysSGwn(^SkGsd+)jDp7-vh)kHT|5it^o7lCBaW`MlRI5+r+Bd<^!hB$lUgKI13 zGS^8XRHOoxDMz!Q-82cVKr`Sb+`efj*U(XYU0o^~eFP7L|9MmAc2zhv9GFv*WE&dg|=Ge#M`D0^m zRG)i~Nc;YoEAPM3EWX(NRECG^-zDp|C zvQaO2!7bUc)9;0|w~^4-Ke(T2^4W5M)?z76;p1=TS$<+X3;WJUhCbfQfG|>)QSl6W znPg0NO85xaOHRwKQG#LQO01#pj`GOZ1EaLP!YZ2Wk0-E~?NH!ra0{QzsY_q1@XMI3 zgn1DKL>nAccGw;N1W;~i16p_hxMT+z-vRK7wgjJ47^je(T?x>YuoDIT!^#=o;s1L< z)<7w_Zh%)R+1gMZfTZer z(!FdOb0$hBYgv-1Nh6c$uoqR=jzzFKR}eqs6WzROm{o9w|4;smf#pO(1M~CMv-9(% zx2|9v%rb<;Ra0LS{s83xKIEKfuoFfG12F6zR*ykaeY&DWQm^Dypv5FjeUYMZ%eFER#+q3~VM6 zj&LXnCT(LuX6S07jag}sAXDXZI1=s%sfups$%MEKijZocl}t1ksUDb6F-IF?5xG%q zV{(SiC0I4O-m2?AXCc2_H=xyP)tO#N&_rodY;f9+B1lepQad_YFoj zNusePKD2i*22K;CUUnMe2I^%0&Bxca#CkS&=}yxeYgwyzZSHZJS2GP-ccUaqPi5G- zf$G3PPYb&~Q2lj|DQtnSp;fm&^R4$-lKg2{U@grX9u3C3Z^^V0)lxur*Zudh1|Qpq&$TBBTaZNzIM2AU@l`2CUD& zn$cR`?yL+6xClJfomj0!B*iF02Z^k@o^TfnDF`MVv6IgH51F;&SUMC{cEZYuz*OC> zSjYMC5NzYI&gQ(DrFm^~)~9u9KAYTzRzGCaEGZWhiGA+XGHI*YX{9rK$eZi5ZsVfS zt*v`g^Zta*ajaV47XAw_^zKj?Qy#-CH$RI%urnr@_H2h5gTcl_KIa}`|Np{WRj8IW z<6E;E@tBFIjmbt9Ml9;REZu;8@k_<;Q?fl_1Q|LISM83A0HtxMp{R=n>qs#*lHt*) z2iQM%9@vp)EgtVBZ-eKO*UI))t-frwk6r0((b*+T=B8ED851M5CSzqC@Q3SaHkq;keGr(VjIM8Bo!3I#i3^|UcUIuP;uet z)30sGzVONm*)6X+r=srr*^E}wi5UC?;&rZ_GUm}0HIZ2}? zYDO%P)$T+=M+P%+f3>oJ&i&PpjL2V3Rlt!C5hg*#-WCW6_!bI-39X+zxSe;7yc`s*MM`+56i`4@(NkJGWo-|Z;glI?-~j**|1P&6c|-bA_4i2}sj>bK^7l z?9|;IcO%2Osp*Mg!Q2>)-`%+@SIA9e%ehhW@G*1x=y;-B9y7Il-S;GHjX*m}N9<80x(~TLhLOw99!EcF zduq~-J3IXB|80l6lX{rIL?$sAUnP}k%)`7SW=BV*kz(nXI5JaKMsxXGIVa^tC(3HR Um>r!cjLW6$G@x(HOq5~m-%L1Bod5s; literal 0 HcmV?d00001 diff --git a/server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/context-menu-icons.ttf b/server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/context-menu-icons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6ecdf1b5a1c0a5d4d12b6c0af227445518aa66d2 GIT binary patch literal 3320 zcmd^BeQaA-6+h>F+wKx4ao$Ut#CdUIyCse7I{s+atu1YqHe1%Pwo5fSsd5u1&2Vg! zIHm1iDs&sGI&~=BA80~IK&x$vgfy{sh#@V1jjkH}F-?GICm{_a@YH}1Ofaa!@4n}L zYz5K(dDcDeoZr3Y$O63mnepCfeS;@B-HU79(>Qfc}H33PuaNyiCG51q}@Q}GyE-5vDZ`5yTD#>-~16J8JZHQGSL6^y8h!pg{Ad)BLlz{>|3ZR0;)p2)0tqr$`x^;g9yh#^j+ihPP8@><0Si6JVa zij0aOYNv`C6+_f&RoKOFDh)46((^pVzm#@KUzL6#d!3Uc`KVf0dfY#@dP~?8@ z5hRt_%xOWAW+x9MO?kn#uGv=6vaeb8ZLHKjW7*3BTDGlQtni8WaHB|GqAm(imEOLc?-LaG6Rh~!1{odP4_rKHFYBhZmJ9NJHpZRtCn2@XZio= z&sqp48X8_)test4EWdsQad6m@l2>hWS^7N|4-1uZw#6r`EEJ&jrgeOgpIv)W7kvp`Aco96M32!F&C-+fbtmqC7K)@YmTGE^tc9XsUDHjyfpx7X z6pduJa5giLw6K|IBr2d7n2du3*)sKH7l$(-LDtnwBpT@n>6&So14(%c7D8&ks)1yS zl}^Eg4j*02QMFa?Vl^v#N#O?5>@>}vaLBJV4I9lyZGNjF8M3l5KC)-`hLo;sjE(F~ zL{)ugNLQnYy(6)WifpY;jP4nUgEJ&$E1!nmaFgo4{@9w1cxqFh>Au?I9c#?KO)2;F zQntnDZ&hUF$t<56t_>gXbnvy|+OP7g@sfz4(+q#;TjvQY>IV^tJKEPj5=`_bhWhpd z6U~OY?sKZqoCrR$K6Jm;FWwy;Qse_w347N0M%x|=b_d!U~}`I#0T4uAaltkLmCZ*^3{MG&>_)@mavDNb2BKoo9DMfy0Tp_ok6PA2<5WbVeX z^k};3mX%w9>86{tj`I^G*d=P67raK;@H*tI&*(LL4!I4jy~EV5XcshvKlU2gOt{t? z&SZtkYio^u>!Q_ftbNV!{*V_0Rx1h%|Am*fZr7N#N8#oAhlvMv#3kGDcCa-VY(406 z?-BO@FWgm)8fg>$Yj&X?vr)Zupp_%2MRO}>TCgv1sl}%;g68N^h5;=pBalo_lKHNG22E zE0MI;>#?%uYA4P~UrU`&`(yqwKezALzr)V*cVNQP0yDJ>1=qk}u0d&m1zE{NTob!= zHSF>7OTq%D)8|uu|9*czwu4^rp1dr-O5G3w4N@p3rBk?5tqo{QK`FTfVkDXlO7imP z(-$vae0sFBbmXa*H|L&v@wwdQm!CSaRN|#PV9e%^zkjOvXXf< z-bXIcKkGfopf+kpEm64Jt%9B`JcxXCa=;Y%Y8i;CpKVpZ(GO83LFP9kLIb(7;L~p$ zu~2(04psMB6+c)_p!*klHsF|5`gCn$rQ>l1VUc8U{LAy*KZZ z{zScqE+Cps99;$^B3gqW;^^w<*NCNkca)^{aN|vmVdW9vD@G8F1#r_ zisFuupWb%8gEm9Pso;_DkJ4P{WpJzOyaK+j&a3!(yuZ$Cz<1SoFIIf9&ikl^o)KRW ziufMurEk}Hi5l=_=;AW)%XMA>zg*{4GP$|VYt+kY>%5o7c%;tzD8k<#FBL2Kqm}-d zd~v>i8q(aJ{M39QH+$#Aoyb^zc5b>_wQWoPaQ*c!*`7#()B%5*P!c^sNI^5U+KIg!mcb?20y^S$kI zpQav`SZ0M){442fu!p@Q=O!kU@lyGiJU(C1Ch~=RC9mWsrYm}(l$)3?PO0VG9H1}H IPgh{=-x>inGXMYp literal 0 HcmV?d00001 diff --git a/server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/context-menu-icons.woff b/server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/context-menu-icons.woff new file mode 100644 index 0000000000000000000000000000000000000000..a37f85566e6f504f5846c85ae5a40e02e1cfb6b4 GIT binary patch literal 2180 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 i, .context-menu-icon.context-menu-icon--fa5.context-menu-hover > svg { + color: #fff; +} +.context-menu-icon.context-menu-icon--fa5.context-menu-disabled i, .context-menu-icon.context-menu-icon--fa5.context-menu-disabled svg { + color: #bbb; +} + +.context-menu-list { + position: absolute; + display: inline-block; + min-width: 13em; + max-width: 26em; + padding: .25em 0; + margin: .3em; + font-family: inherit; + font-size: inherit; + list-style-type: none; + background: #fff; + border: 1px solid #bebebe; + border-radius: .2em; + -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5); + box-shadow: 0 2px 5px rgba(0, 0, 0, .5); +} + +.context-menu-item { + position: relative; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: .2em 2em; + color: #2f2f2f; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: #fff; +} + +.context-menu-separator { + padding: 0; + margin: .35em 0; + border-bottom: 1px solid #e6e6e6; +} + +.context-menu-item > label > input, +.context-menu-item > label > textarea { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.context-menu-item.context-menu-hover { + color: #fff; + cursor: pointer; + background-color: #2980b9; +} + +.context-menu-item.context-menu-disabled { + color: #bbb; + cursor: default; + background-color: #fff; +} + +.context-menu-input.context-menu-hover { + color: #2f2f2f; + cursor: default; +} + +.context-menu-submenu:after { + position: absolute; + top: 50%; + right: .5em; + z-index: 1; + width: 0; + height: 0; + content: ''; + border-color: transparent transparent transparent #2f2f2f; + border-style: solid; + border-width: .25em 0 .25em .25em; + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + transform: translateY(-50%); +} + +/** + * Inputs + */ +.context-menu-item.context-menu-input { + padding: .3em .6em; +} + +/* vertically align inside labels */ +.context-menu-input > label > * { + vertical-align: top; +} + +/* position checkboxes and radios as icons */ +.context-menu-input > label > input[type="checkbox"], +.context-menu-input > label > input[type="radio"] { + position: relative; + top: .12em; + margin-right: .4em; +} + +.context-menu-input > label { + margin: 0; +} + +.context-menu-input > label, +.context-menu-input > label > input[type="text"], +.context-menu-input > label > textarea, +.context-menu-input > label > select { + display: block; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.context-menu-input > label > textarea { + height: 7em; +} + +.context-menu-item > .context-menu-list { + top: .3em; + /* re-positioned by js */ + right: -.3em; + display: none; +} + +.context-menu-item.context-menu-visible > .context-menu-list { + display: block; +} + +.context-menu-accesskey { + text-decoration: underline; +} diff --git a/server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.contextMenu.js b/server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.contextMenu.js new file mode 100644 index 0000000..7eb9623 --- /dev/null +++ b/server/CoreCms.Net.Web.Admin/wwwroot/lib/jquery-contextmenu/2.9.2/jquery.contextMenu.js @@ -0,0 +1,2134 @@ +/** + * jQuery contextMenu v2.9.2 - Plugin for simple contextMenu handling + * + * Version: v2.9.2 + * + * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) + * Web: http://swisnl.github.io/jQuery-contextMenu/ + * + * Copyright (c) 2011-2020 SWIS BV and contributors + * + * Licensed under + * MIT License http://www.opensource.org/licenses/mit-license + * + * Date: 2020-05-13T13:55:36.983Z + */ + +// jscs:disable +/* jshint ignore:start */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as anonymous module. + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // Node / CommonJS + factory(require('jquery')); + } else { + // Browser globals. + factory(jQuery); + } +})(function ($) { + + 'use strict'; + + // TODO: - + // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio + // create structure if $.support[htmlCommand || htmlMenuitem] and !opt.disableNative + + // determine html5 compatibility + $.support.htmlMenuitem = ('HTMLMenuItemElement' in window); + $.support.htmlCommand = ('HTMLCommandElement' in window); + $.support.eventSelectstart = ('onselectstart' in document.documentElement); + /* // should the need arise, test for css user-select + $.support.cssUserSelect = (function(){ + var t = false, + e = document.createElement('div'); + + $.each('Moz|Webkit|Khtml|O|ms|Icab|'.split('|'), function(i, prefix) { + var propCC = prefix + (prefix ? 'U' : 'u') + 'serSelect', + prop = (prefix ? ('-' + prefix.toLowerCase() + '-') : '') + 'user-select'; + + e.style.cssText = prop + ': text;'; + if (e.style[propCC] == 'text') { + t = true; + return false; + } + + return true; + }); + + return t; + })(); + */ + + + if (!$.ui || !$.widget) { + // duck punch $.cleanData like jQueryUI does to get that remove event + $.cleanData = (function (orig) { + return function (elems) { + var events, elem, i; + for (i = 0; elems[i] != null; i++) { + elem = elems[i]; + try { + // Only trigger remove when necessary to save time + events = $._data(elem, 'events'); + if (events && events.remove) { + $(elem).triggerHandler('remove'); + } + + // Http://bugs.jquery.com/ticket/8235 + } catch (e) { + } + } + orig(elems); + }; + })($.cleanData); + } + /* jshint ignore:end */ + // jscs:enable + + var // currently active contextMenu trigger + $currentTrigger = null, + // is contextMenu initialized with at least one menu? + initialized = false, + // window handle + $win = $(window), + // number of registered menus + counter = 0, + // mapping selector to namespace + namespaces = {}, + // mapping namespace to options + menus = {}, + // custom command type handlers + types = {}, + // default values + defaults = { + // selector of contextMenu trigger + selector: null, + // where to append the menu to + appendTo: null, + // method to trigger context menu ["right", "left", "hover"] + trigger: 'right', + // hide menu when mouse leaves trigger / menu elements + autoHide: false, + // ms to wait before showing a hover-triggered context menu + delay: 200, + // flag denoting if a second trigger should simply move (true) or rebuild (false) an open menu + // as long as the trigger happened on one of the trigger-element's child nodes + reposition: true, + // Flag denoting if a second trigger should close the menu, as long as + // the trigger happened on one of the trigger-element's child nodes. + // This overrides the reposition option. + hideOnSecondTrigger: false, + + //ability to select submenu + selectableSubMenu: false, + + // Default classname configuration to be able avoid conflicts in frameworks + classNames: { + hover: 'context-menu-hover', // Item hover + disabled: 'context-menu-disabled', // Item disabled + visible: 'context-menu-visible', // Item visible + notSelectable: 'context-menu-not-selectable', // Item not selectable + + icon: 'context-menu-icon', + iconEdit: 'context-menu-icon-edit', + iconCut: 'context-menu-icon-cut', + iconCopy: 'context-menu-icon-copy', + iconPaste: 'context-menu-icon-paste', + iconDelete: 'context-menu-icon-delete', + iconAdd: 'context-menu-icon-add', + iconQuit: 'context-menu-icon-quit', + iconLoadingClass: 'context-menu-icon-loading' + }, + + // determine position to show menu at + determinePosition: function ($menu) { + // position to the lower middle of the trigger element + if ($.ui && $.ui.position) { + // .position() is provided as a jQuery UI utility + // (...and it won't work on hidden elements) + $menu.css('display', 'block').position({ + my: 'center top', + at: 'center bottom', + of: this, + offset: '0 5', + collision: 'fit' + }).css('display', 'none'); + } else { + // determine contextMenu position + var offset = this.offset(); + offset.top += this.outerHeight(); + offset.left += this.outerWidth() / 2 - $menu.outerWidth() / 2; + $menu.css(offset); + } + }, + // position menu + position: function (opt, x, y) { + var offset; + // determine contextMenu position + if (!x && !y) { + opt.determinePosition.call(this, opt.$menu); + return; + } else if (x === 'maintain' && y === 'maintain') { + // x and y must not be changed (after re-show on command click) + offset = opt.$menu.position(); + } else { + // x and y are given (by mouse event) + var offsetParentOffset = opt.$menu.offsetParent().offset(); + offset = {top: y - offsetParentOffset.top, left: x -offsetParentOffset.left}; + } + + // correct offset if viewport demands it + var bottom = $win.scrollTop() + $win.height(), + right = $win.scrollLeft() + $win.width(), + height = opt.$menu.outerHeight(), + width = opt.$menu.outerWidth(); + + if (offset.top + height > bottom) { + offset.top -= height; + } + + if (offset.top < 0) { + offset.top = 0; + } + + if (offset.left + width > right) { + offset.left -= width; + } + + if (offset.left < 0) { + offset.left = 0; + } + + opt.$menu.css(offset); + }, + // position the sub-menu + positionSubmenu: function ($menu) { + if (typeof $menu === 'undefined') { + // When user hovers over item (which has sub items) handle.focusItem will call this. + // but the submenu does not exist yet if opt.items is a promise. just return, will + // call positionSubmenu after promise is completed. + return; + } + if ($.ui && $.ui.position) { + // .position() is provided as a jQuery UI utility + // (...and it won't work on hidden elements) + $menu.css('display', 'block').position({ + my: 'left top-5', + at: 'right top', + of: this, + collision: 'flipfit fit' + }).css('display', ''); + } else { + // determine contextMenu position + var offset = { + top: -9, + left: this.outerWidth() - 5 + }; + $menu.css(offset); + } + }, + // offset to add to zIndex + zIndex: 1, + // show hide animation settings + animation: { + duration: 50, + show: 'slideDown', + hide: 'slideUp' + }, + // events + events: { + preShow: $.noop, + show: $.noop, + hide: $.noop, + activated: $.noop + }, + // default callback + callback: null, + // list of contextMenu items + items: {} + }, + // mouse position for hover activation + hoveract = { + timer: null, + pageX: null, + pageY: null + }, + // determine zIndex + zindex = function ($t) { + var zin = 0, + $tt = $t; + + while (true) { + zin = Math.max(zin, parseInt($tt.css('z-index'), 10) || 0); + $tt = $tt.parent(); + if (!$tt || !$tt.length || 'html body'.indexOf($tt.prop('nodeName').toLowerCase()) > -1) { + break; + } + } + return zin; + }, + // event handlers + handle = { + // abort anything + abortevent: function (e) { + e.preventDefault(); + e.stopImmediatePropagation(); + }, + // contextmenu show dispatcher + contextmenu: function (e) { + var $this = $(this); + + //Show browser context-menu when preShow returns false + if (e.data.events.preShow($this,e) === false) { + return; + } + + // disable actual context-menu if we are using the right mouse button as the trigger + if (e.data.trigger === 'right') { + e.preventDefault(); + e.stopImmediatePropagation(); + } + + // abort native-triggered events unless we're triggering on right click + if ((e.data.trigger !== 'right' && e.data.trigger !== 'demand') && e.originalEvent) { + return; + } + + // Let the current contextmenu decide if it should show or not based on its own trigger settings + if (typeof e.mouseButton !== 'undefined' && e.data) { + if (!(e.data.trigger === 'left' && e.mouseButton === 0) && !(e.data.trigger === 'right' && e.mouseButton === 2)) { + // Mouse click is not valid. + return; + } + } + + // abort event if menu is visible for this trigger + if ($this.hasClass('context-menu-active')) { + return; + } + + if (!$this.hasClass('context-menu-disabled')) { + // theoretically need to fire a show event at + // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus + // var evt = jQuery.Event("show", { data: data, pageX: e.pageX, pageY: e.pageY, relatedTarget: this }); + // e.data.$menu.trigger(evt); + + $currentTrigger = $this; + if (e.data.build) { + var built = e.data.build($currentTrigger, e); + // abort if build() returned false + if (built === false) { + return; + } + + // dynamically build menu on invocation + e.data = $.extend(true, {}, defaults, e.data, built || {}); + + // abort if there are no items to display + if (!e.data.items || $.isEmptyObject(e.data.items)) { + // Note: jQuery captures and ignores errors from event handlers + if (window.console) { + (console.error || console.log).call(console, 'No items specified to show in contextMenu'); + } + + throw new Error('No Items specified'); + } + + // backreference for custom command type creation + e.data.$trigger = $currentTrigger; + + op.create(e.data); + } + op.show.call($this, e.data, e.pageX, e.pageY); + } + }, + // contextMenu left-click trigger + click: function (e) { + e.preventDefault(); + e.stopImmediatePropagation(); + $(this).trigger($.Event('contextmenu', {data: e.data, pageX: e.pageX, pageY: e.pageY})); + }, + // contextMenu right-click trigger + mousedown: function (e) { + // register mouse down + var $this = $(this); + + // hide any previous menus + if ($currentTrigger && $currentTrigger.length && !$currentTrigger.is($this)) { + $currentTrigger.data('contextMenu').$menu.trigger('contextmenu:hide'); + } + + // activate on right click + if (e.button === 2) { + $currentTrigger = $this.data('contextMenuActive', true); + } + }, + // contextMenu right-click trigger + mouseup: function (e) { + // show menu + var $this = $(this); + if ($this.data('contextMenuActive') && $currentTrigger && $currentTrigger.length && $currentTrigger.is($this) && !$this.hasClass('context-menu-disabled')) { + e.preventDefault(); + e.stopImmediatePropagation(); + $currentTrigger = $this; + $this.trigger($.Event('contextmenu', {data: e.data, pageX: e.pageX, pageY: e.pageY})); + } + + $this.removeData('contextMenuActive'); + }, + // contextMenu hover trigger + mouseenter: function (e) { + var $this = $(this), + $related = $(e.relatedTarget), + $document = $(document); + + // abort if we're coming from a menu + if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { + return; + } + + // abort if a menu is shown + if ($currentTrigger && $currentTrigger.length) { + return; + } + + hoveract.pageX = e.pageX; + hoveract.pageY = e.pageY; + hoveract.data = e.data; + $document.on('mousemove.contextMenuShow', handle.mousemove); + hoveract.timer = setTimeout(function () { + hoveract.timer = null; + $document.off('mousemove.contextMenuShow'); + $currentTrigger = $this; + $this.trigger($.Event('contextmenu', { + data: hoveract.data, + pageX: hoveract.pageX, + pageY: hoveract.pageY + })); + }, e.data.delay); + }, + // contextMenu hover trigger + mousemove: function (e) { + hoveract.pageX = e.pageX; + hoveract.pageY = e.pageY; + }, + // contextMenu hover trigger + mouseleave: function (e) { + // abort if we're leaving for a menu + var $related = $(e.relatedTarget); + if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { + return; + } + + try { + clearTimeout(hoveract.timer); + } catch (e) { + } + + hoveract.timer = null; + }, + // click on layer to hide contextMenu + layerClick: function (e) { + var $this = $(this), + root = $this.data('contextMenuRoot'), + button = e.button, + x = e.pageX, + y = e.pageY, + fakeClick = x === undefined, + target, + offset; + + e.preventDefault(); + + setTimeout(function () { + // If the click is not real, things break: https://github.com/swisnl/jQuery-contextMenu/issues/132 + if(fakeClick){ + if (root !== null && typeof root !== 'undefined' && root.$menu !== null && typeof root.$menu !== 'undefined') { + root.$menu.trigger('contextmenu:hide'); + } + return; + } + + var $window; + var triggerAction = ((root.trigger === 'left' && button === 0) || (root.trigger === 'right' && button === 2)); + + // find the element that would've been clicked, wasn't the layer in the way + if (document.elementFromPoint && root.$layer) { + root.$layer.hide(); + target = document.elementFromPoint(x - $win.scrollLeft(), y - $win.scrollTop()); + + // also need to try and focus this element if we're in a contenteditable area, + // as the layer will prevent the browser mouse action we want + if (target !== null && target.isContentEditable) { + var range = document.createRange(), + sel = window.getSelection(); + range.selectNode(target); + range.collapse(true); + sel.removeAllRanges(); + sel.addRange(range); + } + $(target).trigger(e); + root.$layer.show(); + } + + if (root.hideOnSecondTrigger && triggerAction && root.$menu !== null && typeof root.$menu !== 'undefined') { + root.$menu.trigger('contextmenu:hide'); + return; + } + + if (root.reposition && triggerAction) { + if (document.elementFromPoint) { + if (root.$trigger.is(target)) { + root.position.call(root.$trigger, root, x, y); + return; + } + } else { + offset = root.$trigger.offset(); + $window = $(window); + // while this looks kinda awful, it's the best way to avoid + // unnecessarily calculating any positions + offset.top += $window.scrollTop(); + if (offset.top <= e.pageY) { + offset.left += $window.scrollLeft(); + if (offset.left <= e.pageX) { + offset.bottom = offset.top + root.$trigger.outerHeight(); + if (offset.bottom >= e.pageY) { + offset.right = offset.left + root.$trigger.outerWidth(); + if (offset.right >= e.pageX) { + // reposition + root.position.call(root.$trigger, root, x, y); + return; + } + } + } + } + } + } + + if (target && triggerAction) { + root.$trigger.one('contextmenu:hidden', function () { + $(target).contextMenu({x: x, y: y, button: button}); + }); + } + + if (root !== null && typeof root !== 'undefined' && root.$menu !== null && typeof root.$menu !== 'undefined') { + root.$menu.trigger('contextmenu:hide'); + } + }, 50); + }, + // key handled :hover + keyStop: function (e, opt) { + if (!opt.isInput) { + e.preventDefault(); + } + + e.stopPropagation(); + }, + key: function (e) { + + var opt = {}; + + // Only get the data from $currentTrigger if it exists + if ($currentTrigger) { + opt = $currentTrigger.data('contextMenu') || {}; + } + // If the trigger happen on a element that are above the contextmenu do this + if (typeof opt.zIndex === 'undefined') { + opt.zIndex = 0; + } + var targetZIndex = 0; + var getZIndexOfTriggerTarget = function (target) { + if (target.style.zIndex !== '') { + targetZIndex = target.style.zIndex; + } else { + if (target.offsetParent !== null && typeof target.offsetParent !== 'undefined') { + getZIndexOfTriggerTarget(target.offsetParent); + } + else if (target.parentElement !== null && typeof target.parentElement !== 'undefined') { + getZIndexOfTriggerTarget(target.parentElement); + } + } + }; + getZIndexOfTriggerTarget(e.target); + // If targetZIndex is heigher then opt.zIndex dont progress any futher. + // This is used to make sure that if you are using a dialog with a input / textarea / contenteditable div + // and its above the contextmenu it wont steal keys events + if (opt.$menu && parseInt(targetZIndex,10) > parseInt(opt.$menu.css("zIndex"),10)) { + return; + } + switch (e.keyCode) { + case 9: + case 38: // up + handle.keyStop(e, opt); + // if keyCode is [38 (up)] or [9 (tab) with shift] + if (opt.isInput) { + if (e.keyCode === 9 && e.shiftKey) { + e.preventDefault(); + if (opt.$selected) { + opt.$selected.find('input, textarea, select').blur(); + } + if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { + opt.$menu.trigger('prevcommand'); + } + return; + } else if (e.keyCode === 38 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') { + // checkboxes don't capture this key + e.preventDefault(); + return; + } + } else if (e.keyCode !== 9 || e.shiftKey) { + if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { + opt.$menu.trigger('prevcommand'); + } + return; + } + break; + // omitting break; + // case 9: // tab - reached through omitted break; + case 40: // down + handle.keyStop(e, opt); + if (opt.isInput) { + if (e.keyCode === 9) { + e.preventDefault(); + if (opt.$selected) { + opt.$selected.find('input, textarea, select').blur(); + } + if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { + opt.$menu.trigger('nextcommand'); + } + return; + } else if (e.keyCode === 40 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') { + // checkboxes don't capture this key + e.preventDefault(); + return; + } + } else { + if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { + opt.$menu.trigger('nextcommand'); + } + return; + } + break; + + case 37: // left + handle.keyStop(e, opt); + if (opt.isInput || !opt.$selected || !opt.$selected.length) { + break; + } + + if (!opt.$selected.parent().hasClass('context-menu-root')) { + var $parent = opt.$selected.parent().parent(); + opt.$selected.trigger('contextmenu:blur'); + opt.$selected = $parent; + return; + } + break; + + case 39: // right + handle.keyStop(e, opt); + if (opt.isInput || !opt.$selected || !opt.$selected.length) { + break; + } + + var itemdata = opt.$selected.data('contextMenu') || {}; + if (itemdata.$menu && opt.$selected.hasClass('context-menu-submenu')) { + opt.$selected = null; + itemdata.$selected = null; + itemdata.$menu.trigger('nextcommand'); + return; + } + break; + + case 35: // end + case 36: // home + if (opt.$selected && opt.$selected.find('input, textarea, select').length) { + return; + } else { + (opt.$selected && opt.$selected.parent() || opt.$menu) + .children(':not(.' + opt.classNames.disabled + ', .' + opt.classNames.notSelectable + ')')[e.keyCode === 36 ? 'first' : 'last']() + .trigger('contextmenu:focus'); + e.preventDefault(); + return; + } + break; + + case 13: // enter + handle.keyStop(e, opt); + if (opt.isInput) { + if (opt.$selected && !opt.$selected.is('textarea, select')) { + e.preventDefault(); + return; + } + break; + } + if (typeof opt.$selected !== 'undefined' && opt.$selected !== null) { + opt.$selected.trigger('mouseup'); + } + return; + + case 32: // space + case 33: // page up + case 34: // page down + // prevent browser from scrolling down while menu is visible + handle.keyStop(e, opt); + return; + + case 27: // esc + handle.keyStop(e, opt); + if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { + opt.$menu.trigger('contextmenu:hide'); + } + return; + + default: // 0-9, a-z + var k = (String.fromCharCode(e.keyCode)).toUpperCase(); + if (opt.accesskeys && opt.accesskeys[k]) { + // according to the specs accesskeys must be invoked immediately + opt.accesskeys[k].$node.trigger(opt.accesskeys[k].$menu ? 'contextmenu:focus' : 'mouseup'); + return; + } + break; + } + // pass event to selected item, + // stop propagation to avoid endless recursion + e.stopPropagation(); + if (typeof opt.$selected !== 'undefined' && opt.$selected !== null) { + opt.$selected.trigger(e); + } + }, + // select previous possible command in menu + prevItem: function (e) { + e.stopPropagation(); + var opt = $(this).data('contextMenu') || {}; + var root = $(this).data('contextMenuRoot') || {}; + + // obtain currently selected menu + if (opt.$selected) { + var $s = opt.$selected; + opt = opt.$selected.parent().data('contextMenu') || {}; + opt.$selected = $s; + } + + var $children = opt.$menu.children(), + $prev = !opt.$selected || !opt.$selected.prev().length ? $children.last() : opt.$selected.prev(), + $round = $prev; + + // skip disabled or hidden elements + while ($prev.hasClass(root.classNames.disabled) || $prev.hasClass(root.classNames.notSelectable) || $prev.is(':hidden')) { + if ($prev.prev().length) { + $prev = $prev.prev(); + } else { + $prev = $children.last(); + } + if ($prev.is($round)) { + // break endless loop + return; + } + } + + // leave current + if (opt.$selected) { + handle.itemMouseleave.call(opt.$selected.get(0), e); + } + + // activate next + handle.itemMouseenter.call($prev.get(0), e); + + // focus input + var $input = $prev.find('input, textarea, select'); + if ($input.length) { + $input.focus(); + } + }, + // select next possible command in menu + nextItem: function (e) { + e.stopPropagation(); + var opt = $(this).data('contextMenu') || {}; + var root = $(this).data('contextMenuRoot') || {}; + + // obtain currently selected menu + if (opt.$selected) { + var $s = opt.$selected; + opt = opt.$selected.parent().data('contextMenu') || {}; + opt.$selected = $s; + } + + var $children = opt.$menu.children(), + $next = !opt.$selected || !opt.$selected.next().length ? $children.first() : opt.$selected.next(), + $round = $next; + + // skip disabled + while ($next.hasClass(root.classNames.disabled) || $next.hasClass(root.classNames.notSelectable) || $next.is(':hidden')) { + if ($next.next().length) { + $next = $next.next(); + } else { + $next = $children.first(); + } + if ($next.is($round)) { + // break endless loop + return; + } + } + + // leave current + if (opt.$selected) { + handle.itemMouseleave.call(opt.$selected.get(0), e); + } + + // activate next + handle.itemMouseenter.call($next.get(0), e); + + // focus input + var $input = $next.find('input, textarea, select'); + if ($input.length) { + $input.focus(); + } + }, + // flag that we're inside an input so the key handler can act accordingly + focusInput: function () { + var $this = $(this).closest('.context-menu-item'), + data = $this.data(), + opt = data.contextMenu, + root = data.contextMenuRoot; + + root.$selected = opt.$selected = $this; + root.isInput = opt.isInput = true; + }, + // flag that we're inside an input so the key handler can act accordingly + blurInput: function () { + var $this = $(this).closest('.context-menu-item'), + data = $this.data(), + opt = data.contextMenu, + root = data.contextMenuRoot; + + root.isInput = opt.isInput = false; + }, + // :hover on menu + menuMouseenter: function () { + var root = $(this).data().contextMenuRoot; + root.hovering = true; + }, + // :hover on menu + menuMouseleave: function (e) { + var root = $(this).data().contextMenuRoot; + if (root.$layer && root.$layer.is(e.relatedTarget)) { + root.hovering = false; + } + }, + // :hover done manually so key handling is possible + itemMouseenter: function (e) { + var $this = $(this), + data = $this.data(), + opt = data.contextMenu, + root = data.contextMenuRoot; + + root.hovering = true; + + // abort if we're re-entering + if (e && root.$layer && root.$layer.is(e.relatedTarget)) { + e.preventDefault(); + e.stopImmediatePropagation(); + } + + // make sure only one item is selected + (opt.$menu ? opt : root).$menu + .children('.' + root.classNames.hover).trigger('contextmenu:blur') + .children('.hover').trigger('contextmenu:blur'); + + if ($this.hasClass(root.classNames.disabled) || $this.hasClass(root.classNames.notSelectable)) { + opt.$selected = null; + return; + } + + + $this.trigger('contextmenu:focus'); + }, + // :hover done manually so key handling is possible + itemMouseleave: function (e) { + var $this = $(this), + data = $this.data(), + opt = data.contextMenu, + root = data.contextMenuRoot; + + if (root !== opt && root.$layer && root.$layer.is(e.relatedTarget)) { + if (typeof root.$selected !== 'undefined' && root.$selected !== null) { + root.$selected.trigger('contextmenu:blur'); + } + e.preventDefault(); + e.stopImmediatePropagation(); + root.$selected = opt.$selected = opt.$node; + return; + } + + if(opt && opt.$menu && opt.$menu.hasClass('context-menu-visible')){ + return; + } + + $this.trigger('contextmenu:blur'); + }, + // contextMenu item click + itemClick: function (e) { + var $this = $(this), + data = $this.data(), + opt = data.contextMenu, + root = data.contextMenuRoot, + key = data.contextMenuKey, + callback; + + // abort if the key is unknown or disabled or is a menu + if (!opt.items[key] || $this.is('.' + root.classNames.disabled + ', .context-menu-separator, .' + root.classNames.notSelectable) || ($this.is('.context-menu-submenu') && root.selectableSubMenu === false )) { + return; + } + + e.preventDefault(); + e.stopImmediatePropagation(); + + if ($.isFunction(opt.callbacks[key]) && Object.prototype.hasOwnProperty.call(opt.callbacks, key)) { + // item-specific callback + callback = opt.callbacks[key]; + } else if ($.isFunction(root.callback)) { + // default callback + callback = root.callback; + } else { + // no callback, no action + return; + } + + // hide menu if callback doesn't stop that + if (callback.call(root.$trigger, key, root, e) !== false) { + root.$menu.trigger('contextmenu:hide'); + } else if (root.$menu.parent().length) { + op.update.call(root.$trigger, root); + } + }, + // ignore click events on input elements + inputClick: function (e) { + e.stopImmediatePropagation(); + }, + // hide + hideMenu: function (e, data) { + var root = $(this).data('contextMenuRoot'); + op.hide.call(root.$trigger, root, data && data.force); + }, + // focus + focusItem: function (e) { + e.stopPropagation(); + var $this = $(this), + data = $this.data(), + opt = data.contextMenu, + root = data.contextMenuRoot; + + if ($this.hasClass(root.classNames.disabled) || $this.hasClass(root.classNames.notSelectable)) { + return; + } + + $this + .addClass([root.classNames.hover, root.classNames.visible].join(' ')) + // select other items and included items + .parent().find('.context-menu-item').not($this) + .removeClass(root.classNames.visible) + .filter('.' + root.classNames.hover) + .trigger('contextmenu:blur'); + + // remember selected + opt.$selected = root.$selected = $this; + + + if(opt && opt.$node && opt.$node.hasClass('context-menu-submenu')){ + opt.$node.addClass(root.classNames.hover); + } + + // position sub-menu - do after show so dumb $.ui.position can keep up + if (opt.$node) { + root.positionSubmenu.call(opt.$node, opt.$menu); + } + }, + // blur + blurItem: function (e) { + e.stopPropagation(); + var $this = $(this), + data = $this.data(), + opt = data.contextMenu, + root = data.contextMenuRoot; + + if (opt.autoHide) { // for tablets and touch screens this needs to remain + $this.removeClass(root.classNames.visible); + } + $this.removeClass(root.classNames.hover); + opt.$selected = null; + } + }, + // operations + op = { + show: function (opt, x, y) { + var $trigger = $(this), + css = {}; + + // hide any open menus + $('#context-menu-layer').trigger('mousedown'); + + // backreference for callbacks + opt.$trigger = $trigger; + + // show event + if (opt.events.show.call($trigger, opt) === false) { + $currentTrigger = null; + return; + } + + // create or update context menu + var hasVisibleItems = op.update.call($trigger, opt); + if (hasVisibleItems === false) { + $currentTrigger = null; + return; + } + + // position menu + opt.position.call($trigger, opt, x, y); + + // make sure we're in front + if (opt.zIndex) { + var additionalZValue = opt.zIndex; + // If opt.zIndex is a function, call the function to get the right zIndex. + if (typeof opt.zIndex === 'function') { + additionalZValue = opt.zIndex.call($trigger, opt); + } + css.zIndex = zindex($trigger) + additionalZValue; + } + + // add layer + op.layer.call(opt.$menu, opt, css.zIndex); + + // adjust sub-menu zIndexes + opt.$menu.find('ul').css('zIndex', css.zIndex + 1); + + // position and show context menu + opt.$menu.css(css)[opt.animation.show](opt.animation.duration, function () { + $trigger.trigger('contextmenu:visible'); + + op.activated(opt); + opt.events.activated(opt); + }); + // make options available and set state + $trigger + .data('contextMenu', opt) + .addClass('context-menu-active'); + + // register key handler + $(document).off('keydown.contextMenu').on('keydown.contextMenu', handle.key); + // register autoHide handler + if (opt.autoHide) { + // mouse position handler + $(document).on('mousemove.contextMenuAutoHide', function (e) { + // need to capture the offset on mousemove, + // since the page might've been scrolled since activation + var pos = $trigger.offset(); + pos.right = pos.left + $trigger.outerWidth(); + pos.bottom = pos.top + $trigger.outerHeight(); + + if (opt.$layer && !opt.hovering && (!(e.pageX >= pos.left && e.pageX <= pos.right) || !(e.pageY >= pos.top && e.pageY <= pos.bottom))) { + /* Additional hover check after short time, you might just miss the edge of the menu */ + setTimeout(function () { + if (!opt.hovering && opt.$menu !== null && typeof opt.$menu !== 'undefined') { + opt.$menu.trigger('contextmenu:hide'); + } + }, 50); + } + }); + } + }, + hide: function (opt, force) { + var $trigger = $(this); + if (!opt) { + opt = $trigger.data('contextMenu') || {}; + } + + // hide event + if (!force && opt.events && opt.events.hide.call($trigger, opt) === false) { + return; + } + + // remove options and revert state + $trigger + .removeData('contextMenu') + .removeClass('context-menu-active'); + + if (opt.$layer) { + // keep layer for a bit so the contextmenu event can be aborted properly by opera + setTimeout((function ($layer) { + return function () { + $layer.remove(); + }; + })(opt.$layer), 10); + + try { + delete opt.$layer; + } catch (e) { + opt.$layer = null; + } + } + + // remove handle + $currentTrigger = null; + // remove selected + opt.$menu.find('.' + opt.classNames.hover).trigger('contextmenu:blur'); + opt.$selected = null; + // collapse all submenus + opt.$menu.find('.' + opt.classNames.visible).removeClass(opt.classNames.visible); + // unregister key and mouse handlers + // $(document).off('.contextMenuAutoHide keydown.contextMenu'); // http://bugs.jquery.com/ticket/10705 + $(document).off('.contextMenuAutoHide').off('keydown.contextMenu'); + // hide menu + if (opt.$menu) { + opt.$menu[opt.animation.hide](opt.animation.duration, function () { + // tear down dynamically built menu after animation is completed. + if (opt.build) { + opt.$menu.remove(); + $.each(opt, function (key) { + switch (key) { + case 'ns': + case 'selector': + case 'build': + case 'trigger': + return true; + + default: + opt[key] = undefined; + try { + delete opt[key]; + } catch (e) { + } + return true; + } + }); + } + + setTimeout(function () { + $trigger.trigger('contextmenu:hidden'); + }, 10); + }); + } + }, + create: function (opt, root) { + if (typeof root === 'undefined') { + root = opt; + } + + // create contextMenu + opt.$menu = $('
    ').addClass(opt.className || '').data({ + 'contextMenu': opt, + 'contextMenuRoot': root + }); + if(opt.dataAttr){ + $.each(opt.dataAttr, function (key, item) { + opt.$menu.attr('data-' + opt.key, item); + }); + } + + $.each(['callbacks', 'commands', 'inputs'], function (i, k) { + opt[k] = {}; + if (!root[k]) { + root[k] = {}; + } + }); + + if (!root.accesskeys) { + root.accesskeys = {}; + } + + function createNameNode(item) { + var $name = $(''); + if (item._accesskey) { + if (item._beforeAccesskey) { + $name.append(document.createTextNode(item._beforeAccesskey)); + } + $('') + .addClass('context-menu-accesskey') + .text(item._accesskey) + .appendTo($name); + if (item._afterAccesskey) { + $name.append(document.createTextNode(item._afterAccesskey)); + } + } else { + if (item.isHtmlName) { + // restrict use with access keys + if (typeof item.accesskey !== 'undefined') { + throw new Error('accesskeys are not compatible with HTML names and cannot be used together in the same item'); + } + $name.html(item.name); + } else { + $name.text(item.name); + } + } + return $name; + } + + // create contextMenu items + $.each(opt.items, function (key, item) { + var $t = $('
  • ').addClass(item.className || ''), + $label = null, + $input = null; + + // iOS needs to see a click-event bound to an element to actually + // have the TouchEvents infrastructure trigger the click event + $t.on('click', $.noop); + + // Make old school string seperator a real item so checks wont be + // akward later. + // And normalize 'cm_separator' into 'cm_seperator'. + if (typeof item === 'string' || item.type === 'cm_separator') { + item = {type: 'cm_seperator'}; + } + + item.$node = $t.data({ + 'contextMenu': opt, + 'contextMenuRoot': root, + 'contextMenuKey': key + }); + + // register accesskey + // NOTE: the accesskey attribute should be applicable to any element, but Safari5 and Chrome13 still can't do that + if (typeof item.accesskey !== 'undefined') { + var aks = splitAccesskey(item.accesskey); + for (var i = 0, ak; ak = aks[i]; i++) { + if (!root.accesskeys[ak]) { + root.accesskeys[ak] = item; + var matched = item.name.match(new RegExp('^(.*?)(' + ak + ')(.*)$', 'i')); + if (matched) { + item._beforeAccesskey = matched[1]; + item._accesskey = matched[2]; + item._afterAccesskey = matched[3]; + } + break; + } + } + } + + if (item.type && types[item.type]) { + // run custom type handler + types[item.type].call($t, item, opt, root); + // register commands + $.each([opt, root], function (i, k) { + k.commands[key] = item; + // Overwrite only if undefined or the item is appended to the root. This so it + // doesn't overwrite callbacks of root elements if the name is the same. + if ($.isFunction(item.callback) && (typeof k.callbacks[key] === 'undefined' || typeof opt.type === 'undefined')) { + k.callbacks[key] = item.callback; + } + }); + } else { + // add label for input + if (item.type === 'cm_seperator') { + $t.addClass('context-menu-separator ' + root.classNames.notSelectable); + } else if (item.type === 'html') { + $t.addClass('context-menu-html ' + root.classNames.notSelectable); + } else if (item.type !== 'sub' && item.type) { + $label = $('').appendTo($t); + createNameNode(item).appendTo($label); + + $t.addClass('context-menu-input'); + opt.hasTypes = true; + $.each([opt, root], function (i, k) { + k.commands[key] = item; + k.inputs[key] = item; + }); + } else if (item.items) { + item.type = 'sub'; + } + + switch (item.type) { + case 'cm_seperator': + break; + + case 'text': + $input = $('') + .attr('name', 'context-menu-input-' + key) + .val(item.value || '') + .appendTo($label); + break; + + case 'textarea': + $input = $('') + .attr('name', 'context-menu-input-' + key) + .val(item.value || '') + .appendTo($label); + + if (item.height) { + $input.height(item.height); + } + break; + + case 'checkbox': + $input = $('') + .attr('name', 'context-menu-input-' + key) + .val(item.value || '') + .prop('checked', !!item.selected) + .prependTo($label); + break; + + case 'radio': + $input = $('') + .attr('name', 'context-menu-input-' + item.radio) + .val(item.value || '') + .prop('checked', !!item.selected) + .prependTo($label); + break; + + case 'select': + $input = $('') + .attr('name', 'context-menu-input-' + key) + .appendTo($label); + if (item.options) { + $.each(item.options, function (value, text) { + $('').val(value).text(text).appendTo($input); + }); + $input.val(item.selected); + } + break; + + case 'sub': + createNameNode(item).appendTo($t); + item.appendTo = item.$node; + $t.data('contextMenu', item).addClass('context-menu-submenu'); + item.callback = null; + + // If item contains items, and this is a promise, we should create it later + // check if subitems is of type promise. If it is a promise we need to create + // it later, after promise has been resolved. + if ('function' === typeof item.items.then) { + // probably a promise, process it, when completed it will create the sub menu's. + op.processPromises(item, root, item.items); + } else { + // normal submenu. + op.create(item, root); + } + break; + + case 'html': + $(item.html).appendTo($t); + break; + + default: + $.each([opt, root], function (i, k) { + k.commands[key] = item; + // Overwrite only if undefined or the item is appended to the root. This so it + // doesn't overwrite callbacks of root elements if the name is the same. + if ($.isFunction(item.callback) && (typeof k.callbacks[key] === 'undefined' || typeof opt.type === 'undefined')) { + k.callbacks[key] = item.callback; + } + }); + createNameNode(item).appendTo($t); + break; + } + + // disable key listener in + if (item.type && item.type !== 'sub' && item.type !== 'html' && item.type !== 'cm_seperator') { + $input + .on('focus', handle.focusInput) + .on('blur', handle.blurInput); + + if (item.events) { + $input.on(item.events, opt); + } + } + + // add icons + if (item.icon) { + if ($.isFunction(item.icon)) { + item._icon = item.icon.call(this, this, $t, key, item); + } else { + if (typeof(item.icon) === 'string' && ( + item.icon.substring(0, 4) === 'fab ' + || item.icon.substring(0, 4) === 'fas ' + || item.icon.substring(0, 4) === 'fad ' + || item.icon.substring(0, 4) === 'far ' + || item.icon.substring(0, 4) === 'fal ') + ) { + // to enable font awesome + $t.addClass(root.classNames.icon + ' ' + root.classNames.icon + '--fa5'); + item._icon = $(''); + } else if (typeof(item.icon) === 'string' && item.icon.substring(0, 3) === 'fa-') { + item._icon = root.classNames.icon + ' ' + root.classNames.icon + '--fa fa ' + item.icon; + } else { + item._icon = root.classNames.icon + ' ' + root.classNames.icon + '-' + item.icon; + } + } + + if(typeof(item._icon) === "string"){ + $t.addClass(item._icon); + } else { + $t.prepend(item._icon); + } + } + } + + // cache contained elements + item.$input = $input; + item.$label = $label; + + // attach item to menu + $t.appendTo(opt.$menu); + + // Disable text selection + if (!opt.hasTypes && $.support.eventSelectstart) { + // browsers support user-select: none, + // IE has a special event for text-selection + // browsers supporting neither will not be preventing text-selection + $t.on('selectstart.disableTextSelect', handle.abortevent); + } + }); + // attach contextMenu to (to bypass any possible overflow:hidden issues on parents of the trigger element) + if (!opt.$node) { + opt.$menu.css('display', 'none').addClass('context-menu-root'); + } + opt.$menu.appendTo(opt.appendTo || document.body); + }, + resize: function ($menu, nested) { + var domMenu; + // determine widths of submenus, as CSS won't grow them automatically + // position:absolute within position:absolute; min-width:100; max-width:200; results in width: 100; + // kinda sucks hard... + + // determine width of absolutely positioned element + $menu.css({position: 'absolute', display: 'block'}); + // don't apply yet, because that would break nested elements' widths + $menu.data('width', + (domMenu = $menu.get(0)).getBoundingClientRect ? + Math.ceil(domMenu.getBoundingClientRect().width) : + $menu.outerWidth() + 1); // outerWidth() returns rounded pixels + // reset styles so they allow nested elements to grow/shrink naturally + $menu.css({ + position: 'static', + minWidth: '0px', + maxWidth: '100000px' + }); + // identify width of nested menus + $menu.find('> li > ul').each(function () { + op.resize($(this), true); + }); + // reset and apply changes in the end because nested + // elements' widths wouldn't be calculatable otherwise + if (!nested) { + $menu.find('ul').addBack().css({ + position: '', + display: '', + minWidth: '', + maxWidth: '' + }).outerWidth(function () { + return $(this).data('width'); + }); + } + }, + update: function (opt, root) { + var $trigger = this; + if (typeof root === 'undefined') { + root = opt; + op.resize(opt.$menu); + } + + var hasVisibleItems = false; + + // re-check disabled for each item + opt.$menu.children().each(function () { + var $item = $(this), + key = $item.data('contextMenuKey'), + item = opt.items[key], + disabled = ($.isFunction(item.disabled) && item.disabled.call($trigger, key, root)) || item.disabled === true, + visible; + if ($.isFunction(item.visible)) { + visible = item.visible.call($trigger, key, root); + } else if (typeof item.visible !== 'undefined') { + visible = item.visible === true; + } else { + visible = true; + } + + if (visible) { + hasVisibleItems = true; + } + + $item[visible ? 'show' : 'hide'](); + + // dis- / enable item + $item[disabled ? 'addClass' : 'removeClass'](root.classNames.disabled); + + if ($.isFunction(item.icon)) { + $item.removeClass(item._icon); + var iconResult = item.icon.call(this, $trigger, $item, key, item); + if(typeof(iconResult) === "string"){ + $item.addClass(iconResult); + } else { + $item.prepend(iconResult); + } + } + + if (item.type) { + // dis- / enable input elements + $item.find('input, select, textarea').prop('disabled', disabled); + + // update input states + switch (item.type) { + case 'text': + case 'textarea': + item.$input.val(item.value || ''); + break; + + case 'checkbox': + case 'radio': + item.$input.val(item.value || '').prop('checked', !!item.selected); + break; + + case 'select': + item.$input.val((item.selected === 0 ? "0" : item.selected) || ''); + break; + } + } + + if (item.$menu) { + // update sub-menu + var subMenuHasVisibleItems = op.update.call($trigger, item, root); + if (subMenuHasVisibleItems) { + hasVisibleItems = true; + } + } + }); + return hasVisibleItems; + }, + layer: function (opt, zIndex) { + // add transparent layer for click area + // filter and background for Internet Explorer, Issue #23 + var $layer = opt.$layer = $('
    ') + .css({ + height: $win.height(), + width: $win.width(), + display: 'block', + position: 'fixed', + 'z-index': zIndex - 1, + top: 0, + left: 0, + opacity: 0, + filter: 'alpha(opacity=0)', + 'background-color': '#000' + }) + .data('contextMenuRoot', opt) + .appendTo(document.body) + .on('contextmenu', handle.abortevent) + .on('mousedown', handle.layerClick); + + // IE6 doesn't know position:fixed; + if (typeof document.body.style.maxWidth === 'undefined') { // IE6 doesn't support maxWidth + $layer.css({ + 'position': 'absolute', + 'height': $(document).height() + }); + } + + return $layer; + }, + processPromises: function (opt, root, promise) { + // Start + opt.$node.addClass(root.classNames.iconLoadingClass); + + function completedPromise(opt, root, items) { + // Completed promise (dev called promise.resolve). We now have a list of items which can + // be used to create the rest of the context menu. + if (typeof items === 'undefined') { + // Null result, dev should have checked + errorPromise(undefined);//own error object + } + finishPromiseProcess(opt, root, items); + } + + function errorPromise(opt, root, errorItem) { + // User called promise.reject() with an error item, if not, provide own error item. + if (typeof errorItem === 'undefined') { + errorItem = { + "error": { + name: "No items and no error item", + icon: "context-menu-icon context-menu-icon-quit" + } + }; + if (window.console) { + (console.error || console.log).call(console, 'When you reject a promise, provide an "items" object, equal to normal sub-menu items'); + } + } else if (typeof errorItem === 'string') { + errorItem = {"error": {name: errorItem}}; + } + finishPromiseProcess(opt, root, errorItem); + } + + function finishPromiseProcess(opt, root, items) { + if (typeof root.$menu === 'undefined' || !root.$menu.is(':visible')) { + return; + } + opt.$node.removeClass(root.classNames.iconLoadingClass); + opt.items = items; + op.create(opt, root, true); // Create submenu + op.update(opt, root); // Correctly update position if user is already hovered over menu item + root.positionSubmenu.call(opt.$node, opt.$menu); // positionSubmenu, will only do anything if user already hovered over menu item that just got new subitems. + } + + // Wait for promise completion. .then(success, error, notify) (we don't track notify). Bind the opt + // and root to avoid scope problems + promise.then(completedPromise.bind(this, opt, root), errorPromise.bind(this, opt, root)); + }, + // operation that will run after contextMenu showed on screen + activated: function(opt){ + var $menu = opt.$menu; + var $menuOffset = $menu.offset(); + var winHeight = $(window).height(); + var winScrollTop = $(window).scrollTop(); + var menuHeight = $menu.height(); + if(menuHeight > winHeight){ + $menu.css({ + 'height' : winHeight + 'px', + 'overflow-x': 'hidden', + 'overflow-y': 'auto', + 'top': winScrollTop + 'px' + }); + } else if(($menuOffset.top < winScrollTop) || ($menuOffset.top + menuHeight > winScrollTop + winHeight)){ + $menu.css({ + 'top': winScrollTop + 'px' + }); + } + } + }; + + // split accesskey according to http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#assigned-access-key + function splitAccesskey(val) { + var t = val.split(/\s+/); + var keys = []; + + for (var i = 0, k; k = t[i]; i++) { + k = k.charAt(0).toUpperCase(); // first character only + // theoretically non-accessible characters should be ignored, but different systems, different keyboard layouts, ... screw it. + // a map to look up already used access keys would be nice + keys.push(k); + } + + return keys; + } + +// handle contextMenu triggers + $.fn.contextMenu = function (operation) { + var $t = this, $o = operation; + if (this.length > 0) { // this is not a build on demand menu + if (typeof operation === 'undefined') { + this.first().trigger('contextmenu'); + } else if (typeof operation.x !== 'undefined' && typeof operation.y !== 'undefined') { + this.first().trigger($.Event('contextmenu', { + pageX: operation.x, + pageY: operation.y, + mouseButton: operation.button + })); + } else if (operation === 'hide') { + var $menu = this.first().data('contextMenu') ? this.first().data('contextMenu').$menu : null; + if ($menu) { + $menu.trigger('contextmenu:hide'); + } + } else if (operation === 'destroy') { + $.contextMenu('destroy', {context: this}); + } else if ($.isPlainObject(operation)) { + operation.context = this; + $.contextMenu('create', operation); + } else if (operation) { + this.removeClass('context-menu-disabled'); + } else if (!operation) { + this.addClass('context-menu-disabled'); + } + } else { + $.each(menus, function () { + if (this.selector === $t.selector) { + $o.data = this; + + $.extend($o.data, {trigger: 'demand'}); + } + }); + + handle.contextmenu.call($o.target, $o); + } + + return this; + }; + + // manage contextMenu instances + $.contextMenu = function (operation, options) { + if (typeof operation !== 'string') { + options = operation; + operation = 'create'; + } + + if (typeof options === 'string') { + options = {selector: options}; + } else if (typeof options === 'undefined') { + options = {}; + } + + // merge with default options + var o = $.extend(true, {}, defaults, options || {}); + var $document = $(document); + var $context = $document; + var _hasContext = false; + + if (!o.context || !o.context.length) { + o.context = document; + } else { + // you never know what they throw at you... + $context = $(o.context).first(); + o.context = $context.get(0); + _hasContext = !$(o.context).is(document); + } + + switch (operation) { + + case 'update': + // Updates visibility and such + if(_hasContext){ + op.update($context); + } else { + for(var menu in menus){ + if(menus.hasOwnProperty(menu)){ + op.update(menus[menu]); + } + } + } + break; + + case 'create': + // no selector no joy + if (!o.selector) { + throw new Error('No selector specified'); + } + // make sure internal classes are not bound to + if (o.selector.match(/.context-menu-(list|item|input)($|\s)/)) { + throw new Error('Cannot bind to selector "' + o.selector + '" as it contains a reserved className'); + } + if (!o.build && (!o.items || $.isEmptyObject(o.items))) { + throw new Error('No Items specified'); + } + counter++; + o.ns = '.contextMenu' + counter; + if (!_hasContext) { + namespaces[o.selector] = o.ns; + } + menus[o.ns] = o; + + // default to right click + if (!o.trigger) { + o.trigger = 'right'; + } + + if (!initialized) { + var itemClick = o.itemClickEvent === 'click' ? 'click.contextMenu' : 'mouseup.contextMenu'; + var contextMenuItemObj = { + // 'mouseup.contextMenu': handle.itemClick, + // 'click.contextMenu': handle.itemClick, + 'contextmenu:focus.contextMenu': handle.focusItem, + 'contextmenu:blur.contextMenu': handle.blurItem, + 'contextmenu.contextMenu': handle.abortevent, + 'mouseenter.contextMenu': handle.itemMouseenter, + 'mouseleave.contextMenu': handle.itemMouseleave + }; + contextMenuItemObj[itemClick] = handle.itemClick; + // make sure item click is registered first + $document + .on({ + 'contextmenu:hide.contextMenu': handle.hideMenu, + 'prevcommand.contextMenu': handle.prevItem, + 'nextcommand.contextMenu': handle.nextItem, + 'contextmenu.contextMenu': handle.abortevent, + 'mouseenter.contextMenu': handle.menuMouseenter, + 'mouseleave.contextMenu': handle.menuMouseleave + }, '.context-menu-list') + .on('mouseup.contextMenu', '.context-menu-input', handle.inputClick) + .on(contextMenuItemObj, '.context-menu-item'); + + initialized = true; + } + + // engage native contextmenu event + $context + .on('contextmenu' + o.ns, o.selector, o, handle.contextmenu); + + if (_hasContext) { + // add remove hook, just in case + $context.on('remove' + o.ns, function () { + $(this).contextMenu('destroy'); + }); + } + + switch (o.trigger) { + case 'hover': + $context + .on('mouseenter' + o.ns, o.selector, o, handle.mouseenter) + .on('mouseleave' + o.ns, o.selector, o, handle.mouseleave); + break; + + case 'left': + $context.on('click' + o.ns, o.selector, o, handle.click); + break; + case 'touchstart': + $context.on('touchstart' + o.ns, o.selector, o, handle.click); + break; + /* + default: + // http://www.quirksmode.org/dom/events/contextmenu.html + $document + .on('mousedown' + o.ns, o.selector, o, handle.mousedown) + .on('mouseup' + o.ns, o.selector, o, handle.mouseup); + break; + */ + } + + // create menu + if (!o.build) { + op.create(o); + } + break; + + case 'destroy': + var $visibleMenu; + if (_hasContext) { + // get proper options + var context = o.context; + $.each(menus, function (ns, o) { + + if (!o) { + return true; + } + + // Is this menu equest to the context called from + if (!$(context).is(o.selector)) { + return true; + } + + $visibleMenu = $('.context-menu-list').filter(':visible'); + if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is($(o.context).find(o.selector))) { + $visibleMenu.trigger('contextmenu:hide', {force: true}); + } + + try { + if (menus[o.ns].$menu) { + menus[o.ns].$menu.remove(); + } + + delete menus[o.ns]; + } catch (e) { + menus[o.ns] = null; + } + + $(o.context).off(o.ns); + + return true; + }); + } else if (!o.selector) { + $document.off('.contextMenu .contextMenuAutoHide'); + $.each(menus, function (ns, o) { + $(o.context).off(o.ns); + }); + + namespaces = {}; + menus = {}; + counter = 0; + initialized = false; + + $('#context-menu-layer, .context-menu-list').remove(); + } else if (namespaces[o.selector]) { + $visibleMenu = $('.context-menu-list').filter(':visible'); + if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is(o.selector)) { + $visibleMenu.trigger('contextmenu:hide', {force: true}); + } + + try { + if (menus[namespaces[o.selector]].$menu) { + menus[namespaces[o.selector]].$menu.remove(); + } + + delete menus[namespaces[o.selector]]; + } catch (e) { + menus[namespaces[o.selector]] = null; + } + + $document.off(namespaces[o.selector]); + } + break; + + case 'html5': + // if and are not handled by the browser, + // or options was a bool true, + // initialize $.contextMenu for them + if ((!$.support.htmlCommand && !$.support.htmlMenuitem) || (typeof options === 'boolean' && options)) { + $('menu[type="context"]').each(function () { + if (this.id) { + $.contextMenu({ + selector: '[contextmenu=' + this.id + ']', + items: $.contextMenu.fromMenu(this) + }); + } + }).css('display', 'none'); + } + break; + + default: + throw new Error('Unknown operation "' + operation + '"'); + } + + return this; + }; + +// import values into commands + $.contextMenu.setInputValues = function (opt, data) { + if (typeof data === 'undefined') { + data = {}; + } + + $.each(opt.inputs, function (key, item) { + switch (item.type) { + case 'text': + case 'textarea': + item.value = data[key] || ''; + break; + + case 'checkbox': + item.selected = data[key] ? true : false; + break; + + case 'radio': + item.selected = (data[item.radio] || '') === item.value; + break; + + case 'select': + item.selected = data[key] || ''; + break; + } + }); + }; + +// export values from commands + $.contextMenu.getInputValues = function (opt, data) { + if (typeof data === 'undefined') { + data = {}; + } + + $.each(opt.inputs, function (key, item) { + switch (item.type) { + case 'text': + case 'textarea': + case 'select': + data[key] = item.$input.val(); + break; + + case 'checkbox': + data[key] = item.$input.prop('checked'); + break; + + case 'radio': + if (item.$input.prop('checked')) { + data[item.radio] = item.value; + } + break; + } + }); + + return data; + }; + +// find