So….I always wanted to Drag and Drop an external file into a Flex application running in the browser. Well.. you can use the FileReference API to browse and load a file, but I really wanted to DnD. Unfortunately, this is not allowed due to security constraints of the Flash Player. However, using the latest proposed W3C
DnD API and the
File API, I should be able to use the last two and the AS3 ExternalInterface API to accomplish what I want. So, here is the content of a simple xml file that I would like to DnD and render on a map:
<markers>
<marker x="45" y="45" label="M 1" info="This is M1"/>
<marker x="-45" y="-45" label="M 2" info="This is M2"/>
</markers>
I modified the HTML wrapper to use the JavaScript DnD and File API (if available) to listening for “dragenter”, “dragover” and “drop” events on the FlashPlayer container.
var DnD = {
loadHandler:function () {
var dropContainer = document.getElementById("DnDApp");
dropContainer.addEventListener("dragenter", function (event) {
event.stopPropagation();
event.preventDefault();
}, false);
dropContainer.addEventListener("dragover", function (event) {
event.stopPropagation();
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
}, false);
dropContainer.addEventListener("drop", function (event) {
event.stopPropagation();
event.preventDefault();
var files = event.dataTransfer.files, len = files.length;
for (var i = 0; i < len; i++) {
var file = files[i];
var fileReader = new FileReader();
fileReader.onload = function (event) {
dndApp.drop(event.target.result);
}
fileReader.readAsText(file);
}
}, false);
}
};
if( window.File && window.FileReader){
window.addEventListener("load", DnD.loadHandler, false);
} else {
alert('Your browser does not support File/FileReader !');
}
On dragenter, I stop the event propagation and prevent the default behavior. On dragover, I do the same and in addition, I update the drop effect to show a “+” icon over the drop area. And finally, on drop, I iterate over the list of dropped files, whereupon I read as text each file using the FileReader API. When the file is read (remember, this is all asynchronous), I hand over the content to the Flex application. On creation completion of the Flex application, the “drop” callback is registered using the EternalInterface enabling the host javascript wrapper to invoke the internal dropHandler function.
private function this_creationCompleteHandler(event:FlexEvent):void
{
ExternalInterface.call("setObjectID",
ExternalInterface.objectID);
ExternalInterface.addCallback("drop", dropHandler);
}
private function dropHandler(text:String):void
{
const doc:XML = new XML(text);
for each (var markerXML:XML in doc.marker)
{
const mapPoint:MapPoint = new WebMercatorMapPoint(
markerXML.@x,
markerXML.@y);
arrcol.addItem(new Graphic(
mapPoint,
null, {
label: markerXML.@label,
info: markerXML.@info }));
}
}
The latter accepts a String argument that is converted into an XML instance, and using E4X, each child marker element is converted to a Graphic that is added to a graphic layer graphic provider array collection. Cool, eh ? Note that the graphic layer has it infoWindowRenderer property defined, is such a way that if you click on any of its graphics, a info window content will be displayed whose content is an instance of the defined component. Like usual all the source code is available
here. Have fun DnD’ing.
You can see the application in action by download the
markers.xml file, and drag and drop it on the application running
here.
PS: As of this writing, the two JS APIs work in Google Chrome 16 and later, Firefox 3.6 and later, Safari 6 will support the standard File API and our favorite (Not!) Internet Explorer 10 (Preview 2+). One of these days, will come back to this and use something like Dojo DnD to abstract me from all this - that will be a nice post!