diff --git a/imagefield_focus.css b/imagefield_focus.css
index 7de3fe6..3447000 100644
--- a/imagefield_focus.css
+++ b/imagefield_focus.css
@@ -8,7 +8,25 @@
   border: 2px solid #999;
 }
 
-.imagefield-focus.focus-box
-{
-  min-width: 500px;
+#imagefield-focus-crop-focus-media-form {
+  display: none;
+  width: 47%;
+  position: absolute;
+  right: 0;
+  top: 0;
+  padding: 0 10px;
+  margin: 0;
+  background-color: #f7ecc8;
+  border: 1px solid #CCC;
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
+#imagefield-focus-crop-focus-media-form .close-button {
+  color: #999;
+  cursor: pointer;
+  right: .1em;
+  font-size: 2em;
+  position: absolute;
+  top: .1em;
 }
diff --git a/imagefield_focus.js b/imagefield_focus.js
deleted file mode 100644
index 31fafb2..0000000
--- a/imagefield_focus.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * @file
- * Written by Henri MEDOT <henri.medot[AT]absyx[DOT]fr>
- * http://www.absyx.fr
- */
-
-(function($) {
-
-  // Helper functions.
-  var valToSel = function(val) {
-    var sel = val.split(',');
-    if (sel.length != 4) {
-      return null;
-    }
-
-    for (var i = 0; i < 4; i++) {
-      var v = Number(sel[i]);
-      if (isNaN(v)) {
-        return null;
-      }
-      sel[i] = Math.round(v);
-    }
-
-    if ((sel[0] < 0) || (sel[1] < 0) || (sel[2] <= 0) || (sel[3] <= 0)) {
-      return null;
-    }
-
-    return sel;
-  };
-
-  var selToVal = function(sel) {
-    return (sel) ? sel.join(',') : '';
-  };
-  //~Helper functions.
-
-
-
-  // Drupal behavior.
-  Drupal.behaviors.imagefield_focus = {
-    attach: function(context) {
-      $('.imagefield-focus.focus-box', context).once('imagefield_focus', function() {
-        var $this = $(this);
-        var img = $('> img', $this);
-        var inputs = $('input.imagefield-focus', $this.parent());
-        var fieldName = inputs.attr('name').replace(/^([a-z0-9_]+)\[.+$/, '$1');
-        var settings = Drupal.settings.imagefield_focus[fieldName];
-
-        var minSize, ratio;
-        if (settings.min_width && settings.min_height) {
-          minSize = [settings.min_width, settings.min_height];
-          ratio = settings.lock_ratio;
-        }
-
-        var options = {
-          boxSize: $this.width() || parseInt($this.css('min-width')),
-          realSize: img.attr('alt').split('x'),
-          minSize: minSize
-        };
-        img.css('display', 'block').imgfocus(options);
-
-        inputs.focus(function(e, init) {
-          img.unbind('change.imagefield-focus');
-          var input = $(this).addClass('active');
-          var other = inputs.eq((inputs.index(input) + 1) % 2).removeClass('active');
-          var otherSel = valToSel(other.val());
-          var options = (input.hasClass('crop-rect')) ? {
-            ratio: 0,
-            minRect: otherSel,
-            maxRect: null
-          } : {
-            ratio: ratio,
-            minRect: null,
-            maxRect: otherSel
-          };
-          img.imgfocus('options', options).imgfocus('selection', valToSel(input.val()));
-          img.bind('change.imagefield-focus', function() {
-            input.val(selToVal(img.imgfocus('selection')));
-          });
-
-          if (init) {
-            e.stopPropagation();
-            e.preventDefault();
-            return false;
-          }
-        }).change(function() {
-          $(this).val(selToVal(img.imgfocus('selection', valToSel($(this).val())).imgfocus('selection')));
-        });
-
-        inputs.eq(0).trigger('focus', [true]);
-      });
-    }
-  };
-  //~Drupal behavior.
-
-})(jQuery);
diff --git a/imagefield_focus.module b/imagefield_focus.module
index ec05312..5dac4d7 100644
--- a/imagefield_focus.module
+++ b/imagefield_focus.module
@@ -8,7 +8,27 @@
 
 require_once dirname(__FILE__) .'/imagefield_focus.effects.inc';
 
+/**
+ * Implements hook_menu().
+ */
+function imagefield_focus_menu() {
+  $items['imagefield_focus/file/%file/%'] = array(
+    'description' => t('AJAX callback that returns the crop and focus form.'),
+    'page callback' => 'imagefield_focus_crop_focus_form',
+    'page arguments' => array(2, 3),
+    'access arguments' => array('view'),
+    'type' => MENU_CALLBACK,
+  );
+  $items['imagefield_focus/file/%file/save'] = array(
+    'description' => t('AJAX callback for saving crop and focus information for the given file.'),
+    'page callback' => 'imagefield_focus_crop_focus_post',
+    'page arguments' => array(2),
+    'access arguments' => array('view'),
+    'type' => MENU_CALLBACK,
+  );
 
+  return $items;
+}
 
 /**
  * Implementation of hook_field_info_alter().
@@ -21,102 +41,102 @@ function imagefield_focus_field_info_alter(&$info) {
   );
 }
 
-
-
 /**
- * Implementation of hook_form_FORM_ID_alter(); alter field_ui_field_edit_form.
+ * Implementation of hook_form_FORM_ID_alter().
  */
 function imagefield_focus_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
+  // Add imagefield_focus settings to the field settings form if the field is an
+  // image and has a "process" function.
   if (($form['#field']['type'] == 'image') && imagefield_focus_widget_support($form['#instance']['widget']['type'])) {
-    $settings = $form['#instance']['settings'];
-
-    $additions['focus_settings'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Focus settings'),
-      '#collapsible' => TRUE,
-      '#collapsed' => empty($settings['focus']),
-      '#parents' => array('instance', 'settings'),
-      '#weight' => 17,
-    );
+    $fields = imagefield_focus_settings_form_fields($form, $form_state, $form['#instance']['settings']);
+    $form['instance']['settings'] += $fields;
+  }
+}
 
