Drag and Drop
Table of Contents
Introduction
This document describes how drag and drop works in the EPiServer user interface. Drag-and-drop in EPiServer uses the dnd system in Dojo with some additions. A good tutorial about the dnd system in Dojo can be found here http://www.sitepen.com/blog/2011/12/05/dojo-drag-n-drop-redux/. Note: To use the additional functionality in EPiServer your drag source or target class should implement the EPiServer shell classes instead of the Dojo base classes ("epi/shell/dnd/Source" or "epi/shell/dnd/Target").
Creating Sources and Targets
The following example is a fully functioning component that both implements a drag source and a drag target.
define("epi/samples/dnd/DndSamples", [
"dojo",
"dijit",
"dijit/_Widget",
"dijit/_TemplatedMixin",
//EPiServer dnd classes
"epi/shell/dnd/Source",
"epi/shell/dnd/Target"
],
function (dojo, dijit, _Widget, _TemplatedMixin, Source, Target) {
return dojo.declare("epi.samples.dnd.DndSamples", [_Widget, _TemplatedMixin], {
templateString: '\
<div>\
<h2>Source</h2>\
<ul dojoAttachPoint="source"></ul>\
<h2>Target</h2>\
<ul dojoAttachPoint="target"><li>Test</li></ul>\
</div>',
//Seems to be a bug when we have an empty list so we just create a dummy list item for the example.
postCreate: function () {
this.inherited(arguments);
this._setupSource();
this._setupTarget();
},
_setupSource: function () {
var source = new Source(this.source, {
creator: dojo.hitch(this, this._createDragItem),
copyOnly: true,
selfAccept: false,
selfCopy: false
});
source.insertNodes(false,
[{ url: "http://www.fakecompany.com", text: "Company web site", type: ["link"] },
{ url: "http://www.episerver.com", text: "EPiServer", type: ["link"] },
{ url: "http://www.dn.se", text: "Daily news", type: ["link"]}]);
},
_setupTarget: function () {
var target = new Target(this.target, {
accept: ["link", "someothertype"],
//Set createItemOnDrop if you're only interested to receive the data, and not create a new node.
createItemOnDrop: true
});
this.connect(target, "onDropData", "_onDropData");
},
_createDragItem: function (item, hint) {
var node;
if (hint == "avatar") {
//This node is used when dragging the item.
node = dojo.create("div", { innerHTML: item.text });
} else {
node = dojo.create("li", { innerHTML: item.text });
}
return {
"node": node,
"type": item.type,
"data": item
};
},
_onDropData: function (dndData, source, nodes, copy) {
//Drop item is an array with dragged items. This example just handles the first item.
var dropItem = dndData ? (dndData.length ? dndData[0] : dndData) : null;
if (dropItem) {
//The data property might be a deffered so we need to call dojo.when just in case.
dojo.when(dropItem.data, dojo.hitch(this, function (value) {
//Do something with the data, here we just log it to the console.
console.log(value);
}));
}
}
});
});
Data Types
Since JavaScript is a loosely typed language data types should be seen as a contract or interface. EPiServer uses a number of known types for drag and drop which are described here:Type name | Data format |
---|---|
"epi.cms.pagereference" "epi.cms.blockreference" "epi.cms.folderreference" "epi.cms.contentreference" |
A string representation of a content reference, for instance "3_122". There are converters that converts specific types (ie page reference) to content references. This means that a drag target that accepts content references also accepts page references but not vice versa. |
"fileurl" | A URL to any file, for instance "/myvpp/myfolder/mydocument.pdf" |
"imageurl" | A URL to an image, for instance "/myvpp/myfolder/myimage.jpg" |
"link" | An object with two properties:
|
"epi.cms.content.lighturi" (there are specific versions as well like "epi.cms.page.lighturi") | An object with two properties:
|
"epi.cms.content.light" (there are specific versions as well like "epi.cms.page.light") | An object with two properties:
|
Type Converters
Sometimes you might want to support drag and drop to or from something that you do not control the source code for. You might also have the need to convert the dragged data to another format. Since you might specify types as an array both for the source and the target you can sometimes handle this by specifying all types that the source can deliver and that the target accepts. For a target you might have to convert the data on acceptance. For the source this works fine as long as the object can be created and still support several types. An example: A dnd source delivers image URL:s. It may specify the type as ["imageurl", "fileurl"] as both these types expects the object to the a string with the URL to the actual content. This content can then be dragged to targets that accepts either an "imageurl" or a "fileurl". But what if you want to drag this content to a target that accepts a link? The source cannot create an object that can be both an "imageurl" or "fileurl" and a link at the same time since the link expects a complex object with two properties: url and text.
To handle these kind of scenarios EPiServer has added something called object type converters. These are simple classes that implements a convert method with sourceDataType, targetDataType and data as parameters. There is a converter registry, which is a singleton, that can be used to register a converter for different combinations of conversions. These can be registered in the initialization method of your module class:
var converterRegistry = this.resolveDependency("epi.objectconverterregistry");
var converter = new ContentLightUriConverter();
converter.registerConverter(converterRegistry);
The following example shows how to convert between different content dnd formats. In a standard installation, this is used to be able to drag pages from for instance the page tree to a page selector to select a page reference or a Tiny MCE editor to create a link to the page.
define(
["dojo",
"epi/dependency",
"epi/UriParser",
"epi/cms/core/ContentReference"
],
function (dojo, dependency, UriParser, ContentReference) {
return dojo.declare("epi.cms.conversion.ContentLightUriConverter", null, {
// summary:
// Converts data between light weight content types containing an URI as a specifyer and other types.
//
// tags:
// public
_pageDataStore: null,
constructor: function(params) {
// summary:
// The constructor function
//
// params: Object
// The parameters that define the object.
// tags:
// public
dojo.mixin(this, params);
this._pageDataStore = this._pageDataStore || dependency.resolve("epi.storeregistry").get("epi.cms.contentdata");
},
registerConverter: function(/*Object*/registry) {
// summary:
// Registers the type conversions that this class supports to the given registry.
//
// registry: Object
// The converter registry to add mappings to.
registry.registerConverter("epi.cms.page.lighturi", "link", this);
registry.registerConverter("epi.cms.page.lighturi", "epi.cms.pagereference", this);
registry.registerConverter("epi.cms.block.lighturi", "epi.cms.blockreference", this);
registry.registerConverter("epi.cms.folder.lighturi", "epi.cms.folderreference", this);
registry.registerConverter("epi.cms.content.lighturi", "epi.cms.contentreference", this);
registry.registerConverter("epi.cms.content.lighturi", "epi.cms.content.light", this);
registry.registerConverter("epi.cms.page.lighturi", "epi.cms.content.light", this);
registry.registerConverter("epi.cms.block.lighturi", "epi.cms.content.light", this);
registry.registerConverter("epi.cms.folder.lighturi", "epi.cms.content.light", this);
},
convert: function(/*String*/sourceDataType, /*String*/targetDataType, /*Object*/data) {
// summary:
// Converts data between two types.
//
// sourceDataType: String
// The source data type.
//
// targetDataType: String
// The target data type.
//
// converter: Object
// The class that performs the actual conversion.
// tags:
// public
if (!data) {
return null;
} else if (targetDataType === "epi.cms.content.lighturi") {
return data;
}
var uri = new UriParser(data.uri);
var link = uri.getId();
var versionAgnosticId = new ContentReference(link).createVersionUnspecificReference().toString();
if (targetDataType === "epi.cms.content.light"
|| targetDataType === "epi.cms.page.light"
|| targetDataType === "epi.cms.block.light"
|| targetDataType === "epi.cms.folder.light") {
return { contentLink: versionAgnosticId, name: data.name };
} else if (targetDataType === "epi.cms.contentreference"
|| targetDataType === "epi.cms.pagereference"
|| targetDataType === "epi.cms.blockreference"
|| targetDataType === "epi.cms.folderreference") {
return versionAgnosticId;
}
if (targetDataType === "link") {
//should only be available for pages right now but allow anything for future types as files etc.
return dojo.when(this._pageDataStore.get(versionAgnosticId), function(page) {
return { url: page.properties.pageLinkURL, text: page.name };
});
}
}
});
});
Last updated: Mar 21, 2013