Skip to content

Commit fc7c450

Browse files
committed
Merge branch 'Ruud-cb-master'
# Conflicts: # README.md # dist/font/context-menu-icons.eot # dist/font/context-menu-icons.ttf # dist/font/context-menu-icons.woff # dist/font/context-menu-icons.woff2 # dist/jquery.contextMenu.css # dist/jquery.contextMenu.js # dist/jquery.contextMenu.min.css # dist/jquery.contextMenu.min.css.map # dist/jquery.contextMenu.min.js # dist/jquery.contextMenu.min.js.map # src/sass/icons/_variables.scss
2 parents 45bdfc9 + e211c0c commit fc7c450

22 files changed

Lines changed: 401 additions & 58 deletions

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
# jQuery contextMenu plugin & polyfill #
22

3-
__IMPORTANT: 2.0.0 is release and has change the default names of the icon classes in order to stop CSS conflicts with frameworks which define the class 'icon'.__
4-
5-
63
[![Travis Build Status](https://travis-ci.org/swisnl/jQuery-contextMenu.svg?branch=master)](https://travis-ci.org/swisnl/jQuery-contextMenu)
74

85
$.contextMenu is a management facility for - you guessed it - context menus. It was designed for an application where there are hundreds of elements that may show a context menu - so intialization speed and memory usage are kept fairly small. It also allows to register context menus without providing actual markup, as $.contextMenu generates DOMElements as needed.
@@ -97,14 +94,14 @@ Font-Awesome icons used from [encharm/Font-Awesome-SVG-PNG](https://github.com/e
9794

9895
## Changelog ##
9996

100-
10197
### Unreleased ###
10298

10399
#### Fixed
104100

105101
* Font family when using font awesome ([Issue #433](https://github.com/swisnl/jQuery-contextMenu/issues/433))
106102
* make `<input>` and `<select>` tags xhtml compatible ([Issue #451](https://github.com/swisnl/jQuery-contextMenu/issues/451)) thanks @andreasplesch
107103
* Add check for `opt.$menu` is null when handling callbacks. ([Issue #462](https://github.com/swisnl/jQuery-contextMenu/issues/462) thanks @andreasrosdal
104+
* Asynchronous promise support for submenu's ([Issue #429](https://github.com/swisnl/jQuery-contextMenu/issues/429)) thanks @Ruud-cb for the hard work.
108105

109106
### 2.2.4 ###
110107

dist/font/context-menu-icons.eot

132 Bytes
Binary file not shown.

dist/font/context-menu-icons.ttf

132 Bytes
Binary file not shown.

dist/font/context-menu-icons.woff

104 Bytes
Binary file not shown.

dist/font/context-menu-icons.woff2

96 Bytes
Binary file not shown.

dist/jquery.contextMenu.css

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/*!
33
* jQuery contextMenu - Plugin for simple contextMenu handling
44
*
5-
* Version: v2.2.5-dev
5+
* Version: v2.2.4
66
*
77
* Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF)
88
* Web: http://swisnl.github.io/jQuery-contextMenu/
@@ -12,15 +12,50 @@
1212
* Licensed under
1313
* MIT License http://www.opensource.org/licenses/mit-license
1414
*
15-
* Date: 2016-10-11T14:11:13.190Z
15+
* Date: 2016-10-24T15:43:59.639Z
1616
*/
17+
@-webkit-keyframes cm-spin {
18+
0% {
19+
-webkit-transform: translateY(-50%) rotate(0deg);
20+
transform: translateY(-50%) rotate(0deg);
21+
}
22+
100% {
23+
-webkit-transform: translateY(-50%) rotate(359deg);
24+
transform: translateY(-50%) rotate(359deg);
25+
}
26+
}
27+
@-o-keyframes cm-spin {
28+
0% {
29+
-webkit-transform: translateY(-50%) rotate(0deg);
30+
-o-transform: translateY(-50%) rotate(0deg);
31+
transform: translateY(-50%) rotate(0deg);
32+
}
33+
100% {
34+
-webkit-transform: translateY(-50%) rotate(359deg);
35+
-o-transform: translateY(-50%) rotate(359deg);
36+
transform: translateY(-50%) rotate(359deg);
37+
}
38+
}
39+
@keyframes cm-spin {
40+
0% {
41+
-webkit-transform: translateY(-50%) rotate(0deg);
42+
-o-transform: translateY(-50%) rotate(0deg);
43+
transform: translateY(-50%) rotate(0deg);
44+
}
45+
100% {
46+
-webkit-transform: translateY(-50%) rotate(359deg);
47+
-o-transform: translateY(-50%) rotate(359deg);
48+
transform: translateY(-50%) rotate(359deg);
49+
}
50+
}
51+
1752
@font-face {
1853
font-family: "context-menu-icons";
1954
font-style: normal;
2055
font-weight: normal;
2156

22-
src: url("font/context-menu-icons.eot?2i55x");
23-
src: url("font/context-menu-icons.eot?2i55x#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?2i55x") format("woff2"), url("font/context-menu-icons.woff?2i55x") format("woff"), url("font/context-menu-icons.ttf?2i55x") format("truetype");
57+
src: url("font/context-menu-icons.eot?386c8");
58+
src: url("font/context-menu-icons.eot?386c8#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?386c8") format("woff2"), url("font/context-menu-icons.woff?386c8") format("woff"), url("font/context-menu-icons.ttf?386c8") format("truetype");
2459
}
2560

2661
.context-menu-icon-add:before {
@@ -43,14 +78,18 @@
4378
content: "\EA05";
4479
}
4580

46-
.context-menu-icon-paste:before {
81+
.context-menu-icon-loading:before {
4782
content: "\EA06";
4883
}
4984

50-
.context-menu-icon-quit:before {
85+
.context-menu-icon-paste:before {
5186
content: "\EA07";
5287
}
5388

89+
.context-menu-icon-quit:before {
90+
content: "\EA08";
91+
}
92+
5493
.context-menu-icon::before {
5594
position: absolute;
5695
top: 50%;
@@ -80,9 +119,14 @@
80119
color: #bbb;
81120
}
82121

122+
.context-menu-icon.context-menu-icon-loading:before {
123+
-webkit-animation: cm-spin 2s infinite;
124+
-o-animation: cm-spin 2s infinite;
125+
animation: cm-spin 2s infinite;
126+
}
127+
83128
.context-menu-icon.context-menu-icon--fa {
84129
display: list-item;
85-
font-family: inherit;
86130
}
87131
.context-menu-icon.context-menu-icon--fa::before {
88132
position: absolute;

dist/jquery.contextMenu.js

Lines changed: 77 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*!
2-
* jQuery contextMenu v2.2.5-dev - Plugin for simple contextMenu handling
2+
* jQuery contextMenu v2.2.4 - Plugin for simple contextMenu handling
33
*
4-
* Version: v2.2.5-dev
4+
* Version: v2.2.4
55
*
66
* Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF)
77
* Web: http://swisnl.github.io/jQuery-contextMenu/
@@ -12,7 +12,7 @@
1212
* MIT License http://www.opensource.org/licenses/mit-license
1313
* GPL v3 http://opensource.org/licenses/GPL-3.0
1414
*
15-
* Date: 2016-10-11T14:20:03.931Z
15+
* Date: 2016-10-24T15:42:19.850Z
1616
*/
1717

1818
(function (factory) {
@@ -116,7 +116,6 @@
116116

117117
// Default classname configuration to be able avoid conflicts in frameworks
118118
classNames : {
119-
120119
hover: 'context-menu-hover', // Item hover
121120
disabled: 'context-menu-disabled', // Item disabled
122121
visible: 'context-menu-visible', // Item visible
@@ -129,7 +128,8 @@
129128
iconPaste: 'context-menu-icon-paste',
130129
iconDelete: 'context-menu-icon-delete',
131130
iconAdd: 'context-menu-icon-add',
132-
iconQuit: 'context-menu-icon-quit'
131+
iconQuit: 'context-menu-icon-quit',
132+
iconLoadingClass: 'context-menu-icon-loading'
133133
},
134134

135135
// determine position to show menu at
@@ -194,6 +194,12 @@
194194
},
195195
// position the sub-menu
196196
positionSubmenu: function ($menu) {
197+
if ($menu === undefined) {
198+
// When user hovers over item (which has sub items) handle.focusItem will call this.
199+
// but the submenu does not exist yet if opt.items is a promise. just return, will
200+
// call positionSubmenu after promise is completed.
201+
return;
202+
}
197203
if ($.ui && $.ui.position) {
198204
// .position() is provided as a jQuery UI utility
199205
// (...and it won't work on hidden elements)
@@ -539,15 +545,15 @@
539545
if(opt.$selected) {
540546
opt.$selected.find('input, textarea, select').blur();
541547
}
542-
if (opt.$menu != null) opt.$menu.trigger('prevcommand');
548+
opt.$menu.trigger('prevcommand');
543549
return;
544550
} else if (e.keyCode === 38 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') {
545551
// checkboxes don't capture this key
546552
e.preventDefault();
547553
return;
548554
}
549555
} else if (e.keyCode !== 9 || e.shiftKey) {
550-
if (opt.$menu != null) opt.$menu.trigger('prevcommand');
556+
opt.$menu.trigger('prevcommand');
551557
return;
552558
}
553559
break;
@@ -561,15 +567,15 @@
561567
if(opt.$selected) {
562568
opt.$selected.find('input, textarea, select').blur();
563569
}
564-
if (opt.$menu != null) opt.$menu.trigger('nextcommand');
570+
opt.$menu.trigger('nextcommand');
565571
return;
566572
} else if (e.keyCode === 40 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') {
567573
// checkboxes don't capture this key
568574
e.preventDefault();
569575
return;
570576
}
571577
} else {
572-
if (opt.$menu != null) opt.$menu.trigger('nextcommand');
578+
opt.$menu.trigger('nextcommand');
573579
return;
574580
}
575581
break;
@@ -639,7 +645,7 @@
639645

640646
case 27: // esc
641647
handle.keyStop(e, opt);
642-
if (opt.$menu != null) opt.$menu.trigger('contextmenu:hide');
648+
opt.$menu.trigger('contextmenu:hide');
643649
return;
644650

645651
default: // 0-9, a-z
@@ -1159,6 +1165,8 @@
11591165
$t.addClass('context-menu-separator ' + root.classNames.notSelectable);
11601166
} else if (item.type === 'html') {
11611167
$t.addClass('context-menu-html ' + root.classNames.notSelectable);
1168+
} else if (item.type === 'sub') {
1169+
// We don't want to execute the next else-if if it is a sub.
11621170
} else if (item.type) {
11631171
$label = $('<label></label>').appendTo($t);
11641172
createNameNode(item).appendTo($label);
@@ -1178,7 +1186,7 @@
11781186
break;
11791187

11801188
case 'text':
1181-
$input = $('<input type="text" value="1" name="" />')
1189+
$input = $('<input type="text" value="1" name="" value="">')
11821190
.attr('name', 'context-menu-input-' + key)
11831191
.val(item.value || '')
11841192
.appendTo($label);
@@ -1196,23 +1204,23 @@
11961204
break;
11971205

11981206
case 'checkbox':
1199-
$input = $('<input type="checkbox" value="1" name="" />')
1207+
$input = $('<input type="checkbox" value="1" name="" value="">')
12001208
.attr('name', 'context-menu-input-' + key)
12011209
.val(item.value || '')
12021210
.prop('checked', !!item.selected)
12031211
.prependTo($label);
12041212
break;
12051213

12061214
case 'radio':
1207-
$input = $('<input type="radio" value="1" name="" />')
1215+
$input = $('<input type="radio" value="1" name="" value="">')
12081216
.attr('name', 'context-menu-input-' + item.radio)
12091217
.val(item.value || '')
12101218
.prop('checked', !!item.selected)
12111219
.prependTo($label);
12121220
break;
12131221

12141222
case 'select':
1215-
$input = $('<select name=""></select>')
1223+
$input = $('<select name="">')
12161224
.attr('name', 'context-menu-input-' + key)
12171225
.appendTo($label);
12181226
if (item.options) {
@@ -1225,11 +1233,23 @@
12251233

12261234
case 'sub':
12271235
createNameNode(item).appendTo($t);
1228-
12291236
item.appendTo = item.$node;
1230-
op.create(item, root);
12311237
$t.data('contextMenu', item).addClass('context-menu-submenu');
12321238
item.callback = null;
1239+
1240+
// If item contains items, and this is a promise, we should create it later
1241+
// check if subitems is of type promise. If it is a promise we need to create
1242+
// it later, after promise has been resolved.
1243+
if ('function' === typeof item.items.then) {
1244+
// probably a promise, process it, when completed it will create the sub menu's.
1245+
// @todo Add a loading class to the item so you know it is loading.
1246+
1247+
1248+
op.processPromises(item, root, item.items);
1249+
} else {
1250+
// normal submenu.
1251+
op.create(item, root);
1252+
}
12331253
break;
12341254

12351255
case 'html':
@@ -1411,6 +1431,47 @@
14111431
}
14121432

14131433
return $layer;
1434+
},
1435+
processPromises: function (opt, root, promise) {
1436+
// Start
1437+
opt.$node.addClass(opt.classNames.iconLoadingClass);
1438+
1439+
1440+
function completedPromise(opt,root,items) {
1441+
// Completed promise (dev called promise.resolve). We now have a list of items which can
1442+
// be used to create the rest of the context menu.
1443+
if (items === undefined) {
1444+
// Null result, dev should have checked
1445+
errorPromise(undefined);//own error object
1446+
}
1447+
finishPromiseProcess(opt,root, items);
1448+
};
1449+
function errorPromise(opt,root,errorItem) {
1450+
// User called promise.reject() with an error item, if not, provide own error item.
1451+
if (errorItem === undefined) {
1452+
errorItem = { "error": { name: "No items and no error item", icon: "context-menu-icon context-menu-icon-quit" } };
1453+
if (window.console) {
1454+
(console.error || console.log).call(console, 'When you reject a promise, provide an "items" object, equal to normal sub-menu items');
1455+
}
1456+
} else if(typeof errorItem === 'string'){
1457+
errorItem = { "error": { name: errorItem } };
1458+
}
1459+
finishPromiseProcess(opt,root,errorItem);
1460+
};
1461+
function finishPromiseProcess(opt,root,items) {
1462+
if(root.$menu === undefined || !root.$menu.is(':visible')){
1463+
return;
1464+
}
1465+
opt.$node.removeClass(opt.classNames.iconLoadingClass);
1466+
opt.items = items;
1467+
op.create(opt, root, true); // Create submenu
1468+
op.update(opt, root); // Correctly update position if user is already hovered over menu item
1469+
root.positionSubmenu.call(opt.$node, opt.$menu); // positionSubmenu, will only do anything if user already hovered over menu item that just got new subitems.
1470+
};
1471+
1472+
// Wait for promise completion. .then(success, error, notify) (we don't track notify). Bind the opt
1473+
// and root to avoid scope problems
1474+
promise.then(completedPromise.bind(this, opt, root), errorPromise.bind(this, opt, root));
14141475
}
14151476
};
14161477

0 commit comments

Comments
 (0)