diff --git a/core/misc/create/create-editonly.js b/core/misc/create/create-editonly.js
deleted file mode 100644
index aed84a4ffcddc04e78f9e37b68b7e278793efe87..0000000000000000000000000000000000000000
--- a/core/misc/create/create-editonly.js
+++ /dev/null
@@ -1,1651 +0,0 @@
-//     Create.js - On-site web editing interface
-//     (c) 2011-2012 Henri Bergius, IKS Consortium
-//     Create may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://createjs.org/
-(function (jQuery, undefined) {
-  // Run JavaScript in strict mode
-  /*global jQuery:false _:false window:false console:false */
-  'use strict';
-
-  // # Widget for adding items to a collection
-  jQuery.widget('Midgard.midgardCollectionAdd', {
-    options: {
-      editingWidgets: null,
-      collection: null,
-      model: null,
-      definition: null,
-      view: null,
-      disabled: false,
-      vie: null,
-      editableOptions: null,
-      templates: {
-        button: '<button class="btn"><i class="icon-<%= icon %>"></i> <%= label %></button>'
-      }
-    },
-
-    _create: function () {
-      this.addButtons = [];
-      var widget = this;
-      if (!widget.options.collection.localStorage) {
-        try {
-          widget.options.collection.url = widget.options.model.url();
-        } catch (e) {
-          if (window.console) {
-            console.log(e);
-          }
-        }
-      }
-
-      widget.options.collection.on('add', function (model) {
-        model.primaryCollection = widget.options.collection;
-        widget.options.vie.entities.add(model);
-        model.collection = widget.options.collection;
-      });
-
-      // Re-check collection constraints
-      widget.options.collection.on('add remove reset', widget.checkCollectionConstraints, widget);
-
-      widget._bindCollectionView(widget.options.view);
-    },
-
-    _bindCollectionView: function (view) {
-      var widget = this;
-      view.on('add', function (itemView) {
-        itemView.$el.effect('slide', function () {
-          widget._makeEditable(itemView);
-        });
-      });
-    },
-
-    _makeEditable: function (itemView) {
-      this.options.editableOptions.disabled = this.options.disabled;
-      this.options.editableOptions.model = itemView.model;
-      itemView.$el.midgardEditable(this.options.editableOptions);
-    },
-
-    _init: function () {
-      if (this.options.disabled) {
-        this.disable();
-        return;
-      }
-      this.enable();
-    },
-
-    hideButtons: function () {
-      _.each(this.addButtons, function (button) {
-        button.hide();
-      });
-    },
-
-    showButtons: function () {
-      _.each(this.addButtons, function (button) {
-        button.show();
-      });
-    },
-
-    checkCollectionConstraints: function () {
-      if (this.options.disabled) {
-        return;
-      }
-
-      if (!this.options.view.canAdd()) {
-        this.hideButtons();
-        return;
-      }
-
-      if (!this.options.definition) {
-        // We have now information on the constraints applying to this collection
-        this.showButtons();
-        return;
-      }
-
-      if (!this.options.definition.max || this.options.definition.max === -1) {
-        // No maximum constraint
-        this.showButtons();
-        return;
-      }
-
-      if (this.options.collection.length < this.options.definition.max) {
-        this.showButtons();
-        return;
-      }
-      // Collection is already full by its definition
-      this.hideButtons();
-    },
-
-    enable: function () {
-      var widget = this;
-
-      var addButton = jQuery(_.template(this.options.templates.button, {
-        icon: 'plus',
-        label: this.options.editableOptions.localize('Add', this.options.editableOptions.language)
-      })).button();
-      addButton.addClass('midgard-create-add');
-      addButton.click(function () {
-        widget.addItem(addButton);
-      });
-      jQuery(widget.options.view.el).after(addButton);
-
-      widget.addButtons.push(addButton);
-      widget.checkCollectionConstraints();
-    },
-
-    disable: function () {
-      _.each(this.addButtons, function (button) {
-        button.remove();
-      });
-      this.addButtons = [];
-    },
-
-    _getTypeActions: function (options) {
-      var widget = this;
-      var actions = [];
-      _.each(this.options.definition.range, function (type) {
-        var nsType = widget.options.collection.vie.namespaces.uri(type);
-        if (!widget.options.view.canAdd(nsType)) {
-          return;
-        }
-        actions.push({
-          name: type,
-          label: type,
-          cb: function () {
-            widget.options.collection.add({
-              '@type': type
-            }, options);
-          },
-          className: 'create-ui-btn'
-        });
-      });
-      return actions;
-    },
-
-    addItem: function (button, options) {
-      if (options === undefined) {
-          options = {};
-      }
-      var addOptions = _.extend({}, options, { validate: false });
-
-      var itemData = {};
-      if (this.options.definition && this.options.definition.range) {
-        if (this.options.definition.range.length === 1) {
-          // Items can be of single type, add that
-          itemData['@type'] = this.options.definition.range[0];
-        } else {
-          // Ask user which type to add
-          jQuery('body').midgardNotifications('create', {
-            bindTo: button,
-            gravity: 'L',
-            body: this.options.editableOptions.localize('Choose type to add', this.options.editableOptions.language),
-            timeout: 0,
-            actions: this._getTypeActions(addOptions)
-          });
-          return;
-        }
-      } else {
-        // Check the view templates for possible non-Thing type to use
-        var keys = _.keys(this.options.view.templates);
-        if (keys.length == 2) {
-          itemData['@type'] = keys[0];
-        }
-      }
-      this.options.collection.add(itemData, addOptions);
-    }
-  });
-})(jQuery);
-//     Create.js - On-site web editing interface
-//     (c) 2011-2012 Henri Bergius, IKS Consortium
-//     Create may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://createjs.org/
-(function (jQuery, undefined) {
-  // Run JavaScript in strict mode
-  /*global jQuery:false _:false window:false console:false */
-  'use strict';
-
-  // # Widget for adding items anywhere inside a collection
-  jQuery.widget('Midgard.midgardCollectionAddBetween', jQuery.Midgard.midgardCollectionAdd, {
-    _bindCollectionView: function (view) {
-      var widget = this;
-      view.on('add', function (itemView) {
-        //itemView.el.effect('slide');
-        widget._makeEditable(itemView);
-        widget._refreshButtons();
-      });
-      view.on('remove', function () {
-        widget._refreshButtons();
-      });
-    },
-
-    _refreshButtons: function () {
-      var widget = this;
-      window.setTimeout(function () {
-        widget.disable();
-        widget.enable();
-      }, 1);
-    },
-
-    prepareButton: function (index) {
-      var widget = this;
-      var addButton = jQuery(_.template(this.options.templates.button, {
-        icon: 'plus',
-        label: ''
-      })).button();
-      addButton.addClass('midgard-create-add');
-      addButton.click(function () {
-        widget.addItem(addButton, {
-          at: index
-        });
-      });
-      return addButton;
-    },
-
-    enable: function () {
-      var widget = this;
-
-      var firstAddButton = widget.prepareButton(0);
-      jQuery(widget.options.view.el).prepend(firstAddButton);
-      widget.addButtons.push(firstAddButton);
-      jQuery.each(widget.options.view.entityViews, function (cid, view) {
-        var index = widget.options.collection.indexOf(view.model);
-        var addButton = widget.prepareButton(index + 1);
-        jQuery(view.el).append(addButton);
-        widget.addButtons.push(addButton);
-      });
-
-      this.checkCollectionConstraints();
-    },
-
-    disable: function () {
-      var widget = this;
-      jQuery.each(widget.addButtons, function (idx, button) {
-        button.remove();
-      });
-      widget.addButtons = [];
-    }
-  });
-})(jQuery);
-//     Create.js - On-site web editing interface
-//     (c) 2011-2012 Henri Bergius, IKS Consortium
-//     Create may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://createjs.org/
-(function (jQuery, undefined) {
-  // Run JavaScript in strict mode
-  /*global jQuery:false _:false window:false VIE:false */
-  'use strict';
-
-  // Define Create's EditableEntity widget.
-  jQuery.widget('Midgard.midgardEditable', {
-    options: {
-      propertyEditors: {},
-      collections: [],
-      model: null,
-      // the configuration (mapping and options) of property editor widgets
-      propertyEditorWidgetsConfiguration: {
-        hallo: {
-          widget: 'halloWidget',
-          options: {}
-        }
-      },
-      // the available property editor widgets by data type
-      propertyEditorWidgets: {
-        'default': 'hallo'
-      },
-      collectionWidgets: {
-        'default': 'midgardCollectionAdd'
-      },
-      toolbarState: 'full',
-      vie: null,
-      domService: 'rdfa',
-      predicateSelector: '[property]',
-      disabled: false,
-      localize: function (id, language) {
-        return window.midgardCreate.localize(id, language);
-      },
-      language: null,
-      // Current state of the Editable
-      state: null,
-      // Callback function for validating changes between states. Receives the previous state, new state, possibly property, and a callback
-      acceptStateChange: true,
-      // Callback function for listening (and reacting) to state changes.
-      stateChange: null,
-      // Callback function for decorating the full editable. Will be called on instantiation
-      decorateEditableEntity: null,
-      // Callback function for decorating a single property editor widget. Will
-      // be called on editing widget instantiation.
-      decoratePropertyEditor: null,
-
-      // Deprecated.
-      editables: [], // Now `propertyEditors`.
-      editors: {}, // Now `propertyEditorWidgetsConfiguration`.
-      widgets: {} // Now `propertyEditorW
-    },
-
-    // Aids in consistently passing parameters to events and callbacks.
-    _params: function(predicate, extended) {
-      var entityParams = {
-        entity: this.options.model,
-        editableEntity: this,
-        entityElement: this.element,
-
-        // Deprecated.
-        editable: this,
-        element: this.element,
-        instance: this.options.model
-      };
-      var propertyParams = (predicate) ? {
-        predicate: predicate,
-        propertyEditor: this.options.propertyEditors[predicate],
-        propertyElement: this.options.propertyEditors[predicate].element,
-
-        // Deprecated.
-        property: predicate,
-        element: this.options.propertyEditors[predicate].element
-      } : {};
-
-      return _.extend(entityParams, propertyParams, extended);
-    },
-
-    _create: function () {
-      // Backwards compatibility:
-      // - this.options.propertyEditorWidgets used to be this.options.widgets
-      // - this.options.propertyEditorWidgetsConfiguration used to be
-      //   this.options.editors
-      if (this.options.widgets) {
-        this.options.propertyEditorWidgets = _.extend(this.options.propertyEditorWidgets, this.options.widgets);
-      }
-      if (this.options.editors) {
-        this.options.propertyEditorWidgetsConfiguration = _.extend(this.options.propertyEditorWidgetsConfiguration, this.options.editors);
-      }
-
-      this.vie = this.options.vie;
-      this.domService = this.vie.service(this.options.domService);
-      if (!this.options.model) {
-        var widget = this;
-        this.vie.load({
-          element: this.element
-        }).from(this.options.domService).execute().done(function (entities) {
-          widget.options.model = entities[0];
-        });
-      }
-      if (_.isFunction(this.options.decorateEditableEntity)) {
-        this.options.decorateEditableEntity(this._params());
-      }
-    },
-
-    _init: function () {
-      // Backwards compatibility:
-      // - this.options.propertyEditorWidgets used to be this.options.widgets
-      // - this.options.propertyEditorWidgetsConfiguration used to be
-      //   this.options.editors
-      if (this.options.widgets) {
-        this.options.propertyEditorWidgets = _.extend(this.options.propertyEditorWidgets, this.options.widgets);
-      }
-      if (this.options.editors) {
-        this.options.propertyEditorWidgetsConfiguration = _.extend(this.options.propertyEditorWidgetsConfiguration, this.options.editors);
-      }
-
-      // Old way of setting the widget inactive
-      if (this.options.disabled === true) {
-        this.setState('inactive');
-        return;
-      }
-
-      if (this.options.disabled === false && this.options.state === 'inactive') {
-        this.setState('candidate');
-        return;
-      }
-      this.options.disabled = false;
-
-      if (this.options.state) {
-        this.setState(this.options.state);
-        return;
-      }
-      this.setState('candidate');
-    },
-
-    // Method used for cycling between the different states of the Editable widget:
-    //
-    // * Inactive: editable is loaded but disabled
-    // * Candidate: editable is enabled but not activated
-    // * Highlight: user is hovering over the editable (not set by Editable widget directly)
-    // * Activating: an editor widget is being activated for user to edit with it (skipped for editors that activate instantly)
-    // * Active: user is actually editing something inside the editable
-    // * Changed: user has made changes to the editable
-    // * Invalid: the contents of the editable have validation errors
-    //
-    // In situations where state changes are triggered for a particular property editor, the `predicate`
-    // argument will provide the name of that property.
-    //
-    // State changes may carry optional context information in a JavaScript object. The payload of these context objects is not
-    // standardized, and is meant to be set and used by the application controller
-    //
-    // The callback parameter is optional and will be invoked after a state change has been accepted (after the 'statechange'
-    // event) or rejected.
-    setState: function (state, predicate, context, callback) {
-      var previous = this.options.state;
-      var current = state;
-      if (current === previous) {
-        return;
-      }
-
-      if (this.options.acceptStateChange === undefined || !_.isFunction(this.options.acceptStateChange)) {
-        // Skip state transition validation
-        this._doSetState(previous, current, predicate, context);
-        if (_.isFunction(callback)) {
-          callback(true);
-        }
-        return;
-      }
-
-      var widget = this;
-      this.options.acceptStateChange(previous, current, predicate, context, function (accepted) {
-        if (accepted) {
-          widget._doSetState(previous, current, predicate, context);
-        }
-        if (_.isFunction(callback)) {
-          callback(accepted);
-        }
-        return;
-      });
-    },
-
-    getState: function () {
-      return this.options.state;
-    },
-
-    _doSetState: function (previous, current, predicate, context) {
-      this.options.state = current;
-      if (current === 'inactive') {
-        this.disable();
-      } else if ((previous === null || previous === 'inactive') && current !== 'inactive') {
-        this.enable();
-      }
-
-      this._trigger('statechange', null, this._params(predicate, {
-        previous: previous,
-        current: current,
-        context: context
-      }));
-    },
-
-    findEditablePredicateElements: function (callback) {
-      this.domService.findPredicateElements(this.options.model.id, jQuery(this.options.predicateSelector, this.element), false).each(callback);
-    },
-
-    getElementPredicate: function (element) {
-      return this.domService.getElementPredicate(element);
-    },
-
-    enable: function () {
-      var editableEntity = this;
-      if (!this.options.model) {
-        return;
-      }
-
-      this.findEditablePredicateElements(function () {
-        editableEntity._enablePropertyEditor(jQuery(this));
-      });
-
-      this._trigger('enable', null, this._params());
-
-      if (!this.vie.view || !this.vie.view.Collection) {
-        return;
-      }
-
-      _.each(this.domService.views, function (view) {
-        if (view instanceof this.vie.view.Collection && this.options.model === view.owner) {
-          var predicate = view.collection.predicate;
-          var editableOptions = _.clone(this.options);
-          editableOptions.state = null;
-          var collection = this.enableCollection({
-            model: this.options.model,
-            collection: view.collection,
-            property: predicate,
-            definition: this.getAttributeDefinition(predicate),
-            view: view,
-            element: view.el,
-            vie: editableEntity.vie,
-            editableOptions: editableOptions
-          });
-          editableEntity.options.collections.push(collection);
-        }
-      }, this);
-    },
-
-    disable: function () {
-      _.each(this.options.propertyEditors, function (editable) {
-        this.disablePropertyEditor({
-          widget: this,
-          editable: editable,
-          entity: this.options.model,
-          element: editable.element
-        });
-      }, this);
-      this.options.propertyEditors = {};
-
-      // Deprecated.
-      this.options.editables = [];
-
-      _.each(this.options.collections, function (collectionWidget) {
-        var editableOptions = _.clone(this.options);
-        editableOptions.state = 'inactive';
-        this.disableCollection({
-          widget: this,
-          model: this.options.model,
-          element: collectionWidget,
-          vie: this.vie,
-          editableOptions: editableOptions
-        });
-      }, this);
-      this.options.collections = [];
-
-      this._trigger('disable', null, this._params());
-    },
-
-    _enablePropertyEditor: function (element) {
-      var widget = this;
-      var predicate = this.getElementPredicate(element);
-      if (!predicate) {
-        return true;
-      }
-      if (this.options.model.get(predicate) instanceof Array) {
-        // For now we don't deal with multivalued properties in the editable
-        return true;
-      }
-
-      var propertyElement = this.enablePropertyEditor({
-        widget: this,
-        element: element,
-        entity: this.options.model,
-        property: predicate,
-        vie: this.vie,
-        decorate: this.options.decoratePropertyEditor,
-        decorateParams: _.bind(this._params, this),
-        changed: function (content) {
-          widget.setState('changed', predicate);
-
-          var changedProperties = {};
-          changedProperties[predicate] = content;
-          widget.options.model.set(changedProperties, {
-            silent: true
-          });
-
-          widget._trigger('changed', null, widget._params(predicate));
-        },
-        activating: function () {
-          widget.setState('activating', predicate);
-        },
-        activated: function () {
-          widget.setState('active', predicate);
-          widget._trigger('activated', null, widget._params(predicate));
-        },
-        deactivated: function () {
-          widget.setState('candidate', predicate);
-          widget._trigger('deactivated', null, widget._params(predicate));
-        }
-      });
-
-      if (!propertyElement) {
-        return;
-      }
-      var widgetType = propertyElement.data('createWidgetName');
-      this.options.propertyEditors[predicate] = propertyElement.data('Midgard-' + widgetType);
-
-      // Deprecated.
-      this.options.editables.push(propertyElement);
-
-      this._trigger('enableproperty', null, this._params(predicate));
-    },
-
-    // returns the name of the property editor widget to use for the given property
-    _propertyEditorName: function (data) {
-      if (this.options.propertyEditorWidgets[data.property] !== undefined) {
-        // Property editor widget configuration set for specific RDF predicate
-        return this.options.propertyEditorWidgets[data.property];
-      }
-
-      // Load the property editor widget configuration for the data type
-      var propertyType = 'default';
-      var attributeDefinition = this.getAttributeDefinition(data.property);
-      if (attributeDefinition) {
-        propertyType = attributeDefinition.range[0];
-      }
-      if (this.options.propertyEditorWidgets[propertyType] !== undefined) {
-        return this.options.propertyEditorWidgets[propertyType];
-      }
-      return this.options.propertyEditorWidgets['default'];
-    },
-
-    _propertyEditorWidget: function (editor) {
-      return this.options.propertyEditorWidgetsConfiguration[editor].widget;
-    },
-
-    _propertyEditorOptions: function (editor) {
-      return this.options.propertyEditorWidgetsConfiguration[editor].options;
-    },
-
-    getAttributeDefinition: function (property) {
-      var type = this.options.model.get('@type');
-      if (!type) {
-        return;
-      }
-      if (!type.attributes) {
-        return;
-      }
-      return type.attributes.get(property);
-    },
-
-    // Deprecated.
-    enableEditor: function (data) {
-      return this.enablePropertyEditor(data);
-    },
-
-    enablePropertyEditor: function (data) {
-      var editorName = this._propertyEditorName(data);
-      if (editorName === null) {
-        return;
-      }
-
-      var editorWidget = this._propertyEditorWidget(editorName);
-
-      data.editorOptions = this._propertyEditorOptions(editorName);
-      data.toolbarState = this.options.toolbarState;
-      data.disabled = false;
-      // Pass metadata that could be useful for some implementations.
-      data.editorName = editorName;
-      data.editorWidget = editorWidget;
-
-      if (typeof jQuery(data.element)[editorWidget] !== 'function') {
-        throw new Error(editorWidget + ' widget is not available');
-      }
-
-      jQuery(data.element)[editorWidget](data);
-      jQuery(data.element).data('createWidgetName', editorWidget);
-      return jQuery(data.element);
-    },
-
-    // Deprecated.
-    disableEditor: function (data) {
-      return this.disablePropertyEditor(data);
-    },
-
-    disablePropertyEditor: function (data) {
-      data.element[data.editable.widgetName]({
-        disabled: true
-      });
-      jQuery(data.element).removeClass('ui-state-disabled');
-
-      if (data.element.is(':focus')) {
-        data.element.blur();
-      }
-    },
-
-    collectionWidgetName: function (data) {
-      if (this.options.collectionWidgets[data.property] !== undefined) {
-        // Widget configuration set for specific RDF predicate
-        return this.options.collectionWidgets[data.property];
-      }
-
-      var propertyType = 'default';
-      var attributeDefinition = this.getAttributeDefinition(data.property);
-      if (attributeDefinition) {
-        propertyType = attributeDefinition.range[0];
-      }
-      if (this.options.collectionWidgets[propertyType] !== undefined) {
-        return this.options.collectionWidgets[propertyType];
-      }
-      return this.options.collectionWidgets['default'];
-    },
-
-    enableCollection: function (data) {
-      var widgetName = this.collectionWidgetName(data);
-      if (widgetName === null) {
-        return;
-      }
-      data.disabled = false;
-      if (typeof jQuery(data.element)[widgetName] !== 'function') {
-        throw new Error(widgetName + ' widget is not available');
-      }
-      jQuery(data.element)[widgetName](data);
-      jQuery(data.element).data('createCollectionWidgetName', widgetName);
-      return jQuery(data.element);
-    },
-
-    disableCollection: function (data) {
-      var widgetName = jQuery(data.element).data('createCollectionWidgetName');
-      if (widgetName === null) {
-        return;
-      }
-      data.disabled = true;
-      if (widgetName) {
-        // only if there has been an editing widget registered
-        jQuery(data.element)[widgetName](data);
-        jQuery(data.element).removeClass('ui-state-disabled');
-      }
-    }
-  });
-})(jQuery);
-//     Create.js - On-site web editing interface
-//     (c) 2012 Tobias Herrmann, IKS Consortium
-//     Create may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://createjs.org/
-(function (jQuery, undefined) {
-  // Run JavaScript in strict mode
-  /*global jQuery:false _:false document:false */
-  'use strict';
-
-  // # Base property editor widget
-  //
-  // This property editor widget provides a very simplistic `contentEditable`
-  // property editor that can be used as standalone, but should more usually be
-  // used as the base class for other property editor widgets.
-  // This property editor widget is only useful for textual properties!
-  //
-  // Subclassing this base property editor widget is easy:
-  //
-  //     jQuery.widget('Namespace.MyWidget', jQuery.Create.editWidget, {
-  //       // override any properties
-  //     });
-  jQuery.widget('Midgard.editWidget', {
-    options: {
-      disabled: false,
-      vie: null
-    },
-    // override to enable the widget
-    enable: function () {
-      this.element.attr('contenteditable', 'true');
-    },
-    // override to disable the widget
-    disable: function (disable) {
-      this.element.attr('contenteditable', 'false');
-    },
-    // called by the jQuery UI plugin factory when creating the property editor
-    // widget instance
-    _create: function () {
-      this._registerWidget();
-      this._initialize();
-
-      if (_.isFunction(this.options.decorate) && _.isFunction(this.options.decorateParams)) {
-        // TRICKY: we can't use this.options.decorateParams()'s 'propertyName'
-        // parameter just yet, because it will only be available after this
-        // object has been created, but we're currently in the constructor!
-        // Hence we have to duplicate part of its logic here.
-        this.options.decorate(this.options.decorateParams(null, {
-          propertyName: this.options.property,
-          propertyEditor: this,
-          propertyElement: this.element,
-          // Deprecated.
-          editor: this,
-          predicate: this.options.property,
-          element: this.element
-        }));
-      }
-    },
-    // called every time the property editor widget is called
-    _init: function () {
-      if (this.options.disabled) {
-        this.disable();
-        return;
-      }
-      this.enable();
-    },
-    // override this function to initialize the property editor widget functions
-    _initialize: function () {
-      var self = this;
-      this.element.on('focus', function () {
-        if (self.options.disabled) {
-          return;
-        }
-        self.options.activated();
-      });
-      this.element.on('blur', function () {
-        if (self.options.disabled) {
-          return;
-        }
-        self.options.deactivated();
-      });
-      var before = this.element.html();
-      this.element.on('keyup paste', function (event) {
-        if (self.options.disabled) {
-          return;
-        }
-        var current = jQuery(this).html();
-        if (before !== current) {
-          before = current;
-          self.options.changed(current);
-        }
-      });
-    },
-    // used to register the property editor widget name with the DOM element
-    _registerWidget: function () {
-      this.element.data("createWidgetName", this.widgetName);
-    }
-  });
-})(jQuery);
-//     Create.js - On-site web editing interface
-//     (c) 2012 Tobias Herrmann, IKS Consortium
-//     (c) 2011 Rene Kapusta, Evo42
-//     Create may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://createjs.org/
-(function (jQuery, undefined) {
-  // Run JavaScript in strict mode
-  /*global jQuery:false _:false document:false Aloha:false */
-  'use strict';
-
-  // # Aloha editing widget
-  //
-  // This widget allows editing textual contents using the
-  // [Aloha](http://aloha-editor.org) rich text editor.
-  //
-  // Due to licensing incompatibilities, Aloha Editor needs to be installed
-  // and configured separately.
-  jQuery.widget('Midgard.alohaWidget', jQuery.Midgard.editWidget, {
-    _initialize: function () {},
-    enable: function () {
-      var options = this.options;
-      var editable;
-      var currentElement = Aloha.jQuery(options.element.get(0)).aloha();
-      _.each(Aloha.editables, function (aloha) {
-        // Find the actual editable instance so we can hook to the events
-        // correctly
-        if (aloha.obj.get(0) === currentElement.get(0)) {
-          editable = aloha;
-        }
-      });
-      if (!editable) {
-        return;
-      }
-      editable.vieEntity = options.entity;
-
-      // Subscribe to activation and deactivation events
-      Aloha.bind('aloha-editable-activated', function (event, data) {
-        if (data.editable !== editable) {
-          return;
-        }
-        options.activated();
-      });
-      Aloha.bind('aloha-editable-deactivated', function (event, data) {
-        if (data.editable !== editable) {
-          return;
-        }
-        options.deactivated();
-      });
-
-      Aloha.bind('aloha-smart-content-changed', function (event, data) {
-        if (data.editable !== editable) {
-          return;
-        }
-        if (!data.editable.isModified()) {
-          return true;
-        }
-        options.changed(data.editable.getContents());
-        data.editable.setUnmodified();
-      });
-      this.options.disabled = false;
-    },
-    disable: function () {
-      Aloha.jQuery(this.options.element.get(0)).mahalo();
-      this.options.disabled = true;
-    }
-  });
-})(jQuery);
-//     Create.js - On-site web editing interface
-//     (c) 2012 Tobias Herrmann, IKS Consortium
-//     Create may be freely distributed under the MIT license.
-//     For all details and documentation:
-(function (jQuery, undefined) {
-  // Run JavaScript in strict mode
-  /*global jQuery:false _:false document:false CKEDITOR:false */
-  'use strict';
-
-  // # CKEditor editing widget
-  //
-  // This widget allows editing textual content areas with the
-  // [CKEditor](http://ckeditor.com/) rich text editor.
-  jQuery.widget('Midgard.ckeditorWidget', jQuery.Midgard.editWidget, {
-    enable: function () {
-      this.element.attr('contentEditable', 'true');
-      this.editor = CKEDITOR.inline(this.element.get(0));
-      this.options.disabled = false;
-
-      var widget = this;
-      this.editor.on('focus', function () {
-        widget.options.activated();
-      });
-      this.editor.on('blur', function () {
-        widget.options.activated();
-        widget.options.changed(widget.editor.getData());
-      });
-      this.editor.on('key', function () {
-        widget.options.changed(widget.editor.getData());
-      });
-      this.editor.on('paste', function () {
-        widget.options.changed(widget.editor.getData());
-      });
-      this.editor.on('afterCommandExec', function () {
-        widget.options.changed(widget.editor.getData());
-      });
-      this.editor.on('configLoaded', function() {
-        jQuery.each(widget.options.editorOptions, function(optionName, option) {
-          widget.editor.config[optionName] = option;
-        });
-      });
-    },
-
-    disable: function () {
-      if (!this.editor) {
-        return;
-      }
-      this.element.attr('contentEditable', 'false');
-      this.editor.destroy();
-      this.editor = null;
-    },
-
-    _initialize: function () {
-      CKEDITOR.disableAutoInline = true;
-    }
-  });
-})(jQuery);
-//     Create.js - On-site web editing interface
-//     (c) 2012 Tobias Herrmann, IKS Consortium
-//     Create may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://createjs.org/
-(function (jQuery, undefined) {
-  // Run JavaScript in strict mode
-  /*global jQuery:false _:false document:false */
-  'use strict';
-
-  // # Hallo editing widget
-  //
-  // This widget allows editing textual content areas with the
-  // [Hallo](http://hallojs.org) rich text editor.
-  jQuery.widget('Midgard.halloWidget', jQuery.Midgard.editWidget, {
-    options: {
-      editorOptions: {},
-      disabled: true,
-      toolbarState: 'full',
-      vie: null,
-      entity: null
-    },
-    enable: function () {
-      jQuery(this.element).hallo({
-        editable: true
-      });
-      this.options.disabled = false;
-    },
-
-    disable: function () {
-      jQuery(this.element).hallo({
-        editable: false
-      });
-      this.options.disabled = true;
-    },
-
-    _initialize: function () {
-      jQuery(this.element).hallo(this.getHalloOptions());
-      var self = this;
-      jQuery(this.element).on('halloactivated', function (event, data) {
-        self.options.activated();
-      });
-      jQuery(this.element).on('hallodeactivated', function (event, data) {
-        self.options.deactivated();
-      });
-      jQuery(this.element).on('hallomodified', function (event, data) {
-        self.options.changed(data.content);
-        data.editable.setUnmodified();
-      });
-
-      jQuery(document).on('midgardtoolbarstatechange', function(event, data) {
-        // Switch between Hallo configurations when toolbar state changes
-        if (data.display === self.options.toolbarState) {
-          return;
-        }
-        self.options.toolbarState = data.display;
-        if (!self.element.data('IKS-hallo')) {
-          // Hallo not yet instantiated
-          return;
-        }
-        var newOptions = self.getHalloOptions();
-        self.element.hallo('changeToolbar', newOptions.parentElement, newOptions.toolbar, true);
-      });
-    },
-
-    getHalloOptions: function() {
-      var defaults = {
-        plugins: {
-          halloformat: {},
-          halloblock: {},
-          hallolists: {},
-          hallolink: {},
-          halloimage: {
-            entity: this.options.entity
-          }
-        },
-        buttonCssClass: 'create-ui-btn-small',
-        placeholder: '[' + this.options.property + ']'
-      };
-
-      if (typeof this.element.annotate === 'function' && this.options.vie.services.stanbol) {
-        // Enable Hallo Annotate plugin by default if user has annotate.js
-        // loaded and VIE has Stanbol enabled
-        defaults.plugins.halloannotate = {
-            vie: this.options.vie
-        };
-      }
-
-      if (this.options.toolbarState === 'full') {
-        // Use fixed toolbar in the Create tools area
-        defaults.parentElement = jQuery('.create-ui-toolbar-dynamictoolarea .create-ui-tool-freearea');
-        defaults.toolbar = 'halloToolbarFixed';
-      } else {
-        // Tools area minimized, use floating toolbar
-        defaults.parentElement = 'body';
-        defaults.toolbar = 'halloToolbarContextual';
-      }
-      return _.extend(defaults, this.options.editorOptions);
-    }
-  });
-})(jQuery);
-//     Create.js - On-site web editing interface
-//     (c) 2012 Henri Bergius, IKS Consortium
-//     Create may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://createjs.org/
-(function (jQuery, undefined) {
-  // Run JavaScript in strict mode
-  /*global jQuery:false _:false document:false */
-  'use strict';
-
-  // # Redactor editing widget
-  //
-  // This widget allows editing textual content areas with the
-  // [Redactor](http://redactorjs.com/) rich text editor.
-  jQuery.widget('Midgard.redactorWidget', jQuery.Midgard.editWidget, {
-    editor: null,
-
-    options: {
-      editorOptions: {},
-      disabled: true
-    },
-
-    enable: function () {
-      jQuery(this.element).redactor(this.getRedactorOptions());
-      this.options.disabled = false;
-    },
-
-    disable: function () {
-      jQuery(this.element).destroyEditor();
-      this.options.disabled = true;
-    },
-
-    _initialize: function () {
-      var self = this;
-      jQuery(this.element).on('focus', function (event) {
-        self.options.activated(); 
-      });
-      /*
-      jQuery(this.element).on('blur', function (event) {
-        self.options.deactivated(); 
-      });
-      */
-    },
-
-    getRedactorOptions: function () {
-      var self = this;
-      var overrides = {
-        keyupCallback: function (obj, event) {
-          self.options.changed(jQuery(self.element).getCode());
-        },
-        execCommandCallback: function (obj, command) {
-          self.options.changed(jQuery(self.element).getCode());
-        }
-      };
-
-      return _.extend(self.options.editorOptions, overrides);
-    }
-  });
-})(jQuery);
-//     Create.js - On-site web editing interface
-//     (c) 2011-2012 Henri Bergius, IKS Consortium
-//     Create may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://createjs.org/
-(function (jQuery, undefined) {
-  // Run JavaScript in strict mode
-  /*global jQuery:false _:false window:false */
-  'use strict';
-
-  jQuery.widget('Midgard.midgardStorage', {
-    saveEnabled: true,
-    options: {
-      // Whether to use localstorage
-      localStorage: false,
-      removeLocalstorageOnIgnore: true,
-      // VIE instance to use for storage handling
-      vie: null,
-      // URL callback for Backbone.sync
-      url: '',
-      // Whether to enable automatic saving
-      autoSave: false,
-      // How often to autosave in milliseconds
-      autoSaveInterval: 5000,
-      // Whether to save entities that are referenced by entities
-      // we're saving to the server.
-      saveReferencedNew: false,
-      saveReferencedChanged: false,
-      // Namespace used for events from midgardEditable-derived widget
-      editableNs: 'midgardeditable',
-      // CSS selector for the Edit button, leave to null to not bind
-      // notifications to any element
-      editSelector: '#midgardcreate-edit a',
-      localize: function (id, language) {
-        return window.midgardCreate.localize(id, language);
-      },
-      language: null
-    },
-
-    _create: function () {
-      var widget = this;
-      this.changedModels = [];
-
-      if (window.localStorage) {
-        this.options.localStorage = true;
-      }
-
-      this.vie = this.options.vie;
-
-      this.vie.entities.on('add', function (model) {
-        // Add the back-end URL used by Backbone.sync
-        model.url = widget.options.url;
-        model.toJSON = model.toJSONLD;
-      });
-
-      widget._bindEditables();
-      if (widget.options.autoSave) {
-        widget._autoSave();
-      }
-    },
-
-    _autoSave: function () {
-      var widget = this;
-      widget.saveEnabled = true;
-
-      var doAutoSave = function () {
-        if (!widget.saveEnabled) {
-          return;
-        }
-
-        if (widget.changedModels.length === 0) {
-          return;
-        }
-
-        widget.saveRemoteAll({
-          // We make autosaves silent so that potential changes from server
-          // don't disrupt user while writing.
-          silent: true
-        });
-      };
-
-      var timeout = window.setInterval(doAutoSave, widget.options.autoSaveInterval);
-
-      this.element.on('startPreventSave', function () {
-        if (timeout) {
-          window.clearInterval(timeout);
-          timeout = null;
-        }
-        widget.disableAutoSave();
-      });
-      this.element.on('stopPreventSave', function () {
-        if (!timeout) {
-          timeout = window.setInterval(doAutoSave, widget.options.autoSaveInterval);
-        }
-        widget.enableAutoSave();
-      });
-
-    },
-
-    enableAutoSave: function () {
-      this.saveEnabled = true;
-    },
-
-    disableAutoSave: function () {
-      this.saveEnabled = false;
-    },
-
-    _bindEditables: function () {
-      var widget = this;
-      this.restorables = [];
-      var restorer;
-
-      widget.element.on(widget.options.editableNs + 'changed', function (event, options) {
-        if (_.indexOf(widget.changedModels, options.instance) === -1) {
-          widget.changedModels.push(options.instance);
-        }
-        widget._saveLocal(options.instance);
-      });
-
-      widget.element.on(widget.options.editableNs + 'disable', function (event, options) {
-        widget.revertChanges(options.instance);
-      });
-
-      widget.element.on(widget.options.editableNs + 'enable', function (event, options) {
-        if (!options.instance._originalAttributes) {
-          options.instance._originalAttributes = _.clone(options.instance.attributes);
-        }
-
-        if (!options.instance.isNew() && widget._checkLocal(options.instance)) {
-          // We have locally-stored modifications, user needs to be asked
-          widget.restorables.push(options.instance);
-        }
-
-        /*_.each(options.instance.attributes, function (attributeValue, property) {
-          if (attributeValue instanceof widget.vie.Collection) {
-            widget._readLocalReferences(options.instance, property, attributeValue);
-          }
-        });*/
-      });
-
-      widget.element.on('midgardcreatestatechange', function (event, options) {
-        if (options.state === 'browse' || widget.restorables.length === 0) {
-          widget.restorables = [];
-          if (restorer) {
-            restorer.close();
-          }
-          return;
-        }
-        restorer = widget.checkRestore();
-      });
-
-      widget.element.on('midgardstorageloaded', function (event, options) {
-        if (_.indexOf(widget.changedModels, options.instance) === -1) {
-          widget.changedModels.push(options.instance);
-        }
-      });
-    },
-
-    checkRestore: function () {
-      var widget = this;
-      if (widget.restorables.length === 0) {
-        return;
-      }
-
-      var message;
-      var restorer;
-      if (widget.restorables.length === 1) {
-        message = _.template(widget.options.localize('localModification', widget.options.language), {
-          label: widget.restorables[0].getSubjectUri()
-        });
-      } else {
-        message = _.template(widget.options.localize('localModifications', widget.options.language), {
-          number: widget.restorables.length
-        });
-      }
-
-      var doRestore = function (event, notification) {
-        widget.restoreLocalAll();
-        restorer.close();
-      };
-
-      var doIgnore = function (event, notification) {
-        widget.ignoreLocal();
-        restorer.close();
-      };
-
-      restorer = jQuery('body').midgardNotifications('create', {
-        bindTo: widget.options.editSelector,
-        gravity: 'TR',
-        body: message,
-        timeout: 0,
-        actions: [
-          {
-            name: 'restore',
-            label: widget.options.localize('Restore', widget.options.language),
-            cb: doRestore,
-            className: 'create-ui-btn'
-          },
-          {
-            name: 'ignore',
-            label: widget.options.localize('Ignore', widget.options.language),
-            cb: doIgnore,
-            className: 'create-ui-btn'
-          }
-        ],
-        callbacks: {
-          beforeShow: function () {
-            if (!window.Mousetrap) {
-              return;
-            }
-            window.Mousetrap.bind(['command+shift+r', 'ctrl+shift+r'], function (event) {
-              event.preventDefault();
-              doRestore();
-            });
-            window.Mousetrap.bind(['command+shift+i', 'ctrl+shift+i'], function (event) {
-              event.preventDefault();
-              doIgnore();
-            });
-          },
-          afterClose: function () {
-            if (!window.Mousetrap) {
-              return;
-            }
-            window.Mousetrap.unbind(['command+shift+r', 'ctrl+shift+r']);
-            window.Mousetrap.unbind(['command+shift+i', 'ctrl+shift+i']);
-          }
-        }
-      });
-      return restorer;
-    },
-
-    restoreLocalAll: function () {
-      _.each(this.restorables, function (instance) {
-        this.readLocal(instance);
-      }, this);
-      this.restorables = [];
-    },
-
-    ignoreLocal: function () {
-      if (this.options.removeLocalstorageOnIgnore) {
-        _.each(this.restorables, function (instance) {
-          this._removeLocal(instance);
-        }, this);
-      }
-      this.restorables = [];
-    },
-
-    saveReferences: function (model) {
-      _.each(model.attributes, function (value, property) {
-        if (!value || !value.isCollection) {
-          return;
-        }
-
-        value.each(function (referencedModel) {
-          if (this.changedModels.indexOf(referencedModel) !== -1) {
-            // The referenced model is already in the save queue
-            return;
-          }
-
-          if (referencedModel.isNew() && this.options.saveReferencedNew) {
-            return referencedModel.save();
-          }
-
-          if (referencedModel.hasChanged() && this.options.saveReferencedChanged) {
-            return referencedModel.save();
-          }
-        }, this);
-      }, this);
-    },
-
-    saveRemote: function (model, options) {
-      // Optionally handle entities referenced in this model first
-      this.saveReferences(model);
-
-      this._trigger('saveentity', null, {
-        entity: model,
-        options: options
-      });
-
-      var widget = this;
-      model.save(null, _.extend({}, options, {
-        success: function (m, response) {
-          // From now on we're going with the values we have on server
-          model._originalAttributes = _.clone(model.attributes);
-          widget._removeLocal(model);
-          window.setTimeout(function () {
-            // Remove the model from the list of changed models after saving
-            widget.changedModels.splice(widget.changedModels.indexOf(model), 1);
-          }, 0);
-          if (_.isFunction(options.success)) {
-            options.success(m, response);
-          }
-          widget._trigger('savedentity', null, {
-            entity: model,
-            options: options
-          });
-        },
-        error: function (m, response) {
-          if (_.isFunction(options.error)) {
-            options.error(m, response);
-          }
-        }
-      }));
-    },
-
-    saveRemoteAll: function (options) {
-      var widget = this;
-      if (widget.changedModels.length === 0) {
-        return;
-      }
-
-      widget._trigger('save', null, {
-        entities: widget.changedModels,
-        options: options,
-        // Deprecated
-        models: widget.changedModels
-      });
-
-      var notification_msg;
-      var needed = widget.changedModels.length;
-      if (needed > 1) {
-        notification_msg = _.template(widget.options.localize('saveSuccessMultiple', widget.options.language), {
-          number: needed
-        });
-      } else {
-        notification_msg = _.template(widget.options.localize('saveSuccess', widget.options.language), {
-          label: widget.changedModels[0].getSubjectUri()
-        });
-      }
-
-      widget.disableAutoSave();
-      _.each(widget.changedModels, function (model) {
-        this.saveRemote(model, {
-          success: function (m, response) {
-            needed--;
-            if (needed <= 0) {
-              // All models were happily saved
-              widget._trigger('saved', null, {
-                options: options
-              });
-              if (options && _.isFunction(options.success)) {
-                options.success(m, response);
-              }
-              jQuery('body').midgardNotifications('create', {
-                body: notification_msg
-              });
-              widget.enableAutoSave();
-            }
-          },
-          error: function (m, err) {
-            if (options && _.isFunction(options.error)) {
-              options.error(m, err);
-            }
-            jQuery('body').midgardNotifications('create', {
-              body: _.template(widget.options.localize('saveError', widget.options.language), {
-                error: err.responseText || ''
-              }),
-              timeout: 0
-            });
-
-            widget._trigger('error', null, {
-              instance: model
-            });
-          }
-        });
-      }, this);
-    },
-
-    _saveLocal: function (model) {
-      if (!this.options.localStorage) {
-        return;
-      }
-
-      if (model.isNew()) {
-        // Anonymous object, save as refs instead
-        if (!model.primaryCollection) {
-          return;
-        }
-        return this._saveLocalReferences(model.primaryCollection.subject, model.primaryCollection.predicate, model);
-      }
-      window.localStorage.setItem(model.getSubjectUri(), JSON.stringify(model.toJSONLD()));
-    },
-
-    _getReferenceId: function (model, property) {
-      return model.id + ':' + property;
-    },
-
-    _saveLocalReferences: function (subject, predicate, model) {
-      if (!this.options.localStorage) {
-        return;
-      }
-
-      if (!subject || !predicate) {
-        return;
-      }
-
-      var widget = this;
-      var identifier = subject + ':' + predicate;
-      var json = model.toJSONLD();
-      if (window.localStorage.getItem(identifier)) {
-        var referenceList = JSON.parse(window.localStorage.getItem(identifier));
-        var index = _.pluck(referenceList, '@').indexOf(json['@']);
-        if (index !== -1) {
-          referenceList[index] = json;
-        } else {
-          referenceList.push(json);
-        }
-        window.localStorage.setItem(identifier, JSON.stringify(referenceList));
-        return;
-      }
-      window.localStorage.setItem(identifier, JSON.stringify([json]));
-    },
-
-    _checkLocal: function (model) {
-      if (!this.options.localStorage) {
-        return false;
-      }
-
-      var local = window.localStorage.getItem(model.getSubjectUri());
-      if (!local) {
-        return false;
-      }
-
-      return true;
-    },
-
-    hasLocal: function (model) {
-      if (!this.options.localStorage) {
-        return false;
-      }
-
-      if (!window.localStorage.getItem(model.getSubjectUri())) {
-        return false;
-      }
-      return true;
-    },
-
-    readLocal: function (model) {
-      if (!this.options.localStorage) {
-        return;
-      }
-
-      var local = window.localStorage.getItem(model.getSubjectUri());
-      if (!local) {
-        return;
-      }
-      if (!model._originalAttributes) {
-        model._originalAttributes = _.clone(model.attributes);
-      }
-      var parsed = JSON.parse(local);
-      var entity = this.vie.entities.addOrUpdate(parsed, {
-        overrideAttributes: true
-      });
-
-      this._trigger('loaded', null, {
-        instance: entity
-      });
-    },
-
-    _readLocalReferences: function (model, property, collection) {
-      if (!this.options.localStorage) {
-        return;
-      }
-
-      var identifier = this._getReferenceId(model, property);
-      var local = window.localStorage.getItem(identifier);
-      if (!local) {
-        return;
-      }
-      collection.add(JSON.parse(local));
-    },
-
-    revertChanges: function (model) {
-      var widget = this;
-
-      // Remove unsaved collection members
-      if (!model) { return; }
-      _.each(model.attributes, function (attributeValue, property) {
-        if (attributeValue instanceof widget.vie.Collection) {
-          var removables = [];
-          attributeValue.forEach(function (model) {
-            if (model.isNew()) {
-              removables.push(model);
-            }
-          });
-          attributeValue.remove(removables);
-        }
-      });
-
-      // Restore original object properties
-      if (!model.changedAttributes()) {
-        if (model._originalAttributes) {
-          model.set(model._originalAttributes);
-        }
-        return;
-      }
-
-      model.set(model.previousAttributes());
-    },
-
-    _removeLocal: function (model) {
-      if (!this.options.localStorage) {
-        return;
-      }
-
-      window.localStorage.removeItem(model.getSubjectUri());
-    }
-  });
-})(jQuery);
-if (window.midgardCreate === undefined) {
-  window.midgardCreate = {};
-}
-if (window.midgardCreate.locale === undefined) {
-  window.midgardCreate.locale = {};
-}
-
-window.midgardCreate.locale.en = {
-  // Session-state buttons for the main toolbar
-  'Save': 'Save',
-  'Saving': 'Saving',
-  'Cancel': 'Cancel',
-  'Edit': 'Edit',
-  // Storage status messages
-  'localModification': 'Item "<%= label %>" has local modifications',
-  'localModifications': '<%= number %> items on this page have local modifications',
-  'Restore': 'Restore',
-  'Ignore': 'Ignore',
-  'saveSuccess': 'Item "<%= label %>" saved successfully',
-  'saveSuccessMultiple': '<%= number %> items saved successfully',
-  'saveError': 'Error occurred while saving<br /><%= error %>',
-  // Tagging
-  'Item tags': 'Item tags',
-  'Suggested tags': 'Suggested tags',
-  'Tags': 'Tags',
-  'add a tag': 'add a tag',
-  // Collection widgets
-  'Add': 'Add',
-  'Choose type to add': 'Choose type to add'
-};
diff --git a/core/misc/vie/vie-core.js b/core/misc/vie/vie-core.js
deleted file mode 100644
index 4e1526ebcb5b3fcaa11abf8c0c5ce2a2926157e1..0000000000000000000000000000000000000000
--- a/core/misc/vie/vie-core.js
+++ /dev/null
@@ -1,3719 +0,0 @@
-/*Copyright (c) 2011 Henri Bergius, IKS Consortium
-Copyright (c) 2011 Sebastian Germesin, IKS Consortium
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-*/(function(){//     VIE - Vienna IKS Editables
-//     (c) 2011 Henri Bergius, IKS Consortium
-//     (c) 2011 Sebastian Germesin, IKS Consortium
-//     (c) 2011 Szaby Grünwald, IKS Consortium
-//     VIE may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://viejs.org/
-
-/*global console:false exports:false require:false */
-
-var root = this,
-    jQuery = root.jQuery,
-    Backbone = root.Backbone,
-    _ = root._;
-
-
-// ## VIE constructor
-//
-// The VIE constructor is the way to initialize VIE for your
-// application. The instance of VIE handles all management of
-// semantic interaction, including keeping track of entities,
-// changes to them, the possible RDFa views on the page where
-// the entities are displayed, and connections to external
-// services like Stanbol and DBPedia.
-//
-// To get a VIE instance, simply run:
-//
-//     var vie = new VIE();
-//
-// You can also pass configurations to the VIE instance through
-// the constructor. For example, to set a different default
-// namespace to be used for names that don't have a namespace
-// specified, do:
-//
-//     var vie = new VIE({
-//         baseNamespace: 'http://example.net'
-//     });
-//
-// ### Differences with VIE 1.x
-//
-// VIE 1.x used singletons for managing entities and views loaded
-// from a page. This has been changed with VIE 2.x, and now all
-// data managed by VIE is tied to the instance of VIE being used.
-//
-// This means that VIE needs to be instantiated before using. So,
-// when previously you could get entities from page with:
-//
-//     VIE.RDFaEntities.getInstances();
-//
-// Now you need to instantiate VIE first. This example uses the
-// Classic API compatibility layer instead of the `load` method:
-//
-//     var vie = new VIE();
-//     vie.RDFaEntities.getInstances();
-//
-// Currently the Classic API is enabled by default, but it is
-// recommended to ensure it is enabled before using it. So:
-//
-//     var vie = new VIE({classic: true});
-//     vie.RDFaEntities.getInstances();
-var VIE = root.VIE = function(config) {
-    this.config = (config) ? config : {};
-    this.services = {};
-    this.jQuery = jQuery;
-    this.entities = new this.Collection([], {
-        vie: this
-    });
-
-    this.Entity.prototype.entities = this.entities;
-    this.Entity.prototype.entityCollection = this.Collection;
-    this.Entity.prototype.vie = this;
-
-    this.Namespaces.prototype.vie = this;
-// ### Namespaces in VIE
-// VIE supports different ontologies and an easy use of them.
-// Namespace prefixes reduce the amount of code you have to
-// write. In VIE, it does not matter if you access an entitie's
-// property with
-// `entity.get('<http://dbpedia.org/property/capitalOf>')` or
-// `entity.get('dbprop:capitalOf')` or even
-// `entity.get('capitalOf')` once the corresponding namespace
-// is registered as *baseNamespace*.
-// By default `"http://viejs.org/ns/"`is set as base namespace.
-// For more information about how to set, get and list all
-// registered namespaces, refer to the
-// <a href="Namespace.html">Namespaces documentation</a>.
-    this.namespaces = new this.Namespaces(
-        (this.config.baseNamespace) ? this.config.baseNamespace : "http://viejs.org/ns/",
-
-// By default, VIE is shipped with common namespace prefixes:
-
-// +    owl    : "http://www.w3.org/2002/07/owl#"
-// +    rdfs   : "http://www.w3.org/2000/01/rdf-schema#"
-// +    rdf    : "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-// +    schema : 'http://schema.org/'
-// +    foaf   : 'http://xmlns.com/foaf/0.1/'
-// +    geo    : 'http://www.w3.org/2003/01/geo/wgs84_pos#'
-// +    dbpedia: "http://dbpedia.org/ontology/"
-// +    dbprop : "http://dbpedia.org/property/"
-// +    skos   : "http://www.w3.org/2004/02/skos/core#"
-// +    xsd    : "http://www.w3.org/2001/XMLSchema#"
-// +    sioc   : "http://rdfs.org/sioc/ns#"
-// +    dcterms: "http://purl.org/dc/terms/"
-        {
-            owl    : "http://www.w3.org/2002/07/owl#",
-            rdfs   : "http://www.w3.org/2000/01/rdf-schema#",
-            rdf    : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
-            schema : 'http://schema.org/',
-            foaf   : 'http://xmlns.com/foaf/0.1/',
-            geo    : 'http://www.w3.org/2003/01/geo/wgs84_pos#',
-            dbpedia: "http://dbpedia.org/ontology/",
-            dbprop : "http://dbpedia.org/property/",
-            skos   : "http://www.w3.org/2004/02/skos/core#",
-            xsd    : "http://www.w3.org/2001/XMLSchema#",
-            sioc   : "http://rdfs.org/sioc/ns#",
-            dcterms: "http://purl.org/dc/terms/"
-        }
-    );
-
-
-    this.Type.prototype.vie = this;
-    this.Types.prototype.vie = this;
-    this.Attribute.prototype.vie = this;
-    this.Attributes.prototype.vie = this;
-// ### Type hierarchy in VIE
-// VIE takes care about type hierarchy of entities
-// (aka. *schema* or *ontology*).
-// Once a type hierarchy is known to VIE, we can leverage
-// this information, to easily ask, whether an entity
-// is of type, e.g., *foaf:Person* or *schema:Place*.
-// For more information about how to generate such a type
-// hierarchy, refer to the
-// <a href="Type.html">Types documentation</a>.
-    this.types = new this.Types();
-// By default, there is a parent type in VIE, called
-// *owl:Thing*. All types automatically inherit from this
-// type and all registered entities, are of this type.
-    this.types.add("owl:Thing");
-
-// As described above, the Classic API of VIE 1.x is loaded
-// by default. As this might change in the future, it is
-// recommended to ensure it is enabled before using it. So:
-//
-//     var vie = new VIE({classic: true});
-//     vie.RDFaEntities.getInstances();
-    if (this.config.classic === true) {
-        /* Load Classic API as well */
-        this.RDFa = new this.ClassicRDFa(this);
-        this.RDFaEntities = new this.ClassicRDFaEntities(this);
-        this.EntityManager = new this.ClassicEntityManager(this);
-
-        this.cleanup = function() {
-            this.entities.reset();
-        };
-    }
-};
-
-// ### use(service, name)
-// This method registers services within VIE.
-// **Parameters**:
-// *{string|object}* **service** The service to be registered.
-// *{string}* **name** An optional name to register the service with. If this
-// is not set, the default name that comes with the service is taken.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE}* : The current VIE instance.
-// **Example usage**:
-//
-//     var vie = new VIE();
-//     var conf1 = {...};
-//     var conf2 = {...};
-//     vie.use(new vie.StanbolService());
-//     vie.use(new vie.StanbolService(conf1), "stanbol_1");
-//     vie.use(new vie.StanbolService(conf2), "stanbol_2");
-//     // <-- this means that there are now 3 services registered!
-VIE.prototype.use = function(service, name) {
-  if (!name && !service.name) {
-    throw new Error("Please provide a name for the service!");
-  }
-  service.vie = this;
-  service.name = (name)? name : service.name;
-  if (service.init) {
-      service.init();
-  }
-  this.services[service.name] = service;
-
-  return this;
-};
-
-// ### service(name)
-// This method returns the service object that is
-// registered under the given name.
-// **Parameters**:
-// *{string}* **name** ...
-// **Throws**:
-// *{Error}* if no service could be found.
-// **Returns**:
-// *{object}* : The service to be queried.
-// **Example usage**:
-//
-//     var vie = new VIE();
-//     vie.use(new vie.StanbolService(), "stanbol");
-//     var service = vie.service("stanbol");
-VIE.prototype.service = function(name) {
-  if (!this.hasService(name)) {
-    throw "Undefined service " + name;
-  }
-  return this.services[name];
-};
-
-// ### hasService(name)
-// This method returns a boolean telling whether VIE has a particular
-// service loaded.
-// **Parameters**:
-// *{string}* **name**
-// **Returns**:
-// *{boolean}* whether service is available
-VIE.prototype.hasService = function(name) {
-  if (!this.services[name]) {
-    return false;
-  }
-  return true;
-};
-
-// ### getServicesArray()
-// This method returns an array of all registered services.
-// **Parameters**:
-// *nothing*
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{array}* : An array of service instances.
-// **Example usage**:
-//
-//     var vie = new VIE();
-//     vie.use(new vie.StanbolService(), "stanbol");
-//     var services = vie.getServicesArray();
-//     services.length; // <-- 1
-VIE.prototype.getServicesArray = function() {
-  return _.map(this.services, function (v) {return v;});
-};
-
-// ### load(options)
-// This method instantiates a new VIE.Loadable in order to
-// perform queries on the services.
-// **Parameters**:
-// *{object}* **options** Options to be set.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Loadable}* : A new instance of VIE.Loadable.
-// **Example usage**:
-//
-//     var vie = new VIE();
-//     vie.use(new vie.StanbolService(), "stanbol");
-//     var loader = vie.load({...});
-VIE.prototype.load = function(options) {
-  if (!options) { options = {}; }
-  options.vie = this;
-  return new this.Loadable(options);
-};
-
-// ### save(options)
-// This method instantiates a new VIE.Savable in order to
-// perform queries on the services.
-// **Parameters**:
-// *{object}* **options** Options to be set.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Savable}* : A new instance of VIE.Savable.
-// **Example usage**:
-//
-//     var vie = new VIE();
-//     vie.use(new vie.StanbolService(), "stanbol");
-//     var saver = vie.save({...});
-VIE.prototype.save = function(options) {
-  if (!options) { options = {}; }
-  options.vie = this;
-  return new this.Savable(options);
-};
-
-// ### remove(options)
-// This method instantiates a new VIE.Removable in order to
-// perform queries on the services.
-// **Parameters**:
-// *{object}* **options** Options to be set.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Removable}* : A new instance of VIE.Removable.
-// **Example usage**:
-//
-//     var vie = new VIE();
-//     vie.use(new vie.StanbolService(), "stanbol");
-//     var remover = vie.remove({...});
-VIE.prototype.remove = function(options) {
-  if (!options) { options = {}; }
-  options.vie = this;
-  return new this.Removable(options);
-};
-
-// ### analyze(options)
-// This method instantiates a new VIE.Analyzable in order to
-// perform queries on the services.
-// **Parameters**:
-// *{object}* **options** Options to be set.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Analyzable}* : A new instance of VIE.Analyzable.
-// **Example usage**:
-//
-//     var vie = new VIE();
-//     vie.use(new vie.StanbolService(), "stanbol");
-//     var analyzer = vie.analyze({...});
-VIE.prototype.analyze = function(options) {
-  if (!options) { options = {}; }
-  options.vie = this;
-  return new this.Analyzable(options);
-};
-
-// ### find(options)
-// This method instantiates a new VIE.Findable in order to
-// perform queries on the services.
-// **Parameters**:
-// *{object}* **options** Options to be set.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Findable}* : A new instance of VIE.Findable.
-// **Example usage**:
-//
-//     var vie = new VIE();
-//     vie.use(new vie.StanbolService(), "stanbol");
-//     var finder = vie.find({...});
-VIE.prototype.find = function(options) {
-  if (!options) { options = {}; }
-  options.vie = this;
-  return new this.Findable(options);
-};
-
-// ### loadSchema(url, options)
-// VIE only knows the *owl:Thing* type by default.
-// You can use this method to import another
-// schema (ontology) from an external resource.
-// (Currently, this supports only the JSON format!!)
-// As this method works asynchronously, you might want
-// to register `success` and `error` callbacks via the
-// options.
-// **Parameters**:
-// *{string}* **url** The url, pointing to the schema to import.
-// *{object}* **options** Options to be set.
-// (Set ```success``` and ```error``` as callbacks.).
-// **Throws**:
-// *{Error}* if the url is not set.
-// **Returns**:
-// *{VIE}* : The VIE instance itself.
-// **Example usage**:
-//
-//     var vie = new VIE();
-//     vie.loadSchema("http://schema.rdfs.org/all.json",
-//        {
-//          baseNS : "http://schema.org/",
-//          success : function () {console.log("success");},
-//          error  : function (msg) {console.warn(msg);}
-//        });
-VIE.prototype.loadSchema = function(url, options) {
-    options = (!options)? {} : options;
-
-    if (!url) {
-        throw new Error("Please provide a proper URL");
-    }
-    else {
-        var vie = this;
-        jQuery.getJSON(url)
-        .success(function(data) {
-            try {
-                VIE.Util.loadSchemaOrg(vie, data, options.baseNS);
-                if (options.success) {
-                    options.success.call(vie);
-                }
-            } catch (e) {
-                options.error.call(vie, e);
-                return;
-            }
-         })
-        .error(function(data, textStatus, jqXHR) {
-            if (options.error) {
-                console.warn(data, textStatus, jqXHR);
-                options.error.call(vie, "Could not load schema from URL (" + url + ")");
-            }
-         });
-    }
-
-    return this;
-};
-
-// ### getTypedEntityClass(type)
-// This method generates a special type of `Entity` based on the given type.
-// **Parameters**:
-// *{string}* **type** The type.
-// **Throws**:
-// *{Error}* if the type is unknown to VIE.
-// **Returns**:
-// *{VIE.Entity}* : A subclass of `VIE.Entity`.
-// **Example usage**:
-//
-//     var vie = new VIE();
-//     vie.types.add("Person");
-//     var PersonClass = vie.getTypedEntityClass("Person");
-//     var Person = new PersonClass({"name", "Sebastian"});
-VIE.prototype.getTypedEntityClass = function (type) {
-  var typeType = this.types.get(type);
-  if (!typeType) {
-    throw new Error("Unknown type " + type);
-  }
-  var TypedEntityClass = function (attrs, opts) {
-    if (!attrs) {
-      attrs = {};
-    }
-    attrs["@type"] = type;
-    this.set(attrs, opts);
-  };
-  TypedEntityClass.prototype = new this.Entity();
-  TypedEntityClass.prototype.schema = function () {
-    return VIE.Util.getFormSchemaForType(typeType);
-  };
-  return TypedEntityClass;
-};
-
-// ## Running VIE on Node.js
-//
-// When VIE is running under Node.js we can use the CommonJS
-// require interface to load our dependencies automatically.
-//
-// This means Node.js users don't need to care about dependencies
-// and can just run VIE with:
-//
-//     var VIE = require('vie');
-//
-// In browser environments the dependencies have to be included
-// before including VIE itself.
-if (typeof exports === 'object') {
-    exports.VIE = VIE;
-
-    if (!jQuery) {
-        jQuery = require('jquery');
-    }
-    if (!Backbone) {
-        Backbone = require('backbone');
-        Backbone.setDomLibrary(jQuery);
-    }
-    if (!_) {
-        _ = require('underscore')._;
-    }
-}
-//     VIE - Vienna IKS Editables
-//     (c) 2011 Henri Bergius, IKS Consortium
-//     (c) 2011 Sebastian Germesin, IKS Consortium
-//     (c) 2011 Szaby Grünwald, IKS Consortium
-//     VIE may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://viejs.org/
-
-// ## VIE.Able
-// VIE implements asynchronius service methods through
-// [jQuery.Deferred](http://api.jquery.com/category/deferred-object/) objects.
-// Loadable, Analysable, Savable, etc. are part of the VIE service API and
-// are implemented with the generic VIE.Able class.
-// Example:
-//
-//      VIE.prototype.Loadable = function (options) {
-//          this.init(options,"load");
-//      };
-//      VIE.prototype.Loadable.prototype = new VIE.prototype.Able();
-//
-// This defines
-//
-//     someVIEService.load(options)
-//     .using(...)
-//     .execute()
-//     .success(...)
-//     .fail(...)
-// which will run the asynchronius `load` function of the service with the created Loadable
-// object.
-
-// ### VIE.Able()
-// This is the constructor of a VIE.Able. This should not be called
-// globally but using the inherited classes below.
-// **Parameters**:
-// *nothing*
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Able}* : A **new** VIE.Able object.
-// Example:
-//
-//      VIE.prototype.Loadable = function (options) {
-//          this.init(options,"load");
-//      };
-//      VIE.prototype.Loadable.prototype = new VIE.prototype.Able();
-VIE.prototype.Able = function(){
-
-// ### init(options, methodName)
-// Internal method, called during initialization.
-// **Parameters**:
-// *{object}* **options** the *able* options coming from the API call
-// *{string}* **methodName** the service method called on `.execute`.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Able}* : The current instance.
-// **Example usage**:
-//
-//      VIE.prototype.Loadable = function (options) {
-//          this.init(options,"load");
-//      };
-//      VIE.prototype.Loadable.prototype = new VIE.prototype.Able();
-    this.init = function(options, methodName) {
-        this.options = options;
-        this.services = options.from || options.using || options.to || [];
-        this.vie = options.vie;
-
-        this.methodName = methodName;
-
-        // Instantiate the deferred object
-        this.deferred = jQuery.Deferred();
-
-// In order to get more information and documentation about the passed-through
-// deferred methods and their synonyms, please see the documentation of
-// the [jQuery.Deferred object](http://api.jquery.com/category/deferred-object/)
-        /* Public deferred-methods */
-        this.resolve = this.deferred.resolve;
-        this.resolveWith = this.deferred.resolveWith;
-        this.reject = this.deferred.reject;
-        this.rejectWith = this.deferred.rejectWith;
-        this.success = this.done = this.deferred.done;
-        this.fail = this.deferred.fail;
-        this.then = this.deferred.then;
-        this.always = this.deferred.always;
-        this.from = this.using;
-        this.to = this.using;
-
-        return this;
-    };
-
-
-// ### using(services)
-// This method registers services with the current able instance.
-// **Parameters**:
-// *{string|array}* **services** An id of a service or an array of strings.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Able}* : The current instance.
-// **Example usage**:
-//
-//     var loadable = vie.load({id: "http://example.com/entity/1234"});
-//     able.using("myService");
-    this.using = function(services) {
-        var self = this;
-        services = (_.isArray(services))? services : [ services ];
-        _.each (services, function (s) {
-            var obj = (typeof s === "string")? self.vie.service(s) : s;
-            self.services.push(obj);
-        });
-        return this;
-    };
-
-// ### execute()
-// This method runs the actual method on all registered services.
-// **Parameters**:
-// *nothing*
-// **Throws**:
-// *nothing* ...
-// **Returns**:
-// *{VIE.Able}* : The current instance.
-// **Example usage**:
-//
-//     var able = new vie.Able().init();
-//     able.using("stanbol")
-//     .done(function () {alert("finished");})
-//     .execute();
-    this.execute = function() {
-        /* call service[methodName] */
-        var able = this;
-        _(this.services).each(function(service){
-            service[able.methodName](able);
-        });
-        return this;
-    };
-};
-
-// ## VIE.Loadable
-// A ```VIE.Loadable``` is a wrapper around the deferred object
-// to **load** semantic data from a semantic web service.
-VIE.prototype.Loadable = function (options) {
-    this.init(options,"load");
-};
-VIE.prototype.Loadable.prototype = new VIE.prototype.Able();
-
-// ## VIE.Savable
-// A ```VIE.Savable``` is a wrapper around the deferred object
-// to **save** entities by a VIE service. The RDFaService would write the data
-// in the HTML as RDFa, the StanbolService stores the data in its Entityhub, etc.
-VIE.prototype.Savable = function(options){
-    this.init(options, "save");
-};
-VIE.prototype.Savable.prototype = new VIE.prototype.Able();
-
-// ## VIE.Removable
-// A ```VIE.Removable``` is a wrapper around the deferred object
-// to **remove** semantic data from a semantic web service.
-VIE.prototype.Removable = function(options){
-    this.init(options, "remove");
-};
-VIE.prototype.Removable.prototype = new VIE.prototype.Able();
-
-// ## VIE.Analyzable
-// A ```VIE.Analyzable``` is a wrapper around the deferred object
-// to **analyze** data and extract semantic information with the
-// help of a semantic web service.
-VIE.prototype.Analyzable = function (options) {
-    this.init(options, "analyze");
-};
-VIE.prototype.Analyzable.prototype = new VIE.prototype.Able();
-
-// ## VIE.Findable
-// A ```VIE.Findable``` is a wrapper around the deferred object
-// to **find** semantic data on a semantic storage.
-VIE.prototype.Findable = function (options) {
-    this.init(options, "find");
-};
-VIE.prototype.Findable.prototype = new VIE.prototype.Able();
-
-//     VIE - Vienna IKS Editables
-//     (c) 2011 Henri Bergius, IKS Consortium
-//     (c) 2011 Sebastian Germesin, IKS Consortium
-//     (c) 2011 Szaby Grünwald, IKS Consortium
-//     VIE may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://viejs.org/
-
-// ## VIE Utils
-//
-// The here-listed methods are utility methods for the day-to-day
-// VIE.js usage. All methods are within the static namespace ```VIE.Util```.
-VIE.Util = {
-
-// ### VIE.Util.toCurie(uri, safe, namespaces)
-// This method converts a given
-// URI into a CURIE (or SCURIE), based on the given ```VIE.Namespaces``` object.
-// If the given uri is already a URI, it is left untouched and directly returned.
-// If no prefix could be found, an ```Error``` is thrown.
-// **Parameters**:
-// *{string}* **uri** The URI to be transformed.
-// *{boolean}* **safe** A flag whether to generate CURIEs or SCURIEs.
-// *{VIE.Namespaces}* **namespaces** The namespaces to be used for the prefixes.
-// **Throws**:
-// *{Error}* If no prefix could be found in the passed namespaces.
-// **Returns**:
-// *{string}* The CURIE or SCURIE.
-// **Example usage**:
-//
-//     var ns = new myVIE.Namespaces(
-//           "http://viejs.org/ns/",
-//           { "dbp": "http://dbpedia.org/ontology/" }
-//     );
-//     var uri = "<http://dbpedia.org/ontology/Person>";
-//     VIE.Util.toCurie(uri, false, ns); // --> dbp:Person
-//     VIE.Util.toCurie(uri, true, ns); // --> [dbp:Person]
-    toCurie : function (uri, safe, namespaces) {
-        if (VIE.Util.isCurie(uri, namespaces)) {
-            return uri;
-        }
-        var delim = ":";
-        for (var k in namespaces.toObj()) {
-            if (uri.indexOf(namespaces.get(k)) === 1) {
-                var pattern = new RegExp("^" + "<?" + namespaces.get(k));
-                if (k === '') {
-                    delim = '';
-                }
-                return ((safe)? "[" : "") +
-                        uri.replace(pattern, k + delim).replace(/>$/, '') +
-                        ((safe)? "]" : "");
-            }
-        }
-        throw new Error("No prefix found for URI '" + uri + "'!");
-    },
-
-// ### VIE.Util.isCurie(curie, namespaces)
-// This method checks, whether
-// the given string is a CURIE and returns ```true``` if so and ```false```otherwise.
-// **Parameters**:
-// *{string}* **curie** The CURIE (or SCURIE) to be checked.
-// *{VIE.Namespaces}* **namespaces** The namespaces to be used for the prefixes.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{boolean}* ```true``` if the given curie is a CURIE or SCURIE and ```false``` otherwise.
-// **Example usage**:
-//
-//     var ns = new myVIE.Namespaces(
-//           "http://viejs.org/ns/",
-//           { "dbp": "http://dbpedia.org/ontology/" }
-//     );
-//     var uri = "<http://dbpedia.org/ontology/Person>";
-//     var curie = "dbp:Person";
-//     var scurie = "[dbp:Person]";
-//     var text = "This is some text.";
-//     VIE.Util.isCurie(uri, ns);    // --> false
-//     VIE.Util.isCurie(curie, ns);  // --> true
-//     VIE.Util.isCurie(scurie, ns); // --> true
-//     VIE.Util.isCurie(text, ns);   // --> false
-    isCurie : function (curie, namespaces) {
-        if (VIE.Util.isUri(curie)) {
-            return false;
-        } else {
-            try {
-                VIE.Util.toUri(curie, namespaces);
-                return true;
-            } catch (e) {
-                return false;
-            }
-        }
-    },
-
-// ### VIE.Util.toUri(curie, namespaces)
-// This method converts a
-// given CURIE (or save CURIE) into a URI, based on the given ```VIE.Namespaces``` object.
-// **Parameters**:
-// *{string}* **curie** The CURIE to be transformed.
-// *{VIE.Namespaces}* **namespaces** The namespaces object
-// **Throws**:
-// *{Error}* If no URI could be assembled.
-// **Returns**:
-// *{string}* : A string, representing the URI.
-// **Example usage**:
-//
-//     var ns = new myVIE.Namespaces(
-//           "http://viejs.org/ns/",
-//           { "dbp": "http://dbpedia.org/ontology/" }
-//     );
-//     var curie = "dbp:Person";
-//     var scurie = "[dbp:Person]";
-//     VIE.Util.toUri(curie, ns);
-//          --> <http://dbpedia.org/ontology/Person>
-//     VIE.Util.toUri(scurie, ns);
-//          --> <http://dbpedia.org/ontology/Person>
-    toUri : function (curie, namespaces) {
-        if (VIE.Util.isUri(curie)) {
-            return curie;
-        }
-        var delim = ":";
-        for (var prefix in namespaces.toObj()) {
-            if (prefix !== "" && (curie.indexOf(prefix + ":") === 0 || curie.indexOf("[" + prefix + ":") === 0)) {
-                var pattern = new RegExp("^" + "\\[{0,1}" + prefix + delim);
-                return "<" + curie.replace(pattern, namespaces.get(prefix)).replace(/\]{0,1}$/, '') + ">";
-            }
-        }
-        /* check for the default namespace */
-        if (curie.indexOf(delim) === -1) {
-            return "<" + namespaces.base() + curie + ">";
-        }
-        throw new Error("No prefix found for CURIE '" + curie + "'!");
-    },
-
-// ### VIE.Util.isUri(something)
-// This method checks, whether the given string is a URI.
-// **Parameters**:
-// *{string}* **something** : The string to be checked.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{boolean}* : ```true``` if the string is a URI, ```false``` otherwise.
-// **Example usage**:
-//
-//     var uri = "<http://dbpedia.org/ontology/Person>";
-//     var curie = "dbp:Person";
-//     VIE.Util.isUri(uri);   // --> true
-//     VIE.Util.isUri(curie); // --> false
-    isUri : function (something) {
-        return (typeof something === "string" && something.search(/^<.+>$/) === 0);
-    },
-
-// ### VIE.Util.mapAttributeNS(attr, ns)
-// This method maps an attribute of an entity into namespaces if they have CURIEs.
-// **Parameters**:
-// *{string}* **attr** : The attribute to be transformed.
-// *{VIE.Namespaces}* **ns** : The namespaces.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{string}* : The transformed attribute's name.
-// **Example usage**:
-//
-//      var attr = "name";
-//      var ns = myVIE.namespaces;
-//      VIE.Util.mapAttributeNS(attr, ns); // '<' + ns.base() + attr + '>';
-    mapAttributeNS : function (attr, ns) {
-        var a = attr;
-        if (ns.isUri (attr) || attr.indexOf('@') === 0) {
-            //ignore
-        } else if (ns.isCurie(attr)) {
-            a = ns.uri(attr);
-        } else if (!ns.isUri(attr)) {
-            if (attr.indexOf(":") === -1) {
-                a = '<' + ns.base() + attr + '>';
-            } else {
-                a = '<' + attr + '>';
-            }
-        }
-        return a;
-    },
-
-// ### VIE.Util.rdf2Entities(service, results)
-// This method converts *rdf/json* data from an external service
-// into VIE.Entities.
-// **Parameters**:
-// *{object}* **service** The service that retrieved the data.
-// *{object}* **results** The data to be transformed.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{[VIE.Entity]}* : An array, containing VIE.Entity instances which have been transformed from the given data.
-    rdf2Entities: function (service, results) {
-        if (typeof jQuery.rdf !== 'function') {
-            /* fallback if no rdfQuery has been loaded */
-            return VIE.Util._rdf2EntitiesNoRdfQuery(service, results);
-        }
-        try {
-            var rdf = (results instanceof jQuery.rdf)?
-                    results.base(service.vie.namespaces.base()) :
-                        jQuery.rdf().base(service.vie.namespaces.base()).load(results, {});
-
-            /* if the service contains rules to apply special transformation, they are executed here.*/
-            if (service.rules) {
-                var rules = jQuery.rdf.ruleset();
-                for (var prefix in service.vie.namespaces.toObj()) {
-                    if (prefix !== "") {
-                        rules.prefix(prefix, service.vie.namespaces.get(prefix));
-                    }
-                }
-                for (var i = 0; i < service.rules.length; i++)if(service.rules.hasOwnProperty(i)) {
-                    var rule = service.rules[i];
-                    rules.add(rule.left, rule.right);
-                }
-                rdf = rdf.reason(rules, 10); /* execute the rules only 10 times to avoid looping */
-            }
-            var entities = {};
-            rdf.where('?subject ?property ?object').each(function() {
-                var subject = this.subject.toString();
-                if (!entities[subject]) {
-                    entities[subject] = {
-                        '@subject': subject,
-                        '@context': service.vie.namespaces.toObj(true),
-                        '@type': []
-                    };
-                }
-                var propertyUri = this.property.toString();
-                var propertyCurie;
-
-                try {
-                    propertyCurie = service.vie.namespaces.curie(propertyUri);
-                    //jQuery.createCurie(propertyUri, {namespaces: service.vie.namespaces.toObj(true)});
-                } catch (e) {
-                    propertyCurie = propertyUri;
-                    // console.warn(propertyUri + " doesn't have a namespace definition in '", service.vie.namespaces.toObj());
-                }
-                entities[subject][propertyCurie] = entities[subject][propertyCurie] || [];
-
-                function getValue(rdfQueryLiteral){
-                    if(typeof rdfQueryLiteral.value === "string"){
-                        if (rdfQueryLiteral.lang){
-                            var literal = {
-                                toString: function(){
-                                    return this["@value"];
-                                },
-                                "@value": rdfQueryLiteral.value.replace(/^"|"$/g, ''),
-                                "@language": rdfQueryLiteral.lang
-                            };
-                            return literal;
-                        }
-                        else
-                            return rdfQueryLiteral.value;
-                        return rdfQueryLiteral.value.toString();
-                    } else if (rdfQueryLiteral.type === "uri"){
-                        return rdfQueryLiteral.toString();
-                    } else {
-                        return rdfQueryLiteral.value;
-                    }
-                }
-                entities[subject][propertyCurie].push(getValue(this.object));
-            });
-
-            _(entities).each(function(ent){
-                ent["@type"] = ent["@type"].concat(ent["rdf:type"]);
-                delete ent["rdf:type"];
-                _(ent).each(function(value, property){
-                    if(value.length === 1){
-                        ent[property] = value[0];
-                    }
-                });
-            });
-
-            var vieEntities = [];
-            jQuery.each(entities, function() {
-                var entityInstance = new service.vie.Entity(this);
-                entityInstance = service.vie.entities.addOrUpdate(entityInstance);
-                vieEntities.push(entityInstance);
-            });
-            return vieEntities;
-        } catch (e) {
-            console.warn("Something went wrong while parsing the returned results!", e);
-            return [];
-        }
-    },
-
-    /*
-    VIE.Util.getPreferredLangForPreferredProperty(entity, preferredFields, preferredLanguages)
-    looks for specific ranking fields and languages. It calculates all possibilities and gives them
-    a score. It returns the value with the best score.
-    */
-    getPreferredLangForPreferredProperty: function(entity, preferredFields, preferredLanguages) {
-      var l, labelArr, lang, p, property, resArr, valueArr, _len, _len2,
-        _this = this;
-      resArr = [];
-      /* Try to find a label in the preferred language
-      */
-      _.each(preferredLanguages, function (lang) {
-        _.each(preferredFields, function (property) {
-          labelArr = null;
-          /* property can be a string e.g. "skos:prefLabel"
-          */
-          if (typeof property === "string" && entity.get(property)) {
-            labelArr = _.flatten([entity.get(property)]);
-            _(labelArr).each(function(label) {
-              /*
-              The score is a natural number with 0 for the
-              best candidate with the first preferred language
-              and first preferred property
-              */
-              var labelLang, score, value;
-              score = p;
-              labelLang = label["@language"];
-              /*
-                                      legacy code for compatibility with uotdated stanbol,
-                                      to be removed after may 2012
-              */
-              if (typeof label === "string" && (label.indexOf("@") === label.length - 3 || label.indexOf("@") === label.length - 5)) {
-                labelLang = label.replace(/(^\"*|\"*@)..(..)?$/g, "");
-              }
-              /* end of legacy code
-              */
-              if (labelLang) {
-                if (labelLang === lang) {
-                  score += l;
-                } else {
-                  score += 20;
-                }
-              } else {
-                score += 10;
-              }
-              value = label.toString();
-              /* legacy code for compatibility with uotdated stanbol, to be removed after may 2012
-              */
-              value = value.replace(/(^\"*|\"*@..$)/g, "");
-              /* end of legacy code
-              */
-              return resArr.push({
-                score: score,
-                value: value
-              });
-            });
-            /*
-            property can be an object like
-            {
-              property: "skos:broader",
-              makeLabel: function(propertyValueArr) { return "..."; }
-            }
-            */
-          } else if (typeof property === "object" && entity.get(property.property)) {
-            valueArr = _.flatten([entity.get(property.property)]);
-            valueArr = _(valueArr).map(function(termUri) {
-              if (termUri.isEntity) {
-                return termUri.getSubject();
-              } else {
-                return termUri;
-              }
-            });
-            resArr.push({
-              score: p,
-              value: property.makeLabel(valueArr)
-            });
-          }
-        });
-      });
-      /*
-              take the result with the best score
-      */
-      resArr = _(resArr).sortBy(function(a) {
-        return a.score;
-      });
-      if(resArr.length) {
-        return resArr[0].value;
-      } else {
-        return "n/a";
-      }
-    },
-
-
-// ### VIE.Util._rdf2EntitiesNoRdfQuery(service, results)
-// This is a **private** method which should
-// only be accessed through ```VIE.Util._rdf2Entities()``` and is a helper method in case there is no
-// rdfQuery loaded (*not recommended*).
-// **Parameters**:
-// *{object}* **service** The service that retrieved the data.
-// *{object}* **results** The data to be transformed.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{[VIE.Entity]}* : An array, containing VIE.Entity instances which have been transformed from the given data.
-    _rdf2EntitiesNoRdfQuery: function (service, results) {
-        var jsonLD = [];
-        _.forEach(results, function(value, key) {
-            var entity = {};
-            entity['@subject'] = '<' + key + '>';
-            _.forEach(value, function(triples, predicate) {
-                predicate = '<' + predicate + '>';
-                _.forEach(triples, function(triple) {
-                    if (triple.type === 'uri') {
-                        triple.value = '<' + triple.value + '>';
-                    }
-
-                    if (entity[predicate] && !_.isArray(entity[predicate])) {
-                        entity[predicate] = [entity[predicate]];
-                    }
-
-                    if (_.isArray(entity[predicate])) {
-                        entity[predicate].push(triple.value);
-                        return;
-                    }
-                    entity[predicate] = triple.value;
-                });
-            });
-            jsonLD.push(entity);
-        });
-        return jsonLD;
-    },
-
-// ### VIE.Util.loadSchemaOrg(vie, SchemaOrg, baseNS)
-// This method is a wrapper around
-// the <a href="http://schema.org/">schema.org</a> ontology. It adds all the
-// given types and properties as ```VIE.Type``` instances to the given VIE instance.
-// If the paramenter **baseNS** is set, the method automatically sets the namespace
-// to the provided one. If it is not set, it will keep the base namespace of VIE untouched.
-// **Parameters**:
-// *{VIE}* **vie** The instance of ```VIE```.
-// *{object}* **SchemaOrg** The data imported from schema.org.
-// *{string|undefined}* **baseNS** If set, this will become the new baseNamespace within the given ```VIE``` instance.
-// **Throws**:
-// *{Error}* If the parameter was not given.
-// **Returns**:
-// *nothing*
-    loadSchemaOrg : function (vie, SchemaOrg, baseNS) {
-
-        if (!SchemaOrg) {
-            throw new Error("Please load the schema.json file.");
-        }
-        vie.types.remove("<http://schema.org/Thing>");
-
-        var baseNSBefore = (baseNS)? baseNS : vie.namespaces.base();
-        vie.namespaces.base(baseNS);
-
-        var datatypeMapping = {
-            'DataType': 'xsd:anyType',
-            'Boolean' : 'xsd:boolean',
-            'Date'    : 'xsd:date',
-            'DateTime': 'xsd:dateTime',
-            'Time'    : 'xsd:time',
-            'Float'   : 'xsd:float',
-            'Integer' : 'xsd:integer',
-            'Number'  : 'xsd:anySimpleType',
-            'Text'    : 'xsd:string',
-            'URL'     : 'xsd:anyURI'
-        };
-
-        var dataTypeHelper = function (ancestors, id) {
-            var type = vie.types.add(id, [{'id' : 'value', 'range' : datatypeMapping[id]}]);
-
-            for (var i = 0; i < ancestors.length; i++) {
-                var supertype = (vie.types.get(ancestors[i]))? vie.types.get(ancestors[i]) :
-                    dataTypeHelper.call(vie, SchemaOrg.datatypes[ancestors[i]].supertypes, ancestors[i]);
-                type.inherit(supertype);
-            }
-            return type;
-        };
-
-        for (var dt in SchemaOrg.datatypes) {
-            if (!vie.types.get(dt)) {
-                var ancestors = SchemaOrg.datatypes[dt].supertypes;
-                dataTypeHelper.call(vie, ancestors, dt);
-            }
-        }
-
-        var metadataHelper = function (definition) {
-            var metadata = {};
-
-            if (definition.label) {
-              metadata.label = definition.label;
-            }
-
-            if (definition.url) {
-              metadata.url = definition.url;
-            }
-
-            if (definition.comment) {
-              metadata.comment = definition.comment;
-            }
-
-            if (definition.metadata) {
-              metadata = _.extend(metadata, definition.metadata);
-            }
-            return metadata;
-        };
-
-        var typeProps = function (id) {
-            var props = [];
-            _.each(SchemaOrg.types[id].specific_properties, function (pId) {
-                var property = SchemaOrg.properties[pId];
-                props.push({
-                    'id'    : property.id,
-                    'range' : property.ranges,
-                    'min'   : property.min,
-                    'max'   : property.max,
-                    'metadata': metadataHelper(property)
-                });
-            });
-            return props;
-        };
-
-        var typeHelper = function (ancestors, id, props, metadata) {
-            var type = vie.types.add(id, props, metadata);
-
-            for (var i = 0; i < ancestors.length; i++) {
-                var supertype = (vie.types.get(ancestors[i]))? vie.types.get(ancestors[i]) :
-                    typeHelper.call(vie, SchemaOrg.types[ancestors[i]].supertypes, ancestors[i], typeProps.call(vie, ancestors[i]), metadataHelper(SchemaOrg.types[ancestors[i]]));
-                type.inherit(supertype);
-            }
-            if (id === "Thing" && !type.isof("owl:Thing")) {
-                type.inherit("owl:Thing");
-            }
-            return type;
-        };
-
-        _.each(SchemaOrg.types, function (typeDef) {
-            if (vie.types.get(typeDef.id)) {
-                return;
-            }
-            var ancestors = typeDef.supertypes;
-            var metadata = metadataHelper(typeDef);
-            typeHelper.call(vie, ancestors, typeDef.id, typeProps.call(vie, typeDef.id), metadata);
-        });
-
-        /* set the namespace to either the old value or the provided baseNS value */
-        vie.namespaces.base(baseNSBefore);
-    },
-
-// ### VIE.Util.getEntityTypeUnion(entity)
-// This generates a entity-specific VIE type that is a subtype of all the
-// types of the entity. This makes it easier to deal with attribute definitions
-// specific to an entity because they're merged to a single list. This custom
-// type is transient, meaning that it won't be automatilly added to the entity
-// or the VIE type registry.
-    getEntityTypeUnion : function(entity) {
-      var vie = entity.vie;
-      return new vie.Type('Union').inherit(entity.get('@type'));
-    },
-
-// ### VIE.Util.getFormSchemaForType(type)
-// This creates a [Backbone Forms](https://github.com/powmedia/backbone-forms)
-// -compatible form schema for any VIE Type.
-    getFormSchemaForType : function(type, allowNested) {
-      var schema = {};
-
-      // Generate a schema
-      _.each(type.attributes.toArray(), function (attribute) {
-        var key = VIE.Util.toCurie(attribute.id, false, attribute.vie.namespaces);
-        schema[key] = VIE.Util.getFormSchemaForAttribute(attribute);
-      });
-
-      // Clean up unknown attribute types
-      _.each(schema, function (field, id) {
-        if (!field.type) {
-          delete schema[id];
-        }
-
-        if (field.type === 'URL') {
-          field.type = 'Text';
-          field.dataType = 'url';
-        }
-
-        if (field.type === 'List' && !field.listType) {
-          delete schema[id];
-        }
-
-        if (!allowNested) {
-          if (field.type === 'NestedModel' || field.listType === 'NestedModel') {
-            delete schema[id];
-          }
-        }
-      });
-
-      return schema;
-    },
-
-/// ### VIE.Util.getFormSchemaForAttribute(attribute)
-    getFormSchemaForAttribute : function(attribute) {
-      var primaryType = attribute.range[0];
-      var schema = {};
-
-      var getWidgetForType = function (type) {
-        switch (type) {
-          case 'xsd:anySimpleType':
-          case 'xsd:float':
-          case 'xsd:integer':
-            return 'Number';
-          case 'xsd:string':
-            return 'Text';
-          case 'xsd:date':
-            return 'Date';
-          case 'xsd:dateTime':
-            return 'DateTime';
-          case 'xsd:boolean':
-            return 'Checkbox';
-          case 'xsd:anyURI':
-            return 'URL';
-          default:
-            var typeType = attribute.vie.types.get(type);
-            if (!typeType) {
-              return null;
-            }
-            if (typeType.attributes.get('value')) {
-              // Convert to proper xsd type
-              return getWidgetForType(typeType.attributes.get('value').range[0]);
-            }
-            return 'NestedModel';
-        }
-      };
-
-      // TODO: Generate a nicer label
-      schema.title = VIE.Util.toCurie(attribute.id, false, attribute.vie.namespaces);
-
-      // TODO: Handle attributes linking to other VIE entities
-
-      if (attribute.min > 0) {
-        schema.validators = ['required'];
-      }
-
-      if (attribute.max > 1) {
-        schema.type = 'List';
-        schema.listType = getWidgetForType(primaryType);
-        if (schema.listType === 'NestedModel') {
-          schema.nestedModelType = primaryType;
-        }
-        return schema;
-      }
-
-      schema.type = getWidgetForType(primaryType);
-      if (schema.type === 'NestedModel') {
-        schema.nestedModelType = primaryType;
-      }
-      return schema;
-    },
-
-// ### VIE.Util.getFormSchema(entity)
-// This creates a [Backbone Forms](https://github.com/powmedia/backbone-forms)
-// -compatible form schema for any VIE Entity. The form schema creation
-// utilizes type information attached to the entity.
-// **Parameters**:
-// *{```Entity```}* **entity** An instance of VIE ```Entity```.
-// **Throws**:
-// *nothing*..
-// **Returns**:
-// *{object}* a JavaScript object representation of the form schema
-    getFormSchema : function(entity) {
-      if (!entity || !entity.isEntity) {
-        return {};
-      }
-
-      var unionType = VIE.Util.getEntityTypeUnion(entity);
-      var schema = VIE.Util.getFormSchemaForType(unionType, true);
-
-      // Handle nested models
-      _.each(schema, function (property, id) {
-        if (property.type !== 'NestedModel' && property.listType !== 'NestedModel') {
-          return;
-        }
-        schema[id].model = entity.vie.getTypedEntityClass(property.nestedModelType);
-      });
-
-      return schema;
-    },
-
-// ### VIE.Util.xsdDateTime(date)
-// This transforms a ```Date``` instance into an xsd:DateTime format.
-// **Parameters**:
-// *{```Date```}* **date** An instance of a javascript ```Date```.
-// **Throws**:
-// *nothing*..
-// **Returns**:
-// *{string}* A string representation of the dateTime in the xsd:dateTime format.
-    xsdDateTime : function(date) {
-        function pad(n) {
-            var s = n.toString();
-            return s.length < 2 ? '0'+s : s;
-        }
-
-        var yyyy = date.getFullYear();
-        var mm1  = pad(date.getMonth()+1);
-        var dd   = pad(date.getDate());
-        var hh   = pad(date.getHours());
-        var mm2  = pad(date.getMinutes());
-        var ss   = pad(date.getSeconds());
-
-        return yyyy +'-' +mm1 +'-' +dd +'T' +hh +':' +mm2 +':' +ss;
-    },
-
-// ### VIE.Util.extractLanguageString(entity, attrs, langs)
-// This method extracts a literal string from an entity, searching through the given attributes and languages.
-// **Parameters**:
-// *{```VIE.Entity```}* **entity** An instance of a VIE.Entity.
-// *{```array|string```}* **attrs** Either a string or an array of possible attributes.
-// *{```array|string```}* **langs** Either a string or an array of possible languages.
-// **Throws**:
-// *nothing*..
-// **Returns**:
-// *{string|undefined}* The string that was found at the attribute with the wanted language, undefined if nothing could be found.
-// **Example usage**:
-//
-//          var attrs = ["name", "rdfs:label"];
-//          var langs = ["en", "de"];
-//          VIE.Util.extractLanguageString(someEntity, attrs, langs); // "Barack Obama";
-    extractLanguageString : function(entity, attrs, langs) {
-        var p, attr, name, i, n;
-        if (entity && typeof entity !== "string") {
-            attrs = (_.isArray(attrs))? attrs : [ attrs ];
-            langs = (_.isArray(langs))? langs : [ langs ];
-            for (p = 0; p < attrs.length; p++) {
-                for (var l = 0; l < langs.length; l++) {
-                    var lang = langs[l];
-                    attr = attrs[p];
-                    if (entity.has(attr)) {
-                        name = entity.get(attr);
-                        name = (_.isArray(name))? name : [ name ];
-                        for (i = 0; i < name.length; i++) {
-                            n = name[i];
-                            if (n.isEntity) {
-                                n = VIE.Util.extractLanguageString(n, attrs, lang);
-                            } else if (typeof n === "string") {
-                                n = n;
-                            } else {
-                                n = "";
-                            }
-                            if (n && n.indexOf('@' + lang) > -1) {
-                                return n.replace(/"/g, "").replace(/@[a-z]+/, '').trim();
-                            }
-                        }
-                    }
-                }
-            }
-            /* let's do this again in case we haven't found a name but are dealing with
-            broken data where no language is given */
-            for (p = 0; p < attrs.length; p++) {
-                attr = attrs[p];
-                if (entity.has(attr)) {
-                    name = entity.get(attr);
-                    name = (_.isArray(name))? name : [ name ];
-                    for (i = 0; i < name.length; i++) {
-                        n = name[i];
-                        if (n.isEntity) {
-                            n = VIE.Util.extractLanguageString(n, attrs, []);
-                        }
-                        if (n && (typeof n === "string") && n.indexOf('@') === -1) {
-                            return n.replace(/"/g, "").replace(/@[a-z]+/, '').trim();
-                        }
-                    }
-                }
-            }
-        }
-        return undefined;
-    },
-
-// ### VIE.Util.transformationRules(service)
-// This returns a default set of rdfQuery rules that transform semantic data into the
-// VIE entity types.
-// **Parameters**:
-// *{object}* **service** An instance of a vie.service.
-// **Throws**:
-// *nothing*..
-// **Returns**:
-// *{array}* An array of rules with 'left' and 'right' side.
-    transformationRules : function (service) {
-        var res = [
-            // rule(s) to transform a dbpedia:Person into a VIE:Person
-             {
-                'left' : [
-                    '?subject a dbpedia:Person',
-                    '?subject rdfs:label ?label'
-                 ],
-                 'right': function(ns){
-                     return function(){
-                         return [
-                             jQuery.rdf.triple(this.subject.toString(),
-                                 'a',
-                                 '<' + ns.base() + 'Person>', {
-                                     namespaces: ns.toObj()
-                                 }),
-                             jQuery.rdf.triple(this.subject.toString(),
-                                 '<' + ns.base() + 'name>',
-                                 this.label, {
-                                     namespaces: ns.toObj()
-                                 })
-                             ];
-                     };
-                 }(service.vie.namespaces)
-             },
-             // rule(s) to transform a foaf:Person into a VIE:Person
-             {
-             'left' : [
-                     '?subject a foaf:Person',
-                     '?subject rdfs:label ?label'
-                  ],
-                  'right': function(ns){
-                      return function(){
-                          return [
-                              jQuery.rdf.triple(this.subject.toString(),
-                                  'a',
-                                  '<' + ns.base() + 'Person>', {
-                                      namespaces: ns.toObj()
-                                  }),
-                              jQuery.rdf.triple(this.subject.toString(),
-                                  '<' + ns.base() + 'name>',
-                                  this.label, {
-                                      namespaces: ns.toObj()
-                                  })
-                              ];
-                      };
-                  }(service.vie.namespaces)
-              },
-             // rule(s) to transform a dbpedia:Place into a VIE:Place
-             {
-                 'left' : [
-                     '?subject a dbpedia:Place',
-                     '?subject rdfs:label ?label'
-                  ],
-                  'right': function(ns) {
-                      return function() {
-                          return [
-                          jQuery.rdf.triple(this.subject.toString(),
-                              'a',
-                              '<' + ns.base() + 'Place>', {
-                                  namespaces: ns.toObj()
-                              }),
-                          jQuery.rdf.triple(this.subject.toString(),
-                                  '<' + ns.base() + 'name>',
-                              this.label.toString(), {
-                                  namespaces: ns.toObj()
-                              })
-                          ];
-                      };
-                  }(service.vie.namespaces)
-              },
-             // rule(s) to transform a dbpedia:City into a VIE:City
-              {
-                 'left' : [
-                     '?subject a dbpedia:City',
-                     '?subject rdfs:label ?label',
-                     '?subject dbpedia:abstract ?abs',
-                     '?subject dbpedia:country ?country'
-                  ],
-                  'right': function(ns) {
-                      return function() {
-                          return [
-                          jQuery.rdf.triple(this.subject.toString(),
-                              'a',
-                              '<' + ns.base() + 'City>', {
-                                  namespaces: ns.toObj()
-                              }),
-                          jQuery.rdf.triple(this.subject.toString(),
-                                  '<' + ns.base() + 'name>',
-                              this.label.toString(), {
-                                  namespaces: ns.toObj()
-                              }),
-                          jQuery.rdf.triple(this.subject.toString(),
-                                  '<' + ns.base() + 'description>',
-                              this.abs.toString(), {
-                                  namespaces: ns.toObj()
-                              }),
-                          jQuery.rdf.triple(this.subject.toString(),
-                                  '<' + ns.base() + 'containedIn>',
-                              this.country.toString(), {
-                                  namespaces: ns.toObj()
-                              })
-                          ];
-                      };
-                  }(service.vie.namespaces)
-              }
-        ];
-        return res;
-    },
-
-    getAdditionalRules : function (service) {
-
-        var mapping = {
-            Work : "CreativeWork",
-            Film : "Movie",
-            TelevisionEpisode : "TVEpisode",
-            TelevisionShow : "TVSeries", // not listed as equivalent class on dbpedia.org
-            Website : "WebPage",
-            Painting : "Painting",
-            Sculpture : "Sculpture",
-
-            Event : "Event",
-            SportsEvent : "SportsEvent",
-            MusicFestival : "Festival",
-            FilmFestival : "Festival",
-
-            Place : "Place",
-            Continent : "Continent",
-            Country : "Country",
-            City : "City",
-            Airport : "Airport",
-            Station : "TrainStation", // not listed as equivalent class on dbpedia.org
-            Hospital : "GovernmentBuilding",
-            Mountain : "Mountain",
-            BodyOfWater : "BodyOfWater",
-
-            Company : "Organization",
-            Person : "Person"
-        };
-
-        var additionalRules = [];
-        _.each(mapping, function (map, key) {
-            var tripple = {
-                'left' : [ '?subject a dbpedia:' + key, '?subject rdfs:label ?label' ],
-                'right' : function(ns) {
-                    return function() {
-                        return [ jQuery.rdf.triple(this.subject.toString(), 'a', '<' + ns.base() + map + '>', {
-                            namespaces : ns.toObj()
-                        }), jQuery.rdf.triple(this.subject.toString(), '<' + ns.base() + 'name>', this.label.toString(), {
-                            namespaces : ns.toObj()
-                        }) ];
-                    };
-                }(service.vie.namespaces)
-            };
-            additionalRules.push(tripple);
-        });
-        return additionalRules;
-    }
-};
-//     VIE - Vienna IKS Editables
-//     (c) 2011 Henri Bergius, IKS Consortium
-//     (c) 2011 Sebastian Germesin, IKS Consortium
-//     (c) 2011 Szaby Grünwald, IKS Consortium
-//     VIE may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://viejs.org/
-
-// ## VIE Entities
-//
-// In VIE there are two low-level model types for storing data.
-// **Collections** and **Entities**. Considering `var v = new VIE();` a VIE instance,
-// `v.entities` is a Collection with `VIE Entity` objects in it.
-// VIE internally uses JSON-LD to store entities.
-//
-// Each Entity has a few special attributes starting with an `@`. VIE has an API
-// for correctly using these attributes, so in order to stay compatible with later
-// versions of the library, possibly using a later version of JSON-LD, use the API
-// to interact with your entities.
-//
-// * `@subject` stands for the identifier of the entity. Use `e.getSubject()`
-// * `@type` stores the explicit entity types. VIE internally handles Type hierarchy,
-// which basically enables to define subtypes and supertypes. Every entity has
-// the type 'owl:Thing'. Read more about Types in <a href="Type.html">VIE.Type</a>.
-// * `@context` stores namespace definitions used in the entity. Read more about
-// Namespaces in <a href="Namespace.html">VIE Namespaces</a>.
-VIE.prototype.Entity = function(attrs, opts) {
-
-    attrs = (attrs)? attrs : {};
-    opts = (opts)? opts : {};
-
-    var self = this;
-
-    if (attrs['@type'] !== undefined) {
-        attrs['@type'] = (_.isArray(attrs['@type']))? attrs['@type'] : [ attrs['@type'] ];
-        attrs['@type'] = _.map(attrs['@type'], function(val){
-            if (!self.vie.types.get(val)) {
-                //if there is no such type -> add it and let it inherit from "owl:Thing"
-                self.vie.types.add(val).inherit("owl:Thing");
-            }
-            return self.vie.types.get(val).id;
-        });
-        attrs['@type'] = (attrs['@type'].length === 1)? attrs['@type'][0] : attrs['@type'];
-    } else {
-        // provide "owl:Thing" as the default type if none was given
-        attrs['@type'] = self.vie.types.get("owl:Thing").id;
-    }
-
-    //the following provides full seamless namespace support
-    //for attributes. It should not matter, if you
-    //query for `model.get('name')` or `model.get('foaf:name')`
-    //or even `model.get('http://xmlns.com/foaf/0.1/name');`
-    //However, if we just overwrite `set()` and `get()`, this
-    //raises a lot of side effects, so we need to expand
-    //the attributes before we create the model.
-    _.each (attrs, function (value, key) {
-        var newKey = VIE.Util.mapAttributeNS(key, this.namespaces);
-        if (key !== newKey) {
-            delete attrs[key];
-            attrs[newKey] = value;
-        }
-    }, self.vie);
-
-    var Model = Backbone.Model.extend({
-        idAttribute: '@subject',
-
-        initialize: function(attributes, options) {
-            if (attributes['@subject']) {
-                this.id = this['@subject'] = this.toReference(attributes['@subject']);
-            } else {
-                this.id = this['@subject'] = attributes['@subject'] = this.cid.replace('c', '_:bnode');
-            }
-            return this;
-        },
-
-        schema: function() {
-          return VIE.Util.getFormSchema(this);
-        },
-
-        // ### Getter, Has, Setter
-        // #### `.get(attr)`
-        // To be able to communicate to a VIE Entity you can use a simple get(property)
-        // command as in `entity.get('rdfs:label')` which will give you one or more literals.
-        // If the property points to a collection, its entities can be browsed further.
-        get: function (attr) {
-            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);
-            var value = Backbone.Model.prototype.get.call(this, attr);
-            value = (_.isArray(value))? value : [ value ];
-
-            value = _.map(value, function(v) {
-                if (v !== undefined && attr === '@type' && self.vie.types.get(v)) {
-                    return self.vie.types.get(v);
-                } else if (v !== undefined && self.vie.entities.get(v)) {
-                    return self.vie.entities.get(v);
-                } else {
-                    return v;
-                }
-            }, this);
-            if(value.length === 0) {
-                return undefined;
-            }
-            // if there is only one element, just return that one
-            value = (value.length === 1)? value[0] : value;
-            return value;
-        },
-
-        // #### `.has(attr)`
-        // Sometimes you'd like to determine if a specific attribute is set
-        // in an entity. For this reason you can call for example `person.has('friend')`
-        // to determine if a person entity has friends.
-        has: function(attr) {
-            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);
-            return Backbone.Model.prototype.has.call(this, attr);
-        },
-
-        // #### `.set(attrName, value, opts)`,
-        // The `options` parameter always refers to a `Backbone.Model.set` `options` object.
-        //
-        // **`.set(attributes, options)`** is the most universal way of calling the
-        // `.set` method. In this case the `attributes` object is a map of all
-        // attributes to be changed.
-        set : function(attrs, options, opts) {
-            if (!attrs) {
-                return this;
-            }
-
-            if (attrs['@subject']) {
-                attrs['@subject'] = this.toReference(attrs['@subject']);
-            }
-
-            // Use **`.set(attrName, value, options)`** for setting or changing exactly one
-            // entity attribute.
-            if (typeof attrs === "string") {
-                var obj = {};
-                obj[attrs] = options;
-                return this.set(obj, opts);
-            }
-            // **`.set(entity)`**: In case you'd pass a VIE entity,
-            // the passed entities attributes are being set for the entity.
-            if (attrs.attributes) {
-                attrs = attrs.attributes;
-            }
-            var self = this;
-            var coll;
-            // resolve shortened URIs like rdfs:label..
-            _.each (attrs, function (value, key) {
-                var newKey = VIE.Util.mapAttributeNS(key, self.vie.namespaces);
-                if (key !== newKey) {
-                    delete attrs[key];
-                    attrs[newKey] = value;
-                }
-            }, this);
-            // Finally iterate through the *attributes* to be set and prepare
-            // them for the Backbone.Model.set method.
-            _.each (attrs, function (value, key) {
-               if (!value) { return; }
-               if (key.indexOf('@') === -1) {
-                   if (value.isCollection) {
-                       // ignore
-                       value.each(function (child) {
-                           self.vie.entities.addOrUpdate(child);
-                       });
-                   } else if (value.isEntity) {
-                       self.vie.entities.addOrUpdate(value);
-                       coll = new self.vie.Collection(value, {
-                         vie: self.vie,
-                         predicate: key
-                       });
-                       attrs[key] = coll;
-                   } else if (_.isArray(value)) {
-                       if (this.attributes[key] && this.attributes[key].isCollection) {
-                         var newEntities = this.attributes[key].addOrUpdate(value);
-                         attrs[key] = this.attributes[key];
-                         attrs[key].reset(newEntities);
-                       }
-                   } else if (value["@value"]) {
-                       // The value is a literal object, ignore
-                   } else if (_.isObject(value) && !_.isDate(value)) {
-                       // The value is another VIE Entity
-                       var child = new self.vie.Entity(value, options);
-                       // which is being stored in `v.entities`
-                       self.vie.entities.addOrUpdate(child);
-                       // and set as VIE Collection attribute on the original entity
-                       coll = new self.vie.Collection(value, {
-                         vie: self.vie,
-                         predicate: key
-                       });
-                       attrs[key] = coll;
-                   } else {
-                       // ignore
-                   }
-               }
-            }, this);
-            var ret = Backbone.Model.prototype.set.call(this, attrs, options);
-            if (options && options.ignoreChanges) {
-                this.changed = {};
-                this._previousAttributes = _.clone(this.attributes);
-            }
-            return ret;
-        },
-
-        // **`.unset(attr, opts)` ** removes an attribute from the entity.
-        unset: function (attr, opts) {
-            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);
-            return Backbone.Model.prototype.unset.call(this, attr, opts);
-        },
-
-        // Validation based on type rules.
-        //
-        // There are two ways to skip validation for entity operations:
-        //
-        // * `options.silent = true`
-        // * `options.validate = false`
-        validate: function (attrs, opts) {
-            if (opts && opts.validate === false) {
-                return;
-            }
-            var types = this.get('@type');
-            if (_.isArray(types)) {
-                var results = [];
-                _.each(types, function (type) {
-                    var res = this.validateByType(type, attrs, opts);
-                    if (res) {
-                        results.push(res);
-                    }
-                }, this);
-                if (_.isEmpty(results)) {
-                  return;
-                }
-                return _.flatten(results);
-            }
-
-            return this.validateByType(types, attrs, opts);
-        },
-
-        validateByType: function (type, attrs, opts) {
-            var messages = {
-              max: '<%= property %> cannot contain more than <%= num %> items',
-              min: '<%= property %> must contain at least <%= num %> items',
-              required: '<%= property %> is required'
-            };
-
-            if (!type.attributes) {
-                return;
-            }
-
-            var toError = function (definition, constraint, messageValues) {
-                return {
-                    property: definition.id,
-                    constraint: constraint,
-                    message: _.template(messages[constraint], _.extend({
-                        property: definition.id
-                    }, messageValues))
-                };
-            };
-
-            var checkMin = function (definition, attrs) {
-                if (!attrs[definition.id] || _.isEmpty(attrs[definition.id])) {
-                    return toError(definition, 'required', {});
-                }
-            };
-
-            // Check the number of items in attr against max
-            var checkMax = function (definition, attrs) {
-                if (!attrs[definition.id]) {
-                    return;
-                }
-
-                if (!attrs[definition.id].isCollection && !_.isArray(attrs[definition.id])) {
-                    return;
-                }
-
-                if (attrs[definition.id].length > definition.max) {
-                    return toError(definition, 'max', {
-                        num: definition.max
-                    });
-                }
-            };
-
-            var results = [];
-            _.each(type.attributes.list(), function (definition) {
-                var res;
-                if (definition.max && definition.max != -1) {
-                    res = checkMax(definition, attrs);
-                    if (res) {
-                        results.push(res);
-                    }
-                }
-
-                if (definition.min && definition.min > 0) {
-                    res = checkMin(definition, attrs);
-                    if (res) {
-                        results.push(res);
-                    }
-                }
-            });
-
-            if (_.isEmpty(results)) {
-              return;
-            }
-            return results;
-        },
-
-        isNew: function() {
-            if (this.getSubjectUri().substr(0, 7) === '_:bnode') {
-                return true;
-            }
-            return false;
-        },
-
-        hasChanged: function(attr) {
-            if (this.markedChanged) {
-                return true;
-            }
-
-            return Backbone.Model.prototype.hasChanged.call(this, attr);
-        },
-
-        // Force hasChanged to return true
-        forceChanged: function(changed) {
-            this.markedChanged = changed ? true : false;
-        },
-
-        // **`getSubject()`** is the getter for the entity identifier.
-        getSubject: function(){
-            if (typeof this.id === "undefined") {
-                this.id = this.attributes[this.idAttribute];
-            }
-            if (typeof this.id === 'string') {
-                if (this.id.substr(0, 7) === 'http://' || this.id.substr(0, 4) === 'urn:') {
-                    return this.toReference(this.id);
-                }
-                return this.id;
-            }
-            return this.cid.replace('c', '_:bnode');
-        },
-
-        // TODO describe
-        getSubjectUri: function(){
-            return this.fromReference(this.getSubject());
-        },
-
-        isReference: function(uri){
-            var matcher = new RegExp("^\\<([^\\>]*)\\>$");
-            if (matcher.exec(uri)) {
-                return true;
-            }
-            return false;
-        },
-
-        toReference: function(uri){
-            if (_.isArray(uri)) {
-              var self = this;
-              return _.map(uri, function(part) {
-                 return self.toReference(part);
-              });
-            }
-            var ns = this.vie.namespaces;
-            var ret = uri;
-            if (uri.substring(0, 2) === "_:") {
-                ret = uri;
-            }
-            else if (ns.isCurie(uri)) {
-                ret = ns.uri(uri);
-                if (ret === "<" + ns.base() + uri + ">") {
-                    /* no base namespace extension with IDs */
-                    ret = '<' + uri + '>';
-                }
-            } else if (!ns.isUri(uri)) {
-                ret = '<' + uri + '>';
-            }
-            return ret;
-        },
-
-        fromReference: function(uri){
-            var ns = this.vie.namespaces;
-            if (!ns.isUri(uri)) {
-                return uri;
-            }
-            return uri.substring(1, uri.length - 1);
-        },
-
-        as: function(encoding){
-            if (encoding === "JSON") {
-                return this.toJSON();
-            }
-            if (encoding === "JSONLD") {
-                return this.toJSONLD();
-            }
-            throw new Error("Unknown encoding " + encoding);
-        },
-
-        toJSONLD: function(){
-            var instanceLD = {};
-            var instance = this;
-            _.each(instance.attributes, function(value, name){
-                var entityValue = value; //instance.get(name);
-
-                if (value instanceof instance.vie.Collection) {
-                    entityValue = value.map(function(instance) {
-                        return instance.getSubject();
-                    });
-                }
-
-                // TODO: Handle collections separately
-                instanceLD[name] = entityValue;
-            });
-
-            instanceLD['@subject'] = instance.getSubject();
-
-            return instanceLD;
-        },
-
-        // **`.setOrAdd(arg1, arg2)`** similar to `.set(..)`, `.setOrAdd(..)` can
-        // be used for setting one or more attributes of an entity, but in
-        // this case it's a collection of values, not just one. That means, if the
-        // entity already has the attribute set, make the value to a VIE Collection
-        // and use the collection as value. The collection can contain entities
-        // or literals, but not both at the same time.
-        setOrAdd: function (arg1, arg2, option) {
-            var entity = this;
-            if (typeof arg1 === "string" && arg2) {
-                // calling entity.setOrAdd("rdfs:type", "example:Musician")
-                entity._setOrAddOne(arg1, arg2, option);
-            }
-            else
-                if (typeof arg1 === "object") {
-                    // calling entity.setOrAdd({"rdfs:type": "example:Musician", ...})
-                    _(arg1).each(function(val, key){
-                        entity._setOrAddOne(key, val, arg2);
-                    });
-                }
-            return this;
-        },
-
-
-        /* attr is always of type string */
-        /* value can be of type: string,int,double,object,VIE.Entity,VIE.Collection */
-       /*  val can be of type: undefined,string,int,double,array,VIE.Collection */
-
-        /* depending on the type of value and the type of val, different actions need to be made */
-        _setOrAddOne: function (attr, value, options) {
-            if (!attr || !value)
-                return;
-            options = (options)? options : {};
-            var v;
-
-            attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces);
-
-            if (_.isArray(value)) {
-                for (v = 0; v < value.length; v++) {
-                    this._setOrAddOne(attr, value[v], options);
-                }
-                return;
-            }
-
-            if (attr === "@type" && value instanceof self.vie.Type) {
-                value = value.id;
-            }
-
-            var obj = {};
-            var existing = Backbone.Model.prototype.get.call(this, attr);
-
-            if (!existing) {
-                obj[attr] = value;
-                this.set(obj, options);
-            } else if (existing.isCollection) {
-                if (value.isCollection) {
-                    value.each(function (model) {
-                        existing.add(model);
-                    });
-                } else if (value.isEntity) {
-                    existing.add(value);
-                } else if (typeof value === "object") {
-                    value = new this.vie.Entity(value);
-                    existing.add(value);
-                } else {
-                    throw new Error("you cannot add a literal to a collection of entities!");
-                }
-                this.trigger('change:' + attr, this, value, {});
-                this.change({});
-            } else if (_.isArray(existing)) {
-                if (value.isCollection) {
-                    for (v = 0; v < value.size(); v++) {
-                        this._setOrAddOne(attr, value.at(v).getSubject(), options);
-                    }
-                } else if (value.isEntity) {
-                    this._setOrAddOne(attr, value.getSubject(), options);
-                } else if (typeof value === "object") {
-                    value = new this.vie.Entity(value);
-                    this._setOrAddOne(attr, value, options);
-                } else {
-                    /* yes, we (have to) allow multiple equal values */
-                    existing.push(value);
-                    obj[attr] = existing;
-                    this.set(obj);
-                }
-            } else {
-                var arr = [ existing ];
-                arr.push(value);
-                obj[attr] = arr;
-                return this.set(obj, options);
-            }
-        },
-
-        // **`.hasType(type)`** determines if the entity has the explicit `type` set.
-        hasType: function(type){
-            type = self.vie.types.get(type);
-            return this.hasPropertyValue("@type", type);
-        },
-
-        // TODO describe
-        hasPropertyValue: function(property, value) {
-            var t = this.get(property);
-            if (!(value instanceof Object)) {
-                value = self.vie.entities.get(value);
-            }
-            if (t instanceof Array) {
-                return t.indexOf(value) !== -1;
-            }
-            else {
-                return t === value;
-            }
-        },
-
-        // **`.isof(type)`** determines if the entity is of `type` by explicit or implicit
-        // declaration. E.g. if Employee is a subtype of Person and e Entity has
-        // explicitly set type Employee, e.isof(Person) will evaluate to true.
-        isof: function (type) {
-            var types = this.get('@type');
-
-            if (types === undefined) {
-                return false;
-            }
-            types = (_.isArray(types))? types : [ types ];
-
-            type = (self.vie.types.get(type))? self.vie.types.get(type) : new self.vie.Type(type);
-            for (var t = 0; t < types.length; t++) {
-                if (self.vie.types.get(types[t])) {
-                    if (self.vie.types.get(types[t]).isof(type)) {
-                        return true;
-                    }
-                } else {
-                    var typeTmp = new self.vie.Type(types[t]);
-                    if (typeTmp.id === type.id) {
-                        return true;
-                    }
-                }
-            }
-            return false;
-        },
-        // TODO describe
-        addTo : function (collection, update) {
-            var self = this;
-            if (collection instanceof self.vie.Collection) {
-                if (update) {
-                    collection.addOrUpdate(self);
-                } else {
-                    collection.add(self);
-                }
-                return this;
-            }
-            throw new Error("Please provide a proper collection of type VIE.Collection as argument!");
-        },
-
-        isEntity: true,
-
-        vie: self.vie
-    });
-
-    return new Model(attrs, opts);
-};
-//     VIE - Vienna IKS Editables
-//     (c) 2011 Henri Bergius, IKS Consortium
-//     (c) 2011 Sebastian Germesin, IKS Consortium
-//     (c) 2011 Szaby Grünwald, IKS Consortium
-//     VIE may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://viejs.org/
-VIE.prototype.Collection = Backbone.Collection.extend({
-    model: VIE.prototype.Entity,
-
-    initialize: function (models, options) {
-      if (!options || !options.vie) {
-        throw new Error('Each collection needs a VIE reference');
-      }
-      this.vie = options.vie;
-      this.predicate = options.predicate;
-    },
-
-    canAdd: function (type) {
-      return true;
-    },
-
-    get: function(id) {
-        if (id === null) {
-            return null;
-        }
-
-        id = (id.getSubject)? id.getSubject() : id;
-        if (typeof id === "string" && id.indexOf("_:") === 0) {
-            if (id.indexOf("bnode") === 2) {
-                //bnode!
-                id = id.replace("_:bnode", 'c');
-                return this._byCid[id];
-            } else {
-                return this._byId["<" + id + ">"];
-            }
-        } else {
-            id = this.toReference(id);
-            return this._byId[id];
-        }
-    },
-
-    addOrUpdate: function(model, options) {
-        options = options || {};
-
-        var collection = this;
-        var existing;
-        if (_.isArray(model)) {
-            var entities = [];
-            _.each(model, function(item) {
-                entities.push(collection.addOrUpdate(item, options));
-            });
-            return entities;
-        }
-
-        if (model === undefined) {
-            throw new Error("No model given");
-        }
-
-        if (_.isString(model)) {
-          model = {
-            '@subject': model,
-            id: model
-          };
-        }
-
-        if (!model.isEntity) {
-            model = new this.model(model);
-        }
-
-        if (model.id && this.get(model.id)) {
-            existing = this.get(model.id);
-        }
-        if (this.getByCid(model.cid)) {
-            existing = this.getByCid(model.cid);
-        }
-        if (existing) {
-            var newAttribs = {};
-            _.each(model.attributes, function(value, attribute) {
-                if (!existing.has(attribute)) {
-                    newAttribs[attribute] = value;
-                    return true;
-                }
-
-                if (attribute === '@subject') {
-                    if (model.isNew() && !existing.isNew()) {
-                        // Save order issue, skip
-                        return true;
-                    }
-                }
-
-                if (existing.get(attribute) === value) {
-                    return true;
-                }
-                //merge existing attribute values with new ones!
-                //not just overwrite 'em!!
-                var oldVals = existing.attributes[attribute];
-                var newVals = value;
-                if (oldVals instanceof collection.vie.Collection) {
-                    // TODO: Merge collections
-                    return true;
-                }
-                if (options.overrideAttributes) {
-                   newAttribs[attribute] = value;
-                   return true;
-                }
-                if (attribute === '@context') {
-                    newAttribs[attribute] = jQuery.extend(true, {}, oldVals, newVals);
-                } else {
-                    oldVals = (jQuery.isArray(oldVals))? oldVals : [ oldVals ];
-                    newVals = (jQuery.isArray(newVals))? newVals : [ newVals ];
-                    newAttribs[attribute] = _.uniq(oldVals.concat(newVals));
-                    newAttribs[attribute] = (newAttribs[attribute].length === 1)? newAttribs[attribute][0] : newAttribs[attribute];
-                }
-            });
-
-            if (!_.isEmpty(newAttribs)) {
-                existing.set(newAttribs, options.updateOptions);
-            }
-            return existing;
-        }
-        this.add(model, options.addOptions);
-        return model;
-    },
-
-    isReference: function(uri){
-        var matcher = new RegExp("^\\<([^\\>]*)\\>$");
-        if (matcher.exec(uri)) {
-            return true;
-        }
-        return false;
-    },
-
-    toReference: function(uri){
-        if (this.isReference(uri)) {
-            return uri;
-        }
-        return '<' + uri + '>';
-    },
-
-    fromReference: function(uri){
-        if (!this.isReference(uri)) {
-            return uri;
-        }
-        return uri.substring(1, uri.length - 1);
-    },
-
-    isCollection: true
-});
-//     VIE - Vienna IKS Editables
-//     (c) 2011 Henri Bergius, IKS Consortium
-//     (c) 2011 Sebastian Germesin, IKS Consortium
-//     (c) 2011 Szaby Grünwald, IKS Consortium
-//     VIE may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://viejs.org/
-//
-
-// ## VIE.Types
-// Within VIE, we provide special capabilities of handling types of entites. This helps
-// for example to query easily for certain entities (e.g., you only need to query for *Person*s
-// and not for all subtypes).
-if (VIE.prototype.Type) {
-    throw new Error("ERROR: VIE.Type is already defined. Please check your installation!");
-}
-if (VIE.prototype.Types) {
-    throw new Error("ERROR: VIE.Types is already defined. Please check your installation!");
-}
-
-// ### VIE.Type(id, attrs, metadata)
-// This is the constructor of a VIE.Type.
-// **Parameters**:
-// *{string}* **id** The id of the type.
-// *{string|array|VIE.Attribute}* **attrs** A string, proper ```VIE.Attribute``` or an array of these which
-// *{object}* **metadata** Possible metadata about the type
-// are the possible attributes of the type
-// **Throws**:
-// *{Error}* if one of the given paramenters is missing.
-// **Returns**:
-// *{VIE.Type}* : A **new** VIE.Type object.
-// **Example usage**:
-//
-//     var person = new vie.Type("Person", ["name", "knows"]);
-VIE.prototype.Type = function (id, attrs, metadata) {
-    if (id === undefined || typeof id !== 'string') {
-        throw "The type constructor needs an 'id' of type string! E.g., 'Person'";
-    }
-
-// ### id
-// This field stores the id of the type's instance.
-// **Parameters**:
-// nothing
-// **Throws**:
-// nothing
-// **Returns**:
-// *{string}* : The id of the type as a URI.
-// **Example usage**:
-//
-//     console.log(person.id);
-//      // --> "<http://viejs.org/ns/Person>"
-    this.id = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id);
-
-    /* checks whether such a type is already defined. */
-    if (this.vie.types.get(this.id)) {
-        throw new Error("The type " + this.id + " is already defined!");
-    }
-
-// ### supertypes
-// This field stores all parent types of the type's instance. This
-// is set if the current type inherits from another type.
-// **Parameters**:
-// nothing
-// **Throws**:
-// nothing
-// **Returns**:
-// *{VIE.Types}* : The supertypes (parents) of the type.
-// **Example usage**:
-//
-//     console.log(person.supertypes);
-    this.supertypes = new this.vie.Types();
-
-// ### subtypes
-// This field stores all children types of the type's instance. This
-// will be set if another type inherits from the current type.
-// **Parameters**:
-// nothing
-// **Throws**:
-// nothing
-// **Returns**:
-// *{VIE.Types}* : The subtypes (parents) of the type.
-// **Example usage**:
-//
-//     console.log(person.subtypes);
-    this.subtypes = new this.vie.Types();
-
-// ### attributes
-// This field stores all attributes of the type's instance as
-// a proper ```VIE.Attributes``` class. (see also <a href="Attribute.html">VIE.Attributes</a>)
-// **Parameters**:
-// nothing
-// **Throws**:
-// nothing
-// **Returns**:
-// *{VIE.Attributes}* : The attributes of the type.
-// **Example usage**:
-//
-//     console.log(person.attributes);
-    this.attributes = new this.vie.Attributes(this, (attrs)? attrs : []);
-
-// ### metadata
-// This field stores possible additional information about the type, like
-// a human-readable label.
-    this.metadata = metadata ? metadata : {};
-
-// ### isof(type)
-// This method checks whether the current type is a child of the given type.
-// **Parameters**:
-// *{string|VIE.Type}* **type** The type (or the id of that type) to be checked.
-// **Throws**:
-// *{Error}* If the type is not valid.
-// **Returns**:
-// *{boolean}* : ```true``` if the current type inherits from the type, ```false``` otherwise.
-// **Example usage**:
-//
-//     console.log(person.isof("owl:Thing"));
-//     // <-- true
-    this.isof = function (type) {
-        type = this.vie.types.get(type);
-        if (type) {
-            return type.subsumes(this.id);
-        } else {
-            throw new Error("No valid type given");
-        }
-    };
-
-// ### subsumes(type)
-// This method checks whether the current type is a parent of the given type.
-// **Parameters**:
-// *{string|VIE.Type}* **type** The type (or the id of that type) to be checked.
-// **Throws**:
-// *{Error}* If the type is not valid.
-// **Returns**:
-// *{boolean}* : ```true``` if the current type is a parent of the type, ```false``` otherwise.
-// **Example usage**:
-//
-//     var x = new vie.Type(...);
-//     var y = new vie.Type(...).inherit(x);
-//     y.isof(x) === x.subsumes(y);
-    this.subsumes = function (type) {
-        type = this.vie.types.get(type);
-        if (type) {
-            if (this.id === type.id) {
-                return true;
-            }
-            var subtypes = this.subtypes.list();
-            for (var c = 0; c < subtypes.length; c++) {
-                var childObj = subtypes[c];
-                if (childObj) {
-                     if (childObj.id === type.id || childObj.subsumes(type)) {
-                         return true;
-                     }
-                }
-            }
-            return false;
-        } else {
-            throw new Error("No valid type given");
-        }
-    };
-
-// ### inherit(supertype)
-// This method invokes inheritance throught the types. This adds the current type to the
-// subtypes of the supertype and vice versa.
-// **Parameters**:
-// *{string|VIE.Type|array}* **supertype** The type to be inherited from. If this is an array
-// the inherit method is called sequentially on all types.
-// **Throws**:
-// *{Error}* If the type is not valid.
-// **Returns**:
-// *{VIE.Type}* : The instance itself.
-// **Example usage**:
-//
-//     var x = new vie.Type(...);
-//     var y = new vie.Type(...).inherit(x);
-//     y.isof(x) // <-- true
-    this.inherit = function (supertype) {
-        if (typeof supertype === "string") {
-            this.inherit(this.vie.types.get(supertype));
-        }
-        else if (supertype instanceof this.vie.Type) {
-            supertype.subtypes.addOrOverwrite(this);
-            this.supertypes.addOrOverwrite(supertype);
-            try {
-                /* only for validation of attribute-inheritance!
-                   if this throws an error (inheriting two attributes
-                   that cannot be combined) we reverse all changes. */
-                this.attributes.list();
-            } catch (e) {
-                supertype.subtypes.remove(this);
-                this.supertypes.remove(supertype);
-                throw e;
-            }
-        } else if (jQuery.isArray(supertype)) {
-            for (var i = 0, slen = supertype.length; i < slen; i++) {
-                this.inherit(supertype[i]);
-            }
-        } else {
-            throw new Error("Wrong argument in VIE.Type.inherit()");
-        }
-        return this;
-    };
-
-// ### hierarchy()
-// This method serializes the hierarchy of child types into an object.
-// **Parameters**:
-// *nothing*
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{object}* : The hierachy of child types as an object.
-// **Example usage**:
-//
-//     var x = new vie.Type(...);
-//     var y = new vie.Type(...).inherit(x);
-//     x.hierarchy();
-    this.hierarchy = function () {
-        var obj = {id : this.id, subtypes: []};
-        var list = this.subtypes.list();
-        for (var c = 0, llen = list.length; c < llen; c++) {
-            var childObj = this.vie.types.get(list[c]);
-            obj.subtypes.push(childObj.hierarchy());
-        }
-        return obj;
-    };
-
-// ### instance()
-// This method creates a ```VIE.Entity``` instance from this type.
-// **Parameters**:
-// *{object}* **attrs**  see <a href="Entity.html">constructor of VIE.Entity</a>
-// *{object}* **opts**  see <a href="Entity.html">constructor of VIE.Entity</a>
-// **Throws**:
-// *{Error}* if the instance could not be built
-// **Returns**:
-// *{VIE.Entity}* : A **new** instance of a ```VIE.Entity``` with the current type.
-// **Example usage**:
-//
-//     var person = new vie.Type("person");
-//     var sebastian = person.instance(
-//         {"@subject" : "#me",
-//          "name" : "Sebastian"});
-//     console.log(sebastian.get("name")); // <-- "Sebastian"
-    this.instance = function (attrs, opts) {
-        attrs = (attrs)? attrs : {};
-        opts = (opts)? opts : {};
-
-        /* turn type/attribute checking on by default! */
-        if (opts.typeChecking !== false) {
-            for (var a in attrs) {
-                if (a.indexOf('@') !== 0 && !this.attributes.get(a)) {
-                    throw new Error("Cannot create an instance of " + this.id + " as the type does not allow an attribute '" + a + "'!");
-                }
-            }
-        }
-
-        if (attrs['@type']) {
-            attrs['@type'].push(this.id);
-        } else {
-            attrs['@type'] = this.id;
-        }
-
-        return new this.vie.Entity(attrs, opts);
-    };
-
-// ### toString()
-// This method returns the id of the type.
-// **Parameters**:
-// *nothing*
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{string}* : The id of the type.
-// **Example usage**:
-//
-//     var x = new vie.Type(...);
-//     x.toString() === x.id;
-    this.toString = function () {
-        return this.id;
-    };
-};
-
-// ### VIE.Types()
-// This is the constructor of a VIE.Types. This is a convenience class
-// to store ```VIE.Type``` instances properly.
-// **Parameters**:
-// *nothing*
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Types}* : A **new** VIE.Types object.
-// **Example usage**:
-//
-//     var types = new vie.Types();
-VIE.prototype.Types = function () {
-
-    this._types = {};
-
-// ### add(id, attrs, metadata)
-// This method adds a `VIE.Type` to the types.
-// **Parameters**:
-// *{string|VIE.Type}* **id** If this is a string, the type is created and directly added.
-// *{string|object}* **attrs** Only used if ```id``` is a string.
-// *{object}* **metadata** potential additional metadata about the type.
-// **Throws**:
-// *{Error}* if a type with the given id already exists a ```VIE.Entity``` instance from this type.
-// **Returns**:
-// *{VIE.Types}* : The instance itself.
-// **Example usage**:
-//
-//     var types = new vie.Types();
-//     types.add("Person", ["name", "knows"]);
-    this.add = function (id, attrs, metadata) {
-        if (_.isArray(id)) {
-           _.each(id, function (type) {
-             this.add(type);
-           }, this);
-           return this;
-        }
-
-        if (this.get(id)) {
-            throw new Error("Type '" + id + "' already registered.");
-        }  else {
-            if (typeof id === "string") {
-                var t = new this.vie.Type(id, attrs, metadata);
-                this._types[t.id] = t;
-                return t;
-            } else if (id instanceof this.vie.Type) {
-                this._types[id.id] = id;
-                return id;
-            } else {
-                throw new Error("Wrong argument to VIE.Types.add()!");
-            }
-        }
-        return this;
-    };
-
-// ### addOrOverwrite(id, attrs)
-// This method adds or overwrites a `VIE.Type` to the types. This is the same as
-// ``this.remove(id); this.add(id, attrs);``
-// **Parameters**:
-// *{string|VIE.Type}* **id** If this is a string, the type is created and directly added.
-// *{string|object}* **attrs** Only used if ```id``` is a string.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Types}* : The instance itself.
-// **Example usage**:
-//
-//     var types = new vie.Types();
-//     types.addOrOverwrite("Person", ["name", "knows"]);
-    this.addOrOverwrite = function(id, attrs){
-        if (this.get(id)) {
-            this.remove(id);
-        }
-        return this.add(id, attrs);
-    };
-
-// ### get(id)
-// This method retrieves a `VIE.Type` from the types by it's id.
-// **Parameters**:
-// *{string|VIE.Type}* **id** The id or the type itself.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Type}* : The instance of the type or ```undefined```.
-// **Example usage**:
-//
-//     var types = new vie.Types();
-//     types.addOrOverwrite("Person", ["name", "knows"]);
-//     types.get("Person");
-    this.get = function (id) {
-        if (!id) {
-            return undefined;
-        }
-        if (typeof id === 'string') {
-            var lid = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id);
-            return this._types[lid];
-        } else if (id instanceof this.vie.Type) {
-            return this.get(id.id);
-        }
-        return undefined;
-    };
-
-// ### remove(id)
-// This method removes a type of given id from the type. This also
-// removes all children if their only parent were this
-// type. Furthermore, this removes the link from the
-// super- and subtypes.
-// **Parameters**:
-// *{string|VIE.Type}* **id** The id or the type itself.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Type}* : The removed type.
-// **Example usage**:
-//
-//     var types = new vie.Types();
-//     types.addOrOverwrite("Person", ["name", "knows"]);
-//     types.remove("Person");
-    this.remove = function (id) {
-        var t = this.get(id);
-        /* test whether the type actually exists in VIE
-         * and prevents removing *owl:Thing*.
-         */
-        if (!t) {
-            return this;
-        }
-        if (!t || t.subsumes("owl:Thing")) {
-            console.warn("You are not allowed to remove 'owl:Thing'.");
-            return this;
-        }
-        delete this._types[t.id];
-
-        var subtypes = t.subtypes.list();
-        for (var c = 0; c < subtypes.length; c++) {
-            var childObj = subtypes[c];
-            if (childObj.supertypes.list().length === 1) {
-                /* recursively remove all children
-                   that inherit only from this type */
-                this.remove(childObj);
-            } else {
-                childObj.supertypes.remove(t.id);
-            }
-        }
-        return t;
-    };
-
-// ### toArray() === list()
-// This method returns an array of all types.
-// **Parameters**:
-// *nothing*
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{array}* : An array of ```VIE.Type``` instances.
-// **Example usage**:
-//
-//     var types = new vie.Types();
-//     types.addOrOverwrite("Person", ["name", "knows"]);
-//     types.list();
-    this.toArray = this.list = function () {
-        var ret = [];
-        for (var i in this._types) {
-            ret.push(this._types[i]);
-        }
-        return ret;
-    };
-
-// ### sort(types, desc)
-// This method sorts an array of types in their order, given by the
-// inheritance. This returns a copy and leaves the original array untouched.
-// **Parameters**:
-// *{array|VIE.Type}* **types** The array of ```VIE.Type``` instances or ids of types to be sorted.
-// *{boolean}* **desc** If 'desc' is given and 'true', the array will be sorted
-// in descendant order.
-// *nothing*
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{array}* : A sorted copy of the array.
-// **Example usage**:
-//
-//     var types = new vie.Types();
-//     types.addOrOverwrite("Person", ["name", "knows"]);
-//     types.sort(types.list(), true);
-    this.sort = function (types, desc) {
-        var self = this;
-        types = (jQuery.isArray(types))? types : [ types ];
-        desc = (desc)? true : false;
-
-        if (types.length === 0) return [];
-        var copy = [ types[0] ];
-        var x, tlen;
-        for (x = 1, tlen = types.length; x < tlen; x++) {
-            var insert = types[x];
-            var insType = self.get(insert);
-            if (insType) {
-                for (var y = 0; y < copy.length; y++) {
-                    if (insType.subsumes(copy[y])) {
-                        copy.splice(y,0,insert);
-                        break;
-                    } else if (y === copy.length - 1) {
-                        copy.push(insert);
-                    }
-                }
-            }
-        }
-
-        //unduplicate
-        for (x = 0; x < copy.length; x++) {
-            if (copy.lastIndexOf(copy[x]) !== x) {
-                copy.splice(x, 1);
-                x--;
-            }
-        }
-
-        if (!desc) {
-            copy.reverse();
-        }
-        return copy;
-    };
-};
-//     VIE - Vienna IKS Editables
-//     (c) 2011 Henri Bergius, IKS Consortium
-//     (c) 2011 Sebastian Germesin, IKS Consortium
-//     (c) 2011 Szaby Grünwald, IKS Consortium
-//     VIE may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://viejs.org/
-//
-
-// ## VIE.Attributes
-// Within VIE, we provide special capabilities of handling attributes of types of entites. This
-// helps first of all to list all attributes of an entity type, but furthermore fully supports
-// inheritance of attributes from the type-class to inherit from.
-if (VIE.prototype.Attribute) {
-	throw new Error("ERROR: VIE.Attribute is already defined. Please check your VIE installation!");
-}
-if (VIE.prototype.Attributes) {
-	throw new Error("ERROR: VIE.Attributes is already defined. Please check your VIE installation!");
-}
-
-// ### VIE.Attribute(id, range, domain, minCount, maxCount, metadata)
-// This is the constructor of a VIE.Attribute.
-// **Parameters**:
-// *{string}* **id** The id of the attribute.
-// *{string|array}* **range** A string or an array of strings of the target range of
-// the attribute.
-// *{string}* **domain** The domain of the attribute.
-// *{number}* **minCount** The minimal number this attribute can occur. (needs to be >= 0)
-// *{number}* **maxCount** The maximal number this attribute can occur. (needs to be >= minCount, use `-1` for unlimited)
-// *{object}* **metadata** Possible metadata about the attribute
-// **Throws**:
-// *{Error}* if one of the given paramenters is missing.
-// **Returns**:
-// *{VIE.Attribute}* : A **new** VIE.Attribute object.
-// **Example usage**:
-//
-//     var knowsAttr = new vie.Attribute("knows", ["Person"], "Person", 0, 10);
-//      // Creates an attribute to describe a *knows*-relationship
-//      // between persons. Each person can only have
-VIE.prototype.Attribute = function (id, range, domain, minCount, maxCount, metadata) {
-    if (id === undefined || typeof id !== 'string') {
-        throw new Error("The attribute constructor needs an 'id' of type string! E.g., 'Person'");
-    }
-    if (range === undefined) {
-        throw new Error("The attribute constructor of " + id + " needs 'range'.");
-    }
-    if (domain === undefined) {
-        throw new Error("The attribute constructor of " + id + " needs a 'domain'.");
-    }
-
-    this._domain = domain;
-
-// ### id
-// This field stores the id of the attribute's instance.
-// **Parameters**:
-// nothing
-// **Throws**:
-// nothing
-// **Returns**:
-// *{string}* : A URI, representing the id of the attribute.
-// **Example usage**:
-//
-//     var knowsAttr = new vie.Attribute("knows", ["Person"], "Person");
-//     console.log(knowsAttr.id);
-//     // --> <http://viejs.org/ns/knows>
-    this.id = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id);
-
-// ### range
-// This field stores the ranges of the attribute's instance.
-// **Parameters**:
-// nothing
-// **Throws**:
-// nothing
-// **Returns**:
-// *{array}* : An array of strings which represent the types.
-// **Example usage**:
-//
-//     var knowsAttr = new vie.Attribute("knows", ["Person"], "Person");
-//     console.log(knowsAttr.range);
-//      // --> ["Person"]
-    this.range = (_.isArray(range))? range : [ range ];
-
-// ### min
-// This field stores the minimal amount this attribute can occur in the type's instance. The number
-// needs to be greater or equal to zero.
-// **Parameters**:
-// nothing
-// **Throws**:
-// nothing
-// **Returns**:
-// *{int}* : The minimal amount this attribute can occur.
-// **Example usage**:
-//
-//     console.log(person.min);
-//      // --> 0
-    minCount = minCount ? minCount : 0;
-    this.min = (minCount > 0) ? minCount : 0;
-
-// ### max
-// This field stores the maximal amount this attribute can occur in the type's instance.
-// This number cannot be smaller than min
-// **Parameters**:
-// nothing
-// **Throws**:
-// nothing
-// **Returns**:
-// *{int}* : The maximal amount this attribute can occur.
-// **Example usage**:
-//
-//     console.log(person.max);
-//      // --> 1.7976931348623157e+308
-    maxCount = maxCount ? maxCount : 1;
-    if (maxCount === -1) {
-      maxCount = Number.MAX_VALUE;
-    }
-    this.max = (maxCount >= this.min)? maxCount : this.min;
-
-// ### metadata
-// This field holds potential metadata about the attribute.
-    this.metadata = metadata ? metadata : {};
-
-// ### applies(range)
-// This method checks, whether the current attribute applies in the given range.
-// If ```range``` is a string and cannot be transformed into a ```VIE.Type```,
-// this performs only string comparison, if it is a VIE.Type
-// or an ID of a VIE.Type, then inheritance is checked as well.
-// **Parameters**:
-// *{string|VIE.Type}* **range** The ```VIE.Type``` (or it's string representation) to be checked.
-// **Throws**:
-// nothing
-// **Returns**:
-// *{boolean}* : ```true``` if the given type applies to this attribute and ```false``` otherwise.
-// **Example usage**:
-//
-//     var knowsAttr = new vie.Attribute("knows", ["Person"], "Person");
-//     console.log(knowsAttr.applies("Person")); // --> true
-//     console.log(knowsAttr.applies("Place")); // --> false
-    this.applies = function (range) {
-        if (this.vie.types.get(range)) {
-            range = this.vie.types.get(range);
-        }
-        for (var r = 0, len = this.range.length; r < len; r++) {
-            var x = this.vie.types.get(this.range[r]);
-            if (x === undefined && typeof range === "string") {
-                if (range === this.range[r]) {
-                    return true;
-                }
-            }
-            else {
-                if (range.isof(this.range[r])) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    };
-
-};
-
-// ## VIE.Attributes(domain, attrs)
-// This is the constructor of a VIE.Attributes. Basically a convenience class
-// that represents a list of ```VIE.Attribute```. As attributes are part of a
-// certain ```VIE.Type```, it needs to be passed for inheritance checks.
-// **Parameters**:
-// *{string}* **domain** The domain of the attributes (the type they will be part of).
-// *{string|VIE.Attribute|array}* **attrs** Either a string representation of an attribute,
-// a proper instance of ```VIE.Attribute``` or an array of both.
-// *{string}* **domain** The domain of the attribute.
-// **Throws**:
-// *{Error}* if one of the given paramenters is missing.
-// **Returns**:
-// *{VIE.Attribute}* : A **new** VIE.Attribute instance.
-// **Example usage**:
-//
-//     var knowsAttr = new vie.Attribute("knows", ["Person"], "Person");
-//     var personAttrs = new vie.Attributes("Person", knowsAttr);
-VIE.prototype.Attributes = function (domain, attrs) {
-
-    this._local = {};
-    this._attributes = {};
-
-// ### domain
-// This field stores the domain of the attributes' instance.
-// **Parameters**:
-// nothing
-// **Throws**:
-// nothing
-// **Returns**:
-// *{string}* : The string representation of the domain.
-// **Example usage**:
-//
-//     console.log(personAttrs.domain);
-//     // --> ["Person"]
-    this.domain = domain;
-
-// ### add(id, range, min, max, metadata)
-// This method adds a ```VIE.Attribute``` to the attributes instance.
-// **Parameters**:
-// *{string|VIE.Attribute}* **id** The string representation of an attribute, or a proper
-// instance of a ```VIE.Attribute```.
-// *{string|array}* **range** An array representing the target range of the attribute.
-// *{number}* **min** The minimal amount this attribute can appear.
-// instance of a ```VIE.Attribute```.
-// *{number}* **max** The maximal amount this attribute can appear.
-// *{object}* **metadata** Additional metadata for the attribute.
-// **Throws**:
-// *{Error}* If an atribute with the given id is already registered.
-// *{Error}* If the ```id``` parameter is not a string, nor a ```VIE.Type``` instance.
-// **Returns**:
-// *{VIE.Attribute}* : The generated or passed attribute.
-// **Example usage**:
-//
-//     personAttrs.add("name", "Text", 0, 1);
-    this.add = function (id, range, min, max, metadata) {
-        if (_.isArray(id)) {
-          _.each(id, function (attribute) {
-            this.add(attribute);
-          }, this);
-          return this;
-        }
-
-        if (this.get(id)) {
-            throw new Error("Attribute '" + id + "' already registered for domain " + this.domain.id + "!");
-        } else {
-            if (typeof id === "string") {
-                var a = new this.vie.Attribute(id, range, this.domain, min, max, metadata);
-                this._local[a.id] = a;
-                return a;
-            } else if (id instanceof this.vie.Attribute) {
-                id.domain = this.domain;
-                id.vie = this.vie;
-                this._local[id.id] = id;
-                return id;
-            } else {
-                throw new Error("Wrong argument to VIE.Types.add()!");
-            }
-        }
-    };
-
-// ### remove(id)
-// This method removes a ```VIE.Attribute``` from the attributes instance.
-// **Parameters**:
-// *{string|VIE.Attribute}* **id** The string representation of an attribute, or a proper
-// instance of a ```VIE.Attribute```.
-// **Throws**:
-// *{Error}* When the attribute is inherited from a parent ```VIE.Type``` and thus cannot be removed.
-// **Returns**:
-// *{VIE.Attribute}* : The removed attribute.
-// **Example usage**:
-//
-//     personAttrs.remove("knows");
-    this.remove = function (id) {
-        var a = this.get(id);
-        if (a.id in this._local) {
-            delete this._local[a.id];
-            return a;
-        }
-        throw new Error("The attribute " + id + " is inherited and cannot be removed from the domain " + this.domain.id + "!");
-    };
-
-// ### get(id)
-// This method returns a ```VIE.Attribute``` from the attributes instance by it's id.
-// **Parameters**:
-// *{string|VIE.Attribute}* **id** The string representation of an attribute, or a proper
-// instance of a ```VIE.Attribute```.
-// **Throws**:
-// *{Error}* When the method is called with an unknown datatype.
-// **Returns**:
-// *{VIE.Attribute}* : The attribute.
-// **Example usage**:
-//
-//     personAttrs.get("knows");
-    this.get = function (id) {
-        if (typeof id === 'string') {
-            var lid = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id);
-            return this._inherit()._attributes[lid];
-        } else if (id instanceof this.vie.Attribute) {
-            return this.get(id.id);
-        } else {
-            throw new Error("Wrong argument in VIE.Attributes.get()");
-        }
-    };
-
-// ### _inherit()
-// The private method ```_inherit``` creates a full list of all attributes. This includes
-// local attributes as well as inherited attributes from the parents. The ranges of attributes
-// with the same id will be merged. This method is called everytime an attribute is requested or
-// the list of all attributes. Usually this method should not be invoked outside of the class.
-// **Parameters**:
-// *nothing*
-// instance of a ```VIE.Attribute```.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *nothing*
-// **Example usage**:
-//
-//     personAttrs._inherit();
-    this._inherit = function () {
-        var a, x, id;
-        var attributes = jQuery.extend(true, {}, this._local);
-
-        var inherited = _.map(this.domain.supertypes.list(),
-            function (x) {
-               return x.attributes;
-            }
-        );
-
-        var add = {};
-        var merge = {};
-        var ilen, alen;
-        for (a = 0, ilen = inherited.length; a < ilen; a++) {
-            var attrs = inherited[a].list();
-            for (x = 0, alen = attrs.length; x < alen; x++) {
-                id = attrs[x].id;
-                if (!(id in attributes)) {
-                    if (!(id in add) && !(id in merge)) {
-                        add[id] = attrs[x];
-                    }
-                    else {
-                        if (!merge[id]) {
-                            merge[id] = {range : [], mins : [], maxs: [], metadatas: []};
-                        }
-                        if (id in add) {
-                            merge[id].range = jQuery.merge(merge[id].range, add[id].range);
-                            merge[id].mins = jQuery.merge(merge[id].mins, [ add[id].min ]);
-                            merge[id].maxs = jQuery.merge(merge[id].maxs, [ add[id].max ]);
-                            merge[id].metadatas = jQuery.merge(merge[id].metadatas, [ add[id].metadata ]);
-                            delete add[id];
-                        }
-                        merge[id].range = jQuery.merge(merge[id].range, attrs[x].range);
-                        merge[id].mins = jQuery.merge(merge[id].mins, [ attrs[x].min ]);
-                        merge[id].maxs = jQuery.merge(merge[id].maxs, [ attrs[x].max ]);
-                        merge[id].metadatas = jQuery.merge(merge[id].metadatas, [ attrs[x].metadata ]);
-                        merge[id].range = _.uniq(merge[id].range);
-                        merge[id].mins = _.uniq(merge[id].mins);
-                        merge[id].maxs = _.uniq(merge[id].maxs);
-                        merge[id].metadatas = _.uniq(merge[id].metadatas);
-                    }
-                }
-            }
-        }
-
-        /* adds inherited attributes that do not need to be merged */
-        jQuery.extend(attributes, add);
-
-        /* merges inherited attributes */
-        for (id in merge) {
-            var mranges = merge[id].range;
-            var mins = merge[id].mins;
-            var maxs = merge[id].maxs;
-            var metadatas = merge[id].metadatas;
-            var ranges = [];
-            //merging ranges
-            for (var r = 0, mlen = mranges.length; r < mlen; r++) {
-                var p = this.vie.types.get(mranges[r]);
-                var isAncestorOf = false;
-                if (p) {
-                    for (x = 0; x < mlen; x++) {
-                        if (x === r) {
-                            continue;
-                        }
-                        var c = this.vie.types.get(mranges[x]);
-                        if (c && c.isof(p)) {
-                            isAncestorOf = true;
-                            break;
-                        }
-                    }
-                }
-                if (!isAncestorOf) {
-                    ranges.push(mranges[r]);
-                }
-            }
-
-            var maxMin = _.max(mins);
-            var minMax = _.min(maxs);
-            if (maxMin <= minMax && minMax >= 0 && maxMin >= 0) {
-                attributes[id] = new this.vie.Attribute(id, ranges, this, maxMin, minMax, metadatas[0]);
-            } else {
-                throw new Error("This inheritance is not allowed because of an invalid minCount/maxCount pair!");
-            }
-        }
-
-        this._attributes = attributes;
-        return this;
-    };
-
-// ### toArray() === list()
-// This method return an array of ```VIE.Attribute```s from the attributes instance.
-// **Parameters**:
-// *nothing.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{array}* : An array of ```VIE.Attribute```.
-// **Example usage**:
-//
-//     personAttrs.list();
-    this.toArray = this.list = function (range) {
-        var ret = [];
-        var attributes = this._inherit()._attributes;
-        for (var a in attributes) {
-            if (!range || attributes[a].applies(range)) {
-                ret.push(attributes[a]);
-            }
-        }
-        return ret;
-    };
-
-    attrs = _.isArray(attrs) ? attrs : [ attrs ];
-    _.each(attrs, function (attr) {
-        this.add(attr.id, attr.range, attr.min, attr.max, attr.metadata);
-    }, this);
-};
-//     VIE - Vienna IKS Editables
-//     (c) 2011 Henri Bergius, IKS Consortium
-//     (c) 2011 Sebastian Germesin, IKS Consortium
-//     (c) 2011 Szaby Grünwald, IKS Consortium
-//     VIE may be freely distributed under the MIT license.
-//     For all details and documentation:
-//     http://viejs.org/
-if (VIE.prototype.Namespaces) {
-    throw new Error("ERROR: VIE.Namespaces is already defined. " +
-        "Please check your VIE installation!");
-}
-
-// ## VIE Namespaces
-//
-// In general, a namespace is a container that provides context for the identifiers.
-// Within VIE, namespaces are used to distinguish different ontolgies or vocabularies
-// of identifiers, types and attributes. However, because of their verbosity, namespaces
-// tend to make their usage pretty circuitous. The ``VIE.Namespaces(...)`` class provides VIE
-// with methods to maintain abbreviations (akak **prefixes**) for namespaces in order to
-// alleviate their usage. By default, every VIE instance is equipped with a main instance
-// of the namespaces in ``myVIE.namespaces``. Furthermore, VIE uses a **base namespace**,
-// which is used if no prefix is given (has an empty prefix).
-// In the upcoming sections, we will explain the
-// methods to add, access and remove prefixes.
-
-
-
-// ## VIE.Namespaces(base, namespaces)
-// This is the constructor of a VIE.Namespaces. The constructor initially
-// needs a *base namespace* and can optionally be initialised with an
-// associative array of prefixes and namespaces. The base namespace is used in a way
-// that every non-prefixed, non-expanded attribute or type is assumed to be of that
-// namespace. This helps, e.g., in an environment where only one namespace is given.
-// **Parameters**:
-// *{string}* **base** The base namespace.
-// *{object}* **namespaces** Initial namespaces to bootstrap the namespaces. (optional)
-// **Throws**:
-// *{Error}* if the base namespace is missing.
-// **Returns**:
-// *{VIE.Attribute}* : A **new** VIE.Attribute object.
-// **Example usage**:
-//
-//     var ns = new myVIE.Namespaces("http://viejs.org/ns/",
-//           {
-//            "foaf": "http://xmlns.com/foaf/0.1/"
-//           });
-VIE.prototype.Namespaces = function (base, namespaces) {
-
-    if (!base) {
-        throw new Error("Please provide a base namespace!");
-    }
-    this._base = base;
-
-    this._namespaces = (namespaces)? namespaces : {};
-    if (typeof this._namespaces !== "object" || _.isArray(this._namespaces)) {
-        throw new Error("If you want to initialise VIE namespace prefixes, " +
-            "please provide a proper object!");
-    }
-};
-
-
-// ### base(ns)
-// This is a **getter** and **setter** for the base
-// namespace. If called like ``base();`` it
-// returns the actual base namespace as a string. If provided
-// with a string, e.g., ``base("http://viejs.org/ns/");``
-// it sets the current base namespace and retuns the namespace object
-// for the purpose of chaining. If provided with anything except a string,
-// it throws an Error.
-// **Parameters**:
-// *{string}* **ns** The namespace to be set. (optional)
-// **Throws**:
-// *{Error}* if the namespace is not of type string.
-// **Returns**:
-// *{string}* : The current base namespace.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     console.log(namespaces.base()); // <-- "http://base.ns/"
-//     namespaces.base("http://viejs.org/ns/");
-//     console.log(namespaces.base()); // <-- "http://viejs.org/ns/"
-VIE.prototype.Namespaces.prototype.base = function (ns) {
-    if (!ns) {
-        return this._base;
-    }
-    else if (typeof ns === "string") {
-        /* remove another mapping */
-        this.removeNamespace(ns);
-        this._base = ns;
-        return this._base;
-    } else {
-        throw new Error("Please provide a valid namespace!");
-    }
-};
-
-// ### add(prefix, namespace)
-// This method adds new prefix mappings to the
-// current instance. If a prefix or a namespace is already
-// present (in order to avoid ambiguities), an Error is thrown.
-// ``prefix`` can also be an object in which case, the method
-// is called sequentially on all elements.
-// **Parameters**:
-// *{string|object}* **prefix** The prefix to be set. If it is an object, the
-// method will be applied to all key,value pairs sequentially.
-// *{string}* **namespace** The namespace to be set.
-// **Throws**:
-// *{Error}* If a prefix or a namespace is already
-// present (in order to avoid ambiguities).
-// **Returns**:
-// *{VIE.Namespaces}* : The current namespaces instance.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.add("", "http://...");
-//     // is always equal to
-//     namespaces.base("http://..."); // <-- setter of base namespace
-VIE.prototype.Namespaces.prototype.add = function (prefix, namespace) {
-    if (typeof prefix === "object") {
-        for (var k1 in prefix) {
-            this.add(k1, prefix[k1]);
-        }
-        return this;
-    }
-    if (prefix === "") {
-        this.base(namespace);
-        return this;
-    }
-    /* checking if we overwrite existing mappings */
-    else if (this.contains(prefix) && namespace !== this._namespaces[prefix]) {
-        throw new Error("ERROR: Trying to register namespace prefix mapping (" + prefix + "," + namespace + ")!" +
-              "There is already a mapping existing: '(" + prefix + "," + this.get(prefix) + ")'!");
-    } else {
-        jQuery.each(this._namespaces, function (k1,v1) {
-            if (v1 === namespace && k1 !== prefix) {
-                throw new Error("ERROR: Trying to register namespace prefix mapping (" + prefix + "," + namespace + ")!" +
-                      "There is already a mapping existing: '(" + k1 + "," + namespace + ")'!");
-            }
-        });
-    }
-    /* if not, just add them */
-    this._namespaces[prefix] = namespace;
-    return this;
-};
-
-// ### addOrReplace(prefix, namespace)
-// This method adds new prefix mappings to the
-// current instance. This will overwrite existing mappings.
-// **Parameters**:
-// *{string|object}* **prefix** The prefix to be set. If it is an object, the
-// method will be applied to all key,value pairs sequentially.
-// *{string}* **namespace** The namespace to be set.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Namespaces}* : The current namespaces instance.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.addOrReplace("", "http://...");
-//     // is always equal to
-//     namespaces.base("http://..."); // <-- setter of base namespace
-VIE.prototype.Namespaces.prototype.addOrReplace = function (prefix, namespace) {
-    if (typeof prefix === "object") {
-        for (var k1 in prefix) {
-            this.addOrReplace(k1, prefix[k1]);
-        }
-        return this;
-    }
-    this.remove(prefix);
-    this.removeNamespace(namespace);
-    return this.add(prefix, namespace);
-};
-
-// ### get(prefix)
-// This method retrieves a namespaces, given a prefix. If the
-// prefix is the empty string, the base namespace is returned.
-// **Parameters**:
-// *{string}* **prefix** The prefix to be retrieved.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{string|undefined}* : The namespace or ```undefined``` if no namespace could be found.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.addOrReplace("test", "http://test.ns");
-//     console.log(namespaces.get("test")); // <-- "http://test.ns"
-VIE.prototype.Namespaces.prototype.get = function (prefix) {
-    if (prefix === "") {
-        return this.base();
-    }
-    return this._namespaces[prefix];
-};
-
-// ### getPrefix(namespace)
-// This method retrieves a prefix, given a namespace.
-// **Parameters**:
-// *{string}* **namespace** The namespace to be retrieved.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{string|undefined}* : The prefix or ```undefined``` if no prefix could be found.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.addOrReplace("test", "http://test.ns");
-//     console.log(namespaces.getPrefix("http://test.ns")); // <-- "test"
-VIE.prototype.Namespaces.prototype.getPrefix = function (namespace) {
-    var prefix;
-    if (namespace.indexOf('<') === 0) {
-        namespace = namespace.substring(1, namespace.length - 1);
-    }
-    jQuery.each(this._namespaces, function (k1,v1) {
-        if (namespace.indexOf(v1) === 0) {
-            prefix = k1;
-        }
-
-        if (namespace.indexOf(k1 + ':') === 0) {
-            prefix = k1;
-        }
-    });
-    return prefix;
-};
-
-// ### contains(prefix)
-// This method checks, whether a prefix is stored in the instance.
-// **Parameters**:
-// *{string}* **prefix** The prefix to be checked.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{boolean}* : ```true``` if the prefix could be found, ```false``` otherwise.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.addOrReplace("test", "http://test.ns");
-//     console.log(namespaces.contains("test")); // <-- true
-VIE.prototype.Namespaces.prototype.contains = function (prefix) {
-    return (prefix in this._namespaces);
-};
-
-// ### containsNamespace(namespace)
-// This method checks, whether a namespace is stored in the instance.
-// **Parameters**:
-// *{string}* **namespace** The namespace to be checked.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{boolean}* : ```true``` if the namespace could be found, ```false``` otherwise.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.addOrReplace("test", "http://test.ns");
-//     console.log(namespaces.containsNamespace("http://test.ns")); // <-- true
-VIE.prototype.Namespaces.prototype.containsNamespace = function (namespace) {
-    return this.getPrefix(namespace) !== undefined;
-};
-
-// ### update(prefix, namespace)
-// This method overwrites the namespace that is stored under the
-// prefix ``prefix`` with the new namespace ``namespace``.
-// If a namespace is already bound to another prefix, an Error is thrown.
-// **Parameters**:
-// *{string}* **prefix** The prefix.
-// *{string}* **namespace** The namespace.
-// **Throws**:
-// *{Error}* If a namespace is already bound to another prefix.
-// **Returns**:
-// *{VIE.Namespaces}* : The namespace instance.
-// **Example usage**:
-//
-//     ...
-VIE.prototype.Namespaces.prototype.update = function (prefix, namespace) {
-    this.remove(prefix);
-    return this.add(prefix, namespace);
-};
-
-// ### updateNamespace(prefix, namespace)
-// This method overwrites the prefix that is bound to the
-// namespace ``namespace`` with the new prefix ``prefix``. If another namespace is
-// already registered with the given ``prefix``, an Error is thrown.
-// **Parameters**:
-// *{string}* **prefix** The prefix.
-// *{string}* **namespace** The namespace.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Namespaces}* : The namespace instance.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.add("test", "http://test.ns");
-//     namespaces.updateNamespace("test2", "http://test.ns");
-//     namespaces.get("test2"); // <-- "http://test.ns"
-VIE.prototype.Namespaces.prototype.updateNamespace = function (prefix, namespace) {
-    this.removeNamespace(prefix);
-    return this.add(prefix, namespace);
-};
-
-// ### remove(prefix)
-// This method removes the namespace that is stored under the prefix ``prefix``.
-// **Parameters**:
-// *{string}* **prefix** The prefix to be removed.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Namespaces}* : The namespace instance.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.add("test", "http://test.ns");
-//     namespaces.get("test"); // <-- "http://test.ns"
-//     namespaces.remove("test");
-//     namespaces.get("test"); // <-- undefined
-VIE.prototype.Namespaces.prototype.remove = function (prefix) {
-    if (prefix) {
-        delete this._namespaces[prefix];
-    }
-    return this;
-};
-
-// ### removeNamespace(namespace)
-// This method removes removes the namespace ``namespace`` from the instance.
-// **Parameters**:
-// *{string}* **namespace** The namespace to be removed.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{VIE.Namespaces}* : The namespace instance.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.add("test", "http://test.ns");
-//     namespaces.get("test"); // <-- "http://test.ns"
-//     namespaces.removeNamespace("http://test.ns");
-//     namespaces.get("test"); // <-- undefined
-VIE.prototype.Namespaces.prototype.removeNamespace = function (namespace) {
-    var prefix = this.getPrefix(namespace);
-    if (prefix) {
-        delete this._namespaces[prefix];
-    }
-    return this;
-};
-
-// ### toObj()
-// This method serializes the namespace instance into an associative
-// array representation. The base namespace is given an empty
-// string as key.
-// **Parameters**:
-// *{boolean}* **omitBase** If set to ```true``` this omits the baseNamespace.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{object}* : A serialization of the namespaces as an object.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.add("test", "http://test.ns");
-//     console.log(namespaces.toObj());
-//     // <-- {""    : "http://base.ns/",
-//             "test": "http://test.ns"}
-//     console.log(namespaces.toObj(true));
-//     // <-- {"test": "http://test.ns"}
-VIE.prototype.Namespaces.prototype.toObj = function (omitBase) {
-    if (omitBase) {
-        return jQuery.extend({}, this._namespaces);
-    }
-    return jQuery.extend({'' : this._base}, this._namespaces);
-};
-
-// ### curie(uri, safe)
-// This method converts a given
-// URI into a CURIE (or SCURIE), based on the given ```VIE.Namespaces``` object.
-// If the given uri is already a URI, it is left untouched and directly returned.
-// If no prefix could be found, an ```Error``` is thrown.
-// **Parameters**:
-// *{string}* **uri** The URI to be transformed.
-// *{boolean}* **safe** A flag whether to generate CURIEs or SCURIEs.
-// **Throws**:
-// *{Error}* If no prefix could be found in the passed namespaces.
-// **Returns**:
-// *{string}* The CURIE or SCURIE.
-// **Example usage**:
-//
-//     var ns = new myVIE.Namespaces(
-//           "http://viejs.org/ns/",
-//           { "dbp": "http://dbpedia.org/ontology/" }
-//     );
-//     var uri = "<http://dbpedia.org/ontology/Person>";
-//     ns.curie(uri, false); // --> dbp:Person
-//     ns.curie(uri, true); // --> [dbp:Person]
-VIE.prototype.Namespaces.prototype.curie = function(uri, safe){
-    return VIE.Util.toCurie(uri, safe, this);
-};
-
-// ### isCurie(curie)
-// This method checks, whether
-// the given string is a CURIE and returns ```true``` if so and ```false```otherwise.
-// **Parameters**:
-// *{string}* **curie** The CURIE (or SCURIE) to be checked.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{boolean}* ```true``` if the given curie is a CURIE or SCURIE and ```false``` otherwise.
-// **Example usage**:
-//
-//     var ns = new myVIE.Namespaces(
-//           "http://viejs.org/ns/",
-//           { "dbp": "http://dbpedia.org/ontology/" }
-//     );
-//     var uri = "<http://dbpedia.org/ontology/Person>";
-//     var curie = "dbp:Person";
-//     var scurie = "[dbp:Person]";
-//     var text = "This is some text.";
-//     ns.isCurie(uri);    // --> false
-//     ns.isCurie(curie);  // --> true
-//     ns.isCurie(scurie); // --> true
-//     ns.isCurie(text);   // --> false
-VIE.prototype.Namespaces.prototype.isCurie = function (something) {
-    return VIE.Util.isCurie(something, this);
-};
-
-// ### uri(curie)
-// This method converts a
-// given CURIE (or save CURIE) into a URI, based on the given ```VIE.Namespaces``` object.
-// **Parameters**:
-// *{string}* **curie** The CURIE to be transformed.
-// **Throws**:
-// *{Error}* If no URI could be assembled.
-// **Returns**:
-// *{string}* : A string, representing the URI.
-// **Example usage**:
-//
-//     var ns = new myVIE.Namespaces(
-//           "http://viejs.org/ns/",
-//           { "dbp": "http://dbpedia.org/ontology/" }
-//     );
-//     var curie = "dbp:Person";
-//     var scurie = "[dbp:Person]";
-//     ns.uri(curie);
-//          --> <http://dbpedia.org/ontology/Person>
-//     ns.uri(scurie);
-//          --> <http://dbpedia.org/ontology/Person>
-VIE.prototype.Namespaces.prototype.uri = function (curie) {
-    return VIE.Util.toUri(curie, this);
-};
-
-// ### isUri(something)
-// This method checks, whether the given string is a URI.
-// **Parameters**:
-// *{string}* **something** : The string to be checked.
-// **Throws**:
-// *nothing*
-// **Returns**:
-// *{boolean}* : ```true``` if the string is a URI, ```false``` otherwise.
-// **Example usage**:
-//
-//     var namespaces = new vie.Namespaces("http://base.ns/");
-//     namespaces.addOrReplace("test", "http://test.ns");
-//     var uri = "<http://test.ns/Person>";
-//     var curie = "test:Person";
-//     namespaces.isUri(uri);   // --> true
-//     namespaces.isUri(curie); // --> false
-VIE.prototype.Namespaces.prototype.isUri = VIE.Util.isUri;
-/*global VIE:false Backbone:false _:false */
-if (!VIE.prototype.view) {
-    VIE.prototype.view = {};
-}
-
-VIE.prototype.view.Entity = Backbone.View.extend({
-    initialize: function(options) {
-        this.service = options.service ? options.service : 'rdfa';
-        this.vie = options.vie;
-
-        // Ensure view gets updated when properties of the Entity change.
-        _.bindAll(this, 'render', 'renderAbout');
-        this.model.on('change', this.render);
-        this.model.on('change:@subject', this.renderAbout);
-    },
-
-    // Rendering a view means writing the properties of the Entity back to
-    // the element containing our RDFa annotations.
-    render: function() {
-        this.vie.save({
-                element: this.el,
-                entity: this.model
-            }).
-            to(this.service).
-            execute();
-        return this;
-    },
-
-    renderAbout: function () {
-        this.vie.service(this.service).setElementSubject(this.model.getSubjectUri(), this.el);
-    }
-});
-})();
\ No newline at end of file
diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module
index 0c8eb0f185e5ba8978bf84d8c603795fd4fb8dba..6a73d8976d12110a17427aed9faf6e87c7f9efd1 100644
--- a/core/modules/edit/edit.module
+++ b/core/modules/edit/edit.module
@@ -39,21 +39,25 @@ function edit_permission() {
 }
 
 /**
- * Implements hook_contextual_links_view_alter().
+ * Implements hook_page_build().
  *
- * In-place editing builds upon contextual.module, but doesn't actually add its
- * "Quick edit" contextual link in PHP (i.e. here) because:
- *  - that would require to add a local task menu item in the menu system, which
- *    doesn't make any sense, since there is no corresponding page;
- *  - it should only work when JavaScript is enabled, because only then in-place
- *    editing is possible.
+ * Adds the edit library to the page for any user who has the 'access in-place
+ * editing' permission.
  */
-function edit_contextual_links_view_alter(&$element, $items) {
+function edit_page_build(&$page) {
   if (!user_access('access in-place editing')) {
     return;
   }
 
-  $element['#attached']['library'][] = array('edit', 'edit');
+  $page['#attached']['js'][] = array(
+    'type' => 'setting',
+    'data' => array('edit' => array(
+      'metadataURL' => url('edit/metadata'),
+      'fieldFormURL' => url('edit/form/!entity_type/!id/!field_name/!langcode/!view_mode'),
+      'context' => 'body',
+    )),
+  );
+  $page['#attached']['library'][] = array('edit', 'edit');
 }
 
 /**
@@ -66,38 +70,25 @@ function edit_library_info() {
   );
   $libraries['edit'] = array(
     'title' => 'Edit: in-place editing',
-    'website' => 'http://drupal.org/project/edit',
     'version' => VERSION,
     'js' => array(
       // Core.
       $path . '/js/edit.js' => $options,
-      $path . '/js/app.js' => $options,
       // Models.
-      $path . '/js/models/edit-app-model.js' => $options,
+      $path . '/js/models/AppModel.js' => $options,
+      $path . '/js/models/EntityModel.js' => $options,
+      $path . '/js/models/FieldModel.js' => $options,
+      $path . '/js/models/EditorModel.js' => $options,
       // Views.
-      $path . '/js/views/propertyeditordecoration-view.js' => $options,
-      $path . '/js/views/contextuallink-view.js' => $options,
-      $path . '/js/views/modal-view.js' => $options,
-      $path . '/js/views/toolbar-view.js' => $options,
-      // Backbone.sync implementation on top of Drupal forms.
-      $path . '/js/backbone.drupalform.js' => $options,
-      // VIE service.
-      $path . '/js/viejs/EditService.js' => $options,
-      // Create.js subclasses.
-      $path . '/js/createjs/editable.js' => $options,
-      $path . '/js/createjs/storage.js' => $options,
+      $path . '/js/views/AppView.js' => $options,
+      $path . '/js/views/EditorDecorationView.js' => $options,
+      $path . '/js/views/ContextualLinkView.js' => $options,
+      $path . '/js/views/ModalView.js' => $options,
+      $path . '/js/views/FieldToolbarView.js' => $options,
+      $path . '/js/views/EditorView.js' => $options,
       // Other.
       $path . '/js/util.js' => $options,
       $path . '/js/theme.js' => $options,
-      // Basic settings.
-      array(
-        'data' => array('edit' => array(
-          'metadataURL' => url('edit/metadata'),
-          'fieldFormURL' => url('edit/form/!entity_type/!id/!field_name/!langcode/!view_mode'),
-          'context' => 'body',
-        )),
-        'type' => 'setting',
-      ),
     ),
     'css' => array(
       $path . '/css/edit.css' => array(),
@@ -106,8 +97,6 @@ function edit_library_info() {
       array('system', 'jquery'),
       array('system', 'underscore'),
       array('system', 'backbone'),
-      array('system', 'vie.core'),
-      array('system', 'create.editonly'),
       array('system', 'jquery.form'),
       array('system', 'drupal.form'),
       array('system', 'drupal.ajax'),
@@ -115,20 +104,20 @@ function edit_library_info() {
     ),
   );
   $libraries['edit.editorWidget.form'] = array(
-    'title' => '"Form" Create.js PropertyEditor widget',
+    'title' => 'Form in-place editor',
     'version' => VERSION,
     'js' => array(
-      $path . '/js/createjs/editingWidgets/formwidget.js' => $options,
+      $path . '/js/editors/formEditor.js' => $options,
     ),
     'dependencies' => array(
       array('edit', 'edit'),
     ),
   );
   $libraries['edit.editorWidget.direct'] = array(
-    'title' => '"Direct" Create.js PropertyEditor widget',
+    'title' => 'Direct in-place editor',
     'version' => VERSION,
     'js' => array(
-      $path . '/js/createjs/editingWidgets/drupalcontenteditablewidget.js' => $options,
+      $path . '/js/editors/directEditor.js' => $options,
     ),
     'dependencies' => array(
       array('edit', 'edit'),
diff --git a/core/modules/edit/js/app.js b/core/modules/edit/js/app.js
deleted file mode 100644
index 14d76a087f52d4e99b4c970a07eb13b34fdea2c2..0000000000000000000000000000000000000000
--- a/core/modules/edit/js/app.js
+++ /dev/null
@@ -1,391 +0,0 @@
-/**
- * @file
- * A Backbone View that is the central app controller.
- */
-(function ($, _, Backbone, Drupal, VIE) {
-
-"use strict";
-
-  Drupal.edit = Drupal.edit || {};
-  Drupal.edit.EditAppView = Backbone.View.extend({
-    vie: null,
-    domService: null,
-
-    // Configuration for state handling.
-    states: [],
-    activeEditorStates: [],
-    singleEditorStates: [],
-
-    // State.
-    $entityElements: null,
-
-    /**
-     * Implements Backbone Views' initialize() function.
-     */
-    initialize: function() {
-      _.bindAll(this, 'appStateChange', 'acceptEditorStateChange', 'editorStateChange');
-
-      // VIE instance for Edit.
-      this.vie = new VIE();
-      // Use our custom DOM parsing service until RDFa is available.
-      this.vie.use(new this.vie.EditService());
-      this.domService = this.vie.service('edit');
-
-      // Instantiate configuration for state handling.
-      this.states = [
-        null, 'inactive', 'candidate', 'highlighted',
-        'activating', 'active', 'changed', 'saving', 'saved', 'invalid'
-      ];
-      this.activeEditorStates = ['activating', 'active'];
-      this.singleEditorStates = _.union(['highlighted'], this.activeEditorStates);
-
-      this.$entityElements = $([]);
-
-      // Use Create's Storage widget.
-      this.$el.createStorage({
-        vie: this.vie,
-        editableNs: 'createeditable'
-      });
-
-      // When view/edit mode is toggled in the menu, update the editor widgets.
-      this.model.on('change:activeEntity', this.appStateChange);
-    },
-
-    /**
-     * Finds editable properties within a given context.
-     *
-     * Finds editable properties, registers them with the app, updates their
-     * state to match the current app state.
-     *
-     * @param $context
-     *   A jQuery-wrapped context DOM element within which will be searched.
-     */
-    findEditableProperties: function($context) {
-      var that = this;
-      var activeEntity = this.model.get('activeEntity');
-
-      this.domService.findSubjectElements($context).each(function() {
-        var $element = $(this);
-
-        // Ignore editable properties for which we've already set up Create.js.
-        if (that.$entityElements.index($element) !== -1) {
-          return;
-        }
-
-        $element
-          // Instantiate an EditableEntity widget.
-          .createEditable({
-            vie: that.vie,
-            disabled: true,
-            state: 'inactive',
-            acceptStateChange: that.acceptEditorStateChange,
-            statechange: function(event, data) {
-              that.editorStateChange(data.previous, data.current, data.propertyEditor);
-            },
-            decoratePropertyEditor: function(data) {
-              that.decorateEditor(data.propertyEditor);
-            }
-          })
-          // This event is triggered just before Edit removes an EditableEntity
-          // widget, so that we can do proper clean-up.
-          .on('destroyedPropertyEditor.edit', function(event, editor) {
-            that.undecorateEditor(editor);
-            that.$entityElements = that.$entityElements.not($(this));
-          })
-          // Transition the new PropertyEditor into the default state.
-          .createEditable('setState', 'inactive');
-
-        // If the new PropertyEditor is for the entity that's currently being
-        // edited, then transition it to the 'candidate' state.
-        // (This happens when a field was modified and is re-rendered.)
-        var entityOfProperty = $element.createEditable('option', 'model');
-        if (entityOfProperty.getSubjectUri() === activeEntity) {
-          $element.createEditable('setState', 'candidate');
-        }
-
-        // Add this new EditableEntity widget element to the list.
-        that.$entityElements = that.$entityElements.add($element);
-      });
-    },
-
-    /**
-     * Sets the state of PropertyEditor widgets when edit mode begins or ends.
-     *
-     * Should be called whenever EditAppModel's "activeEntity" changes.
-     */
-    appStateChange: function() {
-      // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133, https://github.com/bergie/create/issues/140)
-      // We're currently setting the state on EditableEntity widgets instead of
-      // PropertyEditor widgets, because of
-      // https://github.com/bergie/create/issues/133.
-
-      var activeEntity = this.model.get('activeEntity');
-      var $editableFieldsForEntity = $('[data-edit-id^="' + activeEntity + '/"]');
-
-      // First, change the status of all PropertyEditor widgets to 'inactive'.
-      this.$entityElements.each(function() {
-        $(this).createEditable('setState', 'inactive', null, {reason: 'stop'});
-      });
-
-      // Then, change the status of PropertyEditor widgets of the currently
-      // active entity to 'candidate'.
-      $editableFieldsForEntity.each(function() {
-        $(this).createEditable('setState', 'candidate');
-      });
-
-      // Manage the page's tab indexes.
-    },
-
-    /**
-     * Accepts or reject editor (PropertyEditor) state changes.
-     *
-     * This is what ensures that the app is in control of what happens.
-     *
-     * @param from
-     *   The previous state.
-     * @param to
-     *   The new state.
-     * @param predicate
-     *   The predicate of the property for which the state change is happening.
-     * @param context
-     *   The context that is trying to trigger the state change.
-     * @param callback
-     *   The callback function that should receive the state acceptance result.
-     */
-    acceptEditorStateChange: function(from, to, predicate, context, callback) {
-      var accept = true;
-
-      // If the app is in view mode, then reject all state changes except for
-      // those to 'inactive'.
-      if (context && context.reason === 'stop') {
-        if (from === 'candidate' && to === 'inactive') {
-          accept = true;
-        }
-      }
-      // Handling of edit mode state changes is more granular.
-      else {
-        // In general, enforce the states sequence. Disallow going back from a
-        // "later" state to an "earlier" state, except in explicitly allowed
-        // cases.
-        if (_.indexOf(this.states, from) > _.indexOf(this.states, to)) {
-          accept = false;
-          // Allow: activating/active -> candidate.
-          // Necessary to stop editing a property.
-          if (_.indexOf(this.activeEditorStates, from) !== -1 && to === 'candidate') {
-            accept = true;
-          }
-          // Allow: changed/invalid -> candidate.
-          // Necessary to stop editing a property when it is changed or invalid.
-          else if ((from === 'changed' || from === 'invalid') && to === 'candidate') {
-            accept = true;
-          }
-          // Allow: highlighted -> candidate.
-          // Necessary to stop highlighting a property.
-          else if (from === 'highlighted' && to === 'candidate') {
-            accept = true;
-          }
-          // Allow: saved -> candidate.
-          // Necessary when successfully saved a property.
-          else if (from === 'saved' && to === 'candidate') {
-            accept = true;
-          }
-          // Allow: invalid -> saving.
-          // Necessary to be able to save a corrected, invalid property.
-          else if (from === 'invalid' && to === 'saving') {
-            accept = true;
-          }
-        }
-
-        // If it's not against the general principle, then here are more
-        // disallowed cases to check.
-        if (accept) {
-          // Ensure only one editor (field) at a time may be higlighted or active.
-          if (from === 'candidate' && _.indexOf(this.singleEditorStates, to) !== -1) {
-            if (this.model.get('highlightedEditor') || this.model.get('activeEditor')) {
-              accept = false;
-            }
-          }
-          // Reject going from activating/active to candidate because of a
-          // mouseleave.
-          else if (_.indexOf(this.activeEditorStates, from) !== -1 && to === 'candidate') {
-            if (context && context.reason === 'mouseleave') {
-              accept = false;
-            }
-          }
-          // When attempting to stop editing a changed/invalid property, ask for
-          // confirmation.
-          else if ((from === 'changed' || from === 'invalid') && to === 'candidate') {
-            if (context && context.reason === 'mouseleave') {
-              accept = false;
-            }
-            else {
-              // Check whether the transition has been confirmed?
-              if (context && context.confirmed) {
-                accept = true;
-              }
-              // Confirm this transition.
-              else {
-                // The callback will be called from the helper function.
-                this._confirmStopEditing(callback);
-                return;
-              }
-            }
-          }
-        }
-      }
-
-      callback(accept);
-    },
-
-    /**
-     * Asks the user to confirm whether he wants to stop editing via a modal.
-     *
-     * @param acceptCallback
-     *   The callback function as passed to acceptEditorStateChange(). This
-     *   callback function will be called with the user's choice.
-     *
-     * @see acceptEditorStateChange()
-     */
-    _confirmStopEditing: function(acceptCallback) {
-      // Only instantiate if there isn't a modal instance visible yet.
-      if (!this.model.get('activeModal')) {
-        var that = this;
-        var modal = new Drupal.edit.views.ModalView({
-          model: this.model,
-          message: Drupal.t('You have unsaved changes'),
-          buttons: [
-            { action: 'discard', classes: 'gray-button', label: Drupal.t('Discard changes') },
-            { action: 'save', type: 'submit', classes: 'blue-button', label: Drupal.t('Save') }
-          ],
-          callback: function(action) {
-            // The active modal has been removed.
-            that.model.set('activeModal', null);
-            if (action === 'discard') {
-              acceptCallback(true);
-            }
-            else {
-              acceptCallback(false);
-              var editor = that.model.get('activeEditor');
-              editor.options.widget.setState('saving', editor.options.property);
-            }
-          }
-        });
-        this.model.set('activeModal', modal);
-        // The modal will set the activeModal property on the model when rendering
-        // to prevent multiple modals from being instantiated.
-        modal.render();
-      }
-      else {
-        // Reject as there is still an open transition waiting for confirmation.
-        acceptCallback(false);
-      }
-    },
-
-    /**
-     * Reacts to editor (PropertyEditor) state changes; tracks global state.
-     *
-     * @param from
-     *   The previous state.
-     * @param to
-     *   The new state.
-     * @param editor
-     *   The PropertyEditor widget object.
-     */
-    editorStateChange: function(from, to, editor) {
-      // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133)
-      // Get rid of this once that issue is solved.
-      if (!editor) {
-        return;
-      }
-      else {
-        editor.stateChange(from, to);
-      }
-
-      // Keep track of the highlighted editor in the global state.
-      if (_.indexOf(this.singleEditorStates, to) !== -1 && this.model.get('highlightedEditor') !== editor) {
-        this.model.set('highlightedEditor', editor);
-      }
-      else if (this.model.get('highlightedEditor') === editor && to === 'candidate') {
-        this.model.set('highlightedEditor', null);
-      }
-
-      // Keep track of the active editor in the global state.
-      if (_.indexOf(this.activeEditorStates, to) !== -1 && this.model.get('activeEditor') !== editor) {
-        this.model.set('activeEditor', editor);
-      }
-      else if (this.model.get('activeEditor') === editor && to === 'candidate') {
-        // Discarded if it transitions from a changed state to 'candidate'.
-        if (from === 'changed' || from === 'invalid') {
-          // Retrieve the storage widget from DOM.
-          var createStorageWidget = this.$el.data('DrupalCreateStorage');
-          // Revert changes in the model, this will trigger the direct editable
-          // content to be reset and redrawn.
-          createStorageWidget.revertChanges(editor.options.entity);
-        }
-        this.model.set('activeEditor', null);
-      }
-
-      // Propagate the state change to the decoration and toolbar views.
-      // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133)
-      // Uncomment this once that issue is solved.
-      // editor.decorationView.stateChange(from, to);
-      // editor.toolbarView.stateChange(from, to);
-    },
-
-    /**
-     * Decorates an editor (PropertyEditor).
-     *
-     * Upon the page load, all appropriate editors are initialized and decorated
-     * (i.e. even before anything of the editing UI becomes visible; even before
-     * edit mode is enabled).
-     *
-     * @param editor
-     *   The PropertyEditor widget object.
-     */
-    decorateEditor: function(editor) {
-      // Toolbars are rendered "on-demand" (highlighting or activating).
-      // They are a sibling element before the editor's DOM element.
-      editor.toolbarView = new Drupal.edit.views.ToolbarView({
-        editor: editor,
-        $storageWidgetEl: this.$el
-      });
-
-      // Decorate the editor's DOM element depending on its state.
-      editor.decorationView = new Drupal.edit.views.PropertyEditorDecorationView({
-        el: editor.element,
-        editor: editor,
-        toolbarId: editor.toolbarView.getId()
-      });
-
-      // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133)
-      // Get rid of this once that issue is solved.
-      editor.options.widget.element.on('createeditablestatechange', function(event, data) {
-        editor.decorationView.stateChange(data.previous, data.current);
-        editor.toolbarView.stateChange(data.previous, data.current);
-      });
-    },
-
-    /**
-     * Undecorates an editor (PropertyEditor).
-     *
-     * Whenever a property has been updated, the old HTML will be replaced by
-     * the new (re-rendered) HTML. The EditableEntity widget will be destroyed,
-     * as will be the PropertyEditor widget. This method ensures Edit's editor
-     * views also are removed properly.
-     *
-     * @param editor
-     *   The PropertyEditor widget object.
-     */
-    undecorateEditor: function(editor) {
-      editor.toolbarView.undelegateEvents();
-      editor.toolbarView.remove();
-      delete editor.toolbarView;
-      editor.decorationView.undelegateEvents();
-      // Don't call .remove() on the decoration view, because that would remove
-      // a potentially rerendered field.
-      delete editor.decorationView;
-    }
-
-  });
-
-})(jQuery, _, Backbone, Drupal, VIE);
diff --git a/core/modules/edit/js/backbone.drupalform.js b/core/modules/edit/js/backbone.drupalform.js
deleted file mode 100644
index 750d16f6d90f9f6d697bf5148bbc5642a5a32daf..0000000000000000000000000000000000000000
--- a/core/modules/edit/js/backbone.drupalform.js
+++ /dev/null
@@ -1,184 +0,0 @@
-/**
- * @file
- * Backbone.sync implementation for Edit. This is the beating heart.
- */
-(function (jQuery, Backbone, Drupal) {
-
-"use strict";
-
-Backbone.defaultSync = Backbone.sync;
-Backbone.sync = function(method, model, options) {
-  if (options.editor.options.editorName === 'form') {
-    return Backbone.syncDrupalFormWidget(method, model, options);
-  }
-  else {
-    return Backbone.syncDirect(method, model, options);
-  }
-};
-
-/**
- * Performs syncing for "form" PredicateEditor widgets.
- *
- * Implemented on top of Form API and the AJAX commands framework. Sets up
- * scoped AJAX command closures specifically for a given PredicateEditor widget
- * (which contains a pre-existing form). By submitting the form through
- * Drupal.ajax and leveraging Drupal.ajax' ability to have scoped (per-instance)
- * command implementations, we are able to update the VIE model, re-render the
- * form when there are validation errors and ensure no Drupal.ajax memory leaks.
- *
- * @see Drupal.edit.util.form
- */
-Backbone.syncDrupalFormWidget = function(method, model, options) {
-  if (method === 'update') {
-    var predicate = options.editor.options.property;
-
-    var $formContainer = options.editor.$formContainer;
-    var $submit = $formContainer.find('.edit-form-submit');
-    var base = $submit.attr('id');
-
-    // Successfully saved.
-    Drupal.ajax[base].commands.editFieldFormSaved = function(ajax, response, status) {
-      Drupal.edit.util.form.unajaxifySaving(jQuery(ajax.element));
-
-      // Call Backbone.sync's success callback with the rerendered field.
-      var changedAttributes = {};
-      // @todo: POSTPONED_ON(Drupal core, http://drupal.org/node/1784216)
-      // Once full JSON-LD support in Drupal core lands, we can ensure that the
-      // models that VIE maintains are properly updated.
-      changedAttributes[predicate] = undefined;
-      changedAttributes[predicate + '/rendered'] = response.data;
-      options.success(changedAttributes);
-    };
-
-    // Unsuccessfully saved; validation errors.
-    Drupal.ajax[base].commands.editFieldFormValidationErrors = function(ajax, response, status) {
-      // Call Backbone.sync's error callback with the validation error messages.
-      options.error(response.data);
-    };
-
-    // The edit_field_form AJAX command is only called upon loading the form for
-    // the first time, and when there are validation errors in the form; Form
-    // API then marks which form items have errors. Therefor, we have to replace
-    // the existing form, unbind the existing Drupal.ajax instance and create a
-    // new Drupal.ajax instance.
-    Drupal.ajax[base].commands.editFieldForm = function(ajax, response, status) {
-      Drupal.edit.util.form.unajaxifySaving(jQuery(ajax.element));
-
-      Drupal.ajax.prototype.commands.insert(ajax, {
-        data: response.data,
-        selector: '#' + $formContainer.attr('id') + ' form'
-      });
-
-      // Create a Drupa.ajax instance for the re-rendered ("new") form.
-      var $newSubmit = $formContainer.find('.edit-form-submit');
-      Drupal.edit.util.form.ajaxifySaving({ nocssjs: false }, $newSubmit);
-    };
-
-    // Click the form's submit button; the scoped AJAX commands above will
-    // handle the server's response.
-    $submit.trigger('click.edit');
-  }
-};
-
-/**
-* Performs syncing for "direct" PredicateEditor widgets.
- *
- * @see Backbone.syncDrupalFormWidget()
- * @see Drupal.edit.util.form
- */
-Backbone.syncDirect = function(method, model, options) {
-  if (method === 'update') {
-    var fillAndSubmitForm = function(value) {
-      var $form = jQuery('#edit_backstage form');
-        // Fill in the value in any <input> that isn't hidden or a submit button.
-      $form.find(':input[type!="hidden"][type!="submit"]:not(select)')
-          // Don't mess with the node summary.
-          .not('[name$="\[summary\]"]').val(value);
-        // Submit the form.
-      $form.find('.edit-form-submit').trigger('click.edit');
-    };
-    var entity = options.editor.options.entity;
-    var predicate = options.editor.options.property;
-    var value = model.get(predicate);
-
-    // If form doesn't already exist, load it and then submit.
-    if (jQuery('#edit_backstage form').length === 0) {
-      var formOptions = {
-        propertyID: Drupal.edit.util.calcPropertyID(entity, predicate),
-        $editorElement: options.editor.element,
-        nocssjs: true
-      };
-      Drupal.edit.util.form.load(formOptions, function(form, ajax) {
-        // Create a backstage area for storing forms that are hidden from view
-        // (hence "backstage" — since the editing doesn't happen in the form, it
-        // happens "directly" in the content, the form is only used for saving).
-        jQuery(Drupal.theme('editBackstage', { id: 'edit_backstage' })).appendTo('body');
-        // Direct forms are stuffed into #edit_backstage, apparently.
-        jQuery('#edit_backstage').append(form);
-        // Disable the browser's HTML5 validation; we only care about server-
-        // side validation. (Not disabling this will actually cause problems
-        // because browsers don't like to set HTML5 validation errors on hidden
-        // forms.)
-        jQuery('#edit_backstage form').prop('novalidate', true);
-        var $submit = jQuery('#edit_backstage form .edit-form-submit');
-        var base = Drupal.edit.util.form.ajaxifySaving(formOptions, $submit);
-
-        // Successfully saved.
-        Drupal.ajax[base].commands.editFieldFormSaved = function (ajax, response, status) {
-          Backbone.syncDirectCleanUp();
-
-          // Call Backbone.sync's success callback with the rerendered field.
-          var changedAttributes = {};
-          // @todo: POSTPONED_ON(Drupal core, http://drupal.org/node/1784216)
-          // Once full JSON-LD support in Drupal core lands, we can ensure that the
-          // models that VIE maintains are properly updated.
-          changedAttributes[predicate] = jQuery(response.data).find('.field-item').html();
-          changedAttributes[predicate + '/rendered'] = response.data;
-          options.success(changedAttributes);
-        };
-
-        // Unsuccessfully saved; validation errors.
-        Drupal.ajax[base].commands.editFieldFormValidationErrors = function(ajax, response, status) {
-          // Call Backbone.sync's error callback with the validation error messages.
-          options.error(response.data);
-        };
-
-        // The editFieldForm AJAX command is only called upon loading the form
-        // for the first time, and when there are validation errors in the form;
-        // Form API then marks which form items have errors. This is useful for
-        // "form" editors, but pointless for "direct" editors: the form itself
-        // won't be visible at all anyway! Therefor, we ignore the new form and
-        // we continue to use the existing form.
-        Drupal.ajax[base].commands.editFieldForm = function(ajax, response, status) {
-          // no-op
-        };
-
-        fillAndSubmitForm(value);
-      });
-    }
-    else {
-      fillAndSubmitForm(value);
-    }
-  }
-};
-
-/**
- * Cleans up the hidden form that Backbone.syncDirect uses for syncing.
- *
- * This is called automatically by Backbone.syncDirect when saving is successful
- * (i.e. when there are no validation errors). Only when editing is canceled
- * while a PropertyEditor widget is in the invalid state, this must be called
- * "manually" (in practice, ToolbarView does this). This is necessary because
- * Backbone.syncDirect is not aware of the application state, it only does the
- * syncing.
- * An alternative could be to also remove the hidden form when validation errors
- * occur, but then the form must be retrieved again, thus resulting in another
- * roundtrip, which is bad for front-end performance.
- */
-Backbone.syncDirectCleanUp = function() {
-  var $submit = jQuery('#edit_backstage form .edit-form-submit');
-  Drupal.edit.util.form.unajaxifySaving($submit);
-  jQuery('#edit_backstage form').remove();
-};
-
-})(jQuery, Backbone, Drupal);
diff --git a/core/modules/edit/js/createjs/editable.js b/core/modules/edit/js/createjs/editable.js
deleted file mode 100644
index 1316023b9e05af1e32d9558b88dfff368aac1952..0000000000000000000000000000000000000000
--- a/core/modules/edit/js/createjs/editable.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * @file
- * Determines which editor (Create.js PropertyEditor widget) to use.
- */
-(function (jQuery, Drupal, drupalSettings) {
-
-"use strict";
-
-  jQuery.widget('Drupal.createEditable', jQuery.Midgard.midgardEditable, {
-    _create: function() {
-      this.vie = this.options.vie;
-
-      this.options.domService = 'edit';
-      this.options.predicateSelector = '*'; //'.edit-field.edit-allowed';
-
-      // The Create.js PropertyEditor widget configuration is not hardcoded; it
-      // is generated by the server.
-      this.options.propertyEditorWidgetsConfiguration = drupalSettings.edit.editors;
-
-      jQuery.Midgard.midgardEditable.prototype._create.call(this);
-    },
-
-    _propertyEditorName: function(data) {
-      // Pick a PropertyEditor widget for a property depending on its metadata.
-      var propertyID = Drupal.edit.util.calcPropertyID(data.entity, data.property);
-      return Drupal.edit.metadataCache[propertyID].editor;
-    }
-  });
-
-})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js b/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js
deleted file mode 100644
index cde6163730a0dace7cc4d4eaffeaa228d094d1ff..0000000000000000000000000000000000000000
--- a/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * @file
- * Override of Create.js' default "base" (plain contentEditable) widget.
- */
-(function (jQuery, Drupal) {
-
-"use strict";
-
-  // @todo D8: use jQuery UI Widget bridging.
-  // @see http://drupal.org/node/1874934#comment-7124904
-  jQuery.widget('Midgard.direct', jQuery.Midgard.editWidget, {
-
-    /**
-     * Implements getEditUISettings() method.
-     */
-    getEditUISettings: function() {
-      return { padding: true, unifiedToolbar: false, fullWidthToolbar: false };
-    },
-
-    /**
-     * Implements jQuery UI widget factory's _init() method.
-     *
-     * @todo: POSTPONED_ON(Create.js, https://github.com/bergie/create/issues/142)
-     * Get rid of this once that issue is solved.
-     */
-    _init: function() {},
-
-    /**
-     * Implements Create's _initialize() method.
-     */
-    _initialize: function() {
-      var that = this;
-
-      // Sets the state to 'changed' whenever the content has changed.
-      var before = jQuery.trim(this.element.text());
-      this.element.on('keyup paste', function (event) {
-        if (that.options.disabled) {
-          return;
-        }
-        var current = jQuery.trim(that.element.text());
-        if (before !== current) {
-          before = current;
-          that.options.changed(current);
-        }
-      });
-    },
-
-    /**
-     * Makes this PropertyEditor widget react to state changes.
-     */
-    stateChange: function(from, to) {
-      switch (to) {
-        case 'inactive':
-          break;
-        case 'candidate':
-          if (from !== 'inactive') {
-            // Removes the "contenteditable" attribute.
-            this.disable();
-          }
-          break;
-        case 'highlighted':
-          break;
-        case 'activating':
-          this.options.activated();
-          break;
-        case 'active':
-          // Sets the "contenteditable" attribute to "true".
-          this.enable();
-          break;
-        case 'changed':
-          break;
-        case 'saving':
-          break;
-        case 'saved':
-          break;
-        case 'invalid':
-          break;
-      }
-    }
-
-  });
-
-})(jQuery, Drupal);
diff --git a/core/modules/edit/js/createjs/editingWidgets/formwidget.js b/core/modules/edit/js/createjs/editingWidgets/formwidget.js
deleted file mode 100644
index aa2dd0abda8b35a07d64fc4543ef61e57db5ecb9..0000000000000000000000000000000000000000
--- a/core/modules/edit/js/createjs/editingWidgets/formwidget.js
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
- * @file
- * Form-based Create.js widget for structured content in Drupal.
- */
-(function ($, Drupal) {
-
-"use strict";
-
-  // @todo D8: change the name to "form" + use jQuery UI Widget bridging.
-  // @see http://drupal.org/node/1874934#comment-7124904
-  $.widget('Midgard.formEditEditor', $.Midgard.editWidget, {
-
-    id: null,
-    $formContainer: null,
-
-    /**
-     * Implements getEditUISettings() method.
-     */
-    getEditUISettings: function() {
-      return { padding: false, unifiedToolbar: false, fullWidthToolbar: false };
-    },
-
-    /**
-     * Implements jQuery UI widget factory's _init() method.
-     *
-     * @todo: POSTPONED_ON(Create.js, https://github.com/bergie/create/issues/142)
-     * Get rid of this once that issue is solved.
-     */
-    _init: function() {},
-
-    /**
-     * Implements Create's _initialize() method.
-     */
-    _initialize: function() {},
-
-    /**
-     * Makes this PropertyEditor widget react to state changes.
-     */
-    stateChange: function(from, to) {
-      switch (to) {
-        case 'inactive':
-          break;
-        case 'candidate':
-          if (from !== 'inactive') {
-            this.disable();
-          }
-          break;
-        case 'highlighted':
-          break;
-        case 'activating':
-          this.enable();
-          break;
-        case 'active':
-          break;
-        case 'changed':
-          break;
-        case 'saving':
-          break;
-        case 'saved':
-          break;
-        case 'invalid':
-          break;
-      }
-    },
-
-    /**
-     * Enables the widget.
-     */
-    enable: function () {
-      var $editorElement = $(this.options.widget.element);
-      var propertyID = Drupal.edit.util.calcPropertyID(this.options.entity, this.options.property);
-
-      // Generate a DOM-compatible ID for the form container DOM element.
-      this.id = 'edit-form-for-' + propertyID.replace(/\//g, '_');
-
-      // Render form container.
-      this.$formContainer = $(Drupal.theme('editFormContainer', {
-        id: this.id,
-        loadingMsg: Drupal.t('Loading…')}
-      ));
-      this.$formContainer
-        .find('.edit-form')
-        .addClass('edit-editable edit-highlighted edit-editing')
-        .attr('role', 'dialog');
-
-      // Insert form container in DOM.
-      if ($editorElement.css('display') === 'inline') {
-        // @todo: POSTPONED_ON(Drupal core, title/author/date as Entity Properties)
-        // This is untested in Drupal 8, because in Drupal 8 we don't yet
-        // have the ability to edit the node title/author/date, because they
-        // haven't been converted into Entity Properties yet, and they're the
-        // only examples in core of "display: inline" properties.
-        this.$formContainer.prependTo($editorElement.offsetParent());
-
-        var pos = $editorElement.position();
-        this.$formContainer.css('left', pos.left).css('top', pos.top);
-      }
-      else {
-        this.$formContainer.insertBefore($editorElement);
-      }
-
-      // Load form, insert it into the form container and attach event handlers.
-      var widget = this;
-      var formOptions = {
-        propertyID: propertyID,
-        $editorElement: $editorElement,
-        nocssjs: false
-      };
-      Drupal.edit.util.form.load(formOptions, function(form, ajax) {
-        Drupal.ajax.prototype.commands.insert(ajax, {
-          data: form,
-          selector: '#' + widget.id + ' .placeholder'
-        });
-
-        var $submit = widget.$formContainer.find('.edit-form-submit');
-        Drupal.edit.util.form.ajaxifySaving(formOptions, $submit);
-        widget.$formContainer
-          .on('formUpdated.edit', ':input', function () {
-            // Sets the state to 'changed'.
-            widget.options.changed();
-          })
-          .on('keypress.edit', 'input', function (event) {
-            if (event.keyCode === 13) {
-              return false;
-            }
-          });
-
-        // Sets the state to 'activated'.
-        widget.options.activated();
-      });
-    },
-
-    /**
-     * Disables the widget.
-     */
-    disable: function () {
-      if (this.$formContainer === null) {
-        return;
-      }
-
-      Drupal.edit.util.form.unajaxifySaving(this.$formContainer.find('.edit-form-submit'));
-      // Allow form widgets to detach properly.
-      Drupal.detachBehaviors(this.$formContainer, null, 'unload');
-      this.$formContainer
-        .off('change.edit', ':input')
-        .off('keypress.edit', 'input')
-        .remove();
-      this.$formContainer = null;
-    }
-  });
-
-})(jQuery, Drupal);
diff --git a/core/modules/edit/js/createjs/storage.js b/core/modules/edit/js/createjs/storage.js
deleted file mode 100644
index 580ff82002f079de73fd2618ba5474014fbc8322..0000000000000000000000000000000000000000
--- a/core/modules/edit/js/createjs/storage.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * @file
- * Subclasses jQuery.Midgard.midgardStorage to have consistent namespaces.
- */
-(function(jQuery) {
-
-"use strict";
-
-  jQuery.widget('Drupal.createStorage', jQuery.Midgard.midgardStorage, {});
-
-})(jQuery);
diff --git a/core/modules/edit/js/edit.js b/core/modules/edit/js/edit.js
index 61157c4df9690d12f4569fccf52bb3b6b01bac00..689912537975078f1b8030644ac5814a304f0f6c 100644
--- a/core/modules/edit/js/edit.js
+++ b/core/modules/edit/js/edit.js
@@ -1,103 +1,112 @@
 /**
  * @file
  * Attaches behavior for the Edit module.
+ *
+ * Everything happens asynchronously, to allow for:
+ *   - dynamically rendered contextual links
+ *   - asynchronously retrieved (and cached) per-field in-place editing metadata
+ *   - asynchronous setup of in-place editable field and "Quick edit" link
+ *
+ * To achieve this, there are several queues:
+ *   - fieldsMetadataQueue: fields whose metadata still needs to be fetched.
+ *   - fieldsAvailableQueue: queue of fields whose metadata is known, and for
+ *     which it has been confirmed that the user has permission to edit them.
+ *     However, FieldModels will only be created for them once there's a
+ *     contextual link for their entity: when it's possible to initiate editing.
+ *   - contextualLinksQueue: queue of contextual links on entities for which it
+ *     is not yet known whether the user has permission to edit at >=1 of them.
  */
 (function ($, _, Backbone, Drupal, drupalSettings) {
 
 "use strict";
 
-Drupal.edit = { metadataCache: {}, contextualLinksQueue: [] };
+var options = $.extend({
+  strings: {
+    quickEdit: Drupal.t('Quick edit'),
+    stopQuickEdit: Drupal.t('Stop quick edit')
+  }
+}, drupalSettings.edit);
+
+/**
+ * Tracks fields without metadata. Contains objects with the following keys:
+ *   - DOM el
+ *   - String fieldID
+ */
+var fieldsMetadataQueue = [];
 
 /**
- * Attach toggling behavior and in-place editing.
+ * Tracks fields ready for use. Contains objects with the following keys:
+ *   - DOM el
+ *   - String fieldID
+ *   - String entityID
  */
+var fieldsAvailableQueue = [];
+
+/**
+ * Tracks contextual links on entities. Contains objects with the following
+ * keys:
+ *   - String entityID
+ *   - DOM el
+ *   - DOM region
+ */
+var contextualLinksQueue = [];
+
 Drupal.behaviors.edit = {
   attach: function (context) {
-    var $context = $(context);
-    var $fields = $context.find('[data-edit-id]');
-
-    // Initialize the Edit app.
-    $('body').once('edit-init', Drupal.edit.init);
-
-    function annotateField (field) {
-      var hasField = _.has(Drupal.edit.metadataCache, field.editID);
-      if (hasField) {
-        var meta = Drupal.edit.metadataCache[field.editID];
-
-        field.$el.addClass((meta.access) ? 'edit-allowed' : 'edit-disallowed');
-        if (meta.access) {
-          field.$el
-            .attr('data-edit-field-label', meta.label)
-            .attr('aria-label', meta.aria)
-            .addClass('edit-field edit-type-' + ((meta.editor === 'form') ? 'form' : 'direct'));
-        }
-      }
-      return hasField;
-    }
+    // Initialize the Edit app once per page load.
+    $('body').once('edit-init', initEdit);
 
-    // Find all fields in the context without metadata.
-    var fieldsToAnnotate = _.map($fields.not('.edit-allowed, .edit-disallowed'), function (el) {
-      var $el = $(el);
-      return { $el: $el, editID: $el.attr('data-edit-id') };
+    // Process each field element: queue to be used or to fetch metadata.
+    $(context).find('[data-edit-id]').once('edit').each(function (index, fieldElement) {
+      processField(fieldElement);
     });
 
-    // Fields whose metadata is known (typically when they were just modified)
-    // can be annotated immediately, those remaining must be requested.
-    var remainingFieldsToAnnotate = _.reduce(fieldsToAnnotate, function (result, field) {
-      if (!annotateField(field)) {
-        result.push(field);
-      }
-      return result;
-    }, []);
-
-    // Make fields that could be annotated immediately available for editing.
-    Drupal.edit.app.findEditableProperties($context);
-
-    if (remainingFieldsToAnnotate.length) {
-      $(window).ready(function () {
-        var id = 'edit-load-metadata';
-        // Create a temporary element to be able to use Drupal.ajax.
-        var $el = jQuery('<div id="' + id + '" class="element-hidden"></div>').appendTo('body');
-        // Create a Drupal.ajax instance to load the form.
-        Drupal.ajax[id] = new Drupal.ajax(id, $el, {
-          url: drupalSettings.edit.metadataURL,
-          event: 'edit-internal.edit',
-          submit: { 'fields[]': _.pluck(remainingFieldsToAnnotate, 'editID') },
-          // No progress indicator.
-          progress: { type: null }
-        });
-        // Implement a scoped editMetaData AJAX command: calls the callback.
-        Drupal.ajax[id].commands.editMetadata = function (ajax, response, status) {
-          // Update the metadata cache.
-          _.each(response.data, function (metadata, editID) {
-            Drupal.edit.metadataCache[editID] = metadata;
-          });
-
-          // Annotate the remaining fields based on the updated access cache.
-          _.each(remainingFieldsToAnnotate, annotateField);
-
-          // Find editable fields, make them editable.
-          Drupal.edit.app.findEditableProperties($context);
-
-          // Metadata cache has been updated, try to set up more contextual
-          // links now.
-          Drupal.edit.contextualLinksQueue = _.filter(Drupal.edit.contextualLinksQueue, function (data) {
-            return !Drupal.edit.setUpContextualLink(data);
-          });
-
-          // Delete the Drupal.ajax instance that called this very function.
-          delete Drupal.ajax[id];
-
-          // Also delete the temporary element.
-          // $el.remove();
-        };
-        // This will ensure our scoped editMetadata AJAX command gets called.
-        $el.trigger('edit-internal.edit');
+    // Fetch metadata for any fields that are queued to retrieve it.
+    fetchMissingMetadata(function (fieldElementsWithFreshMetadata) {
+      // Metadata has been fetched, reprocess fields whose metadata was missing.
+      _.each(fieldElementsWithFreshMetadata, processField);
+
+      // Metadata has been fetched, try to set up more contextual links now.
+      contextualLinksQueue = _.filter(contextualLinksQueue, function (contextualLink) {
+        return !initializeEntityContextualLink(contextualLink);
       });
+    });
+  },
+  detach: function (context, settings, trigger) {
+    if (trigger === 'unload') {
+      deleteContainedModelsAndQueues($(context));
     }
   }
 };
 
+Drupal.edit = {
+  // A Drupal.edit.AppView instance.
+  app: null,
+
+  collections: {
+    // All in-place editable entities (Drupal.edit.EntityModel) on the page.
+    entities: null,
+    // All in-place editable fields (Drupal.edit.FieldModel) on the page.
+    fields: null
+  },
+
+  // In-place editors will register themselves in this object.
+  editors: {},
+
+  // Per-field metadata that indicates whether in-place editing is allowed,
+  // which in-place editor should be used, etc.
+  metadata: {
+    has: function (fieldID) { return _.has(this.data, fieldID); },
+    add: function (fieldID, metadata) { this.data[fieldID] = metadata; },
+    get: function (fieldID, key) {
+      return (key === undefined) ? this.data[fieldID] : this.data[fieldID][key];
+    },
+    intersection: function (fieldIDs) { return _.intersection(fieldIDs, _.keys(this.data)); },
+    // Contains the actual metadata, keyed by field ID.
+    data: {}
+  }
+};
+
 /**
  * Detect contextual links on entities annotated by Edit; queue these to be
  * processed.
@@ -105,27 +114,166 @@ Drupal.behaviors.edit = {
 $(document).on('drupalContextualLinkAdded', function (event, data) {
   if (data.$region.is('[data-edit-entity]')) {
     var contextualLink = {
-      entity: data.$region.attr('data-edit-entity'),
-      $el: data.$el,
-      $region: data.$region
+      entityID: data.$region.attr('data-edit-entity'),
+      el: data.$el[0],
+      region: data.$region[0]
     };
     // Set up contextual links for this, otherwise queue it to be set up later.
-    if (!Drupal.edit.setUpContextualLink(contextualLink)) {
-      Drupal.edit.contextualLinksQueue.push(contextualLink);
+    if (!initializeEntityContextualLink(contextualLink)) {
+      contextualLinksQueue.push(contextualLink);
     }
   }
 });
 
 /**
- * Attempts to set up a "Quick edit" contextual link.
+ * Extracts the entity ID from a field ID.
+ *
+ * @param String fieldID
+ *   A field ID: a string of the format
+ *   `<entity type>/<id>/<field name>/<language>/<view mode>`.
+ * @return String
+ *   An entity ID: a string of the format `<entity type>/<id>`.
+ */
+function extractEntityID (fieldID) {
+  return fieldID.split('/').slice(0, 2).join('/');
+}
+
+/**
+ * Initialize the Edit app.
+ *
+ * @param DOM bodyElement
+ *   This document's body element.
+ */
+function initEdit (bodyElement) {
+  Drupal.edit.collections.entities = new Drupal.edit.EntityCollection();
+  Drupal.edit.collections.fields = new Drupal.edit.FieldCollection();
+
+  // Instantiate AppModel (application state) and AppView, which is the
+  // controller of the whole in-place editing experience.
+  Drupal.edit.app = new Drupal.edit.AppView({
+    el: bodyElement,
+    model: new Drupal.edit.AppModel(),
+    entitiesCollection: Drupal.edit.collections.entities,
+    fieldsCollection: Drupal.edit.collections.fields
+  });
+}
+
+/**
+ * Fetch the field's metadata; queue or initialize it (if EntityModel exists).
+ *
+ * @param DOM fieldElement
+ *   A Drupal Field API field's DOM element with a data-edit-id attribute.
+ */
+function processField (fieldElement) {
+  var metadata = Drupal.edit.metadata;
+  var fieldID = fieldElement.getAttribute('data-edit-id');
+
+  // Early-return if metadata for this field is mising.
+  if (!metadata.has(fieldID)) {
+    fieldsMetadataQueue.push({ el: fieldElement, fieldID: fieldID });
+    return;
+  }
+  // Early-return if the user is not allowed to in-place edit this field.
+  if (metadata.get(fieldID, 'access') !== true) {
+    return;
+  }
+
+  // If an EntityModel for this field already exists (and hence also a "Quick
+  // edit" contextual link), then initialize it immediately.
+  var entityID = extractEntityID(fieldID);
+  if (Drupal.edit.collections.entities.where({ id: entityID }).length > 0) {
+    initializeField(fieldElement, fieldID);
+  }
+  // Otherwise: queue the field. It is now available to be set up when its
+  // corresponding entity becomes in-place editable.
+  else {
+    fieldsAvailableQueue.push({ el: fieldElement, fieldID: fieldID, entityID: entityID });
+  }
+}
+
+/**
+ * Initialize a field; create FieldModel.
+ *
+ * @param DOM fieldElement
+ *   The field's DOM element.
+ * @param String fieldID
+ *   The field's ID.
+ */
+function initializeField (fieldElement, fieldID) {
+  var entityId = extractEntityID(fieldID);
+  var entity = Drupal.edit.collections.entities.where({ id: entityId })[0];
+
+  // @todo Refactor CSS to get rid of this.
+  $(fieldElement).addClass('edit-field');
+
+  // The FieldModel stores the state of an in-place editable entity field.
+  var field = new Drupal.edit.FieldModel({
+    el: fieldElement,
+    id: fieldID,
+    entity: entity,
+    metadata: Drupal.edit.metadata.get(fieldID),
+    acceptStateChange: _.bind(Drupal.edit.app.acceptEditorStateChange, Drupal.edit.app)
+  });
+
+  // Track all fields on the page.
+  Drupal.edit.collections.fields.add(field);
+}
+
+/**
+ * Fetches metadata for fields whose metadata is missing.
+ *
+ * Fields whose metadata is missing are tracked at fieldsMetadataQueue.
+ *
+ * @param Function callback
+ *   A callback function that receives field elements whose metadata will just
+ *   have been fetched.
+ */
+function fetchMissingMetadata (callback) {
+  if (fieldsMetadataQueue.length) {
+    var fieldIDs = _.pluck(fieldsMetadataQueue, 'fieldID');
+    var fieldElementsWithoutMetadata = _.pluck(fieldsMetadataQueue, 'el');
+    fieldsMetadataQueue = [];
+
+    $(window).ready(function () {
+      var id = 'edit-load-metadata';
+      // Create a temporary element to be able to use Drupal.ajax.
+      var $el = $('<div id="' + id + '" class="element-hidden"></div>').appendTo('body');
+      // Create a Drupal.ajax instance to load the form.
+      Drupal.ajax[id] = new Drupal.ajax(id, $el, {
+        url: drupalSettings.edit.metadataURL,
+        event: 'edit-internal.edit',
+        submit: { 'fields[]': fieldIDs },
+        // No progress indicator.
+        progress: { type: null }
+      });
+      // Implement a scoped editMetaData AJAX command: calls the callback.
+      Drupal.ajax[id].commands.editMetadata = function (ajax, response, status) {
+        // Store the metadata.
+        _.each(response.data, function (fieldMetadata, fieldID) {
+          Drupal.edit.metadata.add(fieldID, fieldMetadata);
+        });
+        // Clean-up.
+        delete Drupal.ajax[id];
+        $el.remove();
+
+        callback(fieldElementsWithoutMetadata);
+      };
+      // This will ensure our scoped editMetadata AJAX command gets called.
+      $el.trigger('edit-internal.edit');
+    });
+  }
+}
+
+/**
+ * Attempts to set up a "Quick edit" link and corresponding EntityModel.
  *
  * @param Object contextualLink
  *   An object with the following properties:
- *     - entity: an Edit entity identifier, e.g. "node/1" or "custom_block/5".
- *     - $el: a jQuery element pointing to the contextual links for this entity.
- *     - $region: a jQuery element pointing to the contextual region for this
+ *     - String entity: an Edit entity identifier, e.g. "node/1" or
+ *       "custom_block/5".
+ *     - jQuery $el: element pointing to the contextual links for this entity.
+ *     - jQuery $region: element pointing to the contextual region for this
  *       entity.
- *
  * @return Boolean
  *   Returns true when a contextual the given contextual link metadata can be
  *   removed from the queue (either because the contextual link has been set up
@@ -133,63 +281,118 @@ $(document).on('drupalContextualLinkAdded', function (event, data) {
  *   its fields).
  *   Returns false otherwise.
  */
-Drupal.edit.setUpContextualLink = function (contextualLink) {
+function initializeEntityContextualLink (contextualLink) {
+  var metadata = Drupal.edit.metadata;
   // Check if the user has permission to edit at least one of them.
-  function hasFieldWithPermission (editIDs) {
-    var i, meta = Drupal.edit.metadataCache;
-    for (i = 0; i < editIDs.length; i++) {
-      var editID = editIDs[i];
-      if (_.has(meta, editID) && meta[editID].access === true) {
+  function hasFieldWithPermission (fieldIDs) {
+    for (var i = 0; i < fieldIDs.length; i++) {
+      var fieldID = fieldIDs[i];
+      if (metadata.get(fieldID, 'access') === true) {
         return true;
       }
     }
     return false;
   }
 
-  // Checks if the metadata for all given editIDs exists.
-  function allMetadataExists (editIDs) {
-    var editIDsWithMetadata = _.intersection(editIDs, _.keys(Drupal.edit.metadataCache));
-    return editIDs.length === editIDsWithMetadata.length;
+  // Checks if the metadata for all given field IDs exists.
+  function allMetadataExists (fieldIDs) {
+    return fieldIDs.length === metadata.intersection(fieldIDs).length;
   }
 
-  // Find the Edit IDs of all fields within this entity.
-  var editIDs = [];
-  contextualLink.$region
-    .find('[data-edit-id^="' + contextualLink.entity + '/"]')
-    .each(function () {
-      editIDs.push($(this).attr('data-edit-id'));
-    });
+  // Find all fields for this entity and collect their field IDs.
+  var fields = _.where(fieldsAvailableQueue, { entityID: contextualLink.entityID });
+  var fieldIDs = _.pluck(fields, 'fieldID');
 
+  // No fields found yet.
+  if (fieldIDs.length === 0) {
+    return false;
+  }
   // The entity for the given contextual link contains at least one field that
-  // the current user may edit in-place; instantiate ContextualLinkView.
-  if (hasFieldWithPermission(editIDs)) {
-    new Drupal.edit.views.ContextualLinkView({
-      el: $('<li class="quick-edit"><a href=""></a></li>').prependTo(contextualLink.$el),
-      model: Drupal.edit.app.model,
-      entity: contextualLink.entity
+  // the current user may edit in-place; instantiate EntityModel and
+  // ContextualLinkView.
+  else if (hasFieldWithPermission(fieldIDs)) {
+    var entityModel = new Drupal.edit.EntityModel({
+      el: contextualLink.region,
+      id: contextualLink.entityID
+    });
+    Drupal.edit.collections.entities.add(entityModel);
+
+    // Initialize all queued fields within this entity (creates FieldModels).
+    _.each(fields, function (field) {
+      initializeField(field.el, field.fieldID);
     });
+    fieldsAvailableQueue = _.difference(fieldsAvailableQueue, fields);
+
+    // Set up contextual link view.
+    var contextualLinkView = new Drupal.edit.ContextualLinkView($.extend({
+      el: $('<li class="quick-edit"><a href=""></a></li>').prependTo(contextualLink.el),
+      model: entityModel,
+      appModel: Drupal.edit.app.model
+    }, options));
+    entityModel.set('contextualLinkView', contextualLinkView);
+
     return true;
   }
   // There was not at least one field that the current user may edit in-place,
   // even though the metadata for all fields within this entity is available.
-  else if (allMetadataExists(editIDs)) {
+  else if (allMetadataExists(fieldIDs)) {
     return true;
   }
 
   return false;
-};
+}
 
-Drupal.edit.init = function () {
-  // Instantiate EditAppView, which is the controller of it all. EditAppModel
-  // instance tracks global state (viewing/editing in-place).
-  var appModel = new Drupal.edit.models.EditAppModel();
-  // For now, we work with a singleton app, because for Drupal.behaviors to be
-  // able to discover new editable properties that get AJAXed in, it must know
-  // with which app instance they should be associated.
-  Drupal.edit.app = new Drupal.edit.EditAppView({
-    el: $('body'),
-    model: appModel
+/**
+ * Delete models and queue items that are contained within a given context.
+ *
+ * Deletes any contained EntityModels (plus their associated FieldModels and
+ * ContextualLinkView) and FieldModels, as well as the corresponding queues.
+ *
+ * After EntityModels, FieldModels must also be deleted, because it is possible
+ * in Drupal for a field DOM element to exist outside of the entity DOM element,
+ * e.g. when viewing the full node, the title of the node is not rendered within
+ * the node (the entity) but as the page title.
+ *
+ * Note: this will not delete an entity that is actively being in-place edited.
+ *
+ * @param jQuery $context
+ *   The context within which to delete.
+ */
+function deleteContainedModelsAndQueues($context) {
+  $context.find('[data-edit-entity]').addBack('[data-edit-entity]').each(function (index, entityElement) {
+    // Delete entity model.
+    // @todo change to findWhere() as soon as we have Backbone 1.0 in Drupal
+    // core.
+    var entityModels = Drupal.edit.collections.entities.where({el: entityElement});
+    if (entityModels.length) {
+      // @todo Make this cleaner; let EntityModel.destroy() do this?
+      var contextualLinkView = entityModels[0].get('contextualLinkView');
+      contextualLinkView.undelegateEvents();
+      contextualLinkView.remove();
+
+      entityModels[0].destroy();
+    }
+
+    // Filter queue.
+    function hasOtherRegion (contextualLink) {
+      return contextualLink.region !== entityElement;
+    }
+    contextualLinksQueue = _.filter(contextualLinksQueue, hasOtherRegion);
   });
-};
+
+  $context.find('[data-edit-id]').addBack('[data-edit-id]').each(function (index, fieldElement) {
+    // Delete field models.
+    Drupal.edit.collections.fields.chain()
+      .filter(function (fieldModel) { return fieldModel.get('el') === fieldElement; })
+      .invoke('destroy');
+
+    // Filter queues.
+    function hasOtherFieldElement (field) {
+      return field.el !== fieldElement;
+    }
+    fieldsMetadataQueue = _.filter(fieldsMetadataQueue, hasOtherFieldElement);
+    fieldsAvailableQueue = _.filter(fieldsAvailableQueue, hasOtherFieldElement);
+  });
+}
 
 })(jQuery, _, Backbone, Drupal, drupalSettings);
diff --git a/core/modules/edit/js/editors/directEditor.js b/core/modules/edit/js/editors/directEditor.js
new file mode 100644
index 0000000000000000000000000000000000000000..aafbca3c07790100344bf63e20f299f8863cfba8
--- /dev/null
+++ b/core/modules/edit/js/editors/directEditor.js
@@ -0,0 +1,107 @@
+/**
+ * @file
+ * contentEditable-based in-place editor for plain text content.
+ */
+(function ($, _, Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit.editors.direct = Drupal.edit.EditorView.extend({
+
+  // Stores the textual DOM element that is being in-place edited.
+  $textElement: null,
+
+  /**
+   * {@inheritdoc}
+   */
+  initialize: function (options) {
+    Drupal.edit.EditorView.prototype.initialize.call(this, options);
+
+    var editorModel = this.model;
+    var fieldModel = this.fieldModel;
+
+    // Store the original value of this field. Necessary for reverting changes.
+    var $textElement = this.$textElement = this.$el.find('.field-item:first');
+    editorModel.set('originalValue', $.trim(this.$textElement.text()));
+
+    // Sets the state to 'changed' whenever the value changes
+    var previousText = editorModel.get('originalValue');
+    $textElement.on('keyup paste', function (event) {
+      var currentText = $.trim($textElement.text());
+      if (previousText !== currentText) {
+        previousText = currentText;
+        editorModel.set('currentValue', currentText);
+        fieldModel.set('state', 'changed');
+      }
+    });
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  getEditedElement: function () {
+    return this.$textElement;
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  stateChange: function (fieldModel, state) {
+    var from = fieldModel.previous('state');
+    var to = state;
+    switch (to) {
+      case 'inactive':
+        break;
+      case 'candidate':
+        if (from !== 'inactive') {
+          this.$textElement.removeAttr('contentEditable');
+        }
+        if (from === 'invalid') {
+          this.removeValidationErrors();
+        }
+        break;
+      case 'highlighted':
+        break;
+      case 'activating':
+        // Defer updating the field model until the current state change has
+        // propagated, to not trigger a nested state change event.
+        _.defer(function () {
+          fieldModel.set('state', 'active');
+        });
+        break;
+      case 'active':
+        this.$textElement.attr('contentEditable', 'true');
+        break;
+      case 'changed':
+        break;
+      case 'saving':
+        if (from === 'invalid') {
+          this.removeValidationErrors();
+        }
+        this.save();
+        break;
+      case 'saved':
+        break;
+      case 'invalid':
+        this.showValidationErrors();
+        break;
+    }
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  getEditUISettings: function () {
+    return { padding: true, unifiedToolbar: false, fullWidthToolbar: false };
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  revert: function () {
+    this.$textElement.html(this.model.get('originalValue'));
+  }
+
+});
+
+})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/edit/js/editors/formEditor.js b/core/modules/edit/js/editors/formEditor.js
new file mode 100644
index 0000000000000000000000000000000000000000..e14384058076c6edd182eb2c3e32d79985d318f4
--- /dev/null
+++ b/core/modules/edit/js/editors/formEditor.js
@@ -0,0 +1,190 @@
+/**
+ * @file
+ * Form-based in-place editor. Works for any field type.
+ */
+(function ($, Drupal) {
+
+"use strict";
+
+Drupal.edit.editors.form = Drupal.edit.EditorView.extend({
+
+  // Tracks the form container DOM element that is used while in-place editing.
+  $formContainer: null,
+
+  /**
+   * {@inheritdoc}
+   */
+  stateChange: function (fieldModel, state) {
+    var from = fieldModel.previous('state');
+    var to = state;
+    switch (to) {
+      case 'inactive':
+        break;
+      case 'candidate':
+        if (from !== 'inactive') {
+          this.removeForm();
+        }
+        if (from === 'invalid') {
+          // No need to call removeValidationErrors() for this in-place editor!
+        }
+        break;
+      case 'highlighted':
+        break;
+      case 'activating':
+        this.loadForm();
+        break;
+      case 'active':
+        break;
+      case 'changed':
+        break;
+      case 'saving':
+        this.save();
+        break;
+      case 'saved':
+        break;
+      case 'invalid':
+        this.showValidationErrors();
+        break;
+    }
+  },
+
+  /**
+   * Loads the form for this field, displays it on top of the actual field.
+   */
+  loadForm: function () {
+    var fieldModel = this.fieldModel;
+
+    // Generate a DOM-compatible ID for the form container DOM element.
+    var id = 'edit-form-for-' + fieldModel.id.replace(/\//g, '_');
+
+    // Render form container.
+    var $formContainer = this.$formContainer = $(Drupal.theme('editFormContainer', {
+      id: id,
+      loadingMsg: Drupal.t('Loading…')}
+    ));
+    $formContainer
+      .find('.edit-form')
+      .addClass('edit-editable edit-highlighted edit-editing')
+      .attr('role', 'dialog');
+
+    // Insert form container in DOM.
+    if (this.$el.css('display') === 'inline') {
+      $formContainer.prependTo(this.$el.offsetParent());
+      // Position the form container to render on top of the field's element.
+      var pos = this.$el.position();
+      $formContainer.css('left', pos.left).css('top', pos.top);
+    }
+    else {
+      $formContainer.insertBefore(this.$el);
+    }
+
+    // Load form, insert it into the form container and attach event handlers.
+    var formOptions = {
+      fieldID: fieldModel.id,
+      $el: this.$el,
+      nocssjs: false
+    };
+    Drupal.edit.util.form.load(formOptions, function (form, ajax) {
+      Drupal.ajax.prototype.commands.insert(ajax, {
+        data: form,
+        selector: '#' + id + ' .placeholder'
+      });
+
+      var $submit = $formContainer.find('.edit-form-submit');
+      Drupal.edit.util.form.ajaxifySaving(formOptions, $submit);
+      $formContainer
+        .on('formUpdated.edit', ':input', function () {
+          fieldModel.set('state', 'changed');
+        })
+        .on('keypress.edit', 'input', function (event) {
+          if (event.keyCode === 13) {
+            return false;
+          }
+        });
+
+      // The in-place editor has loaded; change state to 'active'.
+      fieldModel.set('state', 'active');
+    });
+  },
+
+  /**
+   * Removes the form for this field and detaches behaviors and event handlers.
+   */
+  removeForm: function () {
+    if (this.$formContainer === null) {
+      return;
+    }
+
+    Drupal.edit.util.form.unajaxifySaving(this.$formContainer.find('.edit-form-submit'));
+    // Allow form widgets to detach properly.
+    Drupal.detachBehaviors(this.$formContainer, null, 'unload');
+    this.$formContainer
+      .off('change.edit', ':input')
+      .off('keypress.edit', 'input')
+      .remove();
+    this.$formContainer = null;
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  save: function () {
+    var $formContainer = this.$formContainer;
+    var $submit = $formContainer.find('.edit-form-submit');
+    var base = $submit.attr('id');
+    var editorModel = this.model;
+    var fieldModel = this.fieldModel;
+
+    // Successfully saved.
+    Drupal.ajax[base].commands.editFieldFormSaved = function (ajax, response, status) {
+      Drupal.edit.util.form.unajaxifySaving($(ajax.element));
+
+      // First, transition the state to 'saved'.
+      fieldModel.set('state', 'saved');
+      // Then, set the 'html' attribute on the field model. This will cause the
+      // field to be rerendered.
+      fieldModel.set('html', response.data);
+     };
+
+    // Unsuccessfully saved; validation errors.
+    Drupal.ajax[base].commands.editFieldFormValidationErrors = function (ajax, response, status) {
+      editorModel.set('validationErrors', response.data);
+      fieldModel.set('state', 'invalid');
+    };
+
+    // The edit_field_form AJAX command is only called upon loading the form for
+    // the first time, and when there are validation errors in the form; Form
+    // API then marks which form items have errors. Therefor, we have to replace
+    // the existing form, unbind the existing Drupal.ajax instance and create a
+    // new Drupal.ajax instance.
+    Drupal.ajax[base].commands.editFieldForm = function (ajax, response, status) {
+      Drupal.edit.util.form.unajaxifySaving($(ajax.element));
+
+      Drupal.ajax.prototype.commands.insert(ajax, {
+        data: response.data,
+        selector: '#' + $formContainer.attr('id') + ' form'
+      });
+
+      // Create a Drupal.ajax instance for the re-rendered ("new") form.
+      var $newSubmit = $formContainer.find('.edit-form-submit');
+      Drupal.edit.util.form.ajaxifySaving({ nocssjs: false }, $newSubmit);
+    };
+
+    // Click the form's submit button; the scoped AJAX commands above will
+    // handle the server's response.
+    $submit.trigger('click.edit');
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  showValidationErrors: function () {
+    this.$formContainer
+      .find('.edit-form')
+      .addClass('edit-validation-error')
+      .find('form')
+      .prepend(this.model.get('validationErrors'));
+  }
+});
+
+})(jQuery, Drupal);
diff --git a/core/modules/edit/js/models/AppModel.js b/core/modules/edit/js/models/AppModel.js
new file mode 100644
index 0000000000000000000000000000000000000000..2489949adcf4b3cfded14c9b05b847c632ce7327
--- /dev/null
+++ b/core/modules/edit/js/models/AppModel.js
@@ -0,0 +1,15 @@
+(function ($, _, Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit.AppModel = Backbone.Model.extend({
+  defaults: {
+    highlightedEditor: null,
+    activeEditor: null,
+    // Reference to a ModalView-instance if a state change requires
+    // confirmation.
+    activeModal: null
+  }
+});
+
+}(jQuery, _, Backbone, Drupal));
diff --git a/core/modules/edit/js/models/EditorModel.js b/core/modules/edit/js/models/EditorModel.js
new file mode 100644
index 0000000000000000000000000000000000000000..bb48c0a365ae7f4debb85fdf5c8db566f8bdd004
--- /dev/null
+++ b/core/modules/edit/js/models/EditorModel.js
@@ -0,0 +1,23 @@
+(function (Backbone, Drupal) {
+
+"use strict";
+
+/**
+ * State of an in-place editor.
+ */
+Drupal.edit.EditorModel = Backbone.Model.extend({
+
+  defaults: {
+    // Not the full HTML representation of this field, but the "actual"
+    // original value of the field, stored by the used in-place editor, and
+    // in a representation that can be chosen by the in-place editor.
+    originalValue: null,
+    // Analogous to originalValue, but the current value.
+    currentValue: null,
+    // Stores any validation errors to be rendered.
+    validationErrors: null
+  }
+
+});
+
+}(Backbone, Drupal));
diff --git a/core/modules/edit/js/models/EntityModel.js b/core/modules/edit/js/models/EntityModel.js
new file mode 100644
index 0000000000000000000000000000000000000000..e484b35c177a6da4c7fcf8f1b1160687f842fcb3
--- /dev/null
+++ b/core/modules/edit/js/models/EntityModel.js
@@ -0,0 +1,61 @@
+(function (Backbone, Drupal) {
+
+"use strict";
+
+/**
+ * State of an in-place editable entity in the DOM.
+ */
+Drupal.edit.EntityModel = Backbone.Model.extend({
+
+  defaults: {
+    // The DOM element that represents this entity. It may seem bizarre to
+    // have a DOM element in a Backbone Model, but we need to be able to map
+    // entities in the DOM to EntityModels in memory.
+    el: null,
+    // An entity ID, of the form "<entity type>/<entity ID>", e.g. "node/1".
+    id: null,
+    // A Drupal.edit.FieldCollection for all fields of this entity.
+    fields: null,
+
+    // The attributes below are stateful. The ones above will never change
+    // during the life of a EntityModel instance.
+
+    // Indicates whether this instance of this entity is currently being
+    // edited in-place.
+    isActive: false
+  },
+
+  /**
+   * @inheritdoc
+   */
+  initialize: function () {
+    this.set('fields', new Drupal.edit.FieldCollection());
+  },
+
+  /**
+   * @inheritdoc
+   */
+  destroy: function (options) {
+    Backbone.Model.prototype.destroy.apply(this, options);
+
+    // Destroy all fields of this entity.
+    this.get('fields').each(function (fieldModel) {
+      fieldModel.destroy();
+    });
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  sync: function () {
+    // We don't use REST updates to sync.
+    return;
+  }
+
+});
+
+Drupal.edit.EntityCollection = Backbone.Collection.extend({
+  model: Drupal.edit.EntityModel
+});
+
+}(Backbone, Drupal));
diff --git a/core/modules/edit/js/models/FieldModel.js b/core/modules/edit/js/models/FieldModel.js
new file mode 100644
index 0000000000000000000000000000000000000000..dc02eaba853affdf6c97dccc1d1bfba53a39fafc
--- /dev/null
+++ b/core/modules/edit/js/models/FieldModel.js
@@ -0,0 +1,132 @@
+(function (_, Backbone, Drupal) {
+
+"use strict";
+
+/**
+ * State of an in-place editable field in the DOM.
+ */
+Drupal.edit.FieldModel = Backbone.Model.extend({
+
+  defaults: {
+    // The DOM element that represents this field. It may seem bizarre to have
+    // a DOM element in a Backbone Model, but we need to be able to map fields
+    // in the DOM to FieldModels in memory.
+    el: null,
+    // A field ID, of the form
+    // "<entity type>/<id>/<field name>/<language>/<view mode>", e.g.
+    // "node/1/field_tags/und/full".
+    id: null,
+    // A Drupal.edit.EntityModel. Its "fields" attribute, which is a
+    // FieldCollection, is automatically updated to include this FieldModel.
+    entity: null,
+    // This field's metadata as returned by the EditController::metadata().
+    metadata: null,
+    // Callback function for validating changes between states. Receives the
+    // previous state, new state, context, and a callback
+    acceptStateChange: null,
+
+    // The attributes below are stateful. The ones above will never change
+    // during the life of a FieldModel instance.
+
+    // In-place editing state of this field. Defaults to the initial state.
+    // Possible values: @see Drupal.edit.FieldModel.states.
+    state: 'inactive',
+    // The full HTML representation of this field (with the element that has
+    // the data-edit-id as the outer element). Used to propagate changes from
+    // this field instance to other instances of the same field.
+    html: null
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  initialize: function (options) {
+    // Store the original full HTML representation of this field.
+    this.set('html', options.el.outerHTML);
+
+    // Enlist field automatically in the associated entity's field collection.
+    this.get('entity').get('fields').add(this);
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  destroy: function (options) {
+    if (this.get('state') !== 'inactive') {
+      throw new Error("FieldModel cannot be destroyed if it is not inactive state.");
+    }
+    Backbone.Model.prototype.destroy.apply(this, options);
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  sync: function () {
+    // We don't use REST updates to sync.
+    return;
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  validate: function (attrs, options) {
+    // We only care about validating the 'state' attribute.
+    if (!_.has(attrs, 'state')) {
+      return;
+    }
+
+    var current = this.get('state');
+    var next = attrs.state;
+    if (current !== next) {
+      // Ensure it's a valid state.
+      if (_.indexOf(this.constructor.states, next) === -1) {
+        return '"' + next + '" is an invalid state';
+      }
+      // Check if the acceptStateChange callback accepts it.
+      if (!this.get('acceptStateChange')(current, next, options)) {
+        return 'state change not accepted';
+      }
+    }
+  },
+
+  /**
+   * Extracts the entity ID from this field's ID.
+   *
+   * @return String
+   *   An entity ID: a string of the format `<entity type>/<id>`.
+   */
+  getEntityID: function () {
+    return this.id.split('/').slice(0, 2).join('/');
+  }
+
+}, {
+
+  /**
+   * A list (sequence) of all possible states a field can be in during in-place
+   * editing.
+   */
+  states: [
+    'inactive', 'candidate', 'highlighted',
+    'activating', 'active', 'changed', 'saving', 'saved', 'invalid'
+  ],
+
+  /**
+   * Indicates whether the 'from' state comes before the 'to' state.
+   *
+   * @param String from
+   *   One of Drupal.edit.FieldModel.states.
+   * @param String to
+   *   One of Drupal.edit.FieldModel.states.
+   * @return Boolean
+   */
+  followsStateSequence: function (from, to) {
+    return _.indexOf(this.states, from) < _.indexOf(this.states, to);
+  }
+
+});
+
+Drupal.edit.FieldCollection = Backbone.Collection.extend({
+  model: Drupal.edit.FieldModel
+});
+
+}(_, Backbone, Drupal));
diff --git a/core/modules/edit/js/models/edit-app-model.js b/core/modules/edit/js/models/edit-app-model.js
deleted file mode 100644
index 0c90fd005191fabe6076583587752f96f985338e..0000000000000000000000000000000000000000
--- a/core/modules/edit/js/models/edit-app-model.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * @file
- * A Backbone Model that models the current Edit application state.
- */
-(function(Backbone, Drupal) {
-
-"use strict";
-
-Drupal.edit = Drupal.edit || {};
-Drupal.edit.models = Drupal.edit.models || {};
-Drupal.edit.models.EditAppModel = Backbone.Model.extend({
-  defaults: {
-    activeEntity: null,
-    highlightedEditor: null,
-    activeEditor: null,
-    // Reference to a ModalView-instance if a transition requires confirmation.
-    activeModal: null
-  }
-});
-
-})(Backbone, Drupal);
diff --git a/core/modules/edit/js/theme.js b/core/modules/edit/js/theme.js
index 7bef55385e907976c3a6f4bf00d1f3cf154af1bb..3e6e2500f5b79b955abb0dac1b784c3a968c723b 100644
--- a/core/modules/edit/js/theme.js
+++ b/core/modules/edit/js/theme.js
@@ -2,35 +2,20 @@
  * @file
  * Provides overridable theme functions for all of Edit's client-side HTML.
  */
-(function($, Drupal) {
+(function ($, Drupal) {
 
 "use strict";
 
-/**
- * Theme function for the overlay of the Edit module.
- *
- * @param settings
- *   An object with the following keys:
- *   - None.
- * @return
- *   The corresponding HTML.
- */
-Drupal.theme.editOverlay = function(settings) {
-  var html = '';
-  html += '<div id="edit_overlay" />';
-  return html;
-};
-
 /**
  * Theme function for a "backstage" for the Edit module.
  *
- * @param settings
+ * @param Object settings
  *   An object with the following keys:
- *   - id: the id to apply to the backstage.
- * @return
+ *   - String id: the id to apply to the backstage.
+ * @return String
  *   The corresponding HTML.
  */
-Drupal.theme.editBackstage = function(settings) {
+Drupal.theme.editBackstage = function (settings) {
   var html = '';
   html += '<div id="' + settings.id + '" />';
   return html;
@@ -39,13 +24,10 @@ Drupal.theme.editBackstage = function(settings) {
 /**
  * Theme function for a modal of the Edit module.
  *
- * @param settings
- *   An object with the following keys:
- *   - None.
- * @return
+ * @return String
  *   The corresponding HTML.
  */
-Drupal.theme.editModal = function(settings) {
+Drupal.theme.editModal = function () {
   var classes = 'edit-animate-slow edit-animate-invisible edit-animate-delay-veryfast';
   var html = '';
   html += '<div id="edit_modal" class="' + classes + '" role="dialog">';
@@ -60,11 +42,11 @@ Drupal.theme.editModal = function(settings) {
  *
  * @param settings
  *   An object with the following keys:
- *   - id: the id to apply to the toolbar container.
- * @return
+ *   - String id: the id to apply to the toolbar container.
+ * @return String
  *   The corresponding HTML.
  */
-Drupal.theme.editToolbarContainer = function(settings) {
+Drupal.theme.editToolbarContainer = function (settings) {
   var html = '';
   html += '<div id="' + settings.id + '" class="edit-toolbar-container">';
   html += '  <div class="edit-toolbar-heightfaker edit-animate-fast">';
@@ -77,15 +59,15 @@ Drupal.theme.editToolbarContainer = function(settings) {
 /**
  * Theme function for a toolbar toolgroup of the Edit module.
  *
- * @param settings
+ * @param Object settings
  *   An object with the following keys:
- *   - id: (optional) the id of the toolgroup
- *   - classes: the class of the toolgroup.
- *   - buttons: @see Drupal.theme.prototype.editButtons().
- * @return
+ *   - String id: (optional) the id of the toolgroup
+ *   - String classes: the class of the toolgroup.
+ *   - Array buttons: @see Drupal.theme.prototype.editButtons().
+ * @return String
  *   The corresponding HTML.
  */
-Drupal.theme.editToolgroup = function(settings) {
+Drupal.theme.editToolgroup = function (settings) {
   var classes = 'edit-toolgroup edit-animate-slow edit-animate-invisible edit-animate-delay-veryfast';
   var html = '';
   html += '<div class="' + classes + ' ' + settings.classes + '"';
@@ -103,17 +85,17 @@ Drupal.theme.editToolgroup = function(settings) {
  *
  * Can be used for the buttons both in the toolbar toolgroups and in the modal.
  *
- * @param settings
+ * @param Object settings
  *   An object with the following keys:
  *   - buttons: an array of objects with the following keys:
- *     - type: the type of the button (defaults to 'button')
- *     - classes: the classes of the button.
- *     - label: the label of the button.
- *     - action: sets a data-edit-modal-action attribute.
- * @return
+ *     - String type: the type of the button (defaults to 'button')
+ *     - Array classes: the classes of the button.
+ *     - String label: the label of the button.
+ *     - String action: sets a data-edit-modal-action attribute.
+ * @return String
  *   The corresponding HTML.
  */
-Drupal.theme.editButtons = function(settings) {
+Drupal.theme.editButtons = function (settings) {
   var html = '';
   for (var i = 0; i < settings.buttons.length; i++) {
     var button = settings.buttons[i];
@@ -133,14 +115,14 @@ Drupal.theme.editButtons = function(settings) {
 /**
  * Theme function for a form container of the Edit module.
  *
- * @param settings
+ * @param Object settings
  *   An object with the following keys:
- *   - id: the id to apply to the toolbar container.
- *   - loadingMsg: The message to show while loading.
- * @return
+ *   - String id: the id to apply to the toolbar container.
+ *   - String loadingMsg: The message to show while loading.
+ * @return String
  *   The corresponding HTML.
  */
-Drupal.theme.editFormContainer = function(settings) {
+Drupal.theme.editFormContainer = function (settings) {
   var html = '';
   html += '<div id="' + settings.id + '" class="edit-form-container">';
   html += '  <div class="edit-form">';
diff --git a/core/modules/edit/js/util.js b/core/modules/edit/js/util.js
index e0aa490715f7f131ce6a0640c539c8434937e4e7..ad7136ec049893517c25c02ef3e7737fd852a6de 100644
--- a/core/modules/edit/js/util.js
+++ b/core/modules/edit/js/util.js
@@ -2,48 +2,25 @@
  * @file
  * Provides utility functions for Edit.
  */
-(function($, _, Drupal, drupalSettings) {
+(function ($, _, Drupal, drupalSettings) {
 
 "use strict";
 
-Drupal.edit = Drupal.edit || {};
 Drupal.edit.util = Drupal.edit.util || {};
 
 Drupal.edit.util.constants = {};
 Drupal.edit.util.constants.transitionEnd = "transitionEnd.edit webkitTransitionEnd.edit transitionend.edit msTransitionEnd.edit oTransitionEnd.edit";
 
-Drupal.edit.util.calcPropertyID = function(entity, predicate) {
-  return entity.getSubjectUri() + '/' + predicate;
-};
-
 /**
- * Retrieves a setting of the editor-specific Edit UI integration.
- *
- * If the editor does not implement the optional getEditUISettings() method, or
- * if it doesn't set a value for a certain setting, then the default value will
- * be used.
+ * Converts a field id into a formatted url path.
  *
- * @param editor
- *   A Create.js PropertyEditor widget instance.
- * @param setting
- *   Name of the Edit UI integration setting.
- *
- * @return {*}
+ * @param String id
+ *   The id of an editable field. For example, 'node/1/body/und/full'.
+ * @param String urlFormat
+ *   The Controller route for field processing. For example,
+ *   '/edit/form/%21entity_type/%21id/%21field_name/%21langcode/%21view_mode'.
  */
-Drupal.edit.util.getEditUISetting = function(editor, setting) {
-  var settings = {};
-  var defaultSettings = {
-    padding: false,
-    unifiedToolbar: false,
-    fullWidthToolbar: false
-  };
-  if (typeof editor.getEditUISettings === 'function') {
-    settings = editor.getEditUISettings();
-  }
-  return _.extend(defaultSettings, settings)[setting];
-};
-
-Drupal.edit.util.buildUrl = function(id, urlFormat) {
+Drupal.edit.util.buildUrl = function (id, urlFormat) {
   var parts = id.split('/');
   return Drupal.formatString(decodeURIComponent(urlFormat), {
     '!entity_type': parts[0],
@@ -61,49 +38,52 @@ Drupal.edit.util.form = {
    * Leverages Drupal.ajax' ability to have scoped (per-instance) command
    * implementations to be able to call a callback.
    *
-   * @param options
+   * @param Object options
    *   An object with the following keys:
-   *    - $editorElement (required): the PredicateEditor DOM element.
-   *    - propertyID (required): the property ID that uniquely identifies the
-   *      property for which this form will be loaded.
-   *    - nocssjs (required): boolean indicating whether no CSS and JS should be
-   *      returned (necessary when the form is invisible to the user).
-   * @param callback
+   *    - jQuery $el: (required) DOM element necessary for Drupal.ajax to
+   *      perform AJAX commands.
+   *    - String fieldID: (required) the field ID that uniquely identifies the
+   *      field for which this form will be loaded.
+   *    - Boolean nocssjs: (required) boolean indicating whether no CSS and JS
+   *      should be returned (necessary when the form is invisible to the user).
+   * @param Function callback
    *   A callback function that will receive the form to be inserted, as well as
    *   the ajax object, necessary if the callback wants to perform other AJAX
    *   commands.
    */
-  load: function(options, callback) {
+  load: function (options, callback) {
+    var $el = options.$el;
+    var fieldID = options.fieldID;
+
     // Create a Drupal.ajax instance to load the form.
-    Drupal.ajax[options.propertyID] = new Drupal.ajax(options.propertyID, options.$editorElement, {
-      url: Drupal.edit.util.buildUrl(options.propertyID, drupalSettings.edit.fieldFormURL),
+    Drupal.ajax[fieldID] = new Drupal.ajax(fieldID, $el, {
+      url: Drupal.edit.util.buildUrl(fieldID, drupalSettings.edit.fieldFormURL),
       event: 'edit-internal.edit',
       submit: { nocssjs : options.nocssjs },
       progress: { type : null } // No progress indicator.
     });
     // Implement a scoped editFieldForm AJAX command: calls the callback.
-    Drupal.ajax[options.propertyID].commands.editFieldForm = function(ajax, response, status) {
+    Drupal.ajax[fieldID].commands.editFieldForm = function (ajax, response, status) {
       callback(response.data, ajax);
       // Delete the Drupal.ajax instance that called this very function.
-      delete Drupal.ajax[options.propertyID];
-      options.$editorElement.off('edit-internal.edit');
+      delete Drupal.ajax[fieldID];
+      $el.off('edit-internal.edit');
     };
     // This will ensure our scoped editFieldForm AJAX command gets called.
-    options.$editorElement.trigger('edit-internal.edit');
+    $el.trigger('edit-internal.edit');
   },
 
   /**
    * Creates a Drupal.ajax instance that is used to save a form.
    *
-   * @param options
+   * @param Object options
    *   An object with the following keys:
-   *    - nocssjs (required): boolean indicating whether no CSS and JS should be
+   *    - nocssjs: (required) boolean indicating whether no CSS and JS should be
    *      returned (necessary when the form is invisible to the user).
-   *
-   * @return
+   * @return String
    *   The key of the Drupal.ajax instance.
    */
-  ajaxifySaving: function(options, $submit) {
+  ajaxifySaving: function (options, $submit) {
     // Re-wire the form to handle submit.
     var element_settings = {
       url: $submit.closest('form').attr('action'),
@@ -131,10 +111,10 @@ Drupal.edit.util.form = {
   /**
    * Cleans up the Drupal.ajax instance that is used to save the form.
    *
-   * @param $submit
+   * @param jQuery $submit
    *   The jQuery-wrapped submit DOM element that should be unajaxified.
    */
-  unajaxifySaving: function($submit) {
+  unajaxifySaving: function ($submit) {
     delete Drupal.ajax[$submit.attr('id')];
     $submit.off('click.edit');
   }
diff --git a/core/modules/edit/js/viejs/EditService.js b/core/modules/edit/js/viejs/EditService.js
deleted file mode 100644
index 00cb04b380fb770c4dca262fd0cb07cb598995ad..0000000000000000000000000000000000000000
--- a/core/modules/edit/js/viejs/EditService.js
+++ /dev/null
@@ -1,289 +0,0 @@
-/**
- * @file
- * VIE DOM parsing service for Edit.
- */
-(function(jQuery, _, VIE, Drupal, drupalSettings) {
-
-"use strict";
-
-  VIE.prototype.EditService = function (options) {
-    var defaults = {
-      name: 'edit',
-      subjectSelector: '.edit-field.edit-allowed'
-    };
-    this.options = _.extend({}, defaults, options);
-
-    this.views = [];
-    this.vie = null;
-    this.name = this.options.name;
-  };
-
-  VIE.prototype.EditService.prototype = {
-    load: function (loadable) {
-      var correct = loadable instanceof this.vie.Loadable;
-      if (!correct) {
-        throw new Error('Invalid Loadable passed');
-      }
-
-      var element;
-      if (!loadable.options.element) {
-        if (typeof document === 'undefined') {
-          return loadable.resolve([]);
-        } else {
-          element = drupalSettings.edit.context;
-        }
-      } else {
-        element = loadable.options.element;
-      }
-
-      var entities = this.readEntities(element);
-      loadable.resolve(entities);
-    },
-
-    _getViewForElement:function (element, collectionView) {
-      var viewInstance;
-
-      jQuery.each(this.views, function () {
-        if (jQuery(this.el).get(0) === element.get(0)) {
-          if (collectionView && !this.template) {
-            return true;
-          }
-          viewInstance = this;
-          return false;
-        }
-      });
-      return viewInstance;
-    },
-
-    _registerEntityView:function (entity, element, isNew) {
-      if (!element.length) {
-        return;
-      }
-
-      // Let's only have this overhead for direct types. Form-based editors are
-      // handled in backbone.drupalform.js and the PropertyEditor instance.
-      if (jQuery(element).hasClass('edit-type-form')) {
-        return;
-      }
-
-      var service = this;
-      var viewInstance = this._getViewForElement(element);
-      if (viewInstance) {
-        return viewInstance;
-      }
-
-      viewInstance = new this.vie.view.Entity({
-        model:entity,
-        el:element,
-        tagName:element.get(0).nodeName,
-        vie:this.vie,
-        service:this.name
-      });
-
-      this.views.push(viewInstance);
-
-      return viewInstance;
-    },
-
-    save: function(saveable) {
-      var correct = saveable instanceof this.vie.Savable;
-      if (!correct) {
-        throw "Invalid Savable passed";
-      }
-
-      if (!saveable.options.element) {
-        // FIXME: we could find element based on subject
-        throw "Unable to write entity to edit.module-markup, no element given";
-      }
-
-      if (!saveable.options.entity) {
-        throw "Unable to write to edit.module-markup, no entity given";
-      }
-
-      var $element = jQuery(saveable.options.element);
-      this._writeEntity(saveable.options.entity, saveable.options.element);
-      saveable.resolve();
-    },
-
-    _writeEntity:function (entity, element) {
-      var service = this;
-      this.findPredicateElements(this.getElementSubject(element), element, true).each(function () {
-        var predicateElement = jQuery(this);
-        var predicate = service.getElementPredicate(predicateElement);
-        if (!entity.has(predicate)) {
-          return true;
-        }
-
-        var value = entity.get(predicate);
-        if (value && value.isCollection) {
-          // Handled by CollectionViews separately
-          return true;
-        }
-        if (value === service.readElementValue(predicate, predicateElement)) {
-          return true;
-        }
-        // Unlike in the VIE's RdfaService no (re-)mapping needed here.
-        predicateElement.html(value);
-      });
-      return true;
-    },
-
-    // The edit-id data attribute contains the full identifier of
-    // each entity element in the format
-    // `<entity type>:<id>:<field name>:<language code>:<view mode>`.
-    _getID: function (element) {
-      var id = jQuery(element).attr('data-edit-id');
-      if (!id) {
-        id = jQuery(element).closest('[data-edit-id]').attr('data-edit-id');
-      }
-      return id;
-    },
-
-    // Returns the "URI" of an entity of an element in format
-    // `<entity type>/<id>`.
-    getElementSubject: function (element) {
-      return this._getID(element).split('/').slice(0, 2).join('/');
-    },
-
-    // Returns the field name for an element in format
-    // `<field name>/<language code>/<view mode>`.
-    // (Slashes instead of colons because the field name is no namespace.)
-    getElementPredicate: function (element) {
-      if (!this._getID(element)) {
-        throw new Error('Could not find predicate for element');
-      }
-      return this._getID(element).split('/').slice(2, 5).join('/');
-    },
-
-    getElementType: function (element) {
-      return this._getID(element).split('/').slice(0, 1)[0];
-    },
-
-    // Reads all editable entities (currently each Drupal field is considered an
-    // entity, in the future Drupal entities should be mapped to VIE entities)
-    // from DOM and returns the VIE enties it found.
-    readEntities: function (element) {
-      var service = this;
-      var entities = [];
-      var entityElements = jQuery(this.options.subjectSelector, element);
-      entityElements = entityElements.add(jQuery(element).filter(this.options.subjectSelector));
-      entityElements.each(function () {
-        var entity = service._readEntity(jQuery(this));
-        if (entity) {
-          entities.push(entity);
-        }
-      });
-      return entities;
-    },
-
-    // Returns a filled VIE Entity instance for a DOM element. The Entity
-    // is also registered in the VIE entities collection.
-    _readEntity: function (element) {
-      var subject = this.getElementSubject(element);
-      var type = this.getElementType(element);
-      var entity = this._readEntityPredicates(subject, element, false);
-      if (jQuery.isEmptyObject(entity)) {
-        return null;
-      }
-      entity['@subject'] = subject;
-      if (type) {
-        entity['@type'] = this._registerType(type, element);
-      }
-
-      var entityInstance = new this.vie.Entity(entity);
-      entityInstance = this.vie.entities.addOrUpdate(entityInstance, {
-        updateOptions: {
-          silent: true,
-          ignoreChanges: true
-        }
-      });
-
-      this._registerEntityView(entityInstance, element);
-      return entityInstance;
-    },
-
-    _registerType: function (typeId, element) {
-      typeId = '<http://viejs.org/ns/' + typeId + '>';
-      var type = this.vie.types.get(typeId);
-      if (!type) {
-        this.vie.types.add(typeId, []);
-        type = this.vie.types.get(typeId);
-      }
-
-      var predicate = this.getElementPredicate(element);
-      if (type.attributes.get(predicate)) {
-        return type;
-      }
-      var range = predicate.split('/')[0];
-      type.attributes.add(predicate, [range], 0, 1, {
-        label: element.data('edit-field-label')
-      });
-
-      return type;
-    },
-
-    _readEntityPredicates: function (subject, element, emptyValues) {
-      var entityPredicates = {};
-      var service = this;
-      this.findPredicateElements(subject, element, true).each(function () {
-        var predicateElement = jQuery(this);
-        var predicate = service.getElementPredicate(predicateElement);
-        if (!predicate) {
-          return;
-        }
-        var value = service.readElementValue(predicate, predicateElement);
-        if (value === null && !emptyValues) {
-          return;
-        }
-
-        entityPredicates[predicate] = value;
-        entityPredicates[predicate + '/rendered'] = predicateElement[0].outerHTML;
-      });
-      return entityPredicates;
-    },
-
-    readElementValue : function(predicate, element) {
-      // Unlike in RdfaService there is parsing needed here.
-      if (element.hasClass('edit-type-form')) {
-        return undefined;
-      }
-      else {
-        return jQuery.trim(element.html());
-      }
-    },
-
-    // Subject elements are the DOM elements containing a single or multiple
-    // editable fields.
-    findSubjectElements: function (element) {
-      if (!element) {
-        element = drupalSettings.edit.context;
-      }
-      return jQuery(this.options.subjectSelector, element);
-    },
-
-    // Predicate Elements are the actual DOM elements that users will be able
-    // to edit.
-    findPredicateElements: function (subject, element, allowNestedPredicates, stop) {
-      var predicates = jQuery();
-      // Make sure that element is wrapped by jQuery.
-      var $element = jQuery(element);
-
-      // Form-type predicates
-      predicates = predicates.add($element.filter('.edit-type-form'));
-
-      // Direct-type predicates
-      var direct = $element.filter('.edit-type-direct');
-      predicates = predicates.add(direct.find('.field-item'));
-
-      if (!predicates.length && !stop) {
-        var parentElement = $element.parent(this.options.subjectSelector);
-        if (parentElement.length) {
-          return this.findPredicateElements(subject, parentElement, allowNestedPredicates, true);
-        }
-      }
-
-      return predicates;
-    }
-  };
-
-})(jQuery, _, VIE, Drupal, drupalSettings);
diff --git a/core/modules/edit/js/views/AppView.js b/core/modules/edit/js/views/AppView.js
new file mode 100644
index 0000000000000000000000000000000000000000..2e617f6d586e6d74ab4e9b220f9a132eac9d8a33
--- /dev/null
+++ b/core/modules/edit/js/views/AppView.js
@@ -0,0 +1,392 @@
+(function ($, _, Backbone, Drupal) {
+
+"use strict";
+
+/**
+ *
+ */
+Drupal.edit.AppView = Backbone.View.extend({
+
+  // Configuration for state handling.
+  activeEditorStates: [],
+  singleEditorStates: [],
+
+  /**
+   * {@inheritdoc}
+   *
+   * @param Object options
+   *   An object with the following keys:
+   *   - Drupal.edit.AppModel model: the application state model
+   *   - Drupal.edit.EntityCollection entitiesCollection: all on-page entities
+   *   - Drupal.edit.FieldCollection fieldsCollection: all on-page fields
+   */
+  initialize: function (options) {
+    // AppView's configuration for handling states.
+    // @see Drupal.edit.FieldModel.states
+    this.activeEditorStates = ['activating', 'active'];
+    this.singleEditorStates = ['highlighted', 'activating', 'active'];
+
+    options.entitiesCollection
+      // Track app state.
+      .on('change:isActive', this.appStateChange, this)
+      .on('change:isActive', this.enforceSingleActiveEntity, this);
+
+    options.fieldsCollection
+      // Track app state.
+      .on('change:state', this.editorStateChange, this)
+      // Respond to field model HTML representation change events.
+      .on('change:html', this.renderUpdatedField, this)
+      // Respond to addition.
+      .on('add', this.rerenderedFieldToCandidate, this)
+      // Respond to destruction.
+      .on('destroy', this.teardownEditor, this);
+  },
+
+  /**
+   * Handles setup/teardown and state changes when the active entity changes.
+   *
+   * @param Drupal.edit.EntityModel entityModel
+   *   An instance of the EntityModel class.
+   * @param Boolean isActive
+   *   A boolean that represents the changed active state of the entityModel.
+   */
+  appStateChange: function (entityModel, isActive) {
+    var app = this;
+    if (isActive) {
+      // Move all fields of this entity from the 'inactive' state to the
+      // 'candidate' state.
+      entityModel.get('fields').each(function (fieldModel) {
+        // First, set up editors; they must be notified of state changes.
+        app.setupEditor(fieldModel);
+        // Second, change the field's state.
+        fieldModel.set('state', 'candidate');
+      });
+    }
+    else {
+      // Move all fields of this entity from whatever state they are in to
+      // the 'inactive' state.
+      entityModel.get('fields').each(function (fieldModel) {
+        // First, change the field's state.
+        fieldModel.set('state', 'inactive', { reason: 'stop' });
+        // Second, tear down editors.
+        app.teardownEditor(fieldModel);
+      });
+    }
+  },
+
+  /**
+   * Accepts or reject editor (Editor) state changes.
+   *
+   * This is what ensures that the app is in control of what happens.
+   *
+   * @param String from
+   *   The previous state.
+   * @param String to
+   *   The new state.
+   * @param null|Object context
+   *   The context that is trying to trigger the state change.
+   * @param Function callback
+   *   The callback function that should receive the state acceptance result.
+   */
+  acceptEditorStateChange: function (from, to, context, callback) {
+    var accept = true;
+
+    // If the app is in view mode, then reject all state changes except for
+    // those to 'inactive'.
+    if (context && (context.reason === 'stop' || context.reason === 'rerender')) {
+      if (from === 'candidate' && to === 'inactive') {
+        accept = true;
+      }
+    }
+    // Handling of edit mode state changes is more granular.
+    else {
+      // In general, enforce the states sequence. Disallow going back from a
+      // "later" state to an "earlier" state, except in explicitly allowed
+      // cases.
+      if (!Drupal.edit.FieldModel.followsStateSequence(from, to)) {
+        accept = false;
+        // Allow: activating/active -> candidate.
+        // Necessary to stop editing a property.
+        if (_.indexOf(this.activeEditorStates, from) !== -1 && to === 'candidate') {
+          accept = true;
+        }
+        // Allow: changed/invalid -> candidate.
+        // Necessary to stop editing a property when it is changed or invalid.
+        else if ((from === 'changed' || from === 'invalid') && to === 'candidate') {
+          accept = true;
+        }
+        // Allow: highlighted -> candidate.
+        // Necessary to stop highlighting a property.
+        else if (from === 'highlighted' && to === 'candidate') {
+          accept = true;
+        }
+        // Allow: saved -> candidate.
+        // Necessary when successfully saved a property.
+        else if (from === 'saved' && to === 'candidate') {
+          accept = true;
+        }
+        // Allow: invalid -> saving.
+        // Necessary to be able to save a corrected, invalid property.
+        else if (from === 'invalid' && to === 'saving') {
+          accept = true;
+        }
+      }
+
+      // If it's not against the general principle, then here are more
+      // disallowed cases to check.
+      if (accept) {
+        // Ensure only one editor (field) at a time may be higlighted or active.
+        if (from === 'candidate' && _.indexOf(this.singleEditorStates, to) !== -1) {
+          if (this.model.get('highlightedEditor') || this.model.get('activeEditor')) {
+            accept = false;
+          }
+        }
+        // Reject going from activating/active to candidate because of a
+        // mouseleave.
+        else if (_.indexOf(this.activeEditorStates, from) !== -1 && to === 'candidate') {
+          if (context && context.reason === 'mouseleave') {
+            accept = false;
+          }
+        }
+        // When attempting to stop editing a changed/invalid property, ask for
+        // confirmation.
+        else if ((from === 'changed' || from === 'invalid') && to === 'candidate') {
+          if (context && context.reason === 'mouseleave') {
+            accept = false;
+          }
+          else {
+            // Check whether the transition has been confirmed?
+            if (context && context.confirmed) {
+              accept = true;
+            }
+            // Confirm this transition.
+            else {
+              // Do not accept this change right now, instead open a modal
+              // that will ask the user to confirm his choice.
+              accept = false;
+              // The callback will be called from the helper function.
+              this._confirmStopEditing(callback);
+            }
+          }
+        }
+      }
+    }
+
+    return accept;
+  },
+
+  /**
+   * Sets up the in-place editor for the given field.
+   *
+   * Must happen before the fieldModel's state is changed to 'candidate'.
+   *
+   * @param Drupal.edit.FieldModel fieldModel
+   *   The field for which an in-place editor must be set up.
+   */
+  setupEditor: function (fieldModel) {
+    // Create in-place editor.
+    var editorName = fieldModel.get('metadata').editor;
+    var editorModel = new Drupal.edit.EditorModel();
+    var editorView = new Drupal.edit.editors[editorName]({
+      el: $(fieldModel.get('el')),
+      model: editorModel,
+      fieldModel: fieldModel
+    });
+
+    // Create in-place editor's toolbar — positions appropriately above the
+    // edited element.
+    var toolbarView = new Drupal.edit.FieldToolbarView({
+      model: fieldModel,
+      $editedElement: $(editorView.getEditedElement()),
+      editorView: editorView
+    });
+
+    // Create decoration for edited element: padding if necessary, sets classes
+    // on the element to style it according to the current state.
+    var decorationView = new Drupal.edit.EditorDecorationView({
+      el: $(editorView.getEditedElement()),
+      model: fieldModel,
+      editorView: editorView,
+      toolbarId: toolbarView.getId()
+    });
+
+    // Track these three views in FieldModel so that we can tear them down
+    // correctly.
+    fieldModel.editorView = editorView;
+    fieldModel.toolbarView = toolbarView;
+    fieldModel.decorationView = decorationView;
+  },
+
+  /**
+   * Tears down the in-place editor for the given field.
+   *
+   * Must happen after the fieldModel's state is changed to 'inactive'.
+   *
+   * @param Drupal.edit.FieldModel fieldModel
+   *   The field for which an in-place editor must be torn down.
+   */
+  teardownEditor: function (fieldModel) {
+    // Early-return if this field was not yet decorated.
+    if (fieldModel.editorView === undefined) {
+      return;
+    }
+
+    // Unbind event handlers; remove toolbar element; delete toolbar view.
+    fieldModel.toolbarView.remove();
+    delete fieldModel.toolbarView;
+
+    // Unbind event handlers; delete decoration view. Don't remove the element
+    // because that would remove the field itself.
+    fieldModel.decorationView.remove();
+    delete fieldModel.decorationView;
+
+    // Unbind event handlers; delete editor view. Don't remove the element
+    // because that would remove the field itself.
+    fieldModel.editorView.remove();
+    delete fieldModel.editorView;
+  },
+
+  /**
+   * Asks the user to confirm whether he wants to stop editing via a modal.
+   *
+   * @see acceptEditorStateChange()
+   */
+  _confirmStopEditing: function () {
+    // Only instantiate if there isn't a modal instance visible yet.
+    if (!this.model.get('activeModal')) {
+      var that = this;
+      var modal = new Drupal.edit.ModalView({
+        model: this.model,
+        message: Drupal.t('You have unsaved changes'),
+        buttons: [
+          { action: 'discard', classes: 'gray-button', label: Drupal.t('Discard changes') },
+          { action: 'save', type: 'submit', classes: 'blue-button', label: Drupal.t('Save') }
+        ],
+        callback: function (action) {
+          // The active modal has been removed.
+          that.model.set('activeModal', null);
+          // Set the state that matches the user's action.
+          var targetState = (action === 'discard') ? 'candidate' : 'saving';
+          that.model.get('activeEditor').set('state', 'candidate', { confirmed: true });
+        }
+      });
+      this.model.set('activeModal', modal);
+      // The modal will set the activeModal property on the model when rendering
+      // to prevent multiple modals from being instantiated.
+      modal.render();
+    }
+  },
+
+  /**
+   * Reacts to field state changes; tracks global state.
+   *
+   * @param Drupal.edit.FieldModel fieldModel
+   * @param String state
+   *   The state of the associated field. One of Drupal.edit.FieldModel.states.
+   */
+  editorStateChange: function (fieldModel, state) {
+    var from = fieldModel.previous('state');
+    var to = state;
+
+    // Keep track of the highlighted editor in the global state.
+    if (_.indexOf(this.singleEditorStates, to) !== -1 && this.model.get('highlightedEditor') !== fieldModel) {
+      this.model.set('highlightedEditor', fieldModel);
+    }
+    else if (this.model.get('highlightedEditor') === fieldModel && to === 'candidate') {
+      this.model.set('highlightedEditor', null);
+    }
+
+    // Keep track of the active editor in the global state.
+    if (_.indexOf(this.activeEditorStates, to) !== -1 && this.model.get('activeEditor') !== fieldModel) {
+      this.model.set('activeEditor', fieldModel);
+    }
+    else if (this.model.get('activeEditor') === fieldModel && to === 'candidate') {
+      // Discarded if it transitions from a changed state to 'candidate'.
+      if (from === 'changed' || from === 'invalid') {
+        fieldModel.editorView.revert();
+      }
+      this.model.set('activeEditor', null);
+    }
+  },
+
+  /**
+   * Render an updated field (a field whose 'html' attribute changed).
+   *
+   * @param Drupal.edit.FieldModel fieldModel
+   *   The FieldModel whose 'html' attribute changed.
+   */
+  renderUpdatedField: function (fieldModel) {
+    // Get data necessary to rerender property before it is unavailable.
+    var html = fieldModel.get('html');
+    var $fieldWrapper = $(fieldModel.get('el'));
+    var $context = $fieldWrapper.parent();
+
+    // First set the state to 'candidate', to allow all attached views to
+    // clean up all their "active state"-related changes.
+    fieldModel.set('state', 'candidate');
+
+    // Set the field's state to 'inactive', to enable the updating of its DOM
+    // value.
+    fieldModel.set('state', 'inactive', { reason: 'rerender' });
+
+    // Destroy the field model; this will cause all attached views to be
+    // destroyed too, and removal from all collections in which it exists.
+    fieldModel.destroy();
+
+    // Replace the old content with the new content.
+    $fieldWrapper.replaceWith(html);
+
+    // Attach behaviors again to the modified piece of HTML; this will create
+    // a new field model and call rerenderedFieldToCandidate() with it.
+    Drupal.attachBehaviors($context);
+  },
+
+  /**
+   * If the new in-place editable field is for the entity that's currently
+   * being edited, then transition it to the 'candidate' state.
+   *
+   * This happens when a field was modified, saved and hence rerendered.
+   *
+   * @param Drupal.edit.FieldModel fieldModel
+   *   A field that was just added to the collection of fields.
+   */
+  rerenderedFieldToCandidate: function (fieldModel) {
+    var activeEntity = Drupal.edit.collections.entities.where({ isActive: true })[0];
+
+    // Early-return if there is no active entity.
+    if (activeEntity === null) {
+      return;
+    }
+
+    // If the field's entity is the active entity, make it a candidate.
+    if (fieldModel.get('entity') === activeEntity) {
+      this.setupEditor(fieldModel);
+      fieldModel.set('state', 'candidate');
+    }
+  },
+
+  /**
+   * EntityModel Collection change handler, called on change:isActive, enforces
+   * a single active entity.
+   *
+   * @param Drupal.edit.EntityModel
+   *   The entityModel instance whose active state has changed.
+   */
+  enforceSingleActiveEntity: function (changedEntityModel) {
+    // When an entity is deactivated, we don't need to enforce anything.
+    if (changedEntityModel.get('isActive') === false) {
+      return;
+    }
+
+    // This entity was activated; deactivate all other entities.
+    changedEntityModel.collection.chain()
+      .filter(function (entityModel) {
+        return entityModel.get('isActive') === true && entityModel !== changedEntityModel;
+      })
+      .each(function (entityModel) {
+        entityModel.set('isActive', false);
+      });
+  }
+});
+
+}(jQuery, _, Backbone, Drupal));
diff --git a/core/modules/edit/js/views/ContextualLinkView.js b/core/modules/edit/js/views/ContextualLinkView.js
new file mode 100644
index 0000000000000000000000000000000000000000..4b4d075de8848d7a94a9d8f05f3e2e6a12d007a5
--- /dev/null
+++ b/core/modules/edit/js/views/ContextualLinkView.js
@@ -0,0 +1,70 @@
+/**
+ * @file
+ * A Backbone View that a dynamic contextual link.
+ */
+(function ($, _, Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit.ContextualLinkView = Backbone.View.extend({
+
+   events: function () {
+    // Prevents delay and simulated mouse events.
+    function touchEndToClick (event) {
+      event.preventDefault();
+      event.target.click();
+    }
+    return {
+      'click a': function (event) {
+        event.preventDefault();
+        this.model.set('isActive', !this.model.get('isActive'));
+      },
+      'touchEnd a': touchEndToClick
+    };
+  },
+
+  /**
+   * {@inheritdoc}
+   *
+   * @param Object options
+   *   An object with the following keys:
+   *   - Drupal.edit.EntityModel model: the associated entity's model
+   *   - Drupal.edit.AppModel appModel: the application state model
+   *   - strings: the strings for the "Quick edit" link
+   */
+  initialize: function (options) {
+    // Initial render.
+    this.render();
+
+    // Re-render whenever this entity's isActive attribute changes.
+    this.model.on('change:isActive', this.render, this);
+
+    // Hide the contextual links whenever an in-place editor is active.
+    this.options.appModel.on('change:activeEditor', this.toggleContextualLinksVisibility, this);
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  render: function () {
+    var strings = this.options.strings;
+    var text = !this.model.get('isActive') ? strings.quickEdit : strings.stopQuickEdit;
+    this.$el.find('a').text(text);
+    return this;
+  },
+
+  /**
+   * Hides the contextual links if an in-place editor is active.
+   *
+   * @param Drupal.edit.AppModel appModel
+   *   The application state model.
+   * @param null|Drupal.edit.FieldModel activeEditor
+   *   The model of the field that is currently being edited, or, if none, null.
+   */
+  toggleContextualLinksVisibility: function (appModel, activeEditor) {
+    this.$el.parents('.contextual').toggle(activeEditor === null);
+  }
+
+});
+
+})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/propertyeditordecoration-view.js b/core/modules/edit/js/views/EditorDecorationView.js
similarity index 59%
rename from core/modules/edit/js/views/propertyeditordecoration-view.js
rename to core/modules/edit/js/views/EditorDecorationView.js
index bee33d10f560eee023108085658d904a9d7d51d4..b9efdc25bc9ad6db578bd81720bc8b916f62da3a 100644
--- a/core/modules/edit/js/views/propertyeditordecoration-view.js
+++ b/core/modules/edit/js/views/EditorDecorationView.js
@@ -1,17 +1,12 @@
 /**
  * @file
- * A Backbone View that decorates a Property Editor widget.
- *
- * It listens to state changes of the property editor.
+ * A Backbone View that decorates the in-place edited element.
  */
-(function($, Backbone, Drupal) {
+(function ($, Backbone, Drupal) {
 
 "use strict";
 
-Drupal.edit = Drupal.edit || {};
-Drupal.edit.views = Drupal.edit.views || {};
-Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
-
+Drupal.edit.EditorDecorationView = Backbone.View.extend({
   toolbarId: null,
 
   _widthAttributeIsEmpty: null,
@@ -25,40 +20,45 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
   },
 
   /**
-   * Implements Backbone Views' initialize() function.
+   * {@inheritdoc}
    *
-   * @param options
+   * @param Object options
    *   An object with the following keys:
-   *   - editor: the editor object with an 'options' object that has these keys:
-   *      * entity: the VIE entity for the property.
-   *      * property: the predicate of the property.
-   *      * widget: the parent EditableEntity widget.
-   *      * editorName: the name of the PropertyEditor widget
-   *   - toolbarId: the ID attribute of the toolbar as rendered in the DOM.
+   *   - Drupal.edit.EditorView editorView: the editor object view.
+   *   - String toolbarId: the ID attribute of the toolbar as rendered in the
+   *     DOM.
    */
-  initialize: function(options) {
-    this.editor = options.editor;
+  initialize: function (options) {
+    this.editorView = options.editorView;
+
     this.toolbarId = options.toolbarId;
 
-    this.predicate = this.editor.options.property;
-    this.editorName = this.editor.options.editorName;
+    this.model.on('change:state', this.stateChange, this);
+  },
 
-    // Only start listening to events as soon as we're no longer in the 'inactive' state.
-    this.undelegateEvents();
+  /**
+   * {@inheritdoc}
+   */
+  remove: function () {
+    // The el property is the field, which should not be removed. Remove the
+    // pointer to it, then call Backbone.View.prototype.remove().
+    this.setElement();
+    Backbone.View.prototype.remove.call(this);
   },
 
   /**
-   * Listens to editor state changes.
+   * Determines the actions to take given a change of state.
+   *
+   * @param Drupal.edit.FieldModel model
+   * @param String state
+   *   The state of the associated field. One of Drupal.edit.FieldModel.states.
    */
-  stateChange: function(from, to) {
+  stateChange: function (model, state) {
+    var from = model.previous('state');
+    var to = state;
     switch (to) {
       case 'inactive':
-        if (from !== null) {
-          this.undecorate();
-          if (from === 'invalid') {
-            this._removeValidationErrors();
-          }
-        }
+        this.undecorate();
         break;
       case 'candidate':
         this.decorate();
@@ -66,9 +66,6 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
           this.stopHighlight();
           if (from !== 'highlighted') {
             this.stopEdit();
-            if (from === 'invalid') {
-              this._removeValidationErrors();
-            }
           }
         }
         break;
@@ -89,9 +86,6 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
       case 'changed':
         break;
       case 'saving':
-        if (from === 'invalid') {
-          this._removeValidationErrors();
-        }
         break;
       case 'saved':
         break;
@@ -101,106 +95,114 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
   },
 
   /**
-   * Starts hover: transition to 'highlight' state.
+   * Starts hover; transitions to 'highlight' state.
    *
-   * @param event
+   * @param jQuery event
    */
-  onMouseEnter: function(event) {
+  onMouseEnter: function (event) {
     var that = this;
     this._ignoreHoveringVia(event, '#' + this.toolbarId, function () {
-      var editableEntity = that.editor.options.widget;
-      editableEntity.setState('highlighted', that.predicate);
+      that.model.set('state', 'highlighted');
       event.stopPropagation();
     });
   },
 
   /**
-   * Stops hover: back to 'candidate' state.
+   * Stops hover; transitions to 'candidate' state.
    *
-   * @param event
+   * @param jQuery event
    */
-  onMouseLeave: function(event) {
+  onMouseLeave: function (event) {
     var that = this;
     this._ignoreHoveringVia(event, '#' + this.toolbarId, function () {
-      var editableEntity = that.editor.options.widget;
-      editableEntity.setState('candidate', that.predicate, { reason: 'mouseleave' });
+      that.model.set('state', 'candidate', { reason: 'mouseleave' });
       event.stopPropagation();
     });
   },
 
   /**
-   * Clicks: transition to 'activating' stage.
+   * Transition to 'activating' stage.
    *
-   * @param event
+   * @param jQuery event
    */
-  onClick: function(event) {
-    var editableEntity = this.editor.options.widget;
-    editableEntity.setState('activating', this.predicate);
+  onClick: function (event) {
+    this.model.set('state', 'activating');
     event.preventDefault();
     event.stopPropagation();
   },
 
+  /**
+   * Adds classes used to indicate an elements editable state.
+   */
   decorate: function () {
     this.$el.addClass('edit-animate-fast edit-candidate edit-editable');
-    this.delegateEvents();
   },
 
+  /**
+   * Removes classes used to indicate an elements editable state.
+   */
   undecorate: function () {
-    this.$el
-      .removeClass('edit-candidate edit-editable edit-highlighted edit-editing');
-    this.undelegateEvents();
+    this.$el.removeClass('edit-candidate edit-editable edit-highlighted edit-editing');
   },
 
+  /**
+   * Adds that class that indicates that an element is highlighted.
+   */
   startHighlight: function () {
     // Animations.
     var that = this;
-    setTimeout(function() {
+    // Use a timeout to grab the next available animation frame.
+    setTimeout(function () {
       that.$el.addClass('edit-highlighted');
     }, 0);
   },
 
-  stopHighlight: function() {
-    this.$el
-      .removeClass('edit-highlighted');
+  /**
+   * Removes the class that indicates that an element is highlighted.
+   */
+  stopHighlight: function () {
+    this.$el.removeClass('edit-highlighted');
   },
 
-  prepareEdit: function() {
+  /**
+   * Removes the class that indicates that an element as editable.
+   */
+  prepareEdit: function () {
     this.$el.addClass('edit-editing');
 
-    // While editing, don't show *any* other editors.
-    // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133)
-    // Revisit this.
+    // While editing, do not show any other editors.
     $('.edit-candidate').not('.edit-editing').removeClass('edit-editable');
   },
 
-  startEdit: function() {
-    if (this.getEditUISetting('padding')) {
+  /**
+   * Updates the display of the editable element once editing has begun.
+   */
+  startEdit: function () {
+    if (this.editorView.getEditUISettings().padding) {
       this._pad();
     }
   },
 
-  stopEdit: function() {
+  /**
+   * Removes the class that indicates that an element is being edited.
+   *
+   * Reapplies the class that indicates that a candidate editable element is
+   * again available to be edited.
+   */
+  stopEdit: function () {
     this.$el.removeClass('edit-highlighted edit-editing');
 
     // Make the other editors show up again.
-    // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133)
-    // Revisit this.
     $('.edit-candidate').addClass('edit-editable');
 
-    if (this.getEditUISetting('padding')) {
+    if (this.editorView.getEditUISettings().padding) {
       this._unpad();
     }
   },
 
   /**
-   * Retrieves a setting of the editor-specific Edit UI integration.
-   *
-   * @see Drupal.edit.util.getEditUISetting().
+   * Adds padding around the editable element in order to make it pop visually.
    */
-  getEditUISetting: function(setting) {
-    return Drupal.edit.util.getEditUISetting(this.editor, setting);
-  },
-
   _pad: function () {
     var self = this;
 
@@ -218,7 +220,7 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
 
     // 2) Add padding; use animations.
     var posProp = this._getPositionProperties(this.$el);
-    setTimeout(function() {
+    setTimeout(function () {
       // Re-enable width animations (padding changes affect width too!).
       self.$el.removeClass('edit-animate-disable-width');
 
@@ -237,6 +239,9 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
     }, 0);
   },
 
+  /**
+   * Removes the padding around the element being edited when editing ceases.
+   */
   _unpad: function () {
     var self = this;
 
@@ -251,7 +256,7 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
     // 2) Remove padding; use animations (these will run simultaneously with)
     // the fading out of the toolbar as its gets removed).
     var posProp = this._getPositionProperties(this.$el);
-    setTimeout(function() {
+    setTimeout(function () {
       // Re-enable width animations (padding changes affect width too!).
       self.$el.removeClass('edit-animate-disable-width');
 
@@ -273,10 +278,9 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
   /**
    * Gets the background color of an element (or the inherited one).
    *
-   * @param $e
-   *   A DOM element.
+   * @param DOM $e
    */
-  _getBgColor: function($e) {
+  _getBgColor: function ($e) {
     var c;
 
     if ($e === null || $e[0].nodeName === 'HTML') {
@@ -293,13 +297,14 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
   },
 
   /**
-   * Gets the top and left properties of an element and convert extraneous
-   * values and information into numbers ready for subtraction.
+   * Gets the top and left properties of an element.
+   *
+   * Convert extraneous values and information into numbers ready for
+   * subtraction.
    *
-   * @param $e
-   *   A DOM element.
+   * @param DOM $e
    */
-  _getPositionProperties: function($e) {
+  _getPositionProperties: function ($e) {
     var p,
         r = {},
         props = [
@@ -319,10 +324,10 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
   /**
    * Replaces blank or 'auto' CSS "position: <value>" values with "0px".
    *
-   * @param pos
-   *   The value for a CSS position declaration.
+   * @param String pos
+   *   (optional) The value for a CSS position declaration.
    */
-  _replaceBlankPosition: function(pos) {
+  _replaceBlankPosition: function (pos) {
     if (pos === 'auto' || !pos) {
       pos = '0px';
     }
@@ -330,34 +335,24 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
   },
 
   /**
-   * Ignores hovering to/from the given closest element, but as soon as a hover
-   * occurs to/from *another* element, then call the given callback.
+   * Ignores hovering to/from the given closest element.
+   *
+   * When a hover occurs to/from another element, invoke the callback.
+   *
+   * @param jQuery event
+   * @param jQuery closest
+   *   A jQuery-wrapped DOM element or compatibale jQuery input. The element
+   *   whose mouseenter and mouseleave events should be ignored.
+   * @param Function callback
    */
-  _ignoreHoveringVia: function(event, closest, callback) {
+  _ignoreHoveringVia: function (event, closest, callback) {
     if ($(event.relatedTarget).closest(closest).length > 0) {
       event.stopPropagation();
     }
     else {
       callback();
     }
-  },
-
-  /**
-   * Removes validation errors' markup changes, if any.
-   *
-   * Note: this only needs to happen for type=direct, because for type=direct,
-   * the property DOM element itself is modified; this is not the case for
-   * type=form.
-   */
-  _removeValidationErrors: function() {
-    if (this.editorName !== 'form') {
-      this.$el
-        .removeClass('edit-validation-error')
-        .next('.edit-validation-errors')
-        .remove();
-    }
   }
-
 });
 
 })(jQuery, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/EditorView.js b/core/modules/edit/js/views/EditorView.js
new file mode 100644
index 0000000000000000000000000000000000000000..221765de87a6fd15c8239b1a6e6feefd43395190
--- /dev/null
+++ b/core/modules/edit/js/views/EditorView.js
@@ -0,0 +1,271 @@
+(function ($, _, Backbone, Drupal) {
+
+"use strict";
+
+/**
+ * A base implementation that outlines the structure for in-place editors.
+ *
+ * Specific in-place editor implementations should subclass (extend) this View
+ * and override whichever method they deem necessary to override.
+ *
+ * Look at Drupal.edit.editors.form and Drupal.edit.editors.direct for
+ * examples.
+ */
+Drupal.edit.EditorView = Backbone.View.extend({
+
+  /**
+   * {@inheritdoc}
+   *
+   * Typically you would want to override this method to set the originalValue
+   * attribute in the FieldModel to such a value that your in-place editor can
+   * revert to the original value when necessary.
+   *
+   * If you override this method, you should call this method (the parent
+   * class' initialize()) first, like this:
+   *   Drupal.edit.EditorView.prototype.initialize.call(this, options);
+   *
+   * For an example, @see Drupal.edit.editors.direct.
+   *
+   * @param Object options
+   *   An object with the following keys:
+   *   - Drupal.edit.EditorModel model: the in-place editor state model
+   *   - Drupal.edit.FieldModel fieldModel: the field model
+   */
+  initialize: function (options) {
+    this.fieldModel = options.fieldModel;
+    this.fieldModel.on('change:state', this.stateChange, this);
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  remove: function () {
+    // The el property is the field, which should not be removed. Remove the
+    // pointer to it, then call Backbone.View.prototype.remove().
+    this.setElement();
+    Backbone.View.prototype.remove.call(this);
+  },
+
+  /**
+   * Returns the edited element.
+   *
+   * For some single cardinality fields, it may be necessary or useful to
+   * not in-place edit (and hence decorate) the DOM element with the
+   * data-edit-id attribute (which is the field's wrapper), but a specific
+   * element within the field's wrapper.
+   * e.g. using a WYSIWYG editor on a body field should happen on the DOM
+   * element containing the text itself, not on the field wrapper.
+   *
+   * For example, @see Drupal.edit.editors.direct.
+   *
+   * @return jQuery
+   *   A jQuery-wrapped DOM element.
+   */
+  getEditedElement: function () {
+    return this.$el;
+  },
+
+  /**
+   * Returns 3 Edit UI settings that depend on the in-place editor:
+   *  - Boolean padding: indicates whether padding should be applied to the
+   *    edited element, to guarantee legibility of text.
+   *  - Boolean unifiedToolbar: provides the in-place editor with the ability to
+   *    insert its own toolbar UI into Edit's tightly integrated toolbar.
+   *  - Boolean fullWidthToolbar: indicates whether Edit's tightly integrated
+   *    toolbar should consume the full width of the element, rather than being
+   *    just long enough to accomodate a label.
+   */
+  getEditUISettings: function () {
+    return { padding: false, unifiedToolbar: false, fullWidthToolbar: false };
+  },
+
+  /**
+   * Determines the actions to take given a change of state.
+   *
+   * @param Drupal.edit.FieldModel fieldModel
+   * @param String state
+   *   The state of the associated field. One of Drupal.edit.FieldModel.states.
+   */
+  stateChange: function (fieldModel, state) {
+    var from = fieldModel.previous('state');
+    var to = state;
+    switch (to) {
+      case 'inactive':
+        // An in-place editor view will not yet exist in this state, hence
+        // this will never be reached. Listed for sake of completeness.
+        break;
+      case 'candidate':
+        // Nothing to do for the typical in-place editor: it should not be
+        // visible yet.
+
+        // Except when we come from the 'invalid' state, then we clean up.
+        if (from === 'invalid') {
+          this.removeValidationErrors();
+        }
+        break;
+      case 'highlighted':
+        // Nothing to do for the typical in-place editor: it should not be
+        // visible yet.
+        break;
+      case 'activating':
+        // The user has indicated he wants to do in-place editing: if
+        // something needs to be loaded (CSS/JavaScript/server data/…), then
+        // do so at this stage, and once the in-place editor is ready,
+        // set the 'active' state.
+        // A "loading" indicator will be shown in the UI for as long as the
+        // field remains in this state.
+        var loadDependencies = function (callback) {
+          // Do the loading here.
+          callback();
+        };
+        loadDependencies(function () {
+          fieldModel.set('state', 'active');
+        });
+        break;
+      case 'active':
+        // The user can now actually use the in-place editor.
+        break;
+      case 'changed':
+        // Nothing to do for the typical in-place editor. The UI will show an
+        // indicator that the field has changed.
+        break;
+      case 'saving':
+        // When the user has indicated he wants to save his changes to this
+        // field, this state will be entered.
+        // If the previous saving attempt resulted in validation errors, the
+        // previous state will be 'invalid'. Clean up those validation errors
+        // while the user is saving.
+        if (from === 'invalid') {
+          this.removeValidationErrors();
+        }
+        this.save();
+        break;
+      case 'saved':
+        // Nothing to do for the typical in-place editor. Immediately after
+        // being saved, a field will go to the 'candidate' state, where it
+        // should no longer be visible (after all, the field will then again
+        // just be a *candidate* to be in-place edited).
+        break;
+      case 'invalid':
+        // The modified field value was attempted to be saved, but there were
+        // validation errors.
+        this.showValidationErrors();
+        break;
+    }
+  },
+
+  /**
+   * Reverts the modified value back to the original value (before editing
+   * started).
+   */
+  revert: function () {
+    // A no-op by default; each editor should implement reverting itself.
+
+    // Note that if the in-place editor does not cause the FieldModel's
+    // element to be modified, then nothing needs to happen.
+  },
+
+  /**
+   * Saves the modified value in the in-place editor for this field.
+   */
+  save: function () {
+    var fieldModel = this.fieldModel;
+    var editorModel = this.model;
+
+    function fillAndSubmitForm (value) {
+      var $form = $('#edit_backstage form');
+      // Fill in the value in any <input> that isn't hidden or a submit
+      // button.
+      $form.find(':input[type!="hidden"][type!="submit"]:not(select)')
+        // Don't mess with the node summary.
+        .not('[name$="\\[summary\\]"]').val(value);
+      // Submit the form.
+      $form.find('.edit-form-submit').trigger('click.edit');
+    }
+
+    var formOptions = {
+      fieldID: this.fieldModel.id,
+      $el: this.$el,
+      nocssjs: true
+    };
+    Drupal.edit.util.form.load(formOptions, function (form, ajax) {
+      // Create a backstage area for storing forms that are hidden from view
+      // (hence "backstage" — since the editing doesn't happen in the form, it
+      // happens "directly" in the content, the form is only used for saving).
+      $(Drupal.theme('editBackstage', { id: 'edit_backstage' })).appendTo('body');
+      // Direct forms are stuffed into #edit_backstage, apparently.
+      $('#edit_backstage').append(form);
+      // Disable the browser's HTML5 validation; we only care about server-
+      // side validation. (Not disabling this will actually cause problems
+      // because browsers don't like to set HTML5 validation errors on hidden
+      // forms.)
+      $('#edit_backstage form').prop('novalidate', true);
+      var $submit = $('#edit_backstage form .edit-form-submit');
+      var base = Drupal.edit.util.form.ajaxifySaving(formOptions, $submit);
+
+      function removeHiddenForm () {
+        Drupal.edit.util.form.unajaxifySaving($submit);
+        $('#edit_backstage').remove();
+      }
+
+      // Successfully saved.
+      Drupal.ajax[base].commands.editFieldFormSaved = function (ajax, response, status) {
+        removeHiddenForm();
+
+        // First, transition the state to 'saved'.
+        fieldModel.set('state', 'saved');
+        // Then, set the 'html' attribute on the field model. This will cause
+        // the field to be rerendered.
+        fieldModel.set('html', response.data);
+      };
+
+      // Unsuccessfully saved; validation errors.
+      Drupal.ajax[base].commands.editFieldFormValidationErrors = function (ajax, response, status) {
+        removeHiddenForm();
+
+        editorModel.set('validationErrors', response.data);
+        fieldModel.set('state', 'invalid');
+      };
+
+      // The editFieldForm AJAX command is only called upon loading the form
+      // for the first time, and when there are validation errors in the form;
+      // Form API then marks which form items have errors. This is useful for
+      // the form-based in-place editor, but pointless for any other: the form
+      // itself won't be visible at all anyway! So, we just ignore it.
+      Drupal.ajax[base].commands.editFieldForm = function () {};
+
+      fillAndSubmitForm(editorModel.get('currentValue'));
+    });
+  },
+
+  /**
+   * Shows validation error messages.
+   *
+   * Should be called when the state is changed to 'invalid'.
+   */
+  showValidationErrors: function () {
+    var $errors = $('<div class="edit-validation-errors"></div>')
+      .append(this.model.get('validationErrors'));
+    $(this.fieldModel.get('el'))
+      .addClass('edit-validation-error')
+      .after($errors);
+  },
+
+  /**
+   * Cleans up validation error messages.
+   *
+   * Should be called when the state is changed to 'candidate' or 'saving'. In
+   * the case of the latter: the user has modified the value in the in-place
+   * editor again to attempt to save again. In the case of the latter: the
+   * invalid value was discarded.
+   */
+  removeValidationErrors: function () {
+    $(this.fieldModel.get('el'))
+      .removeClass('edit-validation-error')
+      .next('.edit-validation-errors')
+      .remove();
+  }
+
+});
+
+}(jQuery, _, Backbone, Drupal));
diff --git a/core/modules/edit/js/views/FieldToolbarView.js b/core/modules/edit/js/views/FieldToolbarView.js
new file mode 100644
index 0000000000000000000000000000000000000000..4cdd53d3acc5184c8dbef75daa352d102b43566d
--- /dev/null
+++ b/core/modules/edit/js/views/FieldToolbarView.js
@@ -0,0 +1,402 @@
+/**
+ * @file
+ * A Backbone View that provides an interactive toolbar (1 per in-place editor).
+ */
+(function ($, _, Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit.FieldToolbarView = Backbone.View.extend({
+
+  // The edited element, as indicated by EditorView.getEditedElement().
+  $editedElement: null,
+
+  // A reference to the in-place editor.
+  editorView: null,
+
+  _loader: null,
+  _loaderVisibleStart: 0,
+
+  _id: null,
+
+  events: {
+    'click.edit button.label': 'onClickInfoLabel',
+    'mouseleave.edit': 'onMouseLeave',
+    'click.edit button.field-save': 'onClickSave',
+    'click.edit button.field-close': 'onClickClose'
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  initialize: function (options) {
+    this.$editedElement = options.$editedElement;
+    this.editorView = options.editorView;
+
+    this._loader = null;
+    this._loaderVisibleStart = 0;
+
+    // Generate a DOM-compatible ID for the form container DOM element.
+    this._id = 'edit-toolbar-for-' + this.model.id.replace(/\//g, '_');
+
+    this.model.on('change:state', this.stateChange, this);
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  render: function () {
+    // Render toolbar.
+    this.setElement($(Drupal.theme('editToolbarContainer', {
+      id: this._id
+    })));
+
+    // Insert in DOM.
+    if (this.$editedElement.css('display') === 'inline') {
+      this.$el.prependTo(this.$editedElement.offsetParent());
+      var pos = this.$editedElement.position();
+      this.$el.css('left', pos.left).css('top', pos.top);
+    }
+    else {
+      this.$el.insertBefore(this.$editedElement);
+    }
+
+    return this;
+  },
+
+  /**
+   * Determines the actions to take given a change of state.
+   *
+   * @param Drupal.edit.FieldModel model
+   * @param String state
+   *   The state of the associated field. One of Drupal.edit.FieldModel.states.
+   */
+  stateChange: function (model, state) {
+    var from = model.previous('state');
+    var to = state;
+    switch (to) {
+      case 'inactive':
+        if (from) {
+          this.remove();
+        }
+        break;
+      case 'candidate':
+        if (from === 'inactive') {
+          this.render();
+        }
+        else {
+          // Remove all toolgroups; they're no longer necessary.
+          this.$el
+            .removeClass('edit-highlighted edit-editing')
+            .find('.edit-toolbar .edit-toolgroup').remove();
+          if (from !== 'highlighted' && this.editorView.getEditUISettings().padding) {
+            this._unpad();
+          }
+        }
+        break;
+      case 'highlighted':
+        // As soon as we highlight, make sure we have a toolbar in the DOM (with
+        // at least a title).
+        this.startHighlight();
+        break;
+      case 'activating':
+        this.setLoadingIndicator(true);
+        break;
+      case 'active':
+        this.startEdit();
+        this.setLoadingIndicator(false);
+        if (this.editorView.getEditUISettings().fullWidthToolbar) {
+          this.$el.addClass('edit-toolbar-fullwidth');
+        }
+
+        if (this.editorView.getEditUISettings().padding) {
+          this._pad();
+        }
+        if (this.editorView.getEditUISettings().unifiedToolbar) {
+          this.insertWYSIWYGToolGroups();
+        }
+        break;
+      case 'changed':
+        this.$el
+          .find('button.save')
+          .addClass('blue-button')
+          .removeClass('gray-button');
+        break;
+      case 'saving':
+        this.setLoadingIndicator(true);
+        break;
+      case 'saved':
+        this.setLoadingIndicator(false);
+        break;
+      case 'invalid':
+        this.setLoadingIndicator(false);
+        break;
+    }
+  },
+
+  /**
+   * Redirects the click.edit-event to the editor DOM element.
+   *
+   * @param jQuery event
+   */
+  onClickInfoLabel: function (event) {
+    event.stopPropagation();
+    event.preventDefault();
+    // Redirects the event to the editor DOM element.
+    this.$editedElement.trigger('click.edit');
+  },
+
+  /**
+   * Controls mouseleave events.
+   *
+   * A mouseleave to the editor doesn't matter; a mouseleave to something else
+   * counts as a mouseleave on the editor itself.
+   *
+   * @param jQuery event
+   */
+  onMouseLeave: function (event) {
+    if (event.relatedTarget !== this.$editedElement[0] && !$.contains(this.$editedElement, event.relatedTarget)) {
+      this.$editedElement.trigger('mouseleave.edit');
+    }
+    event.stopPropagation();
+  },
+
+  /**
+   * Set the model state to 'saving' when the save button is clicked.
+   *
+   * @param jQuery event
+   */
+  onClickSave: function (event) {
+    event.stopPropagation();
+    event.preventDefault();
+    this.model.set('state', 'saving');
+  },
+
+  /**
+   * Sets the model state to candidate when the cancel button is clicked.
+   *
+   * @param jQuery event
+   */
+  onClickClose: function (event) {
+    event.stopPropagation();
+    event.preventDefault();
+    this.model.set('state', 'candidate', { reason: 'cancel' });
+  },
+
+  /**
+   * Indicates in the 'info' toolgroup that we're waiting for a server reponse.
+   *
+   * Prevents flickering loading indicator by only showing it after 0.6 seconds
+   * and if it is shown, only hiding it after another 0.6 seconds.
+   *
+   * @param Boolean enabled
+   *   Whether the loading indicator should be displayed or not.
+   */
+  setLoadingIndicator: function (enabled) {
+    var that = this;
+    if (enabled) {
+      this._loader = setTimeout(function () {
+        that.addClass('info', 'loading');
+        that._loaderVisibleStart = new Date().getTime();
+      }, 600);
+    }
+    else {
+      var currentTime = new Date().getTime();
+      clearTimeout(this._loader);
+      if (this._loaderVisibleStart) {
+        setTimeout(function () {
+          that.removeClass('info', 'loading');
+        }, this._loaderVisibleStart + 600 - currentTime);
+      }
+      this._loader = null;
+      this._loaderVisibleStart = 0;
+    }
+  },
+
+  /**
+   * Decorate the field with markup to indicate it is highlighted.
+   */
+  startHighlight: function () {
+    // Retrieve the lavel to show for this field.
+    var label = this.model.get('metadata').label;
+
+    this.$el
+      .addClass('edit-highlighted')
+      .find('.edit-toolbar')
+      // Append the "info" toolgroup into the toolbar.
+      .append(Drupal.theme('editToolgroup', {
+        classes: 'info edit-animate-only-background-and-padding',
+        buttons: [
+          { label: label, classes: 'blank-button label' }
+        ]
+      }));
+
+    // Animations.
+    var that = this;
+    setTimeout(function () {
+      that.show('info');
+    }, 0);
+  },
+
+  /**
+   * Decorate the field with markup to indicate edit state; append a toolbar.
+   */
+  startEdit: function () {
+    this.$el
+      .addClass('edit-editing')
+      .find('.edit-toolbar')
+      // Append the "ops" toolgroup into the toolbar.
+      .append(Drupal.theme('editToolgroup', {
+        classes: 'ops',
+        buttons: [
+          { label: Drupal.t('Save'), type: 'submit', classes: 'field-save save gray-button' },
+          { label: '<span class="close">' + Drupal.t('Close') + '</span>', classes: 'field-close close gray-button' }
+        ]
+      }));
+    this.show('ops');
+  },
+
+  /**
+   * Adjusts the toolbar to accomodate padding on the editor.
+   *
+   * @see EditorDecorationView._pad().
+   */
+  _pad: function () {
+    // The whole toolbar must move to the top when the property's DOM element
+    // is displayed inline.
+    if (this.$editedElement.css('display') === 'inline') {
+      this.$el.css('top', parseInt(this.$el.css('top'), 10) - 5 + 'px');
+    }
+
+    // The toolbar must move to the top and the left.
+    var $hf = this.$el.find('.edit-toolbar-heightfaker');
+    $hf.css({ bottom: '6px', left: '-5px' });
+
+    if (this.editorView.getEditUISettings().fullWidthToolbar) {
+      $hf.css({ width: this.$editedElement.width() + 10 });
+    }
+  },
+
+  /**
+   * Undoes the changes made by _pad().
+   *
+   * @see EditorDecorationView._unpad().
+   */
+  _unpad: function () {
+    // Move the toolbar back to its original position.
+    var $hf = this.$el.find('.edit-toolbar-heightfaker');
+    $hf.css({ bottom: '1px', left: '' });
+
+    if (this.editorView.getEditUISettings().fullWidthToolbar) {
+      $hf.css({ width: '' });
+    }
+  },
+
+  /**
+   * Insert WYSIWYG markup into the associated toolbar.
+   */
+  insertWYSIWYGToolGroups: function () {
+    this.$el
+      .find('.edit-toolbar')
+      .append(Drupal.theme('editToolgroup', {
+        id: this.getFloatedWysiwygToolgroupId(),
+        classes: 'wysiwyg-floated',
+        buttons: []
+      }))
+      .append(Drupal.theme('editToolgroup', {
+        id: this.getMainWysiwygToolgroupId(),
+        classes: 'wysiwyg-main',
+        buttons: []
+      }));
+
+    // Animate the toolgroups into visibility.
+    var that = this;
+    setTimeout(function () {
+      that.show('wysiwyg-floated');
+      that.show('wysiwyg-main');
+    }, 0);
+  },
+
+  /**
+   * Retrieves the ID for this toolbar's container.
+   *
+   * Only used to make sane hovering behavior possible.
+   *
+   * @return String
+   *   A string that can be used as the ID for this toolbar's container.
+   */
+  getId: function () {
+    return 'edit-toolbar-for-' + this._id;
+  },
+
+  /**
+   * Retrieves the ID for this toolbar's floating WYSIWYG toolgroup.
+   *
+   * Used to provide an abstraction for any WYSIWYG editor to plug in.
+   *
+   * @return String
+   *   A string that can be used as the ID.
+   */
+  getFloatedWysiwygToolgroupId: function () {
+    return 'edit-wysiwyg-floated-toolgroup-for-' + this._id;
+  },
+
+  /**
+   * Retrieves the ID for this toolbar's main WYSIWYG toolgroup.
+   *
+   * Used to provide an abstraction for any WYSIWYG editor to plug in.
+   *
+   * @return String
+   *   A string that can be used as the ID.
+   */
+  getMainWysiwygToolgroupId: function () {
+    return 'edit-wysiwyg-main-toolgroup-for-' + this._id;
+  },
+
+  /**
+   * Shows a toolgroup.
+   *
+   * @param String toolgroup
+   *   A toolgroup name.
+   */
+  show: function (toolgroup) {
+    this._find(toolgroup).removeClass('edit-animate-invisible');
+  },
+
+  /**
+   * Adds classes to a toolgroup.
+   *
+   * @param String toolgroup
+   *   A toolgroup name.
+   * @param String classes
+   *   A space delimited list of class names to add to the toolgroup.
+   */
+  addClass: function (toolgroup, classes) {
+    this._find(toolgroup).addClass(classes);
+  },
+
+  /**
+   * Removes classes from a toolgroup.
+   *
+   * @param String toolgroup
+   *   A toolgroup name.
+   * @param String classes
+   *   A space delimited list of class names to remove from the toolgroup.
+   */
+  removeClass: function (toolgroup, classes) {
+    this._find(toolgroup).removeClass(classes);
+  },
+
+  /**
+   * Finds a toolgroup.
+   *
+   * @param String toolgroup
+   *   A toolgroup name.
+   * @return jQuery
+   */
+  _find: function (toolgroup) {
+    return this.$el.find('.edit-toolbar .edit-toolgroup.' + toolgroup);
+  }
+});
+
+})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/modal-view.js b/core/modules/edit/js/views/ModalView.js
similarity index 58%
rename from core/modules/edit/js/views/modal-view.js
rename to core/modules/edit/js/views/ModalView.js
index b98c8762d60f47ca9215dcdb192189af1e29a54c..9f096ba560de049f7e63c29324a5c0eee8d383e0 100644
--- a/core/modules/edit/js/views/modal-view.js
+++ b/core/modules/edit/js/views/ModalView.js
@@ -2,13 +2,11 @@
  * @file
  * A Backbone View that provides an interactive modal.
  */
-(function($, Backbone, Drupal) {
+(function ($, Backbone, Drupal) {
 
 "use strict";
 
-Drupal.edit = Drupal.edit || {};
-Drupal.edit.views = Drupal.edit.views || {};
-Drupal.edit.views.ModalView = Backbone.View.extend({
+Drupal.edit.ModalView = Backbone.View.extend({
 
   message: null,
   buttons: null,
@@ -20,29 +18,29 @@ Drupal.edit.views.ModalView = Backbone.View.extend({
   },
 
   /**
-   * Implements Backbone Views' initialize() function.
+   * {@inheritdoc}
    *
-   * @param options
+   * @param Object options
    *   An object with the following keys:
-   *   - message: a message to show in the modal.
-   *   - buttons: a set of buttons with 'action's defined, ready to be passed to
-   *     Drupal.theme.editButtons().
-   *   - callback: a callback that will receive the 'action' of the clicked
-   *     button.
+   *   - String message: a message to show in the modal.
+   *   - Array buttons: a set of buttons with 'action's defined, ready to be
+         passed to Drupal.theme.editButtons().
+   *   - Function callback: a callback that will receive the 'action' of the
+   *     clicked button.
    *
    * @see Drupal.theme.editModal()
    * @see Drupal.theme.editButtons()
    */
-  initialize: function(options) {
+  initialize: function (options) {
     this.message = options.message;
     this.buttons = options.buttons;
     this.callback = options.callback;
   },
 
   /**
-   * Implements Backbone Views' render() function.
+   * {@inheritdoc}
    */
-  render: function() {
+  render: function () {
     this.setElement(Drupal.theme('editModal', {}));
     this.$el.appendTo('body');
     // Template.
@@ -52,18 +50,17 @@ Drupal.edit.views.ModalView = Backbone.View.extend({
 
     // Show the modal with an animation.
     var that = this;
-    setTimeout(function() {
+    setTimeout(function () {
       that.$el.removeClass('edit-animate-invisible');
     }, 0);
   },
 
   /**
-   * When the user clicks on any of the buttons, the modal should be removed
-   * and the result should be passed to the callback.
+   * Passes the clicked button action to the callback; closes the modal.
    *
-   * @param event
+   * @param jQuery event
    */
-  onButtonClick: function(event) {
+  onButtonClick: function (event) {
     event.stopPropagation();
     event.preventDefault();
 
@@ -71,7 +68,7 @@ Drupal.edit.views.ModalView = Backbone.View.extend({
     var that = this;
     this.$el
       .addClass('edit-animate-invisible')
-      .on(Drupal.edit.util.constants.transitionEnd, function(e) {
+      .on(Drupal.edit.util.constants.transitionEnd, function (e) {
         that.remove();
       });
 
diff --git a/core/modules/edit/js/views/contextuallink-view.js b/core/modules/edit/js/views/contextuallink-view.js
deleted file mode 100644
index 472bd6efcfb3a413770aa62dba2194fc957bcfc4..0000000000000000000000000000000000000000
--- a/core/modules/edit/js/views/contextuallink-view.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * @file
- * A Backbone View that a dynamic contextual link.
- */
-(function ($, _, Backbone, Drupal) {
-
-"use strict";
-
-Drupal.edit = Drupal.edit || {};
-Drupal.edit.views = Drupal.edit.views || {};
-Drupal.edit.views.ContextualLinkView = Backbone.View.extend({
-
-  entity: null,
-
-  events: function () {
-    // Prevents delay and simulated mouse events.
-    function touchEndToClick (event) {
-      event.preventDefault();
-      event.target.click();
-    }
-    return {
-      'click a': 'onClick',
-      'touchEnd a': touchEndToClick
-    };
-  },
-
-  /**
-   * Implements Backbone Views' initialize() function.
-   *
-   * @param options
-   *   An object with the following keys:
-   *   - entity: the entity ID (e.g. node/1) of the entity
-   */
-  initialize: function (options) {
-    this.entity = options.entity;
-
-    // Initial render.
-    this.render();
-
-    // Re-render whenever the app state's active entity changes.
-    this.model.on('change:activeEntity', this.render, this);
-
-    // Hide the contextual links whenever an in-place editor is active.
-    this.model.on('change:activeEditor', this.toggleContextualLinksVisibility, this);
-  },
-
-  /**
-   * Equates clicks anywhere on the overlay to clicking the active editor's (if
-   * any) "close" button.
-   *
-   * @param {Object} event
-   */
-  onClick: function (event) {
-    event.preventDefault();
-
-    var that = this;
-    function updateActiveEntity () {
-      // The active entity is the current entity, i.e. stop editing the current
-      // entity.
-      if (that.model.get('activeEntity') === that.entity) {
-        that.model.set('activeEntity', null);
-      }
-      // The active entity is different from the current entity, i.e. start
-      // editing this entity instead of the previous one.
-      else {
-        that.model.set('activeEntity', that.entity);
-      }
-    }
-
-    // If there's an active editor, attempt to set its state to 'candidate', and
-    // only then do what the user asked.
-    // (Only when all PropertyEditor widgets of an entity are in the 'candidate'
-    // state, it is possible to stop editing it.)
-    var activeEditor = this.model.get('activeEditor');
-    if (activeEditor) {
-      var editableEntity = activeEditor.options.widget;
-      var predicate = activeEditor.options.property;
-      editableEntity.setState('candidate', predicate, { reason: 'stop or switch' }, function (accepted) {
-        if (accepted) {
-          updateActiveEntity();
-        }
-        else {
-          // No change.
-        }
-      });
-    }
-    // Otherwise, we can immediately do what the user asked.
-    else {
-      updateActiveEntity();
-    }
-  },
-
-  /**
-   * Render the "Quick edit" contextual link.
-   */
-  render: function () {
-    var activeEntity = this.model.get('activeEntity');
-    var string = (activeEntity !== this.entity) ? Drupal.t('Quick edit') : Drupal.t('Stop quick edit');
-    this.$el.find('a').text(string);
-    return this;
-  },
-
-  /**
-   * Model change handler; hides the contextual links if an editor is active.
-   *
-   * @param Drupal.edit.models.EditAppModel model
-   *   An EditAppModel model.
-   * @param jQuery|null activeEditor
-   *   The active in-place editor (jQuery object) or, if none, null.
-   */
-  toggleContextualLinksVisibility: function (model, activeEditor) {
-    this.$el.parents('.contextual').toggle(activeEditor === null);
-  }
-
-});
-
-})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/toolbar-view.js b/core/modules/edit/js/views/toolbar-view.js
deleted file mode 100644
index f4b2123b5e70242364dbf6bed8be2acd10fca557..0000000000000000000000000000000000000000
--- a/core/modules/edit/js/views/toolbar-view.js
+++ /dev/null
@@ -1,490 +0,0 @@
-/**
- * @file
- * A Backbone View that provides an interactive toolbar (1 per property editor).
- *
- * It listens to state changes of the property editor. It also triggers state
- * changes in response to user interactions with the toolbar, including saving.
- */
-(function ($, _, Backbone, Drupal) {
-
-"use strict";
-
-Drupal.edit = Drupal.edit || {};
-Drupal.edit.views = Drupal.edit.views || {};
-Drupal.edit.views.ToolbarView = Backbone.View.extend({
-
-  editor: null,
-  $storageWidgetEl: null,
-
-  entity: null,
-  predicate : null,
-  editorName: null,
-
-  _loader: null,
-  _loaderVisibleStart: 0,
-
-  _id: null,
-
-  events: {
-    'click.edit button.label': 'onClickInfoLabel',
-    'mouseleave.edit': 'onMouseLeave',
-    'click.edit button.field-save': 'onClickSave',
-    'click.edit button.field-close': 'onClickClose'
-  },
-
-  /**
-   * Implements Backbone Views' initialize() function.
-   *
-   * @param options
-   *   An object with the following keys:
-   *   - editor: the editor object with an 'options' object that has these keys:
-   *      * entity: the VIE entity for the property.
-   *      * property: the predicate of the property.
-   *      * editorName: the editor name.
-   *      * element: the jQuery-wrapped editor DOM element
-   *   - $storageWidgetEl: the DOM element on which the Create Storage widget is
-   *     initialized.
-   */
-  initialize: function(options) {
-    this.editor = options.editor;
-    this.$storageWidgetEl = options.$storageWidgetEl;
-
-    this.entity = this.editor.options.entity;
-    this.predicate = this.editor.options.property;
-    this.editorName = this.editor.options.editorName;
-
-    this._loader = null;
-    this._loaderVisibleStart = 0;
-
-    // Generate a DOM-compatible ID for the toolbar DOM element.
-    this._id = Drupal.edit.util.calcPropertyID(this.entity, this.predicate).replace(/\//g, '_');
-  },
-
-  /**
-   * Listens to editor state changes.
-   */
-  stateChange: function(from, to) {
-    switch (to) {
-      case 'inactive':
-        if (from) {
-          this.remove();
-          if (this.editorName !== 'form') {
-            Backbone.syncDirectCleanUp();
-          }
-        }
-        break;
-      case 'candidate':
-        if (from === 'inactive') {
-          this.render();
-        }
-        else {
-          if (this.editorName !== 'form') {
-            Backbone.syncDirectCleanUp();
-          }
-          // Remove all toolgroups; they're no longer necessary.
-          this.$el
-            .removeClass('edit-highlighted edit-editing')
-            .find('.edit-toolbar .edit-toolgroup').remove();
-          if (from !== 'highlighted' && this.getEditUISetting('padding')) {
-            this._unpad();
-          }
-        }
-        break;
-      case 'highlighted':
-        // As soon as we highlight, make sure we have a toolbar in the DOM (with at least a title).
-        this.startHighlight();
-        break;
-      case 'activating':
-        this.setLoadingIndicator(true);
-        break;
-      case 'active':
-        this.startEdit();
-        this.setLoadingIndicator(false);
-        if (this.getEditUISetting('fullWidthToolbar')) {
-          this.$el.addClass('edit-toolbar-fullwidth');
-        }
-
-        if (this.getEditUISetting('padding')) {
-          this._pad();
-        }
-        if (this.getEditUISetting('unifiedToolbar')) {
-          this.insertWYSIWYGToolGroups();
-        }
-        break;
-      case 'changed':
-        this.$el
-          .find('button.save')
-          .addClass('blue-button')
-          .removeClass('gray-button');
-        break;
-      case 'saving':
-        this.setLoadingIndicator(true);
-        this.save();
-        break;
-      case 'saved':
-        this.setLoadingIndicator(false);
-        break;
-      case 'invalid':
-        this.setLoadingIndicator(false);
-        break;
-    }
-  },
-
-  /**
-   * Saves a property.
-   *
-   * This method deals with the complexity of the editor-dependent ways of
-   * inserting updated content and showing validation error messages.
-   *
-   * One might argue that this does not belong in a view. However, there is no
-   * actual "save" logic here, that lives in Backbone.sync. This is just some
-   * glue code, along with the logic for inserting updated content as well as
-   * showing validation error messages, the latter of which is certainly okay.
-   */
-  save: function() {
-    var that = this;
-    var editor = this.editor;
-    var editableEntity = editor.options.widget;
-    var entity = editor.options.entity;
-    var predicate = editor.options.property;
-
-    // Use Create.js' Storage widget to handle saving. (Uses Backbone.sync.)
-    this.$storageWidgetEl.createStorage('saveRemote', entity, {
-      editor: editor,
-
-      // Successfully saved without validation errors.
-      success: function (model) {
-        editableEntity.setState('saved', predicate);
-
-        // Now that the changes to this property have been saved, the saved
-        // attributes are now the "original" attributes.
-        entity._originalAttributes = entity._previousAttributes = _.clone(entity.attributes);
-
-        // Get data necessary to rerender property before it is unavailable.
-        var updatedProperty = entity.get(predicate + '/rendered');
-        var $propertyWrapper = editor.element.closest('.edit-field');
-        var $context = $propertyWrapper.parent();
-
-        editableEntity.setState('candidate', predicate);
-        // Unset the property, because it will be parsed again from the DOM, iff
-        // its new value causes it to still be rendered.
-        entity.unset(predicate, { silent: true });
-        entity.unset(predicate + '/rendered', { silent: true });
-        // Trigger event to allow for proper clean-up of editor-specific views.
-        editor.element.trigger('destroyedPropertyEditor.edit', editor);
-
-        // Replace the old content with the new content.
-        $propertyWrapper.replaceWith(updatedProperty);
-        Drupal.attachBehaviors($context);
-      },
-
-      // Save attempted but failed due to validation errors.
-      error: function (validationErrorMessages) {
-        editableEntity.setState('invalid', predicate);
-
-        if (that.editorName === 'form') {
-          editor.$formContainer
-            .find('.edit-form')
-            .addClass('edit-validation-error')
-            .find('form')
-            .prepend(validationErrorMessages);
-        }
-        else {
-          var $errors = $('<div class="edit-validation-errors"></div>')
-            .append(validationErrorMessages);
-          editor.element
-            .addClass('edit-validation-error')
-            .after($errors);
-        }
-      }
-    });
-  },
-
-  /**
-   * When the user clicks the info label, nothing should happen.
-   * @note currently redirects the click.edit-event to the editor DOM element.
-   *
-   * @param event
-   */
-  onClickInfoLabel: function(event) {
-    event.stopPropagation();
-    event.preventDefault();
-    // Redirects the event to the editor DOM element.
-    this.editor.element.trigger('click.edit');
-  },
-
-  /**
-   * A mouseleave to the editor doesn't matter; a mouseleave to something else
-   * counts as a mouseleave on the editor itself.
-   *
-   * @param event
-   */
-  onMouseLeave: function(event) {
-    var el = this.editor.element[0];
-    if (event.relatedTarget != el && !$.contains(el, event.relatedTarget)) {
-      this.editor.element.trigger('mouseleave.edit');
-    }
-    event.stopPropagation();
-  },
-
-  /**
-   * Upon clicking "Save", trigger a custom event to save this property.
-   *
-   * @param event
-   */
-  onClickSave: function(event) {
-    event.stopPropagation();
-    event.preventDefault();
-    this.editor.options.widget.setState('saving', this.predicate);
-  },
-
-  /**
-   * Upon clicking "Close", trigger a custom event to stop editing.
-   *
-   * @param event
-   */
-  onClickClose: function(event) {
-    event.stopPropagation();
-    event.preventDefault();
-    this.editor.options.widget.setState('candidate', this.predicate, { reason: 'cancel' });
-  },
-
-  /**
-   * Indicates in the 'info' toolgroup that we're waiting for a server reponse.
-   *
-   * Prevents flickering loading indicator by only showing it after 0.6 seconds
-   * and if it is shown, only hiding it after another 0.6 seconds.
-   *
-   * @param bool enabled
-   *   Whether the loading indicator should be displayed or not.
-   */
-  setLoadingIndicator: function(enabled) {
-    var that = this;
-    if (enabled) {
-      this._loader = setTimeout(function() {
-        that.addClass('info', 'loading');
-        that._loaderVisibleStart = new Date().getTime();
-      }, 600);
-    }
-    else {
-      var currentTime = new Date().getTime();
-      clearTimeout(this._loader);
-      if (this._loaderVisibleStart) {
-        setTimeout(function() {
-          that.removeClass('info', 'loading');
-        }, this._loaderVisibleStart + 600 - currentTime);
-      }
-      this._loader = null;
-      this._loaderVisibleStart = 0;
-    }
-  },
-
-  startHighlight: function() {
-    // We get the label to show for this property from VIE's type system.
-    var label = this.predicate;
-    var attributeDef = this.entity.get('@type').attributes.get(this.predicate);
-    if (attributeDef && attributeDef.metadata) {
-      label = attributeDef.metadata.label;
-    }
-
-    this.$el
-      .addClass('edit-highlighted')
-      .find('.edit-toolbar')
-      // Append the "info" toolgroup into the toolbar.
-      .append(Drupal.theme('editToolgroup', {
-        classes: 'info edit-animate-only-background-and-padding',
-        buttons: [
-          { label: label, classes: 'blank-button label' }
-        ]
-      }));
-
-    // Animations.
-    var that = this;
-    setTimeout(function () {
-      that.show('info');
-    }, 0);
-  },
-
-  startEdit: function() {
-    this.$el
-      .addClass('edit-editing')
-      .find('.edit-toolbar')
-      // Append the "ops" toolgroup into the toolbar.
-      .append(Drupal.theme('editToolgroup', {
-        classes: 'ops',
-        buttons: [
-          { label: Drupal.t('Save'), type: 'submit', classes: 'field-save save gray-button' },
-          { label: '<span class="close">' + Drupal.t('Close') + '</span>', classes: 'field-close close gray-button' }
-        ]
-      }));
-    this.show('ops');
-  },
-
-  /**
-   * Retrieves a setting of the editor-specific Edit UI integration.
-   *
-   * @see Drupal.edit.util.getEditUISetting().
-   */
-  getEditUISetting: function(setting) {
-    return Drupal.edit.util.getEditUISetting(this.editor, setting);
-  },
-
-  /**
-   * Adjusts the toolbar to accomodate padding on the PropertyEditor widget.
-   *
-   * @see PropertyEditorDecorationView._pad().
-   */
-  _pad: function() {
-    // The whole toolbar must move to the top when the property's DOM element
-    // is displayed inline.
-    if (this.editor.element.css('display') === 'inline') {
-      this.$el.css('top', parseInt(this.$el.css('top'), 10) - 5 + 'px');
-    }
-
-    // The toolbar must move to the top and the left.
-    var $hf = this.$el.find('.edit-toolbar-heightfaker');
-    $hf.css({ bottom: '6px', left: '-5px' });
-
-    if (this.getEditUISetting('fullWidthToolbar')) {
-      $hf.css({ width: this.editor.element.width() + 10 });
-    }
-  },
-
-  /**
-   * Undoes the changes made by _pad().
-   *
-   * @see PropertyEditorDecorationView._unpad().
-   */
-  _unpad: function() {
-    // Move the toolbar back to its original position.
-    var $hf = this.$el.find('.edit-toolbar-heightfaker');
-    $hf.css({ bottom: '1px', left: '' });
-
-    if (this.getEditUISetting('fullWidthToolbar')) {
-      $hf.css({ width: '' });
-    }
-  },
-
-  insertWYSIWYGToolGroups: function() {
-    this.$el
-      .find('.edit-toolbar')
-      .append(Drupal.theme('editToolgroup', {
-        id: this.getFloatedWysiwygToolgroupId(),
-        classes: 'wysiwyg-floated',
-        buttons: []
-      }))
-      .append(Drupal.theme('editToolgroup', {
-        id: this.getMainWysiwygToolgroupId(),
-        classes: 'wysiwyg-main',
-        buttons: []
-      }));
-
-    // Animate the toolgroups into visibility.
-    var that = this;
-    setTimeout(function () {
-      that.show('wysiwyg-floated');
-      that.show('wysiwyg-main');
-    }, 0);
-  },
-
-  /**
-   * Renders the Toolbar's markup into the DOM.
-   *
-   * Note: depending on whether the 'display' property of the $el for which a
-   * toolbar is being inserted into the DOM, it will be inserted differently.
-   */
-  render: function () {
-    // Render toolbar.
-    this.setElement($(Drupal.theme('editToolbarContainer', {
-      id: this.getId()
-    })));
-
-    // Insert in DOM.
-    if (this.editor.element.css('display') === 'inline') {
-      this.$el.prependTo(this.editor.element.offsetParent());
-      var pos = this.editor.element.position();
-      this.$el.css('left', pos.left).css('top', pos.top);
-    }
-    else {
-      this.$el.insertBefore(this.editor.element);
-    }
-  },
-
-  /**
-   * Retrieves the ID for this toolbar's container.
-   *
-   * Only used to make sane hovering behavior possible.
-   *
-   * @return string
-   *   A string that can be used as the ID for this toolbar's container.
-   */
-  getId: function () {
-    return 'edit-toolbar-for-' + this._id;
-  },
-
-  /**
-   * Retrieves the ID for this toolbar's floating WYSIWYG toolgroup.
-   *
-   * Used to provide an abstraction for any WYSIWYG editor to plug in.
-   *
-   * @return string
-   *   A string that can be used as the ID.
-   */
-  getFloatedWysiwygToolgroupId: function () {
-    return 'edit-wysiwyg-floated-toolgroup-for-' + this._id;
-  },
-
-  /**
-   * Retrieves the ID for this toolbar's main WYSIWYG toolgroup.
-   *
-   * Used to provide an abstraction for any WYSIWYG editor to plug in.
-   *
-   * @return string
-   *   A string that can be used as the ID.
-   */
-  getMainWysiwygToolgroupId: function () {
-    return 'edit-wysiwyg-main-toolgroup-for-' + this._id;
-  },
-
-  /**
-   * Shows a toolgroup.
-   *
-   * @param string toolgroup
-   *   A toolgroup name.
-   */
-  show: function (toolgroup) {
-    this._find(toolgroup).removeClass('edit-animate-invisible');
-  },
-
-  /**
-   * Adds classes to a toolgroup.
-   *
-   * @param string toolgroup
-   *   A toolgroup name.
-   */
-  addClass: function (toolgroup, classes) {
-    this._find(toolgroup).addClass(classes);
-  },
-
-  /**
-   * Removes classes from a toolgroup.
-   *
-   * @param string toolgroup
-   *   A toolgroup name.
-   */
-  removeClass: function (toolgroup, classes) {
-    this._find(toolgroup).removeClass(classes);
-  },
-
-  /**
-   * Finds a toolgroup.
-   *
-   * @param string toolgroup
-   *   A toolgroup name.
-   */
-  _find: function (toolgroup) {
-    return this.$el.find('.edit-toolbar .edit-toolgroup.' + toolgroup);
-  }
-});
-
-})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/edit/lib/Drupal/edit/EditorBase.php b/core/modules/edit/lib/Drupal/edit/EditorBase.php
index 2bef9cfddcbb72af1aeaa1614167f4db84789088..39abaee738335d8e89f2890d5ba1446f63fd2859 100644
--- a/core/modules/edit/lib/Drupal/edit/EditorBase.php
+++ b/core/modules/edit/lib/Drupal/edit/EditorBase.php
@@ -12,7 +12,7 @@
 use Drupal\field\Plugin\Core\Entity\FieldInstance;
 
 /**
- * Defines a base editor (Create.js PropertyEditor widget) implementation.
+ * Defines a base editor implementation.
  */
 abstract class EditorBase extends PluginBase implements EditPluginInterface {
 
diff --git a/core/modules/edit/lib/Drupal/edit/EditorSelector.php b/core/modules/edit/lib/Drupal/edit/EditorSelector.php
index ce80defc925f9e50a20a7d72d437fa2ee24e65cd..c42cb0242177f43dee86db1f758858ef2e2ab57e 100644
--- a/core/modules/edit/lib/Drupal/edit/EditorSelector.php
+++ b/core/modules/edit/lib/Drupal/edit/EditorSelector.php
@@ -17,7 +17,7 @@
 class EditorSelector implements EditorSelectorInterface {
 
   /**
-   * The manager for editor (Create.js PropertyEditor widget) plugins.
+   * The manager for editor plugins.
    *
    * @var \Drupal\Component\Plugin\PluginManagerInterface
    */
@@ -34,7 +34,7 @@ class EditorSelector implements EditorSelectorInterface {
    * Constructs a new EditorSelector.
    *
    * @param \Drupal\Component\Plugin\PluginManagerInterface
-   *   The manager for Create.js PropertyEditor widget plugins.
+   *   The manager for editor plugins.
    */
   public function __construct(PluginManagerInterface $editor_manager) {
     $this->editorManager = $editor_manager;
@@ -102,22 +102,6 @@ public function getEditorAttachments(array $editor_ids) {
       $attachments[] = $editor->getAttachments();
     }
 
-    // JavaScript settings for Edit.
-    $definitions = $this->editorManager->getDefinitions();
-    foreach ($definitions as $definition) {
-      $attachments[] = array(
-        // This will be used in Create.js' propertyEditorWidgetsConfiguration.
-        'js' => array(
-          array(
-            'type' => 'setting',
-            'data' => array('edit' => array('editors' => array(
-              $definition['id'] => array('widget' => $definition['jsClassName'])
-            )))
-          )
-        )
-      );
-    }
-
     return NestedArray::mergeDeepArray($attachments);
   }
 
diff --git a/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php b/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php
index b613442c251d4b550a8245409cadd8a0b8d9efeb..cecc67694fa1129a123239085b27c729834ca187 100644
--- a/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php
+++ b/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php
@@ -33,7 +33,7 @@ class MetadataGenerator implements MetadataGeneratorInterface {
   protected $editorSelector;
 
   /**
-   * The manager for editor (Create.js PropertyEditor widget) plugins.
+   * The manager for editor plugins.
    *
    * @var \Drupal\Component\Plugin\PluginManagerInterface
    */
diff --git a/core/modules/edit/lib/Drupal/edit/Plugin/EditorManager.php b/core/modules/edit/lib/Drupal/edit/Plugin/EditorManager.php
index 96bbf36395b6643a3314f2ca07b14feeebf2926f..eeb9aae1e4f770968340d9150388401d2c450cd2 100644
--- a/core/modules/edit/lib/Drupal/edit/Plugin/EditorManager.php
+++ b/core/modules/edit/lib/Drupal/edit/Plugin/EditorManager.php
@@ -17,7 +17,7 @@
 /**
  * Editor manager.
  *
- * The "Form" Create.js PropertyEditor widget must always be available.
+ * The form editor must always be available.
  */
 class EditorManager extends PluginManagerBase {
 
diff --git a/core/modules/edit/lib/Drupal/edit/Plugin/edit/editor/DirectEditor.php b/core/modules/edit/lib/Drupal/edit/Plugin/edit/editor/DirectEditor.php
index c3b9b55093e93da3d0e2bcf22c72d2228fc9f7a4..00f8ce26de83d1e8f0a25df92ec53f0da7fdc412 100644
--- a/core/modules/edit/lib/Drupal/edit/Plugin/edit/editor/DirectEditor.php
+++ b/core/modules/edit/lib/Drupal/edit/Plugin/edit/editor/DirectEditor.php
@@ -12,11 +12,10 @@
 use Drupal\field\Plugin\Core\Entity\FieldInstance;
 
 /**
- * Defines the "direct" Create.js PropertyEditor widget.
+ * Defines the direct editor.
  *
  * @Plugin(
  *   id = "direct",
- *   jsClassName = "direct",
  *   module = "edit"
  * )
  */
diff --git a/core/modules/edit/lib/Drupal/edit/Plugin/edit/editor/FormEditor.php b/core/modules/edit/lib/Drupal/edit/Plugin/edit/editor/FormEditor.php
index c8d8bc849128c5a6e5198ac02ef519b13b24c1af..71106050da3c1841d122b3e77c4a58056034481d 100644
--- a/core/modules/edit/lib/Drupal/edit/Plugin/edit/editor/FormEditor.php
+++ b/core/modules/edit/lib/Drupal/edit/Plugin/edit/editor/FormEditor.php
@@ -12,11 +12,10 @@
 use Drupal\field\Plugin\Core\Entity\FieldInstance;
 
 /**
- * Defines the "form" Create.js PropertyEditor widget.
+ * Defines the form editor.
  *
  * @Plugin(
  *   id = "form",
- *   jsClassName = "formEditEditor",
  *   module = "edit"
  * )
  */
diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditLoadingTest.php b/core/modules/edit/lib/Drupal/edit/Tests/EditLoadingTest.php
index e1ce8514c37c598f68ab6b7e5aa591bc52757f8b..d3e5e9739ba2b45f565cd1ac76473d889fd2cc55 100644
--- a/core/modules/edit/lib/Drupal/edit/Tests/EditLoadingTest.php
+++ b/core/modules/edit/lib/Drupal/edit/Tests/EditLoadingTest.php
@@ -110,7 +110,7 @@ function testUserWithPermission() {
     $this->assertRaw('data-edit-id="node/1/body/und/full"');
 
     // Retrieving the metadata should result in a 200 response, containing:
-    //  1. a settings command with correct in-place editor metadata
+    //  1. a settings command with useless metadata: AjaxController is dumb
     //  2. an insert command that loads the required in-place editors
     //  3. a metadata command with correct per-field metadata
     $response = $this->retrieveMetadata(array('node/1/body/und/full'));
@@ -120,15 +120,10 @@ function testUserWithPermission() {
 
     // First command: settings.
     $this->assertIdentical('settings', $ajax_commands[0]['command'], 'The first AJAX command is a settings command.');
-    $edit_editors = array(
-      'direct' => array('widget' => 'direct'),
-      'form' => array('widget' => 'formEditEditor'),
-    );
-    $this->assertIdentical($edit_editors, $ajax_commands[0]['settings']['edit']['editors'], 'The settings command contains the expected settings.');
 
     // Second command: insert libraries into DOM.
     $this->assertIdentical('insert', $ajax_commands[1]['command'], 'The second AJAX command is an append command.');
-    $command = new AppendCommand('body', '<script src="' . file_create_url('core/modules/edit/js/createjs/editingWidgets/formwidget.js') . '?v=' . VERSION . '"></script>' . "\n");
+    $command = new AppendCommand('body', '<script src="' . file_create_url('core/modules/edit/js/editors/formEditor.js') . '?v=' . VERSION . '"></script>' . "\n");
     $this->assertIdentical($command->render(), $ajax_commands[1], 'The append command contains the expected data.');
 
     // Third command: actual metadata.
diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditorSelectionTest.php b/core/modules/edit/lib/Drupal/edit/Tests/EditorSelectionTest.php
index 26ec6c0837d81447910cc77f48e6d4e43065f596..767c570454eb4f8c301aafa722554627782a6fc4 100644
--- a/core/modules/edit/lib/Drupal/edit/Tests/EditorSelectionTest.php
+++ b/core/modules/edit/lib/Drupal/edit/Tests/EditorSelectionTest.php
@@ -16,7 +16,7 @@
 class EditorSelectionTest extends EditTestBase {
 
   /**
-   * The manager for editor (Create.js PropertyEditor widget) plugins.
+   * The manager for editor plugins.
    *
    * @var \Drupal\Component\Plugin\PluginManagerInterface
    */
@@ -106,8 +106,7 @@ function testText() {
    * processing, but with varying text format compatibility.
    */
   function testTextWysiwyg() {
-    // Enable edit_test module so that the 'wysiwyg' Create.js PropertyEditor
-    // widget becomes available.
+    // Enable edit_test module so that the 'wysiwyg' editor becomes available.
     $this->enableModules(array('edit_test'));
 
     $field_name = 'field_textarea';
diff --git a/core/modules/edit/lib/Drupal/edit/Tests/MetadataGeneratorTest.php b/core/modules/edit/lib/Drupal/edit/Tests/MetadataGeneratorTest.php
index 6c4569fde416dd75bda0c01b4054df4a4b9ef368..1394285cd738ddda1a00997d4b824cb0cb9ec228 100644
--- a/core/modules/edit/lib/Drupal/edit/Tests/MetadataGeneratorTest.php
+++ b/core/modules/edit/lib/Drupal/edit/Tests/MetadataGeneratorTest.php
@@ -18,7 +18,7 @@
 class MetadataGeneratorTest extends EditTestBase {
 
   /**
-   * The manager for editor (Create.js PropertyEditor widget) plugins.
+   * The manager for editor plugins.
    *
    * @var \Drupal\Component\Plugin\PluginManagerInterface
    */
@@ -128,8 +128,7 @@ function testEditorWithCustomMetadata() {
     $this->installSchema('system', 'url_alias');
     $this->enableModules(array('user', 'filter'));
 
-    // Enable edit_test module so that the WYSIWYG Create.js PropertyEditor
-    // widget becomes available.
+    // Enable edit_test module so that the WYSIWYG editor becomes available.
     $this->enableModules(array('edit_test'));
 
     // Create a rich text field.
diff --git a/core/modules/edit/tests/modules/lib/Drupal/edit_test/Plugin/edit/editor/WysiwygEditor.php b/core/modules/edit/tests/modules/lib/Drupal/edit_test/Plugin/edit/editor/WysiwygEditor.php
index e40e8b4f10c6970d45d7e0f584ffbf566660e39c..16aa4a899ce37f869e94a2ff134067f081c518f9 100644
--- a/core/modules/edit/tests/modules/lib/Drupal/edit_test/Plugin/edit/editor/WysiwygEditor.php
+++ b/core/modules/edit/tests/modules/lib/Drupal/edit_test/Plugin/edit/editor/WysiwygEditor.php
@@ -12,11 +12,10 @@
 use Drupal\field\Plugin\Core\Entity\FieldInstance;
 
 /**
- * Defines the "wysiwyg" Create.js PropertyEditor widget.
+ * Defines the wysiwyg editor.
  *
  * @Plugin(
  *   id = "wysiwyg",
- *   jsClassName = "not needed for test",
  *   alternativeTo = {"direct"},
  *   module = "edit_test"
  * )
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index 10f938b2b22bfc160a3fffc1572fb2d5fc63cbf5..0f317b99aaaa366932fd124aa38026465569c06d 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -78,12 +78,12 @@ function editor_library_info() {
       array('system', 'jquery.once'),
     ),
   );
-  // Create.js PropertyEditor widget library names begin with "edit.editor".
-  $libraries['edit.editorWidget.editor'] = array(
-    'title' => '"Editor" Create.js PropertyEditor widget',
+
+  $libraries['edit.formattedTextEditor.editor'] = array(
+    'title' => 'Formatted text editor',
     'version' => VERSION,
     'js' => array(
-      $path . '/js/editor.createjs.js' => array(
+      $path . '/js/editor.formattedTextEditor.js' => array(
         'scope' => 'footer',
         'attributes' => array('defer' => TRUE),
       ),
diff --git a/core/modules/editor/js/editor.createjs.js b/core/modules/editor/js/editor.createjs.js
deleted file mode 100644
index 693d9d91fcf0e857312a327317931ae5e3c70498..0000000000000000000000000000000000000000
--- a/core/modules/editor/js/editor.createjs.js
+++ /dev/null
@@ -1,157 +0,0 @@
-/**
- * @file
- * Text editor-based Create.js widget for processed text content in Drupal.
- *
- * Depends on editor.module. Works with any (WYSIWYG) editor that implements the
- * editor.js API, including the optional attachInlineEditor() and onChange()
- * methods.
- * For example, assuming that a hypothetical editor's name was "Magical Editor"
- * and its editor.js API implementation lived at Drupal.editors.magical, this
- * JavaScript would use:
- *  - Drupal.editors.magical.attachInlineEditor()
- *  - Drupal.editors.magical.onChange()
- *  - Drupal.editors.magical.detach()
- */
-(function (jQuery, Drupal, drupalSettings) {
-
-"use strict";
-
-// @todo D8: use jQuery UI Widget bridging.
-// @see http://drupal.org/node/1874934#comment-7124904
-jQuery.widget('Midgard.editor', jQuery.Midgard.editWidget, {
-
-  textFormat: null,
-  textFormatHasTransformations: null,
-  textEditor: null,
-
-  /**
-   * Implements Create.editWidget.getEditUISettings.
-   */
-  getEditUISettings: function () {
-    return { padding: true, unifiedToolbar: true, fullWidthToolbar: true };
-  },
-
-  /**
-   * Implements jQuery.widget._init.
-   *
-   * @todo D8: Remove this.
-   * @see http://drupal.org/node/1874934
-   */
-  _init: function () {},
-
-  /**
-   * Implements Create.editWidget._initialize.
-   */
-  _initialize: function () {
-    var propertyID = Drupal.edit.util.calcPropertyID(this.options.entity, this.options.property);
-    var metadata = Drupal.edit.metadataCache[propertyID].custom;
-
-    this.textFormat = drupalSettings.editor.formats[metadata.format];
-    this.textFormatHasTransformations = metadata.formatHasTransformations;
-    this.textEditor = Drupal.editors[this.textFormat.editor];
-  },
-
-  /**
-   * Implements Create.editWidget.stateChange.
-   */
-  stateChange: function (from, to) {
-    var that = this;
-    switch (to) {
-      case 'inactive':
-        break;
-
-      case 'candidate':
-        // Detach the text editor when entering the 'candidate' state from one
-        // of the states where it could have been attached.
-        if (from !== 'inactive' && from !== 'highlighted') {
-            this.textEditor.detach(this.element.get(0), this.textFormat);
-        }
-        break;
-
-      case 'highlighted':
-        break;
-
-      case 'activating':
-        // When transformation filters have been been applied to the processed
-        // text of this field, then we'll need to load a re-processed version of
-        // it without the transformation filters.
-        if (this.textFormatHasTransformations) {
-          var propertyID = Drupal.edit.util.calcPropertyID(this.options.entity, this.options.property);
-          this._getUntransformedText(propertyID, this.element, function (untransformedText) {
-            that.element.html(untransformedText);
-            that.options.activated();
-          });
-        }
-        // When no transformation filters have been applied: start WYSIWYG
-        // editing immediately!
-        else {
-          this.options.activated();
-        }
-        break;
-
-      case 'active':
-        this.textEditor.attachInlineEditor(
-          this.element.get(0),
-          this.textFormat,
-          this.toolbarView.getMainWysiwygToolgroupId(),
-          this.toolbarView.getFloatedWysiwygToolgroupId()
-        );
-        // Set the state to 'changed' whenever the content has changed.
-        this.textEditor.onChange(this.element.get(0), function (html) {
-          that.options.changed(html);
-        });
-        break;
-
-      case 'changed':
-        break;
-
-      case 'saving':
-        break;
-
-      case 'saved':
-        break;
-
-      case 'invalid':
-        break;
-    }
-  },
-
-  /**
-   * Loads untransformed text for a given property.
-   *
-   * More accurately: it re-processes processed text to exclude transformation
-   * filters used by the text format.
-   *
-   * @param String propertyID
-   *   A property ID that uniquely identifies the given property.
-   * @param jQuery $editorElement
-   *   The property's PropertyEditor DOM element.
-   * @param Function callback
-   *   A callback function that will receive the untransformed text.
-   *
-   * @see \Drupal\editor\Ajax\GetUntransformedTextCommand
-   */
-  _getUntransformedText: function (propertyID, $editorElement, callback) {
-    // Create a Drupal.ajax instance to load the form.
-    Drupal.ajax[propertyID] = new Drupal.ajax(propertyID, $editorElement, {
-      url: Drupal.edit.util.buildUrl(propertyID, drupalSettings.editor.getUntransformedTextURL),
-      event: 'editor-internal.editor',
-      submit: { nocssjs : true },
-      progress: { type : null } // No progress indicator.
-    });
-    // Implement a scoped editorGetUntransformedText AJAX command: calls the
-    // callback.
-    Drupal.ajax[propertyID].commands.editorGetUntransformedText = function(ajax, response, status) {
-      callback(response.data);
-      // Delete the Drupal.ajax instance that called this very function.
-      delete Drupal.ajax[propertyID];
-      $editorElement.off('editor-internal.editor');
-    };
-    // This will ensure our scoped editorGetUntransformedText AJAX command
-    // gets called.
-    $editorElement.trigger('editor-internal.editor');
-  }
-
-});
-
-})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/editor/js/editor.formattedTextEditor.js b/core/modules/editor/js/editor.formattedTextEditor.js
new file mode 100644
index 0000000000000000000000000000000000000000..91e35217e544c70d34551aae54ad4932cd98c5d1
--- /dev/null
+++ b/core/modules/editor/js/editor.formattedTextEditor.js
@@ -0,0 +1,189 @@
+/**
+ * @file
+ * Text editor-based in-place editor for processed text content in Drupal.
+ *
+ * Depends on editor.module. Works with any (WYSIWYG) editor that implements the
+ * editor.js API, including the optional attachInlineEditor() and onChange()
+ * methods.
+ * For example, assuming that a hypothetical editor's name was "Magical Editor"
+ * and its editor.js API implementation lived at Drupal.editors.magical, this
+ * JavaScript would use:
+ *  - Drupal.editors.magical.attachInlineEditor()
+ */
+(function ($, Drupal, drupalSettings) {
+
+"use strict";
+
+Drupal.edit.editors.editor = Drupal.edit.EditorView.extend({
+
+  // The text format for this field.
+  textFormat: null,
+
+  // Indicates whether this text format has transformations.
+  textFormatHasTransformations: null,
+
+  // Stores a reference to the text editor object for this field.
+  textEditor: null,
+
+  // Stores the textual DOM element that is being in-place edited.
+  $textElement: null,
+
+  /**
+   * {@inheritdoc}
+   */
+  initialize: function (options) {
+    Drupal.edit.EditorView.prototype.initialize.call(this, options);
+
+    var metadata = Drupal.edit.metadata.get(this.fieldModel.id, 'custom');
+    this.textFormat = drupalSettings.editor.formats[metadata.format];
+    this.textFormatHasTransformations = metadata.formatHasTransformations;
+    this.textEditor = Drupal.editors[this.textFormat.editor];
+
+    // Store the actual value of this field. We'll need this to restore the
+    // original value when the user discards his modifications.
+    this.$textElement = this.$el.find('.field-item:first');
+    this.model.set('originalValue', this.$textElement.html());
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  getEditedElement: function () {
+    return this.$textElement;
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  stateChange: function (fieldModel, state) {
+    var editorModel = this.model;
+    var from = fieldModel.previous('state');
+    var to = state;
+    switch (to) {
+      case 'inactive':
+        break;
+
+      case 'candidate':
+        // Detach the text editor when entering the 'candidate' state from one
+        // of the states where it could have been attached.
+        if (from !== 'inactive' && from !== 'highlighted') {
+          this.textEditor.detach(this.$textElement.get(0), this.textFormat);
+        }
+        if (from === 'invalid') {
+          this.removeValidationErrors();
+        }
+        break;
+
+      case 'highlighted':
+        break;
+
+      case 'activating':
+        // When transformation filters have been been applied to the processed
+        // text of this field, then we'll need to load a re-processed version of
+        // it without the transformation filters.
+        if (this.textFormatHasTransformations) {
+          var $textElement = this.$textElement;
+          this._getUntransformedText(function (untransformedText) {
+            $textElement.html(untransformedText);
+            fieldModel.set('state', 'active');
+          });
+        }
+        // When no transformation filters have been applied: start WYSIWYG
+        // editing immediately!
+        else {
+          // Defer updating the model until the current state change has
+          // propagated, to not trigger a nested state change event.
+          _.defer(function () {
+            fieldModel.set('state', 'active');
+          });
+        }
+        break;
+
+      case 'active':
+        var textElement = this.$textElement.get(0);
+        var toolbarView = fieldModel.toolbarView;
+        this.textEditor.attachInlineEditor(
+          textElement,
+          this.textFormat,
+          toolbarView.getMainWysiwygToolgroupId(),
+          toolbarView.getFloatedWysiwygToolgroupId()
+        );
+        // Set the state to 'changed' whenever the content has changed.
+        this.textEditor.onChange(textElement, function (htmlText) {
+          editorModel.set('currentValue', htmlText);
+          fieldModel.set('state', 'changed');
+        });
+        break;
+
+      case 'changed':
+        break;
+
+      case 'saving':
+        if (from === 'invalid') {
+          this.removeValidationErrors();
+        }
+        this.save();
+        break;
+
+      case 'saved':
+        break;
+
+      case 'invalid':
+        this.showValidationErrors();
+        break;
+    }
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  getEditUISettings: function () {
+    return { padding: true, unifiedToolbar: true, fullWidthToolbar: true };
+  },
+
+  /**
+   * {@inheritdoc}
+   */
+  revert: function () {
+    this.$textElement.html(this.model.get('originalValue'));
+  },
+
+  /**
+   * Loads untransformed text for this field.
+   *
+   * More accurately: it re-processes processed text to exclude transformation
+   * filters used by the text format.
+   *
+   * @param Function callback
+   *   A callback function that will receive the untransformed text.
+   *
+   * @see \Drupal\editor\Ajax\GetUntransformedTextCommand
+   */
+  _getUntransformedText: function (callback) {
+    var fieldID = this.fieldModel.id;
+
+    // Create a Drupal.ajax instance to load the form.
+    Drupal.ajax[fieldID] = new Drupal.ajax(fieldID, this.$el, {
+      url: Drupal.edit.util.buildUrl(fieldID, drupalSettings.editor.getUntransformedTextURL),
+      event: 'editor-internal.editor',
+      submit: { nocssjs : true },
+      progress: { type : null } // No progress indicator.
+    });
+
+    // Implement a scoped editorGetUntransformedText AJAX command: calls the
+    // callback.
+    Drupal.ajax[fieldID].commands.editorGetUntransformedText = function (ajax, response, status) {
+      callback(response.data);
+      // Delete the Drupal.ajax instance that called this very function.
+      delete Drupal.ajax[fieldID];
+      this.$el.off('editor-internal.editor');
+    };
+
+    // This will ensure our scoped editorGetUntransformedText AJAX command
+    // gets called.
+    this.$el.trigger('editor-internal.editor');
+  }
+
+});
+
+})(jQuery, Drupal, drupalSettings);
diff --git a/core/modules/editor/js/editor.js b/core/modules/editor/js/editor.js
index 6483170bf99418d272c111caf5151ff4bb9d2214..9bbce1d2e5ba1ecb33ee859fde837b5004a41f00 100644
--- a/core/modules/editor/js/editor.js
+++ b/core/modules/editor/js/editor.js
@@ -83,8 +83,9 @@ Drupal.behaviors.editor = {
       var $this = $(this);
       var activeFormatID = $this.val();
       var field = behavior.findFieldForFormatSelector($this);
-
-      Drupal.editorDetach(field, settings.editor.formats[activeFormatID], trigger);
+      if (activeFormatID in settings.editor.formats) {
+        Drupal.editorDetach(field, settings.editor.formats[activeFormatID], trigger);
+      }
     });
   },
 
diff --git a/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
index 81c23d7d5378364d83f4b4c933446b9795065533..8e6311ccd5b69d8eba8794c641ef1e7047343710 100644
--- a/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
+++ b/core/modules/editor/lib/Drupal/editor/Plugin/edit/editor/Editor.php
@@ -14,11 +14,10 @@
 use Drupal\field\Plugin\Core\Entity\FieldInstance;
 
 /**
- * Defines the "editor" Create.js PropertyEditor widget.
+ * Defines the formatted text editor.
  *
  * @Plugin(
  *   id = "editor",
- *   jsClassName = "editor",
  *   alternativeTo = {"direct"},
  *   module = "editor"
  * )
@@ -90,8 +89,8 @@ public function getAttachments() {
     // Get the attachments for all text editors that the user might use.
     $attachments = $manager->getAttachments($formats);
 
-    // Also include editor.module's Create.js PropertyEditor widget.
-    $attachments['library'][] = array('editor', 'edit.editorWidget.editor');
+    // Also include editor.module's formatted text editor.
+    $attachments['library'][] = array('editor', 'edit.formattedTextEditor.editor');
 
     return $attachments;
   }
diff --git a/core/modules/editor/lib/Drupal/editor/Tests/EditIntegrationTest.php b/core/modules/editor/lib/Drupal/editor/Tests/EditIntegrationTest.php
index 1dcadc8383fea6e2843e1e662dafaea5aad7f81f..6e8e36f0b8d6c6d09a5013c6f948b5d52bf15a73 100644
--- a/core/modules/editor/lib/Drupal/editor/Tests/EditIntegrationTest.php
+++ b/core/modules/editor/lib/Drupal/editor/Tests/EditIntegrationTest.php
@@ -21,7 +21,7 @@
 class EditIntegrationTest extends EditTestBase {
 
   /**
-   * The manager for editor (Create.js PropertyEditor widget) plug-ins.
+   * The manager for editor plug-ins.
    *
    * @var \Drupal\Component\Plugin\PluginManagerInterface
    */
@@ -143,7 +143,7 @@ function testEditorSelection() {
   }
 
   /**
-   * Tests (custom) metadata when the "Editor" Create.js editor is used.
+   * Tests (custom) metadata when the formatted text editor is used.
    */
   function testMetadata() {
     $this->editorManager = new EditorManager($this->container->get('container.namespaces'));
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 7cb55485aa83b01b7dc2ab131465b8ec7c5cbddc..c3c6584cceaa6782f709b147bdb3248d022bbd0f 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2104,35 +2104,6 @@ function system_library_info() {
     ),
   );
 
-  // VIE.
-  $libraries['vie.core'] = array(
-    'title' => 'VIE.js core (excluding services, views and xdr)',
-    'website' => 'http://viejs.org/',
-    'version' => '2.1.0alpha2',
-    'js' => array(
-      'core/misc/vie/vie-core.js' => array('group' => JS_LIBRARY),
-    ),
-    'dependencies' => array(
-      array('system', 'jquery'),
-      array('system', 'underscore'),
-      array('system', 'backbone'),
-    ),
-  );
-
-  // Create.
-  $libraries['create.editonly'] = array(
-    'title' => 'Create.js edit-only (editing features only)',
-    'website' => 'http://backbonejs.org/',
-    'version' => '1.0.0-dev',
-    'js' => array(
-      'core/misc/create/create-editonly.js' => array('group' => JS_LIBRARY),
-    ),
-    'dependencies' => array(
-      array('system', 'vie.core'),
-      array('system', 'jquery.ui.widget'),
-    ),
-  );
-
   // Cookie.
   $libraries['jquery.cookie'] = array(
     'title' => 'Cookie',