-    $additions['focus_settings']['focus'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Enable focus'),
-      '#default_value' => (!empty($settings['focus'])) ? $settings['focus'] : 0,
-      '#description' => t('Enable user-defined focus and crop rectangles for images.'),
-    );
+/**
+ * Element #process callback function; process widget type image_image.
+ */
+function imagefield_focus_widget_image_image_process($element, &$form_state, $form) {
+  $added = &drupal_static(__FUNCTION__);
 
-    $focus_min_size = explode('x', $settings['focus_min_size']) + array('', '');
-    $additions['focus_settings']['focus_min_size'] = array(
-      '#type' => 'item',
-      '#title' => t('Minimum size'),
-      '#element_validate' => array('_image_field_resolution_validate', '_imagefield_focus_focus_min_size_validate'),
-      '#field_prefix' => '<div class="container-inline">',
-      '#field_suffix' => '</div>',
-      '#description' => t('The minimum allowed focus rectangle size expressed as WIDTHxHEIGHT (e.g. 100x75). Leave empty for no restriction.'),
-    );
-    $additions['focus_settings']['focus_min_size']['x'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Minimum width'),
-      '#title_display' => 'invisible',
-      '#default_value' => $focus_min_size[0],
-      '#size' => 5,
-      '#maxlength' => 5,
-      '#field_suffix' => ' x ',
-    );
-    $additions['focus_settings']['focus_min_size']['y'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Minimum height'),
-      '#title_display' => 'invisible',
-      '#default_value' => $focus_min_size[1],
-      '#size' => 5,
-      '#maxlength' => 5,
-      '#field_suffix' => ' '. t('pixels'),
-    );
+  $item = $element['#value'];
+  $item['key'] = $element['#field_name'];
+  $field_name = $element['#field_name'];
+  $instance = field_widget_instance($element, $form_state);
 
-    $additions['focus_settings']['focus_lock_ratio'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Lock ratio'),
-      '#default_value' => (!empty($settings['focus_lock_ratio'])) ? $settings['focus_lock_ratio'] : 0,
-      '#description' => 'Whether focus rectangle aspect ratio should be maintained based on the minimum size specified above. Checking this box has no effect if no minimum size is specified.',
-    );
+  if ($element['#file']) {
+    // Add JS and CSS.
+    $settings = _imagefield_focus_build_js_settings($field_name, $instance['settings']);
+    _imagefield_focus_attach_files($settings);
 
-    $form['instance']['settings'] += $additions;
+    $crop_focus_form = imagefield_focus_crop_focus_form_fields($element['#file']->uri, $item);
+    $element += $crop_focus_form;
+    $element['#element_validate'][] = 'imagefield_focus_widget_validate';
   }
+
+  return $element;
+}
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ */
+function imagefield_focus_form_media_admin_config_browser_alter(&$form, &$form_state) {
+  // Add imagefield_focus settings to the media browser settings form.
+  $fields = imagefield_focus_settings_form_fields($form, $form_state, variable_get('imagefield_focus_media_settings', array()));
+  $form += $fields;
+  $form['#submit'][] = 'imagefield_focus_settings_media_configuration_form_submit';
+}
+
+/**
+ * Form submit function that handles focus settings for the media module.
+ */
+function imagefield_focus_settings_media_configuration_form_submit($form, $form_state) {
+  // Create an array of all focus settings and save them as a variable.
+  if ($form_state['values']['focus']) {
+    $focus_settings = array(
+      'focus' => $form_state['values']['focus'],
+      'focus_lock_ratio' => $form_state['values']['focus_lock_ratio'],
+      'focus_min_size' => $form_state['values']['focus_min_size'],
+    );
+  }
+  else {
+    $focus_settings = array();
+  }
+
+  variable_set('imagefield_focus_media_settings', $focus_settings);
 }
 
 /**
  * Element validation function; validate focus_min_size.
  */
