Skip to content
This repository has been archived by the owner on Mar 10, 2024. It is now read-only.

Data Transfer Design

Marcel Juenemann edited this page Feb 20, 2016 · 2 revisions

This design document outlines how the dataTransfer object will be used in version 2.0 of angular-drag-and-drop-lists. The focus lies mainly on which mime type will be used when calling setData on the dataTransfer object.

Basics

When calling setData we want to store a serialized version of the item that the user provided us in dnd-draggable. The HTML5 spec says that the format we pass to setData can be any string, but will be converted to ASCII lowercase. It is recommended to use a mime type here, since the drag & drop API is native and therefore the format might read by other applications. Also, when using dnd-external-sources we don't usually want to accept data that was not generated by this directive.

Since the data we send is in JSON format, a good mime type to use would be application/json. However, it's not really useful for us to accept drops from other applications when using dnd-external-sources, since we will most likely not be able to process it correctly. Therefore, we choose a custom mime type, namely application/x-dnd.

Encoding the dnd-type in the mime type

The user can give objects specific types using the dnd-type attribute, and accordingly limit the types accepted by lists with dnd-allowed-types. In previous versions of the directive we implemented this using a global variable, which means the type was not transferred when dragging objects across documents with dnd-external-sources. We can enable this by encoding the dnd-type in the mime type, e.g. for objects of type foo our mime type would be application/x-dnd-foo.

On the receiving side, the dnd-list will accept all drops that start with the prefix application/x-dnd. If the dnd-allowed-types attribute is set, it will limit the mime types it accepts accordingly.

Workaround for Micorsoft Internet Explorer

Unfortunately, IE only allows to call setData with either Text or URL format. For this reason, previous versions of the directive were always using the Text format, which has the disadvantage that our objects can be dropped into any text field outside of our application, and vice versa if we allowed drops from external sources.

The new idea for version 2.0 is to try to set a custom mime type, but then catch the exception that will only be thrown by IE. We can then use the Text format as fallback. Luckily, IE does also not convert Text into text/plain as specified by the HTML5 standard, therefore we can just accept Text drops on the receiving side without having a negative effect on modern browsers (because modern browsers will never have a Text type in dataTransfer).

Now that we use the Text mime type for Internet Explorer, we have to solve the problem of handling dnd-type again. There are two options:

  1. Storing the actual mime type in a global variable. This will work perfectly for operations within the same window, but will fail miserably with dnd-external-sources.
  2. Sending the actual mime type within the transferred data, e.g. {mimeType: 'application/x-dnd-foo', item: {...}}. This will work for external drops as well as for internal drops. The only catch is that we don't have access to getData in the dragover callback, therefore this will always show a placeholder when dragging over a dnd-list, even when dropping will actually not work.

To get the best possible solution in Internet Explorer, we will use both options. As a result IE will work nearly as good as modern browsers, the only thing is that all drops from external sources will show the placeholder while dragging over the target list.

Workaround for Microsoft Edge

So far so good. Now Microsoft developed a new browser, Edge, that kind of follows the HTML5 standard, except it doesn't. As of Feb 2016 setData fails when passing a custom mime type, but seems to work for some set of officially defined mime types. Even worse, the workaround for MSIE using Text no longer works for Edge, because Edge follows the standard in this matter and converts it to text/plain. We don't want to accept text/plain though, because that allows any kind of text to be dropped into our lists.

Therefore, we need another mime type for Edge. I decided on using application/json. As above, we can wrap it inside a try-catch block so it works with MSIE, and then we use the same workarounds for transferring the dnd-type.

Summary

Browser Compatibility Issues (as of Feb 2016)

  • IE: Only supports Text and URL format. Does not convert Text to text/plain in dropzone events
  • Edge: Does not support custom mime types. Would be interesting to get a list of all supported mime types.

Pseudo-Code for dragstart

  • mimeType := "application/x-dnd"
  • if dnd-type set, append it to the mimeType
  • try setData(mimeType, item)
  • on exception:
    • Store the mimeType in a global variable
    • data := {mimeType: mimeType, item: item}
    • try setData("application/json", data)
    • on exception setData("Text", data)

Pseudo-Code for dragenter and dragover

  • Go through dataTransfer.types to find an acceptable type
    • "Text" and "application/json" are always accepted
    • If dnd-allowed-types is not set, any type starting with application/x-dnd is accepted
    • If dnd-allowed-types is set, the mime type's suffix must be one of those
    • If no acceptable type is found, the drop is cancelled
  • If the accepted type above was "Text" or "application/json", also do this:
    • If the drop comes from an internal source, check the mime type stored in the global variable and cancel the drop if it is not acceptable
    • If the drop comes from an external sources, allow it

Pseudo-Code for drop

  • Go through dataTransfer.types to find an acceptable type, as for dragover
  • Call getData with the type from above and deserialize the data
  • If the accepted type above was "Text" or "application/json", also do this:
    • Check the mime type stored in the mimeType field of the transferred object cancel the drop if it is not acceptable