diff --git a/platform/representation/bundle.json b/platform/representation/bundle.json
new file mode 100644
index 0000000000..00a4e5ee41
--- /dev/null
+++ b/platform/representation/bundle.json
@@ -0,0 +1,33 @@
+{
+ "extensions": {
+ "directives": [
+ {
+ "key": "mctInclude",
+ "implementation": "MCTInclude.js",
+ "depends": [ "templates[]" ]
+ },
+ {
+ "key": "mctRepresentation",
+ "implementation": "MCTRepresentation.js",
+ "depends": [ "representations[]", "views[]", "gestures[]", "$q", "$log" ]
+ }
+ ],
+ "gestures": [
+ {
+ "key": "drag",
+ "implementation": "gestures/DragGesture.js",
+ "depends": [ "$log" ]
+ },
+ {
+ "key": "drop",
+ "implementation": "gestures/DropGesture.js",
+ "depends": [ "$q" ]
+ },
+ {
+ "key": "menu",
+ "implementation": "gestures/ContextMenuGesture.js",
+ "depends": [ "$compile", "$document", "$window", "$rootScope" ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/platform/representation/src/MCTInclude.js b/platform/representation/src/MCTInclude.js
new file mode 100644
index 0000000000..aa9e36da5f
--- /dev/null
+++ b/platform/representation/src/MCTInclude.js
@@ -0,0 +1,43 @@
+/*global define,Promise*/
+
+/**
+ * Module defining MCTInclude. Created by vwoeltje on 11/7/14.
+ */
+define(
+ [],
+ function () {
+ "use strict";
+
+ /**
+ * Defines the mct-include directive. This acts like the
+ * ng-include directive, except it accepts a symbolic
+ * key which can be exposed by bundles.
+ * @constructor
+ */
+ function MCTInclude(templates) {
+ var templateMap = {};
+
+ templates.forEach(function (template) {
+ var path = [
+ template.bundle.path,
+ template.bundle.resources,
+ template.templateUrl
+ ].join("/");
+ templateMap[template.key] = path;
+ });
+
+ function controller($scope) {
+ $scope.inclusion = templateMap[$scope.key];
+ }
+
+ return {
+ restrict: "E",
+ controller: controller,
+ template: '',
+ scope: { key: "=", ngModel: "=", parameters: "=" }
+ };
+ }
+
+ return MCTInclude;
+ }
+);
\ No newline at end of file
diff --git a/platform/representation/src/MCTRepresentation.js b/platform/representation/src/MCTRepresentation.js
new file mode 100644
index 0000000000..1ce2f61970
--- /dev/null
+++ b/platform/representation/src/MCTRepresentation.js
@@ -0,0 +1,122 @@
+/*global define,Promise*/
+
+/**
+ * Module defining MCTRepresentation. Created by vwoeltje on 11/7/14.
+ */
+define(
+ [],
+ function () {
+ "use strict";
+
+ /**
+ *
+ * @constructor
+ */
+ function MCTRepresentation(representations, views, gestures, $q, $log) {
+ var pathMap = {},
+ representationMap = {},
+ gestureMap = {};
+
+ // Assemble all representations and views
+ // The distinction between views and representations is
+ // not important her (view is-a representation)
+ representations.concat(views).forEach(function (representation) {
+ var path = [
+ representation.bundle.path,
+ representation.bundle.resources,
+ representation.templateUrl
+ ].join("/");
+
+ // Consider allowing multiple templates with the same key
+ pathMap[representation.key] = path;
+ representationMap[representation.key] = representation;
+ });
+
+ // Assemble all gestures into a map, similarly
+ gestures.forEach(function (gesture) {
+ gestureMap[gesture.key] = gesture;
+ });
+
+ function findRepresentation(key, domainObject) {
+ return representationMap[key];
+ }
+
+ function createGestures(element, domainObject, gestureKeys) {
+ return gestureKeys.map(function (key) {
+ return gestureMap[key];
+ }).filter(function (Gesture) {
+ return Gesture !== undefined && (Gesture.appliesTo ?
+ Gesture.appliesTo(domainObject) :
+ true);
+ }).map(function (Gesture) {
+ return new Gesture(element, domainObject);
+ });
+ }
+
+ function releaseGestures(gestures) {
+ gestures.forEach(function (gesture) {
+ if (gesture && gesture.destroy) {
+ gesture.destroy();
+ }
+ });
+ }
+
+ function link($scope, element) {
+ var linkedGestures = [];
+
+ function refresh() {
+ var representation = representationMap[$scope.key],
+ domainObject = $scope.domainObject,
+ uses = ((representation || {}).uses || []),
+ gestureKeys = ((representation || {}).gestures || []);
+
+ $scope.representation = {};
+ $scope.inclusion = pathMap[$scope.key];
+
+ // Any existing gestures are no longer valid; release them.
+ releaseGestures(linkedGestures);
+
+ if (!representation && $scope.key) {
+ $log.warn("No representation found for " + $scope.key);
+ }
+ if (domainObject) {
+ $scope.model = domainObject.getModel();
+
+ uses.forEach(function (used) {
+ $log.debug([
+ "Requesting capability ",
+ used,
+ " for representation ",
+ $scope.key
+ ].join(""));
+ $q.when(
+ domainObject.useCapability(used)
+ ).then(function (c) {
+ $scope[used] = c;
+ });
+ });
+
+ linkedGestures = createGestures(
+ element,
+ domainObject,
+ gestureKeys
+ );
+ }
+ }
+
+ $scope.$watch("key", refresh);
+ $scope.$watch("domainObject", refresh);
+ $scope.$watch("domainObject.getModel().modified", refresh);
+ }
+
+ return {
+ restrict: "E",
+ link: link,
+ template: '',
+ scope: { key: "=", domainObject: "=mctObject", parameters: "=" }
+ };
+ }
+
+ return MCTRepresentation;
+ }
+);
\ No newline at end of file
diff --git a/platform/representation/src/gestures/ContextMenuGesture.js b/platform/representation/src/gestures/ContextMenuGesture.js
new file mode 100644
index 0000000000..2a553cca53
--- /dev/null
+++ b/platform/representation/src/gestures/ContextMenuGesture.js
@@ -0,0 +1,86 @@
+/*global define,Promise*/
+
+/**
+ * Module defining ContextMenuGesture. Created by vwoeltje on 11/17/14.
+ */
+define(
+ [],
+ function () {
+ "use strict";
+
+ var MENU_TEMPLATE = "" +
+ "";
+
+ /**
+ * Add listeners to a view such that it launches a context menu for the
+ * object it contains.
+ *
+ * @constructor
+ */
+ function ContextMenuGesture($compile, $document, $window, $rootScope, element, domainObject) {
+ function showMenu(event) {
+ var winDim = [$window.innerWidth, $window.innerHeight],
+ eventCoors = [event.pageX, event.pageY],
+ menuDim = [170, 200],
+ body = $document.find('body'),
+ scope = $rootScope.$new(),
+ goLeft = eventCoors[0] + menuDim[0] > winDim[0],
+ goUp = eventCoors[1] + menuDim[1] > winDim[1],
+ menu;
+
+ // Remove the context menu
+ function dismiss() {
+ menu.remove();
+ body.off("click", dismiss);
+ ContextMenuGesture.dismissExistingMenu = undefined;
+ }
+
+ // Dismiss any menu which was already showing
+ if (ContextMenuGesture.dismissExistingMenu) {
+ ContextMenuGesture.dismissExistingMenu();
+ }
+
+ // ...and record the presence of this menu.
+ ContextMenuGesture.dismissExistingMenu = dismiss;
+
+ // Set up the scope, including menu positioning
+ scope.domainObject = domainObject;
+ scope.menuStyle = {};
+ scope.menuStyle[goLeft ? "right" : "left"] =
+ eventCoors[0] + 'px';
+ scope.menuStyle[goUp ? "bottom" : "top"] =
+ eventCoors[1] + 'px';
+ scope.menuClass = { "go-left": goLeft, "go-up": goUp, "context-menu-holder": true };
+
+ // Create the context menu
+ menu = $compile(MENU_TEMPLATE)(scope);
+
+ // Add the menu to the body
+ body.append(menu);
+
+ // Dismiss the menu when body is clicked elsewhere
+ body.on('click', dismiss);
+
+ // Don't launch browser's context menu
+ event.preventDefault();
+ }
+
+ // When context menu event occurs, show object actions instead
+ element.on('contextmenu', showMenu);
+
+ return {
+ destroy: function () {
+ if (ContextMenuGesture.dismissExistingMenu) {
+ ContextMenuGesture.dismissExistingMenu();
+ }
+ element.off('contextmenu', showMenu);
+ }
+ };
+ }
+
+ return ContextMenuGesture;
+ }
+);
\ No newline at end of file
diff --git a/platform/representation/src/gestures/DragGesture.js b/platform/representation/src/gestures/DragGesture.js
new file mode 100644
index 0000000000..9832010cc9
--- /dev/null
+++ b/platform/representation/src/gestures/DragGesture.js
@@ -0,0 +1,61 @@
+/*global define,Promise*/
+
+/**
+ * Module defining DragGesture. Created by vwoeltje on 11/17/14.
+ */
+define(
+ ['./GestureConstants'],
+ function (GestureConstants) {
+ "use strict";
+
+ /**
+ *
+ * @constructor
+ */
+
+ function DragGesture($log, element, domainObject) {
+ function startDrag(e) {
+ var event = (e || {}).originalEvent || e;
+
+ $log.debug("Initiating drag");
+
+ try {
+ event.dataTransfer.effectAllowed = 'move';
+ event.dataTransfer.setData(
+ 'text/plain',
+ JSON.stringify({
+ id: domainObject.getId(),
+ model: domainObject.getModel()
+ })
+ );
+ event.dataTransfer.setData(
+ GestureConstants.MCT_DRAG_TYPE,
+ domainObject.getId()
+ );
+
+ } catch (err) {
+ $log.warn([
+ "Could not initiate drag due to ",
+ err.message
+ ].join(""));
+ }
+
+ }
+
+ $log.debug("Attaching drag gesture");
+ element.attr('draggable', 'true');
+ element.on('dragstart', startDrag);
+
+ return {
+ destroy: function () {
+ // Detach listener
+ element.attr('draggable', false);
+ element.off('dragstart', startDrag);
+ }
+ };
+ }
+
+
+ return DragGesture;
+ }
+);
\ No newline at end of file
diff --git a/platform/representation/src/gestures/DropGesture.js b/platform/representation/src/gestures/DropGesture.js
new file mode 100644
index 0000000000..0a25f49068
--- /dev/null
+++ b/platform/representation/src/gestures/DropGesture.js
@@ -0,0 +1,74 @@
+/*global define,Promise*/
+
+/**
+ * Module defining DropGesture. Created by vwoeltje on 11/17/14.
+ */
+define(
+ ['./GestureConstants'],
+ function (GestureConstants) {
+ "use strict";
+
+ /**
+ *
+ * @constructor
+ */
+
+ function DropGesture($q, element, domainObject) {
+
+ function doPersist() {
+ var persistence = domainObject.getCapability("persistence");
+ return $q.when(persistence && peristence.persist());
+ }
+
+ function dragOver(e) {
+ var event = (e || {}).originalEvent || e;
+ //event.stopPropagation();
+
+ // TODO: Vary this based on modifier keys
+ event.dataTransfer.dropEffect = 'move';
+ event.preventDefault(); // Required in Chrome?
+ return false;
+ }
+
+ function drop(e) {
+ var event = (e || {}).originalEvent || e,
+ id = event.dataTransfer.getData(GestureConstants.MCT_DRAG_TYPE);
+
+ if (id) {
+ $q.when(domainObject.useCapability(
+ 'mutation',
+ function (model) {
+ var composition = model.composition;
+ if (composition && // not-contains
+ !(composition.map(function (i) {
+ return i === id;
+ }).reduce(function (a, b) {
+ return a || b;
+ }, false))) {
+ model.composition.push(id);
+ }
+ }
+ )).then(function (result) {
+ return result && doPersist();
+ });
+ }
+
+ }
+
+
+ element.on('dragover', dragOver);
+ element.on('drop', drop);
+
+ return {
+ destroy: function () {
+ element.off('dragover', dragOver);
+ element.off('drop', drop);
+ }
+ };
+
+ }
+
+
+ return DropGesture;
+ }
+);
\ No newline at end of file
diff --git a/platform/representation/src/gestures/GestureConstants.js b/platform/representation/src/gestures/GestureConstants.js
new file mode 100644
index 0000000000..addbf50ae0
--- /dev/null
+++ b/platform/representation/src/gestures/GestureConstants.js
@@ -0,0 +1,8 @@
+/*global define,Promise*/
+
+/**
+ * Module defining GestureConstants. Created by vwoeltje on 11/17/14.
+ */
+define({
+ MCT_DRAG_TYPE: 'mct-domain-object-id'
+});
\ No newline at end of file