Currently what I have partially works (in production seems to work about 50% of the time) in hosted mode it's only working on the first Drop (could be because of recent changes). Currently we are doing this with JSNI. I'll paste the full code below but the key parts are that on the drop event I need to get the 'text' data, e.g.
Then I parse that data/URI and build a div which I then append to the original element.
Some of the things I'm not sure about are...what Document to be working with. The original code used document in the JSNI method but I see online that $doc should be used. Or I could pass into the JSNI method the document, e.g.
Or ideally I'd prefer to do this with a more pure GWT solution and use less JSNI if that's possible. How best to respond to these native drops? Why would this code only be working for the first drop? (Note, we have several instances of this widget in the application) Here is the full code. (The first set of methods ininitDnD just parse URI...nothing interesting until the allowDrop/droppedHandler/addEvent methods)
private native void initDnD(Document document, Element elem) /*-{
function strictEncodeURIComponent(string) {
return encodeURIComponent(string).replace(/[!'()*]/g, escape);
}
encode = strictEncodeURIComponent;
decode = decodeURIComponent;
var parseURI = function (string) {
var pos, t, parts = {};
pos = string.indexOf('#');
if (pos > -1) {
// escaping?
parts.fragment = string.substring(pos + 1) || null;
string = string.substring(0, pos);
}
// extract query
pos = string.indexOf('?');
if (pos > -1) {
// escaping?
parts.query = string.substring(pos + 1) || null;
string = string.substring(0, pos);
}
// extract protocol
if (string.substring(0, 2) === '//') {
// relative-scheme
parts.protocol = '';
string = string.substring(2);
// extract "user:pass@host:port"
string = parseURIAuthority(string, parts);
} else {
pos = string.indexOf(':');
if (pos > -1) {
parts.protocol = string.substring(0, pos);
if (string.substring(pos + 1, pos + 3) === '//') {
string = string.substring(pos + 3);
// extract "user:pass@host:port"
string = parseURIAuthority(string, parts);
} else {
string = string.substring(pos + 1);
parts.urn = true;
}
}
}
// what's left must be the path
parts.path = string;
// and we're done
return parts;
};
var parseURIHost = function (string, parts) {
// extract host:port
var pos = string.indexOf('/'),
t;
if (pos === -1) {
pos = string.length;
}
if (string[0] === "[") {
// I claim most client software breaks on IPv6 anyways. To simplify things, URI only accepts
// IPv6+port in the format [2001:db8::1]:80 (for the time being)
var bracketPos = string.indexOf(']');
parts.hostname = string.substring(1, bracketPos) || null;
parts.port = string.substring(bracketPos + 2, pos) || null;
} else if (string.indexOf(':') !== string.lastIndexOf(':')) {
// IPv6 host contains multiple colons - but no port
// this notation is actually not allowed by RFC 3986, but we're a liberal parser
parts.hostname = string.substring(0, pos) || null;
parts.port = null;
} else {
t = string.substring(0, pos).split(':');
parts.hostname = t[0] || null;
parts.port = t[1] || null;
}
if (parts.hostname && string.substring(pos)[0] !== '/') {
pos++;
string = "/" + string;
}
return string.substring(pos) || '/';
};
var parseURIAuthority = function (string, parts) {
string = parseURIUserinfo(string, parts);
return parseURIHost(string, parts);
};
var parseURIUserinfo = function (string, parts) {
// extract username:password
var pos = string.indexOf('@'),
firstSlash = string.indexOf('/'),
t;
// authority@ must come before /path
if (pos > -1 && (firstSlash === -1 || pos < firstSlash)) {
t = string.substring(0, pos).split(':');
parts.username = t[0] ? decode(t[0]) : null;
parts.password = t[1] ? decode(t[1]) : null;
string = string.substring(pos + 1);
} else {
parts.username = null;
parts.password = null;
}
return string;
};
var parseURIQuery = function (string) {
if (!string) {
return {};
}
// throw out the funky business - "?"[name"="value"&"]+
string = string.replace(/&+/g, '&').replace(/^\?*&*|&+$/g, '');
if (!string) {
return {};
}
var items = {},
splits = string.split('&'),
length = splits.length;
for (var i = 0; i < length; i++) {
var v = splits[i].split('='),
name = encodeURIQuery(v.shift()),
value = v.length ? decodeURIQuery(v.join('=')) : null;
if (items[name]) {
if (typeof items[name] === "string") {
items[name] = [items[name]];
}
items[name].push(value);
} else {
items[name] = value;
}
}
return items;
};
var encodeURIQuery = function (string) {
return encode(string + "").replace(/%20/g, '+');
};
var decodeURIQuery = function (string) {
return decode((string + "").replace(/\+/g, '%20'));
};
function allowDrop(event) {
if (event.stopPropagation) {
event.stopPropagation();
}
if (event.preventDefault) {
event.preventDefault();
}
return false;
}
function droppedHandler(event) {
// Try to use the 'text' everytime.
try {
alert("event= " + event);
var data = event.dataTransfer.getData("Text");
alert("data=" + data);
if (data != null && data != "") {
try {
var parsedURI = parseURI(data);
var urlParameters = parseURIQuery(parsedURI.query);
var appsrc = urlParameters['appsrc'];
if (appsrc != null && appsrc == "COREFX") {
try {
// For some versions of IE, need to do this to prevent duplicates.
event.dataTransfer.setData('Text', '');
}
catch (e) {
}
if (event.preventDefault) {
event.preventDefault();
}
var title = data;
if (urlParameters['title'] != null) {
title = urlParameters['title'];
}
var newContentWrapper = document.createElement('div');
newContentWrapper.setAttribute('draggable', true);
newContentWrapper.setAttribute('ondragstart', "dragStart(event)");
var newContent = document.createElement('a');
newContent.setAttribute('href', data);
newContent.setAttribute('class', "external_system");
if (urlParameters['icon'] != null) {
var icon = decodeURI(urlParameters['icon']);
var newContentIcon = document.createElement('img');
newContentIcon.setAttribute('src', icon);
newContent.appendChild(newContentIcon);
}
var newContentTitle = document.createTextNode(title);
newContent.appendChild(newContentTitle);
newContentWrapper.appendChild(newContent);
var targetElement = event.target || event.srcElement;
targetElement.appendChild(newContentWrapper);
return false;
}
}
catch (e) {
}
}
}
catch (error) {
}
return true;
}
var addEvent = (function () {
if (document.addEventListener) {
return function (el, type, fn) {
if (el && el.nodeName || el === window) {
el.addEventListener(type, fn, false);
} else if (el && el.length) {
for (var i = 0; i < el.length; i++) {
addEvent(el[i], type, fn);
}
}
};
} else {
return function (el, type, fn) {
if (el && el.nodeName || el === window) {
el.attachEvent('on' + type, function (eventParam) {
if (!eventParam) {
eventParam = window.event;
}
return fn.call(el, eventParam);
});
} else if (el && el.length) {
for (var i = 0; i < el.length; i++) {
addEvent(el[i], type, fn);
}
}
};
}
})();
addEvent(elem, 'drop', droppedHandler);
addEvent(elem, 'dragover', allowDrop);
}-*/;