diff --git includes/viewfield.views.inc includes/viewfield.views.inc
index da7b70f..4729c0f 100644
--- includes/viewfield.views.inc
+++ includes/viewfield.views.inc
@@ -12,9 +12,13 @@
  */
 function viewfield_views_query_alter(&$view, &$query) {
   global $_viewfield_stack;
-
-  if (!empty($_viewfield_stack) && in_array('node', $query->table_queue)) {
-    $placeholders = db_placeholders($_viewfield_stack);
-    $query->add_where(0, "node.nid NOT IN ($placeholders)", $_viewfield_stack);
+  if (!empty($_viewfield_stack)) {
+    foreach($_viewfield_stack as $entity_type => $entity_type_entry) {
+      if (isset($query->table_queue) && is_array($query->table_queue) && isset($query->table_queue[$entity_type])) {
+        $field = $query->table_queue[$entity_type]['alias'] . '.' . $entity_type_entry['entity_id_key'];
+        $values = $entity_type_entry['entity_id_set'];
+        $query->add_where(0, $field, $values, 'not in');
+      }
+    }
   }
 }
diff --git theme/viewfield.theme.inc theme/viewfield.theme.inc
deleted file mode 100644
index 2c1c3f3..0000000
--- theme/viewfield.theme.inc
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-
-/**
- * @file
- * Theme functions.
- */
-
-/**
- * Return a themed view avoiding viewfield recursion.
- */
-function theme_viewfield_formatter_default($element) {
-  global $_viewfield_stack;
-
-  $node = $element['#node'];
-  // For safety's sake, we can only display 2 levels of viewfields.
-  if (!isset($_viewfield_stack[$node->nid]) && count($_viewfield_stack) <= 2) {
-    list($view_name, $display) = explode('|', $element['#item']['vname'], 2);
-    $view_args = _viewfield_get_view_args($element['#item']['token_enabled'], $element['#item']['vargs'], $element['#node']);
-
-    // Prevent infinite recursion.
-    if ($node->nid) {
-      $_viewfield_stack[$node->nid] = $node->nid;
-    }
-
-    // Render the view like Views would do.
-    // @see views_embed_view()
-    $view = views_get_view($view_name);
-    if ($view && $view->access($display)) {
-      // Override the view's path to the current path. Otherwise, exposed views
-      // filters would submit to the front page.
-      $view->override_path = $_GET['q'];
-
-      $output = $view->preview($display, $view_args);
-    }
-
-    // This node is "safe" again.
-    if ($node->nid) {
-      unset($_viewfield_stack[$node->nid]);
-    }
-
-    // Only return an actual view result to not break empty value behavior.
-    if (isset($output)) {
-      return $output;
-    }
-  }
-}
-
-/**
- * Perform argument replacement
- */
-function _viewfield_get_view_args($token_enabled, $vargs, $node) {
-  $args = array();
-  // Prevent token_replace() from running this function a second time
-  // before it completes the first time.
-  static $tokens = TRUE;
-  if ($tokens && !empty($vargs)) {
-    $pos = 0;
-    while ($pos < strlen($vargs)) {
-      $found = FALSE;
-      // If string starts with a quote, start after quote and get everything
-      // before next quote.
-      if (strpos($vargs, '"', $pos) === $pos) {
-        if (($quote = strpos($vargs, '"', ++$pos)) !== FALSE) {
-          // Skip pairs of quotes.
-          while (!(($ql = strspn($vargs, '"', $quote)) & 1)) {
-            $quote = strpos($vargs, '"', $quote + $ql);
-          }
-          $args[] = str_replace('""', '"', substr($vargs, $pos, $quote + $ql - $pos - 1));
-          $pos = $quote + $ql + 1;
-          $found = TRUE;
-        }
-      }
-      elseif (($comma = strpos($vargs, ',', $pos)) !== FALSE) {
-        // Otherwise, get everything before next comma.
-        $args[] = substr($vargs, $pos, $comma - $pos);
-        // Skip to after comma and repeat
-        $pos = $comma + 1;
-        $found = TRUE;
-      }
-      if (!$found) {
-        $args[] = substr($vargs, $pos);
-        $pos = strlen($vargs);
-      }
-    }
-    if ($token_enabled) {
-      $tokens = FALSE;
-      // If the view field is being loaded as a "view field" of "view row",
-      // instead of a simple "node field", the node object is not fully populated:
-      // we need a full node to perform a correct replacement.
-      $node_values = node_load($node->nid);
-      $args = token_replace($args, 'node', $node_values);
-      $tokens = TRUE;
-    }
-    // For backwards compatibility, we scan for %nid, etc.
-    foreach ($args as $key => $value) {
-      $args[$key] = strtr($value, array(
-        '%nid' => $node->nid,
-        '%author' => isset($node->uid) ? $node->uid : (isset($node_values->uid) ? $node_values->uid : NULL),
-        '%viewer' => $GLOBALS['user']->uid,
-      ));
-    }
-  }
-  return $args;
-}
-
-function theme_viewfield_select($element) {
-  if (!empty($element['#children'])) {
-    $field = $element['#field_info'][$element['#field_name']];
-    if ($field['multiple'] && $element['#delta'] == 0) {
-      // This is needed only for multiple viewfields.
-      drupal_add_css(drupal_get_path('module', 'viewfield') .'/theme/viewfield.css');
-    }
-    return '<div class="viewfield-select">'. $element['#children'] .'</div>';
-  }
-}
diff --git viewfield.info viewfield.info
index 2585600..0bd825e 100644
--- viewfield.info
+++ viewfield.info
@@ -1,13 +1,5 @@
 name = Viewfield
 description = Defines a field type to display a view.
-package = CCK
+core = 7.x
+package = Fields
 dependencies[] = views
-dependencies[] = content
-core = 6.x
-
-; Information added by drupal.org packaging script on 2011-02-25
-version = "6.x-1.x-dev"
-core = "6.x"
-project = "viewfield"
-datestamp = "1298620631"
-
diff --git viewfield.install viewfield.install
index 99add3f..aa327a9 100644
--- viewfield.install
+++ viewfield.install
@@ -2,78 +2,67 @@
 
 /**
  * @file
- * Installation functions.
+ * Install file for the viewfield module.
  */
 
 /**
- * Implementation of hook_install().
+ * Implements hook_field_schema().
  */
-function viewfield_install() {
-  drupal_load('module', 'content');
-  content_notify('install', 'viewfield');
+function viewfield_field_schema($field) {
+  return array(
+    'columns' => array(
+       'vname' => array(
+         'type' => 'varchar',
+         'not null' => FALSE,
+         // In Views 3 the views name is varchar(32), display id is varchar(64),
+         // so accounting for a 1-character separator, length >= 97.
+         'length' => 100,
+       ),
+       'vargs' => array(
+         'type' => 'varchar',
+         'not null' => FALSE,
+         'length' => 255,
+       ),
+     ),
+  );
 }
 
 /**
- * Implementation of hook_uninstall().
- */
-function viewfield_uninstall() {
-  drupal_load('module', 'content');
-  content_notify('uninstall', 'viewfield');
-}
-
-/**
- * Implementation of hook_enable().
+ * Major module clean-up. (sun D6)
  *
- * Notify content module when this module is enabled.
+ * - $field['super_default'] is gone, which previously allowed to save nodes
+ *   without any viewfield value, but use the field's configured default value
+ *   automatically. Doesn't make sense, because the configured default value is
+ *   preselected in the form already, and CCK/Field API always stores a value,
+ *   even if it is the same as the default value.
+ * - $field['super_default_widget'] is equally gone, without replacement, as it
+ *   merely completed aforementioned functionality.
+ * - $field['widget']['override_default'] is also gone, ditto.
+ * - $field['token_enabled'] is also gone, as we can replace both legacy tokens
+ *   and Token module tokens without conflicts. Form descriptions have been adapted
+ *   to disclose potential performance degradation when Token module tokens
+ *   are used.
  */
-function viewfield_enable() {
-  drupal_load('module', 'content');
-  content_notify('enable', 'viewfield');
-}
 
 /**
- * Implementation of hook_disable().
+ * Initial D7 port (keithm)
  *
- * Notify content module when this module is disabled.
+ * - Designed to work in any fieldable entity.
+ * - Legacy tokens (%nid, %author, %viewer) are gone since they don't play
+ *   well with entities. [node:nid] and [current-user:uid] do not cause an extra
+ *   entity load, so these tokens should perform similarly to %nid and %viewer.
+ * - Token module must be enabled for the construction of Placeholder Token list
+ *   used in editing, but the module need not be enabled to render the field
+ *   itself.
+ * - All calls to theme() are gone, being replaced by D7 render array code.
+ * - theme_viewfield_select() is gone, being replaced by a small bit of code
+ *   pushed into the render array at the bottom of viewfield_select_process().
+ * - The data access code in theme_viewfield_formatter_default() has been pulled
+ *   out of the theme function, resulting in a very small theming job. Rather
+ *   than leave the only surviving theme function (7-lines) as the only contents
+ *   of theme/viewfield.theme.inc, theme_viewfield_formatter_default() has been
+ *   pulled into viewfield.module and theme/viewfield.theme.inc has been removed.
+ * - Increased size of vname to varchar(100) (see http://drupal.org/node/1096718).
+ * - It's possible that the view preview code, _viewfield_get_view_render_array()
+ *   and callees, should be pulled into an include file.
  */
-function viewfield_disable() {
-  drupal_load('module', 'content');
-  content_notify('disable', 'viewfield');
-}
-
-/**
- * Implementation of hook_update_N().
- */
-function viewfield_update_6000() {
-  if ($abort = content_check_update('viewfield')) {
-    return $abort;
-  }
-  $ret = array();
-  $result = db_query("SELECT * FROM {content_node_field} WHERE type = 'viewfield'");
-  while ($field = db_fetch_array($result)) {
-    $field_result = db_query("SELECT * FROM {content_node_field_instance} WHERE field_name = '%s'", $field['field_name']);
-    while ($instance = db_fetch_array($field_result)) {
-      $instance['widget_settings'] = unserialize($instance['widget_settings']);
-      $widget_settings = serialize(array('force_default' => $instance['widget_settings']['force_default']));
-      $instance['display_settings'] = unserialize($instance['display_settings']);
-      foreach ($instance['display_settings'] as $display_context => $display_setting) {
-        $instance['display_settings'][$display_context]['format'] = $display_setting['format'] == 'hidden' ? $display_setting['format'] : 'default';
-      }
-      $display_settings = serialize($instance['display_settings']);
-      $update = db_query("UPDATE {content_node_field_instance} SET widget_settings = '%s', display_settings = '%s' WHERE field_name = '%s' AND type_name = '%s'", $widget_settings, $display_settings, $instance['field_name'], $instance['type_name']);
-      $ret[] = array('success' => $result !== FALSE, 'query' => "UPDATE {content_node_field_instance} SET widget_settings = '$widget_settings', display_settings = '$display_settings' WHERE field_name = '$instance[field_name]' AND type_name = '$instance[type_name]'");
-    }
-    $display = $display_setting['format'] == 'block' ? 'block_1' : 'default';
-    $field['global_settings'] = unserialize($field['global_settings']);
-    $field['global_settings']['token_enabled'] = TRUE;
-    $super_default_widget = $field['global_settings']['super_default_widget'][0][$field['field_name']];
-    $field['global_settings']['super_default_widget'] = array(
-      'vname' => empty($super_default_widget['vnames']) ? NULL : $super_default_widget['vnames'] .'|'. $display,
-      'vargs' => $super_default_widget['vargs'],
-    );
-    $global_settings = serialize($field['global_settings']);
-    $update = db_query("UPDATE {content_node_field} SET global_settings = '%s' WHERE field_name = '%s'", $global_settings, $field['field_name']);
-    $ret[] = array('success' => $update !== FALSE, 'query' => "UPDATE {content_node_field} SET global_settings = '$global_settings' WHERE field_name = '$field[field_name]'");
-  }
-  return $ret;
-}
diff --git viewfield.module viewfield.module
old mode 100755
new mode 100644
index 14d6ba3..dd94b3d
--- viewfield.module
+++ viewfield.module
@@ -3,10 +3,19 @@
 /**
  * @file
  * Core functions.
+ *
+ * @todo Double-check potential performance issues mentioned in code comments.
+ * @todo Support migration of D6 tokens to D7 equivalents.
  */
 
 /**
- * Implementation of hook_field_info().
+ * Allowed levels of viewfield recursion.
+ * @see _viewfield_get_view_render_array()
+ */
+define('VIEWFIELD_MAX_RECURSION_LEVEL', 2);
+
+/**
+ * Implements hook_field_info().
  */
 function viewfield_field_info() {
   return array(
@@ -15,331 +24,415 @@ function viewfield_field_info() {
       // languages due to missing string translation contexts.
       'label' => t('Views'),
       'description' => t('Displays a selected view in a node.'),
-      'callbacks' => array(
-        'tables' => CONTENT_CALLBACK_NONE,
-        'arguments' => CONTENT_CALLBACK_NONE,
-      ),
+      'settings' => array('allowed_views' => array()),
+      'default_widget' => 'viewfield_select',
+      'default_formatter' => 'default',
     ),
   );
 }
 
 /**
- * Implementation of hook_field_settings().
+ * Implements hook_field_settings_form().
  */
-function viewfield_field_settings($op, $field) {
-  switch ($op) {
-    case 'form':
-      $form['allowed_views'] = array(
-        '#type' => 'checkboxes',
-        '#title' => t('Allowed values'),
-        '#default_value' => is_array($field['allowed_views']) ? $field['allowed_views'] : array(),
-        '#options' => drupal_map_assoc(array_keys(views_get_all_views())),
-        '#description' => t('Only selected views will be available for content authors. Leave empty to allow all.'),
-      );
-      $form['super_default'] = array(
-        '#type' => 'checkbox',
-        '#title' => t('Use a common default value for all nodes if the user does not override it on the node form.'),
-        '#default_value' => $field['super_default'],
-      );
-      $form['token_enabled'] = array(
-        '#type' => 'checkbox',
-        '#title' => t('Replace token placeholders'),
-        '#description' => t('Negatively affects site performance in combination with %fields row style.', array('%fields' => t('Fields'))),
-        '#default_value' => $field['token_enabled'],
-        '#access' => module_exists('token'),
-      );
-      $form_state = NULL;
-      $form['#node'] = (object) array('type' => $field['type_name']);
-      module_load_include('inc', 'content', 'includes/content.node_form');
-      $field_form = content_field_form($form, $form_state, $field, 0);
-      $form['super_default_widget'] = &$field_form[$field['field_name']][0];
-      $form['super_default_widget']['#tree'] = TRUE;
-      unset($form['super_default_widget']['#weight']);
-      return $form;
-
-    case 'validate':
-      if ($field['force_default'] && $field['multiple']) {
-        form_set_error('multiple', t('Multiple views are not supported if force default is enabled.'));
-      }
-      break;
-
-    case 'save':
-      return array('allowed_views', 'super_default', 'token_enabled', 'super_default_widget');
-
-    case 'database columns':
-      return array(
-        'vname' => array('type' => 'varchar', 'not null' => FALSE, 'length' => 32),
-        'vargs' => array('type' => 'varchar', 'not null' => FALSE, 'length' => 255),
-      );
-  }
+function viewfield_field_settings_form($field, $instance, $has_data) {
+  $settings = $field['settings'];
+  $form['allowed_views'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Allowed values'),
+    '#options' => drupal_map_assoc(array_keys(views_get_all_views())),
+    '#default_value' => isset($settings['allowed_views']) && is_array($settings['allowed_views']) ? $settings['allowed_views'] : array(),
+    '#description' => t('Only selected views will be available for content authors. Leave empty to allow all.'),
+  );
+  return $form;
 }
 
 /**
- * Implementation of hook_field().
+ * Implements hook_field_presave().
  */
-function viewfield_field($op, &$node, $field, &$items, $teaser, $page) {
-  switch ($op) {
-    case 'presave':
-      foreach ($items as $delta => $item) {
-        if ($field['super_default'] && empty($item['override_default']) || $field['widget']['force_default']) {
-          $items[$delta] = array('vname' => NULL, 'vargs' => NULL);
-        }
-        elseif (empty($item['vname'])) {
-          unset($items[$delta]);
-        }
-      }
-      break;
-
-    case 'sanitize':
-      foreach ($items as $delta => $item) {
-        $item = empty($item['vname']) || $field['widget']['force_default'] ? $field['super_default_widget'] : $item;
-        if ($field['super_default']) {
-          $item['override_default'] = !empty($item['vname']);
-        }
-        $item['token_enabled'] = _viewfield_token_enabled($field);
-        $items[$delta] = $item;
-      }
-      return array($field['field_name'] => $items);
+function viewfield_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  foreach ($items as $delta => $item) {
+    if (empty($item['vname'])) {
+      unset($items[$delta]);
+    }
   }
 }
 
 /**
- * Implementation of hook_content_is_empty().
+ * Implements hook_field_is_empty().
  */
-function viewfield_content_is_empty($item, $field) {
+function viewfield_field_is_empty($item, $field) {
   return empty($item['vname']);
 }
 
 /**
- * Implementation of hook_field_formatter_info().
+ * Implements hook_field_formatter_info().
  */
 function viewfield_field_formatter_info() {
   return array(
     'default' => array(
       'label' => t('Default'),
-      'field types' => array('viewfield')
+      'field types' => array('viewfield'),
     ),
   );
 }
 
 /**
- * Implementation of hook_widget_info().
+ * Implements hook_field_formatter_view().
  */
-function viewfield_widget_info() {
+function viewfield_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+  $elements = array();
+  switch ($display['type']) {
+    case 'default':
+      foreach ($items as $delta => $item) {
+        $elements[$delta] = array(
+          '#theme' => 'viewfield_formatter_default',
+          '#views_data' => _viewfield_get_view_render_array($item, $entity_type, $entity),
+        );
+      }
+      break;
+  }
+  return $elements;
+}
+
+/**
+ * Implements hook_field_widget_info().
+ */
+function viewfield_field_widget_info() {
   return array(
     'viewfield_select' => array(
       'label' => 'Select List',
       'field types' => array('viewfield'),
-      'multiple_values' => CONTENT_HANDLE_CORE,
-      'callbacks' => array(
-        'default value' => CONTENT_CALLBACK_NONE,
-      ),
+      'settings' => array('force_default' => FALSE),
     ),
   );
 }
 
 /**
- * Implementation of hook_widget_settings().
+ * Implements hook_field_widget_settings_form().
  */
-function viewfield_widget_settings($op, $widget) {
-  switch ($op) {
-    case 'form':
-      $form['force_default'] = array(
-        '#type' => 'checkbox',
-        '#title' => t('Always use default value'),
-        '#default_value' => $widget['force_default'],
-        '#description' => t('Hides this field in forms and forces the default value defined below.'),
-      );
-      return $form;
+function viewfield_field_widget_settings_form($field, $instance) {
+  $form['force_default'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Always use default value'),
+    '#default_value' => $instance['widget']['settings']['force_default'],
+    '#description' => t('Hides this field in forms and forces the default value defined below.'),
+  );
+  $form['#element_validate'] = array('viewfield_field_widget_settings_form_validate');
+  return $form;
+}
 
-    case 'save':
-      return array('force_default');
+/**
+ * Implements #element_validate for viewfield_field_widget_settings_form().
+ */
+function viewfield_field_widget_settings_form_validate($form, &$form_state) {
+  $field = $form_state['values']['field'];
+  $settings = $form_state['values']['instance']['widget']['settings'];
+  if ($settings['force_default'] && $field['cardinality'] !== 1) {
+    form_set_error('instance][widget][settings][force_default', t('Multiple views are not supported if force default is enabled.'));
   }
 }
 
 /**
- * Implementation of hook_widget().
+ * Implements hook_field_widget_form().
  */
-function viewfield_widget(&$form, &$form_state, $field, $items, $delta = 0) {
-  // Since tabledrag.js currently cannot handle nested tables, if we are
-  // adding/editing a node we have to show the token help just below the
-  // viewfield select widget.
-  if ($delta == 0 && $field['multiple'] && !$field['widget']['force_default'] && _viewfield_token_enabled($field)) {
-    $form['token_help'] = _viewfield_get_token_help($field);
-    $form['token_help']['#weight'] = $field['widget']['weight'] + 1;
-  }
-  $element = array(
-    '#type' => $field['widget']['type'],
-    '#default_value' => isset($items[$delta]) ? $items[$delta] : $field['super_default_widget'],
+function viewfield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+  $element += array(
+    '#type' => $instance['widget']['type'],
+    '#default_value' => isset($items[$delta]) ? $items[$delta] : NULL,
   );
   return $element;
 }
 
 /**
- * Implementation of FAPI hook_elements().
+ * Implements hook_element_info().
  */
-function viewfield_elements() {
+function viewfield_element_info() {
   return array(
     'viewfield_select' => array(
       '#input' => TRUE,
-      '#columns' => array('vname', 'vargs'),
       '#delta' => 0,
+      '#columns' => array('vname', 'vargs'),
       '#process' => array('viewfield_select_process'),
     ),
   );
 }
 
-function viewfield_select_process($element, $edit, $form_state, $form) {
-  $field = $form['#field_info'][$element['#field_name']];
-  $element['#field_info'] = $form['#field_info'];
+/**
+ * Implements the #process callback for viewfield_element_info().
+ */
+function viewfield_select_process($element, &$form_state, $complete_form) {
   // This form is used for both the default value field in the admin as well as
-  // the node edit form, so we have to make sure we show the default value field
-  // always.
-  $node = isset($form['#node']) ? $form['#node'] : (object) array('type' => $field['type_name']);
-  $field_settings = !isset($node->uid);
+  // the entity edit form, so we have to make sure we always show the default
+  // value field.
+  $is_field_settings_form = !isset($complete_form['#entity_type']);
 
-  if ($field['widget']['force_default'] && !$field_settings) {
-    $element['vname'] = array(
-      '#type' => 'value',
-      '#value' => $element['#default_value']['vname'],
-    );
-    $element['vargs'] = array(
-      '#type' => 'value',
-      '#value' => $element['#default_value']['vargs'], // All views share args (for now).
-    );
+  $field = field_widget_field($element, $form_state);
+  $instance = field_widget_instance($element, $form_state);
+  $widget = $instance['widget'];
+  $element['#field'] = $field;
+  // Display the form to let the user pick a view.
+  $options = _viewfield_potential_references($field, $instance, $element['#delta']);
+  $element['vname'] = array(
+    '#type' => 'select',
+    '#title' => $element['#title'],
+    '#options' => $options,
+    '#default_value' => $element['#default_value']['vname'],
+    '#required' => $instance['required'],
+    '#access' => $is_field_settings_form || !$widget['settings']['force_default'],
+    '#description' => $element['#description'],
+  );
+  // If there is only one option, only show arguments.
+  if (count($options) === 1 && !$is_field_settings_form) {
+    list($key, $label) = each($options);
+    $element['vname']['#access'] = FALSE;
+    $element['vname']['#default_value'] = $key;
   }
-  else {
-    // Display the form to let the user pick a view.
-    $options = _viewfield_potential_references($field, $element['#delta']);
-    // Provide our own overriding of defaults.
-    if ($field['super_default'] && !$field_settings) {
-      $element['override_default'] = array(
-        '#type' => 'checkbox',
-        '#title' => t('Override default'),
-        '#default_value' => $element['#value']['override_default'],
-      );
-    }
-    if (count($options) > 1) {
-      $element['vname'] = array(
-        '#type' => 'select',
-        '#options' => $options,
-        '#default_value' => $element['#default_value']['vname'],
-        '#title' => $element['#title'],
-        '#required' => $element['#required'],
-        '#description' => $element['#description'],
+
+  $element['vargs'] = array(
+    '#type' => 'textfield',
+    '#title' => !isset($label) ? t('Arguments') : t('%field (@value) arguments', array(
+      '%field' => $instance['label'],
+      '@value' => $label,
+    )),
+    '#default_value' => $element['#default_value']['vargs'],
+    '#access' => $is_field_settings_form || !$widget['settings']['force_default'],
+    '#description' => t('A comma separated list of arguments to pass to the selected view. Wrap arguments containing commas in double quotes. Replace double quotes in arguments with two double quotes.'),
+  );
+
+  if (module_exists('token')) {
+    $element['vargs']['#description'] .=
+      '<br />' . t('Available tokens: any token from the placeholder token list.')
+      . '<br />' . t('Note: Using placeholder tokens in combination with the %fields row style may negatively affect site performance.', array('%fields' => t('Fields')));
+    // Only show token help for first value or in field settings form.
+    if ($element['#delta'] === 0 && !$widget['settings']['force_default']) {
+      $element['token_help'] = array(
+        '#type' => 'fieldset',
+        '#title' => t('Placeholder tokens'),
+        '#collapsible' => TRUE,
+        '#collapsed' => TRUE,
       );
-    }
-    else {
-      // There's only the one view, so only show the arguments.
-      list($key, $label) = each($options);
-      $element['vname'] = array(
-        '#type' => 'value',
-        '#value' => $key,
+      $element['token_help']['tokens'] = array(
+        '#theme' => 'token_tree',
+        '#token_types' => array($element['#entity_type']),
+        '#global_types' => TRUE,
       );
     }
-    $element['vargs'] = array(
-      '#type' => 'textfield',
-      '#title' => !isset($label) ? t('Arguments') : t('%field (@value) arguments', array(
-        '%field' => $field['widget']['label'],
-        '@value' => $label,
-      )),
-      '#default_value' => $element['#default_value']['vargs'],
-      '#required' => FALSE,
-      '#description' => t('Provide a comma separated list of arguments to pass to the view. These arguments will be passed to EACH selected view. If an argument contains commas or double quotes, enclose it in double quotes. Replace double quotes that are part of the argument with pairs of double quotes.'),
-    );
-    $token_desc = ($token_enabled = _viewfield_token_enabled($field)) ?
-      t('Use the syntax [token] if you want to insert a replacement pattern.') :
-      t('You may use %nid for the node id of the current node. %author for the node author and %viewer for user viewing the node.');
-    $element['vargs']['#description'] .= "<br/>\n$token_desc";
-    // Since tabledrag.js currently cannot handle nested tables, we show the
-    // token help inside the fieldset only in the field settings form or in
-    // single-value mode.
-    if ($token_enabled && ($field_settings || !$field['multiple'])) {
-      $element['token_help'] = _viewfield_get_token_help($field);
-    }
   }
+  else {
+    $entity_info = entity_get_info($element['#entity_type']);
+    // There's probably an API function to create this token but I can't find
+    // one.
+    $entity_id_token = '[' . $element['#entity_type'] . ':' . $entity_info['entity keys']['id'] . ']';
+    $element['vargs']['#description'] .=
+    '<br />' . t('You may use %entity-id-token for the id of the current entity.', array('%entity-id-token' => $entity_id_token))
+    . '<br />' . t('Enable the %token module to get a complete listing of replacement tokens.', array('%token' => t('Token')));
+  }
+  // Make help text wrap correctly for multiple viewfields displayed in a table.
+  $element['#prefix'] = '<div class="viewfield-select">';
+  $element['#suffix'] = '</div>';
+  if (!$is_field_settings_form && $field['cardinality'] !== 1 && $element['#delta'] === 0) {
+    $element['#attached']['css'][] = drupal_get_path('module', 'viewfield') . '/theme/viewfield.css';
+  }
+
   return $element;
 }
 
 /**
  * Prepare a list of views for selection.
  */
-function _viewfield_potential_references($field, $delta = 0) {
+function _viewfield_potential_references($field, $instance, $delta = 0) {
   $options = array();
-  if (isset($field['allowed_views']) && is_array($field['allowed_views'])) {
-    $field['allowed_views'] = array_filter($field['allowed_views']);
+  if (isset($field['settings']['allowed_views']) && is_array($field['settings']['allowed_views'])) {
+    $field['settings']['allowed_views'] = array_filter($field['settings']['allowed_views']);
   }
-  else {
-    $field['allowed_views'] = array_keys(views_get_all_views());
+  // Allow all views if none are selected.
+  if (empty($field['settings']['allowed_views'])) {
+    $field['settings']['allowed_views'] = array_keys(views_get_all_views());
   }
-  foreach ($field['allowed_views'] as $view_name) {
+  foreach ($field['settings']['allowed_views'] as $view_name) {
     $view = views_get_view($view_name);
     foreach ($view->display as $display) {
-      $options[$view->name .'|'. $display->id] = $view->name .' - '. $display->display_title;
+      $options[$view->name . '|' . $display->id] = $view->name . ' - ' . $display->display_title;
     }
   }
   // Add a '0' option for non-required or subsequent values of multiple fields.
-  if (!$field['required'] || ($field['multiple'] && $delta > 0)) {
-    array_unshift($options, '<'. t('none') .'>');
+  if (!$instance['required'] || ($field['cardinality'] !== 1 && $delta > 0)) {
+    array_unshift($options, '<' . t('none') . '>');
   }
   return $options;
 }
 
 /**
- * Return the token replacement help
- */
-function _viewfield_get_token_help($field) {
-  // TODO: Token support right now is a bit hacked on, needs better integration,
-  // eventually a checkbox to enable/disable use of token-module here.
-  $token_help = array(
-    '#title' => t('Token replacement patterns'),
-    '#type' => 'fieldset',
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-    '#weight' => $field['widget']['weight']
-  );
-  $token_help['help'] = array(
-    '#value' => theme('token_help', 'node'),
-  );
-  return $token_help;
-}
-
-/**
- * Implementation of hook_theme().
+ * Implements hook_theme().
  */
 function viewfield_theme() {
   return array(
-    'viewfield_select' => array(
-      'arguments' => array('element' => NULL),
-      'file' => 'theme/viewfield.theme.inc',
-    ),
     'viewfield_formatter_default' => array(
-      'arguments' => array('element' => NULL),
-      'file' => 'theme/viewfield.theme.inc',
+      'variables' => array(
+        'views_data' => NULL,
+      ),
     ),
   );
 }
 
 /**
- * Implementation of hook_views_api().
+ * Return a themed viewfield.
+ */
+function theme_viewfield_formatter_default($variables) {
+  $views_data = $variables['views_data'];
+  $output = isset($views_data) && is_array($views_data) ? drupal_render($views_data) : NULL;
+  if (isset($output)) {
+    return $output;
+  }
+}
+
+/**
+ * Implements hook_views_api().
  */
 function viewfield_views_api() {
   return array(
-    'api' => 2.0,
-    'path' => drupal_get_path('module', 'viewfield') .'/includes',
+    'api' => 3.0,
+    'path' => drupal_get_path('module', 'viewfield') . '/includes',
   );
 }
 
 /**
- * If the super defaults are enabled return them, otherwise return blank values
+ * Return a view render array while preventing infinite viewfield recursion.
+ */
+function _viewfield_get_view_render_array($item, $entity_type, $entity) {
+  // $_viewfield_stack is used to keep a record of the current entity to prevent
+  // infinite recursion during the view rendering process.
+  // It is organized as follows:
+  //  _viewfield_stack_level_ => the total number of entities on the stack,
+  //  and then for each entity type there is an entry of the form:
+  // entity_type => array(entity_id_key, array(id1, id2, id3, ...)).
+  // The structures for each entity type are used by this function and
+  // viewfield_views_query_alter() in includes/viewfield.views.inc.
+  global $_viewfield_stack;
+  if (!isset($_viewfield_stack)) {
+    $_viewfield_stack['_viewfield_stack_level_'] = 0;
+  }
+
+  $result = array();
+  $entity_info = entity_get_info($entity_type);
+  $entity_id_key = $entity_info['entity keys']['id'];
+  $entity_id = isset($entity) ? $entity->{$entity_id_key} : NULL;
+
+  // We display up to VIEWFIELD_MAX_RECURSION_LEVEL levels of viewfields as a
+  // compromise between fidelity and performance, and we never do this operation
+  // twice for the same entity.
+  if (($_viewfield_stack['_viewfield_stack_level_'] <= VIEWFIELD_MAX_RECURSION_LEVEL) &&
+      !(isset($_viewfield_stack[$entity_type]['entity_id_set']) &&
+        in_array($entity_id, $_viewfield_stack[$entity_type]['entity_id_set']))) {
+    if ($entity_id) {
+      if (!isset($_viewfield_stack[$entity_type])) {
+        $_viewfield_stack[$entity_type] = array(
+          'entity_id_key' => $entity_id_key,
+          'entity_id_set' => array(),
+        );
+      }
+      // Push current entity.
+      $_viewfield_stack['_viewfield_stack_level_']++;
+      $_viewfield_stack[$entity_type]['entity_id_set'][] = $entity_id;
+    }
+
+    list($view_name, $display_id) = explode('|', $item['vname'], 2);
+    $view = views_get_view($view_name);
+    if (is_object($view) && $view->access($display_id)) {
+
+      $view_args = _viewfield_get_view_args($item['vargs'], $entity_type, $entity_id_key, $entity);
+      // Override the view's path to the current path. Otherwise, exposed views
+      // filters would submit to the front page.
+      $view->override_path = $_GET['q'];
+      $result = array(
+        // This will have to do for now until we figure out how to get a more
+        // useful render array out of views. As of Views 3 it's not possible;
+        // see http://drupal.org/node/1096286.
+        // @see views_embed_view()
+        '#markup' => $view->preview($display_id, $view_args),
+      );
+    }
+    // Pop current entity.
+    if ($entity_id) {
+      array_pop($_viewfield_stack[$entity_type]['entity_id_set']);
+      $_viewfield_stack['_viewfield_stack_level_']--;
+      if (count($_viewfield_stack[$entity_type]['entity_id_set'] === 0)) {
+        unset($_viewfield_stack[$entity_type]);
+      }
+    }
+  }
+
+  return $result;
+}
+
+/**
+ * Perform token argument replacement
  */
-function _viewfield_get_super_defaults($field) {
-  return $field['super_default'] ? $field['super_default_widget'] : array('vname' => NULL, 'vargs' => NULL);
+function _viewfield_get_view_args($vargs, $entity_type, $entity_id_key, $entity) {
+  $args = array();
+  // Prevent token_replace() from running this function a second time before it
+  // completes the first time.
+  static $replacing_tokens = FALSE;
+  if (!$replacing_tokens && !empty($vargs)) {
+    $pos = 0;
+    while ($pos < strlen($vargs)) {
+      $found = FALSE;
+      // If string starts with a quote, start after quote and get everything
+      // before next quote.
+      if (strpos($vargs, '"', $pos) === $pos) {
+        if (($quote = strpos($vargs, '"', ++$pos)) !== FALSE) {
+          // Skip pairs of quotes.
+          while (!(($ql = strspn($vargs, '"', $quote)) & 1)) {
+            $quote = strpos($vargs, '"', $quote + $ql);
+          }
+          $args[] = str_replace('""', '"', substr($vargs, $pos, $quote + $ql - $pos - 1));
+          $pos = $quote + $ql + 1;
+          $found = TRUE;
+        }
+      }
+      elseif (($comma = strpos($vargs, ',', $pos)) !== FALSE) {
+        // Otherwise, get everything before next comma.
+        $args[] = substr($vargs, $pos, $comma - $pos);
+        // Skip to after comma and repeat
+        $pos = $comma + 1;
+        $found = TRUE;
+      }
+      if (!$found) {
+        $args[] = substr($vargs, $pos);
+        $pos = strlen($vargs);
+      }
+    }
+
+    $replacing_tokens = TRUE;
+    $token_data = array($entity_type => $entity);
+    $entity_loaded = FALSE;
+    foreach ($args as $key => $value) {
+      // Load the full entity only when required.
+      if (!$entity_loaded && _viewfield_token_requires_load($value, $entity_type, $entity_id_key)) {
+        $id = $entity->{$entity_id_key};
+        $entities = entity_load($entity_type, array($id));
+        $token_data = array($entity_type => $entities[$id]);
+        $entity_loaded = TRUE;
+      }
+     $args[$key] = token_replace($value, $token_data);
+    }
+    $replacing_tokens = FALSE;
+  }
+  return $args;
 }
 
 /**
- * Check if the token replacements are enabled
+ *  Detect if a token requires the full entity to be loaded.
+ *  We conservatively require a load if a token refers to the entity type but
+ *  the token key is not the entity id. For example, if the $entity_type == '
+ *  node' and $entity_id_key == 'nid', we are searching for tokens of the
+ *  form [node:some-key] where some-key != 'nid'.
+ *  Global tokens (e.g., [current-user:uid]) never require entity loading.
  */
-function _viewfield_token_enabled($field) {
-  return $field['token_enabled'] && module_exists('token');
+function _viewfield_token_requires_load($text, $entity_type, $entity_id_key) {
+  $pattern = '/^\[' . $entity_type . ':(.*)\]$/';
+  if (preg_match($pattern, $text, $matches)) {
+    return ($entity_id_key !== $matches[1]);
+  }
+  return FALSE;
 }
