Context menu - almost there, I think

I’m getting close to having a working context menu on my scheduler.

Here’s code which almost works:[code] var modified_event_id = null;
scheduler.templates.event_class = function(start, end, event) {
if (event.id == modified_event_id)
return “copied_event”;
return “”; // default
};

scheduler.attachEvent("onEventCopied", function(ev) {
	dhtmlx.message("Event '<b>"+ev.TaskID+"</b>' copied");
	modified_event_id = ev.id;
	scheduler.updateEvent(ev.id);
});

scheduler.attachEvent("onEventCut", function(ev) {
	dhtmlx.message("Event '<b>"+ev.TaskID+"</b>' cut");
	modified_event_id = ev.id;
	scheduler.updateEvent(ev.id);
});

scheduler.attachEvent("onEventPasted", function(isCopy, modified_ev, original_ev) {
	modified_event_id = null;
	scheduler.updateEvent(modified_ev.id);

	var evs = scheduler.getEvents(modified_ev.start_date, modified_ev.end_date);
	if (evs.length > 1) {
		dhtmlx.modalbox({
			text: "There is already an event here. What do you want to do?",
			width: "500px",
			position: "middle",
			buttons:["Cancel", "Edit the event", "Save changes"],
			callback: function(index) {
				switch(+index) {
					case 0:
						if (isCopy) {
							// This is a copy operation, delete new event
							scheduler.deleteEvent(modified_ev.id);
						} else {
							// This is a cut operation, restore previous dates
							modified_ev.start_date = original_ev.start_date;
							modified_ev.end_date = original_ev.end_date;
							scheduler.setCurrentView();
						}
						break;
					case 1:
						scheduler.showLightbox(modified_ev.id);
						break;
					case 2:
						return;
				}
			}
		});
	}
});

var menu = new dhtmlXMenuObject();
menu.setSkin("dhx_terrace");
menu.setIconsPath("scheduler/ContextMenu/images/");
menu.renderAsContextMenu();
menu.loadStruct("scheduler/ContextMenu/dhxmenu.xml?e=" + new Date().getTime());

menu.attachEvent("onClick", function(id, e) {
	var obj = scheduler.getActionData(e);

	if(id == 'cut'){ // Check the ID of the clicked menu item
		scheduler.callEvent("onEventCut", [e]);

	}else if(id == 'copy'){
		scheduler.callEvent("onEventCopied", [e]);

	}else if(id == 'paste'){
		var ev = scheduler.getEvent(scheduler._buffer_id);
		if (ev) {
			var event_duration = ev.end_date-ev.start_date;
			if (isCopy) {
				var new_ev = scheduler._lame_clone(ev);
			 	// clear_event_after(new_ev);
			 	new_ev.id = scheduler.uid();
			 	new_ev.start_date = new Date(date);
			 	new_ev.end_date = new Date(new_ev.start_date.valueOf() + event_duration);
			 	scheduler.addEvent(new_ev);
			 	scheduler.callEvent("onEventPasted", [isCopy, new_ev, ev]);
			}else { // Cut operation
				var copy = scheduler._lame_copy({}, ev);
				//clear_event_after(copy);
				copy.start_date = new Date(date);
				copy.end_date = new Date(copy.start_date.valueOf() + event_duration);
				var res = scheduler.callEvent("onBeforeEventChanged",[copy, e, false]);
				if (res) {
					ev.start_date = new Date(copy.start_date);
					ev.end_date = new Date(copy.end_date);
					scheduler.render_view_data(); // Redraw all events

					scheduler.callEvent("onEventPasted", [isCopy, ev, copy]);
					isCopy = true; // Switch to copy after first paste operation
				}
			}
	   }
	}
	return false;
});

scheduler.attachEvent("onContextMenu", function(event_id, native_event_object) {
 if (event_id) {
 	var posx = 0;
	var posy = 0;
	if (native_event_object.pageX || native_event_object.pageY) {
		posx = native_event_object.pageX;
		posy = native_event_object.pageY;
	} else if (native_event_object.clientX || native_event_object.clientY) {
		posx = native_event_object.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
		posy = native_event_object.clientY + document.body.scrollTop + document.documentElement.scrollTop;
	}
	menu.showContextMenu(posx, posy);
	return false; // Prevent default action and propagation
 }
return true;
});[/code] What I've tried to do here is to call existing, working functions to avoid duplicating the code which is in the 'Cut/Copy/Paste' in dhtmlxscheduler_key_nav.js. However, when I right-click the event with ID '1', then select one of the menu items - say, 'Copy' - the message box displays "Event 'undefined' copied, whereas using the keyboard shortcut, it displays, correctly, "Event '1' copied". I'm obviously lost in how to pass the correct event details to these functions. 

