├── .babelrc
├── .gitignore
├── .npmignore
├── CITATION.cff
├── LICENSE.md
├── README.md
├── assets
├── add.svg
├── example.png
├── remove.svg
└── submenu-indicator-default.svg
├── bower.json
├── cytoscape-context-menus.css
├── cytoscape-context-menus.js
├── demo-customized.html
├── demo.html
├── jsconfig.json
├── package-lock.json
├── package.json
├── src
├── constants.js
├── context-menu.js
├── cytoscape-context-menus.js
├── index.js
└── utils.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/env"]
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /nbproject/
2 | /nbproject/*
3 | /node_modules/
4 | /bower_components/
5 | .vscode/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 |
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 | cff-version: 1.2.0
2 | message: "If you use this software, please cite it as below."
3 | authors:
4 | - family-names: "Dogrusoz"
5 | given-names: "Ugur"
6 | orcid: "https://orcid.org/0000-0002-7153-0784"
7 | - family-names: "Karacelik"
8 | given-names: "Alper"
9 | orcid: "https://orcid.org/0000-0000-0000-0000"
10 | - family-names: "Safarli"
11 | given-names: "Ilkin"
12 | - family-names: "Balci"
13 | given-names: "Hasan"
14 | orcid: "https://orcid.org/0000-0001-8319-7758"
15 | - family-names: "Dervishi"
16 | given-names: "Leonard"
17 | - family-names: "Siper"
18 | given-names: "Metin Can"
19 | title: "cytoscape-context-menus"
20 | version: 4.1.0
21 | date-released: 2021-06-16
22 | url: "https://github.com/iVis-at-Bilkent/cytoscape.js-context-menus"
23 | preferred-citation:
24 | type: article
25 | authors:
26 | - family-names: "Dogrusoz"
27 | given-names: "Ugur"
28 | orcid: "https://orcid.org/0000-0002-7153-0784"
29 | - family-names: "Karacelik"
30 | given-names: "Alper"
31 | orcid: "https://orcid.org/0000-0000-0000-0000"
32 | - family-names: "Safarli"
33 | given-names: "Ilkin"
34 | - family-names: "Balci"
35 | given-names: "Hasan"
36 | orcid: "https://orcid.org/0000-0001-8319-7758"
37 | - family-names: "Dervishi"
38 | given-names: "Leonard"
39 | - family-names: "Siper"
40 | given-names: "Metin Can"
41 | doi: "10.1371/journal.pone.0197238"
42 | journal: "PLOS ONE"
43 | month: 5
44 | start: 1 # First page number
45 | end: 18 # Last page number
46 | title: "Efficient methods and readily customizable libraries for managing complexity of large networks"
47 | issue: 5
48 | volume: 13
49 | year: 2018
50 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 iVis-at-Bilkent
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | cytoscape-context-menus
2 | ================================================================================
3 |
4 |
5 | ## Description
6 |
7 | A Cytoscape.js extension to provide context menu around elements and core instance distributed under [The MIT License](https://opensource.org/licenses/MIT).
8 |
9 | 
10 |
11 | Please cite the following paper when using this extension:
12 |
13 | U. Dogrusoz , A. Karacelik, I. Safarli, H. Balci, L. Dervishi, and M.C. Siper, "[Efficient methods and readily customizable libraries for managing complexity of large networks](https://doi.org/10.1371/journal.pone.0197238)", PLoS ONE, 13(5): e0197238, 2018.
14 |
15 | ## Demo
16 |
17 | Here are demos: **simple** and **customized**, respectively:
18 |
19 |
20 |
21 |
22 |
23 | ## Dependencies
24 |
25 | * Cytoscape.js ^2.7.0 || ^3.0.0
26 |
27 |
28 | ## Usage instructions
29 |
30 | Download the library:
31 | * via npm: `npm install cytoscape-context-menus`,
32 | * via bower: `bower install cytoscape-context-menus`, or
33 | * via direct download in the repository (probably from a tag).
34 |
35 | Import the library as appropriate for your project:
36 |
37 | ES import:
38 |
39 | Note: es import doesn't work for plain javascript applications because webpack doesn't support es module output at the moment.
40 |
41 | ```js
42 | import cytoscape from 'cytoscape';
43 | import contextMenus from 'cytoscape-context-menus';
44 |
45 | // register extension
46 | cytoscape.use(contextMenus);
47 |
48 | // import CSS as well
49 | import 'cytoscape-context-menus/cytoscape-context-menus.css';
50 | ```
51 |
52 | CommonJS:
53 | ```js
54 | var cytoscape = require('cytoscape');
55 | var contextMenus = require('cytoscape-context-menus');
56 |
57 | contextMenus(cytoscape); // register extension
58 | ```
59 |
60 | AMD:
61 | ```js
62 | require(['cytoscape', 'cytoscape-context-menus'], function(cytoscape, contextMenus) {
63 | contextMenus(cytoscape); // register extension
64 | });
65 | ```
66 |
67 | Plain HTML/JS has the extension registered for you automatically, because no `require()` is needed.
68 |
69 | ## Default Options
70 | ```js
71 | var options = {
72 | // Customize event to bring up the context menu
73 | // Possible options https://js.cytoscape.org/#events/user-input-device-events
74 | evtType: 'cxttap',
75 | // List of initial menu items
76 | // A menu item must have either onClickFunction or submenu or both
77 | menuItems: [/*
78 | {
79 | id: 'remove', // ID of menu item
80 | content: 'remove', // Display content of menu item
81 | tooltipText: 'remove', // Tooltip text for menu item
82 | image: {src : "remove.svg", width : 12, height : 12, x : 6, y : 4}, // menu icon
83 | // Filters the elements to have this menu item on cxttap
84 | // If the selector is not truthy no elements will have this menu item on cxttap
85 | selector: 'node, edge',
86 | onClickFunction: function () { // The function to be executed on click
87 | console.log('remove element');
88 | },
89 | disabled: false, // Whether the item will be created as disabled
90 | show: false, // Whether the item will be shown or not
91 | hasTrailingDivider: true, // Whether the item will have a trailing divider
92 | coreAsWell: false // Whether core instance have this item on cxttap
93 | submenu: [] // Shows the listed menuItems as a submenu for this item. An item must have either submenu or onClickFunction or both.
94 | },
95 | {
96 | id: 'hide',
97 | content: 'hide',
98 | tooltipText: 'hide',
99 | selector: 'node, edge',
100 | onClickFunction: function () {
101 | console.log('hide element');
102 | },
103 | disabled: true
104 | },
105 | {
106 | id: 'add-node',
107 | content: 'add node',
108 | tooltipText: 'add node',
109 | image: {src : "add.svg", width : 12, height : 12, x : 6, y : 4},
110 | selector: 'node',
111 | coreAsWell: true,
112 | onClickFunction: function () {
113 | console.log('add node');
114 | }
115 | }*/
116 | ],
117 | // css classes that menu items will have
118 | menuItemClasses: [
119 | // add class names to this list
120 | ],
121 | // css classes that context menu will have
122 | contextMenuClasses: [
123 | // add class names to this list
124 | ],
125 | // Indicates that the menu item has a submenu. If not provided default one will be used
126 | submenuIndicator: { src: 'assets/submenu-indicator-default.svg', width: 12, height: 12 }
127 | };
128 | ```
129 |
130 | **Note:** `selector` and `coreAsWell` options are ignored for the items that are inside a submenu. Their visiblity depends on their root parent's visibility.
131 |
132 | ## API
133 |
134 | ### Instance API
135 |
136 | ```js
137 | var instance = cy.contextMenus(options);
138 | ```
139 |
140 | #### `instance.isActive()`
141 | * Returns whether the extension is active.
142 |
143 | #### `instance.appendMenuItem(item, parentID = undefined)`
144 | * Appends given menu item to the menu items list.
145 | * If parentID is specified, the item is inserted to the submenu of the item with parentID.
146 | * If the parent has no submenu then it will automatically be created.
147 | * If not specified item will be inserted to the root of the contextmenu
148 |
149 | #### `instance.appendMenuItems(items, parentID = undefined)`
150 | * Same with above but takes an array of items
151 |
152 | #### `instance.removeMenuItem(itemID)`
153 | * Removes the menu item with given ID and its submenu along with
154 |
155 | #### `instance.setTrailingDivider(itemID, status)`
156 | * Sets whether the menuItem with given ID will have a following divider
157 |
158 | #### `instance.insertBeforeMenuItem(item, existingItemID)`
159 | * Inserts given item before the existingitem
160 |
161 | #### `instance.moveToSubmenu(itemID, options = null)`
162 | * Moves the item with given ID to the submenu of the parent with the given ID or to root with the specified options
163 | * If `options` is a `string`, then it is the id of the parent
164 | * If `options` is a `{ selector?: string, coreAsWell?: boolean }`, then old properties are overwritten by them and the menu item is moved to the root. If it doesn't have either properties item is **not moved**.
165 | * If `options` is null or not provided, then it is just moved to the root
166 |
167 | #### `instance.moveBeforeOtherMenuItem(itemID, existingItemID)`
168 | * Inserts the `item` before the `existingItem` and moves it to the submenu that contains the `existingItem`
169 |
170 | #### `instance.disableMenuItem(itemID)`
171 | * Disables the menu item with given ID.
172 |
173 | #### `instance.enableMenuItem(itemID)`
174 | * Enables the menu item with given ID.
175 |
176 | #### `instance.showMenuItem(itemID)`
177 | * Shows the menu item with given ID.
178 |
179 | #### `instance.hideMenuItem(itemID)`
180 | * Hides the menu item with given ID.
181 |
182 | #### `instance.destroy()`
183 | * Destroys the extension instance
184 |
185 | #### `instance.swapItems(x, y)`
186 | * Changes the order of items. `x` and `y` are id of context menu items to be swapped
187 |
188 | #### `instance.getOptions()`
189 | * Returns the used options
190 |
191 | ### Other API
192 |
193 | #### `cy.contextMenus('get')`
194 | * Returns the existing instance to the extension
195 |
196 | ## Build targets
197 |
198 | * `npm run build` : Build `./src/**` into `cytoscape-edge-editing.js` in production environment and minimize the file.
199 | * `npm run build:dev` : Build `./src/**` into `cytoscape-edge-editing.js` in development environment without minimizing the file.
200 |
201 | ## Publishing instructions
202 |
203 | This project is set up to automatically be published to npm and bower. To publish:
204 |
205 | 1. Build the extension : `npm run build`
206 | 1. Commit the build : `git commit -am "Build for release"`
207 | 1. Bump the version number and tag: `npm version major|minor|patch`
208 | 1. Push to origin: `git push && git push --tags`
209 | 1. Publish to npm: `npm publish .`
210 | 1. If publishing to bower for the first time, you'll need to run `bower register cytoscape-context-menus https://github.com/iVis-at-Bilkent/cytoscape.js-context-menus.git`
211 |
212 | ## Team
213 |
214 | * [Hasan Balcı](https://github.com/hasanbalci), [Ugur Dogrusoz](https://github.com/ugurdogrusoz) of [i-Vis at Bilkent University](http://www.cs.bilkent.edu.tr/~ivis)
215 |
216 | ### Alumni
217 | * [Metin Can Siper](https://github.com/metincansiper) and [Onur Sahin](https://github.com/onsah)
218 |
--------------------------------------------------------------------------------
/assets/add.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
44 |
--------------------------------------------------------------------------------
/assets/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iVis-at-Bilkent/cytoscape.js-context-menus/7e1de0b5690ecfce9518bf292e0c07f38c31321d/assets/example.png
--------------------------------------------------------------------------------
/assets/remove.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/assets/submenu-indicator-default.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cytoscape-context-menus",
3 | "description": "A Cytoscape.js extension to provide context menu around elements and core instance.",
4 | "main": "cytoscape-context-menus.js",
5 | "dependencies": {
6 | "cytoscape": "^2.7.0 || ^3.0.0"
7 | },
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/iVis-at-Bilkent/cytoscape.js-context-menus.git"
11 | },
12 | "ignore": [
13 | "**/.*",
14 | "node_modules",
15 | "bower_components",
16 | "test",
17 | "tests"
18 | ],
19 | "keywords": [
20 | "cytoscape",
21 | "cyext"
22 | ],
23 | "license": "MIT"
24 | }
25 |
--------------------------------------------------------------------------------
/cytoscape-context-menus.css:
--------------------------------------------------------------------------------
1 | .cy-context-menus-cxt-menu {
2 | display:none;
3 | z-index: 1000;
4 | position:absolute;
5 | border:1px solid #A0A0A0;
6 | padding: 0;
7 | margin: 0;
8 | width:auto;
9 | }
10 |
11 | .cy-context-menus-cxt-menuitem {
12 | display:block;
13 | width: 100%;
14 | padding: 3px 20px;
15 | position:relative;
16 | margin:0;
17 | background-color:#f8f8f8;
18 | font-weight:normal;
19 | font-size: 12px;
20 | white-space:nowrap;
21 | border: 0;
22 | text-align: left;
23 | }
24 |
25 | .cy-context-menus-cxt-menuitem:enabled {
26 | color: #000000;
27 | }
28 |
29 | .cy-context-menus-ctx-operation:focus {
30 | outline: none;
31 | }
32 |
33 | .cy-context-menus-cxt-menuitem:hover {
34 | color: #ffffff;
35 | text-decoration: none;
36 | background-color: #0B9BCD;
37 | background-image: none;
38 | cursor: pointer;
39 | }
40 |
41 | .cy-context-menus-cxt-menuitem[content]:before {
42 | content:attr(content);
43 | }
44 |
45 | .cy-context-menus-divider {
46 | border-bottom:1px solid #A0A0A0;
47 | }
48 |
49 | .cy-context-menus-submenu-indicator {
50 | position: absolute;
51 | right: 2px;
52 | top: 50%;
53 | transform: translateY(-50%);
54 | }
--------------------------------------------------------------------------------
/cytoscape-context-menus.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.cytoscapeContextMenus=t():e.cytoscapeContextMenus=t()}(this,(function(){return(()=>{var e={621:(e,t,n)=>{"use strict";function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,i=new Array(t);n_});var a="cy-context-menus-divider",c={evtType:"cxttap",menuItems:[],menuItemClasses:["cy-context-menus-cxt-menuitem"],contextMenuClasses:["cy-context-menus-cxt-menu"],submenuIndicator:{src:"assets/submenu-indicator-default.svg",width:12,height:12}};function l(e){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function f(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return d(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?d(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var i=0,o=function(){};return{s:o,n:function(){return i>=e.length?{done:!0}:{done:!1,value:e[i++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,u=!0,s=!1;return{s:function(){n=e[Symbol.iterator]()},n:function(){var e=n.next();return u=e.done,e},e:function(e){s=!0,r=e},f:function(){try{u||null==n.return||n.return()}finally{if(s)throw r}}}}function d(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,i=new Array(t);n1&&void 0!==arguments[1]?arguments[1]:void 0;this.hasSubmenu()||this._createSubmenu(),this.submenu.appendMenuItem(e,t)}},{key:"isClickable",value:function(){return void 0!==this.onClickFunction}},{key:"display",value:function(){this.show=!0,this.style.display="block"}},{key:"isVisible",value:function(){return!0===this.show&&"none"!==this.style.display}},{key:"removeSubmenu",value:function(){this.hasSubmenu()&&(this.submenu.removeAllMenuItems(),this.detachSubmenu())}},{key:"detachSubmenu",value:function(){this.hasSubmenu()&&(this.removeChild(this.submenu),this.removeChild(this.indicator),this.removeEventListener("mouseenter",this.mouseEnterHandler),this.removeEventListener("mouseleave",this.mouseLeaveHandler),this.submenu=void 0,this.indicator=void 0)}},{key:"_onMouseEnter",value:function(e){var t=this.getBoundingClientRect(),i=function(e){e.style.opacity="0",e.style.display="block";var t=e.getBoundingClientRect();return e.style.opacity="1",e.style.display="none",t}(this.submenu),o=t.right+i.width>window.innerWidth,r=t.top+i.height>window.innerHeight;o||r?o&&!r?(this.submenu.style.right=this.clientWidth+"px",this.submenu.style.top="0px",this.submenu.style.left="auto",this.submenu.style.bottom="auto"):o&&r?(this.submenu.style.right=this.clientWidth+"px",this.submenu.style.bottom="0px",this.submenu.style.top="auto",this.submenu.style.left="auto"):(this.submenu.style.left=this.clientWidth+"px",this.submenu.style.bottom="0px",this.submenu.style.right="auto",this.submenu.style.top="auto"):(this.submenu.style.left=this.clientWidth+"px",this.submenu.style.top="0px",this.submenu.style.right="auto",this.submenu.style.bottom="auto"),this.submenu.display();var u=Array.from(this.submenu.children).filter((function(e){if(e instanceof n)return e.isVisible()})),s=u.length;u.forEach((function(e,t){e instanceof n&&(t=(r=n.getBoundingClientRect()).left&&i<=r.right&&o>=r.top&&o<=r.bottom||this.submenu.hide()}},{key:"_createSubmenu",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.indicator=this.scratchpad.submenuIndicatorGen(),this.submenu=new T(this.onMenuItemClick,this.scratchpad),this.appendChild(this.indicator),this.appendChild(this.submenu);var t,i=f(e);try{for(i.s();!(t=i.n()).done;){var o=t.value,r=new n(o,this.onMenuItemClick,this.scratchpad);this.submenu.appendMenuItem(r)}}catch(e){i.e(e)}finally{i.f()}this.mouseEnterHandler=this._onMouseEnter.bind(this),this.mouseLeaveHandler=this._onMouseLeave.bind(this),this.addEventListener("mouseenter",this.mouseEnterHandler),this.addEventListener("mouseleave",this.mouseLeaveHandler)}},{key:"_getMenuItemClassStr",value:function(e,t){return t?e+" "+a:e}}],[{key:"define",value:function(){s("ctx-menu-item",n,"button")}}]),n}(x(HTMLButtonElement)),T=function(e){y(n,e);var t=p(n);function n(e,i){var o,r;return h(this,n),w((o=g(r=t.call(this)),k(n.prototype)),"setAttribute",o).call(o,"class",i.cxtMenuClasses),r.style.position="absolute",r.onMenuItemClick=e,r.scratchpad=i,r}return v(n,[{key:"hide",value:function(){this.isVisible()&&(this.hideSubmenus(),this.style.display="none")}},{key:"display",value:function(){this.style.display="block"}},{key:"isVisible",value:function(){return"none"!==this.style.display}},{key:"hideMenuItems",value:function(){var e,t=f(this.children);try{for(t.s();!(e=t.n()).done;){var n=e.value;n instanceof HTMLElement?n.style.display="none":console.warn("".concat(n," is not a HTMLElement"))}}catch(e){t.e(e)}finally{t.f()}}},{key:"hideSubmenus",value:function(){var e,t=f(this.children);try{for(t.s();!(e=t.n()).done;){var n=e.value;n instanceof S&&n.submenu&&n.submenu.hide()}}catch(e){t.e(e)}finally{t.f()}}},{key:"appendMenuItem",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;if(void 0!==t){if(t.parentNode!==this)throw new Error("The item with id='".concat(t.id,"' is not a child of the context menu"));this.insertBefore(e,t)}else this.appendChild(e);e.isClickable()&&this._performBindings(e)}},{key:"moveBefore",value:function(e,t){if(e.parentNode!==this)throw new Error("The item with id='".concat(e.id,"' is not a child of context menu"));if(t.parentNode!==this)throw new Error("The item with id='".concat(t.id,"' is not a child of context menu"));this.removeChild(e),this.insertBefore(e,t)}},{key:"removeAllMenuItems",value:function(){for(;this.firstChild;){var e=this.lastChild;e instanceof S?this._removeImmediateMenuItem(e):(console.warn("Found non menu item in the context menu: ",e),this.removeChild(e))}}},{key:"_removeImmediateMenuItem",value:function(e){if(!this._detachImmediateMenuItem(e))throw new Error("menu item(id=".concat(e.id,") is not in the context menu"));e.detachSubmenu(),e.unbindOnClickFunctions()}},{key:"_detachImmediateMenuItem",value:function(e){if(e.parentNode===this){if(this.removeChild(e),this.children.length<=0){var t=this.parentNode;t instanceof S&&t.detachSubmenu()}return!0}return!1}},{key:"_performBindings",value:function(e){var t=this._bindOnClick(e.onClickFunction);e.bindOnClickFunction(t),e.bindOnClickFunction(this.onMenuItemClick)}},{key:"_bindOnClick",value:function(e){var t=this;return function(){var n=t.scratchpad.currentCyEvent;e(n)}}}],[{key:"define",value:function(){s("menu-item-list",n,"div")}}]),n}(x(HTMLDivElement)),A=function(e){y(n,e);var t=p(n);function n(e,i){var o;return h(this,n),(o=t.call(this,e,i)).onMenuItemClick=function(t){E(t),o.hide(),e()},o}return v(n,[{key:"removeMenuItem",value:function(e){var t=e.parentElement;t instanceof T&&this.contains(t)&&t._removeImmediateMenuItem(e)}},{key:"appendMenuItem",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;this.ensureDoesntContain(e.id),w(k(n.prototype),"appendMenuItem",this).call(this,e,t)}},{key:"insertMenuItem",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.before,i=t.parent;if(this.ensureDoesntContain(e.id),void 0!==n){if(!this.contains(n))throw new Error("before(id=".concat(n.id,") is not in the context menu"));var o=n.parentNode;if(!(o instanceof T))throw new Error("Parent of before(id=".concat(n.id,") is not a submenu"));o.appendMenuItem(e,n)}else if(void 0!==i){if(!this.contains(i))throw new Error("parent(id=".concat(i.id,") is not a descendant of the context menu"));i.appendSubmenuItem(e)}else this.appendMenuItem(e)}},{key:"moveBefore",value:function(e,t){var n=e.parentElement;if(!this.contains(n))throw new Error("parent(id=".concat(n.id,") is not in the contex menu"));if(!this.contains(t))throw new Error("before(id=".concat(t.id,") is not in the context menu"));n.removeChild(e),this.insertMenuItem(e,{before:t})}},{key:"moveToSubmenu",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,i=e.parentElement;if(!(i instanceof T))throw new Error("current parent(id=".concat(i.id,") is not a submenu"));if(!this.contains(i))throw new Error("parent of the menu item(id=".concat(i.id,") is not in the context menu"));if(null!==t){if(!this.contains(t))throw new Error("parent(id=".concat(t.id,") is not in the context menu"));i._detachImmediateMenuItem(e),t.appendSubmenuItem(e)}else null!==n&&(e.selector=n.selector,e.coreAsWell=n.coreAsWell),i._detachImmediateMenuItem(e),this.appendMenuItem(e)}},{key:"ensureDoesntContain",value:function(e){var t=document.getElementById(e);if(void 0!==t&&this.contains(t))throw new Error("There is already an element with id=".concat(e," in the context menu"))}},{key:"ensureContains",value:function(e){var t=document.getElementById(e);if(null==t||null==t||!this.contains(t))throw new Error("An element with id '".concat(e,"' does not exist!"))}}],[{key:"define",value:function(){s("ctx-menu",n,"div")}}]),n}(T);function O(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return L(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?L(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var i=0,o=function(){};return{s:o,n:function(){return i>=e.length?{done:!0}:{done:!1,value:e[i++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,u=!0,s=!1;return{s:function(){n=e[Symbol.iterator]()},n:function(){var e=n.next();return u=e.done,e},e:function(e){s=!0,r=e},f:function(){try{u||null==n.return||n.return()}finally{if(s)throw r}}}}function L(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,i=new Array(t);n1&&void 0!==arguments[1]?arguments[1]:void 0,n=v(e);if(void 0!==t){var i=p(t);d.insertMenuItem(n,{parent:i})}else d.insertMenuItem(n)},m=function(e){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,n=0;n0&&(u.top+=f,u.left+=f);var h=i.clientHeight,m=i.clientWidth,v=h/2,y=m/2;a.y>v&&a.x<=y?(d.style.left=a.x+"px",d.style.bottom=h-a.y+"px",d.style.right="auto",d.style.top="auto"):a.y>v&&a.x>y?(d.style.right=m-a.x+"px",d.style.bottom=h-a.y+"px",d.style.left="auto",d.style.top="auto"):a.y<=v&&a.x<=y?(d.style.left=a.x+"px",d.style.top=a.y+"px",d.style.right="auto",d.style.bottom="auto"):(d.style.right=m-a.x+"px",d.style.top=a.y+"px",d.style.left="auto",d.style.bottom="auto")}}(e);var n,i=e.target||e.cyTarget,o=O(d.children);try{for(o.s();!(n=o.n()).done;){var r=n.value;r instanceof S&&(i===t?r.coreAsWell:i.is(r.selector))&&r.show&&(d.display(),l("anyVisibleChild",!0),r.display())}}catch(e){o.e(e)}finally{o.f()}var u=Array.from(d.children).filter((function(e){if(e instanceof S)return e.isVisible()})),c=u.length;u.forEach((function(e,t){e instanceof S&&(t=e.length?{done:!0}:{done:!1,value:e[o++]}},e:function(e){throw e},f:r}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var u,s=!0,a=!1;return{s:function(){n=e[Symbol.iterator]()},n:function(){var e=n.next();return s=e.done,e},e:function(e){a=!0,u=e},f:function(){try{s||null==n.return||n.return()}finally{if(a)throw u}}}}(document.getElementsByClassName("cy-context-menus-cxt-menu"));try{for(t.s();!(e=t.n()).done;)e.value.addEventListener("contextmenu",(function(e){return e.preventDefault()}))}catch(e){t.e(e)}finally{t.f()}}()}return function(e){return{isActive:function(){return s("active")},appendMenuItem:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;return h(t,n),e},appendMenuItems:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;return m(t,n),e},removeMenuItem:function(t){var n=p(t);return d.removeMenuItem(n),e},setTrailingDivider:function(t,n){var i=p(t);return i.setHasTrailingDivider(n),n?i.classList.add(a):i.classList.remove(a),e},insertBeforeMenuItem:function(t,n){var i=v(t),o=p(n);return d.insertMenuItem(i,{before:o}),e},moveToSubmenu:function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=p(t);if(null===n)d.moveToSubmenu(i);else if("string"==typeof n){var o=p(n.toString());d.moveToSubmenu(i,o)}else void 0!==n.coreAsWell||void 0!==n.selector?d.moveToSubmenu(i,null,n):console.warn("options neither has coreAsWell nor selector property but it is an object. Are you sure that this is what you want to do?");return e},moveBeforeOtherMenuItem:function(t,n){var i=p(t),o=p(n);return d.moveBefore(i,o),e},disableMenuItem:function(t){return p(t).disable(),e},enableMenuItem:function(t){return p(t).enable(),e},hideMenuItem:function(t){return p(t).hide(),b(),e},showMenuItem:function(t){return p(t).display(),b(),e},destroy:function(){return y(),e},getOptions:function(){return o(c,f)},swapItems:function(e,t){d.ensureContains(e),d.ensureContains(t);var n=document.getElementById(e),i=document.getElementById(t),o=n.nextSibling,r=i.nextSibling,u=n.parentNode,s=i.parentNode;if(!u.isSameNode(s))throw new Error("To swap, the items should have the same parent!");o&&o.isSameNode(i)?u.insertBefore(i,n):r&&r.isSameNode(n)?u.insertBefore(n,i):(u.insertBefore(i,n),u.insertBefore(n,r))}}}(this)}},579:(e,t,n)=>{var i=n(621).contextMenus,o=function(e){e&&e("core","contextMenus",i)};"undefined"!=typeof cytoscape&&o(cytoscape),e.exports=o}},t={};function n(i){var o=t[i];if(void 0!==o)return o.exports;var r=t[i]={exports:{}};return e[i](r,r.exports,n),r.exports}return n.d=(e,t)=>{for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n(579)})()}));
--------------------------------------------------------------------------------
/demo-customized.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | cytoscape-context-menus.js demo
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
57 |
58 |
319 |
320 |
321 |
322 |