|
1 | 1 | /*! |
2 | | - * jQuery contextMenu v2.2.5-dev - Plugin for simple contextMenu handling |
| 2 | + * jQuery contextMenu v2.2.4 - Plugin for simple contextMenu handling |
3 | 3 | * |
4 | | - * Version: v2.2.5-dev |
| 4 | + * Version: v2.2.4 |
5 | 5 | * |
6 | 6 | * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) |
7 | 7 | * Web: http://swisnl.github.io/jQuery-contextMenu/ |
|
12 | 12 | * MIT License http://www.opensource.org/licenses/mit-license |
13 | 13 | * GPL v3 http://opensource.org/licenses/GPL-3.0 |
14 | 14 | * |
15 | | - * Date: 2016-10-11T14:20:03.931Z |
| 15 | + * Date: 2016-10-24T15:42:19.850Z |
16 | 16 | */ |
17 | 17 |
|
18 | 18 | (function (factory) { |
|
116 | 116 |
|
117 | 117 | // Default classname configuration to be able avoid conflicts in frameworks |
118 | 118 | classNames : { |
119 | | - |
120 | 119 | hover: 'context-menu-hover', // Item hover |
121 | 120 | disabled: 'context-menu-disabled', // Item disabled |
122 | 121 | visible: 'context-menu-visible', // Item visible |
|
129 | 128 | iconPaste: 'context-menu-icon-paste', |
130 | 129 | iconDelete: 'context-menu-icon-delete', |
131 | 130 | iconAdd: 'context-menu-icon-add', |
132 | | - iconQuit: 'context-menu-icon-quit' |
| 131 | + iconQuit: 'context-menu-icon-quit', |
| 132 | + iconLoadingClass: 'context-menu-icon-loading' |
133 | 133 | }, |
134 | 134 |
|
135 | 135 | // determine position to show menu at |
|
194 | 194 | }, |
195 | 195 | // position the sub-menu |
196 | 196 | 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 | + } |
197 | 203 | if ($.ui && $.ui.position) { |
198 | 204 | // .position() is provided as a jQuery UI utility |
199 | 205 | // (...and it won't work on hidden elements) |
|
539 | 545 | if(opt.$selected) { |
540 | 546 | opt.$selected.find('input, textarea, select').blur(); |
541 | 547 | } |
542 | | - if (opt.$menu != null) opt.$menu.trigger('prevcommand'); |
| 548 | + opt.$menu.trigger('prevcommand'); |
543 | 549 | return; |
544 | 550 | } else if (e.keyCode === 38 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') { |
545 | 551 | // checkboxes don't capture this key |
546 | 552 | e.preventDefault(); |
547 | 553 | return; |
548 | 554 | } |
549 | 555 | } else if (e.keyCode !== 9 || e.shiftKey) { |
550 | | - if (opt.$menu != null) opt.$menu.trigger('prevcommand'); |
| 556 | + opt.$menu.trigger('prevcommand'); |
551 | 557 | return; |
552 | 558 | } |
553 | 559 | break; |
|
561 | 567 | if(opt.$selected) { |
562 | 568 | opt.$selected.find('input, textarea, select').blur(); |
563 | 569 | } |
564 | | - if (opt.$menu != null) opt.$menu.trigger('nextcommand'); |
| 570 | + opt.$menu.trigger('nextcommand'); |
565 | 571 | return; |
566 | 572 | } else if (e.keyCode === 40 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') { |
567 | 573 | // checkboxes don't capture this key |
568 | 574 | e.preventDefault(); |
569 | 575 | return; |
570 | 576 | } |
571 | 577 | } else { |
572 | | - if (opt.$menu != null) opt.$menu.trigger('nextcommand'); |
| 578 | + opt.$menu.trigger('nextcommand'); |
573 | 579 | return; |
574 | 580 | } |
575 | 581 | break; |
|
639 | 645 |
|
640 | 646 | case 27: // esc |
641 | 647 | handle.keyStop(e, opt); |
642 | | - if (opt.$menu != null) opt.$menu.trigger('contextmenu:hide'); |
| 648 | + opt.$menu.trigger('contextmenu:hide'); |
643 | 649 | return; |
644 | 650 |
|
645 | 651 | default: // 0-9, a-z |
|
1159 | 1165 | $t.addClass('context-menu-separator ' + root.classNames.notSelectable); |
1160 | 1166 | } else if (item.type === 'html') { |
1161 | 1167 | $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. |
1162 | 1170 | } else if (item.type) { |
1163 | 1171 | $label = $('<label></label>').appendTo($t); |
1164 | 1172 | createNameNode(item).appendTo($label); |
|
1178 | 1186 | break; |
1179 | 1187 |
|
1180 | 1188 | case 'text': |
1181 | | - $input = $('<input type="text" value="1" name="" />') |
| 1189 | + $input = $('<input type="text" value="1" name="" value="">') |
1182 | 1190 | .attr('name', 'context-menu-input-' + key) |
1183 | 1191 | .val(item.value || '') |
1184 | 1192 | .appendTo($label); |
|
1196 | 1204 | break; |
1197 | 1205 |
|
1198 | 1206 | case 'checkbox': |
1199 | | - $input = $('<input type="checkbox" value="1" name="" />') |
| 1207 | + $input = $('<input type="checkbox" value="1" name="" value="">') |
1200 | 1208 | .attr('name', 'context-menu-input-' + key) |
1201 | 1209 | .val(item.value || '') |
1202 | 1210 | .prop('checked', !!item.selected) |
1203 | 1211 | .prependTo($label); |
1204 | 1212 | break; |
1205 | 1213 |
|
1206 | 1214 | case 'radio': |
1207 | | - $input = $('<input type="radio" value="1" name="" />') |
| 1215 | + $input = $('<input type="radio" value="1" name="" value="">') |
1208 | 1216 | .attr('name', 'context-menu-input-' + item.radio) |
1209 | 1217 | .val(item.value || '') |
1210 | 1218 | .prop('checked', !!item.selected) |
1211 | 1219 | .prependTo($label); |
1212 | 1220 | break; |
1213 | 1221 |
|
1214 | 1222 | case 'select': |
1215 | | - $input = $('<select name=""></select>') |
| 1223 | + $input = $('<select name="">') |
1216 | 1224 | .attr('name', 'context-menu-input-' + key) |
1217 | 1225 | .appendTo($label); |
1218 | 1226 | if (item.options) { |
|
1225 | 1233 |
|
1226 | 1234 | case 'sub': |
1227 | 1235 | createNameNode(item).appendTo($t); |
1228 | | - |
1229 | 1236 | item.appendTo = item.$node; |
1230 | | - op.create(item, root); |
1231 | 1237 | $t.data('contextMenu', item).addClass('context-menu-submenu'); |
1232 | 1238 | 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 | + } |
1233 | 1253 | break; |
1234 | 1254 |
|
1235 | 1255 | case 'html': |
|
1411 | 1431 | } |
1412 | 1432 |
|
1413 | 1433 | 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)); |
1414 | 1475 | } |
1415 | 1476 | }; |
1416 | 1477 |
|
|
0 commit comments