I then tried some code I found in this forum and substituted what I had with that, as in:[code] scheduler.attachEvent(“onEventCopied”, function(ev) {
dhtmlx.message(“Event '”+ev.TaskID+"’ copied");
modified_event_id = ev.id;
scheduler.updateEvent(ev.id);
});

scheduler.attachEvent("onEventCut", function(ev) {
	dhtmlx.message("Event '<b>"+ev.TaskID+"</b>' cut");
	modified_event_id = ev.id;
	scheduler.updateEvent(ev.id);
});

scheduler.attachEvent("onEventPasted", function(isCopy, modified_ev, original_ev) {
	modified_event_id = null;
	scheduler.updateEvent(modified_ev.id);

	var evs = scheduler.getEvents(modified_ev.start_date, modified_ev.end_date);
	if (evs.length > 1) {
		dhtmlx.modalbox({
			text: "There is already an event here. What do you want to do?",
			width: "500px",
			position: "middle",
			buttons:["Cancel", "Edit the event", "Save changes"],
			callback: function(index) {
				switch(+index) {
					case 0:
						if (isCopy) {
							// This is a copy operation, delete new event
							scheduler.deleteEvent(modified_ev.id);
						} else {
							// This is a cut operation, restore previous dates
							modified_ev.start_date = original_ev.start_date;
							modified_ev.end_date = original_ev.end_date;
							scheduler.setCurrentView();
						}
						break;
					case 1:
						scheduler.showLightbox(modified_ev.id);
						break;
					case 2:
						return;
				}
			}
		});
	}
});

var menu = new dhtmlXMenuObject();
menu.setSkin("dhx_terrace");
menu.setIconsPath("scheduler/ContextMenu/images/");
menu.renderAsContextMenu();

// menu.addContextZone(“scheduler_here”);
// menu.loadStruct(“scheduler/ContextMenu/dhxmenu.xml?e=” + new Date().getTime());

menu.addNewChild(null, 0, "menu_cb_copy", "Copy");
menu.addNewChild(null, 1, "menu_cb_cut", "Cut");
menu.addNewChild(null, 2, "menu_cb_paste", "Paste");

var event_id, cb_date, cb_isCopy, cb_section = null;

menu.attachEvent("onClick", function (id) {
    eval(id)();
});

scheduler.attachEvent("onContextMenu", function (event_id_loc, native_event_object) {
    event_id = event_id_loc;
    //cb_date = scheduler.getActionData(native_event_object).date;
    //cb_date = scheduler.getEvent(event_id_loc);
    var event_loc = scheduler.getEvent(event_id_loc);
    if (event_loc != null)
    {
	cb_date = event_loc.start_date;
    }
    else
    {
	//alert(event_id_loc);
	//cb_date = scheduler.getActionData(native_event_object).date;
	var pos = scheduler.getActionData(native_event_object);

	var date;
	var timeline = scheduler.matrix[scheduler.getState().mode];
	if (timeline && timeline.days) {
	    date = new Date(pos.section);
	    date.setHours(pos.date.getHours());
	    date.setMinutes(pos.date.getMinutes());
	} else {
	    date = new Date(pos.date);
	}
	cb_date = date;
    }
    //alert(cb_date);
    cb_section = scheduler.getActionData(native_event_object).section;
    //alert(cb_date);

    /* Menu position */
    var posx = 0;
    var posy = 0;
    if (native_event_object.pageX || native_event_object.pageY) {
		posx = native_event_object.pageX;
		posy = native_event_object.pageY;
    } else if (native_event_object.clientX || native_event_object.clientY) {
		posx = native_event_object.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
		posy = native_event_object.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }

    /* Menu items */
    if (event_id) {
		menu.showItem("menu_cb_copy");
		menu.showItem("menu_cb_cut");
		menu.hideItem("menu_cb_paste");
		menu.showContextMenu(posx, posy);
    }
    else {
		menu.hideItem("menu_cb_copy");
		menu.hideItem("menu_cb_cut");
		menu.showItem("menu_cb_paste");
		menu.showContextMenu(posx, posy);
    }
    return false; // prevent default action and propagation
});

function menu_cb_copy() {
    scheduler._buffer_id = event_id;
    cb_isCopy = true;
    scheduler.callEvent("onEventCopied", [scheduler.getEvent(event_id)]);
}
function menu_cb_cut() {
    scheduler._buffer_id = event_id;
    cb_isCopy = false;
    scheduler.callEvent("onEventCut", [scheduler.getEvent(event_id)]);
}
function menu_cb_paste() {
    var ev = scheduler.getEvent(scheduler._buffer_id);
    if (ev) {
	if (cb_isCopy) { // copy-paste
	    new_ev = _cb_make_paste_event(ev);
	    new_ev.id = scheduler.uid();

	    scheduler.addEvent(new_ev);
	    scheduler.callEvent("onEventPasted", [cb_isCopy, new_ev, ev]);
	} else { // cut-paste
	    new_ev = _cb_make_paste_event(ev);

	    var a = scheduler.callEvent("onBeforeEventChanged", [new_ev, null, !1, ev]);
	    a && (scheduler.addEvent(new_ev), scheduler.callEvent("onEventPasted", [cb_isCopy, new_ev, ev]));
	}
    }
}
function _cb_make_paste_event(ev) {
    var event_duration = ev.end_date - ev.start_date;
    var new_ev = scheduler._lame_copy({}, ev);
    //alert(cb_date);
    new_ev.start_date = new Date(cb_date);
    new_ev.end_date = new Date(new_ev.start_date.valueOf() + event_duration);

    if (cb_section) {
	var a = scheduler.getState().mode, d = null;
	scheduler.matrix[a] ? d = scheduler.matrix[a].y_property : scheduler._props[a] && (d = scheduler._props[a].property), new_ev[d] = cb_section;
    }

    return new_ev;
}[/code] With this, I get an error when right-clicking the event: " Object doesn't support property or method 'showItem'" That would seem to suggest that the 'menu' object isn't fully initialised.

I’m trying a different tack.

As the keyboard shortcuts work faultlessly, I’m trying to leverage them. It also saves duplicating code. So, when the ‘Copy’ menu item is selected, I want to trigger the ‘Ctrl+C’ key sequence. My problem is passing the document element ID to the function. Here’s the call to the triggering function. Presently, it has ‘document.body’ as the element ID. This obviously isn’t going to work, as there’s no event data associated with that. I want to pass the element ID of the event.[code]var bln_bubbles = true;
var bln_cancelable = true;
var bln_ctrlKey = true;
var bln_altKey = false;
var bln_shiftKey = false;
var bln_metaKey = false;

triggerKeyboardEvent(document.body, bln_bubbles, bln_cancelable, bln_ctrlKey,bln_altKey, bln_shiftKey, bln_metaKey, 88);[/code] Further down, we have the actual function:[code] function triggerKeyboardEvent(el, bubbles, cancelable, ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg, keyCode){
var keyboardEvent = document.createEvent(“KeyboardEvent”);

    var initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";

    keyboardEvent[initMethod](
    "keydown",
    bubbles,
    cancelable,
    window,
    ctrlKeyArg,
    altKeyArg,
    shiftKeyArg,
    metaKeyArg,
    keyCode,
    0          // charCode
	);

	el.dispatchEvent(keyboardEvent);
}[/code]

[sigh…] I’ve now got copy-and-paste and cut-and-paste working when an existing event is selected. What I can’t get working is where I paste into a column where there is no event. Depending on which version of code I’m using, either nothing happens or the event that was copied is duplicated in the same day (obviously along with the prompt that there’s already an event present).

So my mind wanders back to trying to simply call the keyboard shortcuts, which work flawlessly, both when I have an existing event selected and when I click in a day with no event.

So…the question: what code do I need to add in order for my right-click menu options to call the keyboard shortcuts for cut, copy and paste?