-function _imagefield_focus_focus_min_size_validate($element, &$form_state, $form) {
-  $min_resolution_element = $form['instance']['settings']['min_resolution'];
-  if (!form_get_error($element['x']) && !form_get_error($element['y'])
-   && !form_get_error($min_resolution_element['x']) && !form_get_error($min_resolution_element['y'])) {
-    $value = $form_state['values']['instance']['settings']['focus_min_size'];
-    if (!empty($value)) {
-      $dimensions = explode('x', $value);
-      $min_resolution_value = $form_state['values']['instance']['settings']['min_resolution'];
-      if (empty($min_resolution_value)) {
-        $error_element = $min_resolution_element['x'];
-      }
-      else {
-        $min_resolution_dimensions = explode('x', $min_resolution_value);
-        if ($min_resolution_dimensions[0] < $dimensions[0]) {
+function imagefield_focus_focus_min_size_validate($element, &$form_state, $form) {
+  if (isset($form['instance']['settings']['min_resolution'])) {
+    $min_resolution_element = $form['instance']['settings']['min_resolution'];
+    if (!form_get_error($element['x']) && !form_get_error($element['y']) && !form_get_error($min_resolution_element['x']) && !form_get_error($min_resolution_element['y'])) {
+      $value = $form_state['values']['instance']['settings']['focus_min_size'];
+      if (!empty($value)) {
+        $dimensions = explode('x', $value);
+        $min_resolution_value = $form_state['values']['instance']['settings']['min_resolution'];
+        if (empty($min_resolution_value)) {
           $error_element = $min_resolution_element['x'];
         }
-        elseif ($min_resolution_dimensions[1] < $dimensions[1]) {
-          $error_element = $min_resolution_element['y'];
+        else {
+          $min_resolution_dimensions = explode('x', $min_resolution_value);
+          if ($min_resolution_dimensions[0] < $dimensions[0]) {
+            $error_element = $min_resolution_element['x'];
+          }
+          elseif ($min_resolution_dimensions[1] < $dimensions[1]) {
+            $error_element = $min_resolution_element['y'];
+          }
+        }
+        if (isset($error_element)) {
+          form_error($error_element, t('Minimum resolution must be set and be greater or equal to focus minimum size, i.e. %focus_min_size.', array('%focus_min_size' => $value)));
         }
-      }
-      if (isset($error_element)) {
-        form_error($error_element, t('Minimum resolution must be set and be greater or equal to focus minimum size, i.e. %focus_min_size.', array('%focus_min_size' => $value)));
       }
     }
   }
 }
 
-
-
 /**
  * Implementation of hook_field_widget_form_alter().
  */
@@ -130,73 +150,177 @@ function imagefield_focus_field_widget_form_alter(&$element, &$form_state, $cont
 }
 
 /**
- * Element #process callback function; process widget type image_image.
+ * Imlpements hook_form_FORM_ID_alter().
+ *
+ * Alter the file entity upload form to add a "upload and set focus" button.
  */
-function imagefield_focus_widget_image_image_process($element, &$form_state, $form) {
-  static $added;
-
-  $item = $element['#value'];
-  $field_name = $element['#field_name'];
-  $instance = field_widget_instance($element, $form_state);
-  $settings = $instance['settings'];
+function imagefield_focus_form_file_entity_add_upload_alter(&$form, &$form_state) {
+  $form['#submit'][] = 'imagefield_focus_file_entity_add_upload_submit';
+  $form['set_focus'] = array(
+    '#type' => 'submit',
+    '#value' => t('Upload and Set Focus'),
+  );
+}
 
-  // Add JS and CSS.
-  $path = drupal_get_path('module', 'imagefield_focus');
-  drupal_add_js( $path .'/imgfocus/jquery.imgfocus.min.js');
-  drupal_add_js( $path .'/imagefield_focus.js');
-  drupal_add_css($path .'/imgfocus/jquery.imgfocus.css');
-  drupal_add_css($path .'/imagefield_focus.css');
-
-  // Add settings.
-  if (!isset($added[$field_name])) {
-    list($w, $h) = explode('x', $settings['focus_min_size']) + array('', '');
-    drupal_add_js(array(
-      'imagefield_focus' => array(
-        $field_name => array(
-          'min_width' => intval($w),
-          'min_height' => intval($h),
-          'lock_ratio' => !empty($settings['focus_lock_ratio']),
-        ),
-      ),
-    ), 'setting');
-    $added[$field_name] = TRUE;
+/**
+ * Implements hook_form_FORM_ID_alter().
+ * Adds the crop and focus form to the file/%fid/edit form.
+ */
+function imagefield_focus_form_file_entity_edit_alter(&$form, &$form_state) {
+  $settings = variable_get('imagefield_focus_media_settings', array());
+  if ($form['type']['#value'] == 'image' && ['focus']) {
+    $file = file_load($form['fid']['#value']);
+    $file->key = 'media';
+    $item = (array) $file;
+
+    $form['imagefield_focus'] = array(
+      '#prefix' => '<h3>' . t('Image Focus Settings') . '</h3>',
+    ) + imagefield_focus_crop_focus_form_fields($file->uri, $item);
+
+    $form['#validate'][] = 'imagefield_focus_crop_focus_form_validate';
+    $form['#submit'][] = 'imagefield_focus_crop_focus_form_submit';
+
+    // Attach css and js used by imagefield_focus.
+    $settings = _imagefield_focus_build_js_settings('media', $settings);
+    _imagefield_focus_attach_files($settings, $form);
   }
+}
+
+/**
+ * Crop and focus form for the media module.
+ */
+function imagefield_focus_crop_focus_media_form($form, $form_state, $file) {
+  $item = (array) $file;
+  $form = imagefield_focus_crop_focus_form_fields($file->uri, $item);
+
+  $form['focus_box']['#weight'] = -10;
+  $form['form-title'] = array(
+    '#markup' => '<h3>' . t('Image focus and crop') . '</h3>' . '<div class="description">' . t('Focus and crop information is automatically saved.') . '</div>',
+    '#weight' => -20,
+  );
+  $form['close-button'] = array(
+    '#markup' => '<span class="close-button">&#9746;</span>',
+    '#weight' => 100,
+  );
+  return $form;
+}
+
+/**
+ * Returns the fields used by the focus settings form.
+ */
+function imagefield_focus_settings_form_fields($form, $form_state, $values) {
+  $values += array(
+    'focus' => 0,
+    'focus_min_size' => '',
+    'focus_lock_ratio' => 0,
+  );
+
+  $fields['focus_settings'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Focus settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => empty($values['focus']),
+    '#parents' => array('instance', 'settings'),
+    '#weight' => 17,
+  );
+
+  $fields['focus_settings']['focus'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Enable focus'),
+    '#default_value' => (!empty($values['focus'])) ? $values['focus'] : 0,
+    '#description' => t('Enable user-defined focus and crop rectangles for images.'),
+  );
+
+  $focus_min_size = explode('x', $values['focus_min_size']) + array('', '');
+  $fields['focus_settings']['focus_min_size'] = array(
+    '#type' => 'item',
+    '#title' => t('Minimum size'),
+    '#element_validate' => array('_image_field_resolution_validate', 'imagefield_focus_focus_min_size_validate'),
+    '#field_prefix' => '<div class="container-inline">',
+    '#field_suffix' => '</div>',
+    '#description' => t('The minimum allowed focus rectangle size expressed as WIDTHxHEIGHT (e.g. 100x75). Leave empty for no restriction.'),
+  );
+  $fields['focus_settings']['focus_min_size']['x'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Minimum width'),
+    '#title_display' => 'invisible',
+    '#default_value' => $focus_min_size[0],
+    '#size' => 5,
+    '#maxlength' => 5,
+    '#field_suffix' => ' x ',
+  );
+  $fields['focus_settings']['focus_min_size']['y'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Minimum height'),
+    '#title_display' => 'invisible',
+    '#default_value' => $focus_min_size[1],
+    '#size' => 5,
+    '#maxlength' => 5,
+    '#field_suffix' => ' '. t('pixels'),
+  );
+
+  $fields['focus_settings']['focus_lock_ratio'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Lock ratio'),
+    '#default_value' => (!empty($values['focus_lock_ratio'])) ? $values['focus_lock_ratio'] : 0,
+    '#description' => t('Whether focus rectangle aspect ratio should be maintained based on the minimum size specified above. Checking this box has no effect if no minimum size is specified.'),
+  );
+
+  return $fields;
+}
 
-  // Add focus fields.
-  $element['focus_rect'] = array(
+/**
+ * Build the focus and crop form.
+ */
+function imagefield_focus_crop_focus_form_fields($uri, $item) {
+  $info = image_get_info($uri);
+  $form['focus_rect'] = array(
     '#type' => 'textfield',
     '#title' => t('Focus rectangle'),
     '#default_value' => isset($item['focus_rect']) ? $item['focus_rect'] : '',
     '#description' => t('The important portion of the image to set focus to and that should <strong>never</strong> be cut. It is recommended to keep it as small as possible for best results.'),
-    '#attributes' => array('class' => array('imagefield-focus', 'focus-rect')),
+    '#attributes' => array(
+      'class' => array('imagefield-focus', 'focus-rect'),
+      'data-key' => isset($item['key']) ? $item['key'] : '',
+    ),
     '#access' => (bool) $item['fid'],
   );
-  $element['crop_rect'] = array(
+  $form['crop_rect'] = array(
     '#type' => 'textfield',
     '#title' => t('Crop rectangle'),
     '#default_value' => isset($item['crop_rect']) ? $item['crop_rect'] : '',
     '#description' => t('When set, the portions of the image outside this area will <strong>always</strong> be cut out.'),
-    '#attributes' => array('class' => array('imagefield-focus', 'crop-rect')),
+    '#attributes' => array(
+      'class' => array('imagefield-focus', 'crop-rect'),
+      'data-key' => isset($item['key']) ? $item['key'] : '',
+    ),
     '#access' => (bool) $item['fid'],
   );
 
-  // Add focus box.
-  if ($element['#file']) {
-    $uri = $element['#file']->uri;
-    $info = (isset($element['#value']['width']) && isset($element['#value']['height'])) ? array(
-      'width'  => $element['#value']['width'],
-      'height' => $element['#value']['height'],
-    ) : image_get_info($uri);
-
-    if (is_array($info)) {
-      $element['focus_box'] = array(
-        '#markup' => '<div class="imagefield-focus focus-box"><img src="'. file_create_url($uri) .'" alt="'. $info['width'] .'x'. $info['height'] .'" style="display:none;" /></div>',
-      );
-    }
+  if (is_array($info)) {
+    $form['focus_box'] = array(
+      '#markup' => '<div class="imagefield-focus focus-box"><img src="'. file_create_url($uri) .'" alt="'. $info['width'] .'x'. $info['height'] .'" style="display:none;" /></div>',
+    );
   }
 
-  $element['#element_validate'][] = 'imagefield_focus_widget_validate';
-  return $element;
+  return $form;
+}
+
+/**
+ * Submit function for the file entity upload form.
+ */
+function imagefield_focus_file_entity_add_upload_submit($form, &$form_state) {
+  if ($form_state['clicked_button']['#value'] == t('Upload and Set Focus')) {
+    if (!empty($form_state['#media_browser'])) {
+      $form_state['redirect'] = array('media/browser', array('fragment' => 'media-tab-library', 'query' => array(
+        'render' => 'media-popup',
+        'imagefield_focus_fid' => $form_state['file']->fid,
+      )));
+    }
+    else {
+      $form_state['redirect'] = array('file/' . $form_state['file']->fid . '/edit');
+    }
+  }
 }
 
 /**
@@ -204,16 +328,66 @@ function imagefield_focus_widget_image_image_process($element, &$form_state, $fo
  */
 function imagefield_focus_widget_validate($element, &$form_state, $form) {
   foreach (array('focus_rect', 'crop_rect') as $key) {
-    if (isset($element['#value'][$key])) {
-      $value = trim($element['#value'][$key]);
-      if ((strlen($value) > 0) && !imagefield_focus_parse($value)) {
-        form_error($element[$key], t('The specified rectangle value is invalid.'));
-      }
+    if (isset($element['#value'][$key]) && !imagefield_focus_validate_crop_focus_rect($element['#value'][$key])) {
+      form_error($element[$key], t('The specified rectangle value is invalid.'));
     }
   }
 }
 
+/**
+ * AJAX callback. Returns a JSON object containing the crop and focus form.
+ */
+function imagefield_focus_crop_focus_form($file, $key = '') {
+  $file->key = $key;
+  $image = image_load($file->uri);
+
+  $form = drupal_get_form('imagefield_focus_crop_focus_media_form', $file);
+  $output = array(
+    'success' => !empty($image),
+    'markup' => !empty($image) ? drupal_render($form) : '',
+  );
 
+  drupal_json_output($output);
+  exit();
+}
+
+/**
+ * Submit function for the crop and focus form when submitted via AJAX.
+ */
+function imagefield_focus_crop_focus_post($file) {
+  // Sanitize the crop and focus submissions just to be safe.
+  $crop_rect = isset($_POST['crop_rect']) ? trim(check_plain($_POST['crop_rect'])) : '';
+  $focus_rect = isset($_POST['focus_rect']) ? trim(check_plain($_POST['focus_rect'])) : '';
+
+  // If valid, save the crop and focus values.
+  if ((strlen($crop_rect) == 0 || imagefield_focus_parse($crop_rect)) && (strlen($focus_rect) == 0 || imagefield_focus_parse($focus_rect))) {
+    $file->crop_rect = $crop_rect;
+    $file->focus_rect = $focus_rect;
+    _imagefield_focus_file_save($file);
+  }
+}
+
+/**
+ * Form validation function.
+ */
+function imagefield_focus_crop_focus_form_validate($form, &$form_state) {
+  foreach (array('focus_rect', 'crop_rect') as $key) {
+    if (isset($form_state['values'][$key]) && !imagefield_focus_validate_crop_focus_rect($form_state['values'][$key])) {
+      form_error($key, t('The specified rectangle value is invalid.'));
+    }
+  }
+}
+
+/**
+ * Form submit function.
+ */
+function imagefield_focus_crop_focus_form_submit($form, &$form_state) {
+  $file = file_load($form['fid']['#value']);
+  $file->focus_rect = $form_state['values']['focus_rect'];
+  $file->crop_rect = $form_state['values']['crop_rect'];
+
+  _imagefield_focus_file_save($file);
+}
 
 /**
  * Implementation of hook_field_attach_insert().
@@ -247,7 +421,82 @@ function imagefield_focus_field_attach_update($entity_type, $entity) {
   imagefield_focus_field_attach_insert($entity_type, $entity);
 }
 
+/**
+ * Implementation of hook_query_TAG_alter().
+ * Alter queries tagged with file_load_multiple.
+ */
+function imagefield_focus_query_file_load_multiple_alter(QueryAlterableInterface $query) {
+  $query->fields('iff', array('focus_rect', 'crop_rect'));
+  $query->leftJoin('imagefield_focus_file', 'iff', 'iff.fid = base.fid');
+}
+
+/**
+ * Parse a rectangle from a given string.
+ *
+ * @return
+ *   A rectangle array or FALSE.
+ */
+function imagefield_focus_parse($rect) {
+  $values = explode(',', $rect);
+  if (count($values) != 4) {
+    return FALSE;
+  }
 
+  $keys = array('x', 'y', 'width', 'height');
+  $rect = array();
+  for ($i = 0; $i < 4; $i++) {
+    $value = $values[$i];
+    if (!is_numeric($value)) {
+      return FALSE;
+    }
+    $rect[$keys[$i]] = round($value);
+  }
+
+  if (($rect['x'] < 0) || ($rect['y'] < 0) || ($rect['width'] <= 0) || ($rect['height'] <= 0)) {
+    return FALSE;
+  }
+
+  $rect['xoffset'] = $rect['x'];
+  $rect['yoffset'] = $rect['y'];
+  $rect['anchor'] = $rect['x'] .'-'. $rect['y'];
+
+  return $rect;
+}
+
+/**
+ * Implements hook_media_browser_plugins_alter().
+ */
+function imagefield_focus_media_browser_plugins_alter(&$plugins) {
+  // Include the imagefield_focus js & css files whenever the media browser is
+  // opened focus is enabled in the media browser settings form.
+  $settings = _imagefield_focus_build_js_settings('media', variable_get('imagefield_focus_media_settings', array()));
+
+  if (!empty($settings['imagefield_focus']['media'])) {
+    _imagefield_focus_attach_files($settings, $plugins);
+  }
+}
+
+/**
+ * Return whether ImageField Focus has support for the given widget.
+ */
+function imagefield_focus_widget_support($type) {
+  return function_exists('imagefield_focus_widget_'. $type .'_process');
+}
+
+/**
+ * Checks a string to see if it's a valid rect.
+ *
+ * @param  string $value
+ *
+ * @return bool
+ */
+function imagefield_focus_validate_crop_focus_rect($value) {
+  $value = trim($value);
+  $length = strlen($value);
+
+  $valid = ($length == 0) ||  ($length > 0 && imagefield_focus_parse($value));
+  return $valid;
+}
 
 /**
  * Implementation of hook_file_insert().
@@ -270,18 +519,6 @@ function imagefield_focus_file_delete($file) {
   _imagefield_focus_file_delete($file);
 }
 
-
-
-/**
- * Implementation of hook_query_TAG_alter(); alter queries tagged with file_load_multiple.
- */
-function imagefield_focus_query_file_load_multiple_alter(QueryAlterableInterface $query) {
-  $query->fields('iff', array('focus_rect', 'crop_rect'));
-  $query->leftJoin('imagefield_focus_file', 'iff', 'iff.fid = base.fid');
-}
-
-
-
 /**
  * Save focus data.
  */
@@ -305,8 +542,6 @@ function _imagefield_focus_file_save($file) {
   }
 }
 
-
-
 /**
  * Delete focus data.
  */
@@ -314,46 +549,94 @@ function _imagefield_focus_file_delete($file) {
   return db_delete('imagefield_focus_file')->condition('fid', $file->fid)->execute();
 }
 
-
-
 /**
- * Parse a rectangle from a given string.
- * 
- * @return
- *   A rectangle array or FALSE.
+ * Helper function used to attach the required by javascript and css files.
+ *
+ * @param  array  $settings
+ *   An associative array of js settings to include on the page.
+ *
+ * @param  array $render_array
+ *   A render array to which the js and/or css files should be attached. If no
+ *   value is given then the standard druapl_add_css/js functions are used.
  */
-function imagefield_focus_parse($rect) {
-  $values = explode(',', $rect);
-  if (count($values) != 4) {
-    return FALSE;
-  }
-
-  $keys = array('x', 'y', 'width', 'height');
-  $rect = array();
-  for ($i = 0; $i < 4; $i++) {
-    $value = $values[$i];
-    if (!is_numeric($value)) {
-      return FALSE;
+function _imagefield_focus_attach_files($settings = array(), &$render_array = NULL) {
+  $path = drupal_get_path('module', 'imagefield_focus');
+  if (is_null($render_array)) {
+    drupal_add_css($path . '/imgfocus/jquery.imgfocus.css');
+    drupal_add_css($path . '/imagefield_focus.css');
+    drupal_add_js( $path . '/imgfocus/jquery.imgfocus.min.js');
+    drupal_add_js( $path . '/js/imagefield_focus.js');
+    if (module_exists('media') && (arg(0) == 'media' || arg(0) == 'file')) {
+      drupal_add_js($path . '/js/imagefield_focus.media.js');
+    }
+    if (!empty($settings)) {
+      drupal_add_js($settings, 'setting');
     }
-    $rect[$keys[$i]] = round($value);
   }
-
-  if (($rect['x'] < 0) || ($rect['y'] < 0) || ($rect['width'] <= 0) || ($rect['height'] <= 0)) {
-    return FALSE;
+  else {
+    $render_array['#attached']['css'][] = $path . '/imgfocus/jquery.imgfocus.css';
+    $render_array['#attached']['css'][] = $path . '/imagefield_focus.css';
+    $render_array['#attached']['js'][] = $path . '/imgfocus/jquery.imgfocus.min.js';
+    $render_array['#attached']['js'][] = $path . '/js/imagefield_focus.js';
+    if (module_exists('media') && (arg(0) == 'media' || arg(0) == 'file')) {
+      $render_array['#attached']['js'][] = $path . '/js/imagefield_focus.media.js';
+    }
+    if (!empty($settings)) {
+      $render_array['#attached']['js'][] = array(
+        'data' => $settings,
+        'type' => 'setting',
+      );
+    }
   }
-
-  $rect['xoffset'] = $rect['x'];
-  $rect['yoffset'] = $rect['y'];
-  $rect['anchor'] = $rect['x'] .'-'. $rect['y'];
-
-  return $rect;
 }
 
-
-
 /**
- * Return whether ImageField Focus has support for the given widget.
+ * Helper function to build a javascript settings array. The array returned has
+ * two keys: 'new' and 'settings'. 'New' is true if this is settings array has
+ * not previously been built.
+ *
+ * @param  string $key
+ *   The unique string identifiying this settings array.
+ *
+ * @param  array $focus_settings
+ *   The raw settings array including the following keys: 'focus_lock_ratio' and
+ *   'focus_min_size'.
+ *
+ * @param  boolean $force_return
+ *   If true, the settings array will be returned wether or not it is new. If
+ *   false, it will only be returned if it is new.
+ *
+ * @return array
+ *   If this is a newly added array, the settings array is returned. Otherwise
+ *   an emprt array is returned
  */
-function imagefield_focus_widget_support($type) {
-  return function_exists('imagefield_focus_widget_'. $type .'_process');
+function _imagefield_focus_build_js_settings($key, $focus_settings, $force_return = FALSE) {
+  $settings = &drupal_static(__FUNCTION__);
+
+  if (!isset($settings[$key])) {
+    if (!empty($focus_settings)) {
+      list($w, $h) = explode('x', $focus_settings['focus_min_size']) + array('', '');
+      $settings[$key] = array(
+        'min_width' => intval($w),
+        'min_height' => intval($h),
+        'lock_ratio' => !empty($focus_settings['focus_lock_ratio']),
+      );
+    }
+    else {
+      $settings[$key] = array();
+    }
+  }
+  else {
+    if (!$force_return) {
+      return array();
+    }
+  }
+
+  $return = array(
+    'imagefield_focus' => array(
+      $key => $settings[$key]
+    ),
+  );
+
+  return $return;
 }
diff --git a/js/imagefield_focus.js b/js/imagefield_focus.js
new file mode 100644
index 0000000..6dcab0c
--- /dev/null
+++ b/js/imagefield_focus.js
@@ -0,0 +1,95 @@
+/**
+ * @file
+ * Written by Henri MEDOT <henri.medot[AT]absyx[DOT]fr>
+ * http://www.absyx.fr
+ */
+
+(function($) {
+
+  // Helper functions.
+  var valToSel = function(val) {
+    var sel = val.split(',');
+    if (sel.length != 4) {
+      return null;
+    }
+
+    for (var i = 0; i < 4; i++) {
+      var v = Number(sel[i]);
+      if (isNaN(v)) {
+        return null;
+      }
+      sel[i] = Math.round(v);
+    }
+
+    if ((sel[0] < 0) || (sel[1] < 0) || (sel[2] <= 0) || (sel[3] <= 0)) {
+      return null;
+    }
+
+    return sel;
+  };
+
+  var selToVal = function(sel) {
+    return (sel) ? sel.join(',') : '';
+  };
+  //~Helper functions.
+
+
+
+  // Drupal behavior.
+  Drupal.behaviors.imagefield_focus = {
+    attach: function(context) {
+      $('.imagefield-focus.focus-box', context).once('imagefield_focus', function() {
+        var $this = $(this);
+        var img = $('> img', $this);
+        var inputs = $('input.imagefield-focus', $this.parent());
+        var key = inputs.attr('data-key');
+        var settings = Drupal.settings.imagefield_focus[key];
+
+        var minSize, ratio;
+        if (settings.min_width && settings.min_height) {
+          minSize = [settings.min_width, settings.min_height];
+          ratio = settings.lock_ratio;
+        }
+
+        var options = {
+          boxSize: $this.width() || parseInt($this.css('min-width')),
+          realSize: img.attr('alt').split('x'),
+          minSize: minSize
+        };
+        img.css('display', 'block').imgfocus(options);
+
+        inputs.focus(function(e, init) {
+          img.unbind('change.imagefield-focus');
+          var input = $(this).addClass('active');
+          var other = inputs.eq((inputs.index(input) + 1) % 2).removeClass('active');
+          var otherSel = valToSel(other.val());
+          var options = (input.hasClass('crop-rect')) ? {
+            ratio: 0,
+            minRect: otherSel,
+            maxRect: null
+          } : {
+            ratio: ratio,
+            minRect: null,
+            maxRect: otherSel
+          };
+          img.imgfocus('options', options).imgfocus('selection', valToSel(input.val()));
+          img.bind('change.imagefield-focus', function() {
+            input.val(selToVal(img.imgfocus('selection')));
+          });
+
+          if (init) {
+            e.stopPropagation();
+            e.preventDefault();
+            return false;
+          }
+        }).change(function() {
+          $(this).val(selToVal(img.imgfocus('selection', valToSel($(this).val())).imgfocus('selection')));
+        });
+
+        inputs.eq(0).trigger('focus', [true]);
+      });
+    }
+  };
+  //~Drupal behavior.
+
+})(jQuery);
diff --git a/js/imagefield_focus.media.js b/js/imagefield_focus.media.js
new file mode 100644
index 0000000..f6cb586
--- /dev/null
+++ b/js/imagefield_focus.media.js
@@ -0,0 +1,131 @@
+(function ($) {
+  namespace('Drupal.media.browser');
+
+  // Helper code & functions.
+  // Get querystring as an array split on "&"
+  var querystring = location.search.replace('?', '').split('&');
+  var queryObj = {};
+  // Loop through each name-value pair and populate object
+  for (var i=0; i < querystring.length; i++) {
+    queryObj[querystring[i].split('=')[0]] = querystring[i].split('=')[1];
+  }
+
+  var selToVal = function(sel) {
+    return (sel) ? sel.join(',') : '';
+  };
+
+  var isNumericString = function(val) {
+    return (parseInt(val) + "" == val);
+  }
+  //~Helper functions.
+
+  Drupal.behaviors.imagefieldFocusMediaViews = {
+    attach: function (context, settings) {
+      $('.view-content .media-item').bind('click', Drupal.media.browser.library.prototype.focus);
+    }
+  }
+
+  var render = Drupal.media.browser.library.prototype.render;
+  Drupal.media.browser.library.prototype.render = function(renderElement) {
+    if (this.mediaFiles.length < 1) {
+      $('<div id="media-empty-message" class="media-empty-message"></div>').appendTo(renderElement)
+        .html(this.emptyMessage);
+      return;
+    }
+    else {
+      var mediaList = $('#media-browser-library-list', renderElement);
+      // If the list doesn't exist, bail.
+      if (mediaList.length === 0) {
+        throw('Cannot continue, list element is missing');
+      }
+    }
+
+    while (this.cursor < this.mediaFiles.length) {
+      var mediaFile = this.getNextMedia();
+
+      var data = {};
+      data.obj = this;
+      data.file = mediaFile;
+
+      var listItem = $('<li></li>').appendTo(mediaList)
+        .attr('id', 'media-item-' + mediaFile.fid)
+        .html(mediaFile.preview)
+        .bind('click', data, this.clickFunction)
+        .bind('click', data, this.focus);
+    }
+
+    // If a query string exists, auto select the given file.
+    if (isNumericString(queryObj['imagefield_focus_fid'])) {
+      $('#media-item-' + queryObj['imagefield_focus_fid']).click();
+    }
+
+  }
+
+  Drupal.media.browser.library.prototype.focus = function() {
+    var that = $(this);
+    var fid = $(this).attr('id').split('-').pop() || $(this).closest('a[data-fid]').data('fid');
+
+    var closeCropFocusForm = function() {
+      $('#imagefield-focus-crop-focus-media-form').slideUp(function(){
+        that.closest('#scrollbox').animate({marginRight: '0'}, 250);
+        $(this).remove();
+      });
+    }
+
+    // Display the crop and focus form in the correct location.
+    var gotFocusForm = function (data, status) {
+      if (data.success) {
+        // Close the focus form if it's open.
+        $('#imagefield-focus-crop-focus-media-form').remove();
+
+        // Add the html for the form and adjust some styles.
+        var scrollbox = that.closest('#scrollbox');
+        that.closest('#container').css('position', 'relative');
+        scrollbox.after(data.markup).css({marginRight: '45%'});
+        $('#imagefield-focus-crop-focus-media-form').height(scrollbox.innerHeight()).slideDown();
+
+        // Setup the close button.
+        $('#imagefield-focus-crop-focus-media-form .close-button').click(closeCropFocusForm);
+
+        // Whenever the crop or focus data changes, save the change. This is a
+        // more streamlined workflow when working within the media module.
+        $('#imagefield-focus-crop-focus-media-form .focus-box > img').bind('change.focus-box', function() {
+          var cropRect = $(this).closest('form').find('input[name="crop_rect"]');
+          var focusRect = $(this).closest('form').find('input[name="focus_rect"]');
+          var postData = {
+            'crop_rect' : cropRect.hasClass('active') ? selToVal($(this).imgfocus('selection')) : cropRect.val(),
+            'focus_rect' : focusRect.hasClass('active') ? selToVal($(this).imgfocus('selection')) : focusRect.val()
+          };
+
+          $.ajax({
+            url: '/imagefield_focus/file/' + fid + '/save',
+            data: postData,
+            type: 'POST',
+            error: function() { alert(Drupal.t('The focus and crop settings could not be saved.')); }
+          });
+        });
+      }
+      else {
+        closeCropFocusForm();
+      }
+
+      // Make sure all imagefield_focus behaviors have been instantiated. We
+      // cannot use Drupal.attachBehaviors() because the media module will
+      // incorrectly re-add buttons. @see media.js ~line 64
+      Drupal.behaviors.imagefield_focus.attach();
+    };
+
+    // Display an error if the crop and focus form could not be loaded.
+    var errorCallback = function () {
+      alert(Drupal.t('Error loading the image focus form.'));
+    };
+
+    $.ajax({
+      url: '/imagefield_focus/file/' + fid + '/media', //imagefield_focus/form/%fid/%key
+      type: 'GET',
+      error: errorCallback,
+      success: gotFocusForm
+    });
+  }
+
+}(jQuery));
