From 9ed111511bd8dd6ed4387d3d347a18617ac44a03 Mon Sep 17 00:00:00 2001 From: Ruud-cb Date: Fri, 22 Jul 2016 14:24:23 +0200 Subject: [PATCH 1/9] context menu accepts promise for 'items' inside submenu. after promise completion build items. TODO: when already hovered over the menu item for the submenu --- src/jquery.contextMenu.js | 60 +++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/src/jquery.contextMenu.js b/src/jquery.contextMenu.js index e022dd1b..2e189980 100755 --- a/src/jquery.contextMenu.js +++ b/src/jquery.contextMenu.js @@ -1,18 +1,18 @@ /*! - * jQuery contextMenu v@VERSION - Plugin for simple contextMenu handling + * jQuery contextMenu v2.2.4-dev - Plugin for simple contextMenu handling * - * Version: v@VERSION + * Version: v2.2.4-dev * * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) * Web: http://swisnl.github.io/jQuery-contextMenu/ * - * Copyright (c) 2011-@YEAR SWIS BV and contributors + * Copyright (c) 2011-2016 SWIS BV and contributors * * Licensed under * MIT License http://www.opensource.org/licenses/mit-license * GPL v3 http://opensource.org/licenses/GPL-3.0 * - * Date: @DATE + * Date: 2016-07-17T19:45:35.567Z */ (function (factory) { @@ -194,6 +194,11 @@ }, // position the sub-menu positionSubmenu: function ($menu) { + if ($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) @@ -1052,10 +1057,14 @@ }); } }, - create: function (opt, root) { + create: function (opt, root, afterPromise) { if (root === undefined) { root = opt; } + + //this is a recursive function, if item.items == true, this will be called again. + console.log(" create: function (opt, root): opt,root ", opt, root); + // create contextMenu opt.$menu = $('').addClass(opt.className || '').data({ 'contextMenu': opt, @@ -1099,7 +1108,6 @@ } return $name; } - // create contextMenu items $.each(opt.items, function (key, item) { var $t = $('
  • ').addClass(item.className || ''), @@ -1110,6 +1118,10 @@ // have the TouchEvents infrastructure trigger the click event $t.on('click', $.noop); + //if item contains items, and this is a promise, we should create it later + //this is a recursive function, if item.items == true, it will be called again. + console.log("op.create, $.each(opt.items) key,value ", key, item); + // Make old school string seperator a real item so checks wont be // akward later. // And normalize 'cm_separator' into 'cm_seperator'. @@ -1154,6 +1166,7 @@ } }); } else { + var hasSubItems = false; // add label for input if (item.type === 'cm_seperator') { $t.addClass('context-menu-separator ' + root.classNames.notSelectable); @@ -1170,6 +1183,7 @@ k.inputs[key] = item; }); } else if (item.items) { + hasSubItems = true; item.type = 'sub'; } @@ -1227,7 +1241,7 @@ createNameNode(item).appendTo($t); item.appendTo = item.$node; - op.create(item, root); + //op.create(item, root); decide later. $t.data('contextMenu', item).addClass('context-menu-submenu'); item.callback = null; break; @@ -1248,7 +1262,19 @@ createNameNode(item).appendTo($t); break; } - + if (hasSubItems) + { + //check if subitems is of type promise or array. If it is a promise we need to create it later + if ('function' === typeof item.items.then) { + // probably a promise, process it, when completed it will create the sub menu's. + console.log("found a promise in loop"); + op.processPromises(item, root, item.items); + } else { + // definitely not a promise + op.create(item, root); + } + + } // disable key listener in if (item.type && item.type !== 'sub' && item.type !== 'html' && item.type !== 'cm_seperator') { $input @@ -1334,6 +1360,7 @@ } }, update: function (opt, root) { + console.log("update context menu. opt,root", opt, root); var $trigger = this; if (root === undefined) { root = opt; @@ -1411,6 +1438,23 @@ } return $layer; + }, + processPromises: function (opt, root, promise) { + + promise.then(function (items) { + //completed promise, we now have a list of items which can be used to create the rest of the context menu. + opt.items = items;//override promise to items. + op.create(opt, root, true);//create submenu + op.resize(opt.$menu);//does not work + op.update(opt, root);//does not work + console.log("completed promise, items, opt, root", items, opt, root); + //console.log("opt.$node", opt.$node); + //$node.className has an extra context-menu-hover when hovered over item, but yea, not really fancy to select by this. + console.log("opt.$node.className", opt.$node[0].className); + + console.log("opt.$menu", opt.$menu); + + }) } }; From 87eccfccea02b309a4d969b2492cf8b1afb86ff0 Mon Sep 17 00:00:00 2001 From: Ruud-cb Date: Fri, 22 Jul 2016 15:40:37 +0200 Subject: [PATCH 2/9] context menu with promise finished, included error handling if promise.reject is called. --- src/jquery.contextMenu.js | 66 +++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/src/jquery.contextMenu.js b/src/jquery.contextMenu.js index 2e189980..ed142e73 100755 --- a/src/jquery.contextMenu.js +++ b/src/jquery.contextMenu.js @@ -1,18 +1,18 @@ /*! - * jQuery contextMenu v2.2.4-dev - Plugin for simple contextMenu handling + * jQuery contextMenu v@VERSION - Plugin for simple contextMenu handling * - * Version: v2.2.4-dev + * Version: @VERSION * * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) * Web: http://swisnl.github.io/jQuery-contextMenu/ * - * Copyright (c) 2011-2016 SWIS BV and contributors + * Copyright (c) 2011-@YEAR SWIS BV and contributors * * Licensed under * MIT License http://www.opensource.org/licenses/mit-license * GPL v3 http://opensource.org/licenses/GPL-3.0 * - * Date: 2016-07-17T19:45:35.567Z + * Date: @DATE */ (function (factory) { @@ -1057,14 +1057,11 @@ }); } }, - create: function (opt, root, afterPromise) { + create: function (opt, root) { if (root === undefined) { root = opt; } - //this is a recursive function, if item.items == true, this will be called again. - console.log(" create: function (opt, root): opt,root ", opt, root); - // create contextMenu opt.$menu = $('').addClass(opt.className || '').data({ 'contextMenu': opt, @@ -1108,6 +1105,7 @@ } return $name; } + // create contextMenu items $.each(opt.items, function (key, item) { var $t = $('
  • ').addClass(item.className || ''), @@ -1118,10 +1116,6 @@ // have the TouchEvents infrastructure trigger the click event $t.on('click', $.noop); - //if item contains items, and this is a promise, we should create it later - //this is a recursive function, if item.items == true, it will be called again. - console.log("op.create, $.each(opt.items) key,value ", key, item); - // Make old school string seperator a real item so checks wont be // akward later. // And normalize 'cm_separator' into 'cm_seperator'. @@ -1241,7 +1235,7 @@ createNameNode(item).appendTo($t); item.appendTo = item.$node; - //op.create(item, root); decide later. + //op.create(item, root); decide later, might be a promise. $t.data('contextMenu', item).addClass('context-menu-submenu'); item.callback = null; break; @@ -1262,15 +1256,16 @@ createNameNode(item).appendTo($t); break; } + if (hasSubItems) { - //check if subitems is of type promise or array. If it is a promise we need to create it later + //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. - console.log("found a promise in loop"); op.processPromises(item, root, item.items); } else { - // definitely not a promise + // normal submenu. op.create(item, root); } @@ -1360,7 +1355,6 @@ } }, update: function (opt, root) { - console.log("update context menu. opt,root", opt, root); var $trigger = this; if (root === undefined) { root = opt; @@ -1440,21 +1434,33 @@ return $layer; }, processPromises: function (opt, root, promise) { - - promise.then(function (items) { - //completed promise, we now have a list of items which can be used to create the rest of the context menu. + function completedPromise(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 (items === undefined) { + //meh, null result, dev should have checked + errorPromise(undefined);//own error object + } + finishPromiseProcess(items); + }; + function errorPromise(errorItem) { + //user called promise.reject() with an error item, if not, provide own error item. + if ( typeof errorItem === 'string' || 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'); + } + } + finishPromiseProcess(errorItem); + }; + function finishPromiseProcess(items) { opt.items = items;//override promise to items. op.create(opt, root, true);//create submenu - op.resize(opt.$menu);//does not work - op.update(opt, root);//does not work - console.log("completed promise, items, opt, root", items, opt, root); - //console.log("opt.$node", opt.$node); - //$node.className has an extra context-menu-hover when hovered over item, but yea, not really fancy to select by this. - console.log("opt.$node.className", opt.$node[0].className); - - console.log("opt.$menu", opt.$menu); - - }) + 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. success, error, notify. (don't track notify). + promise.then(completedPromise, errorPromise); } }; From 8432265e0fd18e4660b5f96281a69e4f5c4558c2 Mon Sep 17 00:00:00 2001 From: Ruud-cb Date: Fri, 22 Jul 2016 15:42:43 +0200 Subject: [PATCH 3/9] little cleanup. --- src/jquery.contextMenu.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/jquery.contextMenu.js b/src/jquery.contextMenu.js index ed142e73..5007cfc8 100755 --- a/src/jquery.contextMenu.js +++ b/src/jquery.contextMenu.js @@ -1,5 +1,5 @@ /*! - * jQuery contextMenu v@VERSION - Plugin for simple contextMenu handling + * jQuery contextMenu @VERSION - Plugin for simple contextMenu handling * * Version: @VERSION * @@ -1061,7 +1061,6 @@ if (root === undefined) { root = opt; } - // create contextMenu opt.$menu = $('
      ').addClass(opt.className || '').data({ 'contextMenu': opt, From e85f5605a27ed11e5e13b9b8c11a4a315b9e6bfe Mon Sep 17 00:00:00 2001 From: Ruud-cb Date: Fri, 22 Jul 2016 15:44:05 +0200 Subject: [PATCH 4/9] cleanup --- src/jquery.contextMenu.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jquery.contextMenu.js b/src/jquery.contextMenu.js index 5007cfc8..91f500f2 100755 --- a/src/jquery.contextMenu.js +++ b/src/jquery.contextMenu.js @@ -1,7 +1,7 @@ /*! - * jQuery contextMenu @VERSION - Plugin for simple contextMenu handling + * jQuery contextMenu v@VERSION - Plugin for simple contextMenu handling * - * Version: @VERSION + * Version: v@VERSION * * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) * Web: http://swisnl.github.io/jQuery-contextMenu/ From aafa47850d9da174d696ae20d3df90c59621d2c3 Mon Sep 17 00:00:00 2001 From: Ruud-cb Date: Mon, 25 Jul 2016 15:27:32 +0200 Subject: [PATCH 5/9] Fix for promises displaying incorrectly inside a sub menu that was created from a promise. --- src/jquery.contextMenu.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/jquery.contextMenu.js b/src/jquery.contextMenu.js index 91f500f2..cddc8394 100755 --- a/src/jquery.contextMenu.js +++ b/src/jquery.contextMenu.js @@ -1165,7 +1165,11 @@ $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) { + } + else if (item.type === 'sub') { + hasSubItems = true;//we don't want to execute the next else-if if it is a sub. + } + else if (item.type) { $label = $('').appendTo($t); createNameNode(item).appendTo($label); @@ -1433,16 +1437,16 @@ return $layer; }, processPromises: function (opt, root, promise) { - function completedPromise(items) { + 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 (items === undefined) { //meh, null result, dev should have checked errorPromise(undefined);//own error object } - finishPromiseProcess(items); + finishPromiseProcess(opt,root, items); }; - function errorPromise(errorItem) { + function errorPromise(opt,root,errorItem) { //user called promise.reject() with an error item, if not, provide own error item. if ( typeof errorItem === 'string' || errorItem === undefined) { errorItem = { "error": { name: "No items and no error item", icon: "context-menu-icon context-menu-icon-quit" } }; @@ -1450,16 +1454,16 @@ (console.error || console.log).call(console, 'When you reject a promise, provide an "items" object, equal to normal sub-menu items'); } } - finishPromiseProcess(errorItem); + finishPromiseProcess(opt,root,errorItem); }; - function finishPromiseProcess(items) { + function finishPromiseProcess(opt,root,items) { opt.items = items;//override promise to 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. success, error, notify. (don't track notify). - promise.then(completedPromise, errorPromise); + promise.then(completedPromise.bind(this, opt, root), errorPromise.bind(this, opt, root)); } }; From 860c2aaed02dd5123b83c6ed5ba43c82dc18d4a6 Mon Sep 17 00:00:00 2001 From: Ruud-cb Date: Mon, 25 Jul 2016 15:38:45 +0200 Subject: [PATCH 6/9] code cleanup --- src/jquery.contextMenu.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/jquery.contextMenu.js b/src/jquery.contextMenu.js index cddc8394..3abf0de6 100755 --- a/src/jquery.contextMenu.js +++ b/src/jquery.contextMenu.js @@ -1159,7 +1159,7 @@ } }); } else { - var hasSubItems = false; + // add label for input if (item.type === 'cm_seperator') { $t.addClass('context-menu-separator ' + root.classNames.notSelectable); @@ -1167,9 +1167,8 @@ $t.addClass('context-menu-html ' + root.classNames.notSelectable); } else if (item.type === 'sub') { - hasSubItems = true;//we don't want to execute the next else-if if it is a sub. - } - else if (item.type) { + //we don't want to execute the next else-if if it is a sub. + } else if (item.type) { $label = $('').appendTo($t); createNameNode(item).appendTo($label); @@ -1180,7 +1179,6 @@ k.inputs[key] = item; }); } else if (item.items) { - hasSubItems = true; item.type = 'sub'; } @@ -1260,7 +1258,7 @@ break; } - if (hasSubItems) + if (item.type === 'sub') { //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 @@ -1462,7 +1460,7 @@ 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. success, error, notify. (don't track notify). + //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)); } }; From 4464e034ddbae9f66f4fbe55f74d43c0991e9984 Mon Sep 17 00:00:00 2001 From: Ruud-cb Date: Mon, 25 Jul 2016 15:42:04 +0200 Subject: [PATCH 7/9] some white spaces --- src/jquery.contextMenu.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/jquery.contextMenu.js b/src/jquery.contextMenu.js index 3abf0de6..ef7a55e1 100755 --- a/src/jquery.contextMenu.js +++ b/src/jquery.contextMenu.js @@ -1159,14 +1159,12 @@ } }); } 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') { + } else if (item.type === 'sub') { //we don't want to execute the next else-if if it is a sub. } else if (item.type) { $label = $('').appendTo($t); @@ -1258,8 +1256,7 @@ break; } - if (item.type === 'sub') - { + if (item.type === 'sub') { //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) { @@ -1269,7 +1266,6 @@ // normal submenu. op.create(item, root); } - } // disable key listener in if (item.type && item.type !== 'sub' && item.type !== 'html' && item.type !== 'cm_seperator') { From f0a17f3625be7869614f25e526ff3ee9d7b4f3f3 Mon Sep 17 00:00:00 2001 From: Ruud-cb Date: Fri, 26 Aug 2016 17:23:23 +0200 Subject: [PATCH 8/9] on promise reject accept string or an normal menu item object. --- src/jquery.contextMenu.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/jquery.contextMenu.js b/src/jquery.contextMenu.js index ef7a55e1..a97fa3ed 100755 --- a/src/jquery.contextMenu.js +++ b/src/jquery.contextMenu.js @@ -1442,12 +1442,14 @@ }; function errorPromise(opt,root,errorItem) { //user called promise.reject() with an error item, if not, provide own error item. - if ( typeof errorItem === 'string' || errorItem === undefined) { + if (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) { From 1cd7d07229ed54e395382fdc07afbe82a56796ca Mon Sep 17 00:00:00 2001 From: Ruud-cb Date: Fri, 26 Aug 2016 17:27:05 +0200 Subject: [PATCH 9/9] inside switch is nicer --- src/jquery.contextMenu.js | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/jquery.contextMenu.js b/src/jquery.contextMenu.js index a97fa3ed..0a8fff8a 100755 --- a/src/jquery.contextMenu.js +++ b/src/jquery.contextMenu.js @@ -1232,11 +1232,18 @@ case 'sub': createNameNode(item).appendTo($t); - item.appendTo = item.$node; - //op.create(item, root); decide later, might be a promise. $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': @@ -1256,17 +1263,6 @@ break; } - if (item.type === 'sub') { - //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); - } - } // disable key listener in if (item.type && item.type !== 'sub' && item.type !== 'html' && item.type !== 'cm_seperator') { $input