Tuesday, March 20, 2012

DnD File using HTML5 into Flex Web App

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!

4 comments:

Simon said...

I have not been able to drag the markers.xml onto your application with FP11 and either Chrome 17 or FF11. Does this still work for you?

jman said...

I'm having the same problem too

Mansour Raad said...

Hum - tried it on my mac chrome 18 and FF 11 without issues - might be a windows issue !

chris said...

Used FF 11 and GC 18 on a Mac. Awesome work!