I have found a very useful macro which helped me a lot with implementing drag and drop functionality. The macro is built upon ChapelRs simple inventory.
I have two problems which I cannot find solution for, can somebody help me with the Javascript part?
The macro is designed to determine span-elements as droppables. But for my game I would like the droppables to be div elements, as in this prototype (player is supposed to drag files into correspondent folder). The JavaScript part of the macro is:
Macro.add('droppable', {
tags: ["item", "default"],
handler: function() {
var dat = new Map();
for (var i = 1; i < this.payload.length; ++i) {
var key = this.payload[i].name === "default" ? "default" : "inv-" + this.payload[i].args[0];
dat.set(key, this.payload[i].contents);
}
var $wrapper = $('<span class="droppable" ondrop="dropInv(event)" ondragover="allowDrop(event)"></span>');
var $itemTextWrapper = $('<span class="droppableText"/>');
$wrapper.data("actions", dat);
$wrapper.html($itemTextWrapper.wiki(this.args[0]));
$wrapper.appendTo(this.output);
}
});
I tried changing $wrapper to $('<div class="droppable" ondrop="dropInv(event)"..., but still get the same error.
Unlike intended in original macro, I do not know items in advance, because I generate them newly for every game (new file names). Because of this, I cannot follow the structure suggested by macro author:
I need macro to check if dragged item is found in an array or not, like e.g.:
<<droppable "spreadsheets">>\
<<itemInArray "$xls">>\
<< code to be executed if correct file was dragged....>>
<<default>>\
<< code to be executed if incorrect file was dragged>>
<</droppable>>
Full code of drag&drop macro can be found here. Could you guys help me? Thank you very much, hopefully you guys have great holidays.
Happy new year! Sorry guys for bothering again, but could somebody maybe help me with my problem?
For first problem (drag stuff onto div instead of span) I still have no idea what problem could be.
For second problem, I think behaviour is controlled here:
setup.dragDrop.dropInv = function(ev) {
ev.preventDefault();
var invItemID = ev.dataTransfer.getData("text");
var $targetWrapper = $(ev.target).parent();
var actionToPerform = $targetWrapper.data("actions").get(invItemID);
if(actionToPerform === undefined) {
actionToPerform = $targetWrapper.data("actions").get("default");
}
$targetWrapper.wiki(actionToPerform);
};
I am currently doing codeadacademy javascript course, so knowledge is very limited. But I understand this line: var actionToPerform = $targetWrapper.data("actions").get(invItemID);
tells macro to do what is inside item code when item is dropped. Soā¦ do I follow correct way if I fiddle with this line and turn it into something like this?
if (item string finishes with ".doc"){
var actionToPerform = $targetWrapper.data("actions").get(invItemID);}
Small update from me: First problem (error when dragging items on folders) was mistake on my side, CSS style for folders was put onto .droppable instead of .droppabletext. Works now in new prototype.
For second problem (make makro fire correct behaviour on predefined set of āfileā items), I still struggle. As I am not able to change Javascript of macro, I thought I try āstupid print trickā to achieve what I need. So I tried with e.g. $docFiles = ["file1.doc", "file2.doc", "file3.doc"]
Youāre only seeing the <<default>> case because itās the only one that exists when the macro is invoked. You cannot add cases to the macro post-invocation, which is what youāre attempting in your above example. What you would need to do to utilize the Stupid Print Trick is to print the entire macro, so that the cases are already in place when itās invoked.
That said. Hereās a slightly modified version of the macro that should do what you want without needing to use the Stupid Print Trick: (untested)
// Usage:
// <<droppable "TargetText">>
// <<item "InvItem">>
// ā¦your content hereā¦
// <<itemsarray Array>>
// ā¦your content hereā¦
// <<default>>
// ā¦your content hereā¦
// <</droppable>>
Macro.add('droppable', {
tags : ['item', 'itemsarray', 'default'],
handler : function () {
var dat = new Map();
for (var i = 1; i < this.payload.length; ++i) {
var payload = this.payload[i];
if (payload.name === 'itemsarray') {
var items = payload.args[0];
if (!(items instanceof Array)) {
return this.error('itemsarray argument must yield an Array (received: ' + Util.getType(items) + ')');
}
items.forEach(function (item) {
dat.set('inv-' + item, payload.contents);
});
}
else {
dat.set(
payload.name === 'default' ? 'default' : 'inv-' + payload.args[0],
payload.contents
);
}
}
var $wrapper = $('<span class="droppable" ondrop="dropInv(event)" ondragover="allowDrop(event)"></span>');
var $itemTextWrapper = $('<span class="droppableText"></span>');
$wrapper.data('actions', dat);
$wrapper.html($itemTextWrapper.wiki(this.args[0]));
$wrapper.appendTo(this.output);
}
});
Using that version you should be able to do the following:
Thank you very much, @TheMadExile for your help. I integrated your macro into new prototype.
For some reason, I still get āWrong!ā for all items.
Arrays are populated in the same passage as where macro is called, but before (I printed array contents to prove this). Do they need to be populated in a passage before the passage, where the macro is called?
Could you also help me with one last thing? In order to simulate desktop file system, file needs to disappear after being dragged into folder. How can I < < remove > > the file that has just been dragged into correct folder?
Your current <<positionFiles>> widget does not prefix the element IDs of the various items with inv- as it is supposed to do, based on the examples from the original source. Thus, the IDs of the dropped items do not match the IDs generated within the <<droppable>> macro, which is why the default case is always used.
There are a few ways to address this, however, it would probably be best to have your <<positionFiles>> widget add the prefix as shown in the examples. To do so, for each case do the following:
Youāll need to alter your current dropInv() function. Something like the following will remove the itemās element from the DOM. Note: I did not look too deep into your demo to see if youāre actually integrating with Chapelās inventory system or not, so this may need to be extended to remove the items from it as well.
setup.dragDrop.dropInv = function(ev) {
ev.preventDefault();
var invItemId = ev.dataTransfer.getData('text');
var actionMap = $(ev.target).parent().data('actions');
var hasItem = actionMap.has(invItemId);
var action = hasItem ? actionMap.get(invItemId) : actionMap.get('default');
$.wiki(action);
if (hasItem) {
var invItem = document.getElementById(invItemId);
if (invItem && invItem.parentNode) {
invItem.parentNode.removeChild(invItem);
}
}
};
While I tested, one thing happened repeatedly, which I cannot understand: When items are dragged to folders, mouse pointer is mostly displaying āĆā-symbol. If you release mouse button there, nothing happens - as expected.
But on some occasions, mouse pointer changes to drop symbol (pointer with checkered rectangle at bottom). When I release mouse button there, I get Error: actionMap is undefined.
But there are no visible elements, nor does inspector show anything besides folders and items.
I assume youāre using a Blink-/WebKit-based browser to test, because I didnāt observe that behavior in Firefox.
The issue seems to stem from setting your .droppableText class to display as block. While Blink-browsers respect the dimensions you set as far as the borders of the elements go, it does not seem to respect them for the drop target bounding box.
Simply changing the .droppableText class to display as inline-block resolves that issue. For example:
Doing that, however, does cause an issue where your folders are now inline, but that can be fixed by adding a few extra line breaks in your Desktop passage. For example: