 core/modules/edit/css/edit.css                     |  457 +++
 core/modules/edit/edit.info                        |    6 +
 core/modules/edit/edit.module                      |  401 +++
 core/modules/edit/images/attention.png             |    6 +
 core/modules/edit/images/close.png                 |   78 +
 core/modules/edit/images/throbber.gif              |   73 +
 core/modules/edit/includes/form.inc                |  143 +
 core/modules/edit/includes/missing-api.inc         |   43 +
 core/modules/edit/includes/pages.inc               |  172 +
 core/modules/edit/js/app.js                        |  314 ++
 core/modules/edit/js/backbone.drupalform.js        |  158 +
 core/modules/edit/js/createjs/editable.js          |   43 +
 .../createjs/editingWidgets/drupalalohawidget.js   |  159 +
 .../editingWidgets/drupalcontenteditablewidget.js  |  113 +
 .../edit/js/createjs/editingWidgets/formwidget.js  |  151 +
 core/modules/edit/js/createjs/storage.js           |   11 +
 core/modules/edit/js/edit.js                       |   47 +
 core/modules/edit/js/lib/create.js                 | 1643 +++++++++
 core/modules/edit/js/lib/vie.js                    | 3682 ++++++++++++++++++++
 core/modules/edit/js/models/edit-app-model.js      |   22 +
 core/modules/edit/js/routers/edit-router.js        |   54 +
 core/modules/edit/js/theme.js                      |  156 +
 core/modules/edit/js/util.js                       |  140 +
 core/modules/edit/js/viejs/SparkEditService.js     |  208 ++
 core/modules/edit/js/views/fielddecorator-view.js  |  324 ++
 core/modules/edit/js/views/menu-view.js            |   42 +
 core/modules/edit/js/views/modal-view.js           |  108 +
 core/modules/edit/js/views/overlay-view.js         |   84 +
 core/modules/edit/js/views/toolbar-view.js         |  448 +++
 .../field/formatter/TextDefaultFormatter.php       |    3 +
 .../Plugin/field/formatter/TextPlainFormatter.php  |    3 +
 .../formatter/TextSummaryOrTrimmedFormatter.php    |    3 +
 .../field/formatter/TextTrimmedFormatter.php       |    3 +
 33 files changed, 9298 insertions(+)

diff --git a/core/modules/edit/css/edit.css b/core/modules/edit/css/edit.css
new file mode 100644
index 0000000..9109d62
--- /dev/null
+++ b/core/modules/edit/css/edit.css
@@ -0,0 +1,457 @@
+/**
+ * Animations.
+ */
+.edit-animate-invisible {
+  opacity: 0 !important;
+}
+
+.edit-animate-fast {
+-webkit-transition: all .2s ease;
+   -moz-transition: all .2s ease;
+    -ie-transition: all .2s ease;
+     -o-transition: all .2s ease;
+        transition: all .2s ease;
+}
+
+.edit-animate-default {
+  -webkit-transition: all .4s ease;
+     -moz-transition: all .4s ease;
+      -ie-transition: all .4s ease;
+       -o-transition: all .4s ease;
+          transition: all .4s ease;
+}
+
+.edit-animate-slow {
+-webkit-transition: all .6s ease;
+   -moz-transition: all .6s ease;
+    -ie-transition: all .6s ease;
+     -o-transition: all .6s ease;
+        transition: all .6s ease;
+}
+
+.edit-animate-delay-veryfast {
+  -webkit-transition-delay: .05s;
+}
+
+.edit-animate-delay-fast {
+  -webkit-transition-delay: .2s;
+}
+
+.edit-animate-delay-default {
+  -webkit-transition-delay: .4s;
+}
+
+.edit-animate-delay-slow {
+  -webkit-transition-delay: .6s;
+}
+
+.edit-animate-disable-width {
+  -webkit-transition: width 0s;
+}
+
+.edit-animate-only-visibility {
+  -webkit-transition: opacity .2s ease;
+     -moz-transition: opacity .2s ease;
+      -ie-transition: opacity .2s ease;
+       -o-transition: opacity .2s ease;
+          transition: opacity .2s ease;
+  -webkit-transition-delay: 0s;
+}
+
+
+
+
+/**
+ * Edit's bar — inspired by core's toolbar.module & shortcut.module.
+ */
+#editbar,
+#editbar * {
+  border: 0;
+  font-size: 100%;
+  line-height: inherit;
+  list-style: none;
+  margin: 0;
+  outline: 0;
+  padding: 0;
+  text-align: left; /* LTR */
+  vertical-align: baseline;
+}
+#editbar {
+  position: relative;
+  background: #666;
+  color: #ccc;
+  font: normal small "Lucida Grande", Verdana, sans-serif;
+  margin: 0 -20px;
+  padding: 0 20px;
+  -moz-box-shadow: 0 3px 20px #000;
+  -webkit-box-shadow: 0 3px 20px #000;
+  box-shadow: 0 3px 20px #000;
+  z-index: 500;
+}
+#editbar ul {
+  padding: 5px 0 2px 0;
+  height: 28px;
+  line-height: 24px;
+  margin-left:5px; /* LTR */
+}
+#editbar ul li,
+#editbar ul li a {
+  float: left; /* LTR */
+  padding: 0 5px 0 5px;
+  margin-right: 5px; /* LTR */
+  -moz-border-radius: 5px;
+  -webkit-border-radius: 5px;
+  border-radius: 5px;
+}
+#editbar a {
+  padding: 5px 10px 5px 5px;
+  line-height: 24px;
+  color: #fefefe;
+  font-size: .846em;
+  text-decoration: none;
+}
+#editbar a:focus,
+#editbar a:hover,
+#editbar a.active {
+  color: #fff;
+}
+#editbar ul li a:focus,
+#editbar ul li a:hover,
+#editbar ul li a.active:focus {
+  background: #555;
+}
+#editbar ul li a.active:hover,
+#editbar ul li a.active {
+  background: #000;
+}
+
+
+
+
+/**
+ * Edit mode: overlay + candidate editables + editables being edited.
+ *
+ * Note: every class is prefixed with "edit-" to prevent collisions with modules
+ * or themes. In IPE-specific DOM subtrees, this is not necessary.
+ */
+
+#edit_overlay {
+  position: fixed;
+  z-index: 250;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(255,255,255,.5);
+  top: 0px; /* offset for navbar, modified later */
+  left: 0px;
+}
+
+/* Editable. */
+.edit-editable {
+  z-index: 300;
+  position: relative;
+}
+.edit-editable:focus {
+  outline: none;
+}
+.edit-field.edit-editable,
+.edit-field.edit-type-direct .edit-editable {
+  box-shadow: 0px 0px 1px 1px #4D9DE9;
+}
+
+/* Highlighted (hovered) editable. */
+.edit-editable.edit-highlighted {
+  min-width: 200px; /* TODO: we even need them to be at least fairly wide! */
+}
+.edit-field.edit-editable.edit-highlighted,
+.edit-form.edit-editable.edit-highlighted,
+.edit-field.edit-type-direct .edit-editable.edit-highlighted {
+  box-shadow: 0px 0px 1px 1px #0199FF, 0px 0px 3px 3px rgba(153, 153, 153, .5);
+}
+.edit-field.edit-editable.edit-highlighted.edit-validation-error,
+.edit-form.edit-editable.edit-highlighted.edit-validation-error,
+.edit-field.edit-type-direct .edit-editable.edit-highlighted.edit-validation-error {
+  box-shadow: 0px 0px 1px 1px red, 0px 0px 3px 3px rgba(153, 153, 153, .5);
+}
+.edit-form.edit-editable .form-item .error {
+  border: 1px solid #eea0a0;
+}
+
+
+/* Editing (focused) editable. */
+.edit-form.edit-editable.edit-editing,
+.edit-field.edit-type-direct .edit-editable.edit-editing {
+  /* In the latest design, there's no special styling when editing as opposed to
+     just hovering. */
+}
+
+
+
+
+/**
+ * Edit mode: modal.
+ */
+#edit_modal {
+  z-index: 350;
+  position: fixed;
+  top: 40%;
+  left: 40%;
+  box-shadow: 3px 3px 5px #333;
+  background-color: white;
+  border: 1px solid #0199FF;
+  font-family: 'Droid sans', 'Lucida Grande', sans-serif;
+}
+
+#edit_modal .main {
+  font-size: 130%;
+  margin: 25px;
+  padding-left: 40px;
+  background: transparent url('../images/attention.png') no-repeat;
+}
+
+#edit_modal .actions {
+  border-top: 1px solid #ddd;
+  padding: 3px inherit;
+  text-align: right;
+  background: #f5f5f5;
+}
+
+/* Modal active: prevent user from interacting with toolbar & editables. */
+.edit-form-container.edit-belowoverlay,
+.edit-toolbar-container.edit-belowoverlay,
+.edit-validation-errors.edit-belowoverlay {
+  z-index: 210;
+}
+.edit-editable.edit-belowoverlay {
+  z-index: 200;
+}
+
+
+
+
+/**
+ * Edit mode: type=direct.
+ */
+.edit-validation-errors {
+  z-index: 300;
+  position: relative;
+}
+
+.edit-validation-errors .messages.error {
+  position: absolute;
+  top: 6px;
+  left: -5px;
+  margin: 0;
+  border: none;
+  box-shadow: 0px 0px 1px 1px red, 0px 0px 3px 3px rgba(153, 153, 153, .5);
+  background-color: white;
+}
+
+
+
+
+/**
+ * Edit mode: type=form.
+ */
+#edit_backstage {
+  display: none;
+}
+
+.edit-form {
+  position: absolute;
+  z-index: 300;
+  box-shadow: 0 0 30px 4px #4F4F4F;
+  max-width: 35em;
+}
+
+.edit-form .placeholder {
+  min-height: 22px;
+}
+
+/* Default form styling overrides. */
+.edit-form form { padding: 1em; }
+.edit-form .form-item { margin: 0; }
+.edit-form .form-wrapper { margin: .5em; }
+.edit-form .form-actions { display: none; }
+.edit-form input { max-width: 100%; }
+
+
+
+
+/**
+ * Edit mode: toolbars
+ */
+
+/* Trick: wrap statically positioned elements in relatively positioned element
+   without changing its location. This allows us to absolutely position the
+   toolbar.
+*/
+.edit-toolbar-container,
+.edit-form-container {
+  position: relative;
+  padding: 0;
+  border: 0;
+  margin: 0;
+  vertical-align: baseline;
+  z-index: 310;
+  -webkit-user-select: none;
+   -khtml-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+       -o-user-select: none;
+          user-select: none;
+}
+
+.edit-toolbar-heightfaker {
+  height: auto;
+  position: absolute;
+  bottom: 1px;
+  box-shadow: 0px 0px 1px 1px #0199FF, 0px 0px 3px 3px rgba(153, 153, 153, .5);
+  background: #FFF;
+}
+
+/* The toolbar; these are not necessarily visible. */
+.edit-toolbar {
+  position: relative;
+  height: 100%;
+  font-family: 'Droid sans', 'Lucida Grande', sans-serif;
+}
+.edit-toolbar-heightfaker {
+  clip: rect(-1000px, 1000px, auto, -1000px); /* Remove bottom box-shadow. */
+}
+/* Exception: when used for a directly WYSIWYG editable field that is actively
+   being edited. */
+.edit-type-direct-with-wysiwyg .edit-editing .edit-toolbar-heightfaker {
+  width: 100%;
+  clip: auto;
+}
+
+
+/* The toolbar contains toolgroups; these are visible. */
+.edit-toolgroup {
+  float: left; /* LTR */
+}
+
+/* Info toolgroup. */
+.edit-toolgroup.info {
+  float: left; /* LTR */
+  font-weight: bolder;
+  padding: 0 5px;
+  background: #FFF url('../images/throbber.gif') no-repeat -60px 60px;
+}
+.edit-toolgroup.info.loading {
+  padding-right: 35px;
+  background-position: 90% 50%;
+}
+
+/* Operations toolgroup. */
+.edit-toolgroup.ops {
+  float: right; /* LTR */
+  margin-left: 5px;
+}
+
+.edit-toolgroup.wysiwyg-tabs {
+  float: right;
+}
+.edit-toolgroup.wysiwyg {
+  clear: left;
+  width: 100%;
+  padding-left: none;
+}
+
+
+
+/**
+ * Edit mode: buttons (in both modal and toolbar).
+ */
+#edit_modal a,
+.edit-toolbar a {
+  float: left; /* LTR */
+  display: block;
+  height: 21px;
+  min-width: 21px;
+  padding: 3px 6px 3px 6px;
+  margin: 4px 5px 1px 0;
+  border: 1px solid #fff;
+  border-radius: 3px;
+  color: white;
+  text-decoration: none;
+  font-size: 13px;
+}
+#edit_modal a {
+  float: none;
+  display: inline-block;
+}
+
+#edit_modal a:link,
+#edit_modal a:visited,
+#edit_modal a:hover,
+#edit_modal a:active,
+.edit-toolbar a:link,
+.edit-toolbar a:visited,
+.edit-toolbar a:hover,
+.edit-toolbar a:active {
+  text-decoration: none;
+}
+
+/* Button with icons. */
+#edit_modal a span,
+.edit-toolbar a span {
+  width: 22px;
+  height: 19px;
+  display: block;
+  float: left;
+}
+.edit-toolbar a span.close {
+  background: url('../images/close.png') no-repeat 2px 2px;
+}
+
+.edit-toolbar a span.close:hover {
+  /* TODO: use a different "close" image */
+}
+
+
+.edit-toolbar a.blank-button {
+  color: black;
+}
+
+#edit_modal a.blue-button,
+.edit-toolbar a.blue-button {
+  color: white;
+  background-image: -webkit-linear-gradient(top, #6fc2f2 0%, #4e97c0 100%);
+  background-image: linear-gradient(top, #6fc2f2 0%, #4e97c0 100%);
+  border-radius: 5px;
+}
+
+#edit_modal a.gray-button,
+.edit-toolbar a.gray-button {
+  color: #666;
+  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #ccc 100%);
+  background-image: linear-gradient(top, #f5f5f5 0%, #ccc 100%);
+  border-radius: 5px;
+}
+
+#edit_modal a img.gray-button.close img, .gray-button.save img, .blue-button.save img,
+.edit-toolbar a img.gray-button.close img, .gray-button.save img, .blue-button.save img {
+  padding: 0;
+}
+
+.gray-button img, .blue-button img,
+.gray-button img, .blue-button img {
+  padding-right: 5px;
+}
+
+#edit_modal a.blue-button:hover,
+.edit-toolbar a.blue-button:hover,
+#edit_modal a.blue-button:active,
+.edit-toolbar a.blue-button:active {
+  border: 1px solid #55a5d3;
+  box-shadow: 0px 2px 1px rgba(0,0,0,0.2);
+}
+
+#edit_modal a.gray-button:hover,
+.edit-toolbar a.gray-button:hover,
+#edit_modal a.gray-button:active,
+.edit-toolbar a.gray-button:active {
+  border: 1px solid #cdcdcd;
+  box-shadow: 0px 2px 1px rgba(0,0,0,0.1);
+}
diff --git a/core/modules/edit/edit.info b/core/modules/edit/edit.info
new file mode 100644
index 0000000..5298534
--- /dev/null
+++ b/core/modules/edit/edit.info
@@ -0,0 +1,6 @@
+name = Edit
+description = In-place content editing.
+package = Core
+core = 8.x
+
+dependencies[] = field
diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module
new file mode 100644
index 0000000..0e11895
--- /dev/null
+++ b/core/modules/edit/edit.module
@@ -0,0 +1,401 @@
+<?php
+
+/**
+ * @file
+ * Provides in-place content editing functionality for fields.
+ *
+ * The Edit module makes content editable in-place. Rather than having to visit
+ * a separate page to edit content, it may be edited in-place.
+ *
+ * Technically, this module adds classes and data- attributes to fields and
+ * entities, enabling them for in-place editing.
+ */
+
+use Drupal\Core\Template\Attribute;
+use Drupal\field\FieldInstance;
+
+// @todo Get rid of missing-api.inc by improving Entity API.
+module_load_include('inc', 'edit', 'includes/missing-api');
+
+/**
+ * Implements hook_menu()
+ */
+function edit_menu() {
+  $items = array();
+  $items['admin/edit/field/%/%/%/%/%'] = array(
+    // Access is controlled after we have inspected the entity, which can't
+    // easily happen until after the callback.
+    'access arguments' => array(TRUE),
+    'access callback'  => TRUE,
+    'page callback'    => 'edit_field_edit',
+    'page arguments'   => array(3, 4, 5, 6, 7),
+    'file'             => 'includes/pages.inc',
+    'delivery callback'=> 'ajax_deliver',
+    'theme callback'   => 'ajax_base_page_theme',
+  );
+  $items['admin/render-without-transformations/field/%/%/%/%/%'] = array(
+    // Access is controlled after we have inspected the entity, which can't
+    // easily happen until after the callback.
+    'access arguments' => array(TRUE),
+    'access callback'  => TRUE,
+    'page callback'    => 'edit_text_field_render_without_transformation_filters',
+    'page arguments'   => array(3, 4, 5, 6, 7),
+    'file'             => 'includes/pages.inc',
+    'delivery callback'=> 'ajax_deliver',
+    'theme callback'   => 'ajax_base_page_theme',
+  );
+
+  return $items;
+}
+
+/**
+ * Implements hook_page_alter().
+ */
+function edit_page_alter(&$page) {
+  if (path_is_admin(current_path())) {
+    return;
+  }
+
+  $page['page_top']['edit'] = array(
+    'view_edit_toggle' => array(
+      '#prefix' => '<div id="editbar"><h2 class="element-invisible">' . t('In-place edit operations') . '</h2><ul id="edit_view-edit-toggles">',
+      '#suffix' => '</ul></div>',
+      'content' => array(
+        array(
+          '#theme' => 'menu_local_task',
+          '#link' => array('title' => t('View'), 'href' => current_path(), 'localized_options' => array('fragment' => 'view', 'attributes' => array('class' => array('edit_view-edit-toggle', 'edit-view')))),
+          '#active' => TRUE,
+        ),
+        array(
+          '#theme' => 'menu_local_task',
+          '#link' => array('title' => t('Quick edit'), 'href' => current_path(), 'localized_options' => array('fragment' => 'quick-edit', 'attributes' => array('class' => array('edit_view-edit-toggle', 'edit-edit')))),
+        ),
+      ),
+      '#attached' => array(
+        'library' => array(
+          array('edit', 'edit'),
+        ),
+      ),
+    ),
+    '#post_render' => array(
+      'edit_editbar_post_render',
+    ),
+  );
+}
+
+/**
+ * Post-render function to remove the editbar if nothing editable is present.
+ */
+function edit_editbar_post_render($html) {
+  global $editbar;
+  return ($editbar !== TRUE) ? '' : $html;
+}
+
+/**
+ * Implements hook_library().
+ */
+function edit_library_info() {
+  $path = drupal_get_path('module', 'edit');
+  $libraries['edit'] = array(
+    'title' => 'Edit: in-place editing',
+    'website' => 'http://drupal.org/project/edit',
+    'version' => VERSION,
+    'js' => array(
+      $path . '/js/edit.js' => array(
+        'defer' => TRUE,
+      ),
+      $path . '/js/util.js' => array(
+        'defer' => TRUE,
+      ),
+      $path . '/js/theme.js' => array(
+        'defer' => TRUE,
+      ),
+      // Basic settings.
+      array(
+        'data' => array('edit' => array(
+          'fieldFormURL' => url('admin/edit/field/!entity_type/!id/!field_name/!langcode/!view_mode'),
+          'rerenderProcessedTextURL' => url('admin/render-without-transformations/field/!entity_type/!id/!field_name/!langcode/!view_mode'),
+          'context' => 'body',
+        )),
+        'type' => 'setting',
+      ),
+    ),
+    'css' => array(
+      $path . '/css/edit.css',
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.form'),
+      array('system', 'drupal.form'),
+      array('system', 'drupal.ajax'),
+      array('system', 'backbone'),
+      array('edit', 'edit.createjs'),
+    ),
+  );
+
+  $libraries['edit.createjs'] = array(
+    'title' => 'CreateJS and deps',
+    'website' => 'http://createjs.org',
+    'version' => NULL,
+    'js' => array(
+      $path . '/js/lib/create.js' => array('defer' => TRUE),
+      $path . '/js/viejs/SparkEditService.js' => array('defer' => TRUE),
+      $path . '/js/createjs/editable.js' => array('defer' => TRUE),
+      $path . '/js/createjs/storage.js' => array('defer' => TRUE),
+      $path . '/js/createjs/editingWidgets/formwidget.js' => array('defer' => TRUE),
+      $path . '/js/createjs/editingWidgets/drupalcontenteditablewidget.js' => array('defer' => TRUE),
+      $path . '/js/createjs/editingWidgets/drupalalohawidget.js' => array('defer' => TRUE),
+      $path . '/js/views/menu-view.js' => array('defer' => TRUE),
+      $path . '/js/views/overlay-view.js' => array('defer' => TRUE),
+      $path . '/js/views/toolbar-view.js' => array('defer' => TRUE),
+      $path . '/js/views/fielddecorator-view.js' => array('defer' => TRUE),
+      $path . '/js/views/modal-view.js' => array('defer' => TRUE),
+      $path . '/js/routers/edit-router.js' => array('defer' => TRUE),
+      $path . '/js/models/edit-app-model.js' => array('defer' => TRUE),
+      $path . '/js/backbone.drupalform.js' => array('defer' => TRUE),
+      $path . '/js/app.js' => array('defer' => TRUE),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.ui.widget'),
+      array('system', 'backbone'),
+      array('edit', 'edit.vie'),
+    ),
+  );
+
+  $libraries['edit.vie'] = array(
+    'title' => 'Vienna IKS Editables',
+    'website' => 'http://wiki.iks-project.eu/index.php/VIE',
+    'version' => '2.0',
+    'js' => array(
+      $path . '/js/lib/vie.js' => array('defer' => TRUE),
+    ),
+    'dependencies' => array(
+      array('system', 'backbone'),
+    ),
+  );
+
+  // Only add dependencies on the WYSIWYG editor when it's actually available.
+  if (count(module_implements('edit_wysiwyg_info'))) {
+    $libraries['edit']['dependencies'][] = _edit_get_wysiwyg_info('javascript library');
+  }
+
+  return $libraries;
+}
+
+/**
+ * Implements hook_field_attach_view_alter().
+ */
+function edit_field_attach_view_alter(&$output, $context) {
+  // Special case for this special mode.
+  if ($context['display'] == 'edit-render-without-transformation-filters') {
+    $children = element_children($output);
+    $field_name = reset($children);
+    $langcode = $output[$field_name]['#language'];
+    foreach (array_keys($output[$field_name]['#items']) as $item) {
+      $text      = $output[$field_name]['#items'][$item]['value'];
+      $format_id = $output[$field_name]['#items'][$item]['format'];
+      $untransformed = check_markup($text, $format_id, $langcode, FALSE, array(FILTER_TYPE_TRANSFORM_REVERSIBLE, FILTER_TYPE_TRANSFORM_IRREVERSIBLE));
+      $output[$field_name][$item]['#markup'] = $untransformed;
+    }
+  }
+}
+
+/**
+ * Implements hook_preprocess_HOOK() for field.tpl.php.
+ */
+function edit_preprocess_field(&$variables) {
+  $entity = $variables['element']['#object'];
+  $field_name = $variables['element']['#field_name'];
+  $langcode = $variables['element']['#language'];
+  $view_mode = $variables['element']['#view_mode'];
+  $formatter_type = $variables['element']['#formatter'];
+  $items = $entity->{$field_name}[$langcode];;
+  $instance = field_info_instance($entity->entityType(), $field_name, $entity->bundle());
+
+  $entity_access = edit_entity_access('update', $entity->entityType(), $entity);
+  $field_access = field_access('edit', $field_name, $entity->entityType(), $entity);
+  $editability = _edit_analyze_field_editability($items, $instance, $formatter_type);
+  if ($entity_access && $field_access && $editability != 'disabled') {
+    global $editbar;
+    $editbar = TRUE;
+
+    // Mark this field as editable and provide metadata through data- attributes.
+    $variables['attributes']['data-edit-field-label'] = $instance->definition['label'];
+    $variables['attributes']['data-edit-id'] = $entity->entityType() . ':' . $entity->id() . ':' . $field_name . ':' . $langcode . ':' . $view_mode;
+    $variables['attributes']['class'][] = 'edit-field';
+    $variables['attributes']['class'][] = 'edit-allowed';
+    $variables['attributes']['class'][] = 'edit-type-' . $editability;
+    if ($editability == 'direct-with-wysiwyg') {
+      $variables['attributes']['class'][] = 'edit-type-direct';
+      $format_id = $entity->{$field_name}[$langcode][0]['format'];
+      _edit_preprocess_field_wysiwyg($variables, $format_id);
+    }
+  }
+}
+
+/**
+ * Sets attributes on a field that have 'direct-with-wysiwyg' editability.
+ *
+ * @param array $variables
+ *   An associative array containing: the key 'attributes'. See the
+ *   theme_field() function for information about these variables.
+ * @param string $format_id
+ *   A text format id.
+ *
+ * @see theme_field()
+ */
+function _edit_preprocess_field_wysiwyg(&$variables, $format_id) {
+  // Let the WYSIWYG editor know the text format.
+  $variables['attributes']['data-edit-text-format'] = $format_id;
+
+  // Ensure the WYSIWYG editor has the necessary text format related
+  // metadata.
+  $settings_callback = _edit_get_wysiwyg_info('javascript settings callback');
+  $settings_callback();
+
+  // Let the JavaScript logic know whether transformation filters are used
+  // in this format, so it can decide whether to re-render the text or not.
+  $filter_types = filter_get_filter_types_by_format($format_id);
+  $transformation_filter_types = array(
+    FILTER_TYPE_TRANSFORM_REVERSIBLE,
+    FILTER_TYPE_TRANSFORM_IRREVERSIBLE
+  );
+  if (count(array_intersect($transformation_filter_types, $filter_types))) {
+    $variables['attributes']['class'][] = 'edit-text-with-transformation-filters';
+  }
+  else {
+    $variables['attributes']['class'][] = 'edit-text-without-transformation-filters';
+  }
+}
+
+/**
+ * Determines editability given a field, its instance info and its formatter.
+ *
+ * @param array $field
+ *   The field's field array.
+ * @param FieldInstance $instance
+ *   The field's instance info.
+ * @param string $formatter_type
+ *   The field's formatter type name.
+ *
+ * @return string
+ *   The editability: 'disabled', 'form', 'direct' or 'direct-with-wysiwyg'.
+ */
+function _edit_analyze_field_editability($items, FieldInstance $instance, $formatter_type) {
+  $field_name = $instance['field_name'];
+
+  // If the formatter doesn't contain the edit property, default it to 'form'
+  // editability, which should always work.
+  $formatter_info = field_info_formatter_types($formatter_type);
+  if (empty($formatter_info['edit']['editability'])) {
+    $formatter_info['edit']['editability'] = 'form';
+  }
+
+  $editability = $formatter_info['edit']['editability'];
+
+  // If editing is explicitly disabled for this field, return early to avoid
+  // any further processing.
+  if ($editability == 'disabled') {
+    return;
+  }
+
+  // If directly editable, check the cardinality. If the cardinality is greater
+  // than 1, use a form to edit the field.
+  if ($editability == 'direct') {
+    $field = field_info_field($field_name);
+    if ($field['cardinality'] != 1) {
+      $editability = 'form';
+    }
+  }
+
+  // If still directly editable, check whether "regular" direct editing (almost
+  // bare contentEditable) editing should be used or WYSIWYG-based direct
+  // editing should be used. In the latter case
+  if ($editability == 'direct') {
+    // If this field is configured to not use text processing; it is plain text
+    // "regular" direct editing should be used, which is already set.
+    // On the other hand, if it is configured to use text processing; then we
+    // must check whether 'direct-with-wysiwyg' or 'form' editability should be
+    // used.
+    if (!empty($instance['settings']['text_processing'])) {
+      $format_id = $items[0]['format'];
+      $editability = _edit_wysiwyg_analyze_field_editability($format_id);
+    }
+  }
+
+  return $editability;
+}
+
+/**
+ * Determines editability given a directly editable field with text processing.
+ *
+ * Given a text field (with cardinality 1) that defaults to 'direct' editability
+ * and has text processing enabled, check whether the text format allows it to
+ * use WYSIWYG-powered direct editing or whether 'form' based editing needs to
+ * be used.
+ *
+ * @param string|NULL $format_id
+ *   The field's current text format.
+ *
+ * @return string
+ *   The editability: 'direct-with-wysiwyg' or 'form'.
+ */
+function _edit_wysiwyg_analyze_field_editability($format_id = NULL) {
+  // If no format is assigned yet, (e.g. when the field is still empty (NULL)),
+  // then provide form-based editing, so that the user is able to select a text
+  // format. (Direct editing doesn't allow the user to change the format.)
+  if (empty($format_id)) {
+    return 'form';
+  }
+  // If no WYSIWYG editor is available, then fall back to form-based editing.
+  elseif (count(_edit_get_wysiwyg_info()) == 0) {
+    return 'form';
+  }
+  // If the WYSIWYG editor is not compatible with the current format, then fall
+  // back to form-based editing.
+  else {
+    $compatibility_callback = _edit_get_wysiwyg_info('format compatibility callback');
+    if (!$compatibility_callback($format_id)) {
+      return 'form';
+    }
+  }
+
+  return 'direct-with-wysiwyg';
+}
+
+/**
+ * Retrieves a list of all available WYSIWYG integration for Edit. Only the
+ * first is actually used.
+ *
+ * @todo Convert to the plug-in system!
+ *
+ * @param string $key
+ *   The key to get a value for.
+ *
+ * @see hook_edit_wysiwyg_info()
+ * @see hook_edit_wysiwyg_info_alter()
+ */
+function _edit_get_wysiwyg_info($key = NULL) {
+  $edit_wysiwyg_info = &drupal_static(__FUNCTION__, array());
+
+  if (empty($edit_wysiwyg_info)) {
+    $cache = cache()->get('edit_wysiwyg_info');
+    if ($cache === FALSE) {
+      // Rebuild the cache and save it.
+      $edit_wysiwyg_info = module_invoke_all('edit_wysiwyg_info');
+      drupal_alter('edit_wysiwyg_info', $edit_wysiwyg_info);
+    }
+    else {
+      $edit_wysiwyg_info = $cache->data;
+    }
+  }
+
+  if (isset($key)) {
+    $modules = array_keys($edit_wysiwyg_info);
+    $first = $modules[0];
+
+    return $edit_wysiwyg_info[$first][$key];
+  }
+  else {
+    return $edit_wysiwyg_info;
+  }
+}
diff --git a/core/modules/edit/images/attention.png b/core/modules/edit/images/attention.png
new file mode 100644
index 0000000..3c833c9
--- /dev/null
+++ b/core/modules/edit/images/attention.png
@@ -0,0 +1,6 @@
+PNG
+
+   IHDR          Ǎ   tEXtSoftware Adobe ImageReadyqe<  $iTXtXML:com.adobe.xmp     <?xpacket begin="﻿" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27        "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CS6 (Macintosh)" xmpMM:InstanceID="xmp.iid:6D93FC30979811E1A7F2C5FBDFE31AF7" xmpMM:DocumentID="xmp.did:6D93FC31979811E1A7F2C5FBDFE31AF7"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:4650FC73972811E1A7F2C5FBDFE31AF7" stRef:documentID="xmp.did:4650FC74972811E1A7F2C5FBDFE31AF7"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>!  	IDATxڼV]HQ.aRcEPP$Rڏd`ѣ[o	`JH$f{:n;θ3zg{{2q(^uYnx2K_B3Y_us۝Uޖ	ai/0p6~s _LyXڜ~=F0p͏dV7>fP,;Ӊ^Nw-ȁ`ޔԜiW1PǫC;5^q	wp}C& Yk{Ao[9܄Àu-`pP:iю	_tU" zAL)DZ0pU4"c2Jo^h'{Rrt6R<H;>K3P	{]<Fے"%آv#LB%#䶁eބSj\Y zTV|+lIUa%
+,4f {euU#nKΓQv
+%lkyN/0	Ӭk%tf:kӶvioI'eTLNEm1b %q(B+`IIV#k,$%$=<f@ k;:Fpb!rEF0
+"tCzȅ9Ï>6㢝2'"ܼCmw{8{%ބ݆?u*q X L    IENDB`
\ No newline at end of file
diff --git a/core/modules/edit/images/close.png b/core/modules/edit/images/close.png
new file mode 100644
index 0000000..2f5d665
--- /dev/null
+++ b/core/modules/edit/images/close.png
@@ -0,0 +1,78 @@
+PNG
+
+   IHDR         a  
+iTXtXML:com.adobe.xmp     <?xpacket begin="﻿" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.0-c060 61.134777, 2010/02/12-17:32:00        ">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+  <rdf:Description rdf:about=""
+    xmlns:xmpRights="http://ns.adobe.com/xap/1.0/rights/"
+    xmlns:dc="http://purl.org/dc/elements/1.1/"
+    xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"
+    xmlns:plus_1_="http://ns.useplus.org/ldf/xmp/1.0/"
+    xmlns:xmp="http://ns.adobe.com/xap/1.0/"
+    xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
+    xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
+   xmpRights:Marked="True"
+   xmp:MetadataDate="2011-01-25T13:55:09+01:00"
+   xmpMM:InstanceID="xmp.iid:C4D5F5568228E011989CC0A1AD02B5C2"
+   xmpMM:DocumentID="xmp.did:C4D5F5568228E011989CC0A1AD02B5C2"
+   xmpMM:OriginalDocumentID="xmp.did:C4D5F5568228E011989CC0A1AD02B5C2">
+   <xmpRights:UsageTerms>
+    <rdf:Alt>
+     <rdf:li xml:lang="x-default">Creative Commons Attribution-NonCommercial license</rdf:li>
+    </rdf:Alt>
+   </xmpRights:UsageTerms>
+   <dc:creator>
+    <rdf:Seq>
+     <rdf:li>Gentleface custom toolbar icons design</rdf:li>
+    </rdf:Seq>
+   </dc:creator>
+   <dc:description>
+    <rdf:Alt>
+     <rdf:li xml:lang="x-default">Wireframe mono toolbar icons</rdf:li>
+    </rdf:Alt>
+   </dc:description>
+   <dc:subject>
+    <rdf:Bag>
+     <rdf:li>custom icon design</rdf:li>
+     <rdf:li>toolbar icons</rdf:li>
+     <rdf:li>custom icons</rdf:li>
+     <rdf:li>interface design</rdf:li>
+     <rdf:li>ui design</rdf:li>
+     <rdf:li>gui design</rdf:li>
+     <rdf:li>taskbar icons</rdf:li>
+    </rdf:Bag>
+   </dc:subject>
+   <dc:rights>
+    <rdf:Alt>
+     <rdf:li xml:lang="x-default">Creative Commons Attribution-NonCommercial license</rdf:li>
+    </rdf:Alt>
+   </dc:rights>
+   <Iptc4xmpCore:CreatorContactInfo
+    Iptc4xmpCore:CiUrlWork="http://www.gentleface.com"/>
+   <plus_1_:ImageCreator>
+    <rdf:Seq>
+     <rdf:li
+      plus_1_:ImageCreatorName="gentleface.com"/>
+    </rdf:Seq>
+   </plus_1_:ImageCreator>
+   <plus_1_:CopyrightOwner>
+    <rdf:Seq>
+     <rdf:li
+      plus_1_:CopyrightOwnerName="gentleface.com"/>
+    </rdf:Seq>
+   </plus_1_:CopyrightOwner>
+   <xmpMM:History>
+    <rdf:Seq>
+     <rdf:li
+      stEvt:action="saved"
+      stEvt:instanceID="xmp.iid:C4D5F5568228E011989CC0A1AD02B5C2"
+      stEvt:when="2011-01-25T13:55:09+01:00"
+      stEvt:changed="/metadata"/>
+    </rdf:Seq>
+   </xmpMM:History>
+  </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
+<?xpacket end="r"?>KK   tEXtSoftware Adobe ImageReadyqe<   <tEXtALTTag This is the icon from Gentleface.com free icons set. k   tEXtCopyright ROYALTY FREE LICENSE ًi   EiTXtDescription     This is the icon from Gentleface.com free icons set.    #iTXtCopyright     ROYALTY FREE LICENSE ']
+J  iIDATxڤRK@M"(nq17ě87HN0G!9Bn2ApejܹIIK'&LQ]W/mv}4Ow!rs~$l6o"n@i X}x8bIZfɜnכ,-r*˒Eap6CQuL	VVM</8NQZ_	+	-/X,"4Oq>..c2j:Em	ꈠJͳ@qk$	`өltDkd2a.ÑiBrvxݗqߓFd4/ ^A/`0`޲2S^` K&FS    IENDB`
\ No newline at end of file
diff --git a/core/modules/edit/images/throbber.gif b/core/modules/edit/images/throbber.gif
new file mode 100644
index 0000000..58f4a42
--- /dev/null
+++ b/core/modules/edit/images/throbber.gif
@@ -0,0 +1,73 @@
+GIF89a    			      !!!"""######$$$%%%%%%&&&&&&&&&''''''''''''''''''''''''(((((()))******,,,...000222444555666888999:::;;;<<<===???AAACCCEEEFFFHHHJJJKKKLLLLLLMMMNNNOOOOOOPPPPPPQQQRRRRRRRRRSSSSSSSSSTTTTTTTTTTTTUUUUUUVVVXXXYYYZZZ\\\\\\]]]]]]^^^^^^______``````bbbdddfffggghhhiiijjjlllnnnooovvv{{{!NETSCAPE2.0   ! 
+   ,        	8P@{%#\-"V	!׾AWhc}(qR2@(	JK>^WK>$T`4U՚h&d`pʘNlxeIZ-(+8jZ+T5r.RI>x_IecʰF&Kr*\5}¤d<MUXJ@g,JnFW(\\qJTKF*.XrJ/Vz)ի>W05U/OT鐒}Պ%+>Kb')E,FՔVS@ ! 
+   ,      									
+
+
+
+
+
+   """"""###$$$&&&(((***,,,...111222333444444555666666777888888999::::::;;;<<<<<<===>>>>>>??????@@@@@@@@@AAABBBBBBCCCCCCDDDDDDEEEEEEGGGHHHJJJLLLOOORRRUUUWWWXXXZZZ[[[]]]______``````aaaaaaaaaaaabbbbbbbbbccccccdddfffggghhhhhhkkkmmmqqqvvvzzz||| 	8pHQ"Ȱ(]!P#ҏB	aGKc[E	rcHx}hR`G?YTERl%F Sz"*№/T.ZΟR49EUY.]%L+Es4UT%O)!(mUSoZV+R"BT˖8qtYį""՘q[uER<aBqU*)%Kd	JmR`ekX+N>uI*_H=|uJGJ\ku	QZt	6ՔJt0*	D%4#L,E4LqJ ! 
+   ,         			
+
+
+!!!"""""""""######$$$$$$%%%&&&&&&''''''((()))***+++,,,---...000111222333333333444555666777888888999:::;;;<<<<<<===???@@@BBBCCCEEEFFFHHHHHHHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQQQQRRRSSSSSSTTTTTTUUUUUUVVVVVVWWWWWWXXXYYY[[[\\\___aaacccdddfffhhhiiikkkmmmoooqqqssstttuuuwwwxxxzzz{{{|||}}}~~~ 	8PM]#Yb5#0I	3	پJ3죅%usc}8R2S{fV%c>d\'LѝNmZ%K/ZH0@(L&PD`Zr0x[2i8	#8'Qu3z.5X/YLI.x˙@tSĴ$:帱,yD)Dqjt PJDJS&Q"I(^ǌ52Y･S+cNEkUVDE)
+d'K'd0JS5mn/>MXU+-Q,	tJ&4uK2!# {VS@ ! 
+   ,         			   !!!"""###$$$%%%&&&&&&'''((()))***+++,,,...000333666777888888999999999:::::::::::::::;;;;;;;;;<<<===>>>???@@@AAACCCDDDEEEFFFHHHJJJLLLMMMOOOPPPRRRSSSUUUXXXYYYZZZ[[[\\\^^^^^^___`````````aaabbbcccccceeefffgggggghhhiiiiiijjjjjjkkklllnnnoooppprrrsssuuuwwwzzz||| 	8Tk#fWq$SG	ֲD
+*<"#QƁ8]cHE?:),K}bB٬XDVU԰+PefH#('P\	&0רJF=F"	2kM)>dV*Tkwe'<UdEOBOiZlx'k@(LJr&u).0fیyqYkYTrQɑEŚљ0cѲ*J+=Mda{Ok߬KES,q_Q	s,	J%35#zvR@ ! 
+   ,         			
+
+
+   !!!"""###%%%&&&'''((()))***+++,,,---...///000111222333444555666888999:::;;;<<<======>>>>>>??????@@@@@@AAABBBCCCDDDFFFHHHJJJLLLMMMNNNOOOOOOPPPQQQRRRRRRSSSTTTTTTUUUWWWYYY[[[]]]^^^___```aaabbbcccdddeeegggiiilllooorrrvvvxxx{{{ 	8pעU{=#0.h]$U!T	ښ$
+"+A*@d('8$OZ#'վhL)mV@ERT+\5hJQ4Yd6, CI4c%Tbv>U7*c[@YM"ifeEVȬ`DJlY;8Q=HfR AI3gDT뚔ɕ3hD*ثHIca5	Rj!4ɕ0־Yda5TD7a@HQDc4
+T&e ! 
+   ,         			
+
+
+   !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666666777888::::::;;;<<<>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUUUUVVVWWWYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddfffmmmsssvvvyyy||| 	8Z\M4hmAR%
+b$$>$NZ碴^B%>gVGT}>A:M_!SM@'&c@d*qJk%]3}ɜoÐT2U-ܘÊG2<	g5~]Uf$)2<8#R3s!1&@Ir3
+	)還	3h@	
+<Idhؾhc	RjрN4._&[FUpB-") 0f4T& ! 
+   ,         			
+
+
+   !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIIIIKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkkkkkmmmnnnooopppuuuxxx 	8P$X5#Yea	
+%Ж&W	2i!,?	dUƄa2 IN
+<P],s520Y5s*1M~J:5'MˎJYk0⤋1aH&qEA4<O%W
+KըR1K@Z+óD1;|Jq)<E.ŦdI[lEph%?djǙIqJVWcI}eD2(ձf83GUngϯ}zD;!Gˤjɉ;Ae
+j;ء/1gU&t+"P!.uR3t	 ! 
+   ,         			
+
+
+   !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrr{{{ 	8PX5#Yec	%Ж&W	2ia,?	dhUƄa2 IN
+<P%^,q520Y5s*1M~H25'MˎJYk0⤋1aH&qEA4<O%W
+KըR1K@%*Y~$AJ<p(Q+1-[2zם::Z(й3y2N⊅q`)93־Z:+Nzꐲcq]F%s@$ࠊ<o	3gRʴ㠇YeF!B9I2P;$S@ ! 
+   ,         			
+
+
+   !!!"""###$$$%%%&&&'''((()))***+++,,,---...///000111222333444555666777888999::::::<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJJJJLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrzzz 	8PX5#Yec	%Ж&W	2ia,?	dhUƄa2 IN
+<P%^,q520Y5s*1M~H25'MˎJYk0⤋1aH&qEA4<O%W
+KըR1K@%*Y~$AJ<p(Q+/\2K$إF
+vgG%9"CYwJ-%)-}3))ΝVyHBȐ%ƞZvTz7֑v߳T;1a}A)~!$ͱ!JY'@<1_ ! 
+   ,         			   !!!"""###$$$%%%&&&''''''((()))+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKKKKMMMNNNOOOPPPQQQRRRRRRSSSTTTUUUWWWXXXYYYYYYZZZ[[[\\\^^^______aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnoooqqqrrrsss 	8PX5#Yec	
+%W	2ia,?i2*cBH0~J$a&CeBx,gZ&?x˓ePȃZyEV#yϢ C`ҫjTXL]IcSXu2pLY rX,
+/\1OإF
+0=>^^R§-<ZKZ`"aV6kJDM	[V'U@@H"+Q*Bu/0<'*$18Ck$"K_{1@zA@TL(P(u S@ ! 
+   ,      						
+
+
+
+
+
+   !!!###$$$%%%&&&''''''(((***+++---...///000000111222333333555666777999::::::;;;<<<===>>>>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMOOOPPPQQQRRRRRRSSSTTTUUUWWWXXXYYYYYYZZZ[[[\\\^^^___```aaaaaacccdddeeefffggghhhiiilllooosssxxx||| 	8$Z5#-fi
+%VX	Bi!A˄UƄa2UIN
+<
+Q^,u9BZ3LH2UXL̎Jyk*!h/ǆ"}z(PN*Z8pU<0TR2EW-S	Ԡ/Q@Z2iϢ%՝Q2]r*aPBf&0|"ȫ+f&!$l_S*'ZzVsEEn~o9@իT>c-\&8zrz4"&J	J!n1sKP2%@}%S@ ! 
+   ,      						
+
+
+
+
+
+   !!!"""######$$$%%%%%%%%%&&&'''(((***+++---///111222444444555666777888999:::;;;<<<<<<<<<======>>>???@@@AAABBBCCCDDDFFFFFFFFFGGGGGGHHHHHHHHHIIIJJJJJJLLLMMMOOOPPPQQQRRRSSSTTTUUUVVVXXXYYY[[[]]]^^^```aaacccdddfffgggjjjmmmppprrrtttuuuvvvxxx{{{}}} 	8֤Uy#P#\W%$PVT	Vj`׾U:iѩIc}MR 2:p٩Erl.?{QtmdLXHd3eAADPS%m2&p$Ī	QT
+BADJTMV)^ukћ57
+eYp|gN=iÇuժ)ѫ.9*Es1OxD	lӢDG<|	#xk)c&-䱔:p˴I׫QBJ!ʴ,}HJqZUiQ)r}MJTj_|0G52Ms&rX*	I"'KQ5CGrX@ ;
\ No newline at end of file
diff --git a/core/modules/edit/includes/form.inc b/core/modules/edit/includes/form.inc
new file mode 100644
index 0000000..6936a2f
--- /dev/null
+++ b/core/modules/edit/includes/form.inc
@@ -0,0 +1,143 @@
+<?php
+
+/**
+ * @file
+ * Form callbacks for the Edit module.
+ */
+
+/**
+ * Field editing form. (For editing a field instance.)
+ *
+ * Proudly found elsewhere: FAPE module's fape_field_edit_form() and friends.
+ */
+function edit_field_form($form, &$form_state) {
+  $entity = $form_state['entity'];
+  $langcode = $form_state['langcode'];
+
+  // Attach the actual field form.
+  $options = array('field_name' =>  $form_state['field_name']);
+  field_attach_form($entity->entityType(), $entity, $form, $form_state, $langcode, $options);
+
+  $form['#validate'][] = 'edit_field_form_validate';
+  // @todo Verify that this is indeed not necessary anymore, see edit_field_form_validate().
+  // $form['#submit'][] = '';
+
+  // Add revisions form items if necessary.
+  // @todo We may be able to get rid of this when http://drupal.org/node/1678002 is solved.
+  list($use_revisions, $control_revisions) = edit_entity_allows_revisions($entity->entityType(), $entity->bundle(), $entity);
+  if ($use_revisions) {
+    $form_state['use revisions'] = TRUE;
+    $form['revision_information'] = array(
+      '#weight' => 11,
+    );
+
+    $form['revision_information']['revision'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Create new revision'),
+      '#default_value' => $entity->revision,
+      '#id' => 'edit-revision',
+      '#access' => $control_revisions,
+    );
+
+    if ($control_revisions || $entity->revision) {
+      $form['revision_information']['log'] = array(
+        '#type' => 'textarea',
+        '#title' => t('Log message'),
+        '#description' => t('Provide an explanation of the changes you are making. This will help other authors understand your motivations.'),
+        '#default_value' => $entity->log,
+      );
+
+      if ($control_revisions) {
+        $form['revision_information']['log']['#dependency'] = array('edit-revision' => array(1));
+      }
+    }
+    $form['#submit'][] = 'edit_field_form_revision_submit';
+  }
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+  );
+
+  // Simplify the form.
+  _simplify_edit_field_edit_form($form);
+
+  return $form;
+}
+
+/**
+ * Helper function to simplify the field edit form for in-place editing.
+ */
+function _simplify_edit_field_edit_form(&$form) {
+  $elements = element_children($form);
+
+  // Required internal form properties.
+  $internal_elements = array('actions', 'form_build_id', 'form_token', 'form_id');
+
+  // Calculate the remaining form elements.
+  $remaining_elements = array_diff($elements, $internal_elements);
+
+  // Only simplify the form if there is a single element remaining.
+  if (count($remaining_elements) === 1) {
+    $element = $remaining_elements[0];
+
+    if ($form[$element]['#type'] == 'container') {
+      $language = $form[$element]['#language'];
+      $children = element_children($form[$element][$language]);
+
+      // Certain fields require different processing depending on the form
+      // structure.
+      if (count($children) == 0) {
+        // Checkbox elements don't have a title.
+        if ($form[$element][$language]['#type'] != 'checkbox') {
+          $form[$element][$language]['#title_display'] = 'invisible';
+        }
+      }
+      elseif (count($children) == 1) {
+        $form[$element][$language][0]['value']['#title_display'] = 'invisible';
+
+        // UX improvement: make the number of rows of textarea form elements
+        // fit the content. (i.e. no wads of whitespace)
+        if (isset($form[$element][$language][0]['value']['#type'])
+            && $form[$element][$language][0]['value']['#type'] == 'textarea')
+        {
+          $lines = count(explode("\n", $form[$element][$language][0]['value']['#default_value']));
+          $form[$element][$language][0]['value']['#rows'] = $lines + 1;
+        }
+      }
+    }
+  }
+
+  // Make it easy for the JavaScript to identify the submit button.
+  $form['actions']['submit']['#attributes'] = array('class' => array('edit-form-submit'));
+}
+
+/**
+ * Validate field editing form.
+ *
+ * @todo: clean up once http://drupal.org/node/1846648 is solved.
+ */
+function edit_field_form_validate($form, &$form_state) {
+  $entity = $form_state['entity'];
+  $options = array('field_name' =>  $form_state['field_name']);
+
+  // 'submit' in D8 is for "building the entity object", not for actual
+  // submission. It appears though that if there were no validation errors, it
+  // is submitted automatically.
+  field_attach_submit($entity->entityType(), $entity, $form, $form_state, $options);
+
+  // Validation.
+  field_attach_form_validate($entity->entityType(), $entity, $form, $form_state, $options);
+}
+
+/**
+ * Submit callback that handles entity revisioning.
+ */
+function edit_field_form_revision_submit($form, &$form_state) {
+  $entity = $form_state['entity'];
+  if (!empty($form_state['use revisions'])) {
+    $entity->revision = $form_state['values']['revision'];
+    $entity->log = $form_state['values']['log'];
+  }
+}
diff --git a/core/modules/edit/includes/missing-api.inc b/core/modules/edit/includes/missing-api.inc
new file mode 100644
index 0000000..1b2f08a
--- /dev/null
+++ b/core/modules/edit/includes/missing-api.inc
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @ingroup Missing in Entity API.
+ * @{
+ */
+
+// @todo Entity Access API: http://drupal.org/node/1696660#comment-6618944
+function edit_entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
+  if ($entity_type == 'node') {
+    return node_access($op, $entity, $account);
+  }
+
+  // Currently, only editing of nodes is supported. See the above issue.
+  return FALSE;
+}
+
+// @todo Entity API check to see whether a specific entity supports revisions.
+// Proudly found elsewhere: FAPE module's _fape_entity_allows_revisions().
+function edit_entity_allows_revisions($entity_type, $bundle, $entity) {
+  $retval = array(FALSE, FALSE);
+
+  switch ($entity_type) {
+    case 'node':
+      $node_options = variable_get('node_options_' . $bundle, array('status', 'promote'));
+      $retval[0] = in_array('revision', $node_options);
+      $retval[1] = user_access('administer nodes');
+      break;
+
+    default:
+      $entity_info = entity_get_info($entity_type);
+      $retval[0] = !empty($entity_info['revision table']);
+      break;
+  }
+
+  $entity->revision = $retval[0];
+  $entity->log = '';
+  return $retval;
+}
+
+/**
+ * @} End of "ingroup Missing in Entity API.".
+ */
diff --git a/core/modules/edit/includes/pages.inc b/core/modules/edit/includes/pages.inc
new file mode 100644
index 0000000..dfaae0f
--- /dev/null
+++ b/core/modules/edit/includes/pages.inc
@@ -0,0 +1,172 @@
+<?php
+/**
+ * @file
+ * AJAX endpoints to retrieve & save subforms for fields and re-render fields.
+ */
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\edit\Ajax\FieldFormCommand;
+use Drupal\edit\Ajax\FieldFormSavedCommand;
+use Drupal\edit\Ajax\FieldFormValidationErrorsCommand;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+/**
+ * Page callback: Provides editing of entity fields.
+ *
+ * @param string $entity_type
+ *   The entity type of the entity that is being edited.
+ * @param int $entity_id
+ *   The entity ID of the entity that is being edited.
+ * @param string $field_name
+ *   The name of the field that is being edited.
+ * @param string $langcode
+ *   The name of the language for which the field is being edited.
+ * @param string $view_mode
+ *   The view mode the field should be rerendered in.
+ * @return array
+ *   A render array.
+ */
+function edit_field_edit($entity_type, $entity_id, $field_name, $langcode, $view_mode) {
+  $response = new AjaxResponse();
+
+  // Ensure the entity type is valid.
+  if (empty($entity_type)) {
+    throw new NotFoundHttpException();
+  }
+
+  $entity_info = entity_get_info($entity_type);
+  if (!$entity_info) {
+    throw new NotFoundHttpException();
+  }
+
+  $entity = entity_load($entity_type, $entity_id);
+  if (!$entity) {
+    throw new NotFoundHttpException();
+  }
+
+  // Ensure a valid language code is set.
+  $langcode = field_valid_language($langcode);
+
+  // Ensure access to update this particular entity is granted.
+  if (!edit_entity_access('update', $entity_type, $entity)) {
+    throw new AccessDeniedHttpException();
+  }
+
+  // Ensure access to update this particular field is granted.
+  if (!field_access('edit', $field_name, $entity_type, $entity)) {
+    throw new AccessDeniedHttpException();
+  }
+
+  $field_instance = field_info_instance($entity_type, $field_name, $entity->bundle());
+  if (empty($field_instance)) {
+    throw new NotFoundHttpException();
+  }
+
+  $form_state = array(
+    'entity' => $entity,
+    'field_name' => $field_name,
+    'langcode' => $langcode,
+    'no_redirect' => TRUE,
+    'build_info' => array('args' => array()),
+  );
+  $commands = array();
+  form_load_include($form_state, 'inc', 'edit', 'includes/form');
+  $form = drupal_build_form('edit_field_form', $form_state);
+
+  if (!empty($form_state['executed'])) {
+    // Retrieve the updated entity, save it and render only the modified field.
+    $entity = $form_state['entity'];
+    $entity->save();
+    $options = array('field_name' => $field_name);
+    field_attach_prepare_view($entity->entityType(), array($entity->id() => $entity), $view_mode, $langcode, $options);
+    $output = field_attach_view($entity->entityType(), $entity, $view_mode, $langcode, $options);
+
+    $response->addCommand(new FieldFormSavedCommand(drupal_render($output)));
+  }
+  else {
+    $response->addCommand(new FieldFormCommand(drupal_render($form)));
+
+    $errors = form_get_errors();
+    if (count($errors)) {
+      $response->addCommand(new FieldFormValidationErrorsCommand(theme('status_messages')));
+    }
+  }
+
+  // When working with a hidden form, we don't want any CSS or JS to be loaded.
+  if (isset($_POST['nocssjs']) && $_POST['nocssjs'] === 'true') {
+    drupal_static_reset('drupal_add_css');
+    drupal_static_reset('drupal_add_js');
+  }
+
+  return $response;
+}
+
+/**
+ * Page callback: render a processed text field without transformation filters.
+ *
+ * @param string $entity_type
+ *   The entity type of the entity of which a processed text field is being
+ *   rerendered.
+ * @param int $entity_id
+ *   The entity ID of the entity of which a processed text field is being
+ *   rerendered.
+ * @param string $field_name
+ *   The name of the (processed text) field that that is being rerendered
+ * @param string $langcode
+ *   The name of the language for which the processed text field is being
+ *   rererendered.
+ * @param string $view_mode
+ *   The view mode the processed text field should be rerendered in.
+ * @return array
+ *   A render array.
+ */
+function edit_text_field_render_without_transformation_filters($entity_type, $entity_id, $field_name, $langcode, $view_mode) {
+  // Ensure the entity type is valid.
+  if (empty($entity_type)) {
+    throw new NotFoundHttpException();
+  }
+
+  $entity_info = entity_get_info($entity_type);
+  if (!$entity_info) {
+    throw new NotFoundHttpException();
+  }
+
+  $entity = entity_load($entity_type, $entity_id);
+  if (!$entity) {
+    throw new NotFoundHttpException();
+  }
+
+  // Ensure a valid language code is set.
+  $langcode = field_valid_language($langcode);
+
+  // Ensure access to update this particular entity is granted.
+  if (!edit_entity_access('update', $entity_type, $entity)) {
+    throw new AccessDeniedHttpException();
+  }
+
+  // Ensure access to update this particular field is granted.
+  if (!field_access('edit', $field_name, $entity_type, $entity)) {
+    throw new AccessDeniedHttpException();
+  }
+
+  $field_instance = field_info_instance($entity_type, $field_name, $entity->bundle());
+  if (empty($field_instance)) {
+    throw new NotFoundHttpException();
+  }
+
+  $commands = array();
+
+  // Render the field in our custom display mode; retrieve the re-rendered
+  // markup, this is what we're after.
+  $field_output = field_view_field($entity_type, $entity, $field_name, 'edit-render-without-transformation-filters');
+  $output = $field_output[0]['#markup'];
+
+  $commands[] = array(
+    'command' => 'edit_field_rendered_without_transformation_filters',
+    'id'      => "$entity_type:$entity_id:$field_name:$langcode:$view_mode",
+    'data'    => $output,
+  );
+
+  return array('#type' => 'ajax', '#commands' => $commands);
+}
diff --git a/core/modules/edit/js/app.js b/core/modules/edit/js/app.js
new file mode 100644
index 0000000..b18675c
--- /dev/null
+++ b/core/modules/edit/js/app.js
@@ -0,0 +1,314 @@
+/**
+ * @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: [],
+
+    /**
+     * 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.SparkEditService());
+      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);
+
+      // Use Create's Storage widget.
+      this.$el.createStorage({
+        vie: this.vie,
+        editableNs: 'createeditable'
+      });
+
+      // Instantiate an EditableEntity widget for each property.
+      var that = this;
+      this.$entityElements = this.domService.findSubjectElements().each(function() {
+        $(this).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);
+          }
+        });
+      });
+
+      // Instantiate OverlayView
+      var overlayView = new Drupal.edit.views.OverlayView({
+        model: this.model
+      });
+
+      // Instantiate MenuView
+      var editMenuView = new Drupal.edit.views.MenuView({
+        el: this.el,
+        model: this.model
+      });
+
+      // When view/edit mode is toggled in the menu, update the editor widgets.
+      this.model.on('change:isViewing', this.appStateChange);
+    },
+
+    /**
+     * Sets the state of PropertyEditor widgets when edit mode begins or ends.
+     *
+     * Should be called whenever EditAppModel's "isViewing" changes.
+     */
+    appStateChange: function() {
+      // @todo: we're currently setting the state on EditableEntity widgets
+      // instead of PropertyEditor widgets, because of
+      // https://github.com/bergie/create/issues/140
+      var newState = (this.model.get('isViewing')) ? 'inactive' : 'candidate';
+      this.$entityElements.each(function() {
+        $(this).createEditable('setState', newState);
+      });
+    },
+
+    /**
+     * 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 (this.model.get('isViewing')) {
+        if (to !== 'inactive') {
+          accept = false;
+        }
+      }
+      // 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', 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 get rid of this once https://github.com/bergie/create/issues/133 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') {
+        this.model.set('activeEditor', null);
+      }
+
+      // Propagate the state change to the decoration and toolbar views.
+      // @todo enable this once https://github.com/bergie/create/issues/133 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.FieldDecorationView({
+        el: editor.element,
+        editor: editor,
+        toolbarId: editor.toolbarView.getId()
+      });
+
+      // @todo get rid of this once https://github.com/bergie/create/issues/133 is solved.
+      editor.options.widget.element.bind('createeditablestatechange', function(event, data) {
+        editor.decorationView.stateChange(data.previous, data.current);
+        editor.toolbarView.stateChange(data.previous, data.current);
+      });
+    }
+  });
+
+})(jQuery, _, Backbone, Drupal, VIE);
diff --git a/core/modules/edit/js/backbone.drupalform.js b/core/modules/edit/js/backbone.drupalform.js
new file mode 100644
index 0000000..3285af7
--- /dev/null
+++ b/core/modules/edit/js/backbone.drupalform.js
@@ -0,0 +1,158 @@
+/**
+ * @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
+ *
+ * @todo: HTTP status handling.
+ */
+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.edit_field_form_saved = function(ajax, response, status) {
+      Drupal.edit.util.form.unajaxifySaving(jQuery(ajax.element));
+
+      // Call Backbone.sync's success callback with the rerendered field.
+      var changedAttributes = {};
+      changedAttributes[predicate] = '@todo: JSON-LD representation N/A yet.';
+      changedAttributes[predicate + '/rendered'] = response.data;
+      options.success(changedAttributes);
+    };
+
+    // Unsuccessfully saved; validation errors.
+    Drupal.ajax[base].commands.edit_field_form_validation_errors = 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.edit_field_form = 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
+ *
+ * @todo: HTTP status handling.
+ */
+Backbone.syncDirect = function(method, model, options) {
+  if (method === 'update') {
+    var fillAndSubmitForm = function(value) {
+      jQuery('#edit_backstage form')
+        // Fill in the value in any <input> that isn't hidden or a submit button.
+        .find(':input[type!="hidden"][type!="submit"]:not(select)').val(value).end()
+        // Submit the 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').attr('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.edit_field_form_saved = function (ajax, response, status) {
+          Drupal.edit.util.form.unajaxifySaving(jQuery(ajax.element));
+          jQuery('#edit_backstage form').remove();
+
+          options.success();
+        };
+
+        // Unsuccessfully saved; validation errors.
+        Drupal.ajax[base].commands.edit_field_form_validation_errors = 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. 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.edit_field_form = function(ajax, response, status) {
+          // no-op
+        };
+
+        fillAndSubmitForm(value);
+      });
+    }
+    else {
+      fillAndSubmitForm(value);
+    }
+  }
+};
+
+})(jQuery, Backbone, Drupal);
diff --git a/core/modules/edit/js/createjs/editable.js b/core/modules/edit/js/createjs/editable.js
new file mode 100644
index 0000000..cc34498
--- /dev/null
+++ b/core/modules/edit/js/createjs/editable.js
@@ -0,0 +1,43 @@
+/**
+ * @file
+ * Determines which editor to use based on a class attribute.
+ */
+(function (jQuery, Drupal) {
+
+"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';
+
+      this.options.editors.direct = {
+        widget: 'drupalContentEditableWidget',
+        options: {}
+      };
+      this.options.editors['direct-with-wysiwyg'] = {
+        widget: 'drupalAlohaWidget',
+        options: {}
+      };
+      this.options.editors.form = {
+        widget: 'drupalFormWidget',
+        options: {}
+      };
+
+      jQuery.Midgard.midgardEditable.prototype._create.call(this);
+    },
+
+    _propertyEditorName: function (data) {
+      if (Drupal.settings.edit.wysiwyg && jQuery(this.element).hasClass('edit-type-direct')) {
+        if (jQuery(this.element).hasClass('edit-type-direct-with-wysiwyg')) {
+          return 'direct-with-wysiwyg';
+        }
+        return 'direct';
+      }
+      return 'form';
+    }
+  });
+
+})(jQuery, Drupal);
diff --git a/core/modules/edit/js/createjs/editingWidgets/drupalalohawidget.js b/core/modules/edit/js/createjs/editingWidgets/drupalalohawidget.js
new file mode 100644
index 0000000..c972911
--- /dev/null
+++ b/core/modules/edit/js/createjs/editingWidgets/drupalalohawidget.js
@@ -0,0 +1,159 @@
+/**
+ * @file
+ * Override of Create.js' default Aloha Editor widget.
+ *
+ * NOTE: This does in fact use zero code of jQuery.create.alohaWidget.
+ */
+(function (jQuery, Drupal) {
+
+"use strict";
+
+  jQuery.widget('Drupal.drupalAlohaWidget', jQuery.Create.alohaWidget, {
+
+    // @todo: actually use this when restoring original content, but for that we
+    // first need to know how to restore content in a Create.js context
+    originalTransformedContent: null,
+
+    /**
+     * Implements jQuery UI widget factory's _init() method.
+     *
+     * @todo: get rid of this once https://github.com/bergie/create/issues/142
+     * is solved.
+     */
+    _init: function() {},
+
+    /**
+     * Implements Create's _initialize() method.
+     */
+    _initialize: function() {
+      this._bindEvents();
+
+      // Immediately initialize Aloha, this can take some time. By doing it now
+      // already, it will most likely already be ready when the user actually
+      // wants to use Aloha Editor.
+      Drupal.aloha.init();
+    },
+
+    /**
+     * Binds to events.
+     *
+     * @todo: get rid of this helper function and move it into _initialize()
+     * once https://github.com/alohaeditor/Aloha-Editor/issues/693 is solved.
+     */
+    _bindEvents: function() {
+      var that = this;
+
+      // Sets the state to 'activated' upon clicking the element.
+      this.element.bind("click.edit", function(event) {
+        event.stopPropagation();
+        event.preventDefault();
+        that.options.activating();
+      });
+
+      // Sets the state to 'changed' whenever the content has changed.
+      this.element.bind('aloha-content-changed', function(event, $alohaEditable, data) {
+        if (!data.editable.isModified()) {
+          return true;
+        }
+        that.options.changed(data.editable.getContents());
+        data.editable.setUnmodified();
+      });
+    },
+
+    /**
+     * Makes this PropertyEditor widget react to state changes.
+     *
+     * @todo revisit this once https://github.com/bergie/create/issues/133 is
+     * solved.
+     */
+    stateChange: function(from, to) {
+      switch (to) {
+        case 'inactive':
+          break;
+        case 'candidate':
+          if (from !== 'inactive') {
+            Drupal.aloha.detach(this.element);
+            this._removeValidationErrors();
+            this._cleanUp();
+
+            // TRICKY: work-around for major AE bug. See:
+            //  - http://drupal.org/node/1725032
+            //  - https://github.com/alohaeditor/Aloha-Editor/issues/693.
+            // @todo: get rid of this once https://github.com/alohaeditor/Aloha-Editor/issues/693 is solved.
+            this._bindEvents();
+          }
+          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-rendered version of
+          // it without the transformation filters.
+          if (this.options.widget.element.hasClass('edit-text-with-transformation-filters')) {
+            this.originalTransformedContent = this.element.html();
+
+            var that = this;
+            Drupal.edit.util.loadRerenderedProcessedText({
+              $editorElement: this.element,
+              propertyID: Drupal.edit.util.calcPropertyID(this.options.entity, this.options.property),
+              callback: function (rerendered) {
+                that.element.html(rerendered);
+                that.options.activated();
+              }
+            });
+          }
+          // When no transformation filters have been applied: start WYSIWYG
+          // editing immediately!
+          else {
+            this.options.activated();
+          }
+          break;
+        case 'active':
+          // Attach Aloha Editor with this field's text format.
+          var formatID = this.options.widget.element.attr('data-edit-text-format');
+          var format = Drupal.settings.aloha.formats[formatID];
+          Drupal.aloha.attach(this.element, format);
+          Drupal.aloha.activate(this.element, format);
+          break;
+        case 'changed':
+          break;
+        case 'saving':
+          this._removeValidationErrors();
+          break;
+        case 'saved':
+          break;
+        case 'invalid':
+          break;
+      }
+    },
+
+    /**
+     * 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() {
+      this.element
+        .removeClass('edit-validation-error')
+        .next('.edit-validation-errors').remove();
+    },
+
+    /**
+     * Cleans up after the widget has been saved.
+     *
+     * Note: this is where the Create.Storage and accompanying Backbone.sync
+     * abstractions "leak" implementation details. That is only the case because
+     * we have to use Drupal's Form API as a transport mechanism. It is
+     * unfortunately a stateful transport mechanism, and that's why we have to
+     * clean it up here. This clean-up is only necessary when canceling the
+     * editing of a property after having attempted to save at least once.
+     */
+    _cleanUp: function() {
+      Drupal.edit.util.form.unajaxifySaving(jQuery('#edit_backstage form .edit-form-submit'));
+      jQuery('#edit_backstage form').remove();
+    }
+  });
+
+})(jQuery, Drupal);
diff --git a/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js b/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js
new file mode 100644
index 0000000..a9c9c46
--- /dev/null
+++ b/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js
@@ -0,0 +1,113 @@
+/**
+ * @file
+ * Override of Create.js' default "base" (plain contentEditable) widget.
+ */
+(function (jQuery, Drupal) {
+
+"use strict";
+
+  jQuery.widget('Drupal.drupalContentEditableWidget', jQuery.Create.editWidget, {
+
+    /**
+     * Implements jQuery UI widget factory's _init() method.
+     *
+     * @todo: get rid of this once https://github.com/bergie/create/issues/142
+     * is solved.
+     */
+    _init: function() {},
+
+    /**
+     * Implements Create's _initialize() method.
+     */
+    _initialize: function() {
+      var that = this;
+
+      // Sets the state to 'activated' upon clicking the element.
+      this.element.bind("click.edit", function(event) {
+        event.stopPropagation();
+        event.preventDefault();
+        that.options.activated();
+      });
+
+      // Sets the state to 'changed' whenever the content has changed.
+      var before = jQuery.trim(this.element.text());
+      this.element.bind('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.
+     *
+     * @todo revisit this once https://github.com/bergie/create/issues/133 is
+     * solved.
+     */
+    stateChange: function(from, to) {
+      switch (to) {
+        case 'inactive':
+          break;
+        case 'candidate':
+          if (from !== 'inactive') {
+            // Removes the "contenteditable" attribute.
+            this.disable();
+            this._removeValidationErrors();
+            this._cleanUp();
+          }
+          break;
+        case 'highlighted':
+          break;
+        case 'activating':
+          break;
+        case 'active':
+          // Sets the "contenteditable" attribute to "true".
+          this.enable();
+          break;
+        case 'changed':
+          break;
+        case 'saving':
+          this._removeValidationErrors();
+          break;
+        case 'saved':
+          break;
+        case 'invalid':
+          break;
+      }
+    },
+
+    /**
+     * 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() {
+      this.element
+        .removeClass('edit-validation-error')
+        .next('.edit-validation-errors').remove();
+    },
+
+    /**
+     * Cleans up after the widget has been saved.
+     *
+     * Note: this is where the Create.Storage and accompanying Backbone.sync
+     * abstractions "leak" implementation details. That is only the case because
+     * we have to use Drupal's Form API as a transport mechanism. It is
+     * unfortunately a stateful transport mechanism, and that's why we have to
+     * clean it up here. This clean-up is only necessary when canceling the
+     * editing of a property after having attempted to save at least once.
+     */
+    _cleanUp: function() {
+      Drupal.edit.util.form.unajaxifySaving(jQuery('#edit_backstage form .edit-form-submit'));
+      jQuery('#edit_backstage form').remove();
+    }
+  });
+
+})(jQuery, Drupal);
diff --git a/core/modules/edit/js/createjs/editingWidgets/formwidget.js b/core/modules/edit/js/createjs/editingWidgets/formwidget.js
new file mode 100644
index 0000000..94ad3f5
--- /dev/null
+++ b/core/modules/edit/js/createjs/editingWidgets/formwidget.js
@@ -0,0 +1,151 @@
+/**
+ * @file
+ * Form-based Create.js widget for structured content in Drupal.
+ */
+(function ($, Drupal) {
+
+"use strict";
+
+  $.widget('Drupal.drupalFormWidget', $.Create.editWidget, {
+
+    id: null,
+    $formContainer: null,
+
+    /**
+     * Implements jQuery UI widget factory's _init() method.
+     *
+     * @todo: get rid of this once https://github.com/bergie/create/issues/142
+     * is solved.
+     */
+    _init: function() {},
+
+    /**
+     * Implements Create's _initialize() method.
+     */
+    _initialize: function() {
+      // Sets the state to 'activating' upon clicking the element.
+      var that = this;
+      this.element.bind("click.edit", function(event) {
+        event.stopPropagation();
+        event.preventDefault();
+        that.options.activating();
+      });
+    },
+
+    /**
+     * Makes this PropertyEditor widget react to state changes.
+     *
+     * @todo revisit this once https://github.com/bergie/create/issues/133 is
+     * solved.
+     */
+    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')
+        .css('background-color', $editorElement.css('background-color'));
+
+      // Insert form container in DOM.
+      if ($editorElement.css('display') === 'inline') {
+        // @todo: 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
+          .delegate(':input', 'formUpdated.edit', function () {
+            // Sets the state to 'changed'.
+            widget.options.changed();
+          })
+          .delegate('input', 'keypress.edit', 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'));
+      this.$formContainer
+        .undelegate(':input', 'change.edit')
+        .undelegate('input', 'keypress.edit')
+        .remove();
+      this.$formContainer = null;
+    }
+  });
+
+})(jQuery, Drupal);
diff --git a/core/modules/edit/js/createjs/storage.js b/core/modules/edit/js/createjs/storage.js
new file mode 100644
index 0000000..580ff82
--- /dev/null
+++ b/core/modules/edit/js/createjs/storage.js
@@ -0,0 +1,11 @@
+/**
+ * @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
new file mode 100644
index 0000000..22c976c
--- /dev/null
+++ b/core/modules/edit/js/edit.js
@@ -0,0 +1,47 @@
+/**
+ * @file
+ * Behaviors for Edit, including the one that initializes Edit's EditAppView.
+ */
+(function ($, Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit = Drupal.edit || {};
+
+Drupal.behaviors.editDiscoverEditables = {
+  attach: function(context) {
+    // @todo: we need to separate the discovery of editables if we want updated
+    // or new content (added by code other than Edit) to be detected
+    // automatically. Once we implement this, we'll be able to get rid of all
+    // calls to Drupal.edit.domService.findSubjectElements() :)
+  }
+};
+
+/**
+ * Attach toggling behavior and in-place editing.
+ */
+Drupal.behaviors.edit = {
+  attach: function(context) {
+    $('#edit_view-edit-toggles').once('edit-init', Drupal.edit.init);
+  }
+};
+
+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();
+  var app = new Drupal.edit.EditAppView({
+    el: $('body'),
+    model: appModel
+  });
+
+  // Instantiate EditRouter.
+  var editRouter = new Drupal.edit.routers.EditRouter({
+    appModel: appModel
+  });
+
+  // Start Backbone's history/route handling.
+  Backbone.history.start();
+};
+
+})(jQuery, Backbone, Drupal);
diff --git a/core/modules/edit/js/lib/create.js b/core/modules/edit/js/lib/create.js
new file mode 100644
index 0000000..1d76bff
--- /dev/null
+++ b/core/modules/edit/js/lib/create.js
@@ -0,0 +1,1643 @@
+//     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.bind('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.bind('add remove reset', widget.checkCollectionConstraints, widget);
+
+      widget._bindCollectionView(widget.options.view);
+    },
+
+    _bindCollectionView: function (view) {
+      var widget = this;
+      view.bind('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.bind('add', function (itemView) {
+        //itemView.el.effect('slide');
+        widget._makeEditable(itemView);
+        widget._refreshButtons();
+      });
+      view.bind('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());
+
+      _.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.disableEditor({
+          widget: this,
+          editable: editable,
+          entity: this.options.model,
+          element: jQuery(editable)
+        });
+      }, 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(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) {
+      var widgetName = jQuery(data.element).data('createWidgetName');
+
+      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');
+
+        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('Create.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.bind('focus', function () {
+        if (self.options.disabled) {
+          return;
+        }
+        self.options.activated();
+      });
+      this.element.bind('blur', function () {
+        if (self.options.disabled) {
+          return;
+        }
+        self.options.deactivated();
+      });
+      var before = this.element.html();
+      this.element.bind('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('Create.alohaWidget', jQuery.Create.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('Create.ckeditorWidget', jQuery.Create.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();
+      });
+      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());
+      });
+    },
+
+    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('Create.halloWidget', jQuery.Create.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).bind('halloactivated', function (event, data) {
+        self.options.activated();
+      });
+      jQuery(this.element).bind('hallodeactivated', function (event, data) {
+        self.options.deactivated();
+      });
+      jQuery(this.element).bind('hallomodified', function (event, data) {
+        self.options.changed(data.content);
+        data.editable.setUnmodified();
+      });
+
+      jQuery(document).bind('midgardtoolbarstatechange', function(event, data) {
+        // Switch between Hallo configurations when toolbar state changes
+        if (data.display === self.options.toolbarState) {
+          return;
+        }
+        self.options.toolbarState = data.display;
+        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('Create.redactorWidget', jQuery.Create.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).bind('focus', function (event) {
+        self.options.activated(); 
+      });
+      /*
+      jQuery(this.element).bind('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.bind('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.bind('startPreventSave', function () {
+        if (timeout) {
+          window.clearInterval(timeout);
+          timeout = null;
+        }
+        widget.disableAutoSave();
+      });
+      this.element.bind('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.bind(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.bind(widget.options.editableNs + 'disable', function (event, options) {
+        widget._restoreLocal(options.instance);
+      });
+
+      widget.element.bind(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.bind('midgardcreatestatechange', function (event, options) {
+        if (options.state === 'browse' || widget.restorables.length === 0) {
+          widget.restorables = [];
+          if (restorer) {
+            restorer.close();
+          }
+          return;
+        }
+        restorer = widget.checkRestore();
+      });
+
+      widget.element.bind('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.restoreLocal();
+        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;
+    },
+
+    restoreLocal: 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));
+    },
+
+    _restoreLocal: 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/modules/edit/js/lib/vie.js b/core/modules/edit/js/lib/vie.js
new file mode 100644
index 0000000..ca5c079
--- /dev/null
+++ b/core/modules/edit/js/lib/vie.js
@@ -0,0 +1,3682 @@
+/*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]));
+                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);
+            return Backbone.Model.prototype.set.call(this, attrs, options);
+        },
+
+        // **`.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;
+})();
\ No newline at end of file
diff --git a/core/modules/edit/js/models/edit-app-model.js b/core/modules/edit/js/models/edit-app-model.js
new file mode 100644
index 0000000..b6ff36f
--- /dev/null
+++ b/core/modules/edit/js/models/edit-app-model.js
@@ -0,0 +1,22 @@
+/**
+ * @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: {
+    // We always begin in view mode.
+    isViewing: true,
+    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/routers/edit-router.js b/core/modules/edit/js/routers/edit-router.js
new file mode 100644
index 0000000..4d7b196
--- /dev/null
+++ b/core/modules/edit/js/routers/edit-router.js
@@ -0,0 +1,54 @@
+/**
+ * @file
+ * A Backbone Router enabling URLs to make the user enter edit mode directly.
+ */
+(function(Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit = Drupal.edit || {};
+Drupal.edit.routers = {};
+Drupal.edit.routers.EditRouter = Backbone.Router.extend({
+
+  appModel: null,
+
+  routes: {
+    "quick-edit": "edit",
+    "view": "view",
+    "": "view"
+  },
+
+  initialize: function(options) {
+    this.appModel = options.appModel;
+  },
+
+  edit: function() {
+    this.appModel.set('isViewing', false);
+  },
+
+  view: function(query, page) {
+    var that = this;
+
+    // If there's an active editor, attempt to set its state to 'candidate', and
+    // then act according to the user's choice.
+    var activeEditor = this.appModel.get('activeEditor');
+    if (activeEditor) {
+      var editableEntity = activeEditor.options.widget;
+      var predicate = activeEditor.options.property;
+      editableEntity.setState('candidate', predicate, { reason: 'menu' }, function(accepted) {
+        if (accepted) {
+          that.appModel.set('isViewing', true);
+        }
+        else {
+          that.navigate('#quick-edit');
+        }
+      });
+    }
+    // Otherwise, we can switch to view mode directly.
+    else {
+      that.appModel.set('isViewing', true);
+    }
+  }
+});
+
+})(Backbone, Drupal);
diff --git a/core/modules/edit/js/theme.js b/core/modules/edit/js/theme.js
new file mode 100644
index 0000000..4853246
--- /dev/null
+++ b/core/modules/edit/js/theme.js
@@ -0,0 +1,156 @@
+/**
+ * @file
+ * Provides overridable theme functions for all of Edit's client-side HTML.
+ */
+(function($) {
+
+"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
+ *   An object with the following keys:
+ *   - id: the id to apply to the backstage.
+ * @return
+ *   The corresponding HTML.
+ */
+Drupal.theme.editBackstage = function(settings) {
+  var html = '';
+  html += '<div id="' + settings.id + '" />';
+  return html;
+};
+
+/**
+ * Theme function for a modal of the Edit module.
+ *
+ * @param settings
+ *   An object with the following keys:
+ *   - None.
+ * @return
+ *   The corresponding HTML.
+ */
+Drupal.theme.editModal = function(settings) {
+  var classes = 'edit-animate-slow edit-animate-invisible edit-animate-delay-veryfast';
+  var html = '';
+  html += '<div id="edit_modal" class="' + classes + '">';
+  html += '  <div class="main"><p></p></div>';
+  html += '  <div class="actions"></div>';
+  html += '</div>';
+  return html;
+};
+
+/**
+ * Theme function for a toolbar container of the Edit module.
+ *
+ * @param settings
+ *   An object with the following keys:
+ *   - id: the id to apply to the toolbar container.
+ * @return
+ *   The corresponding HTML.
+ */
+Drupal.theme.editToolbarContainer = function(settings) {
+  var html = '';
+  html += '<div id="' + settings.id + '" class="edit-toolbar-container edit-animate-invisible edit-animate-only-visibility">';
+  html += '  <div class="edit-toolbar-heightfaker edit-animate-fast">';
+  html += '    <div class="edit-toolbar primary" />';
+  html += '  </div>';
+  html += '</div>';
+  return html;
+};
+
+/**
+ * Theme function for a toolbar toolgroup of the Edit module.
+ *
+ * @param settings
+ *   An object with the following keys:
+ *   - classes: the class of the toolgroup.
+ *   - buttons: @see Drupal.theme.prototype.editButtons().
+ * @return
+ *   The corresponding HTML.
+ */
+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 + '">';
+  html += Drupal.theme('editButtons', { buttons: settings.buttons });
+  html += '</div>';
+  return html;
+};
+
+/**
+ * Theme function for buttons of the Edit module.
+ *
+ * Can be used for the buttons both in the toolbar toolgroups and in the modal.
+ *
+ * @param settings
+ *   An object with the following keys:
+ *   - buttons: an array of objects with the following keys:
+ *     - url: the URL the button should point to.
+ *     - classes: the classes of the button.
+ *     - label: the label of the button.
+ *     - hasButtonRole: whether this button should have its "role" attribute set
+ *       to "button".
+ *     - action: sets a data-edit-modal-action attribute.
+ * @return
+ *   The corresponding HTML.
+ */
+Drupal.theme.editButtons = function(settings) {
+  var html = '';
+  for (var i = 0; i < settings.buttons.length; i++) {
+    var button = settings.buttons[i];
+    if (!button.hasOwnProperty('url')) {
+      button.url = '';
+    }
+    if (!button.hasOwnProperty('hasButtonRole')) {
+      button.hasButtonRole = true;
+    }
+
+    html += '<a href="' + button.url + '" class="' + button.classes + '"';
+    html += (button.hasButtonRole) ? ' role="button"' : '';
+    html += (button.action) ? ' data-edit-modal-action="' + button.action + '"' : '';
+    html += '>';
+    html +=    button.label;
+    html += '</a>';
+  }
+  return html;
+};
+
+/**
+ * Theme function for a form container of the Edit module.
+ *
+ * @param settings
+ *   An object with the following keys:
+ *   - id: the id to apply to the toolbar container.
+ *   - loadingMsg: The message to show while loading.
+ * @return
+ *   The corresponding HTML.
+ */
+Drupal.theme.editFormContainer = function(settings) {
+  var html = '';
+  html += '<div id="' + settings.id + '" class="edit-form-container">';
+  html += '  <div class="edit-form">';
+  html += '    <div class="placeholder">';
+  html +=        settings.loadingMsg;
+  html += '    </div>';
+  html += '  </div>';
+  html += '</div>';
+  return html;
+};
+
+})(jQuery);
diff --git a/core/modules/edit/js/util.js b/core/modules/edit/js/util.js
new file mode 100644
index 0000000..6b0044b
--- /dev/null
+++ b/core/modules/edit/js/util.js
@@ -0,0 +1,140 @@
+/**
+ * @file
+ * Provides utility functions for Edit.
+ */
+(function($, Drupal) {
+
+"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;
+};
+
+Drupal.edit.util.buildUrl = function(id, urlFormat) {
+  var parts = id.split('/');
+  return Drupal.formatString(decodeURIComponent(urlFormat), {
+    '!entity_type': parts[0],
+    '!id'         : parts[1],
+    '!field_name' : parts[2],
+    '!langcode'   : parts[3],
+    '!view_mode'  : parts[4]
+  });
+};
+
+/**
+ * Loads rerendered processed text for a given property.
+ *
+ * Leverages Drupal.ajax' ability to have scoped (per-instance) command
+ * implementations to be able to call a callback.
+ *
+ * @param 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.
+ *    - callback (required: A callback function that will receive the rerendered
+ *      processed text.
+ */
+Drupal.edit.util.loadRerenderedProcessedText = function(options) {
+  // 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, Drupal.settings.edit.rerenderProcessedTextURL),
+    event: 'edit-internal.edit',
+    submit: { nocssjs : true },
+    progress: { type : null } // No progress indicator.
+  });
+  // Implement a scoped edit_field_form AJAX command: calls the callback.
+  Drupal.ajax[options.propertyID].commands.edit_field_rendered_without_transformation_filters = function(ajax, response, status) {
+    options.callback(response.data);
+    // Delete the Drupal.ajax instance that called this very function.
+    delete Drupal.ajax[options.propertyID];
+    options.$editorElement.unbind('edit-internal.edit');
+  };
+  // This will ensure our scoped edit_field_form AJAX command gets called.
+  options.$editorElement.trigger('edit-internal.edit');
+};
+
+Drupal.edit.util.form = {
+  /**
+   * Loads a form, calls a callback to inserts.
+   *
+   * Leverages Drupal.ajax' ability to have scoped (per-instance) command
+   * implementations to be able to call a callback.
+   *
+   * @param 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
+   *   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) {
+    // 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, Drupal.settings.edit.fieldFormURL),
+      event: 'edit-internal.edit',
+      submit: { nocssjs : options.nocssjs },
+      progress: { type : null } // No progress indicator.
+    });
+    // Implement a scoped edit_field_form AJAX command: calls the callback.
+    Drupal.ajax[options.propertyID].commands.edit_field_form = 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.unbind('edit-internal.edit');
+    };
+    // This will ensure our scoped edit_field_form AJAX command gets called.
+    options.$editorElement.trigger('edit-internal.edit');
+  },
+
+  /**
+   * Creates a Drupal.ajax instance that is used to save a form.
+   *
+   * @param options
+   *   An object with the following keys:
+   *    - nocssjs (required): boolean indicating whether no CSS and JS should be
+   *      returned (necessary when the form is invisible to the user).
+   *
+   * @return
+   *   The key of the Drupal.ajax instance.
+   */
+  ajaxifySaving: function(options, $submit) {
+    // Re-wire the form to handle submit.
+    var element_settings = {
+      url: $submit.closest('form').attr('action'),
+      setClick: true,
+      event: 'click.edit',
+      progress: { type:'throbber' },
+      submit: { nocssjs : options.nocssjs }
+    };
+    var base = $submit.attr('id');
+
+    Drupal.ajax[base] = new Drupal.ajax(base, $submit[0], element_settings);
+
+    return base;
+  },
+
+  /**
+   * Cleans up the Drupal.ajax instance that is used to save the form.
+   *
+   * @param $submit
+   *   The jQuery-wrapped submit DOM element that should be unajaxified.
+   */
+  unajaxifySaving: function($submit) {
+    delete Drupal.ajax[$submit.attr('id')];
+    $submit.unbind('click.edit');
+  }
+};
+
+})(jQuery, Drupal);
diff --git a/core/modules/edit/js/viejs/SparkEditService.js b/core/modules/edit/js/viejs/SparkEditService.js
new file mode 100644
index 0000000..ff95266
--- /dev/null
+++ b/core/modules/edit/js/viejs/SparkEditService.js
@@ -0,0 +1,208 @@
+/**
+ * @file
+ * VIE DOM parsing service for Edit.
+ */
+(function(jQuery, _, Drupal, VIE) {
+
+"use strict";
+
+  VIE.prototype.SparkEditService = 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.SparkEditService.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 = Drupal.settings.edit.context;
+        }
+      } else {
+        element = loadable.options.element;
+      }
+
+      var entities = this.readEntities(element);
+      loadable.resolve(entities);
+    },
+
+    // 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.
+    // @todo: check the above.
+    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);
+      }
+
+      // Register with VIE
+      return this._registerEntity(entity);
+    },
+
+    _registerEntity: function (entityData) {
+      var entityInstance = new this.vie.Entity(entityData);
+      return this.vie.entities.addOrUpdate(entityInstance, {
+        updateOptions: {
+          silent: true
+        }
+      });
+    },
+
+    _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 label = element.data('edit-field-label');
+      var range = 'Form';
+      if (element.hasClass('edit-type-direct')) {
+        range = 'Direct';
+      }
+      if (element.hasClass('edit-type-direct-with-wysiwyg')) {
+        range = 'Wysiwyg';
+      }
+      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(predicateElement);
+        if (value === null && !emptyValues) {
+          return;
+        }
+
+        entityPredicates[predicate] = value;
+      });
+      return entityPredicates;
+    },
+
+    _readElementValue: function (element) {
+      return jQuery.trim(element.html());
+    },
+
+    // Subject elements are the DOM elements containing a single or multiple
+    // editable fields. In Spark Edit these elements are called _Fields_,
+    // and the actual DOM elements which are edited are called _Editables_.
+    findSubjectElements: function (element) {
+      if (!element) {
+        element = Drupal.settings.edit.context;
+      }
+      return jQuery(this.options.subjectSelector, element);
+    },
+
+    // Predicate Elements are the actual DOM elements that users will be able
+    // to edit. In regular Spark Edit they are called _Editables_.
+    //
+    // They are contained within Entity elements, which in Spark Edit are called
+    // _Fields_.
+    // @todo: clarify and document what the Best Way to do this is? Should VIE's
+    // entities map to Drupal's entities etc? Also see higher comments.
+    findPredicateElements: function (subject, element, allowNestedPredicates, stop) {
+      var predicates = jQuery();
+
+      // 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, _, Drupal, VIE);
diff --git a/core/modules/edit/js/views/fielddecorator-view.js b/core/modules/edit/js/views/fielddecorator-view.js
new file mode 100644
index 0000000..01a4e83
--- /dev/null
+++ b/core/modules/edit/js/views/fielddecorator-view.js
@@ -0,0 +1,324 @@
+/**
+ * @file
+ * A Backbone View that decorates properties.
+ *
+ * It listens to state changes of the property editor.
+ *
+ * @todo  rename to propertydecorator-view.js + PropertyDecorationView.
+ */
+(function($, Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit = Drupal.edit || {};
+Drupal.edit.views = Drupal.edit.views || {};
+Drupal.edit.views.FieldDecorationView = Backbone.View.extend({
+
+  editor: null,
+  entity: null,
+  predicate : null,
+  editorName: null,
+  toolbarId: null,
+
+  _widthAttributeIsEmpty: null,
+
+  events: {
+    'mouseenter.edit' : 'onMouseEnter',
+    'mouseleave.edit' : 'onMouseLeave'
+  },
+
+  /**
+   * 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: 'form', 'direct' or
+   *        'direct-with-wysiwyg'.
+   *      * widget: the parent EditableeEntity widget.
+   *   - toolbarId: the ID attribute of the toolbar as rendered in the DOM.
+   */
+  initialize: function(options) {
+    this.editor = options.editor;
+    this.toolbarId = options.toolbarId;
+
+    this.entity = this.editor.options.entity;
+    this.predicate = this.editor.options.property;
+    this.editorName = this.editor.options.editorName;
+
+    this.$el.css('background-color', this._getBgColor(this.$el));
+  },
+
+  /**
+   * Listens to editor state changes.
+   */
+  stateChange: function(from, to) {
+    switch (to) {
+      case 'inactive':
+        if (from !== null) {
+          this.undecorate();
+        }
+        break;
+      case 'candidate':
+        this.decorate();
+        if (from !== 'inactive') {
+          this.stopHighlight();
+          if (from !== 'highlighted') {
+            this.stopEdit(this.editorName);
+          }
+        }
+        break;
+      case 'highlighted':
+        this.startHighlight();
+        break;
+      case 'activating':
+        // NOTE: this step only exists for the 'form' editor! It is skipped by
+        // the 'direct' and 'direct-with-wysiwyg' editors, because no loading is
+        // necessary.
+        this.prepareEdit(this.editorName);
+        break;
+      case 'active':
+        if (this.editorName !== 'form') {
+          this.prepareEdit(this.editorName);
+        }
+        this.startEdit(this.editorName);
+        break;
+      case 'changed':
+        break;
+      case 'saving':
+        break;
+      case 'saved':
+        break;
+      case 'invalid':
+        break;
+    }
+  },
+
+  /**
+   * Starts hover: transition to 'highlight' state.
+   *
+   * @param event
+   */
+  onMouseEnter: function(event) {
+    var that = this;
+    this._ignoreHoveringVia(event, '#' + this.toolbarId, function () {
+      var editableEntity = that.editor.options.widget;
+      editableEntity.setState('highlighted', that.predicate);
+      event.stopPropagation();
+    });
+  },
+
+  /**
+   * Stops hover: back to 'candidate' state.
+   *
+   * @param 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' });
+      event.stopPropagation();
+    });
+  },
+
+  decorate: function () {
+    this.$el.addClass('edit-animate-fast edit-candidate edit-editable');
+  },
+
+  undecorate: function () {
+    this.$el
+      .removeClass('edit-candidate edit-editable edit-highlighted edit-editing edit-belowoverlay');
+  },
+
+  startHighlight: function () {
+    // Animations.
+    var that = this;
+    setTimeout(function() {
+      that.$el.addClass('edit-highlighted');
+    }, 0);
+  },
+
+  stopHighlight: function() {
+    this.$el
+      .removeClass('edit-highlighted');
+  },
+
+  prepareEdit: function(editorName) {
+    this.$el.addClass('edit-editing');
+
+    // While editing, don't show *any* other editors.
+    // @todo: revisit this once https://github.com/bergie/create/issues/133 is solved.
+    $('.edit-candidate').not('.edit-editing').removeClass('edit-editable');
+
+    if (editorName === 'form') {
+      this.$el.addClass('edit-belowoverlay');
+    }
+  },
+
+  startEdit: function(editorName) {
+    if (editorName !== 'form') {
+      this._pad();
+    }
+  },
+
+  stopEdit: function(editorName) {
+    this.$el.removeClass('edit-highlighted edit-editing');
+
+    // Make the other editors show up again.
+    // @todo: revisit this once https://github.com/bergie/create/issues/133 is solved.
+    $('.edit-candidate').addClass('edit-editable');
+
+    if (editorName === 'form') {
+      this.$el.removeClass('edit-belowoverlay');
+    }
+    else {
+      this._unpad();
+    }
+  },
+
+  _pad: function () {
+    var self = this;
+
+    // Add 5px padding for readability. This means we'll freeze the current
+    // width and *then* add 5px padding, hence ensuring the padding is added "on
+    // the outside".
+    // 1) Freeze the width (if it's not already set); don't use animations.
+    if (this.$el[0].style.width === "") {
+      this._widthAttributeIsEmpty = true;
+      this.$el
+        .addClass('edit-animate-disable-width')
+        .css('width', this.$el.width());
+    }
+
+    // 2) Add padding; use animations.
+    var posProp = this._getPositionProperties(this.$el);
+    setTimeout(function() {
+      // Re-enable width animations (padding changes affect width too!).
+      self.$el.removeClass('edit-animate-disable-width');
+
+      // Pad the editable.
+      self.$el
+      .css({
+        'position': 'relative',
+        'top':  posProp.top  - 5 + 'px',
+        'left': posProp.left - 5 + 'px',
+        'padding-top'   : posProp['padding-top']    + 5 + 'px',
+        'padding-left'  : posProp['padding-left']   + 5 + 'px',
+        'padding-right' : posProp['padding-right']  + 5 + 'px',
+        'padding-bottom': posProp['padding-bottom'] + 5 + 'px',
+        'margin-bottom':  posProp['margin-bottom'] - 10 + 'px'
+      });
+    }, 0);
+  },
+
+  _unpad: function () {
+    var self = this;
+
+    // 1) Set the empty width again.
+    if (this._widthAttributeIsEmpty) {
+      this.$el
+        .addClass('edit-animate-disable-width')
+        .css('width', '');
+    }
+
+    // 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() {
+      // Re-enable width animations (padding changes affect width too!).
+      self.$el.removeClass('edit-animate-disable-width');
+
+      // Unpad the editable.
+      self.$el
+      .css({
+        'position': 'relative',
+        'top':  posProp.top  + 5 + 'px',
+        'left': posProp.left + 5 + 'px',
+        'padding-top'   : posProp['padding-top']    - 5 + 'px',
+        'padding-left'  : posProp['padding-left']   - 5 + 'px',
+        'padding-right' : posProp['padding-right']  - 5 + 'px',
+        'padding-bottom': posProp['padding-bottom'] - 5 + 'px',
+        'margin-bottom': posProp['margin-bottom'] + 10 + 'px'
+      });
+    }, 0);
+  },
+
+  /**
+   * Gets the background color of an element (or the inherited one).
+   *
+   * @param $e
+   *   A DOM element.
+   */
+  _getBgColor: function($e) {
+    var c;
+
+    if ($e === null || $e[0].nodeName === 'HTML') {
+      // Fallback to white.
+      return 'rgb(255, 255, 255)';
+    }
+    c = $e.css('background-color');
+    // TRICKY: edge case for Firefox' "transparent" here; this is a
+    // browser bug: https://bugzilla.mozilla.org/show_bug.cgi?id=635724
+    if (c === 'rgba(0, 0, 0, 0)' || c === 'transparent') {
+      return this._getBgColor($e.parent());
+    }
+    return c;
+  },
+
+  /**
+   * Gets the top and left properties of an element and convert extraneous
+   * values and information into numbers ready for subtraction.
+   *
+   * @param $e
+   *   A DOM element.
+   */
+  _getPositionProperties: function($e) {
+    var p,
+        r = {},
+        props = [
+          'top', 'left', 'bottom', 'right',
+          'padding-top', 'padding-left', 'padding-right', 'padding-bottom',
+          'margin-bottom'
+        ];
+
+    var propCount = props.length;
+    for (var i = 0; i < propCount; i++) {
+      p = props[i];
+      r[p] = parseInt(this._replaceBlankPosition($e.css(p)), 10);
+    }
+    return r;
+  },
+
+  /**
+   * Replaces blank or 'auto' CSS "position: <value>" values with "0px".
+   *
+   * @param pos
+   *   The value for a CSS position declaration.
+   */
+  _replaceBlankPosition: function(pos) {
+    // @todo: this was pos == NaN (which always returns false, keeping this
+    // comment in case we find a regression.
+    if (pos === 'auto' || !pos) {
+      pos = '0px';
+    }
+    return pos;
+  },
+
+  /**
+   * Ignores hovering to/from the given closest element, but as soon as a hover
+   * occurs to/from *another* element, then call the given callback.
+   */
+  _ignoreHoveringVia: function(event, closest, callback) {
+    if ($(event.relatedTarget).closest(closest).length > 0) {
+      event.stopPropagation();
+    }
+    else {
+      callback();
+    }
+  }
+});
+
+})(jQuery, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/menu-view.js b/core/modules/edit/js/views/menu-view.js
new file mode 100644
index 0000000..834f743
--- /dev/null
+++ b/core/modules/edit/js/views/menu-view.js
@@ -0,0 +1,42 @@
+/**
+ * @file
+ * A Backbone View that provides the app-level interactive menu.
+ */
+(function($, _, Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit = Drupal.edit || {};
+Drupal.edit.views = Drupal.edit.views || {};
+Drupal.edit.views.MenuView = Backbone.View.extend({
+
+  /**
+   * Implements Backbone Views' initialize() function.
+   */
+  initialize: function() {
+    _.bindAll(this, 'stateChange');
+    this.model.on('change:isViewing', this.stateChange);
+
+    // We have to call stateChange() here, because URL fragments are not passed
+    // the server, thus the wrong anchor may be marked as active.
+    this.stateChange();
+  },
+
+  /**
+   * Listens to app state changes.
+   */
+  stateChange: function() {
+    // Unmark whichever one is currently marked as active.
+    this.$('a.edit_view-edit-toggle')
+      .removeClass('active')
+      .parent().removeClass('active');
+
+    // Mark the correct one as active.
+    var activeAnchor = this.model.get('isViewing') ? 'view' : 'edit';
+    this.$('a.edit_view-edit-toggle.edit-' + activeAnchor)
+      .addClass('active')
+      .parent().addClass('active');
+  }
+});
+
+})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/modal-view.js b/core/modules/edit/js/views/modal-view.js
new file mode 100644
index 0000000..0f3c0aa
--- /dev/null
+++ b/core/modules/edit/js/views/modal-view.js
@@ -0,0 +1,108 @@
+/**
+ * @file
+ * A Backbone View that provides an interactive modal.
+ */
+(function($, Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit = Drupal.edit || {};
+Drupal.edit.views = Drupal.edit.views || {};
+Drupal.edit.views.ModalView = Backbone.View.extend({
+
+  message: null,
+  buttons: null,
+  callback: null,
+  $elementsToHide: null,
+
+  events: {
+    'click a[role=button]': 'onButtonClick'
+  },
+
+  /**
+   * Implements Backbone Views' initialize() function.
+   *
+   * @param 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.
+   *
+   * @see Drupal.theme.editModal()
+   * @see Drupal.theme.editButtons()
+   */
+  initialize: function(options) {
+    this.message = options.message;
+    this.buttons = options.buttons;
+    this.callback = options.callback;
+  },
+
+  /**
+   * Implements Backbone Views' render() function.
+   */
+  render: function() {
+    // Step 1: move certain UI elements below the overlay.
+    var editor = this.model.get('activeEditor');
+    this.$elementsToHide = $([])
+      .add((editor.element.hasClass('edit-belowoverlay')) ? null : editor.element)
+      .add(editor.toolbarView.$el)
+      .add((editor.options.editorName === 'form')
+        ? editor.$formContainer
+        : editor.element.next('.edit-validation-errors')
+      );
+    this.$elementsToHide.addClass('edit-belowoverlay');
+
+    // Step 2: the modal. When the user makes a choice, the UI elements that
+    // were moved below the overlay will be restored, and the callback will be
+    // called.
+    this.setElement(Drupal.theme('editModal', {}));
+    this.$el.appendTo('body');
+    // Template.
+    this.$('.main p').text(this.message);
+    var $actions = $(Drupal.theme('editButtons', { 'buttons' : this.buttons}));
+    this.$('.actions').append($actions);
+
+    // Step 3; show the modal with an animation.
+    var that = this;
+    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.
+   *
+   * @param event
+   */
+  onButtonClick: function(event) {
+    event.stopPropagation();
+    event.preventDefault();
+
+    // Remove after animation.
+    var that = this;
+    this.$el
+      .addClass('edit-animate-invisible')
+      .bind(Drupal.edit.util.constants.transitionEnd, function(e) {
+        that.remove();
+      });
+
+    var action = $(event.target).attr('data-edit-modal-action');
+    return this.callback(action);
+  },
+
+  /**
+   * Overrides Backbone Views' remove() function.
+   */
+  remove: function() {
+    // Move the moved UI elements on top of the overlay again.
+    this.$elementsToHide.removeClass('edit-belowoverlay');
+
+    // Remove the modal itself.
+    this.$el.remove();
+  }
+});
+
+})(jQuery, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/overlay-view.js b/core/modules/edit/js/views/overlay-view.js
new file mode 100644
index 0000000..d41f091
--- /dev/null
+++ b/core/modules/edit/js/views/overlay-view.js
@@ -0,0 +1,84 @@
+/**
+ * @file
+ * A Backbone View that provides the app-level overlay.
+ *
+ * The overlay sits on top of the existing content, the properties that are
+ * candidates for editing sit on top of the overlay.
+ */
+(function ($, _, Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit = Drupal.edit || {};
+Drupal.edit.views = Drupal.edit.views || {};
+Drupal.edit.views.OverlayView = Backbone.View.extend({
+
+  events: {
+    'click': 'onClick'
+  },
+
+  /**
+   * Implements Backbone Views' initialize() function.
+   */
+  initialize: function(options) {
+    _.bindAll(this, 'stateChange');
+    this.model.on('change:isViewing', this.stateChange);
+  },
+
+  /**
+   * Listens to app state changes.
+   */
+  stateChange: function() {
+    if (this.model.get('isViewing')) {
+      this.remove();
+      return;
+    }
+    this.render();
+  },
+
+  /**
+   * Equates clicks anywhere on the overlay to clicking the active editor's (if
+   * any) "close" button.
+   *
+   * @param event
+   */
+  onClick: function(event) {
+    event.preventDefault();
+    var activeEditor = this.model.get('activeEditor');
+    if (activeEditor) {
+      var editableEntity = activeEditor.options.widget;
+      var predicate = activeEditor.options.property;
+      editableEntity.setState('candidate', predicate, { reason: 'overlay' });
+    }
+  },
+
+  /**
+   * Inserts the overlay element and appends it to the body.
+   */
+  render: function() {
+    this.setElement(
+      $(Drupal.theme('editOverlay', {}))
+      .appendTo('body')
+      .addClass('edit-animate-slow edit-animate-invisible')
+    );
+    // Animations
+    this.$el.css('top', $('#navbar').outerHeight());
+    this.$el.removeClass('edit-animate-invisible');
+  },
+
+  /**
+   * Remove the overlay element.
+   */
+  remove: function() {
+    var that = this;
+    this.$el
+    .addClass('edit-animate-invisible')
+    .bind(Drupal.edit.util.constants.transitionEnd, function (event) {
+      that.$el.remove();
+      // @todo - should the overlay really do this?
+      $('.edit-form-container, .edit-toolbar-container, #edit_modal, .edit-curtain, .edit-validation-errors').remove();
+    });
+  }
+});
+
+})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/toolbar-view.js b/core/modules/edit/js/views/toolbar-view.js
new file mode 100644
index 0000000..55b5da3
--- /dev/null
+++ b/core/modules/edit/js/views/toolbar-view.js
@@ -0,0 +1,448 @@
+/**
+ * @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,
+
+  _id: null,
+
+  events: {
+    'click.edit a.label': 'onClickInfoLabel',
+    'mouseleave.edit': 'onMouseLeave',
+    'click.edit a.field-save': 'onClickSave',
+    'click.edit a.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: 'form', 'direct' or
+   *        'direct-with-wysiwyg'.
+   *      * 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;
+
+    // Generate a DOM-compatible ID for the toolbar DOM element.
+    var propertyID = Drupal.edit.util.calcPropertyID(this.entity, this.predicate);
+    this._id = 'edit-toolbar-for-' + propertyID.replace(/\//g, '_');
+  },
+
+  /**
+   * Listens to editor state changes.
+   */
+  stateChange: function(from, to) {
+    switch (to) {
+      case 'inactive':
+        // Nothing happens in this stage.
+        break;
+      case 'candidate':
+        if (from !== 'inactive') {
+          if (from !== 'highlighted' && this.editorName !== 'form') {
+            this._unpad(this.editorName);
+          }
+          this.remove();
+        }
+        break;
+      case 'highlighted':
+        // As soon as we highlight, make sure we have a toolbar in the DOM (with at least a title).
+        this.render();
+        this.startHighlight();
+        break;
+      case 'activating':
+        this.setLoadingIndicator(true);
+        break;
+      case 'active':
+        this.startEdit(this.editorName);
+        this.setLoadingIndicator(false);
+        if (this.editorName !== 'form') {
+          this._pad(this.editorName);
+        }
+        if (this.editorName === 'direct-with-wysiwyg') {
+          this.insertWYSIWYGToolGroups();
+        }
+        break;
+      case 'changed':
+        this.$el
+          .find('a.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);
+
+        // Replace the old content with the new content.
+        var updatedField = entity.get(predicate + '/rendered');
+        var $inner = $(updatedField).html();
+        editor.element.html($inner);
+
+        // @todo: VIE doesn't seem to like this? :) It seems that if I delete/
+        // overwrite an existing field, that VIE refuses to find the same
+        // predicate again for the same entity?
+        // self.$el.replaceWith(updatedField);
+        // debugger;
+        // console.log(self.$el, self.el, Drupal.edit.domService.findSubjectElements(self.$el));
+        // Drupal.edit.domService.findSubjectElements(self.$el).each(Drupal.edit.prepareFieldView);
+
+        editableEntity.setState('candidate', predicate);
+      },
+
+      // 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' });
+  },
+
+  /**
+   * Indicate in the 'info' toolgroup that we're waiting for a server reponse.
+   *
+   * @param bool enabled
+   *   Whether the loading indicator should be displayed or not.
+   */
+  setLoadingIndicator: function(enabled) {
+    if (enabled) {
+      this.addClass('info', 'loading');
+    }
+    else {
+      // Only stop showing the loading indicator after half a second to prevent
+      // it from flashing, which is bad UX.
+      var that = this;
+      setTimeout(function() {
+        that.removeClass('info', 'loading');
+      }, 500);
+    }
+  },
+
+  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
+      .find('.edit-toolbar')
+      // Append the "info" toolgroup into the toolbar.
+      .append(Drupal.theme('editToolgroup', {
+        classes: 'info',
+        buttons: [
+          { label: label, classes: 'blank-button label', hasButtonRole: false }
+        ]
+      }));
+
+    // 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'), classes: 'field-save save gray-button' },
+          { label: '<span class="close"></span>', classes: 'field-close close gray-button' }
+        ]
+      }));
+    this.show('ops');
+  },
+
+  /**
+   * Adjusts the toolbar to accomodate padding on the PropertyEditor widget.
+   *
+   * @see FieldDecorationView._pad().
+   */
+  _pad: function(editorName) {
+      // 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' });
+      // When using a WYSIWYG editor, the width of the toolbar must match the
+      // width of the editable.
+      if (editorName === 'direct-with-wysiwyg') {
+        $hf.css({ width: this.editor.element.width() + 10 });
+      }
+  },
+
+  /**
+   * Undoes the changes made by _pad().
+   *
+   * @see FieldDecorationView._unpad().
+   */
+  _unpad: function(editorName) {
+      // Move the toolbar back to its original position.
+      var $hf = this.$el.find('.edit-toolbar-heightfaker');
+      $hf.css({ bottom: '1px', left: '' });
+      // When using a WYSIWYG editor, restore the width of the toolbar.
+      if (editorName === 'direct-with-wysiwyg') {
+        $hf.css({ width: '' });
+      }
+  },
+
+  insertWYSIWYGToolGroups: function() {
+    this.$el
+      .find('.edit-toolbar')
+      .append(Drupal.theme('editToolgroup', {
+        classes: 'wysiwyg-tabs',
+        buttons: []
+      }))
+      .append(Drupal.theme('editToolgroup', {
+        classes: 'wysiwyg',
+        buttons: []
+      }));
+
+    // Animate the toolgroups into visibility.
+    var that = this;
+    setTimeout(function () {
+      that.show('wysiwyg-tabs');
+      that.show('wysiwyg');
+    }, 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.$el.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);
+    }
+
+    var that = this;
+    // Animate the toolbar into visibility.
+    setTimeout(function () {
+      that.$el.removeClass('edit-animate-invisible');
+    }, 0);
+  },
+
+  remove: function () {
+    if (!this.$el) {
+      return;
+    }
+
+    // Remove after animation.
+    var that = this;
+    var $el = this.$el;
+    this.$el
+      .addClass('edit-animate-invisible')
+      // Prevent this toolbar from being detected *while* it is being removed.
+      .removeAttr('id')
+      .find('.edit-toolbar .edit-toolgroup')
+      .addClass('edit-animate-invisible')
+      .bind(Drupal.edit.util.constants.transitionEnd, function (e) {
+        $el.remove();
+      });
+    // @todo: verify/confirm that this really necessary. Messing with this.$el
+    // is not recommended - maybe temporarily unbind/undelegate events?
+    // Immediately set to null, so that if the user hovers over the property
+    // before the removal been completed, a new toolbar can be created.
+    // this.$el = null;
+  },
+
+  /**
+   * Calculates the ID for this toolbar container.
+   *
+   * Only used to make sane hovering behavior possible.
+   *
+   * @return string
+   *   A string that can be used as the ID for this toolbar container.
+   */
+  getId: function() {
+    return 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/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextDefaultFormatter.php b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextDefaultFormatter.php
index 6b34ba9..85d2878 100644
--- a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextDefaultFormatter.php
+++ b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextDefaultFormatter.php
@@ -23,6 +23,9 @@
  *     "text",
  *     "text_long",
  *     "text_with_summary"
+ *   },
+ *   edit = {
+ *    "editability" = "direct"
  *   }
  * )
  */
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextPlainFormatter.php b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextPlainFormatter.php
index 0f7b615..8dc4bf1 100644
--- a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextPlainFormatter.php
+++ b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextPlainFormatter.php
@@ -23,6 +23,9 @@
  *     "text",
  *     "text_long",
  *     "text_with_summary"
+ *   },
+ *   edit = {
+ *    "editability" = "direct"
  *   }
  * )
  */
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextSummaryOrTrimmedFormatter.php b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextSummaryOrTrimmedFormatter.php
index 11f0c14..c69e5e0 100644
--- a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextSummaryOrTrimmedFormatter.php
+++ b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextSummaryOrTrimmedFormatter.php
@@ -22,6 +22,9 @@
  *   },
  *   settings = {
  *     "trim_length" = "600"
+ *   },
+ *   edit = {
+ *    "editability" = "form"
  *   }
  * )
  */
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextTrimmedFormatter.php b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextTrimmedFormatter.php
index 349cf63..add5d55 100644
--- a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextTrimmedFormatter.php
+++ b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter/TextTrimmedFormatter.php
@@ -31,6 +31,9 @@
  *   },
  *   settings = {
  *     "trim_length" = "600"
+ *   },
+ *   edit = {
+ *    "editability" = "form"
  *   }
  * )
  */
