From ddb9b0c61d4610a16a58a9b2fbfb728ca78070ab Mon Sep 17 00:00:00 2001
From: Jacob Singh <jacobsingh@gmail.com>
Date: Thu, 24 Mar 2011 16:20:46 +0530
Subject: [PATCH] Issue #1106922: Change filenames and directory structure to match more standard Drupal conventions

Moves all .inc files to "includes"
Renames all files to use dots.between.parts except where it is a.compound_term.extension
Moves classes to includes
renames javascript to js
renames theme to templates
---
 MediaEntityController.php                    |    7 +-
 README.txt                                   |    9 -
 includes/MediaEntityController.php           |  210 ++++++++
 includes/MediaReadOnlyStreamWrapper.inc      |  486 +++++++++++++++++++
 includes/media.admin.inc                     |  661 ++++++++++++++++++++++++++
 includes/media.browser.inc                   |  427 +++++++++++++++++
 includes/media.fields.inc                    |  456 ++++++++++++++++++
 includes/media.filter.inc                    |  426 +++++++++++++++++
 includes/media.pages.inc                     |  432 +++++++++++++++++
 includes/media.theme.inc                     |  324 +++++++++++++
 includes/media.types.inc                     |  323 +++++++++++++
 includes/media.xml.inc                       |   81 ++++
 javascript/media-admin.js                    |  142 ------
 javascript/media-browser.js                  |  100 ----
 javascript/media-format-form.js              |   48 --
 javascript/media.core.js                     |   19 -
 javascript/media.js                          |   75 ---
 javascript/media.popups.js                   |  355 --------------
 javascript/plugins/media.fromurl.js          |   29 --
 javascript/plugins/media.library.css         |   23 -
 javascript/plugins/media.library.js          |  184 -------
 javascript/plugins/media.upload.js           |   30 --
 javascript/plugins/media.upload_multiple.js  |   13 -
 javascript/util/ba-debug.min.js              |   12 -
 javascript/util/json2.js                     |  481 -------------------
 javascript/wysiwyg-media.js                  |  365 --------------
 js/media.admin.js                            |  142 ++++++
 js/media.browser.js                          |  100 ++++
 js/media.core.js                             |   19 +
 js/media.format_form.js                      |   48 ++
 js/media.js                                  |   75 +++
 js/media.popups.js                           |  355 ++++++++++++++
 js/plugins/media.fromurl.js                  |   29 ++
 js/plugins/media.library.css                 |   23 +
 js/plugins/media.library.js                  |  184 +++++++
 js/plugins/media.upload.js                   |   30 ++
 js/plugins/media.upload_multiple.js          |   13 +
 js/util/ba-debug.min.js                      |   12 +
 js/util/json2.js                             |  481 +++++++++++++++++++
 js/wysiwyg-media.js                          |  365 ++++++++++++++
 media.admin.inc                              |  661 --------------------------
 media.api.php                                |    2 +-
 media.browser.inc                            |  427 -----------------
 media.fields.inc                             |  456 ------------------
 media.filter.inc                             |  425 -----------------
 media.info                                   |    8 +-
 media.install                                |    7 +
 media.module                                 |   76 ++--
 media.pages.inc                              |  432 -----------------
 media.theme.inc                              |  324 -------------
 media.types.inc                              |  323 -------------
 media.xml.inc                                |   81 ----
 modules/media_internet/media_internet.module |    2 +-
 templates/media-dialog-page.tpl.php          |   73 +++
 theme/media-dialog-page.tpl.php              |   73 ---
 wysiwyg_plugins/media.inc                    |    4 +-
 56 files changed, 5834 insertions(+), 5134 deletions(-)
 create mode 100644 includes/MediaEntityController.php
 create mode 100644 includes/MediaReadOnlyStreamWrapper.inc
 create mode 100644 includes/media.admin.inc
 create mode 100644 includes/media.browser.inc
 create mode 100644 includes/media.fields.inc
 create mode 100644 includes/media.filter.inc
 create mode 100644 includes/media.pages.inc
 create mode 100644 includes/media.theme.inc
 create mode 100644 includes/media.types.inc
 create mode 100644 includes/media.xml.inc
 delete mode 100644 javascript/media-admin.js
 delete mode 100644 javascript/media-browser.js
 delete mode 100644 javascript/media-format-form.js
 delete mode 100644 javascript/media.core.js
 delete mode 100644 javascript/media.js
 delete mode 100644 javascript/media.popups.js
 delete mode 100644 javascript/plugins/media.fromurl.js
 delete mode 100644 javascript/plugins/media.library.css
 delete mode 100644 javascript/plugins/media.library.js
 delete mode 100644 javascript/plugins/media.upload.js
 delete mode 100755 javascript/plugins/media.upload_multiple.js
 delete mode 100644 javascript/util/ba-debug.min.js
 delete mode 100644 javascript/util/json2.js
 delete mode 100644 javascript/wysiwyg-media.js
 create mode 100644 js/media.admin.js
 create mode 100644 js/media.browser.js
 create mode 100644 js/media.core.js
 create mode 100644 js/media.format_form.js
 create mode 100644 js/media.js
 create mode 100644 js/media.popups.js
 create mode 100644 js/plugins/media.fromurl.js
 create mode 100644 js/plugins/media.library.css
 create mode 100644 js/plugins/media.library.js
 create mode 100644 js/plugins/media.upload.js
 create mode 100755 js/plugins/media.upload_multiple.js
 create mode 100644 js/util/ba-debug.min.js
 create mode 100644 js/util/json2.js
 create mode 100644 js/wysiwyg-media.js
 delete mode 100644 media.admin.inc
 delete mode 100644 media.browser.inc
 delete mode 100644 media.fields.inc
 delete mode 100644 media.filter.inc
 delete mode 100644 media.pages.inc
 delete mode 100644 media.theme.inc
 delete mode 100644 media.types.inc
 delete mode 100644 media.xml.inc
 create mode 100644 templates/media-dialog-page.tpl.php
 delete mode 100644 theme/media-dialog-page.tpl.php

diff --git a/MediaEntityController.php b/MediaEntityController.php
index 6718a1a..7a0e917 100644
--- a/MediaEntityController.php
+++ b/MediaEntityController.php
@@ -1,5 +1,10 @@
 <?php
-
+/**
+ *  DO NOT EDIT THIS. IT WILL BE REMOVED IN THE NEXT RELEASE.
+ *   This file is only here so that the class autoloader doesn't barf.
+ *
+ * It has been moved to includes
+ */
 /**
  * Extends DrupalEntityControllerInterface.
  *
diff --git a/README.txt b/README.txt
index 9d2fa34..6875a07 100644
--- a/README.txt
+++ b/README.txt
@@ -5,12 +5,3 @@
  */
 
 See http://drupal.org/node/356802
-<<<<<<< HEAD
-
-PLEASE DO NOT USE THIS BRANCH YET!!!
-
-For now, either use the branch of Media corresponding to the Alpha branch of
-Drupal you are using, or if you are working against Drupal HEAD, then use the
-HEAD branch of Media development.
-=======
->>>>>>> master
diff --git a/includes/MediaEntityController.php b/includes/MediaEntityController.php
new file mode 100644
index 0000000..6718a1a
--- /dev/null
+++ b/includes/MediaEntityController.php
@@ -0,0 +1,210 @@
+<?php
+
+/**
+ * Extends DrupalEntityControllerInterface.
+ *
+ * @see: http://drupal.org/project/entity_api (perhaps we should be using this)
+ */
+class MediaEntityController extends DrupalDefaultEntityController {
+
+  public function load($ids = array(), $conditions = array()) {
+    $this->ids = $ids;
+    $this->conditions = $conditions;
+
+    $entities = array();
+
+    // Revisions are not statically cached, and require a different query to
+    // other conditions, so separate the revision id into its own variable.
+    if ($this->revisionKey && isset($this->conditions[$this->revisionKey])) {
+      $this->revisionId = $this->conditions[$this->revisionKey];
+      unset($this->conditions[$this->revisionKey]);
+    }
+    else {
+      $this->revisionId = FALSE;
+    }
+
+    // Create a new variable which is either a prepared version of the $ids
+    // array for later comparison with the entity cache, or FALSE if no $ids
+    // were passed. The $ids array is reduced as items are loaded from cache,
+    // and we need to know if it's empty for this reason to avoid querying the
+    // database when all requested entities are loaded from cache.
+    $passed_ids = !empty($this->ids) ? array_flip($this->ids) : FALSE;
+    // Try to load entities from the static cache, if the entity type supports
+    // static caching.
+    if ($this->cache) {
+      $entities += $this->cacheGet($this->ids, $this->conditions);
+      // If any entities were loaded, remove them from the ids still to load.
+      if ($passed_ids) {
+        $this->ids = array_keys(array_diff_key($passed_ids, $entities));
+      }
+    }
+
+    // Load any remaining entities from the database. This is the case if $ids
+    // is set to FALSE (so we load all entities), if there are any ids left to
+    // load, if loading a revision, or if $conditions was passed without $ids.
+    if ($this->ids === FALSE || $this->ids || $this->revisionId || ($this->conditions && !$passed_ids)) {
+      // Build the query.
+      $this->buildQuery();
+      $queried_entities = $this->query
+        ->execute()
+        ->fetchAllAssoc($this->idKey);
+    }
+
+    // Pass all entities loaded from the database through $this->attachLoad(),
+    // which attaches fields (if supported by the entity type) and calls the
+    // entity type specific load callback, for example hook_node_load().
+    if (!empty($queried_entities)) {
+      $this->attachLoad($queried_entities);
+      $entities += $queried_entities;
+    }
+
+    if ($this->cache) {
+      // Add entities to the cache if we are not loading a revision.
+      if (!empty($queried_entities) && !$this->revisionId) {
+        $this->cacheSet($queried_entities);
+      }
+    }
+
+    // Ensure that the returned array is ordered the same as the original
+    // $ids array if this was passed in and remove any invalid ids.
+    if ($passed_ids) {
+      // Remove any invalid ids from the array.
+      $passed_ids = array_intersect_key($passed_ids, $entities);
+      foreach ($entities as $entity) {
+        $passed_ids[$entity->{$this->idKey}] = $entity;
+      }
+      $entities = $passed_ids;
+    }
+
+    return $entities;
+  }
+  protected function buildQuery() {
+    $this->query = db_select($this->entityInfo['base table'], 'base');
+
+    $this->query->addTag($this->entityType . '_load_multiple');
+
+    if ($this->revisionId) {
+      $this->query->join($this->revisionTable, 'revision', "revision.{$this->idKey} = base.{$this->idKey} AND revision.{$this->revisionKey} = :revisionId", array(':revisionId' => $this->revisionId));
+    }
+    elseif ($this->revisionKey) {
+      $this->query->join($this->revisionTable, 'revision', "revision.{$this->revisionKey} = base.{$this->revisionKey}");
+    }
+
+    // Add fields from the {entity} table.
+    $entity_fields = drupal_schema_fields_sql($this->entityInfo['base table']);
+
+    if ($this->revisionKey) {
+      // Add all fields from the {entity_revision} table.
+      $entity_revision_fields = drupal_map_assoc(drupal_schema_fields_sql($this->revisionTable));
+      // The id field is provided by entity, so remove it.
+      unset($entity_revision_fields[$this->idKey]);
+
+      // Change timestamp to revision_timestamp, and revision uid to
+      // revision_uid before adding them to the query.
+      // TODO: This is node specific and has to be moved into NodeController.
+      unset($entity_revision_fields['timestamp']);
+      $this->query->addField('revision', 'timestamp', 'revision_timestamp');
+      unset($entity_revision_fields['uid']);
+      $this->query->addField('revision', 'uid', 'revision_uid');
+
+      // Remove all fields from the base table that are also fields by the same
+      // name in the revision table.
+      $entity_field_keys = array_flip($entity_fields);
+      foreach ($entity_revision_fields as $key => $name) {
+        if (isset($entity_field_keys[$name])) {
+          unset($entity_fields[$entity_field_keys[$name]]);
+        }
+      }
+      $this->query->fields('revision', $entity_revision_fields);
+    }
+
+    $this->query->fields('base', $entity_fields);
+
+    if ($this->ids) {
+      $this->query->condition("base.{$this->idKey}", $this->ids, 'IN');
+    }
+    if ($this->conditions) {
+      foreach ($this->conditions as $field => $value) {
+        $this->query->condition('base.' . $field, $value);
+      }
+    }
+  }
+
+  /**
+   * Attach data to entities upon loading.
+   *
+   * This will attach fields, if the entity is fieldable. It calls
+   * hook_entity_load() for modules which need to add data to all entities.
+   * It also calls hook_TYPE_load() on the loaded entities. For example
+   * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
+   * expects special parameters apart from the queried entities, you can set
+   * $this->hookLoadArguments prior to calling the method.
+   * See NodeController::attachLoad() for an example.
+   */
+  protected function attachLoad(&$queried_entities) {
+    // Attach fields.
+    if ($this->entityInfo['fieldable']) {
+      if ($this->revisionId) {
+        field_attach_load_revision($this->entityType, $queried_entities);
+      }
+      else {
+        field_attach_load($this->entityType, $queried_entities);
+      }
+    }
+
+    // Call hook_entity_load().
+    foreach (module_implements('entity_load') as $module) {
+      $function = $module . '_entity_load';
+      $function($queried_entities, $this->entityType);
+    }
+    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
+    // always the queried entities, followed by additional arguments set in
+    // $this->hookLoadArguments.
+    $args = array_merge(array($queried_entities), $this->hookLoadArguments);
+    foreach (module_implements($this->entityInfo['load hook']) as $module) {
+      call_user_func_array($module . '_' . $this->entityInfo['load hook'], $args);
+    }
+  }
+
+  /**
+   * Get entities from the static cache.
+   *
+   * @param $ids
+   *   If not empty, return entities that match these IDs.
+   * @param $conditions
+   *   If set, return entities that match all of these conditions.
+   */
+  protected function cacheGet($ids, $conditions = array()) {
+    $entities = array();
+    // Load any available entities from the internal cache.
+    if (!empty($this->entityCache) && !$this->revisionId) {
+      if ($ids) {
+        $entities += array_intersect_key($this->entityCache, array_flip($ids));
+      }
+      // If loading entities only by conditions, fetch all available entities
+      // from the cache. Entities which don't match are removed later.
+      elseif ($conditions) {
+        $entities = $this->entityCache;
+      }
+    }
+
+    // Exclude any entities loaded from cache if they don't match $conditions.
+    // This ensures the same behavior whether loading from memory or database.
+    if ($conditions) {
+      foreach ($entities as $entity) {
+        $entity_values = (array) $entity;
+        if (array_diff_assoc($conditions, $entity_values)) {
+          unset($entities[$entity->{$this->idKey}]);
+        }
+      }
+    }
+    return $entities;
+  }
+
+  /**
+   * Store entities in the static entity cache.
+   */
+  protected function cacheSet($entities) {
+    $this->entityCache += $entities;
+  }
+}
diff --git a/includes/MediaReadOnlyStreamWrapper.inc b/includes/MediaReadOnlyStreamWrapper.inc
new file mode 100644
index 0000000..e771c14
--- /dev/null
+++ b/includes/MediaReadOnlyStreamWrapper.inc
@@ -0,0 +1,486 @@
+<?php
+
+/**
+ * A base class for Resource Stream Wrappers.
+ *
+ * This class provides a complete stream wrapper implementation. It passes
+ * incoming  URL's through an interpolation method then recursively calls
+ * the invoking PHP filesystem function.
+ *
+ * MediaReadOnlyStreamWrapper implementations need to override at least the
+ * interpolateUrl method to rewrite the URL before is it passed back into the
+ * calling function.
+ */
+abstract class MediaReadOnlyStreamWrapper implements DrupalStreamWrapperInterface {
+  protected $parameters = array();
+  protected $base_url = NULL;
+  private $_DEBUG_MODE = NULL;
+
+  public function get_parameters() {
+    return $this->parameters;
+  }
+
+  /**
+   * "Template" for stat calls. All elements must be initialized.
+   * @var array
+   */
+  protected $_stat = array(
+    0 => 0, // device number
+    'dev' => 0,
+    1 => 0, // inode number
+    'ino' => 0,
+    2 => 0, // inode protection mode
+    'mode' => 0,
+    3 => 0, // number of links
+    'nlink' => 0,
+    4 => 0, // userid of owner
+    'uid' => 0,
+    5 => 0, // groupid of owner
+    'gid' => 0,
+    6 => -1, // device type, if inode device *
+    'rdev' => -1,
+    7 => 0, // size in bytes
+    'size' => 0,
+    8 => 0, // time of last access (Unix timestamp)
+    'atime' => 0,
+    9 => 0, // time of last modification (Unix timestamp)
+    'mtime' => 0,
+    10 => 0, // time of last inode change (Unix timestamp)
+    'ctime' => 0,
+    11 => -1, // blocksize of filesystem IO
+    'blksize' => -1,
+    12 => -1, // number of blocks allocated
+    'blocks' => -1,
+  );
+
+  function interpolateUrl() {
+    if ($parameters = $this->get_parameters()) {
+      return $this->base_url .'?'. http_build_query($parameters);
+    }
+  }
+
+  /**
+   * Returns a web accessible URL for the resource.
+   *
+   * This function should return a URL that can be embedded in a web page
+   * and accessed from a browser. For example, the external URL of
+   * "youtube://xIpLd0WQKCY" might be
+   * "http://www.youtube.com/watch?v=xIpLd0WQKCY".
+   *
+   * @return
+   *   Returns a string containing a web accessible URL for the resource.
+   */
+  public function getExternalUrl() {
+    return $this->interpolateUrl();
+  }
+
+  /**
+   * Base implementation of getMimeType().
+   */
+  static function getMimeType($uri, $mapping = NULL) {
+    return 'application/octet-stream';
+  }
+
+  /**
+   * Base implementation of realpath().
+   */
+  function realpath() {
+    return $this->getExternalUrl();
+  }
+
+   /**
+   * Stream context resource.
+   *
+   * @var Resource
+   */
+  public $context;
+
+  /**
+   * A generic resource handle.
+   *
+   * @var Resource
+   */
+  public $handle = NULL;
+
+  /**
+   * Instance URI (stream).
+   *
+   * A stream is referenced as "scheme://target".
+   *
+   * @var String
+   */
+  protected $uri;
+
+  /**
+   * Base implementation of setUri().
+   */
+  function setUri($uri) {
+    $this->uri = $uri;
+    $this->parameters = $this->_parse_url($uri);
+  }
+
+  /**
+   * Base implementation of getUri().
+   */
+  function getUri() {
+    return $this->uri;
+  }
+
+  /**
+   *  Report an error.
+   *  @param $message
+   *    The untranslated string to report.
+   *  @param $options
+   *    An optional array of options to send to t().
+   *  @param $display
+   *    If TRUE, then we display the error to the user.
+   *  @return
+   *    We return FALSE, since we sometimes pass that back from the reporting
+   *    function.
+   */
+  private function _report_error($message, $options = array(), $display = FALSE) {
+    watchdog('resource', $message, $options, WATCHDOG_ERROR);
+    if ($display) {
+      drupal_set_message(t($message, $options), 'error');
+    }
+    return FALSE;
+  }
+
+  private function _debug($message, $type = 'status') {
+    if ($this->_DEBUG_MODE) {
+      drupal_set_message($message, $type);
+    }
+  }
+
+  /**
+   *  Returns an array of any parameters stored in the URL's path.
+   *  @param $url
+   *    The URL to parse, such as youtube://v/[video-code]/t/[tags+more-tags].
+   *  @return
+   *    An associative array of all the parameters in the path,
+   *    or FALSE if the $url is ill-formed.
+   */
+  protected function _parse_url($url) {
+    $path = explode('://', $url);
+    $parts = explode('/',  $path[1]);
+    $params = array();
+    $count = 0;
+    $total = count($parts);
+    if (!$total || ($total % 2)) {
+      // If we have no parts, or an odd number of parts, it's malformed.
+      return FALSE;
+    }
+    while ($count < $total) {
+      // We iterate count for each step of the assignment to keep us honest.
+      $params[$parts[$count++]] = $parts[$count++];
+    }
+    return $params;
+  }
+
+  /**
+   * Support for fopen(), file_get_contents(), file_put_contents() etc.
+   *
+   * @param $path
+   *   A string containing the path to the file to open.
+   * @param $mode
+   *   The file mode ("r", "wb" etc.).
+   * @param $options
+   *   A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
+   * @param &$opened_path
+   *   A string containing the path actually opened.
+   * @return
+   *  TRUE if file was opened successfully.
+   */
+  public function stream_open($url, $mode, $options, &$opened_url) {
+    $this->_debug(t('Stream open: %url', array('%url' => $url)));
+
+    // We only handle Read-Only mode by default.
+    if ($mode != 'r' && $mode != 'rb') {
+      return $this->_report_error('Attempted to open %url as mode: %mode.', array('%url' => $url, '%mode' => $mode), ($options & STREAM_REPORT_ERRORS));
+    }
+
+    // We parse a URL as youtube://v/dsyiufo34/t/cats+dogs to store
+    // the relevant code(s) in our private array of parameters.
+    $this->parameters = $this->_parse_url($url);
+
+    if ($this->parameters === FALSE) {
+      return $this->_report_error('Attempted to parse an ill-formed url: %url.', array('%url' => $url), ($options & STREAM_REPORT_ERRORS));
+    }
+
+    if ((bool)$this->parameters && ($options & STREAM_USE_PATH)) {
+      $opened_url = $url;
+    }
+
+    $this->_debug(t('Stream opened: %parameters', array('%parameters' => print_r($this->parameters, TRUE))));
+
+    return (bool)$this->parameters;
+  }
+
+  // Undocumented PHP stream wrapper method.
+  function stream_lock($operation) {
+    return FALSE;
+  }
+
+  /**
+   * Support for fread(), file_get_contents() etc.
+   *
+   * @param $count
+   *    Maximum number of bytes to be read.
+   * @return
+   *  The string that was read, or FALSE in case of an error.
+   */
+  public function stream_read($count) {
+    return FALSE;
+  }
+
+  /**
+   * Support for fwrite(), file_put_contents() etc.
+   *
+   * @param $data
+   *   The string to be written.
+   * @return
+   *   The number of bytes written.
+   */
+  public function stream_write($data) {
+    return FALSE;
+  }
+
+  /**
+   * Support for feof().
+   *
+   * @return
+   *   TRUE if end-of-file has been reached.
+   */
+  public function stream_eof() {
+    return FALSE;
+  }
+
+  /**
+   * Support for fseek().
+   *
+   * @param $offset
+   *   The byte offset to got to.
+   * @param $whence
+   *   SEEK_SET, SEEK_CUR, or SEEK_END.
+   * @return
+   *   TRUE on success
+   */
+  public function stream_seek($offset, $whence) {
+    return FALSE;
+  }
+
+  /**
+   * Support for fflush().
+   *
+   * @return
+   *   TRUE if data was successfully stored (or there was no data to store).
+   */
+  public function stream_flush() {
+    return FALSE;
+  }
+
+  /**
+   * Support for ftell().
+   *
+   * @return
+   *   The current offset in bytes from the beginning of file.
+   */
+  public function stream_tell() {
+    return FALSE;
+  }
+
+  /**
+   * Support for fstat().
+   *
+   * @return
+   *   An array with file status, or FALSE in case of an error - see fstat()
+   *   for a description of this array.
+   */
+  public function stream_stat() {
+    return $this->_stat;
+  }
+
+  /**
+   * Support for fclose().
+   *
+   * @return
+   *   TRUE if stream was successfully closed.
+   */
+  public function stream_close() {
+    return TRUE;
+  }
+
+
+  /**
+   * Support for unlink().
+   *
+   * @param $uri
+   *   A string containing the uri to the resource to delete.
+   * @return
+   *   TRUE if resource was successfully deleted.
+   * @see http://php.net/manual/en/streamwrapper.unlink.php
+   */
+//   public function unlink($uri) {
+//     $this->uri = $uri;
+//     return unlink($this->getLocalPath());
+//   }
+
+  /**
+   * Support for rename().
+   *
+   * @param $from_uri,
+   *   The uri to the file to rename.
+   * @param $to_uri
+   *   The new uri for file.
+   * @return
+   *   TRUE if file was successfully renamed.
+   * @see http://php.net/manual/en/streamwrapper.rename.php
+   */
+//   public function rename($from_uri, $to_uri) {
+//     return rename($this->getLocalPath($from_uri), $this->getLocalPath($to_uri));
+//   }
+
+  /**
+   * Support for mkdir().
+   *
+   * @param $uri
+   *   A string containing the URI to the directory to create.
+   * @param $mode
+   *   Permission flags - see mkdir().
+   * @param $options
+   *   A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
+   * @return
+   *   TRUE if directory was successfully created.
+   * @see http://php.net/manual/en/streamwrapper.mkdir.php
+   */
+//   public function mkdir($uri, $mode, $options) {
+//     $this->uri = $uri;
+//     $recursive = (bool)($options & STREAM_MKDIR_RECURSIVE);
+//     if ($recursive) {
+//       // $this->getLocalPath() fails if $uri has multiple levels of directories
+//       // that do not yet exist.
+//       $localpath = $this->getDirectoryPath() . '/' . file_uri_target($uri);
+//     }
+//     else {
+//       $localpath = $this->getLocalPath($uri);
+//     }
+//     if ($options & STREAM_REPORT_ERRORS) {
+//       return mkdir($localpath, $mode, $recursive);
+//     }
+//     else {
+//       return @mkdir($localpath, $mode, $recursive);
+//     }
+//   }
+
+  /**
+   * Support for rmdir().
+   *
+   * @param $uri
+   *   A string containing the URI to the directory to delete.
+   * @param $options
+   *   A bit mask of STREAM_REPORT_ERRORS.
+   * @return
+   *   TRUE if directory was successfully removed.
+   * @see http://php.net/manual/en/streamwrapper.rmdir.php
+   */
+//   public function rmdir($uri, $options) {
+//     $this->uri = $uri;
+//     if ($options & STREAM_REPORT_ERRORS) {
+//       return rmdir($this->getLocalPath());
+//     }
+//     else {
+//       return @rmdir($this->getLocalPath());
+//     }
+//   }
+
+  /**
+   * Support for stat().
+   *
+   * @param $url
+   *   A string containing the url to get information about.
+   * @param $flags
+   *   A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
+   * @return
+   *   An array with file status, or FALSE in case of an error - see fstat()
+   *   for a description of this array.
+   */
+  public function url_stat($url, $flags) {
+    return $this->stream_stat();
+  }
+
+  /**
+   * Support for opendir().
+   *
+   * @param $url
+   *   A string containing the url to the directory to open.
+   * @param $options
+   *   Unknown (parameter is not documented in PHP Manual).
+   * @return
+   *   TRUE on success.
+   */
+  public function dir_opendir($url, $options) {
+    return FALSE;
+  }
+
+  /**
+   * Support for readdir().
+   *
+   * @return
+   *   The next filename, or FALSE if there are no more files in the directory.
+   */
+  public function dir_readdir() {
+    return FALSE;
+  }
+
+  /**
+   * Support for rewinddir().
+   *
+   * @return
+   *   TRUE on success.
+   */
+  public function dir_rewinddir() {
+    return FALSE;
+  }
+
+  /**
+   * Support for closedir().
+   *
+   * @return
+   *   TRUE on success.
+   */
+  public function dir_closedir() {
+    return FALSE;
+  }
+
+  public function getDirectoryPath() {
+    return '';
+  }
+
+  /**
+   * DrupalStreamWrapperInterface requires that these methods be implemented,
+   * but none of them apply to a read-only stream wrapper. On failure they
+   * are expected to return FALSE.
+   */
+
+  public function unlink($uri) {
+    return FALSE;
+  }
+  public function rename($from_uri, $to_uri) {
+    return FALSE;
+  }
+  public function mkdir($uri, $mode, $options) {
+    return FALSE;
+  }
+  public function rmdir($uri, $options) {
+    return FALSE;
+  }
+  public function chmod($mode) {
+    return FALSE;
+  }
+  public function dirname($uri = NULL) {
+    return FALSE;
+  }
+
+}
+
+
diff --git a/includes/media.admin.inc b/includes/media.admin.inc
new file mode 100644
index 0000000..89bde2b
--- /dev/null
+++ b/includes/media.admin.inc
@@ -0,0 +1,661 @@
+<?php
+
+/**
+ * @file
+ * This file contains the admin functions for the Media module.
+ */
+
+/**
+ * Include media.pages.inc since it has some form definitions we will use.
+ */
+
+require_once dirname(__FILE__) . '/media.pages.inc';
+
+/**
+ * Display the list or thumbnails media admin display.
+ */
+function media_admin($form, $form_state) {
+  global $user;
+  
+  $path = drupal_get_path('module', 'media');
+
+  $form['#attached'] = array(
+    'js' => array($path . '/js/media.admin.js'),
+    'css' => array($path . '/css/media.css'),
+  );
+
+  if (isset($form_state['values']['operation']) && $form_state['values']['operation'] == 'delete') {
+    $form['#attributes']['class'][] = "media-list-operation";
+    return media_multiple_delete_confirm($form, $form_state, array_filter($form_state['values']['files']), 'admin/content/media', 'admin/content/media');
+  }
+
+  require_once dirname(__FILE__) . '/media.browser.inc';
+  media_attach_browser_js($form);
+  
+  $types = media_display_types();
+
+  if (arg(3)) {
+    $display = arg(3);
+    if (!in_array($display, array_keys($types))) {
+      exit(drupal_not_found());
+    }
+    // Save their preference.
+    db_merge('media_list_type')
+      ->key(array('uid' => $user->uid))
+      ->fields(array(
+          'type' => $display,
+      ))
+      ->execute();
+  }
+  else {
+    $display = db_query("SELECT type FROM {media_list_type} WHERE uid = :uid", array(':uid' => $user->uid))->fetch();
+    if (!$display) {
+      $display = 'list';
+    }
+    else {
+      $display = $display->type;
+    }
+  }
+
+  // Build the display switch.
+  $form['switch'] = media_admin_display_switch(array('active display' => $display));
+
+  // Build the 'Media operations' form.
+  $form['options'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Operations'),
+    '#prefix' => '<div class="container-inline">',
+    '#suffix' => '</div>',
+  );
+  $options = array();
+  foreach (module_invoke_all('media_operations') as $operation => $array) {
+    $options[$operation] = $array['label'];
+  }
+  $form['options']['operation'] = array(
+    '#type' => 'select',
+    '#options' => $options,
+    '#default_value' => 'delete',
+  );
+  $options = array();
+  $form['options']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Submit'),
+    '#submit' => array('media_admin_submit'),
+    '#validate' => array('media_admin_validate'),
+  );
+
+  include_once $types[$display]['file'];
+
+  $form['admin'] = $types[$display]['callback']();
+
+  return $form;
+}
+
+
+/**
+ * Form builder: Builds the media list administration overview.
+ */
+function media_admin_list() {
+  // @todo Change to media_variable_get('admin_pager_limit') for consistency
+  //   with browser_pager_limit?
+  $limit = variable_get('media_admin_limit', 50);
+
+  // Build the sortable table header.
+  $header = array(
+   'title' => array('data' => t('Title'), 'field' => 'f.filename'),
+   'type' => array('data' => t('Type'), 'field' => 'f.filemime'),
+   'size' => array('data' => t('Size'), 'field' => 'f.filesize'),
+   'author' => array('data' => t('Author'), 'field' => 'u.name'),
+   'timestamp' => array('data' => t('Updated'), 'field' => 'f.timestamp', 'sort' => 'asc'),
+   'operations' => array('data' => t('Operations')),
+  );
+
+  $query = db_select('file_managed', 'f')->extend('PagerDefault')->extend('TableSort');
+  $query->join('users', 'u', 'f.uid = u.uid');
+
+  $result = $query
+   ->fields('f')
+   ->fields('u', array('name'))
+   ->condition('f.status', FILE_STATUS_PERMANENT)
+   ->limit($limit)
+   ->orderByHeader($header)
+   ->execute();
+
+  $destination = drupal_get_destination();
+  $files = array();
+  $options = array();
+
+  foreach ($result as $file) {
+    $options[$file->fid] = array(
+      'title' => theme('media_link', array('file' => $file)),
+      'type' =>  check_plain($file->filemime),
+      'size' => format_size($file->filesize),
+      'author' => theme('username', array('account' => $file)),
+      'timestamp' => format_date($file->timestamp, 'short'),
+    );
+    $options[$file->fid]['operations'] = l(t('edit'), 'media/' . $file->fid . '/edit', array('query' => $destination));
+  }
+
+  $form['files'] = array(
+   '#type' => 'tableselect',
+   '#header' => $header,
+   '#options' => $options,
+   '#empty' => t('No media available.'),
+   '#attributes' => array('class' => array('media-display-table', 'media-clear')),
+  );
+  $form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
+
+  return $form;
+}
+
+/**
+ * Form builder: Builds the media thumbnails administration overview.
+ */
+function media_admin_thumbnails() {
+  // @todo Change to media_variable_get('admin_pager_limit') for consistency
+  //   with browser_pager_limit?
+  $limit = variable_get('media_admin_limit', 50);
+
+  $query = new EntityFieldQuery();
+
+  $query->pager($limit);
+  $query->entityCondition('entity_type', 'media');
+  $query->propertyCondition('status', FILE_STATUS_PERMANENT);
+
+  $result = $query->execute();
+  $ids = key_exists('media', $result) ? array_keys($result['media']) : FALSE;
+  if ($ids) {
+    $media_entities = entity_load('media', $ids);
+  }
+  else {
+    $media_entities = array();
+  }
+
+  $destination = drupal_get_destination();
+  $rows = array();
+  $options = array();
+  $form['files'] = array(
+   '#tree' => TRUE,
+   '#prefix' => '<div class="media-display-thumbnails media-clear clearfix"><ul class="media-list-thumbnails">',
+   '#suffix' => '</ul></div>',
+  );
+
+  if (count($media_entities)) {
+    foreach ($media_entities as $media) {
+     $preview = media_get_thumbnail_preview($media, TRUE);
+     $form['files'][$media->fid] = array(
+       '#type' => 'checkbox',
+       '#title' => '',
+       '#prefix' => '<li>' . drupal_render($preview),
+       '#suffix' => '</li>',
+     );
+    }
+  }
+
+  $form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
+
+  return $form;
+}
+
+/**
+ * Build the display switch portion of the file listings form.
+ */
+function media_admin_display_switch($options = array()) {
+  $options += array(
+    'form location' => 'admin/content/media',
+    'active display' => 'list',
+  );
+  $display_types = media_display_types();
+
+  // Build the item list.
+  $display_items = array();
+  foreach ($display_types as $delta => $item) {
+    $attributes = array('title' => $item['description']);
+
+    // Set a seperate icon for the active item.
+    if ($delta == $options['active display']) {
+      $icon = $item['icon_active'];
+      $attributes['class'][] = 'active';
+    }
+    else {
+      $icon = $item['icon'];
+    }
+
+    $display_items[] = array(
+      'data' => l(theme('image', array('path' => $icon, 'alt' => $item['title'])),
+        $options['form location'] . '/' . $delta,
+        array(
+          'html' => TRUE,
+          'attributes' => $attributes,
+        )),
+    );
+  }
+
+  return array(
+    '#type' => 'markup',
+    '#markup' => theme('item_list', array(
+       'items' => $display_items,
+       'attributes' => array('class' => 'media-display-switch'),
+      )
+    ),
+  );
+}
+
+/**
+ * Validate media_admin_list form submissions.
+ *
+ * Check if any files have been selected to perform the chosen
+ * 'Update option' on.
+ */
+function media_admin_validate($form, &$form_state) {
+  $files = array_filter($form_state['values']['files']);
+  if (count($files) == 0) {
+    form_set_error('', t('No items selected.'));
+  }
+}
+
+/**
+ * Process media_admin_list form submissions.
+ *
+ * Execute the chosen 'Update option' on the selected files.
+ */
+function media_admin_submit($form, &$form_state) {
+  $operations = module_invoke_all('media_operations');
+  $operation = $operations[$form_state['values']['operation']];
+  // Filter out unchecked nodes
+  $files = array_filter($form_state['values']['files']);
+  if ($function = $operation['callback']) {
+    // Add in callback arguments if present.
+    if (isset($operation['callback arguments'])) {
+      $args = array_merge(array($files), $operation['callback arguments']);
+    }
+    else {
+      $args = array($files);
+    }
+    call_user_func_array($function, $args);
+    cache_clear_all();
+  }
+  elseif (!empty($operation['redirect'])) {
+    $fids = implode(' ', array_keys(array_filter($form_state['values']['files'])));
+    $form_state['redirect'] = array(str_replace('%fids', $fids, $operation['redirect']), array('query' => array('destination' => 'admin/content/media')));
+  }
+  else {
+    // We need to rebuild the form to go to a second step. For example, to
+    // show the confirmation form for the deletion of nodes.
+    $form_state['rebuild'] = TRUE;
+  }
+}
+
+/**
+ * Display all media types.
+ */
+function media_admin_type_list() {
+  $types = media_type_get_types();
+  $field_ui = module_exists('field_ui');
+  $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => $field_ui ? '4' : '2'));
+  $rows = array();
+
+  foreach ($types as $key => $info) {
+    $type = $types[$key];
+    $type_url_str = str_replace('_', '-', $key);
+    $row = array();
+    $label = $info->label;
+
+    // Set the name column.
+    $row[] = array('data' => t($info->label));
+
+    // Set the edit column.
+    $row[] = array('data' => l(t('settings'), 'admin/config/media/types/manage/' . $type_url_str));
+
+    if ($field_ui) {
+      // Manage fields.
+      $row[] = array('data' => l(t('manage fields'), 'admin/config/media/types/manage/' . $type_url_str . '/fields'));
+
+      // Display fields.
+      $row[] = array('data' => l(t('manage display'), 'admin/config/media/types/manage/' . $type_url_str . '/display'));
+    }
+
+    // Set the delete column.
+    if (empty($type->base)) {
+      $row[] = array('data' => l(t('delete'), 'admin/config/media/types/manage/' . $type_url_str . '/delete'));
+    }
+    else {
+      $row[] = array('data' => '');
+    }
+
+    $rows[] = $row;
+
+  }
+
+  $build['media_type_table'] = array(
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => $rows
+  );
+
+  return $build;
+}
+
+/**
+ *  The administration form for managing media types.
+ */
+function media_admin_type_manage_form($form, &$form_state, $media_type) {
+  $form = array();
+  $form['media_type'] = array(
+    '#type' => 'value',
+    '#value' => $media_type->name,
+  );
+  
+  // If this Media type is handled by us, then we can put in some default
+  // options. Otherwise, we leave it to the implementing module to form_alter.
+  if ($media_type->type_callback == 'media_is_type') {
+    // Options for match_type.
+    $options = array(
+      'all' => t('All'),
+      'any' => t('Any'),
+      '0' => t('Other'),
+    );
+    if ($media_type->type_callback_args['match_type'] && isset($options[$media_type->type_callback_args['match_type']])) {
+      $default_value = $media_type->type_callback_args['match_type'];
+      $other_default_value = '';
+    }
+    else {
+      $default_value = 0;
+      $other_default_value = $media_type->type_callback_args['match_type'];
+    }
+    $form['match_type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Match type'),
+      '#options' => $options,
+      '#default_value' => $default_value,
+    );
+    $form['match_type_other'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Other match type value'),
+      '#default_value' => $other_default_value,
+      '#attached' => array(
+        'js' => array(drupal_get_path('module', 'media') . '/js/media.admin.js'),
+      ),
+    );
+
+    // Options for allowed Streams.
+    $options = array('public' => t('Public files'), 'private' => t('Private files'));
+    foreach(file_get_stream_wrappers() as $stream => $wrapper) {
+      $options[$stream] = $wrapper['name'];
+    }
+    unset($options['temporary']);
+    $default_value = array();
+    if (isset($media_type->type_callback_args['streams'])) {
+      foreach ($media_type->type_callback_args['streams'] as $stream) {
+        $default_value[$stream] = $stream;
+      }
+    }
+    $form['streams'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Allowed streams'),
+      '#options' => $options,
+      '#default_value' => $default_value,
+    );
+
+    // Options for allowed mimetypes & extensions.
+    $default_value = isset($media_type->type_callback_args['mimetypes']) ? implode(' ', $media_type->type_callback_args['mimetypes']) : '';
+    $form['mimetypes'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Allowed mimetypes'),
+      '#description' => t('You may optionally enter one or more allowed file mimetypes for this Media type, if appropriate, separating each with a space. You may use a regular expression for matching, such as %image_match (which would match any mimetype beginning with %image) or %any_match, which would match any file mimetype.', array('%image_match' => '/^image/', '%image' => t('image'), '%any_match' => '/.*/')),
+      '#default_value' => check_plain($default_value),
+    );
+    $default_value = isset($media_type->type_callback_args['extensions']) ? implode(' ', $media_type->type_callback_args['extensions']) : '';
+    $form['extensions'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Allowed extensions'),
+      '#description' => t('You may optionally enter one or more allowed file extensions for this Media type, if appropriate, separating each with a space (and no dots).'),
+      '#default_value' => check_plain($default_value),
+    );
+  }
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#weight' => 100,
+  );
+  return $form;
+}
+
+function media_admin_type_manage_form_submit($form, &$form_state) {
+  $media_type = media_type_load($form_state['values']['media_type']);
+  if ($form_state['values']['match_type']) {
+    $media_type->type_callback_args['match_type'] = $form_state['values']['match_type'];
+  }
+  else {
+    $media_type->type_callback_args['match_type'] = $form_state['values']['match_type_other'];
+  }
+  $media_type->type_callback_args['streams'] = array();
+  foreach ($form_state['values']['streams'] as $stream) {
+    if ($stream) {
+      $media_type->type_callback_args['streams'][] = $stream;
+    }
+  }
+  $media_type->type_callback_args['mimetypes'] = explode(' ', $form_state['values']['mimetypes']);
+  $media_type->type_callback_args['extensions'] = explode(' ', $form_state['values']['extensions']);
+
+  media_type_save($media_type);
+  drupal_set_message(t('The @label media type has been saved.', array('@label' => $media_type->label)));
+}
+
+/**
+ * Form callback for mass import.
+ */
+function media_import($form, &$form_state) {
+  if (!isset($form_state['storage']['files'])) {
+    $form_state['storage']['step'] = 'choose';
+    $form_state['storage']['next_step'] = 'preview';
+    $form['directory'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Directory'),
+      '#required' => TRUE,
+    );
+
+    $form['pattern'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Pattern'),
+      '#description' => 'Only files matching this pattern will be imported.',
+    );
+
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Preview')
+    );
+  }
+  else {
+    $form['preview'] = array(
+      '#markup' => theme('item_list', array('items' => $form_state['storage']['files'])),
+    );
+
+    $form = confirm_form($form, t('Import these files?'), 'admin/content/media/import');
+  }
+  return $form;
+
+}
+
+/**
+ * Validate handler for media_import().
+ */
+function media_import_validate($form, &$form_state) {
+  if ($form_state['values']['op'] != t('Confirm')) {
+    $directory = $form_state['values']['directory'];
+    $pattern = $form_state['values']['pattern'];
+    if (!is_dir($directory)) {
+      form_set_error('directory', t('The provided directory does not exist.'));
+    }
+    $pattern = !empty($pattern) ? $pattern :  '*';
+    $files = glob("$directory/$pattern");
+    if (count($files) == 1) {
+      form_set_error('pattern', t('No files were found in %directory matching %pattern', array('%directory' => $directory, '%pattern' => $pattern)));
+    }
+    $form_state['storage']['files'] = $files;
+  }
+}
+
+/**
+ * Submit handler for media_import().
+ */
+function media_import_submit($form, &$form_state) {
+  if ($form_state['values']['op'] == 'Confirm') {
+    $files = $form_state['storage']['files'];
+    $batch = array(
+      'title' => t('Importing'),
+      'operations' => array(
+        array('media_import_batch_import_files', array($files)),
+      ),
+      'finished' => 'media_import_batch_import_complete',
+      'file' => drupal_get_path('module', 'media') . '/includes/media.admin.inc',
+    );
+    batch_set($batch);
+    return;
+
+  }
+  $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * BatchAPI callback op for media import.
+ */
+function media_import_batch_import_files($files, &$context) {
+  if (!isset($context['sandbox']['files'])) {
+    // This runs the first time the batch runs.
+    // This is stupid, but otherwise, I don't think it will work...
+    $context['results'] = array('success' => array(), 'errors' => array());
+    $context['sandbox']['max'] = count($files);
+    $context['sandbox']['files'] = $files;
+  }
+  $files =& $context['sandbox']['files'];
+
+  // Take a cut of files.  Let's do 10 at a time.
+  $length = (count($files) > media_variable_get('import_batch_size')) ? media_variable_get('import_batch_size') : count($files);
+  $to_process = array_splice($files, 0, $length);
+  $image_in_message = '';
+
+  foreach ($to_process as $file) {
+    try {
+      $file_obj = media_parse_to_file($file);
+      $context['results']['success'][] = $file;
+      if (!$image_in_message) {
+        $media = media_load($file_obj->fid);
+        $image_in_message = field_view_field('media', $media, 'file', 'media_preview');
+      }
+    }
+    catch(Exception $e) {
+      $context['results']['errors'][] = $file . " Reason: " . $e->getMessage();
+    }
+  }
+
+  $context['message'] = "Importing " . theme('item_list', array('items' => $to_process));
+  $context['message'] .= drupal_render($image_in_message); // Just for kicks, show an image we are importing
+
+  $context['finished'] = ($context['sandbox']['max'] - count($files)) / $context['sandbox']['max'];
+
+}
+
+/**
+ * BatchAPI complete callback for media import.
+ */
+function media_import_batch_import_complete($success, $results, $operations) {
+  if ($results['errors']) {
+    drupal_set_message(theme('item_list', array('items' => $results['errors'])), 'error');
+  }
+  if ($results['success']) {
+    drupal_set_message(theme('item_list', array('items' => $results['success'])));
+  }
+}
+
+/**
+ * Admin configruation form for media browser settings.
+ */
+function media_admin_config_browser($form, &$form_state) {
+  $theme_options = array();
+  $theme_options[NULL] = 'Default administration theme';
+  foreach (list_themes() as $key => $theme) {
+    if ($theme->status) {
+      $theme_options[$key] = $theme->info['name'];
+    }
+  }
+
+  $form[media_variable_name('dialog_theme')] = array(
+    '#type' => 'select',
+    '#title' => t('Media browser theme'),
+    '#options' => $theme_options,
+    '#description' => t("This theme will be used for all media related dialogs.  It can be different from your site's theme because many site themes do not work well in the small windows which media uses."),
+    '#default_value' => media_variable_get('dialog_theme'),
+  );
+
+  $form[media_variable_name('file_extensions')] = array(
+    '#type' => 'textfield',
+    '#title' => 'Allowed file extensions',
+    '#default_value' => media_variable_get('file_extensions'),
+    '#description' => t('File extensions which are accepted in the media browser.  Use spaces to separate (e.g. "jpg gif pdf doc"). <br/><em>Note that this can be overriden on a per-field basis when creating multimedia asset fields and files of certain extensions cannot be embedded at this time.</em>'),
+  );
+
+  $form['#submit'][] = 'media_admin_config_browser_pre_submit';
+  return system_settings_form($form);
+}
+
+function media_admin_config_browser_pre_submit(&$form, &$form_state) {
+  if (!$form_state['values'][media_variable_name('dialog_theme')]) {
+    media_variable_del('dialog_theme');
+    unset($form_state['values'][media_variable_name('dialog_theme')]);
+  }
+}
+
+/**
+ * Confirmation form for rebuliding the file_managed table to include type
+ * in rows where there is no type.
+ */
+function media_admin_rebuild_types_form($form, &$form_state) {
+  $total = media_type_invalid_files_count();
+  if ($total == 0) {
+    media_variable_del('show_file_type_rebuild_nag');
+    // @TODO: Make this not sound stupid.
+    drupal_set_message('All files in the system have been assigned types. Media installation complete.');
+    drupal_goto('admin');
+  }
+  $form['total'] = array('#type' => 'value', '#value' => $total);
+  return confirm_form($form, 'Update types for existing files', 'admin/config/media', 'This process is required when installing media on an existing site.  Media needs to scan through existing files and identify the file type. <br/><strong>Update types for ' . $total . ' files?</strogn>');
+}
+
+/**
+ * @see media_admin_rebuild_types_form().
+ */
+function media_admin_rebuild_types_form_submit(&$form, &$form_state) {
+  $total = $form_state['values']['total'];
+
+  $batch = array(
+    'title' => t('Rebuilding type information for ' . $total . ' files'),
+    'operations' => array(
+      array('media_admin_rebuild_types_batch_op', array($total)),
+    ),
+    'finished' => 'media_admin_rebuild_types_batch_complete',
+    'file' => drupal_get_path('module', 'media') . '/includes/media.admin.inc',
+  );
+  batch_set($batch);
+}
+
+/**
+ * Batch operation for fixing the file_managed table for media, adding type values
+ * where no value exists.
+ */
+function media_admin_rebuild_types_batch_op($total, &$context) {
+  $per_run = media_variable_get('media_type_batch_update_per_run', 100);
+  $context['results'] = array_merge($context['results'], media_type_batch_update(FALSE, $per_run));
+  $context['finished'] = count($context['results']) / $total;
+}
+/**
+ * Sets a message informing the user how many file records were updated.
+ */
+function media_admin_rebuild_types_batch_complete($success, $results, $operations) {
+  if ($success) {
+    $message = format_plural(count($results), 'One file identified and given a type.', '@count files identified and given a type.');
+    media_variable_del('show_file_type_rebuild_nag');
+  }
+  drupal_set_message($message);
+}
\ No newline at end of file
diff --git a/includes/media.browser.inc b/includes/media.browser.inc
new file mode 100644
index 0000000..108c32c
--- /dev/null
+++ b/includes/media.browser.inc
@@ -0,0 +1,427 @@
+<?php
+
+/**
+ * @file
+ * Media Browser page callback
+ */
+function media_browser($selected = NULL) {
+  $output = array();
+  $output['#attached']['library'][] = array('media', 'media_browser_page');
+  
+  $params = drupal_get_query_parameters();
+  array_walk_recursive($params, '_media_recursive_check_plain');
+  media_set_browser_params($params);
+  
+  // How do we validate these?  I don't know.
+  // I think PDO should protect them, but I'm not 100% certain.
+  if (isset($params['fid'])) {
+    // A form has submitted, selected a piece (or pieces) of media and we refreshed.
+    // So we're just going to send the media entity out as a JS setting so the browser can close itself.
+    $selected = $params['fid'];
+    if (!is_array($params['fid'])) {
+      $selected = array($params['fid']);
+    }
+    if (!is_numeric($selected[0])) {
+      throw new Exception('Error selecting media, fid param is not an fid or an array of fids');
+    }
+    $selectedMedia = entity_load('media', $selected);
+    foreach ($selectedMedia as &$media) {
+      media_browser_build_media_item($media);
+    }
+    $setting = array('media' => array('selectedMedia' => array_values($selectedMedia)));
+    drupal_add_js($setting, 'setting');
+    return $output;
+  }
+
+  // Normal browser operation.
+  foreach (module_implements('media_browser_plugin_info') as $module) {
+    foreach(module_invoke($module, 'media_browser_plugin_info') as $key => $plugin_data) {
+      $plugins[$key] = $plugin_data + array(
+        '#module' => $module,
+        '#weight' => 0,
+      );
+      $plugins[$key]['#weight'] += count($plugins)/1000;
+    }
+  }
+  
+  // Only the plugins in this array are loaded.
+  if (!empty($params['enabledPlugins'])) {
+    $plugins = array_intersect_key($plugins, array_fill_keys($params['enabledPlugins'], 1));
+  }
+  elseif (!empty($params['disabledPlugins'])) {
+    $plugins = array_diff_key($plugins, array_fill_keys($params['disabledPlugins'], 1));
+  }
+
+  foreach ($plugins as $key => &$plugin) {
+    $plugin += module_invoke($plugin['#module'], 'media_browser_plugin_view', $key, $params);
+  }
+  
+  // Allow modules to change the tab names or whatever else they want to change
+  // before we render.  Perhaps this should be an alter on the theming function
+  // that we should write to be making the tabs.
+  drupal_alter('media_browser_plugins', $plugins);
+  
+  $tabs = array(); // List of tabs to render.
+
+  $settings = array('media' => array('browser' => array()));
+  $browser_settings =& $settings['media']['browser'];
+
+  //@todo: replace with Tabs module if it gets upgraded.
+  foreach (element_children($plugins, TRUE) as $key) {
+    $plugin =& $plugins[$key];
+
+    //Add any JS settings
+    $browser_settings[$key] = isset($plugin['#settings']) ? $plugin['#settings'] : array();
+
+    // If this is a "ajax" style tab, add the href, otherwise an id.
+    $href = isset($plugin['#callback']) ? $plugin['#callback'] : "#media-tab-$key";
+    $tabs[] = "<a href='$href'><span>{$plugin['#title']}</span></a>";
+
+    // Create a div for each tab's content.
+    $plugin['#prefix'] = <<<EOS
+    <div class="media-browser-tab" id="media-tab-$key">
+EOS;
+    $plugin['#suffix'] = <<<EOS
+    </div>
+    <!-- End #media-tab-$key -->
+EOS;
+  }
+
+  drupal_add_js($settings, 'setting');
+
+  $output['tabset'] = array(
+    '#prefix' => '<div id="media-browser-tabset">',
+    '#suffix' => '</div>',
+  );
+  $output['tabset']['list'] = array(
+    '#markup' => '<ul><li>' . implode('</li><li>', $tabs) . '</li></ul>'
+  );
+
+  $output['tabset']['plugins'] = $plugins;
+  return $output;
+}
+
+/**
+ * Provides a singleton of the params passed to the media browser.
+ *
+ * This is useful in situations like form alters because callers can pass
+ * id="wysiywg_form" or whatever they want, and a form alter could pick this up.
+ * We may want to change the hook_media_browser_plugin_view() implementations to
+ * use this function instead of being passed params for consistency.
+ *
+ * It also offers a chance for some meddler to meddle with them.
+ *
+ * @param array $params
+ *  An array of parameters provided when a media_browser is launched.
+ *  See media_browser().
+ */
+function media_set_browser_params(&$params = NULL) {
+  static $stored_params;
+  
+  if (!$params) {
+    return $stored_params;
+  }
+  $stored_params = $params;
+  drupal_alter('media_browser_params', $stored_params);
+}
+
+/**
+ * For sanity in grammar.
+ * @see media_set_browser_params().
+ */
+function media_get_browser_params() {
+  return media_set_browser_params();
+}
+
+/**
+ * AJAX Callback function to return a list of media files
+ */
+function media_browser_list() {
+  $params = drupal_get_query_parameters();
+  // How do we validate these?  I don't know.
+  // I think PDO should protect them, but I'm not 100% certain.
+  array_walk_recursive($params, '_media_recursive_check_plain');
+  
+  $types = isset($params['types']) ? $params['types'] : NULL;
+  $url_include_patterns = isset($params['url_include_patterns']) ? $params['url_include_patterns'] : NULL;
+  $url_exclude_patterns = isset($params['url_exclude_patterns']) ? $params['url_exclude_patterns'] : NULL;
+
+  $start = isset($params['start']) ? $params['start'] : 0;
+  $limit = isset($params['limit']) ? $params['limit'] : media_variable_get('browser_pager_limit');
+  
+  $conditions = array();
+
+  $query = new EntityFieldQuery();
+  
+  $query->range($start, $limit);
+  $query->entityCondition('entity_type', 'media');
+  $query->entityOrderBy('entity_id', 'DESC');
+
+  if ($types) {
+    $query->entityCondition('bundle', $types, 'IN');
+  }
+
+  if ($url_include_patterns) {
+    $query->propertyCondition('uri', $v, 'CONTAINS');
+    // Insert stream related restrictions here.
+  }
+  if ($url_exclude_patterns) {
+    $query->propertyCondition('uri', "%$v%", 'NOT LIKE');
+  }
+
+  // @todo Implement granular editorial access: http://drupal.org/node/696970.
+  //   In the meantime, protect information about private files from being
+  //   discovered by unprivileged users. See also media_file_view().
+  if (!user_access('administer media')) {
+    $query->propertyCondition('uri', 'private://%', 'NOT LIKE');
+  }
+
+  $query->propertyCondition('status', FILE_STATUS_PERMANENT);
+
+  $result = $query->execute();
+  $ids = array_keys($result['media']);
+  if ($ids) {
+    $media_entities = entity_load('media', $ids);
+  }
+  else {
+    $media_entities = array();
+  }
+  
+  foreach ($media_entities as &$media) {
+    media_browser_build_media_item($media);
+  }
+  
+  drupal_json_output(array('media' => array_values($media_entities)));
+  exit();
+
+}
+
+/**
+ * Implements hook_media_browser_plugin_info().
+ */
+function media_media_browser_plugin_info() {
+  $plugins = array();
+
+  // @TODO: Should we add a permission around this?
+  $plugins['library'] = array(
+    '#weight' => 10,
+  );
+
+  if (user_access('administer media') || user_access('edit media')) {
+    $plugins['upload'] = array(
+      '#weight' => -10,
+     );
+  }
+  return $plugins;
+}
+
+/**
+ * Implementaion of hook_media_browser_plugin_view().
+ */
+function media_media_browser_plugin_view($plugin_name, $params) {
+  $path = drupal_get_path('module', 'media');
+
+  module_load_include('inc', 'media', 'includes/media.admin');
+  module_load_include('inc', 'media', 'includes/media.pages');
+
+  $types = isset($params['types']) ? $params['types'] : array();
+
+  // The multiselect parameter is a string.  So we check to see if it is set and
+  // adjust the local variable accordingly.
+  $multiselect = FALSE;
+  if (isset($params['multiselect']) && $params['multiselect'] != 'false') {
+    $multiselect = TRUE;
+  }
+
+  $redirect = array('media/browser', array('query' => array('render' => 'media-popup')));
+  switch ($plugin_name) {
+    case 'upload':
+      $attached = array();
+      if ($multiselect && module_exists('plupload')) {
+        $upload_form_id = 'media_add_upload_multiple';
+        $attached['js'] = array($path . '/js/plugins/media.upload_multiple.js');
+      }
+      else {
+        $upload_form_id = 'media_add_upload';
+      }
+
+      $upload_form = drupal_get_form($upload_form_id, $types);
+      return array(
+        '#title' => t('Upload'),
+        'form' => array($upload_form),
+        '#attached' => $attached,
+      );
+      break;
+    case 'library':
+      return array(
+        '#title' => t('Library'),
+        '#attached' => array(
+          'js' => array(
+            $path . '/js/plugins/media.library.js',
+          ),
+          'css' => array(
+            //@todo: should move this.
+            $path . '/js/plugins/media.library.css',
+          ),
+        ),
+        '#settings' => array(
+          'viewMode' => 'thumbnails',
+          'getMediaUrl' => url('media/browser/list'),
+          'types' => $types,
+          'multiselect' => $multiselect,
+        // We should probably change this to load dynamically when requested
+        // via the JS file.
+        ),
+        '#markup' => '<div id="container"><div id="scrollbox"><ul id="media-browser-library-list" class="media-list-thumbnails"></ul><div id="status"></div></div></div>',
+      );
+    break;
+  }
+
+  return array();
+ }
+
+/**
+ * Silly function to recursively run check_plain on an array.
+ *
+ * There is probably something in core I am not aware of that does this.
+ *
+ * @param $value
+ * @param $key
+ */
+function _media_recursive_check_plain(&$value, $key) {
+  $value = check_plain($value);
+}
+
+/**
+ * Attaches media browser javascript to an element.
+ *
+ * @param $element
+ *  The element array to attach to.
+ */
+function media_attach_browser_js(&$element) {
+  $javascript = media_browser_js();
+  foreach ($javascript as $key => $definitions) {
+    foreach ($definitions as $definition) {
+      $element['#attached'][$key][] = $definition;
+    }
+  }
+}
+
+/**
+ * Helper function to define browser javascript.
+ */
+function media_browser_js() {
+  $settings = array(
+    'browserUrl' => url('media/browser',
+      array('query' => array('render' => 'media-popup'))),
+    'styleSelectorUrl' => url('media/-media_id-/format-form',
+      array('query' => array('render' => 'media-popup'))),
+  );
+
+  $js = array(
+    'library' => array(
+      array('media', 'media_browser'),
+    ),
+    'js' => array(
+      array(
+       'data' => array('media' => $settings),
+       'type' => 'setting',
+      ),
+    ),
+  );
+  return $js;
+}
+
+/**
+ * Menu callback for testing the media browser
+ */
+function media_browser_testbed($form) {
+  media_attach_browser_js($form);
+
+  $form['test_element'] = array(
+    '#type' => 'media',
+    '#media_options' => array(
+      'global' => array(
+        'types' => array('video', 'audio'),
+      ),
+    )
+  );
+  
+  $launcher = '<a href="#" id="launcher"> Launch Media Browser</a>';
+
+  $form['options'] = array(
+    '#type' => 'textarea',
+    '#title' => 'Options (JSON)',
+    '#rows' => 10,
+  );
+
+  $form['launcher'] = array(
+    '#markup' => $launcher,
+  );
+
+  $form['result'] = array(
+    '#type' => 'textarea',
+    '#title' => 'Result',
+  );
+
+
+  $js = <<<EOF
+    Drupal.behaviors.mediaTest = {
+    attach: function(context, settings) {
+      var delim = "---";
+      var recentOptions = [];
+      var recentOptionsCookie = jQuery.cookie("recentOptions");
+      if (recentOptionsCookie) {
+        recentOptions = recentOptionsCookie.split("---");
+      }
+
+      var recentSelectBox = jQuery('<select id="recent_options" style="width:100%"></select>').change(function() { jQuery('#edit-options').val(jQuery(this).val())});
+
+      jQuery('.form-item-options').append('<label for="recent_options">Recent</a>');
+      jQuery('.form-item-options').append(recentSelectBox);
+      jQuery('.form-item-options').append(jQuery('<a href="#">Reset</a>').click(function() {alert('reset'); jQuery.cookie("recentOptions", null); window.location.reload(); }));
+
+      jQuery.each(recentOptions, function (idx, val) {
+        recentSelectBox.append(jQuery('<option></option>').val(val).html(val));
+      });
+
+
+      jQuery('#launcher').click(function () {
+        jQuery('#edit-result').val('');
+        var options = {};
+        var optionsTxt = jQuery('#edit-options').val();
+        if (optionsTxt) {
+          // Store it in the recent box
+          recentOptionsCookie += "---" + optionsTxt
+          jQuery.cookie("recentOptions", recentOptionsCookie, { expires: 7 });
+          recentSelectBox.append(jQuery('<option></option>').val(optionsTxt).html(optionsTxt));
+          options = eval('(' + optionsTxt + ')');
+        }
+        Drupal.media.popups.mediaBrowser(Drupal.behaviors.mediaTest.mediaSelected, options);
+        return false;
+      });
+    },
+
+    mediaSelected: function(selectedMedia) {
+      var result = JSON.stringify(selectedMedia);
+        jQuery('#edit-result').val(result);
+    }
+  }
+
+EOF;
+
+  drupal_add_js($js, array('type' => 'inline'));
+  return $form;
+}
+
+/**
+ * Adds on properties to the media item which are expected by callers who are
+ * going to act on the returned item.
+ *
+ * @param Object &$media;
+ */
+function media_browser_build_media_item(&$media) {
+  $preview = media_get_thumbnail_preview($media);
+  $media->preview = drupal_render($preview);
+  $media->url = file_create_url($media->uri);
+}
diff --git a/includes/media.fields.inc b/includes/media.fields.inc
new file mode 100644
index 0000000..de28823
--- /dev/null
+++ b/includes/media.fields.inc
@@ -0,0 +1,456 @@
+<?php
+
+/**
+ * @file: Provides a "Multimedia asset" field to the fields API
+ */
+
+/**
+ * Implement hook_field_info().
+ */
+function media_field_info() {
+  return array(
+    'media' => array(
+      'label' => t('Multimedia asset'),
+      'description' => t('This field stores a reference to a multimedia asset.'),
+      'settings' => array(),
+      'instance_settings' => array(),
+      'default_widget' => 'media_generic',
+      'default_formatter' => 'media_large',
+    ),
+  );
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function media_field_is_empty($item, $field) {
+  if (!is_array($item) || (empty($item['fid']))) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Implement hook_field_widget_info().
+ */
+function media_field_widget_info() {
+  return array(
+    'media_generic' => array(
+      'label' => t('Media file selector'),
+      'field types' => array('media'),
+      'settings' => array(
+        'progress_indicator' => 'throbber',
+        'allowed_types' => array('image'),
+        'allowed_schemes' => array('public', 'private'),
+      ),
+      'behaviors' => array(
+        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
+        'default value' => FIELD_BEHAVIOR_NONE,
+      ),
+    ),
+  );
+}
+
+/**
+ * Implement hook_field_formatter_info
+ */
+function media_field_formatter_info() {
+  $formatters = array();
+
+  // Formatters for media are the same as build modes for media.
+  $build_modes = media_field_view_modes('media');
+  foreach ($build_modes as $key => $mode) {
+    $formatters[$key] = array(
+      'label' => t($mode['label']),
+      'field types' => array('media'),
+    );
+  }
+
+  // Also add a formatter for files to show larger icons based on mimetype
+  $formatters['media_large_icon'] = array(
+    'label' => t('Large filetype icon'),
+    'field types' => array('file'),
+  );
+  
+  return $formatters;
+}
+
+/**
+ * Used to Implement hook_field_view_modes().
+ */
+function media_field_view_modes($obj_type) {
+  $modes = array();
+  if ($obj_type == 'media') {
+    $modes = array(
+      'media_link' => array('label' => t('Link'), 'custom settings' => FALSE),
+      'media_preview' => array('label' => t('Preview'), 'custom settings' => FALSE),
+      'media_small' => array('label' => t('Small'), 'custom settings' => FALSE),
+      'media_large' => array('label' => t('Large'), 'custom settings' => FALSE),
+      'media_original' => array('label' => t('Original'), 'custom settings' => FALSE),
+    );
+  }
+  return $modes;
+}
+
+function media_field_ui_view_modes_tabs() {
+  $modes = array(
+    'basic' => array(
+      'view modes' => array_keys(media_field_view_modes('media')),
+    ),
+  );
+  return $modes;
+}
+
+/**
+ * Implements hook_field_prepare_view().
+ *
+ * @todo Get resolution on http://drupal.org/node/879034 regarding using this
+ *   hook to load referenced entities.
+ */
+function media_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
+  // Collect all media IDs that need loading.
+  $fids = array();
+  foreach ($entities as $id => $entity) {
+    foreach ($items[$id] as $delta => $item) {
+      if (is_array($item) && !empty($item['fid'])) {
+        $fids[$item['fid']] = $item['fid'];
+      }
+    }
+  }
+
+  // Load the entities in a single operation.
+  $media_entities = media_load_multiple(array_values($fids));
+
+  // Populate that item data with the loaded entities.
+  foreach ($entities as $id => $entity) {
+    foreach ($items[$id] as $delta => $item) {
+      if (is_array($item) && !empty($item['fid'])) {
+        $items[$id][$delta] = $media_entities[$item['fid']];
+      }
+    }
+  }
+}
+
+/**
+ * Implement hook_field_formatter_view
+ */
+function media_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+  
+  $element = array();
+  
+  // This block handles fallback file type icons for media which doesn't have
+  // a thumbnail.  It's kind of a hack, and belongs in the file module.
+  if ($display['type'] == 'media_large_icon') {
+    foreach ($items as $delta => $item) {
+      // Display all values in a single element..
+      $element[$delta] = array(
+        '#theme' => 'media_formatter_large_icon',
+        '#file' => $item,
+      );
+    }
+    return $element;
+  }
+  
+  // The rest of this hook invocation is for rendering media when it is attached
+  // as a field to other entities.  The formatter on the media field is equivilent
+  // to the media view_mode.  So if someone configures a media field on a node
+  // and says that the teaser formatter for the media is "large", it will render
+  // the media entity as per the large view_mode.
+
+  // An array of media items to pass into entity rendering functions.
+  $media_items = array();
+
+  foreach ($items as $delta => $item) {
+    $media_items[$item->fid] = $item;
+    //$media_items[$item['fid']] = media_load($item['fid']);
+  }
+
+  field_attach_prepare_view('media', $media_items, $display['type']);
+  entity_prepare_view('media', $media_items);
+
+
+
+  foreach ($items as $delta => $item) {
+    // @TODO:
+    // This works fine for Media styles, just not the default file types.
+    // Find out why this doesn't automatically already route through file.
+    // We're using a filefield in theory, created during the media_type
+    // instantiation at media_type_configure_fields().
+    // Other fields are routed properly, but not that default field...
+
+    // First check if the default File formatters are sufficient.
+//     $element[$delta] = file_field_formatter_view($obj_type, $object, $field, $instance, $langcode, $items, $display);
+
+    // If this is not handled by the File module, then defer to Styles.
+//     if (!$element[$delta]) {
+      $element[$delta] = field_attach_view('media', $item, $display['type'], $langcode);
+
+      // We'll defer to the Styles module to route formatting for the 'file'.
+      $element[$delta]['file']['#theme'] = 'styles_field_formatter';
+      $element[$delta]['file']['#element'] = $element[$delta]['file'];
+//     }
+  }
+
+  return $element;
+}
+
+/**
+ * Implement hook_field_widget_settings_form().
+ */
+function media_field_widget_settings_form($field, $instance) {
+
+  $widget = $instance['widget'];
+  $settings = $widget['settings'];
+  $form = array();
+
+  // Setup type selection form
+  $types = media_type_get_types();
+  $options = array();
+  foreach ($types as $key => $definition) {
+    $options[$key] = $definition->label;
+  }
+
+  $form['allowed_types'] = array (
+    '#type' => 'checkboxes',
+    '#title' => t('Allowed media types'),
+    '#options' => $options,
+    '#default_value' => $settings['allowed_types'],
+    '#description' => t('Media types which are allowed for this field'),
+    '#weight' => 1,
+  );
+
+  $streams = file_get_stream_wrappers();
+  $options = array();
+  unset($streams['temporary']);
+  foreach ($streams as $scheme => $data) {
+    $options[$scheme] = t('@scheme (@name)', array('@scheme' => $scheme . '://', '@name' => $data['name']));
+  }
+  $form['allowed_schemes'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Allowed URI schemes'),
+    '#options' => $options,
+    '#default_value' => $settings['allowed_schemes'],
+    '#description' => t('URI schemes include public:// and private:// which are the Drupal files directories, and may also refer to remote sites.'),
+    '#weight' => 2,
+  );
+  return $form;
+}
+
+/**
+ * Implements hook_field_widget_form().
+ */
+function media_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
+  $widget_settings = $instance['widget']['settings'];
+
+  $permission = user_access('edit media');
+  if (!$permission) {
+    // @todo: show the default file upload field.
+    return;
+  }
+
+  // @todo The Field API supports automatic serialization / unserialization, so
+  //   this should no longer be needed. After verifying with a module that uses
+  //   the 'data' column, remove this.
+  // @see media_field_widget_value()
+  $current_value = array();
+  if (isset($items[$delta])) {
+    $current_value = $items[$delta];
+    // @todo $items[$delta] is sometimes a loaded media entity (an object)
+    //   rather than an array. This conflicts with Field API expectations (for
+    //   example, it results in fatal errors when previewing a node with a
+    //   multi-valued media field), so should be fixed. In the meantime, don't
+    //   assume that $current_value is an array.
+    if (is_array($current_value) && isset($current_value['data']) && is_string($current_value['data'])) {
+      $current_value['data'] = unserialize($current_value['data']);
+    }
+  }
+
+  $element += array(
+    '#type' => 'media', // Would like to make this a fieldset, but throws some weird warning about element_children... not sure what it is about yet.
+    '#collapsed' => TRUE,
+    '#default_value' => $current_value,
+    '#required' => $instance['required'],
+    //'#media_file_extensions' => $instance['settings']['file_extensions'],
+    // @TODO: Not implemented yet.
+    '#media_options' => array(
+      'global' => array(
+        'types' => array_filter($instance['widget']['settings']['allowed_types']),
+        // @todo: Not implemented
+        'schemes' => $instance['widget']['settings']['allowed_schemes'],
+      ),
+    ),
+  );
+
+  return $element;
+}
+
+/**
+ * Implements hook_field_validate().
+ *
+ * Possible error codes:
+ * - 'media_fid_illegal_value': The value is not part of the list of allowed values.
+ */
+function media_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) {
+
+  $allowed_types = array_keys(array_filter($instance['widget']['settings']['allowed_types']));
+
+    // @TODO: merge in stuff from media_uri_value
+  foreach ($items as $delta => $item) {
+    if ($item['fid'] == 0) {
+      return TRUE;
+      //@TODO: make support for submiting with just a URI here?
+    }
+    $result = db_select('file_managed', 'f')
+      ->fields('f')
+      ->condition('fid', $item['fid'])
+      ->condition('type', $allowed_types, 'IN')
+      ->execute()
+      ->fetchField();
+    if (!$result) {
+      $errors[$field['field_name']][$langcode][$delta][] = array(
+        'error' => 'media_fid_illegal_value',
+        'message' => t('%name: illegal value.', array('%name' => t($instance['label']))),
+      );
+    }
+  }
+}
+
+/**
+ * @todo Is this function ever called? If not, remove it. The Field API now
+ *   supports automatic serialization / unserialization, so this should no
+ *   longer be needed. After verifying with a module that uses the 'data'
+ *   column, remove this.
+ *
+ * @see media_field_widget_form()
+ */
+function media_field_widget_value($element, $input, $form_state) {
+  $return = $input;
+  
+  if (!is_array($return)) {
+    $return = array();
+  }
+
+  if (isset($return['data'])) {
+    $return['data'] = serialize($return['data']);
+  }
+
+  $return += array(
+    'fid' => 0,
+    'title' => '',
+    'data' => NULL,
+  );
+
+  return $return;
+}
+
+/**
+ * @todo The following hook_field_(insert|update|delete|delete_revision)
+ *   implementations are nearly identical to the File module implementations of
+ *   the same field hooks. The only differences are:
+ *   - We pass 'media' rather than 'file' as the module argument to the
+ *     file_usage_(add|delete)() functions.
+ *   - We do not delete the file / media entity when its usage count goes to 0.
+ *   We should submit a core patch to File module to make it flexible with
+ *   respect to the above, so that we can reuse its implementation rather than
+ *   duplicating it.
+ */
+
+/**
+ * Implements hook_field_insert().
+ */
+function media_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+  // Add a new usage of each uploaded file.
+  foreach ($items as $item) {
+    $file = (object) $item;
+    file_usage_add($file, 'media', $entity_type, $id);
+  }
+}
+
+/**
+ * Implements hook_field_update().
+ *
+ * Checks for files that have been removed from the object.
+ */
+function media_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+  // On new revisions, all files are considered to be a new usage and no
+  // deletion of previous file usages are necessary.
+  if (!empty($entity->revision)) {
+    foreach ($items as $item) {
+      $file = (object) $item;
+      file_usage_add($file, 'media', $entity_type, $id);
+    }
+    return;
+  }
+  
+  // Build a display of the current FIDs.
+  $current_fids = array();
+  foreach ($items as $item) {
+    $current_fids[] = $item['fid'];
+  }
+  
+  // Create a bare-bones entity so that we can load its previous values.
+  $original = entity_create_stub_entity($entity_type, array($id, $vid, $bundle));
+  field_attach_load($entity_type, array($id => $original), FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
+
+  // Compare the original field values with the ones that are being saved.
+  $original_fids = array();
+  if (!empty($original->{$field['field_name']}[$langcode])) {
+    foreach ($original->{$field['field_name']}[$langcode] as $original_item) {
+      $original_fids[] = $original_item['fid'];
+      if (isset($original_item['fid']) && !in_array($original_item['fid'], $current_fids)) {
+        // Decrement the file usage count by 1.
+        $file = (object)$original_item;
+        file_usage_delete($file, 'media', $entity_type, $id, 1);
+      }
+    }
+  }
+
+  // Add new usage entries for newly added files.
+  foreach ($items as $item) {
+    if (!in_array($item['fid'], $original_fids)) {
+      $file = (object) $item;
+      file_usage_add($file, 'media', $entity_type, $id);
+    }
+  }
+}
+
+/**
+ * Implements hook_field_delete().
+ */
+function media_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+
+  // Delete all file usages within this entity.
+  foreach ($items as $delta => $item) {
+    $file = (object) $item;
+    file_usage_delete($file, 'media', $entity_type, $id, 0);
+  }
+}
+
+/**
+ * Implements hook_field_delete_revision().
+ */
+function media_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+  foreach ($items as $delta => $item) {
+    // @TODO: Not sure if this is correct
+    $file = (object)$item;
+    if (file_usage_delete($file, 'media', $entity_type, $id, 1)) {
+      $items[$delta] = NULL;
+    }
+  }
+}
+
+/**
+ * Implements hook_field_instance_update().
+ */
+function media_field_update_instance($instance, $prior_instance) {
+  // Clear the filter cache when updating instance settings for a media entity.
+  if ($instance['entity_type'] == 'media') {
+    media_filter_invalidate_caches();
+  }
+}
diff --git a/includes/media.filter.inc b/includes/media.filter.inc
new file mode 100644
index 0000000..f3bd7be
--- /dev/null
+++ b/includes/media.filter.inc
@@ -0,0 +1,426 @@
+<?php
+
+/**
+ * @file
+ * Functions related to the WYSIWYG editor and the media input filter.
+ *
+ * @TODO: Rename this file?
+ */
+
+/**
+ * Implementation of hook_wysiwyg_include_directory()
+ */
+function media_wysiwyg_include_directory($type) {
+  switch($type) {
+    case 'plugins':
+      return 'wysiwyg_plugins';
+        break;
+    }
+}
+
+/**
+ * Filter callback for media markup filter.
+ *
+ * @TODO check for security probably pass text through filter_xss
+ * @return unknown_type
+ */
+function media_filter($text) {
+  $text = ' ' . $text . ' ';
+  $text = preg_replace_callback("/\[\[.*?]]/s",'media_token_to_markup', $text);
+
+  return $text;
+}
+
+/**
+ *  Filter callback for media url filter.
+ *  @TODO: There are currently problems with this. For instance, if a file is
+ *  to be loaded from a remote location here, it will be recreated multiple
+ *  times, each time this filter is called. If we want to continue supporting
+ *  this feature, we would need to probably create a new stream or other way
+ *  to lookup a remote file w/ its local version. Probably best as a contributed
+ *  module because of this difficulty. ~ aaron.
+ */
+function media_url_filter($text, $filter) {
+  $text = ' '. $text .' ';
+
+  // Need to attach the variables to the callback after the regex.
+  $callback = _media_url_curry('_media_url_parse_full_links', 1);
+
+  // Match absolute URLs.
+  $text = preg_replace_callback("`(<p>|<li>|<br\s*/?>|[ \n\r\t\(])((http://|https://)([a-zA-Z0-9@:%_+*~#?&=.,/;-]*[a-zA-Z0-9@:%_+*~#&=/;-]))([.,?!]*?)(?=(</p>|</li>|<br\s*/?>|[ \n\r\t\)]))`i", $callback, $text);
+
+  return $text;
+}
+
+/**
+ * If one of our allowed providers knows what to do with the url,
+ * then let it embed the video.
+ *
+ * @param int $filter
+ *  The filter id.
+ * @param array $match
+ *  The matched text from our regex.
+ *
+ * @return string
+ *  The replacement text for the url.
+ */
+function _media_url_parse_full_links($match) {
+  // Get just the URL.
+  $match[2] = check_url(decode_entities($match[2]));
+  try {
+    $file = media_parse_to_file($match[2]);
+  }
+  catch (Exception $e) {
+    // Ignore errors; pass the original text for other filters to deal with.
+    return $match[0];
+  }
+
+  if ($file->fid) {
+    // Return the embedded media.
+    // Now load the desired media to display.
+    $medias = entity_load('media', array($file->fid));
+
+    if (!empty($medias)) {
+      $media = array_pop($medias);
+
+      // Generate a preview of the file
+      // @TODO: Allow user to change the formatter in the filter settings.
+      $preview = field_view_field('media', $media, 'file', 'media_large');
+      $preview['#show_names'] = TRUE;
+
+      return drupal_render($preview);
+    }
+  }
+
+  // Nothing was parsed; return the original text.
+  return $match[0];
+}
+
+function _media_url_curry($func, $arity) {
+  return create_function('', "
+    \$args = func_get_args();
+    if(count(\$args) >= $arity)
+        return call_user_func_array('$func', \$args);
+    \$args = var_export(\$args, 1);
+    return create_function('','
+        \$a = func_get_args();
+        \$z = ' . \$args . ';
+        \$a = array_merge(\$z,\$a);
+        return call_user_func_array(\'$func\', \$a);
+    ');
+  ");
+}
+
+/**
+ * Replace callback to convert tag into markup
+ * @param string $match
+ * Takes a match of tag code
+ * @param boolean $wysiwyg
+ *   Set to TRUE if called from within the WYSIWYG text area editor.
+ * @return
+ * Return the replaced markup
+ */
+function media_token_to_markup($match, $wysiwyg = FALSE) {
+  $match = str_replace("[[","",$match);
+  $match = str_replace("]]","",$match);
+  $tag = $match[0];
+
+  try {
+    if (!is_string($tag)) {
+      throw new Exception('Unable to find matching tag');
+    }
+    
+    $media = drupal_json_decode($tag);
+
+    if (!isset($media['fid'])) {
+      throw new Exception('No file Id');
+    }
+    if (!isset($media['view_mode'])) {
+      // Should we log or throw an exception here instead?
+      // Do we need to validate the view mode for fields API?
+      $media['view_mode'] = media_variable_get('wysiwyg_default_view_mode');
+    }
+    $media_obj = media_load($media['fid']);
+    if (!$media_obj) {
+     throw new Exception('Could not load media object');
+    }
+
+    // Track the fid of this media object in the {media_filter_usage} table.
+    media_filter_track_usage($media['fid']);
+
+    $settings = is_array($media['attributes']) ? $media['attributes'] : array();
+    $attribute_whitelist = media_variable_get('wysiwyg_allowed_attributes');
+    $settings = array_intersect_key($settings, array_flip($attribute_whitelist));
+
+    // @TODO: What case does this provide for?  Can we add this logic in JS when we embed it?
+    // This doesn't look great to me.  Also won't work if the style has anything
+    // between width and height (or if they are in reverse order).
+    if (isset($settings['style'])) {
+      if (preg_match('@width: (.+?)px; height: (.+?)px;@i', $settings['style'], $matches)) {
+        $settings['width'] = $matches[1];
+        $settings['height'] = $matches[2];
+      }
+    }
+    if ($wysiwyg) {
+      $settings['wysiwyg'] = $wysiwyg;
+    }
+  }
+  catch (Exception $e) {
+    watchdog('media', 'Unable to render media from %tag. Error: %error', array('%tag' => $tag, '%error' => $e->getMessage()));
+    return '';
+  }
+
+  $file_field = media_get_file_without_label($media_obj, $media['view_mode'], $settings);
+  return drupal_render($file_field);
+}
+
+/**
+ * Builds a map of media tags in the element being rendered to their rendered HTML.
+ *
+ * The map is stored in JS, so we can transform them when the editor is being displayed.
+ *
+ * @param array $element
+ */
+function media_pre_render_text_format($element) {
+  // filter_process_format() copies properties to the expanded 'value' child
+  // element.
+  if (!isset($element['format'])) {
+    return $element;
+  }
+  
+  $field = &$element['value'];
+  $settings = array(
+    'field' => $field['#id'],
+  );
+
+  $tagmap = _media_generate_tagMap($field['#value']);
+
+  if (isset($tagmap)) {
+    drupal_add_js(array('tagmap' => array_unique($tagmap)), 'setting');
+  }
+  return $element;
+}
+
+/**
+ * Generates an array of [inline tags] => <html> to be used in filter
+ * replacement and to add the mapping to JS.
+ * @param
+ * The String containing text and html markup of textarea
+ * @return
+ * An associative array with tag code as key and html markup as the value.
+ *
+ * @see
+ * media_process_form
+ * media_token_to_markup
+ */
+function _media_generate_tagMap($text) {
+  // Making $tagmap static as this function is called many times and
+  // adds duplicate markup for each tag code in Drupal.settings JS,
+  // so in media_process_form it adds something like tagCode:<markup>,
+  // <markup> and when we replace in attach see two duplicate images
+  // for one tagCode. Making static would make function remember value
+  // between function calls. Since media_process_form is multiple times
+  // with same form, this function is also called multiple times.
+  static $tagmap = array();
+  preg_match_all("/\[\[.*?]]/s", $text, $matches, PREG_SET_ORDER);
+  foreach($matches as $match) {
+    // We see if tagContent is already in $tagMap, if not we add it
+    // to $tagmap.  If we return an empty array, we break embeddings of the same
+    // media multiple times.
+    if(empty($tagmap[$match[0]])) {
+      // @TODO: Total HACK, but better than nothing.
+      // We should find a better way of cleaning this up.
+      if ($markup_for_media = media_token_to_markup($match, TRUE)) {
+        $tagmap[$match[0]] = $markup_for_media;
+      } else {
+        $tagmap[$match[0]] = '<div><img src="/broken.jpg" height="50px" width="100px"/></div>';
+      }
+    }
+  }
+  return $tagmap;
+}
+
+/**
+ * Form callback used when embedding media.
+ *
+ * Allows the user to pick a format for their media file.
+ * Can also have additional params depending on the media type.
+ */
+function media_format_form($form, $form_state, $media) {
+  // This will vary depending on the media type.
+  $form = array();
+  $form['#media'] = $media;
+
+  $instance_info = field_info_instance('media', 'file', $media->type);
+  $entity_info = entity_get_info('media');
+  $view_modes = $entity_info['view modes'];
+
+  $options = array();
+
+  drupal_alter('media_wysiwyg_allowed_view_modes', $media_type, $view_modes);
+
+  foreach ($view_modes as $key => $title) {
+    $format = field_get_display($instance_info, $key, $media);
+    //If the format is set to hidden, don't offer it
+    if ($format['type'] == 'hidden') {
+      continue;
+    }
+
+    //@TODO: Display more verbose information about which formatter and what it does.
+    $options[$key] = $title['label'];
+    $file_field = media_get_file_without_label($media, $key, array('wysiwyg' => TRUE));
+
+    // Make a pretty name out of this.
+    $formats[$key] = drupal_render($file_field);
+  }
+
+  if (!count($formats)) {
+    throw new Exception('Unable to continue, no available formats for displaying media.');
+    return;
+  }
+
+  $default_view_mode = media_variable_get('wysiwyg_default_view_mode');
+  if (!isset($formats[$default_view_mode])) {
+    $default_view_mode = key($formats);
+  }
+
+
+  // Add JS and settings array of formats.
+  $settings = array();
+  $settings['media'] = array('formatFormFormats' => $formats);
+  drupal_add_js($settings, 'setting');
+
+  drupal_add_library('media', 'media_base');
+  drupal_add_library('system', 'form');
+
+  $path = drupal_get_path('module', 'media');
+  $form['#attached']['js'][] = $path . '/js/media.format_form.js';
+  $form['#attached']['css'][] = $path . '/css/media-format-form.css';
+
+  $form['heading'] = array(
+    '#type' => 'markup',
+    '#prefix' => '<h1 class="title">',
+    '#suffix' => '</h1>',
+    '#markup' => t('Embedding %filename', array('%filename' => $media->filename)),
+  );
+
+  $preview = media_get_thumbnail_preview($media);
+
+  $form['preview'] = array(
+    '#type' => 'markup',
+    '#title' => basename($media->uri),
+    '#markup' => drupal_render($preview),
+  );
+
+  // These will get passed on to WYSIWYG
+  $form['options'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('options'),
+  );
+
+  $form['options']['format'] = array(
+    '#type' => 'select',
+    '#title' => t('Current format is'),
+    '#options' => $options,
+    '#default_value' => $default_view_mode
+  );
+
+  // Similar to a form_alter, but we want this to run first so that media.types.inc
+  // can add the fields specific to a given type (like alt tags on media).
+  // If implemented as an alter, this might not happen, making other alters not
+  // be able to work on those fields.
+  // @TODO: We need to pass in existing values for those attributes.
+  drupal_alter('media_format_form_prepare', $form, $form_state, $media);
+
+  if (!element_children($form['options'])) {
+    $form['options']['#attributes'] = array('style' => 'display:none');
+  }
+
+  return $form;
+}
+
+
+
+/**
+ * Wrapper around field_view_field, returns the file field w/o a label
+ *
+ * @param Object $media
+ * @param string $view_mode
+ * @param array $settings
+ *  Any attribute overrides to pass to the style formatter.
+ * @return array
+ *  drupal_renderable array
+ */
+function media_get_file_without_label($media, $view_mode, $settings = array()) {
+  $instance = field_info_instance('media', 'file', $media->type);
+  $format = field_get_display($instance, $view_mode, $media);
+  $format['label'] = 'hidden';
+  $format['settings'] = array_merge($format['settings'], $settings);
+  // These "overrides" are handled by the file_styles module.
+  $media->override = $settings;
+  return field_view_field('media', $media, 'file', $format);
+}
+
+/**
+ * Clears caches that may be affected by the media filter.
+ *
+ * The media filter calls media_load(). This means that if a media object
+ * is updated, the check_markup() and field caches could return stale content.
+ * There are several possible approaches to deal with this:
+ *  - Disable filter caching in media_filter_info(), this was found to cause a
+ *    30% performance hit from profiling four node teasers, due to both the
+ *    media filter itself, and other filters that can't be cached.
+ *  - Clear the filter and field caches whenever any media node is updated, this
+ *    would ensure cache coherency but would reduce the effectiveness of those
+ *    caches on high traffic sites with lots of media content updates.
+ *  - The approach taken here: Record the fid of all media objects that are
+ *    referenced by the media filter. Only clear the filter and field caches
+ *    when one of these is updated, as opposed to all media objects.
+ *  - @todo: consider an EntityFieldQuery to limit cache clearing to only those
+ *    entities that use a text format with the media filter, possibly checking
+ *    the contents of those fields to further limit this to fields referencing
+ *    the media object being updated. This would need to be implemented
+ *    carefully to avoid scalability issues with large result sets, and may
+ *    not be worth the effort.
+ *
+ * @param $fid
+ *   Optional media fid being updated. If not given, the cache will be cleared
+ *   as long as any file is referenced.
+ */
+function media_filter_invalidate_caches($fid = FALSE) {
+  // If fid is passed, confirm that it has previously been referenced by the
+  // media filter. If not, clear the cache if the {media_filter_usage} has any
+  // valid records.
+  if (($fid && db_query('SELECT fid FROM {media_filter_usage} WHERE fid = :fid', array(':fid' => $fid))->fetchField()) || (!$fid && media_filter_usage_has_records())) {
+    // @todo: support entity cache, either via a hook, or using module_exists().
+    cache_clear_all('*', 'cache_filter', TRUE);
+    cache_clear_all('*', 'cache_field', TRUE);
+  }
+}
+
+/**
+ * Determines if the {media_filter_usage} table has any entries.
+ */
+function media_filter_usage_has_records() {
+  return  (bool) db_query_range('SELECT 1 FROM {media_filter_usage} WHERE fid > :fid', 0, 1, array(':fid' => 0))->fetchField();
+}
+
+/**
+ * Tracks usage of media fids by the media filter.
+ *
+ * @param $fid
+ *   The media fid.
+ */
+function media_filter_track_usage($fid) {
+  // This function only tracks when fids are found by the media filter.
+  // It would be impractical to check when formatted text is edited to remove
+  // references to fids, however by keeping a timestamp, we can implement
+  // rudimentary garbage collection in hook_flush_caches().
+  // However we only need to track that an fid has ever been referenced,
+  // not every time, so avoid updating this table more than once per month,
+  // per fid.
+  $timestamp = db_query('SELECT timestamp FROM {media_filter_usage} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
+  if (!$timestamp || $timestamp <= REQUEST_TIME - 86400 * 30) {
+    db_merge('media_filter_usage')->key(array('fid' => $fid))->fields(array('fid' => $fid, 'timestamp' => REQUEST_TIME))->execute();
+  }
+}
diff --git a/includes/media.pages.inc b/includes/media.pages.inc
new file mode 100644
index 0000000..c005a2e
--- /dev/null
+++ b/includes/media.pages.inc
@@ -0,0 +1,432 @@
+<?php
+
+/**
+ * @file
+ * Common pages for the Media module.
+ */
+
+/**
+ * Menu callback; view a single piece of media.
+ */
+function media_file_view($media) {
+  // @todo Implement granular editorial access: http://drupal.org/node/696970.
+  //   In the meantime, protect information about private files from being
+  //   discovered by unprivileged users. Media IDs are autoincrement, so one can
+  //   attempt discovery by trying to access different media/ID paths. See also
+  //   media_browser_list(). This logic potentially belongs within
+  //   media_access(), but that would require extending that function's
+  //   signature to accept a $media paramter, and this is temporary code anyway.
+  if (!user_access('administer media') && (file_uri_scheme($media->uri) === 'private')) {
+    return MENU_ACCESS_DENIED;
+  }
+
+  drupal_set_title($media->filename);
+
+  // @TODO maybe something more elegant... ?
+  field_attach_prepare_view('media', array($media->fid => $media), 'media_original');
+  $build['media'][$media->fid] = field_attach_view('media', $media, 'media_original');
+
+  return $build;
+}
+
+/**
+ *  Menu callback; display a thumbnail for an AJAX preview.
+ */
+function media_preview_ajax() {
+  $url = $_GET['url'];
+  $uri = media_parse_to_uri($url);
+  if ($uri) {
+    $media = file_uri_to_object($uri);
+    drupal_json_output(array(
+      'preview' => theme('media_admin_thumbnail', array('file' => $media)),
+    ));
+  }
+  else {
+    drupal_json_output(array(
+      'error' => t('Invalid URL.'),
+    ));
+  }
+  die();
+}
+
+/**
+ * Menu callback; presents the Media editing form.
+ */
+function media_page_edit($media) {
+  drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => $media->type, '@title' => $media->filename)), PASS_THROUGH);
+  return drupal_get_form('media_edit', $media);
+}
+
+/**
+ * Menu callback; presents the Media editing form for multiple items.
+ */
+function media_page_multiedit($media_items) {
+  if (!module_exists('multiform')) {
+    drupal_set_message('To edit multiple media items, you must install the multiform module.');
+  }
+  $i = 0;
+  foreach ($media_items as $item) {
+    // To maintain unique form_ids, increment this counter.
+    // @see media_forms().
+    $i++;
+    $forms[] = array("media_edit_$i", $item);
+  }
+  
+  $form = call_user_func_array('multiform_get_form', $forms);
+  $form['#attributes']['class'][] = 'media-multiedit-form';
+  
+  unset($form['buttons']['Delete']);
+  // Would be nice to add this to show a message, but not working.
+  // Can debug.
+  //$form['buttons']['Save']['#submit'][] = 'media_page_multiedit_submit';
+  
+  drupal_set_title(t('Editing multiple media files'));
+  return $form;
+}
+
+/**
+ * Menu callback; shows delete confirmation form.
+ */
+function media_page_delete($media) {
+  drupal_set_title(t('<em>Delete @type</em> @title', array('@type' => $media->type, '@title' => $media->filename)), PASS_THROUGH);
+  // Don't bother showing the form if the item is in use, since we won't allow
+  // them to delete it anyway. Note that file_usage_list() requires a stdClass,
+  // so we can't pass the $media object in directly.
+  $file = file_load($media->fid);
+  $references = file_usage_list($file);
+  if (!empty($references)) {
+    return t('The file %title is in use and cannot be deleted.', array('%title' => $media->filename));
+  }
+  else {
+    $files = array($media->fid => $media);
+    return drupal_get_form('media_multiple_delete_confirm', $files, '<front>', 'media/' . $media->fid);
+  }
+}
+
+/**
+ * Confirm the request to delete files.
+ */
+function media_multiple_delete_confirm($form, &$form_state, $files, $redirect_on_success = NULL, $redirect_on_cancel = NULL) {
+  $form['files'] = array('#tree' => TRUE);
+  $form['file_titles'] = array('#theme' => 'item_list');
+  foreach ($files as $fid => $value) {
+    $title = db_query('SELECT filename FROM {file_managed} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
+    $form['files'][$fid] = array(
+      '#type' => 'value',
+      '#value' => $fid,
+    );
+    $form['file_titles']['#items'][] = check_plain($title);
+  }
+  $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
+  if (isset($redirect_on_success)) {
+    $form['redirect_on_success'] = array(
+      '#type' => 'value',
+      '#value' => $redirect_on_success,
+    );
+  }
+  $form['#submit'][] = 'media_multiple_delete_confirm_submit';
+
+  $confirm_question = format_plural(count($files),
+                                  'Are you sure you want to delete this item?',
+                                  'Are you sure you want to delete these items?');
+
+  return confirm_form($form,
+    $confirm_question,
+    isset($redirect_on_cancel) ? $redirect_on_cancel : current_path(),
+    t('This action cannot be undone.'),
+    t('Delete'),
+    t('Cancel'));
+}
+
+/**
+ * Attempt to delete files and notify the user of the result.
+ */
+function media_multiple_delete_confirm_submit($form, &$form_state) {
+  if ($form_state['values']['confirm']) {
+    $results = array();
+    $files = array_keys($form_state['values']['files']);
+    foreach ($files as $fid) {
+      $file = file_load($fid);
+      $files[$fid] = $file;
+      $results[$fid] = file_delete($file);
+    }
+    // The result of file_delete can be an array if the file is in use, or TRUE/FALSE.
+    foreach ($results as $fid => $result) {
+      if (is_array($result)) {
+        drupal_set_message(t('The file @title is in use and cannot be deleted.', array('@title' => $files[$fid]->filename)), 'warning');
+      }
+      elseif (!$result) {
+        drupal_set_message(t('The file @title was not deleted due to an error.', array('@title' => $files[$fid]->filename)), 'error');
+      }
+      else {
+        $message = t('File @title was deleted', array('@title' => $files[$fid]->filename));
+        watchdog('media', $message);
+        drupal_set_message($message);
+      }
+    }
+    if (isset($form_state['values']['redirect_on_success'])) {
+      $form_state['redirect'] = $form_state['values']['redirect_on_success'];
+    }
+  }
+}
+
+
+/**
+ * Form callback for adding media via an upload form.
+ * @todo: should use the AJAX uploader
+ */
+function media_add_upload($form, &$form_state, $types = NULL) {
+
+  $validators = array(
+   'file_validate_extensions' => array(media_variable_get('file_extensions')),
+   'file_validate_size' => array(parse_size(media_variable_get('max_filesize'))),
+  );
+
+  //@todo add additional validators for media types allowed.
+  if ($types) {
+    $validators['media_file_validate_types'] = array($types);
+  }
+  
+  // A blank set of allowed file extensions means no need to validate.
+  if (!$validators['file_validate_extensions'][0]) {
+    unset($validators['file_validate_extensions']);
+  }
+  // Use the PHP limit for filesize if no variable was set.
+  if (!$validators['file_validate_size']) {
+    $validators['file_validate_size'] = file_upload_max_size();
+  }
+
+  if ($validators['file_validate_size'][0] == 0) {
+    unset($validators['file_validate_size']);
+  }
+
+  $form['#validators'] = $validators;
+  
+  $form['upload'] = array(
+    '#type' => 'file',
+    '#title' => t('Upload a new file'),
+    '#description' => theme('file_upload_help', array('description' => '', 'upload_validators' => $validators)),
+    '#upload_validators' => $validators,
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Submit'),
+  );
+
+  return $form;
+}
+
+/**
+ * Validate the generic file upload with the global media settings.
+ */
+function media_add_upload_validate($form, &$form_state) {
+  // Save the file as a temporary file.
+  $file = file_save_upload('upload', $form['#validators']);
+  
+  if ($file === NULL) {
+    form_set_error('upload', t("No file appears to have been selected."));
+  }
+  elseif ($file === FALSE) {
+    form_set_error('upload', t("Failed to upload the file."));
+  }
+  else {
+    $form_state['values']['upload'] = $file;
+  }
+}
+
+/**
+ * Upload a file.
+ */
+function media_add_upload_submit($form, &$form_state) {
+  $scheme = variable_get('file_default_scheme', 'public') . '://';
+  $file = $form_state['values']['upload'];
+  _media_save_file_permenently($file);
+
+  $destination = file_stream_wrapper_uri_normalize($scheme . $file->filename);
+
+  $defaults = array (
+    'display' => TRUE,
+  );
+
+  // Save the uploaded file.
+  $file = file_move($file, $destination, FILE_EXISTS_RENAME);
+
+  if ($file) {
+    drupal_set_message(t('The file @name was uploaded', array('@name' => $file->filename)));
+  }
+  else {
+    drupal_set_message(t('An error occurred and no file was uploaded.'), 'error');
+    return;
+  }
+
+  $form_state['redirect'] = array('media/browser', array('query' => array('render' => 'media-popup', 'fid' => $file->fid)));
+}
+
+function media_add_upload_multiple($form, &$form_state, $types) {
+  $form = media_add_upload($form, $form_state, $types);
+  unset($form['upload']['#title']);
+  // The validators will be set from plupload anyway.  This isn't pretty, but don't
+  // it to show up twice.
+  unset($form['upload']['#description']);
+  
+  $form['upload']['#type'] = 'plupload';
+  $form['submit']['#value'] = t('Start upload');
+  return $form;
+}
+
+function media_add_upload_multiple_submit($form, &$form_state) {
+  $scheme = variable_get('file_default_scheme', 'public') . '://';
+  $saved_files = array();
+  // We can't use file_save_upload() because of http://www.jacobsingh.name/content/tight-coupling-no-not
+  foreach ($form_state['values']['upload'] as $uploaded_file) {
+    if ($uploaded_file['status'] == 'done') {
+      $source = $uploaded_file['tmppath'];
+      $destination = file_stream_wrapper_uri_normalize($scheme . $uploaded_file['name']);
+      // Rename it to its original name, and put it in its final home.
+      // Note - not using file_move here because if we call file_get_mime
+      // (in file_uri_to_object) while it has a .tmp extension, it horks.
+      
+      $destination = file_unmanaged_move($source, $destination, FILE_EXISTS_RENAME);
+      
+      $file = file_uri_to_object($destination);
+      file_save($file);
+      _media_save_file_permenently($file);
+      
+      $saved_files[] = $file;
+    }
+    else {
+      // @todo: move this to element validate or something and clean up t().
+      form_set_error('pud', "Upload of {$uploaded_file['name']} failed");
+    }
+  }
+  // Get a list of fids to pass back.
+  $fids = array();
+  foreach ($saved_files as $file) {
+    $fids[] = $file->fid;
+  }
+  $form_state['redirect'] = array('media/browser', array('query' => array('render' => 'media-popup', 'fid' => $fids)));
+}
+
+/**
+ * Form builder: Builds the edit file form.
+ */
+function media_edit($form, $form_state, $media) {
+  field_attach_form('media', $media, $form, $form_state);
+  unset($form['file']);
+
+  $form['#attached'] = array(
+    'css' => array(drupal_get_path('module', 'media') . '/css/media.css'),
+  );
+
+  // Not sure about this class name, seems to indicate the style.
+  $form['#attributes']['class'][] = 'media-image-left';
+  $form['#attributes']['class'][] = 'media-edit-form';
+  
+  $form['preview'] = field_view_field('media', $media, 'file', 'media_preview');
+  $form['preview']['#weight'] = -10;
+  $form['preview']['#suffix'] = '<div class="no-overflow">';
+
+  // Add the buttons.
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['#prefix'] = '</div>';
+  $form['actions']['delete'] = array(
+    '#type' => 'submit',
+    '#value' => t('Delete'),
+    '#weight' => 15,
+    '#submit' => array('media_edit_delete_submit'),
+  );
+  
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#weight' => 5,
+    '#submit' => array('media_edit_submit'),
+  );
+
+  $form['fid'] = array(
+    '#type' => 'hidden',
+    '#value' => $media->fid,
+  );
+
+  return $form;
+}
+
+function media_edit_validate($form, &$form_state) {
+  $media = media_load($form_state['values']['fid']);
+  field_attach_form_validate('media', $media, $form, $form_state);
+}
+
+/**
+ * Process media_edit form submissions.
+ */
+function media_edit_submit($form, &$form_state) {
+  $media = media_load($form_state['values']['fid']);
+  field_attach_submit('media', $media, $form, $form_state);
+  media_save($media);
+  $form_state['redirect'] = 'media/' . $media->fid;
+}
+
+function media_edit_delete_submit($form, &$form_state) {
+  $fid = $form_state['values']['fid'];
+  $destination = array();
+  if (isset($_GET['destination'])) {
+    $destination = drupal_get_destination();
+    unset($_GET['destination']);
+  }
+  $form_state['redirect'] = array('media/' . $fid . '/delete', array('query' => $destination));
+}
+
+function media_add_remote($form, &$form_state) {
+  // Totally prototyping code to show designs.
+  $form['sources'] = array(
+    '#type' => 'vertical_tabs',
+    '#title' => 'Sources',
+  );
+
+  $form['sources']['paste'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Paste URL or embed code',
+  );
+
+
+  $providers = '';
+  $providers .= '<img style="height:50px; margin:20px" src="http://www.macuser.com/2008/10/09/top_youtube_logo_31_Dec_06.jpg">';
+  $providers .= '<img style="height:50px; margin:20px" src="http://jasonhilldesign.com/FlikrLogo.jpg">';
+  $form['sources']['paste']['code'] = array(
+    '#type' => 'textarea',
+    '#title' => t('URL or embed code'),
+    '#description' => t('The following providers are supported: <br/>' . $providers),
+  );
+
+  $form['sources']['youtube'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'YouTube',
+    '#description' => t(''),
+    '#attributes' => array('style' => 'height: 300px; overflow:auto'),
+  );
+
+  $form['sources']['flikr'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Flikr',
+  );
+
+  $box = '<div style="width:100px; height:100px; border:1px solid blue; padding:10px; float:left; margin:5px;"> Video </div>';
+  $boxes = '';
+  for ($i = 0; $i < 10; $i++) {
+    $boxes .= $box;
+  }
+  $form['sources']['youtube']['stupid'] = array(
+    '#markup' => $boxes,
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Import'),
+    '#attributes' => array('style' => 'float:right'),
+    '#suffix' => '<br style="clear:both" />',
+  );
+
+  return $form;
+}
+
diff --git a/includes/media.theme.inc b/includes/media.theme.inc
new file mode 100644
index 0000000..1b5f373
--- /dev/null
+++ b/includes/media.theme.inc
@@ -0,0 +1,324 @@
+<?php
+
+/**
+ * @file
+ * Media Theming
+ *
+ * Theming functions for the Media module.
+ */
+
+/**
+ * Display the media file browser.
+ * @TODO this is depreciated I think
+ * @param array $element
+ *   The form element.
+ * @return string
+ */
+function theme_media_file_browser($element) {
+  // Add the CSS for our display.
+  $output = '<div class="media browser">' . $element . '</div>';
+
+  return $output;
+}
+
+/**
+ * Display a media file list.
+ * @TODO this is depreciated I think
+ * @param array $element
+ *   The form element.
+ * @return string
+ */
+function theme_media_file_list($element) {
+  // Add the CSS for our display.
+  return '<div class="media-file-list">' . theme('form_element', $element, $element['#children']) . '</div>';
+}
+
+/**
+ * Display a browser pane.
+ * @TODO this is depreciated I think
+ *
+ * @param array $form
+ *   The form element.
+ * @return string
+ */
+function theme_media_browser_pane($form) {
+  return;
+  $output   = array();
+
+  // render the drawers
+  $output[] = '<div' . drupal_attributes($form['#attributes']) . '>';
+
+  // render the drawer list
+  $output[] = '  <div class="browser drawers">';
+  $output[] = drupal_render_form(null, $form['drawers']);
+  $output[] = ' </div>';
+
+  // render the drawer displays
+  $output[] = drupal_render_form(null, $form);
+  $output[] = '</div>';
+
+  return implode("\n", $output);
+}
+
+
+/**
+ * Default theming function for creating the browser frame.
+ * Assumes an array of file objects as $files and an
+ * array of $parameters
+ * @param $variables
+ *   array of variables
+ * @return unknown_type
+ */
+function theme_media_browser_content_frame($variables) {
+  // Pull out all the variables into a usable form
+  extract($variables);
+  // Did we get any files back?
+  if (! count($files)) {
+    // @TODO display no files found
+  }
+
+  $html = array();
+
+  // On the first invocation, load javascript and build the browser frame
+   if ($invoke) {
+
+
+  }
+   // Render the results limiter
+  $html[] = theme('media_browser_control_result_limit', array('parameters' => $parameters));
+  // Render the actual content
+  $form = drupal_get_form('media_file_listing_form', $files, $parameters);
+  $html[] = drupal_render($form);
+
+  // Make sure to close the wrapping div
+  if ($invoke) {
+    $html[] = '</div>';
+  }
+  return implode("\n", $html);
+}
+
+
+
+/**
+ * Display a item list of files as thumbnails. Implements
+ * the admin thumbnail theme for now- serves as a wrapper
+ *
+ *  @param $files
+ *    An array of file objects to display.
+ *  @return
+ */
+function theme_media_browser_thumbnails($variables) {
+  $files = $variables['files'];
+  $style_name = $variables['style_name'];
+  $thumbnails = array();
+  foreach ($files as $file) {
+    $thumbnails[] = theme('media_admin_thumbnail', array('file' => $file, 'style_name' => $style_name));
+  }
+  return theme('item_list', array('items' => $thumbnails, 'attributes' => array('class' => 'media_content_navigator results')));
+}
+
+
+/**
+ * Theme a thumbnail.
+ * @param $variables
+ *   array items being passed in
+ */
+function theme_media_admin_thumbnail($variables) {
+  $path = drupal_get_path('module', 'media');
+  $file = $variables['file'];
+  $style_name = $variables['style_name'];
+  if (isset($file)) {
+    $file_url = file_create_url($file->uri);
+  }
+  else {
+    return '';
+  }
+  $output = '';
+  if (module_exists('styles')) {
+    $thumbnail = theme('styles',
+      array(
+        'field_type' => 'file',
+        'style_name' => $style_name,
+        'uri' => $file->uri,
+        'description' => t('Thumbnail for !filename.', array('!filename' => $file->filename)),
+        'object' => $variables['file'],
+    ));
+  }
+  else {
+    // Display a thumbnail for images.
+    if (strstr($file->filemime, 'image')) {
+      $thumbnail = theme('image_style',
+        array(
+          'style_name' => 'thumbnail',
+          'path' => $file->uri,
+          'alt' => t('Thumbnail for !filename.', array('!filename' => $file->filename)),
+        )
+      );
+    }
+    // Display the 'unknown' icon for other file types.
+    else {
+      $thumbnail = theme('image',
+        array(
+          'path' => $path . '/images/file-unknown.png',
+          'alt' => t('Thumbnail for !filename.', array('!filename' => $file->filename)),
+          'attributes' => array('class' => 'file-unknown'),
+        ));
+    }
+  }
+  $output .= l($thumbnail,
+    $file_url,
+    array(
+      'html' => TRUE,
+      'attributes' => array('class' => 'media-thumbnail'),
+    ));
+  return $output;
+}
+
+
+/**
+ * Theme operations for a thumbnail.
+ */
+function theme_media_admin_thumbnail_operations($variables) {
+  $destination = drupal_get_destination();
+  $file = $variables['file'];
+  $output = l(t('edit'), 'media/' . $file->fid . '/edit', array('query' => $destination));
+  return $output;
+}
+
+/**
+ * Add messages to the page.
+ */
+function template_preprocess_media_dialog_page(&$variables) {
+  $variables['messages'] = theme('status_messages');
+}
+
+/* ******************************************** */
+/* Content navigation controls                  */
+/* ******************************************** */
+
+
+/**
+ * Theme function to display the results limiting- 10, 30, 50 results
+ * per page.
+ *
+ * @param $variables
+ *   array parameters
+ * @return unknown
+ */
+function theme_media_browser_control_result_limit($variables) {
+  // Pull out all the variables into a usable form
+  extract($variables);
+
+  if (! isset($limits)) {
+    $limits = array(10, 30, 50);
+  }
+  // @NOTE these do not need to be aware of the current
+  //       page because clicking them will reset the
+  //       display to 1 -> $limit
+  $parameters['page'] = 0;
+  // save the active limit
+  $current_limit = $parameters['limit'];
+  foreach ($limits as $limit) {
+    if ($limit == $current_limit) {
+      $class = 'active';
+    }
+    else {
+      $class = '';
+    }
+    // set the value of this limit parameter to this limit value
+    $parameters['limit'] = $limit;
+    $per_display[] = l($limit, $limit, array('query' => $parameters, 'attributes' => array('class' => $class)));
+  }
+
+  return theme('item_list', array('items'  => $per_display, 'attributes' => array('class' => 'result_limit')));
+}
+
+
+/**
+ * Stolen from file.module theme_file_link
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - file: A file object to which the link will be created.
+ */
+function theme_media_link($variables) {
+  $file = $variables['file'];
+
+  $url = 'media/' . $file->fid;
+  $icon = theme('file_icon', array('file' => $file));
+
+  // Set options as per anchor format described at
+  // http://microformats.org/wiki/file-format-examples
+  $options = array(
+    'attributes' => array(
+      'type' => $file->filemime . '; length=' . $file->filesize,
+    ),
+  );
+
+  // Use the description as the link text if available.
+  if (empty($file->description)) {
+    $link_text = check_plain($file->filename);
+  }
+  else {
+    $link_text = check_plain($file->description);
+    $options['attributes']['title'] = check_plain($file->filename);
+  }
+
+  return '<span class="file">' . $icon . ' ' . l($link_text, $url, $options) . '</span>';
+}
+
+/**
+ * Adds a wrapper around a preview of a media file.
+ * @param unknown_type $element
+ * @return unknown_type
+ */
+function theme_media_thumbnail($variables) {
+  $label = '';
+  $element = $variables['element'];
+  $destination = drupal_get_destination();
+
+  // Wrappers to go around the thumbnail
+  $prefix = '<div class="media-item"><div class="media-thumbnail">';
+  $suffix = '</div></div>';
+
+  // Arguments for the thumbnail link
+  $thumb = $element['#children'];
+  $target = 'media/' . $element['#object']->fid . '/edit';
+  $options = array('query' => $destination, 'html' => true, 'attributes' => array('title' => t('Click to edit details')));
+
+  // Element should be a field renderable array... This is a little wonky - admitted.
+  if (!empty($element['#show_names']) && $element['#name']) {
+    $label = '<div class="label-wrapper"><label class="media-filename">' . $element['#name'] . '</label></div>';
+  }
+
+  // How can I attach CSS here?
+  //$element['#attached']['css'][] = drupal_get_path('module', 'media') . '/css/media.css';
+  drupal_add_css(drupal_get_path('module', 'media') . '/css/media.css');
+
+  $output = $prefix;
+  if (!empty($element['#add_link'])) {
+    $output .= l($thumb, $target, $options);
+  }
+  else {
+    $output .= $thumb;
+  }
+  $output .= $label . $suffix;
+  return $output;
+}
+
+function template_preprocess_media_thumbnail(&$variables) {
+  // Set the name for the thumbnail to be the filename.  This is done here so
+  // that other modules can hijack the name displayed if it should not be the
+  // filename.
+  $variables['element']['#name'] = isset($variables['element']['#object']->filename) ? $variables['element']['#object']->filename : NULL;
+}
+
+/**
+ * Field formatter for displaying a file as a large icon.
+ */
+function theme_media_formatter_large_icon($variables) {
+  $file = $variables['file'];
+  $icon_dir = media_variable_get('icon_base_directory') . '/' . media_variable_get('icon_set');
+  $icon = file_icon_path((object)$file, $icon_dir);
+  return theme('image', array('path' => $icon));
+}
diff --git a/includes/media.types.inc b/includes/media.types.inc
new file mode 100644
index 0000000..8f7a813
--- /dev/null
+++ b/includes/media.types.inc
@@ -0,0 +1,323 @@
+<?php
+
+/**
+ *  @file
+ *  Helper functions related to media types.  CRUD for saving their settings mainly.
+ *
+ *  Also contains the media entity class definition.
+ *  @see media.install for a list of the base types.
+ */
+
+/**
+ *  Update an existing media type or create a new one.
+ *
+ *  The default media types are currently 'Audio', 'Image', 'Video', and
+ *  'Other', which are defined in media_install().
+ *
+ *  @param object &$type
+ *    $type is an object with the following fields:
+ *      ->name => The name of the media asset type, such as 'video';
+ *      ->label => The human readable name;
+ *      ->base => boolean: If the media type cannot be removed.
+ *      ->type_callback => Function call to filter an instance to its bundle.
+ *      ->type_callback_args => An array of args to be passed to type_callback.
+ *  @return void;
+ */
+function media_type_save(&$type) {
+  if (empty($type->name)) {
+    throw new Exception('Enable to add type, name not provided');
+  }
+
+  $type = media_type_set_defaults($type);
+  if (!is_array($type->type_callback_args)) {
+    throw new Exception('type_callback_args should be an array');
+  }
+
+  $type->type_callback_args = serialize($type->type_callback_args);
+
+  $ret = db_merge('media_type')
+    ->key(array('name' => $type->name))
+    ->fields((array)$type)
+  ->execute();
+
+  media_type_configure_fields($type);
+
+  // Clear the caches
+  drupal_static_reset('media_type_get_types');
+  drupal_static_reset('media_type_get_mime_map');
+  return;
+}
+
+/**
+ * Assigns formatters to view_modes for the file field of a media type.
+ *
+ * Each media type contains a field called "file" and certain stock view modes.
+ * This function will assign a format to each view mode. For instance, images
+ * would want to use a thumbnail format for files when in the "preview"
+ * view_mode.  The called would pass in 'image' for $name and
+ * array('media_preview' => 'styles_file_square_thumbnail') to $view_modes_to_formatters.
+ *
+ * This will also set the "custom_settings" bit to make sure it actually takes effect.
+ *
+ * @param string $name
+ * @param array $view_modes_to_formatters
+ * @throws Exception
+ * @return void
+ */
+function media_type_configure_formatters($name, $view_modes_to_formatters) {
+  $instance = field_info_instance('media', 'file', $name);
+  $bundle_settings = field_bundle_settings('media', $name);
+
+  if (!$instance) {
+    throw new Exception('Unable to set formatter preferences for '. $name);
+  }
+  foreach ($view_modes_to_formatters as $view_mode => $formatter) {
+    $bundle_settings['view_modes'][$view_mode]['custom_settings'] = 1;
+    if (!isset($instance['display'][$view_mode])) {
+      $instance['display'][$view_mode] = $instance['display']['default'];
+    }
+    $instance['display'][$view_mode]['type'] = $formatter;
+  }
+  field_bundle_settings('media', $name, $bundle_settings);
+  field_update_instance($instance);
+}
+
+/**
+ * Loads a media type based on its machine name.
+ *
+ * @param string $name
+ * @return StdClass
+ */
+function media_type_load($name) {
+  $types = media_type_get_types();
+  if (isset($types[$name])) {
+    return $types[$name];
+  }
+}
+
+/**
+ * Sets up the default fields which a media type needs.
+ *
+ * Currently, this is just a "file" field which contains the reference to the actual file.
+ * @TODO Do we also want to make the file 'description/caption' a field?
+ *
+ * @param StdClass $media_type
+ * @return void
+ */
+function media_type_configure_fields($media_type) {
+  $field = field_info_field('file');
+  $instance = field_info_instance('media', 'file', $media_type->name);
+
+  if (empty($field)) {
+    $field = array(
+      'field_name' => 'file',
+      'type' => 'file',
+    );
+    $field = field_create_field($field);
+  }
+
+  // Perhaps the file field exists already, if so skip.
+  if (empty($instance)) {
+    $weight = -5;
+    $instance = array(
+      'field_name' => 'file',
+      'entity_type' => 'media',
+      'bundle' => $media_type->name,
+      'label' => 'File',
+      'widget_type' => 'file_file',
+      'widget' => array(
+        'weight' => $weight,
+      ),
+      'required' => TRUE,
+      'locked' => TRUE,
+      'display' => array(
+        'media_preview' => array(
+          'label' => 'hidden',
+          'type' => 'file_default',
+          'weight' => $weight,
+        ),
+        'media_original' => array(
+          'label' => 'hidden',
+          'type' => 'file_default',
+          'weight' => $weight,
+        ),
+      ),
+    );
+    field_create_instance($instance);
+  }
+}
+
+/**
+ *  Loads all media types into an array keyed by machine name and sorted
+ *  and weighted lexographically.
+ *
+ * @return array
+ *  Media types keyed by machine name.
+ */
+function media_type_get_types() {
+  $types =& drupal_static(__FUNCTION__);
+  if (!$types) {
+    $types = db_select('media_type', 'mt')
+      ->orderBy('weight')
+      ->fields('mt')
+      ->execute()
+      ->fetchAllAssoc('name'); // Will key by the name field.
+    foreach ($types as &$type) {
+      // I really hate this.
+      $type->type_callback_args = unserialize($type->type_callback_args);
+    }
+  }
+
+  return $types;
+}
+
+/**
+ *  Create the basic class and defaults for a media entity bundle type.
+ */
+function media_type_set_defaults($info) {
+  $type = new StdClass();
+
+  // This is used to filter a file to the proper bundle.
+  $type->type_callback = 'media_is_type';
+  $type->type_callback_args = array();
+  $type->weight = 0;
+
+  foreach ($info as $k => $v) {
+    $type->{$k} = $v;
+  }
+
+  return $type;
+}
+
+/**
+ * Determines the type of media a passed in $file is.
+ *
+ * @todo: integrate this properly with other APIs in media when fields is done
+ * @param unknown_type $file
+ * @return unknown_type
+ */
+function media_get_type($file) {
+  $types = media_type_get_types();
+  foreach ($types as $name => $type) {
+    if (call_user_func_array($type->type_callback, array($file, $type->type_callback_args))) {
+      return $name;
+    }
+  }
+  throw new Exception('Unable to determine type of media from ' . var_export($file, 1));
+}
+
+/**
+ * Default callback used to determine if a piece of media is of a given type.
+ *
+ * @TODO: document 'any' and 'all' matching.
+ *
+ * @param $media
+ *   The media file asset object.
+ * @param $args
+ *
+ * @return unknown_type
+ */
+function media_is_type($media, $args) {
+  $match_type = !empty($args['match_type']) ? 'any' : $args['match_type'];
+  $match_all = $match_type == 'all';
+  if (!empty($args['mimetypes'])) {
+    foreach ($args['mimetypes'] as $expression) {
+      if (preg_match($expression, $media->filemime)) {
+        if (!$match_all) {
+          return TRUE;
+        }
+      }
+    }
+    // Was not matched, so return
+    if ($match_all) {
+      return FALSE;
+    }
+  }
+
+  if (!empty($args['extensions'])) {
+    if (in_array(pathinfo($media->uri, PATHINFO_EXTENSION), $args['extensions'])) {
+      if (!$match_all) {
+        return TRUE;
+      }
+    }
+    // Was not matched, so return
+    if ($match_all) {
+      return FALSE;
+    }
+  }
+
+  if (!empty($args['streams'])) {
+  }
+}
+
+/**
+ * Implement hook_media_format_form_prepare_alter
+ */
+function media_media_format_form_prepare_alter(&$form, &$form_state, $media) {
+  switch($media->type) {
+    case 'image':
+      // @TODO: Isn't there a $file->description?
+      $description = $media->filename;
+      $form['options']['alt'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Description'),
+        '#default_value' => $description,
+        '#description' => t('Alternate text a user will see if the image is not available'),
+      );
+      break;
+  }
+}
+
+/**
+ * Returns the number of files that need to be converted to media.
+ */
+function media_type_invalid_files_count() {
+  return db_select('file_managed', 'fm')
+    ->condition('type', NULL)
+    ->countQuery()
+    ->execute()
+    ->fetchField();
+}
+
+/**
+ * Adds a value for the type column in files_managed.
+ *
+ * If $update_existing is TRUE, will update the type of files with an existing type value.
+ *
+ * @param boolean $update_existing
+ * @param integer $no_to_update
+ * @param integer $offset
+ *
+ * @return array
+ *  A list of updated file ids
+ */
+function media_type_batch_update($update_existing = FALSE, $no_to_update = NULL, $offset = 0) {
+  $results = array();
+  
+  $query = db_select('file_managed', 'fm')
+    ->fields('fm', array('fid'));
+
+  if (!$update_existing) {
+    $query->condition('type', NULL);
+  }
+
+  if ($no_to_update) {
+    $query->range($offset, $no_to_update);
+  }
+  elseif ($offset) {
+    $query->range($offset);
+  }
+
+  $fids = $query->execute()->fetchCol();
+  foreach ($fids as $fid) {
+    $file = file_load($fid);
+    if (!$file->fid) {
+      throw new Exception('Unable to continue, file was not loaded.');
+    }
+    $file->type = media_get_type($file);
+    file_save($file);
+    $results[] = $fid;
+  }
+
+  return $results;
+}
diff --git a/includes/media.xml.inc b/includes/media.xml.inc
new file mode 100644
index 0000000..a237a2b
--- /dev/null
+++ b/includes/media.xml.inc
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ *  @file
+ *  XML data retrieval and storage API for Media.
+ */
+
+/**
+ *  A wrapper around simplexml to retrieve a given XML file.
+ *
+ *  @param $url
+ *    The URL to the XML to retrieve.
+ *  @param $display_errors
+ *    Optional; if TRUE, then we'll display errors to the end user. They'll be
+ *    logged to the watchdog in any case.
+ *  @param $refresh
+ *    Optional; if TRUE, then we'll force a new load of the XML. Otherwise,
+ *    a cached version will be retrieved if possible.
+ *  @return
+ *    A fully populated object, or FALSE on an error.
+ */
+function _media_retrieve_xml($url, $display_errors = FALSE, $refresh = FALSE) {
+  $xmls = &drupal_static(__FUNCTION__, array());
+
+  // Return our cached XML if allowed, and it exists.
+  if (!$refresh && isset($xmls[$url])) {
+    return $xmls[$url];
+  }
+  else if (!$refresh && $cache = cache_get('media:xml:'. $url, 'cache_media_xml')) {
+    $xmls[$url] = $cache->data;
+    return $xmls[$url];
+  }
+
+  // Enable user error handling.
+  libxml_use_internal_errors(TRUE);
+
+  // Load the document
+  $xml = simplexml_load_file($url);
+
+  if (!$xml) {
+    foreach (libxml_get_errors() as $error) {
+      $message = 'Error retrieving XML from %url: %error';
+      $params = array('%url' => $url, '%error' => $error->message);
+      // Handle errors here.
+      if ($display_errors) {
+        drupal_set_message(t($message, $params), 'error');
+      }
+      watchdog('media', $message, $params, WATCHDOG_WARNING);
+    }
+
+    // Clear our error cache.
+    libxml_clear_errors();
+
+    // Set the static cache, but not Drupal's cache, so we can attempt to
+    // retrieve the file another time if possible.
+    $xmls[$url] = FALSE;
+  }
+  else {
+    $xmls[$url] = _media_unserialize_xml($xml);
+    cache_set('media:xml:'. $url, $xmls[$url], 'cache_media_xml', media_variable_get('xml_cache_expire', 3600));
+  }
+
+  return $xmls[$url];
+}
+
+/**
+ *  Recursively converts a SimpleXMLElement object into an array.
+ *  @param $xml
+ *    The original XML object.
+ */
+function _media_unserialize_xml($xml) {
+  if ($xml instanceof SimpleXMLElement) {
+    $xml = (array) $xml;
+  }
+  if (is_array($xml)) {
+    foreach ($xml as $key => $item) {
+      $xml[$key] = _media_unserialize_xml($item);
+    }
+  }
+  return $xml;
+}
diff --git a/javascript/media-admin.js b/javascript/media-admin.js
deleted file mode 100644
index 8716f7b..0000000
--- a/javascript/media-admin.js
+++ /dev/null
@@ -1,142 +0,0 @@
-
-/**
- * @file
- * Javascript for the interface at admin/content/media and also for interfaces
- * related to setting up media fields and for media type administration.
- *
- * Basically, if it's on the /admin path, it's probably here.
- */
-
-(function ($) {
-
-/**
- * Functionality for the thumbnail display
- */
-Drupal.behaviors.mediaAdmin = {
-  attach: function (context) {
-    // Configure the "Add file" link to fire the media browser popup.
-    $('ul.action-links li', context).remove();
-    if ($('form.media-list-operation', context).length != 0) {
-      return;  
-    }
-    var $launcherLink = $('<a class="media-launcher" href="#"></a>').html('Add file');
-    $launcherLink.bind('click', function () {
-      // This option format needs *serious* work.
-      // Not even bothering documenting it because it needs to be thrown.
-      // See media.browser.js and media.browser.inc - media_browser()
-      // For how it gets passed.
-      var options = {
-        disabledPlugins: ['library'],
-        multiselect: true
-      };
-      Drupal.media.popups.mediaBrowser(function (mediaFiles) {
-        // When the media browser succeeds, we refresh
-        // @TODO: Should jump to the new media file and perhaps highlight it.
-        parent.window.location.reload();
-        return false;
-      }, options);
-    });
-    $('ul.action-links', context).append($('<li></li>').append($launcherLink));
-
-
-    if ($('body.page-admin-content-media-thumbnails').length != 0) {
-      // Implements 'select all/none' for thumbnail view.
-      // @TODO: Support grabbing more than one page of thumbnails.
-      var allLink = $('<a href="#">' + Drupal.t('all') + '</a>')
-        .click(function () {
-          $('.media-display-thumbnails', $(this).parents('form')).find(':checkbox').attr('checked', true).change();
-          return false;
-        });
-      var noneLink = $('<a href="#">' + Drupal.t('none') + '</a>')
-        .click(function () {
-          $('.media-display-thumbnails', $(this).parents('form')).find(':checkbox').attr('checked', false).change();
-          return false;
-        });
-      $('<div class="media-thumbnails-select" />')
-        .append('<strong>' + Drupal.t('Select') + ':</strong> ')
-        .append(allLink)
-        .append(', ')
-        .append(noneLink)
-        .prependTo('#media-admin > div')
-      // If the media item is clicked anywhere other than on the image itself
-      // check the checkbox. For the record, JS thinks this is wonky.
-      $('.media-item').bind('click', function (e) {
-        if ($(e.target).is('img, a')) {
-          return;
-        }
-        var checkbox = $(this).parent().find(':checkbox');
-        if (checkbox.is(':checked')) {
-          checkbox.attr('checked', false).change();
-        } else {
-          checkbox.attr('checked', true).change();
-        }
-      });
-  
-      // Add an extra class to selected thumbnails.
-      $('.media-display-thumbnails :checkbox').each(function () {
-        var checkbox = $(this);
-        if (checkbox.is(':checked')) {
-          $(checkbox.parents('li').find('.media-item')).addClass('selected');
-        }
-  
-        checkbox.bind('change.media', function () {
-          if (checkbox.is(':checked')) {
-            $(checkbox.parents('li').find('.media-item')).addClass('selected');
-          }
-          else {
-            $(checkbox.parents('li').find('.media-item')).removeClass('selected');
-          }
-        });
-      });
-    }
-
-    // When any checkboxes are clicked on this form check to see if any are checked.
-    // If any checkboxes are checked, show the edit options (@todo rename to edit-actions).
-    $('#media-admin :checkbox').bind('change', function () {
-      Drupal.behaviors.mediaAdmin.showOrHideEditOptions();
-    });
-
-    Drupal.behaviors.mediaAdmin.showOrHideEditOptions();
-  },
-
-  // Checks if any checkboxes on the form are checked, if so it will show the
-  // edit-options panel.
-  showOrHideEditOptions: function() {
-    var fieldset = $('#edit-options');
-    if (!$('#media-admin input[type=checkbox]:checked').size()) {
-      fieldset.slideUp('fast');
-    }
-    else {
-      fieldset.slideDown('fast');
-    }
-  }
-};
-
-
-/**
- * JavaScript for the Media types administrative form.
- */
-Drupal.behaviors.mediaTypesAdmin = {
-  attach: function (context) {
-    if ($('.form-item-match-type', context).length == 0) {
-      return;
-    }
-    // Toggle the 'other' text field on Match type.
-    if ($('.form-item-match-type input:checked').val() != '0') {
-      $('.form-item-match-type-other').hide();
-    }
-    $('.form-item-match-type input').change(function () {
-      if ($(this).val() == '0') {
-        $('.form-item-match-type-other').slideDown('fast');
-      }
-      else {
-        $('.form-item-match-type-other').slideUp('fast');
-      }
-    });
-  }
-};
-
-
-  
-})(jQuery);
-
diff --git a/javascript/media-browser.js b/javascript/media-browser.js
deleted file mode 100644
index 1b30b1d..0000000
--- a/javascript/media-browser.js
+++ /dev/null
@@ -1,100 +0,0 @@
-
-(function ($) {
-namespace('Drupal.media.browser');
-
-Drupal.media.browser.selectedMedia = [];
-Drupal.media.browser.mediaAdded = function () {};
-Drupal.media.browser.selectionFinalized = function (selectedMedia) {
-  // This is intended to be overridden if a callee wants to be triggered
-  // when the media selection is finalized from inside the browser.
-  // This is used for the file upload form for instance.
-};
-
-Drupal.behaviors.experimentalMediaBrowser = {
-  attach: function (context) {
-    if (Drupal.settings.media.selectedMedia) {
-      Drupal.media.browser.selectMedia(Drupal.settings.media.selectedMedia);
-      // Fire a confirmation of some sort.
-      Drupal.media.browser.finalizeSelection();
-    }
-    $('#media-browser-tabset').tabs({
-      show: Drupal.media.browser.resizeIframe
-    });
-
-    $('.media-browser-tab').each( Drupal.media.browser.validateButtons );
-
-  }
-  // Wait for additional params to be passed in.
-};
-
-Drupal.media.browser.launch = function () {
-
-};
-
-Drupal.media.browser.validateButtons = function() {
-  // The media browser runs in an IFRAME. The Drupal.media.popups.mediaBrowser()
-  // function sets up the IFRAME and "OK" and "Cancel" buttons that are outside
-  // of the IFRAME, so that their click handlers can destroy the IFRAME while
-  // retaining information about what media items were selected. However,
-  // Drupal UI convention is to place all action buttons on the same "line"
-  // at the bottom of the form, so if the form within the IFRAME contains a
-  // "Submit" button or other action buttons, then the "OK" and "Cancel"
-  // buttons below the IFRAME break this convention and confuse the user.
-  // Therefore, we add "Submit" and "Cancel" buttons inside the IFRAME, and
-  // have their click action trigger the click action of the corresonding
-  // "OK" and "Cancel" buttons that are outside the IFRAME. media.css contains
-  // CSS rules that hide the outside buttons.
-  //
-  // We don't add a "Submit" button if the form already has one, since in these
-  // cases, another round-trip to the server is needed before the user's
-  // selection is finalized. For these cases, when the form's real Submit
-  // button is clicked, the server either returns another form for the user to
-  // fill out, or else a completion page that contains or sets the
-  // Drupal.media.browser.selectedMedia variable. If the latter, then
-  // Drupal.media.popups.mediaBrowser.mediaBrowserOnLoad() auto-triggers the
-  // "OK" button action to finalize the selection and remove the IFRAME.
-  //
-  // @todo An alternate, less hacky solution would be most welcome.
-  if (!($('.form-submit', this).length > 0)) {
-    $('<a class="button fake-ok">Submit</a>').appendTo(this).bind('click', Drupal.media.browser.submit);
-    if (!($('.fake-cancel', this).length > 0)) {
-      $('<a class="button fake-cancel">Cancel</a>').appendTo(this).bind('click', Drupal.media.browser.submit);
-    }
-  } else if (!($('.fake-cancel', this).length > 0)) {
-    var parent = $('.form-actions', this);
-    if (!parent.length) {
-      parent = $('form > div', this);
-    }
-    $('<a class="button fake-cancel">Cancel</a>').appendTo(parent).bind('click', Drupal.media.browser.submit);
-  }
-};
-
-Drupal.media.browser.submit = function () {
-  // @see Drupal.media.browser.validateButtons().
-  var buttons = $(parent.window.document.body).find('#mediaBrowser').parent('.ui-dialog').find('.ui-dialog-buttonpane button');
-  if ($(this).hasClass('fake-cancel')) {
-    buttons[1].click();
-  } else {
-    buttons[0].click();
-  }
-}
-
-Drupal.media.browser.selectMedia = function (selectedMedia) {
-  Drupal.media.browser.selectedMedia = selectedMedia;
-};
-
-Drupal.media.browser.finalizeSelection = function () {
-  if (!Drupal.media.browser.selectedMedia) {
-    throw new exception('Cannot continue, nothing selected');
-  }
-  else {
-    Drupal.media.browser.selectionFinalized(Drupal.media.browser.selectedMedia);
-  }
-};
-
-Drupal.media.browser.resizeIframe = function (event) {
-  var h = $('body').height();
-  $(parent.window.document).find('#mediaBrowser').height(h);
-};
-
-}(jQuery));
diff --git a/javascript/media-format-form.js b/javascript/media-format-form.js
deleted file mode 100644
index 4d892b1..0000000
--- a/javascript/media-format-form.js
+++ /dev/null
@@ -1,48 +0,0 @@
-
-/**
- *  @file
- *  Attach behaviors to formatter radio select when selecting a media's display
- *  formatter.
- */
-
-(function ($) {
-namespace('Drupal.media.formatForm');
-
-Drupal.media.mediaFormatSelected = {};
-
-Drupal.behaviors.mediaFormatForm = {
-  attach: function (context, settings) {
-    // Add "Submit" and "Cancel" buttons inside the IFRAME that trigger the
-    // behavior of the hidden "OK" and "Cancel" buttons that are outside the
-    // IFRAME. See Drupal.media.browser.validateButtons() for more details.
-    $('<a class="button fake-ok">Submit</a>').appendTo($('#media-format-form')).bind('click', Drupal.media.formatForm.submit);
-    $('<a class="button fake-cancel">Cancel</a>').appendTo($('#media-format-form')).bind('click', Drupal.media.formatForm.submit);
-
-    if (Drupal.settings.media_format_form.autosubmit) {
-      $('.button.fake-ok').click();
-    }
-  }
-};
-
-Drupal.media.formatForm.getOptions = function () {
-  // Get all the values
-  var ret = {}; $.each($('#media-format-form fieldset#edit-options *').serializeArray(), function (i, field) { ret[field.name] = field.value; });
-  return ret;
-};
-
-Drupal.media.formatForm.getFormattedMedia = function () {
-  var formatType = $("select#edit-format option:selected").val();
-  return { type: formatType, options: Drupal.media.formatForm.getOptions(), html: Drupal.settings.media.formatFormFormats[formatType] };
-};
-
-Drupal.media.formatForm.submit = function () {
-  // @see Drupal.behaviors.mediaFormatForm.attach().
-  var buttons = $(parent.window.document.body).find('#mediaStyleSelector').parent('.ui-dialog').find('.ui-dialog-buttonpane button');
-  if ($(this).hasClass('fake-cancel')) {
-    buttons[1].click();
-  } else {
-    buttons[0].click();
-  }
-}
-
-})(jQuery);
diff --git a/javascript/media.core.js b/javascript/media.core.js
deleted file mode 100644
index f99971c..0000000
--- a/javascript/media.core.js
+++ /dev/null
@@ -1,19 +0,0 @@
-
-/**
- * Creates a namespace.
- *
- * @return
- *   The created namespace object.
- */
-function namespace () {
-  var a=arguments, o=null, i, j, d;
-  for (i=0; i<a.length; i=i+1) {
-    d=a[i].split(".");
-    o=window;
-    for (j=0; j<d.length; j=j+1) {
-      o[d[j]]=o[d[j]] || {};
-      o=o[d[j]];
-    }
-  }
-  return o;
-};
diff --git a/javascript/media.js b/javascript/media.js
deleted file mode 100644
index 91b2c3c..0000000
--- a/javascript/media.js
+++ /dev/null
@@ -1,75 +0,0 @@
-
-/**
- *  @file
- *  This file handles the JS for Media Module functions.
- */
-
-(function ($) {
-
-/**
- * Loads media browsers and callbacks, specifically for media as a field.
- */
-Drupal.behaviors.mediaElement = {
-  attach: function (context, settings) {
-    // Options set from media.fields.inc for the types, etc to show in the browser.
-    
-    // For each widget (in case of multi-entry)
-    $('.media-widget', context).once('mediaBrowserLaunch', function () {
-      var options = settings.media.elements[this.id];
-      globalOptions = {};
-      if (options.global != undefined) {
-        var globalOptions = options.global;
-      }
-      //options = Drupal.settings.media.fields[this.id];
-      var fidField = $('.fid', this);
-      var previewField = $('.preview', this);
-      var removeButton = $('.remove', this); // Actually a link, but looks like a button.
-
-      // Show the Remove button if there's an already selected media.
-      if (fidField.val() != 0) {
-        removeButton.css('display', 'inline-block');
-      }
-
-      // When someone clicks the link to pick media (or clicks on an existing thumbnail)
-      $('.launcher', this).bind('click', function () {
-        // Launch the browser, providing the following callback function
-        // @TODO: This should not be an anomyous function.
-        Drupal.media.popups.mediaBrowser(function (mediaFiles) {
-          if (mediaFiles.length < 0) {
-            return;
-          }
-          var mediaFile = mediaFiles[0];
-          // Set the value of the filefield fid (hidden).
-          fidField.val(mediaFile.fid);
-          // Set the preview field HTML.
-          previewField.html(mediaFile.preview);
-          // Show the Remove button.
-          removeButton.show();
-        }, globalOptions);
-        return false;
-      });
-
-      // When someone clicks the Remove button.
-      $('.remove', this).bind('click', function () {
-        // Set the value of the filefield fid (hidden).
-        fidField.val(0);
-        // Set the preview field HTML.
-        previewField.html('');
-        // Hide the Remove button.
-        removeButton.hide();
-        return false;
-      });
-
-      $('.media-edit-link', this).bind('click', function () {
-        var fid = fidField.val();
-        if (fid) {
-          Drupal.media.popups.mediaFieldEditor(fid, function (r) { alert(r); });
-        }
-        return false;
-      });
-
-    });
-  }
-};
-
-})(jQuery);
diff --git a/javascript/media.popups.js b/javascript/media.popups.js
deleted file mode 100644
index aea0710..0000000
--- a/javascript/media.popups.js
+++ /dev/null
@@ -1,355 +0,0 @@
-
-/**
- * @file: Popup dialog interfaces for the media project.
- *
- * Drupal.media.popups.mediaBrowser
- *   Launches the media browser which allows users to pick a piece of media.
- *
- * Drupal.media.popups.mediaStyleSelector
- *  Launches the style selection form where the user can choose
- *  what format / style they want their media in.
- *
- */
-
-(function ($) {
-namespace('Drupal.media.popups');
-
-/**
- * Media browser popup. Creates a media browser dialog.
- *
- * @param {function}
- *          onSelect Callback for when dialog is closed, received (Array
- *          media, Object extra);
- * @param {Object}
- *          globalOptions Global options that will get passed upon initialization of the browser.
- *          @see Drupal.media.popups.mediaBrowser.getDefaults();
- *
- * @param {Object}
- *          pluginOptions Options for specific plugins. These are passed
- *          to the plugin upon initialization.  If a function is passed here as
- *          a callback, it is obviously not passed, but is accessible to the plugin
- *          in Drupal.settings.variables.
- *
- *          Example
- *          pluginOptions = {library: {url_include_patterns:'/foo/bar'}};
- *          
- * @param {Object}
- *          widgetOptions Options controlling the appearance and behavior of the
- *          modal dialog.
- *          @see Drupal.media.popups.mediaBrowser.getDefaults();
- */
-Drupal.media.popups.mediaBrowser = function (onSelect, globalOptions, pluginOptions, widgetOptions) {
-  var options = Drupal.media.popups.mediaBrowser.getDefaults();
-  options.global = $.extend({}, options.global, globalOptions);
-  options.plugins = pluginOptions;
-  options.widget = $.extend({}, options.widget, widgetOptions);
-  
-  // Create it as a modal window.
-  var browserSrc = options.widget.src;
-  // Params to send along to the iframe.  WIP.
-  var params = {};
-  $.extend(params, options.global);
-  params.plugins = options.plugins;
-
-  browserSrc += '&' + $.param(params);
-  var mediaIframe = Drupal.media.popups.getPopupIframe(browserSrc, 'mediaBrowser');
-  // Attach the onLoad event
-  mediaIframe.bind('load', options, options.widget.onLoad);
-  /**
-   * Setting up the modal dialog
-   */
-  
-  var ok = 'OK';
-  var cancel = 'Cancel';
-  var notSelected = 'You have not selected anything!';
-
-  if (Drupal && Drupal.t) {
-    ok = Drupal.t(ok);
-    cancel = Drupal.t(cancel);
-    notSelected = Drupal.t(notSelected);
-  }
-
-  // @todo: let some options come through here. Currently can't be changed.
-  var dialogOptions = options.dialog;
-
-  dialogOptions.buttons[ok] = function () {
-    var selected = this.contentWindow.Drupal.media.browser.selectedMedia;
-    if (selected.length < 1) {
-      alert(notSelected);
-      return;
-    }
-    onSelect(selected);
-    $(this).dialog("destroy");
-    $(this).remove();
-  };
-
-  dialogOptions.buttons[cancel] = function () {
-    $(this).dialog("destroy");
-    $(this).remove();
-  };
-
-  Drupal.media.popups.setDialogPadding(mediaIframe.dialog(dialogOptions));
-  // Remove the title bar.
-  mediaIframe.parents(".ui-dialog").find(".ui-dialog-titlebar").remove();
-  Drupal.media.popups.overlayDisplace(mediaIframe.parents(".ui-dialog"));
-  return mediaIframe;
-};
-
-Drupal.media.popups.mediaBrowser.mediaBrowserOnLoad = function (e) {
-  var options = e.data;
-  if (this.contentWindow.Drupal.media.browser.selectedMedia.length > 0) {
-    var ok = $(this).dialog('option', 'buttons')['OK'];
-    ok.call(this);
-    return;
-  }
-};
-
-Drupal.media.popups.mediaBrowser.getDefaults = function () {
-  return {
-    global: {
-      types: [], // Types to allow, defaults to all.
-      activePlugins: [] // If provided, a list of plugins which should be enabled.
-    },
-    widget: { // Settings for the actual iFrame which is launched.
-      src: Drupal.settings.media.browserUrl, // Src of the media browser (if you want to totally override it)
-      onLoad: Drupal.media.popups.mediaBrowser.mediaBrowserOnLoad // Onload function when iFrame loads.
-    },
-    dialog: Drupal.media.popups.getDialogOptions()
-  };
-};
-
-Drupal.media.popups.mediaBrowser.finalizeSelection = function () {
-  var selected = this.contentWindow.Drupal.media.browser.selectedMedia;
-  if (selected.length < 1) {
-    alert(notSelected);
-    return;
-  }
-  onSelect(selected);
-  $(this).dialog("destroy");
-  $(this).remove();
-}
-
-/**
- * Style chooser Popup. Creates a dialog for a user to choose a media style.
- *
- * @param mediaFile
- *          The mediaFile you are requesting this formatting form for.
- *          @todo: should this be fid?  That's actually all we need now.
- *
- * @param Function
- *          onSubmit Function to be called when the user chooses a media
- *          style. Takes one parameter (Object formattedMedia).
- *
- * @param Object
- *          options Options for the mediaStyleChooser dialog.
- */
-Drupal.media.popups.mediaStyleSelector = function (mediaFile, onSelect, options) {
-  var defaults = Drupal.media.popups.mediaStyleSelector.getDefaults();
-  // @todo: remove this awful hack :(
-  defaults.src = defaults.src.replace('-media_id-', mediaFile.fid);
-  options = $.extend({}, defaults, options);
-  // Create it as a modal window.
-  var mediaIframe = Drupal.media.popups.getPopupIframe(options.src, 'mediaStyleSelector');
-  // Attach the onLoad event
-  mediaIframe.bind('load', options, options.onLoad);
-
-  /**
-   * Set up the button text
-   */
-  var ok = 'OK';
-  var cancel = 'Cancel';
-  var notSelected = 'Very sorry, there was an unknown error embedding media.';
-
-  if (Drupal && Drupal.t) {
-    ok = Drupal.t(ok);
-    cancel = Drupal.t(cancel);
-    notSelected = Drupal.t(notSelected);
-  }
-
-  // @todo: let some options come through here. Currently can't be changed.
-  var dialogOptions = Drupal.media.popups.getDialogOptions();
-
-  dialogOptions.buttons[ok] = function () {
-
-    var formattedMedia = this.contentWindow.Drupal.media.formatForm.getFormattedMedia();
-    if (!formattedMedia) {
-      alert(notSelected);
-      return;
-    }
-    onSelect(formattedMedia);
-    $(this).dialog("destroy");
-    $(this).remove();
-  };
-
-  dialogOptions.buttons[cancel] = function () {
-    $(this).dialog("destroy");
-    $(this).remove();
-  };
-
-  Drupal.media.popups.setDialogPadding(mediaIframe.dialog(dialogOptions));
-  // Remove the title bar.
-  mediaIframe.parents(".ui-dialog").find(".ui-dialog-titlebar").remove();
-  Drupal.media.popups.overlayDisplace(mediaIframe.parents(".ui-dialog"));
-  return mediaIframe;
-};
-
-Drupal.media.popups.mediaStyleSelector.mediaBrowserOnLoad = function (e) {
-};
-
-Drupal.media.popups.mediaStyleSelector.getDefaults = function () {
-  return {
-    src: Drupal.settings.media.styleSelectorUrl,
-    onLoad: Drupal.media.popups.mediaStyleSelector.mediaBrowserOnLoad
-  };
-};
-
-
-/**
- * Style chooser Popup. Creates a dialog for a user to choose a media style.
- *
- * @param mediaFile
- *          The mediaFile you are requesting this formatting form for.
- *          @todo: should this be fid?  That's actually all we need now.
- *
- * @param Function
- *          onSubmit Function to be called when the user chooses a media
- *          style. Takes one parameter (Object formattedMedia).
- *
- * @param Object
- *          options Options for the mediaStyleChooser dialog.
- */
-Drupal.media.popups.mediaFieldEditor = function (fid, onSelect, options) {
-  var defaults = Drupal.media.popups.mediaFieldEditor.getDefaults();
-  // @todo: remove this awful hack :(
-  defaults.src = defaults.src.replace('-media_id-', fid);
-  options = $.extend({}, defaults, options);
-  // Create it as a modal window.
-  var mediaIframe = Drupal.media.popups.getPopupIframe(options.src, 'mediaFieldEditor');
-  // Attach the onLoad event
-  // @TODO - This event is firing too early in IE on Windows 7,
-  // - so the height being calculated is too short for the content.
-  mediaIframe.bind('load', options, options.onLoad);
-
-  /**
-   * Set up the button text
-   */
-  var ok = 'OK';
-  var cancel = 'Cancel';
-  var notSelected = 'Very sorry, there was an unknown error embedding media.';
-
-  if (Drupal && Drupal.t) {
-    ok = Drupal.t(ok);
-    cancel = Drupal.t(cancel);
-    notSelected = Drupal.t(notSelected);
-  }
-
-  // @todo: let some options come through here. Currently can't be changed.
-  var dialogOptions = Drupal.media.popups.getDialogOptions();
-
-  dialogOptions.buttons[ok] = function () {
-    alert('hell yeah');
-    return "poo";
-
-    var formattedMedia = this.contentWindow.Drupal.media.formatForm.getFormattedMedia();
-    if (!formattedMedia) {
-      alert(notSelected);
-      return;
-    }
-    onSelect(formattedMedia);
-    $(this).dialog("destroy");
-    $(this).remove();
-  };
-
-  dialogOptions.buttons[cancel] = function () {
-    $(this).dialog("destroy");
-    $(this).remove();
-  };
-
-  Drupal.media.popups.setDialogPadding(mediaIframe.dialog(dialogOptions));
-  // Remove the title bar.
-  mediaIframe.parents(".ui-dialog").find(".ui-dialog-titlebar").remove();
-  Drupal.media.popups.overlayDisplace(mediaIframe.parents(".ui-dialog"));
-  return mediaIframe;
-};
-
-Drupal.media.popups.mediaFieldEditor.mediaBrowserOnLoad = function (e) {
-
-};
-
-Drupal.media.popups.mediaFieldEditor.getDefaults = function () {
-  return {
-    // @todo: do this for real
-    src: '/media/-media_id-/edit?render=media-popup',
-    onLoad: Drupal.media.popups.mediaFieldEditor.mediaBrowserOnLoad
-  };
-};
-
-
-/**
- * Generic functions to both the media-browser and style selector
- */
-
-/**
- * Returns the commonly used options for the dialog.
- */
-Drupal.media.popups.getDialogOptions = function () {
-  return {
-    buttons: {},
-    dialogClass: 'media-wrapper',
-    modal: true,
-    draggable: false,
-    resizable: false,
-    minWidth: 600,
-    width: 800,
-    height: 550,
-    position: 'center',
-    overlay: {
-      backgroundColor: '#000000',
-      opacity: 0.4
-    }
-  };
-};
-
-/**
- * Created padding on a dialog
- *
- * @param jQuery dialogElement
- *  The element which has .dialog() attached to it.
- */
-Drupal.media.popups.setDialogPadding = function (dialogElement) {
-  // @TODO: Perhaps remove this hardcoded reference to height.
-  // - It's included to make IE on Windows 7 display the dialog without
-  //   collapsing. 550 is the height that displays all of the tab panes
-  //   within the Add Media overlay. This is either a bug in the jQuery
-  //   UI library, a bug in IE on Windows 7 or a bug in the way the 
-  //   dialog is instantiated. Or a combo of the three.
-  //   All browsers except IE on Win7 ignore these defaults and adjust
-  //   the height of the iframe correctly to match the content in the panes
-  dialogElement.height(dialogElement.dialog('option', 'height'));
-  dialogElement.width(dialogElement.dialog('option', 'width'));
-};
-
-/**
- * Get an iframe to serve as the dialog's contents. Common to both plugins.
- */
-Drupal.media.popups.getPopupIframe = function (src, id, options) {
-  var defaults = {width: '800px', scrolling: 'no'};
-  var options = $.extend({}, defaults, options);
-
-  return $('<iframe class="media-modal-frame"/>')
-  .attr('src', src)
-  .attr('width', options.width)
-  .attr('id', id)
-  .attr('scrolling', options.scrolling);
-};
-
-Drupal.media.popups.overlayDisplace = function (dialog) {
-  if (parent.window.Drupal.overlay) {
-    var overlayDisplace = parent.window.Drupal.overlay.getDisplacement('top');
-    if (dialog.offset().top < overlayDisplace) {
-      dialog.css('top', overlayDisplace);
-    }
-  }
-}
-
-})(jQuery);
diff --git a/javascript/plugins/media.fromurl.js b/javascript/plugins/media.fromurl.js
deleted file mode 100644
index f618e23..0000000
--- a/javascript/plugins/media.fromurl.js
+++ /dev/null
@@ -1,29 +0,0 @@
-
-(function ($) {
-namespace('Drupal.media.browser.plugin');
-
-Drupal.media.browser.plugin.fromurl = function (mediaBrowser, options) {
-  return {
-    init: function () {
-      tabset = mediaBrowser.getTabset();
-      tabset.tabs('add', '#fromurl', 'From URL');
-      mediaBrowser.listen('tabs.show', function (e, id) {
-        if (id == 'fromurl') {
-          // We only need to set this once.
-          // We probably could set it upon load.
-          if (mediaBrowser.getActivePanel().html() == '') {
-            mediaBrowser.getActivePanel().html(options.fromUrlForm);
-          }
-        }
-      });
-    }
-  };
-};
-
-// For now, I guess self registration makes sense.
-// Really though, we should be doing it via drupal_add_js and some settings
-// from the drupal variable.
-// @todo: needs a review.
-Drupal.media.browser.register('fromurl', Drupal.media.browser.plugin.fromurl, {});
-
-})(jQuery);
diff --git a/javascript/plugins/media.library.css b/javascript/plugins/media.library.css
deleted file mode 100644
index dbd68dc..0000000
--- a/javascript/plugins/media.library.css
+++ /dev/null
@@ -1,23 +0,0 @@
-
-ul#media-browser-library-list {
-  list-style: none;
-  margin: -10px 0 0 0;
-  padding: 0px;
-  background-color: white;
-}
-
-#media-tab-library #container{
-  padding:20px 0;
-}
-
-#media-tab-library #scrollbox{
-  height: 300px;
-  overflow:auto;
-  overflow-x:hidden;
-}
-
-#scrollbox #status {
-  clear: both;
-  text-align: center;
-  margin-top: 10px;
-}
diff --git a/javascript/plugins/media.library.js b/javascript/plugins/media.library.js
deleted file mode 100644
index c3b1ee5..0000000
--- a/javascript/plugins/media.library.js
+++ /dev/null
@@ -1,184 +0,0 @@
-
-(function ($) {
-namespace('Drupal.media.browser');
-
-Drupal.behaviors.mediaLibrary = {
-  attach: function (context, settings) {
-    var library = new Drupal.media.browser.library(Drupal.settings.media.browser.library);
-    $('#media-browser-tabset').bind('tabsselect', function (event, ui) {
-      if (ui.tab.hash === '#media-tab-library') {
-        // Grab the parameters from the Drupal.settings object
-        var params = {};
-        for (var parameter in Drupal.settings.media.browser.library) {
-          params[parameter] = Drupal.settings.media.browser.library[parameter];
-        }
-        library.start($(ui.panel), params);
-        $('#scrollbox').bind('scroll', library, library.scrollUpdater);
-      }
-    });
-  }
-};
-
-Drupal.media.browser.library = function (settings) {
-  this.settings = Drupal.media.browser.library.getDefaults();
-  $.extend(this.settings, settings);
-
-  this.done = false; // Keeps track of if the last request for media returned 0 results.
-
-  this.cursor = 0; // keeps track of what the last requested media object was.
-  this.mediaFiles = []; // An array of loaded media files from the server.
-  this.selectedMediaFiles = [];
-};
-
-Drupal.media.browser.library.getDefaults = function () {
-  return {
-    emtpyMessage: "There is nothing in your media library.  Select the Upload tab above to add a file.",
-    limit: 15
-  };
-};
-
-Drupal.media.browser.library.prototype.start = function (renderElement, params) {
-  this.renderElement = renderElement;
-  this.params = params;
-  // Change the behavior dependent on multiselect
-  if (params.multiselect) {
-    this.clickFunction = this.multiSelect;
-  } else {
-    this.clickFunction = this.singleSelect;
-  }
-  this.loadMedia();
-};
-
-/**
- * Appends more media onto the list
- */
-Drupal.media.browser.library.prototype.loadMedia = function () {
-  var that = this;
-  $('#status').text('Loading...').show();
-  $.extend(this.params, {start: this.cursor, limit: this.settings.limit});
-
-  var gotMedia = function (data, status) {
-    $('#status').text('').hide();
-    if (data.media.length < that.params.limit) {
-      // We remove the scroll event listener, nothing more to load after this.
-      $('#scrollbox').unbind('scroll');
-    }
-    that.mediaFiles = that.mediaFiles.concat(data.media);
-    that.render(that.renderElement);
-    // Remove the flag that prevents loading of more media
-    that.loading = false;
-  };
-
-  var errorCallback = function () {
-    alert('Error getting media.');
-  };
-
-  $.ajax({
-    url: this.settings.getMediaUrl,
-    type: 'GET',
-    dataType: 'json',
-    data: this.params,
-    error: errorCallback,
-    success: gotMedia
-  });
-};
-
-Drupal.media.browser.library.prototype.scrollUpdater = function (e){
-  if (!e.data.loading) {
-    var scrollbox = $('#scrollbox');
-    var scrolltop = scrollbox.attr('scrollTop');
-    var scrollheight = scrollbox.attr('scrollHeight');
-    var windowheight = scrollbox.attr('clientHeight');
-    var scrolloffset = 20;
-
-    if(scrolltop >= (scrollheight - (windowheight + scrolloffset))) {
-      // Set a flag so we don't make multiple concurrent AJAX calls
-      e.data.loading = true;
-      // Fetch new items
-      e.data.loadMedia();
-    }
-  }
-};
-
-/**
- * Fetches the next media object and increments the cursor.
- */
-Drupal.media.browser.library.prototype.getNextMedia = function () {
-  if (this.cursor >= this.mediaFiles.length) {
-    return false;
-  }
-  var ret = this.mediaFiles[this.cursor];
-  this.cursor += 1;
-  return ret;
-};
-
-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);
-  }
-};
-
-Drupal.media.browser.library.prototype.mediaSelected = function (media) {
-  Drupal.media.browser.selectMedia(media);
-};
-
-Drupal.media.browser.library.prototype.singleSelect = function (event) {
-  var lib = event.data.obj;
-  var file = event.data.file;
-  event.preventDefault();
-  event.stopPropagation();
-
-  $('.media-item').removeClass('selected');
-  $('.media-item', $(this)).addClass('selected');
-  lib.mediaSelected([event.data.file]);
-  return false;
-}
-
-Drupal.media.browser.library.prototype.multiSelect = function (event) {
-  var lib = event.data.obj
-  var file = event.data.file;
-  event.preventDefault();
-  event.stopPropagation();
-
-  // Turn off or on the selection of this item
-  $('.media-item', $(this)).toggleClass('selected');
-
-  // Add or remove the media file from the array
-  var index = $.inArray(file, lib.selectedMediaFiles);
-  if (index == -1) {
-    // Media file isn't selected, add it
-    lib.selectedMediaFiles.push(file);
-  } else {
-    // Media file has previously been selected, remove it
-    lib.selectedMediaFiles.splice(index, 1);
-  }
-
-  // Pass the array of selected media files to the invoker
-  lib.mediaSelected(lib.selectedMediaFiles);
-  return false;
-}
-
-}(jQuery));
diff --git a/javascript/plugins/media.upload.js b/javascript/plugins/media.upload.js
deleted file mode 100644
index 6f0117d..0000000
--- a/javascript/plugins/media.upload.js
+++ /dev/null
@@ -1,30 +0,0 @@
-
-(function ($) {
-namespace('Drupal.media.browser.plugin');
-
-Drupal.media.browser.plugin.upload = function (mediaBrowser, options) {
-  return {
-    /* Abstract */
-    init: function () {
-      tabset = mediaBrowser.getTabset();
-      tabset.tabs('add', '#upload', 'Upload');
-      mediaBrowser.listen('tabs.show', function (e, id) {
-        if (id == 'upload') {
-          // We only need to set this once.
-          // We probably could set it upon load.
-          if (mediaBrowser.getActivePanel().html() == '') {
-            mediaBrowser.getActivePanel().html(options.uploadForm);
-          }
-        }
-      });
-    }
-  };
-};
-
-// For now, I guess self registration makes sense.
-// Really though, we should be doing it via drupal_add_js and some settings
-// from the drupal variable.
-// @todo: needs a review.
-Drupal.media.browser.register('upload', Drupal.media.browser.plugin.upload, {});
-
-})(jQuery);
diff --git a/javascript/plugins/media.upload_multiple.js b/javascript/plugins/media.upload_multiple.js
deleted file mode 100755
index c82826b..0000000
--- a/javascript/plugins/media.upload_multiple.js
+++ /dev/null
@@ -1,13 +0,0 @@
-Drupal.behaviors.mediaUploadMultiple = {};
-
-Drupal.behaviors.mediaUploadMultiple.attach = function (context, settings) {
-  // When the plupload element initializes, it expands the size of the elements
-  // it has created, so we need to resize the browser iframe after it's done.
-  var uploader = jQuery('#edit-upload').pluploadQueue();
-  if (uploader) {
-    // Handle the case in which the uploader has already finished initializing.
-    Drupal.media.browser.resizeIframe();
-    // Handle the case in which the uploader has not yet initialized.
-    uploader.bind("PostInit", Drupal.media.browser.resizeIframe);
-  }
-};
diff --git a/javascript/util/ba-debug.min.js b/javascript/util/ba-debug.min.js
deleted file mode 100644
index e442a42..0000000
--- a/javascript/util/ba-debug.min.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * debug - v0.3 - 6/8/2009
- * http://benalman.com/projects/javascript-debug-console-log/
- * 
- * Copyright (c) 2009 "Cowboy" Ben Alman
- * Licensed under the MIT license
- * http://benalman.com/about/license/
- * 
- * With lots of help from Paul Irish!
- * http://paulirish.com/
- */
-window.debug=(function(){var c=this,e=Array.prototype.slice,b=c.console,i={},f,g,j=9,d=["error","warn","info","debug","log"],m="assert clear count dir dirxml group groupEnd profile profileEnd time timeEnd trace".split(" "),k=m.length,a=[];while(--k>=0){(function(n){i[n]=function(){j!==0&&b&&b[n]&&b[n].apply(b,arguments)}})(m[k])}k=d.length;while(--k>=0){(function(n,o){i[o]=function(){var q=e.call(arguments),p=[o].concat(q);a.push(p);h(p);if(!b||!l(n)){return}b.firebug?b[o].apply(c,q):b[o]?b[o](q):b.log(q)}})(k,d[k])}function h(n){if(f&&(g||!b||!b.log)){f.apply(c,n)}}i.setLevel=function(n){j=typeof n==="number"?n:9};function l(n){return j>0?j>n:d.length+j<=n}i.setCallback=function(){var o=e.call(arguments),n=a.length,p=n;f=o.shift()||null;g=typeof o[0]==="boolean"?o.shift():false;p-=typeof o[0]==="number"?o.shift():n;while(p<n){h(a[p++])}};return i})();
\ No newline at end of file
diff --git a/javascript/util/json2.js b/javascript/util/json2.js
deleted file mode 100644
index 39d8f37..0000000
--- a/javascript/util/json2.js
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
-    http://www.JSON.org/json2.js
-    2009-09-29
-
-    Public Domain.
-
-    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
-
-    See http://www.JSON.org/js.html
-
-
-    This code should be minified before deployment.
-    See http://javascript.crockford.com/jsmin.html
-
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
-    NOT CONTROL.
-
-
-    This file creates a global JSON object containing two methods: stringify
-    and parse.
-
-        JSON.stringify(value, replacer, space)
-            value       any JavaScript value, usually an object or array.
-
-            replacer    an optional parameter that determines how object
-                        values are stringified for objects. It can be a
-                        function or an array of strings.
-
-            space       an optional parameter that specifies the indentation
-                        of nested structures. If it is omitted, the text will
-                        be packed without extra whitespace. If it is a number,
-                        it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t' or '&nbsp;'),
-                        it contains the characters used to indent at each level.
-
-            This method produces a JSON text from a JavaScript value.
-
-            When an object value is found, if the object contains a toJSON
-            method, its toJSON method will be called and the result will be
-            stringified. A toJSON method does not serialize: it returns the
-            value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method
-            will be passed the key associated with the value, and this will be
-            bound to the value
-
-            For example, this would serialize Dates as ISO strings.
-
-                Date.prototype.toJSON = function (key) {
-                    function f(n) {
-                        // Format integers to have at least two digits.
-                        return n < 10 ? '0' + n : n;
-                    }
-
-                    return this.getUTCFullYear()   + '-' +
-                         f(this.getUTCMonth() + 1) + '-' +
-                         f(this.getUTCDate())      + 'T' +
-                         f(this.getUTCHours())     + ':' +
-                         f(this.getUTCMinutes())   + ':' +
-                         f(this.getUTCSeconds())   + 'Z';
-                };
-
-            You can provide an optional replacer method. It will be passed the
-            key and value of each member, with this bound to the containing
-            object. The value that is returned from your method will be
-            serialized. If your method returns undefined, then the member will
-            be excluded from the serialization.
-
-            If the replacer parameter is an array of strings, then it will be
-            used to select the members to be serialized. It filters the results
-            such that only members with keys listed in the replacer array are
-            stringified.
-
-            Values that do not have JSON representations, such as undefined or
-            functions, will not be serialized. Such values in objects will be
-            dropped; in arrays they will be replaced with null. You can use
-            a replacer function to replace those with JSON values.
-            JSON.stringify(undefined) returns undefined.
-
-            The optional space parameter produces a stringification of the
-            value that is filled with line breaks and indentation to make it
-            easier to read.
-
-            If the space parameter is a non-empty string, then that string will
-            be used for indentation. If the space parameter is a number, then
-            the indentation will be that many spaces.
-
-            Example:
-
-            text = JSON.stringify(['e', {pluribus: 'unum'}]);
-            // text is '["e",{"pluribus":"unum"}]'
-
-
-            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
-            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
-
-            text = JSON.stringify([new Date()], function (key, value) {
-                return this[key] instanceof Date ?
-                    'Date(' + this[key] + ')' : value;
-            });
-            // text is '["Date(---current time---)"]'
-
-
-        JSON.parse(text, reviver)
-            This method parses a JSON text to produce an object or array.
-            It can throw a SyntaxError exception.
-
-            The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values,
-            and its return value is used instead of the original value.
-            If it returns what it received, then the structure is not modified.
-            If it returns undefined then the member is deleted.
-
-            Example:
-
-            // Parse the text. Values that look like ISO date strings will
-            // be converted to Date objects.
-
-            myData = JSON.parse(text, function (key, value) {
-                var a;
-                if (typeof value === 'string') {
-                    a =
-/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
-                    if (a) {
-                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
-                            +a[5], +a[6]));
-                    }
-                }
-                return value;
-            });
-
-            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
-                var d;
-                if (typeof value === 'string' &&
-                        value.slice(0, 5) === 'Date(' &&
-                        value.slice(-1) === ')') {
-                    d = new Date(value.slice(5, -1));
-                    if (d) {
-                        return d;
-                    }
-                }
-                return value;
-            });
-
-
-    This is a reference implementation. You are free to copy, modify, or
-    redistribute.
-*/
-
-/*jslint evil: true, strict: false */
-
-/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
-    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
-    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
-    lastIndex, length, parse, prototype, push, replace, slice, stringify,
-    test, toJSON, toString, valueOf
-*/
-
-
-// Create a JSON object only if one does not already exist. We create the
-// methods in a closure to avoid creating global variables.
-
-if (!this.JSON) {
-    this.JSON = {};
-}
-
-(function () {
-
-    function f(n) {
-        // Format integers to have at least two digits.
-        return n < 10 ? '0' + n : n;
-    }
-
-    if (typeof Date.prototype.toJSON !== 'function') {
-
-        Date.prototype.toJSON = function (key) {
-
-            return isFinite(this.valueOf()) ?
-                   this.getUTCFullYear()   + '-' +
-                 f(this.getUTCMonth() + 1) + '-' +
-                 f(this.getUTCDate())      + 'T' +
-                 f(this.getUTCHours())     + ':' +
-                 f(this.getUTCMinutes())   + ':' +
-                 f(this.getUTCSeconds())   + 'Z' : null;
-        };
-
-        String.prototype.toJSON =
-        Number.prototype.toJSON =
-        Boolean.prototype.toJSON = function (key) {
-            return this.valueOf();
-        };
-    }
-
-    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
-        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
-        gap,
-        indent,
-        meta = {    // table of character substitutions
-            '\b': '\\b',
-            '\t': '\\t',
-            '\n': '\\n',
-            '\f': '\\f',
-            '\r': '\\r',
-            '"' : '\\"',
-            '\\': '\\\\'
-        },
-        rep;
-
-
-    function quote(string) {
-
-// If the string contains no control characters, no quote characters, and no
-// backslash characters, then we can safely slap some quotes around it.
-// Otherwise we must also replace the offending characters with safe escape
-// sequences.
-
-        escapable.lastIndex = 0;
-        return escapable.test(string) ?
-            '"' + string.replace(escapable, function (a) {
-                var c = meta[a];
-                return typeof c === 'string' ? c :
-                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
-            }) + '"' :
-            '"' + string + '"';
-    }
-
-
-    function str(key, holder) {
-
-// Produce a string from holder[key].
-
-        var i,          // The loop counter.
-            k,          // The member key.
-            v,          // The member value.
-            length,
-            mind = gap,
-            partial,
-            value = holder[key];
-
-// If the value has a toJSON method, call it to obtain a replacement value.
-
-        if (value && typeof value === 'object' &&
-                typeof value.toJSON === 'function') {
-            value = value.toJSON(key);
-        }
-
-// If we were called with a replacer function, then call the replacer to
-// obtain a replacement value.
-
-        if (typeof rep === 'function') {
-            value = rep.call(holder, key, value);
-        }
-
-// What happens next depends on the value's type.
-
-        switch (typeof value) {
-        case 'string':
-            return quote(value);
-
-        case 'number':
-
-// JSON numbers must be finite. Encode non-finite numbers as null.
-
-            return isFinite(value) ? String(value) : 'null';
-
-        case 'boolean':
-        case 'null':
-
-// If the value is a boolean or null, convert it to a string. Note:
-// typeof null does not produce 'null'. The case is included here in
-// the remote chance that this gets fixed someday.
-
-            return String(value);
-
-// If the type is 'object', we might be dealing with an object or an array or
-// null.
-
-        case 'object':
-
-// Due to a specification blunder in ECMAScript, typeof null is 'object',
-// so watch out for that case.
-
-            if (!value) {
-                return 'null';
-            }
-
-// Make an array to hold the partial results of stringifying this object value.
-
-            gap += indent;
-            partial = [];
-
-// Is the value an array?
-
-            if (Object.prototype.toString.apply(value) === '[object Array]') {
-
-// The value is an array. Stringify every element. Use null as a placeholder
-// for non-JSON values.
-
-                length = value.length;
-                for (i = 0; i < length; i += 1) {
-                    partial[i] = str(i, value) || 'null';
-                }
-
-// Join all of the elements together, separated with commas, and wrap them in
-// brackets.
-
-                v = partial.length === 0 ? '[]' :
-                    gap ? '[\n' + gap +
-                            partial.join(',\n' + gap) + '\n' +
-                                mind + ']' :
-                          '[' + partial.join(',') + ']';
-                gap = mind;
-                return v;
-            }
-
-// If the replacer is an array, use it to select the members to be stringified.
-
-            if (rep && typeof rep === 'object') {
-                length = rep.length;
-                for (i = 0; i < length; i += 1) {
-                    k = rep[i];
-                    if (typeof k === 'string') {
-                        v = str(k, value);
-                        if (v) {
-                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                        }
-                    }
-                }
-            } else {
-
-// Otherwise, iterate through all of the keys in the object.
-
-                for (k in value) {
-                    if (Object.hasOwnProperty.call(value, k)) {
-                        v = str(k, value);
-                        if (v) {
-                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                        }
-                    }
-                }
-            }
-
-// Join all of the member texts together, separated with commas,
-// and wrap them in braces.
-
-            v = partial.length === 0 ? '{}' :
-                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
-                        mind + '}' : '{' + partial.join(',') + '}';
-            gap = mind;
-            return v;
-        }
-    }
-
-// If the JSON object does not yet have a stringify method, give it one.
-
-    if (typeof JSON.stringify !== 'function') {
-        JSON.stringify = function (value, replacer, space) {
-
-// The stringify method takes a value and an optional replacer, and an optional
-// space parameter, and returns a JSON text. The replacer can be a function
-// that can replace values, or an array of strings that will select the keys.
-// A default replacer method can be provided. Use of the space parameter can
-// produce text that is more easily readable.
-
-            var i;
-            gap = '';
-            indent = '';
-
-// If the space parameter is a number, make an indent string containing that
-// many spaces.
-
-            if (typeof space === 'number') {
-                for (i = 0; i < space; i += 1) {
-                    indent += ' ';
-                }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-            } else if (typeof space === 'string') {
-                indent = space;
-            }
-
-// If there is a replacer, it must be a function or an array.
-// Otherwise, throw an error.
-
-            rep = replacer;
-            if (replacer && typeof replacer !== 'function' &&
-                    (typeof replacer !== 'object' ||
-                     typeof replacer.length !== 'number')) {
-                throw new Error('JSON.stringify');
-            }
-
-// Make a fake root object containing our value under the key of ''.
-// Return the result of stringifying the value.
-
-            return str('', {'': value});
-        };
-    }
-
-
-// If the JSON object does not yet have a parse method, give it one.
-
-    if (typeof JSON.parse !== 'function') {
-        JSON.parse = function (text, reviver) {
-
-// The parse method takes a text and an optional reviver function, and returns
-// a JavaScript value if the text is a valid JSON text.
-
-            var j;
-
-            function walk(holder, key) {
-
-// The walk method is used to recursively walk the resulting structure so
-// that modifications can be made.
-
-                var k, v, value = holder[key];
-                if (value && typeof value === 'object') {
-                    for (k in value) {
-                        if (Object.hasOwnProperty.call(value, k)) {
-                            v = walk(value, k);
-                            if (v !== undefined) {
-                                value[k] = v;
-                            } else {
-                                delete value[k];
-                            }
-                        }
-                    }
-                }
-                return reviver.call(holder, key, value);
-            }
-
-
-// Parsing happens in four stages. In the first stage, we replace certain
-// Unicode characters with escape sequences. JavaScript handles many characters
-// incorrectly, either silently deleting them, or treating them as line endings.
-
-            cx.lastIndex = 0;
-            if (cx.test(text)) {
-                text = text.replace(cx, function (a) {
-                    return '\\u' +
-                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
-                });
-            }
-
-// In the second stage, we run the text against regular expressions that look
-// for non-JSON patterns. We are especially concerned with '()' and 'new'
-// because they can cause invocation, and '=' because it can cause mutation.
-// But just to be safe, we want to reject all unexpected forms.
-
-// We split the second stage into 4 regexp operations in order to work around
-// crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
-// replace all simple value tokens with ']' characters. Third, we delete all
-// open brackets that follow a colon or comma or that begin the text. Finally,
-// we look to see that the remaining characters are only whitespace or ']' or
-// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
-
-            if (/^[\],:{}\s]*$/.
-test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
-replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
-replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
-
-// In the third stage we use the eval function to compile the text into a
-// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
-// in JavaScript: it can begin a block or an object literal. We wrap the text
-// in parens to eliminate the ambiguity.
-
-                j = eval('(' + text + ')');
-
-// In the optional fourth stage, we recursively walk the new structure, passing
-// each name/value pair to a reviver function for possible transformation.
-
-                return typeof reviver === 'function' ?
-                    walk({'': j}, '') : j;
-            }
-
-// If the text is not JSON parseable, then a SyntaxError is thrown.
-
-            throw new SyntaxError('JSON.parse');
-        };
-    }
-}());
diff --git a/javascript/wysiwyg-media.js b/javascript/wysiwyg-media.js
deleted file mode 100644
index 156229a..0000000
--- a/javascript/wysiwyg-media.js
+++ /dev/null
@@ -1,365 +0,0 @@
-
-/**
- *  @file
- *  Attach Media WYSIWYG behaviors.
- */
-
-(function ($) {
-
-Drupal.media = Drupal.media || {};
-
-// Define the behavior.
-Drupal.wysiwyg.plugins.media = {
-
-  /**
-   * Initializes the tag map.
-   */
-  initializeTagMap: function () {
-    if (typeof Drupal.settings.tagmap == 'undefined') {
-      Drupal.settings.tagmap = { };
-    }
-  },
-  /**
-   * Execute the button.
-   * @TODO: Debug calls from this are never called. What's its function?
-   */
-  invoke: function (data, settings, instanceId) {
-    if (data.format == 'html') {
-      Drupal.media.popups.mediaBrowser(function (mediaFiles) {
-        Drupal.wysiwyg.plugins.media.mediaBrowserOnSelect(mediaFiles, instanceId);
-      }, settings['global']);
-    }
-  },
-
-  /**
-   * Respond to the mediaBrowser's onSelect event.
-   * @TODO: Debug calls from this are never called. What's its function?
-   */
-  mediaBrowserOnSelect: function (mediaFiles, instanceId) {
-    var mediaFile = mediaFiles[0];
-    var options = {};
-    Drupal.media.popups.mediaStyleSelector(mediaFile, function (formattedMedia) {
-      Drupal.wysiwyg.plugins.media.insertMediaFile(mediaFile, formattedMedia.type, formattedMedia.html, formattedMedia.options, Drupal.wysiwyg.instances[instanceId]);
-    }, options);
-
-    return;
-  },
-
-  insertMediaFile: function (mediaFile, viewMode, formattedMedia, options, wysiwygInstance) {
-
-    this.initializeTagMap();
-    // @TODO: the folks @ ckeditor have told us that there is no way
-    // to reliably add wrapper divs via normal HTML.
-    // There is some method of adding a "fake element"
-    // But until then, we're just going to embed to img.
-    // This is pretty hacked for now.
-    //
-    var imgElement = $(this.stripDivs(formattedMedia));
-    this.addImageAttributes(imgElement, mediaFile.fid, viewMode, options);
-
-    var toInsert = this.outerHTML(imgElement);
-    // Create an inline tag
-    var inlineTag = Drupal.wysiwyg.plugins.media.createTag(imgElement);
-    // Add it to the tag map in case the user switches input formats
-    Drupal.settings.tagmap[inlineTag] = toInsert;
-    wysiwygInstance.insert(toInsert);
-  },
-
-  /**
-   * Gets the HTML content of an element
-   *
-   * @param jQuery element
-   */
-  outerHTML: function (element) {
-    return $('<div>').append( element.eq(0).clone() ).html();
-  },
-
-  addImageAttributes: function (imgElement, fid, view_mode, additional) {
-    //    imgElement.attr('fid', fid);
-    //    imgElement.attr('view_mode', view_mode);
-    // Class so we can find this image later.
-    imgElement.addClass('media-image');
-    this.forceAttributesIntoClass(imgElement, fid, view_mode, additional);
-    if (additional) {
-      for (k in additional) {
-        if (additional.hasOwnProperty(k)) {
-          if (k === 'attr') {
-            imgElement.attr(k, additional[k]);
-          }
-        }
-      }
-    }
-  },
-
-  /**
-   * Due to problems handling wrapping divs in ckeditor, this is needed.
-   *
-   * Going forward, if we don't care about supporting other editors
-   * we can use the fakeobjects plugin to ckeditor to provide cleaner
-   * transparency between what Drupal will output <div class="field..."><img></div>
-   * instead of just <img>, for now though, we're going to remove all the stuff surrounding the images.
-   *
-   * @param String formattedMedia
-   *  Element containing the image
-   *
-   * @return HTML of <img> tag inside formattedMedia
-   */
-  stripDivs: function (formattedMedia) {
-    // Check to see if the image tag has divs to strip
-    var stripped = null;
-    if ($(formattedMedia).is('img')) {
-      stripped = this.outerHTML($(formattedMedia));
-    } else {
-      stripped = this.outerHTML($('img', $(formattedMedia)));
-    }
-    // This will fail if we pass the img tag without anything wrapping it, like we do when re-enabling WYSIWYG
-    return stripped;
-  },
-
-  /**
-   * Attach function, called when a rich text editor loads.
-   * This finds all [[tags]] and replaces them with the html
-   * that needs to show in the editor.
-   *
-   */
-  attach: function (content, settings, instanceId) {
-    var matches = content.match(/\[\[.*?\]\]/g);
-    this.initializeTagMap();
-    var tagmap = Drupal.settings.tagmap;
-    if (matches) {
-      var inlineTag = "";
-      for (i = 0; i < matches.length; i++) {
-        inlineTag = matches[i];
-        if (tagmap[inlineTag]) {
-          // This probably needs some work...
-          // We need to somehow get the fid propogated here.
-          // We really want to
-          var tagContent = tagmap[inlineTag];
-          var mediaMarkup = this.stripDivs(tagContent); // THis is <div>..<img>
-
-          var _tag = inlineTag;
-          _tag = _tag.replace('[[','');
-          _tag = _tag.replace(']]','');
-          try {
-            mediaObj = JSON.parse(_tag);
-          }
-          catch(err) {
-            mediaObj = null;
-          }
-          if(mediaObj) {
-            var imgElement = $(mediaMarkup);
-            this.addImageAttributes(imgElement, mediaObj.fid, mediaObj.view_mode);
-            var toInsert = this.outerHTML(imgElement);
-            content = content.replace(inlineTag, toInsert);
-          }
-        }
-        else {
-          debug.debug("Could not find content for " + inlineTag);
-        }
-      }
-    }
-    return content;
-  },
-
-  /**
-   * Detach function, called when a rich text editor detaches
-   */
-  detach: function (content, settings, instanceId) {
-    var content = $('<div>' + content + '</div>');
-    $('img.media-image',content).each(function (elem) {
-      var tag = Drupal.wysiwyg.plugins.media.createTag($(this));
-      $(this).replaceWith(tag);
-      var newContent = content.html();
-      var tagContent = $('<div></div>').append($(this)).html();
-      Drupal.settings.tagmap[tag] = tagContent;
-    });
-    return content.html();
-  },
-
-  /**
-   * @param jQuery imgNode
-   *  Image node to create tag from
-   */
-  createTag: function (imgNode) {
-    // Currently this is the <img> itself
-    // Collect all attribs to be stashed into tagContent
-    var mediaAttributes = {};
-    var imgElement = imgNode[0];
-    var sorter = [];
-
-    // @todo: this does not work in IE, width and height are always 0.
-    for (i=0; i< imgElement.attributes.length; i++) {
-      var attr = imgElement.attributes[i];
-      if (attr.specified == true) {
-        if (attr.name !== 'class') {
-          sorter.push(attr);
-        }
-        else {
-          // Exctract expando properties from the class field.
-          var attributes = this.getAttributesFromClass(attr.value);
-          for (var name in attributes) {
-            if (attributes.hasOwnProperty(name)) {
-              var value = attributes[name];
-              if (value.type && value.type === 'attr') {
-                sorter.push(value);
-              }
-            }
-          }
-        }
-      }
-    }
-
-    sorter.sort(this.sortAttributes);
-    
-    for (var prop in sorter) {
-      mediaAttributes[sorter[prop].name] = sorter[prop].value;
-    }
-
-    // The following 5 ifs are dedicated to IE7
-    // If the style is null, it is because IE7 can't read values from itself
-    if (jQuery.browser.msie && jQuery.browser.version == '7.0') {
-      if (mediaAttributes.style === "null") {
-        var imgHeight = imgNode.css('height');
-        var imgWidth = imgNode.css('width');
-        mediaAttributes.style = {
-          height: imgHeight,
-          width: imgWidth
-        }
-        if (!mediaAttributes['width']) {
-          mediaAttributes['width'] = imgWidth;
-        }
-        if (!mediaAttributes['height']) {
-          mediaAttributes['height'] = imgHeight;
-        }
-      }
-      // If the attribute width is zero, get the CSS width
-      if (Number(mediaAttributes['width']) === 0) {
-        mediaAttributes['width'] = imgNode.css('width');
-      }
-      // IE7 does support 'auto' as a value of the width attribute. It will not
-      // display the image if this value is allowed to pass through
-      if (mediaAttributes['width'] === 'auto') {
-        delete mediaAttributes['width'];
-      }
-      // If the attribute height is zero, get the CSS height
-      if (Number(mediaAttributes['height']) === 0) {
-        mediaAttributes['height'] = imgNode.css('height');
-      }
-      // IE7 does support 'auto' as a value of the height attribute. It will not
-      // display the image if this value is allowed to pass through
-      if (mediaAttributes['height'] === 'auto') {
-        delete mediaAttributes['height'];
-      }
-    }
-
-    // Remove elements from attribs using the blacklist
-    for (var blackList in Drupal.settings.media.blacklist) {
-      delete mediaAttributes[Drupal.settings.media.blacklist[blackList]];
-    }
-    tagContent = {
-      "type": 'media',
-      // @todo: This will be selected from the format form
-      "view_mode": attributes['view_mode'].value,
-      "fid" : attributes['fid'].value,
-      "attributes": mediaAttributes
-    };
-    return '[[' + JSON.stringify(tagContent) + ']]';
-  },
-
-  /**
-   * Forces custom attributes into the class field of the specified image.
-   *
-   * Due to a bug in some versions of Firefox
-   * (http://forums.mozillazine.org/viewtopic.php?f=9&t=1991855), the
-   * custom attributes used to share information about the image are
-   * being stripped as the image markup is set into the rich text
-   * editor.  Here we encode these attributes into the class field so
-   * the data survives.
-   *
-   * @param imgElement
-   *   The image
-   * @fid
-   *   The file id.
-   * @param view_mode
-   *   The view mode.
-   * @param additional
-   *   Additional attributes to add to the image.
-   */
-  forceAttributesIntoClass: function (imgElement, fid, view_mode, additional) {
-    var wysiwyg = imgElement.attr('wysiwyg');
-    if (wysiwyg) {
-      imgElement.addClass('attr__wysiwyg__' + wysiwyg);
-    }
-    var format = imgElement.attr('format');
-    if (format) {
-      imgElement.addClass('attr__format__' + format);
-    }
-    var typeOf = imgElement.attr('typeof');
-    if (typeOf) {
-      imgElement.addClass('attr__typeof__' + typeOf);
-    }
-    if (fid) {
-      imgElement.addClass('img__fid__' + fid);
-    }
-    if (view_mode) {
-      imgElement.addClass('img__view_mode__' + view_mode);
-    }
-    if (additional) {
-      for (var name in additional) {
-        if (additional.hasOwnProperty(name)) {
-          if (name !== 'alt') {
-            imgElement.addClass('attr__' + name + '__' + additional[name]);
-          }
-        }
-      }
-    }
-  },
-
-  /**
-   * Retrieves encoded attributes from the specified class string.
-   *
-   * @param classString
-   *   A string containing the value of the class attribute.
-   * @return
-   *   An array containing the attribute names as keys, and an object
-   *   with the name, value, and attribute type (either 'attr' or
-   *   'img', depending on whether it is an image attribute or should
-   *   be it the attributes section)
-   */
-  getAttributesFromClass: function (classString) {
-    var actualClasses = [];
-    var otherAttributes = [];
-    var classes = classString.split(' ');
-    var regexp = new RegExp('^(attr|img)__([^\S]*)__([^\S]*)$');
-    for (var index = 0; index < classes.length; index++) {
-      var matches = classes[index].match(regexp);
-      if (matches && matches.length === 4) {
-        otherAttributes[matches[2]] = {name: matches[2], value: matches[3], type: matches[1]};
-      }
-      else {
-        actualClasses.push(classes[index]);
-      }
-    }
-    if (actualClasses.length > 0) {
-      otherAttributes['class'] = {name: 'class', value: actualClasses.join(' '), type: 'attr'};
-    }
-    return otherAttributes;
-  },
-
-  /*
-   * 
-   */
-  sortAttributes: function (a, b) {
-    var nameA = a.name.toLowerCase();
-    var nameB = b.name.toLowerCase();
-    if (nameA < nameB) {
-      return -1;
-    }
-    if (nameA > nameB) {
-      return 1;
-    }
-    return 0;
-  }
-};
-
-})(jQuery);
diff --git a/js/media.admin.js b/js/media.admin.js
new file mode 100644
index 0000000..8716f7b
--- /dev/null
+++ b/js/media.admin.js
@@ -0,0 +1,142 @@
+
+/**
+ * @file
+ * Javascript for the interface at admin/content/media and also for interfaces
+ * related to setting up media fields and for media type administration.
+ *
+ * Basically, if it's on the /admin path, it's probably here.
+ */
+
+(function ($) {
+
+/**
+ * Functionality for the thumbnail display
+ */
+Drupal.behaviors.mediaAdmin = {
+  attach: function (context) {
+    // Configure the "Add file" link to fire the media browser popup.
+    $('ul.action-links li', context).remove();
+    if ($('form.media-list-operation', context).length != 0) {
+      return;  
+    }
+    var $launcherLink = $('<a class="media-launcher" href="#"></a>').html('Add file');
+    $launcherLink.bind('click', function () {
+      // This option format needs *serious* work.
+      // Not even bothering documenting it because it needs to be thrown.
+      // See media.browser.js and media.browser.inc - media_browser()
+      // For how it gets passed.
+      var options = {
+        disabledPlugins: ['library'],
+        multiselect: true
+      };
+      Drupal.media.popups.mediaBrowser(function (mediaFiles) {
+        // When the media browser succeeds, we refresh
+        // @TODO: Should jump to the new media file and perhaps highlight it.
+        parent.window.location.reload();
+        return false;
+      }, options);
+    });
+    $('ul.action-links', context).append($('<li></li>').append($launcherLink));
+
+
+    if ($('body.page-admin-content-media-thumbnails').length != 0) {
+      // Implements 'select all/none' for thumbnail view.
+      // @TODO: Support grabbing more than one page of thumbnails.
+      var allLink = $('<a href="#">' + Drupal.t('all') + '</a>')
+        .click(function () {
+          $('.media-display-thumbnails', $(this).parents('form')).find(':checkbox').attr('checked', true).change();
+          return false;
+        });
+      var noneLink = $('<a href="#">' + Drupal.t('none') + '</a>')
+        .click(function () {
+          $('.media-display-thumbnails', $(this).parents('form')).find(':checkbox').attr('checked', false).change();
+          return false;
+        });
+      $('<div class="media-thumbnails-select" />')
+        .append('<strong>' + Drupal.t('Select') + ':</strong> ')
+        .append(allLink)
+        .append(', ')
+        .append(noneLink)
+        .prependTo('#media-admin > div')
+      // If the media item is clicked anywhere other than on the image itself
+      // check the checkbox. For the record, JS thinks this is wonky.
+      $('.media-item').bind('click', function (e) {
+        if ($(e.target).is('img, a')) {
+          return;
+        }
+        var checkbox = $(this).parent().find(':checkbox');
+        if (checkbox.is(':checked')) {
+          checkbox.attr('checked', false).change();
+        } else {
+          checkbox.attr('checked', true).change();
+        }
+      });
+  
+      // Add an extra class to selected thumbnails.
+      $('.media-display-thumbnails :checkbox').each(function () {
+        var checkbox = $(this);
+        if (checkbox.is(':checked')) {
+          $(checkbox.parents('li').find('.media-item')).addClass('selected');
+        }
+  
+        checkbox.bind('change.media', function () {
+          if (checkbox.is(':checked')) {
+            $(checkbox.parents('li').find('.media-item')).addClass('selected');
+          }
+          else {
+            $(checkbox.parents('li').find('.media-item')).removeClass('selected');
+          }
+        });
+      });
+    }
+
+    // When any checkboxes are clicked on this form check to see if any are checked.
+    // If any checkboxes are checked, show the edit options (@todo rename to edit-actions).
+    $('#media-admin :checkbox').bind('change', function () {
+      Drupal.behaviors.mediaAdmin.showOrHideEditOptions();
+    });
+
+    Drupal.behaviors.mediaAdmin.showOrHideEditOptions();
+  },
+
+  // Checks if any checkboxes on the form are checked, if so it will show the
+  // edit-options panel.
+  showOrHideEditOptions: function() {
+    var fieldset = $('#edit-options');
+    if (!$('#media-admin input[type=checkbox]:checked').size()) {
+      fieldset.slideUp('fast');
+    }
+    else {
+      fieldset.slideDown('fast');
+    }
+  }
+};
+
+
+/**
+ * JavaScript for the Media types administrative form.
+ */
+Drupal.behaviors.mediaTypesAdmin = {
+  attach: function (context) {
+    if ($('.form-item-match-type', context).length == 0) {
+      return;
+    }
+    // Toggle the 'other' text field on Match type.
+    if ($('.form-item-match-type input:checked').val() != '0') {
+      $('.form-item-match-type-other').hide();
+    }
+    $('.form-item-match-type input').change(function () {
+      if ($(this).val() == '0') {
+        $('.form-item-match-type-other').slideDown('fast');
+      }
+      else {
+        $('.form-item-match-type-other').slideUp('fast');
+      }
+    });
+  }
+};
+
+
+  
+})(jQuery);
+
diff --git a/js/media.browser.js b/js/media.browser.js
new file mode 100644
index 0000000..1b30b1d
--- /dev/null
+++ b/js/media.browser.js
@@ -0,0 +1,100 @@
+
+(function ($) {
+namespace('Drupal.media.browser');
+
+Drupal.media.browser.selectedMedia = [];
+Drupal.media.browser.mediaAdded = function () {};
+Drupal.media.browser.selectionFinalized = function (selectedMedia) {
+  // This is intended to be overridden if a callee wants to be triggered
+  // when the media selection is finalized from inside the browser.
+  // This is used for the file upload form for instance.
+};
+
+Drupal.behaviors.experimentalMediaBrowser = {
+  attach: function (context) {
+    if (Drupal.settings.media.selectedMedia) {
+      Drupal.media.browser.selectMedia(Drupal.settings.media.selectedMedia);
+      // Fire a confirmation of some sort.
+      Drupal.media.browser.finalizeSelection();
+    }
+    $('#media-browser-tabset').tabs({
+      show: Drupal.media.browser.resizeIframe
+    });
+
+    $('.media-browser-tab').each( Drupal.media.browser.validateButtons );
+
+  }
+  // Wait for additional params to be passed in.
+};
+
+Drupal.media.browser.launch = function () {
+
+};
+
+Drupal.media.browser.validateButtons = function() {
+  // The media browser runs in an IFRAME. The Drupal.media.popups.mediaBrowser()
+  // function sets up the IFRAME and "OK" and "Cancel" buttons that are outside
+  // of the IFRAME, so that their click handlers can destroy the IFRAME while
+  // retaining information about what media items were selected. However,
+  // Drupal UI convention is to place all action buttons on the same "line"
+  // at the bottom of the form, so if the form within the IFRAME contains a
+  // "Submit" button or other action buttons, then the "OK" and "Cancel"
+  // buttons below the IFRAME break this convention and confuse the user.
+  // Therefore, we add "Submit" and "Cancel" buttons inside the IFRAME, and
+  // have their click action trigger the click action of the corresonding
+  // "OK" and "Cancel" buttons that are outside the IFRAME. media.css contains
+  // CSS rules that hide the outside buttons.
+  //
+  // We don't add a "Submit" button if the form already has one, since in these
+  // cases, another round-trip to the server is needed before the user's
+  // selection is finalized. For these cases, when the form's real Submit
+  // button is clicked, the server either returns another form for the user to
+  // fill out, or else a completion page that contains or sets the
+  // Drupal.media.browser.selectedMedia variable. If the latter, then
+  // Drupal.media.popups.mediaBrowser.mediaBrowserOnLoad() auto-triggers the
+  // "OK" button action to finalize the selection and remove the IFRAME.
+  //
+  // @todo An alternate, less hacky solution would be most welcome.
+  if (!($('.form-submit', this).length > 0)) {
+    $('<a class="button fake-ok">Submit</a>').appendTo(this).bind('click', Drupal.media.browser.submit);
+    if (!($('.fake-cancel', this).length > 0)) {
+      $('<a class="button fake-cancel">Cancel</a>').appendTo(this).bind('click', Drupal.media.browser.submit);
+    }
+  } else if (!($('.fake-cancel', this).length > 0)) {
+    var parent = $('.form-actions', this);
+    if (!parent.length) {
+      parent = $('form > div', this);
+    }
+    $('<a class="button fake-cancel">Cancel</a>').appendTo(parent).bind('click', Drupal.media.browser.submit);
+  }
+};
+
+Drupal.media.browser.submit = function () {
+  // @see Drupal.media.browser.validateButtons().
+  var buttons = $(parent.window.document.body).find('#mediaBrowser').parent('.ui-dialog').find('.ui-dialog-buttonpane button');
+  if ($(this).hasClass('fake-cancel')) {
+    buttons[1].click();
+  } else {
+    buttons[0].click();
+  }
+}
+
+Drupal.media.browser.selectMedia = function (selectedMedia) {
+  Drupal.media.browser.selectedMedia = selectedMedia;
+};
+
+Drupal.media.browser.finalizeSelection = function () {
+  if (!Drupal.media.browser.selectedMedia) {
+    throw new exception('Cannot continue, nothing selected');
+  }
+  else {
+    Drupal.media.browser.selectionFinalized(Drupal.media.browser.selectedMedia);
+  }
+};
+
+Drupal.media.browser.resizeIframe = function (event) {
+  var h = $('body').height();
+  $(parent.window.document).find('#mediaBrowser').height(h);
+};
+
+}(jQuery));
diff --git a/js/media.core.js b/js/media.core.js
new file mode 100644
index 0000000..f99971c
--- /dev/null
+++ b/js/media.core.js
@@ -0,0 +1,19 @@
+
+/**
+ * Creates a namespace.
+ *
+ * @return
+ *   The created namespace object.
+ */
+function namespace () {
+  var a=arguments, o=null, i, j, d;
+  for (i=0; i<a.length; i=i+1) {
+    d=a[i].split(".");
+    o=window;
+    for (j=0; j<d.length; j=j+1) {
+      o[d[j]]=o[d[j]] || {};
+      o=o[d[j]];
+    }
+  }
+  return o;
+};
diff --git a/js/media.format_form.js b/js/media.format_form.js
new file mode 100644
index 0000000..4d892b1
--- /dev/null
+++ b/js/media.format_form.js
@@ -0,0 +1,48 @@
+
+/**
+ *  @file
+ *  Attach behaviors to formatter radio select when selecting a media's display
+ *  formatter.
+ */
+
+(function ($) {
+namespace('Drupal.media.formatForm');
+
+Drupal.media.mediaFormatSelected = {};
+
+Drupal.behaviors.mediaFormatForm = {
+  attach: function (context, settings) {
+    // Add "Submit" and "Cancel" buttons inside the IFRAME that trigger the
+    // behavior of the hidden "OK" and "Cancel" buttons that are outside the
+    // IFRAME. See Drupal.media.browser.validateButtons() for more details.
+    $('<a class="button fake-ok">Submit</a>').appendTo($('#media-format-form')).bind('click', Drupal.media.formatForm.submit);
+    $('<a class="button fake-cancel">Cancel</a>').appendTo($('#media-format-form')).bind('click', Drupal.media.formatForm.submit);
+
+    if (Drupal.settings.media_format_form.autosubmit) {
+      $('.button.fake-ok').click();
+    }
+  }
+};
+
+Drupal.media.formatForm.getOptions = function () {
+  // Get all the values
+  var ret = {}; $.each($('#media-format-form fieldset#edit-options *').serializeArray(), function (i, field) { ret[field.name] = field.value; });
+  return ret;
+};
+
+Drupal.media.formatForm.getFormattedMedia = function () {
+  var formatType = $("select#edit-format option:selected").val();
+  return { type: formatType, options: Drupal.media.formatForm.getOptions(), html: Drupal.settings.media.formatFormFormats[formatType] };
+};
+
+Drupal.media.formatForm.submit = function () {
+  // @see Drupal.behaviors.mediaFormatForm.attach().
+  var buttons = $(parent.window.document.body).find('#mediaStyleSelector').parent('.ui-dialog').find('.ui-dialog-buttonpane button');
+  if ($(this).hasClass('fake-cancel')) {
+    buttons[1].click();
+  } else {
+    buttons[0].click();
+  }
+}
+
+})(jQuery);
diff --git a/js/media.js b/js/media.js
new file mode 100644
index 0000000..91b2c3c
--- /dev/null
+++ b/js/media.js
@@ -0,0 +1,75 @@
+
+/**
+ *  @file
+ *  This file handles the JS for Media Module functions.
+ */
+
+(function ($) {
+
+/**
+ * Loads media browsers and callbacks, specifically for media as a field.
+ */
+Drupal.behaviors.mediaElement = {
+  attach: function (context, settings) {
+    // Options set from media.fields.inc for the types, etc to show in the browser.
+    
+    // For each widget (in case of multi-entry)
+    $('.media-widget', context).once('mediaBrowserLaunch', function () {
+      var options = settings.media.elements[this.id];
+      globalOptions = {};
+      if (options.global != undefined) {
+        var globalOptions = options.global;
+      }
+      //options = Drupal.settings.media.fields[this.id];
+      var fidField = $('.fid', this);
+      var previewField = $('.preview', this);
+      var removeButton = $('.remove', this); // Actually a link, but looks like a button.
+
+      // Show the Remove button if there's an already selected media.
+      if (fidField.val() != 0) {
+        removeButton.css('display', 'inline-block');
+      }
+
+      // When someone clicks the link to pick media (or clicks on an existing thumbnail)
+      $('.launcher', this).bind('click', function () {
+        // Launch the browser, providing the following callback function
+        // @TODO: This should not be an anomyous function.
+        Drupal.media.popups.mediaBrowser(function (mediaFiles) {
+          if (mediaFiles.length < 0) {
+            return;
+          }
+          var mediaFile = mediaFiles[0];
+          // Set the value of the filefield fid (hidden).
+          fidField.val(mediaFile.fid);
+          // Set the preview field HTML.
+          previewField.html(mediaFile.preview);
+          // Show the Remove button.
+          removeButton.show();
+        }, globalOptions);
+        return false;
+      });
+
+      // When someone clicks the Remove button.
+      $('.remove', this).bind('click', function () {
+        // Set the value of the filefield fid (hidden).
+        fidField.val(0);
+        // Set the preview field HTML.
+        previewField.html('');
+        // Hide the Remove button.
+        removeButton.hide();
+        return false;
+      });
+
+      $('.media-edit-link', this).bind('click', function () {
+        var fid = fidField.val();
+        if (fid) {
+          Drupal.media.popups.mediaFieldEditor(fid, function (r) { alert(r); });
+        }
+        return false;
+      });
+
+    });
+  }
+};
+
+})(jQuery);
diff --git a/js/media.popups.js b/js/media.popups.js
new file mode 100644
index 0000000..aea0710
--- /dev/null
+++ b/js/media.popups.js
@@ -0,0 +1,355 @@
+
+/**
+ * @file: Popup dialog interfaces for the media project.
+ *
+ * Drupal.media.popups.mediaBrowser
+ *   Launches the media browser which allows users to pick a piece of media.
+ *
+ * Drupal.media.popups.mediaStyleSelector
+ *  Launches the style selection form where the user can choose
+ *  what format / style they want their media in.
+ *
+ */
+
+(function ($) {
+namespace('Drupal.media.popups');
+
+/**
+ * Media browser popup. Creates a media browser dialog.
+ *
+ * @param {function}
+ *          onSelect Callback for when dialog is closed, received (Array
+ *          media, Object extra);
+ * @param {Object}
+ *          globalOptions Global options that will get passed upon initialization of the browser.
+ *          @see Drupal.media.popups.mediaBrowser.getDefaults();
+ *
+ * @param {Object}
+ *          pluginOptions Options for specific plugins. These are passed
+ *          to the plugin upon initialization.  If a function is passed here as
+ *          a callback, it is obviously not passed, but is accessible to the plugin
+ *          in Drupal.settings.variables.
+ *
+ *          Example
+ *          pluginOptions = {library: {url_include_patterns:'/foo/bar'}};
+ *          
+ * @param {Object}
+ *          widgetOptions Options controlling the appearance and behavior of the
+ *          modal dialog.
+ *          @see Drupal.media.popups.mediaBrowser.getDefaults();
+ */
+Drupal.media.popups.mediaBrowser = function (onSelect, globalOptions, pluginOptions, widgetOptions) {
+  var options = Drupal.media.popups.mediaBrowser.getDefaults();
+  options.global = $.extend({}, options.global, globalOptions);
+  options.plugins = pluginOptions;
+  options.widget = $.extend({}, options.widget, widgetOptions);
+  
+  // Create it as a modal window.
+  var browserSrc = options.widget.src;
+  // Params to send along to the iframe.  WIP.
+  var params = {};
+  $.extend(params, options.global);
+  params.plugins = options.plugins;
+
+  browserSrc += '&' + $.param(params);
+  var mediaIframe = Drupal.media.popups.getPopupIframe(browserSrc, 'mediaBrowser');
+  // Attach the onLoad event
+  mediaIframe.bind('load', options, options.widget.onLoad);
+  /**
+   * Setting up the modal dialog
+   */
+  
+  var ok = 'OK';
+  var cancel = 'Cancel';
+  var notSelected = 'You have not selected anything!';
+
+  if (Drupal && Drupal.t) {
+    ok = Drupal.t(ok);
+    cancel = Drupal.t(cancel);
+    notSelected = Drupal.t(notSelected);
+  }
+
+  // @todo: let some options come through here. Currently can't be changed.
+  var dialogOptions = options.dialog;
+
+  dialogOptions.buttons[ok] = function () {
+    var selected = this.contentWindow.Drupal.media.browser.selectedMedia;
+    if (selected.length < 1) {
+      alert(notSelected);
+      return;
+    }
+    onSelect(selected);
+    $(this).dialog("destroy");
+    $(this).remove();
+  };
+
+  dialogOptions.buttons[cancel] = function () {
+    $(this).dialog("destroy");
+    $(this).remove();
+  };
+
+  Drupal.media.popups.setDialogPadding(mediaIframe.dialog(dialogOptions));
+  // Remove the title bar.
+  mediaIframe.parents(".ui-dialog").find(".ui-dialog-titlebar").remove();
+  Drupal.media.popups.overlayDisplace(mediaIframe.parents(".ui-dialog"));
+  return mediaIframe;
+};
+
+Drupal.media.popups.mediaBrowser.mediaBrowserOnLoad = function (e) {
+  var options = e.data;
+  if (this.contentWindow.Drupal.media.browser.selectedMedia.length > 0) {
+    var ok = $(this).dialog('option', 'buttons')['OK'];
+    ok.call(this);
+    return;
+  }
+};
+
+Drupal.media.popups.mediaBrowser.getDefaults = function () {
+  return {
+    global: {
+      types: [], // Types to allow, defaults to all.
+      activePlugins: [] // If provided, a list of plugins which should be enabled.
+    },
+    widget: { // Settings for the actual iFrame which is launched.
+      src: Drupal.settings.media.browserUrl, // Src of the media browser (if you want to totally override it)
+      onLoad: Drupal.media.popups.mediaBrowser.mediaBrowserOnLoad // Onload function when iFrame loads.
+    },
+    dialog: Drupal.media.popups.getDialogOptions()
+  };
+};
+
+Drupal.media.popups.mediaBrowser.finalizeSelection = function () {
+  var selected = this.contentWindow.Drupal.media.browser.selectedMedia;
+  if (selected.length < 1) {
+    alert(notSelected);
+    return;
+  }
+  onSelect(selected);
+  $(this).dialog("destroy");
+  $(this).remove();
+}
+
+/**
+ * Style chooser Popup. Creates a dialog for a user to choose a media style.
+ *
+ * @param mediaFile
+ *          The mediaFile you are requesting this formatting form for.
+ *          @todo: should this be fid?  That's actually all we need now.
+ *
+ * @param Function
+ *          onSubmit Function to be called when the user chooses a media
+ *          style. Takes one parameter (Object formattedMedia).
+ *
+ * @param Object
+ *          options Options for the mediaStyleChooser dialog.
+ */
+Drupal.media.popups.mediaStyleSelector = function (mediaFile, onSelect, options) {
+  var defaults = Drupal.media.popups.mediaStyleSelector.getDefaults();
+  // @todo: remove this awful hack :(
+  defaults.src = defaults.src.replace('-media_id-', mediaFile.fid);
+  options = $.extend({}, defaults, options);
+  // Create it as a modal window.
+  var mediaIframe = Drupal.media.popups.getPopupIframe(options.src, 'mediaStyleSelector');
+  // Attach the onLoad event
+  mediaIframe.bind('load', options, options.onLoad);
+
+  /**
+   * Set up the button text
+   */
+  var ok = 'OK';
+  var cancel = 'Cancel';
+  var notSelected = 'Very sorry, there was an unknown error embedding media.';
+
+  if (Drupal && Drupal.t) {
+    ok = Drupal.t(ok);
+    cancel = Drupal.t(cancel);
+    notSelected = Drupal.t(notSelected);
+  }
+
+  // @todo: let some options come through here. Currently can't be changed.
+  var dialogOptions = Drupal.media.popups.getDialogOptions();
+
+  dialogOptions.buttons[ok] = function () {
+
+    var formattedMedia = this.contentWindow.Drupal.media.formatForm.getFormattedMedia();
+    if (!formattedMedia) {
+      alert(notSelected);
+      return;
+    }
+    onSelect(formattedMedia);
+    $(this).dialog("destroy");
+    $(this).remove();
+  };
+
+  dialogOptions.buttons[cancel] = function () {
+    $(this).dialog("destroy");
+    $(this).remove();
+  };
+
+  Drupal.media.popups.setDialogPadding(mediaIframe.dialog(dialogOptions));
+  // Remove the title bar.
+  mediaIframe.parents(".ui-dialog").find(".ui-dialog-titlebar").remove();
+  Drupal.media.popups.overlayDisplace(mediaIframe.parents(".ui-dialog"));
+  return mediaIframe;
+};
+
+Drupal.media.popups.mediaStyleSelector.mediaBrowserOnLoad = function (e) {
+};
+
+Drupal.media.popups.mediaStyleSelector.getDefaults = function () {
+  return {
+    src: Drupal.settings.media.styleSelectorUrl,
+    onLoad: Drupal.media.popups.mediaStyleSelector.mediaBrowserOnLoad
+  };
+};
+
+
+/**
+ * Style chooser Popup. Creates a dialog for a user to choose a media style.
+ *
+ * @param mediaFile
+ *          The mediaFile you are requesting this formatting form for.
+ *          @todo: should this be fid?  That's actually all we need now.
+ *
+ * @param Function
+ *          onSubmit Function to be called when the user chooses a media
+ *          style. Takes one parameter (Object formattedMedia).
+ *
+ * @param Object
+ *          options Options for the mediaStyleChooser dialog.
+ */
+Drupal.media.popups.mediaFieldEditor = function (fid, onSelect, options) {
+  var defaults = Drupal.media.popups.mediaFieldEditor.getDefaults();
+  // @todo: remove this awful hack :(
+  defaults.src = defaults.src.replace('-media_id-', fid);
+  options = $.extend({}, defaults, options);
+  // Create it as a modal window.
+  var mediaIframe = Drupal.media.popups.getPopupIframe(options.src, 'mediaFieldEditor');
+  // Attach the onLoad event
+  // @TODO - This event is firing too early in IE on Windows 7,
+  // - so the height being calculated is too short for the content.
+  mediaIframe.bind('load', options, options.onLoad);
+
+  /**
+   * Set up the button text
+   */
+  var ok = 'OK';
+  var cancel = 'Cancel';
+  var notSelected = 'Very sorry, there was an unknown error embedding media.';
+
+  if (Drupal && Drupal.t) {
+    ok = Drupal.t(ok);
+    cancel = Drupal.t(cancel);
+    notSelected = Drupal.t(notSelected);
+  }
+
+  // @todo: let some options come through here. Currently can't be changed.
+  var dialogOptions = Drupal.media.popups.getDialogOptions();
+
+  dialogOptions.buttons[ok] = function () {
+    alert('hell yeah');
+    return "poo";
+
+    var formattedMedia = this.contentWindow.Drupal.media.formatForm.getFormattedMedia();
+    if (!formattedMedia) {
+      alert(notSelected);
+      return;
+    }
+    onSelect(formattedMedia);
+    $(this).dialog("destroy");
+    $(this).remove();
+  };
+
+  dialogOptions.buttons[cancel] = function () {
+    $(this).dialog("destroy");
+    $(this).remove();
+  };
+
+  Drupal.media.popups.setDialogPadding(mediaIframe.dialog(dialogOptions));
+  // Remove the title bar.
+  mediaIframe.parents(".ui-dialog").find(".ui-dialog-titlebar").remove();
+  Drupal.media.popups.overlayDisplace(mediaIframe.parents(".ui-dialog"));
+  return mediaIframe;
+};
+
+Drupal.media.popups.mediaFieldEditor.mediaBrowserOnLoad = function (e) {
+
+};
+
+Drupal.media.popups.mediaFieldEditor.getDefaults = function () {
+  return {
+    // @todo: do this for real
+    src: '/media/-media_id-/edit?render=media-popup',
+    onLoad: Drupal.media.popups.mediaFieldEditor.mediaBrowserOnLoad
+  };
+};
+
+
+/**
+ * Generic functions to both the media-browser and style selector
+ */
+
+/**
+ * Returns the commonly used options for the dialog.
+ */
+Drupal.media.popups.getDialogOptions = function () {
+  return {
+    buttons: {},
+    dialogClass: 'media-wrapper',
+    modal: true,
+    draggable: false,
+    resizable: false,
+    minWidth: 600,
+    width: 800,
+    height: 550,
+    position: 'center',
+    overlay: {
+      backgroundColor: '#000000',
+      opacity: 0.4
+    }
+  };
+};
+
+/**
+ * Created padding on a dialog
+ *
+ * @param jQuery dialogElement
+ *  The element which has .dialog() attached to it.
+ */
+Drupal.media.popups.setDialogPadding = function (dialogElement) {
+  // @TODO: Perhaps remove this hardcoded reference to height.
+  // - It's included to make IE on Windows 7 display the dialog without
+  //   collapsing. 550 is the height that displays all of the tab panes
+  //   within the Add Media overlay. This is either a bug in the jQuery
+  //   UI library, a bug in IE on Windows 7 or a bug in the way the 
+  //   dialog is instantiated. Or a combo of the three.
+  //   All browsers except IE on Win7 ignore these defaults and adjust
+  //   the height of the iframe correctly to match the content in the panes
+  dialogElement.height(dialogElement.dialog('option', 'height'));
+  dialogElement.width(dialogElement.dialog('option', 'width'));
+};
+
+/**
+ * Get an iframe to serve as the dialog's contents. Common to both plugins.
+ */
+Drupal.media.popups.getPopupIframe = function (src, id, options) {
+  var defaults = {width: '800px', scrolling: 'no'};
+  var options = $.extend({}, defaults, options);
+
+  return $('<iframe class="media-modal-frame"/>')
+  .attr('src', src)
+  .attr('width', options.width)
+  .attr('id', id)
+  .attr('scrolling', options.scrolling);
+};
+
+Drupal.media.popups.overlayDisplace = function (dialog) {
+  if (parent.window.Drupal.overlay) {
+    var overlayDisplace = parent.window.Drupal.overlay.getDisplacement('top');
+    if (dialog.offset().top < overlayDisplace) {
+      dialog.css('top', overlayDisplace);
+    }
+  }
+}
+
+})(jQuery);
diff --git a/js/plugins/media.fromurl.js b/js/plugins/media.fromurl.js
new file mode 100644
index 0000000..f618e23
--- /dev/null
+++ b/js/plugins/media.fromurl.js
@@ -0,0 +1,29 @@
+
+(function ($) {
+namespace('Drupal.media.browser.plugin');
+
+Drupal.media.browser.plugin.fromurl = function (mediaBrowser, options) {
+  return {
+    init: function () {
+      tabset = mediaBrowser.getTabset();
+      tabset.tabs('add', '#fromurl', 'From URL');
+      mediaBrowser.listen('tabs.show', function (e, id) {
+        if (id == 'fromurl') {
+          // We only need to set this once.
+          // We probably could set it upon load.
+          if (mediaBrowser.getActivePanel().html() == '') {
+            mediaBrowser.getActivePanel().html(options.fromUrlForm);
+          }
+        }
+      });
+    }
+  };
+};
+
+// For now, I guess self registration makes sense.
+// Really though, we should be doing it via drupal_add_js and some settings
+// from the drupal variable.
+// @todo: needs a review.
+Drupal.media.browser.register('fromurl', Drupal.media.browser.plugin.fromurl, {});
+
+})(jQuery);
diff --git a/js/plugins/media.library.css b/js/plugins/media.library.css
new file mode 100644
index 0000000..dbd68dc
--- /dev/null
+++ b/js/plugins/media.library.css
@@ -0,0 +1,23 @@
+
+ul#media-browser-library-list {
+  list-style: none;
+  margin: -10px 0 0 0;
+  padding: 0px;
+  background-color: white;
+}
+
+#media-tab-library #container{
+  padding:20px 0;
+}
+
+#media-tab-library #scrollbox{
+  height: 300px;
+  overflow:auto;
+  overflow-x:hidden;
+}
+
+#scrollbox #status {
+  clear: both;
+  text-align: center;
+  margin-top: 10px;
+}
diff --git a/js/plugins/media.library.js b/js/plugins/media.library.js
new file mode 100644
index 0000000..c3b1ee5
--- /dev/null
+++ b/js/plugins/media.library.js
@@ -0,0 +1,184 @@
+
+(function ($) {
+namespace('Drupal.media.browser');
+
+Drupal.behaviors.mediaLibrary = {
+  attach: function (context, settings) {
+    var library = new Drupal.media.browser.library(Drupal.settings.media.browser.library);
+    $('#media-browser-tabset').bind('tabsselect', function (event, ui) {
+      if (ui.tab.hash === '#media-tab-library') {
+        // Grab the parameters from the Drupal.settings object
+        var params = {};
+        for (var parameter in Drupal.settings.media.browser.library) {
+          params[parameter] = Drupal.settings.media.browser.library[parameter];
+        }
+        library.start($(ui.panel), params);
+        $('#scrollbox').bind('scroll', library, library.scrollUpdater);
+      }
+    });
+  }
+};
+
+Drupal.media.browser.library = function (settings) {
+  this.settings = Drupal.media.browser.library.getDefaults();
+  $.extend(this.settings, settings);
+
+  this.done = false; // Keeps track of if the last request for media returned 0 results.
+
+  this.cursor = 0; // keeps track of what the last requested media object was.
+  this.mediaFiles = []; // An array of loaded media files from the server.
+  this.selectedMediaFiles = [];
+};
+
+Drupal.media.browser.library.getDefaults = function () {
+  return {
+    emtpyMessage: "There is nothing in your media library.  Select the Upload tab above to add a file.",
+    limit: 15
+  };
+};
+
+Drupal.media.browser.library.prototype.start = function (renderElement, params) {
+  this.renderElement = renderElement;
+  this.params = params;
+  // Change the behavior dependent on multiselect
+  if (params.multiselect) {
+    this.clickFunction = this.multiSelect;
+  } else {
+    this.clickFunction = this.singleSelect;
+  }
+  this.loadMedia();
+};
+
+/**
+ * Appends more media onto the list
+ */
+Drupal.media.browser.library.prototype.loadMedia = function () {
+  var that = this;
+  $('#status').text('Loading...').show();
+  $.extend(this.params, {start: this.cursor, limit: this.settings.limit});
+
+  var gotMedia = function (data, status) {
+    $('#status').text('').hide();
+    if (data.media.length < that.params.limit) {
+      // We remove the scroll event listener, nothing more to load after this.
+      $('#scrollbox').unbind('scroll');
+    }
+    that.mediaFiles = that.mediaFiles.concat(data.media);
+    that.render(that.renderElement);
+    // Remove the flag that prevents loading of more media
+    that.loading = false;
+  };
+
+  var errorCallback = function () {
+    alert('Error getting media.');
+  };
+
+  $.ajax({
+    url: this.settings.getMediaUrl,
+    type: 'GET',
+    dataType: 'json',
+    data: this.params,
+    error: errorCallback,
+    success: gotMedia
+  });
+};
+
+Drupal.media.browser.library.prototype.scrollUpdater = function (e){
+  if (!e.data.loading) {
+    var scrollbox = $('#scrollbox');
+    var scrolltop = scrollbox.attr('scrollTop');
+    var scrollheight = scrollbox.attr('scrollHeight');
+    var windowheight = scrollbox.attr('clientHeight');
+    var scrolloffset = 20;
+
+    if(scrolltop >= (scrollheight - (windowheight + scrolloffset))) {
+      // Set a flag so we don't make multiple concurrent AJAX calls
+      e.data.loading = true;
+      // Fetch new items
+      e.data.loadMedia();
+    }
+  }
+};
+
+/**
+ * Fetches the next media object and increments the cursor.
+ */
+Drupal.media.browser.library.prototype.getNextMedia = function () {
+  if (this.cursor >= this.mediaFiles.length) {
+    return false;
+  }
+  var ret = this.mediaFiles[this.cursor];
+  this.cursor += 1;
+  return ret;
+};
+
+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);
+  }
+};
+
+Drupal.media.browser.library.prototype.mediaSelected = function (media) {
+  Drupal.media.browser.selectMedia(media);
+};
+
+Drupal.media.browser.library.prototype.singleSelect = function (event) {
+  var lib = event.data.obj;
+  var file = event.data.file;
+  event.preventDefault();
+  event.stopPropagation();
+
+  $('.media-item').removeClass('selected');
+  $('.media-item', $(this)).addClass('selected');
+  lib.mediaSelected([event.data.file]);
+  return false;
+}
+
+Drupal.media.browser.library.prototype.multiSelect = function (event) {
+  var lib = event.data.obj
+  var file = event.data.file;
+  event.preventDefault();
+  event.stopPropagation();
+
+  // Turn off or on the selection of this item
+  $('.media-item', $(this)).toggleClass('selected');
+
+  // Add or remove the media file from the array
+  var index = $.inArray(file, lib.selectedMediaFiles);
+  if (index == -1) {
+    // Media file isn't selected, add it
+    lib.selectedMediaFiles.push(file);
+  } else {
+    // Media file has previously been selected, remove it
+    lib.selectedMediaFiles.splice(index, 1);
+  }
+
+  // Pass the array of selected media files to the invoker
+  lib.mediaSelected(lib.selectedMediaFiles);
+  return false;
+}
+
+}(jQuery));
diff --git a/js/plugins/media.upload.js b/js/plugins/media.upload.js
new file mode 100644
index 0000000..6f0117d
--- /dev/null
+++ b/js/plugins/media.upload.js
@@ -0,0 +1,30 @@
+
+(function ($) {
+namespace('Drupal.media.browser.plugin');
+
+Drupal.media.browser.plugin.upload = function (mediaBrowser, options) {
+  return {
+    /* Abstract */
+    init: function () {
+      tabset = mediaBrowser.getTabset();
+      tabset.tabs('add', '#upload', 'Upload');
+      mediaBrowser.listen('tabs.show', function (e, id) {
+        if (id == 'upload') {
+          // We only need to set this once.
+          // We probably could set it upon load.
+          if (mediaBrowser.getActivePanel().html() == '') {
+            mediaBrowser.getActivePanel().html(options.uploadForm);
+          }
+        }
+      });
+    }
+  };
+};
+
+// For now, I guess self registration makes sense.
+// Really though, we should be doing it via drupal_add_js and some settings
+// from the drupal variable.
+// @todo: needs a review.
+Drupal.media.browser.register('upload', Drupal.media.browser.plugin.upload, {});
+
+})(jQuery);
diff --git a/js/plugins/media.upload_multiple.js b/js/plugins/media.upload_multiple.js
new file mode 100755
index 0000000..c82826b
--- /dev/null
+++ b/js/plugins/media.upload_multiple.js
@@ -0,0 +1,13 @@
+Drupal.behaviors.mediaUploadMultiple = {};
+
+Drupal.behaviors.mediaUploadMultiple.attach = function (context, settings) {
+  // When the plupload element initializes, it expands the size of the elements
+  // it has created, so we need to resize the browser iframe after it's done.
+  var uploader = jQuery('#edit-upload').pluploadQueue();
+  if (uploader) {
+    // Handle the case in which the uploader has already finished initializing.
+    Drupal.media.browser.resizeIframe();
+    // Handle the case in which the uploader has not yet initialized.
+    uploader.bind("PostInit", Drupal.media.browser.resizeIframe);
+  }
+};
diff --git a/js/util/ba-debug.min.js b/js/util/ba-debug.min.js
new file mode 100644
index 0000000..e442a42
--- /dev/null
+++ b/js/util/ba-debug.min.js
@@ -0,0 +1,12 @@
+/*
+ * debug - v0.3 - 6/8/2009
+ * http://benalman.com/projects/javascript-debug-console-log/
+ * 
+ * Copyright (c) 2009 "Cowboy" Ben Alman
+ * Licensed under the MIT license
+ * http://benalman.com/about/license/
+ * 
+ * With lots of help from Paul Irish!
+ * http://paulirish.com/
+ */
+window.debug=(function(){var c=this,e=Array.prototype.slice,b=c.console,i={},f,g,j=9,d=["error","warn","info","debug","log"],m="assert clear count dir dirxml group groupEnd profile profileEnd time timeEnd trace".split(" "),k=m.length,a=[];while(--k>=0){(function(n){i[n]=function(){j!==0&&b&&b[n]&&b[n].apply(b,arguments)}})(m[k])}k=d.length;while(--k>=0){(function(n,o){i[o]=function(){var q=e.call(arguments),p=[o].concat(q);a.push(p);h(p);if(!b||!l(n)){return}b.firebug?b[o].apply(c,q):b[o]?b[o](q):b.log(q)}})(k,d[k])}function h(n){if(f&&(g||!b||!b.log)){f.apply(c,n)}}i.setLevel=function(n){j=typeof n==="number"?n:9};function l(n){return j>0?j>n:d.length+j<=n}i.setCallback=function(){var o=e.call(arguments),n=a.length,p=n;f=o.shift()||null;g=typeof o[0]==="boolean"?o.shift():false;p-=typeof o[0]==="number"?o.shift():n;while(p<n){h(a[p++])}};return i})();
\ No newline at end of file
diff --git a/js/util/json2.js b/js/util/json2.js
new file mode 100644
index 0000000..39d8f37
--- /dev/null
+++ b/js/util/json2.js
@@ -0,0 +1,481 @@
+/*
+    http://www.JSON.org/json2.js
+    2009-09-29
+
+    Public Domain.
+
+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+
+    See http://www.JSON.org/js.html
+
+
+    This code should be minified before deployment.
+    See http://javascript.crockford.com/jsmin.html
+
+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
+    NOT CONTROL.
+
+
+    This file creates a global JSON object containing two methods: stringify
+    and parse.
+
+        JSON.stringify(value, replacer, space)
+            value       any JavaScript value, usually an object or array.
+
+            replacer    an optional parameter that determines how object
+                        values are stringified for objects. It can be a
+                        function or an array of strings.
+
+            space       an optional parameter that specifies the indentation
+                        of nested structures. If it is omitted, the text will
+                        be packed without extra whitespace. If it is a number,
+                        it will specify the number of spaces to indent at each
+                        level. If it is a string (such as '\t' or '&nbsp;'),
+                        it contains the characters used to indent at each level.
+
+            This method produces a JSON text from a JavaScript value.
+
+            When an object value is found, if the object contains a toJSON
+            method, its toJSON method will be called and the result will be
+            stringified. A toJSON method does not serialize: it returns the
+            value represented by the name/value pair that should be serialized,
+            or undefined if nothing should be serialized. The toJSON method
+            will be passed the key associated with the value, and this will be
+            bound to the value
+
+            For example, this would serialize Dates as ISO strings.
+
+                Date.prototype.toJSON = function (key) {
+                    function f(n) {
+                        // Format integers to have at least two digits.
+                        return n < 10 ? '0' + n : n;
+                    }
+
+                    return this.getUTCFullYear()   + '-' +
+                         f(this.getUTCMonth() + 1) + '-' +
+                         f(this.getUTCDate())      + 'T' +
+                         f(this.getUTCHours())     + ':' +
+                         f(this.getUTCMinutes())   + ':' +
+                         f(this.getUTCSeconds())   + 'Z';
+                };
+
+            You can provide an optional replacer method. It will be passed the
+            key and value of each member, with this bound to the containing
+            object. The value that is returned from your method will be
+            serialized. If your method returns undefined, then the member will
+            be excluded from the serialization.
+
+            If the replacer parameter is an array of strings, then it will be
+            used to select the members to be serialized. It filters the results
+            such that only members with keys listed in the replacer array are
+            stringified.
+
+            Values that do not have JSON representations, such as undefined or
+            functions, will not be serialized. Such values in objects will be
+            dropped; in arrays they will be replaced with null. You can use
+            a replacer function to replace those with JSON values.
+            JSON.stringify(undefined) returns undefined.
+
+            The optional space parameter produces a stringification of the
+            value that is filled with line breaks and indentation to make it
+            easier to read.
+
+            If the space parameter is a non-empty string, then that string will
+            be used for indentation. If the space parameter is a number, then
+            the indentation will be that many spaces.
+
+            Example:
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}]);
+            // text is '["e",{"pluribus":"unum"}]'
+
+
+            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
+
+            text = JSON.stringify([new Date()], function (key, value) {
+                return this[key] instanceof Date ?
+                    'Date(' + this[key] + ')' : value;
+            });
+            // text is '["Date(---current time---)"]'
+
+
+        JSON.parse(text, reviver)
+            This method parses a JSON text to produce an object or array.
+            It can throw a SyntaxError exception.
+
+            The optional reviver parameter is a function that can filter and
+            transform the results. It receives each of the keys and values,
+            and its return value is used instead of the original value.
+            If it returns what it received, then the structure is not modified.
+            If it returns undefined then the member is deleted.
+
+            Example:
+
+            // Parse the text. Values that look like ISO date strings will
+            // be converted to Date objects.
+
+            myData = JSON.parse(text, function (key, value) {
+                var a;
+                if (typeof value === 'string') {
+                    a =
+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
+                    if (a) {
+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+                            +a[5], +a[6]));
+                    }
+                }
+                return value;
+            });
+
+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
+                var d;
+                if (typeof value === 'string' &&
+                        value.slice(0, 5) === 'Date(' &&
+                        value.slice(-1) === ')') {
+                    d = new Date(value.slice(5, -1));
+                    if (d) {
+                        return d;
+                    }
+                }
+                return value;
+            });
+
+
+    This is a reference implementation. You are free to copy, modify, or
+    redistribute.
+*/
+
+/*jslint evil: true, strict: false */
+
+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
+    lastIndex, length, parse, prototype, push, replace, slice, stringify,
+    test, toJSON, toString, valueOf
+*/
+
+
+// Create a JSON object only if one does not already exist. We create the
+// methods in a closure to avoid creating global variables.
+
+if (!this.JSON) {
+    this.JSON = {};
+}
+
+(function () {
+
+    function f(n) {
+        // Format integers to have at least two digits.
+        return n < 10 ? '0' + n : n;
+    }
+
+    if (typeof Date.prototype.toJSON !== 'function') {
+
+        Date.prototype.toJSON = function (key) {
+
+            return isFinite(this.valueOf()) ?
+                   this.getUTCFullYear()   + '-' +
+                 f(this.getUTCMonth() + 1) + '-' +
+                 f(this.getUTCDate())      + 'T' +
+                 f(this.getUTCHours())     + ':' +
+                 f(this.getUTCMinutes())   + ':' +
+                 f(this.getUTCSeconds())   + 'Z' : null;
+        };
+
+        String.prototype.toJSON =
+        Number.prototype.toJSON =
+        Boolean.prototype.toJSON = function (key) {
+            return this.valueOf();
+        };
+    }
+
+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+        gap,
+        indent,
+        meta = {    // table of character substitutions
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        },
+        rep;
+
+
+    function quote(string) {
+
+// If the string contains no control characters, no quote characters, and no
+// backslash characters, then we can safely slap some quotes around it.
+// Otherwise we must also replace the offending characters with safe escape
+// sequences.
+
+        escapable.lastIndex = 0;
+        return escapable.test(string) ?
+            '"' + string.replace(escapable, function (a) {
+                var c = meta[a];
+                return typeof c === 'string' ? c :
+                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+            }) + '"' :
+            '"' + string + '"';
+    }
+
+
+    function str(key, holder) {
+
+// Produce a string from holder[key].
+
+        var i,          // The loop counter.
+            k,          // The member key.
+            v,          // The member value.
+            length,
+            mind = gap,
+            partial,
+            value = holder[key];
+
+// If the value has a toJSON method, call it to obtain a replacement value.
+
+        if (value && typeof value === 'object' &&
+                typeof value.toJSON === 'function') {
+            value = value.toJSON(key);
+        }
+
+// If we were called with a replacer function, then call the replacer to
+// obtain a replacement value.
+
+        if (typeof rep === 'function') {
+            value = rep.call(holder, key, value);
+        }
+
+// What happens next depends on the value's type.
+
+        switch (typeof value) {
+        case 'string':
+            return quote(value);
+
+        case 'number':
+
+// JSON numbers must be finite. Encode non-finite numbers as null.
+
+            return isFinite(value) ? String(value) : 'null';
+
+        case 'boolean':
+        case 'null':
+
+// If the value is a boolean or null, convert it to a string. Note:
+// typeof null does not produce 'null'. The case is included here in
+// the remote chance that this gets fixed someday.
+
+            return String(value);
+
+// If the type is 'object', we might be dealing with an object or an array or
+// null.
+
+        case 'object':
+
+// Due to a specification blunder in ECMAScript, typeof null is 'object',
+// so watch out for that case.
+
+            if (!value) {
+                return 'null';
+            }
+
+// Make an array to hold the partial results of stringifying this object value.
+
+            gap += indent;
+            partial = [];
+
+// Is the value an array?
+
+            if (Object.prototype.toString.apply(value) === '[object Array]') {
+
+// The value is an array. Stringify every element. Use null as a placeholder
+// for non-JSON values.
+
+                length = value.length;
+                for (i = 0; i < length; i += 1) {
+                    partial[i] = str(i, value) || 'null';
+                }
+
+// Join all of the elements together, separated with commas, and wrap them in
+// brackets.
+
+                v = partial.length === 0 ? '[]' :
+                    gap ? '[\n' + gap +
+                            partial.join(',\n' + gap) + '\n' +
+                                mind + ']' :
+                          '[' + partial.join(',') + ']';
+                gap = mind;
+                return v;
+            }
+
+// If the replacer is an array, use it to select the members to be stringified.
+
+            if (rep && typeof rep === 'object') {
+                length = rep.length;
+                for (i = 0; i < length; i += 1) {
+                    k = rep[i];
+                    if (typeof k === 'string') {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            } else {
+
+// Otherwise, iterate through all of the keys in the object.
+
+                for (k in value) {
+                    if (Object.hasOwnProperty.call(value, k)) {
+                        v = str(k, value);
+                        if (v) {
+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
+                        }
+                    }
+                }
+            }
+
+// Join all of the member texts together, separated with commas,
+// and wrap them in braces.
+
+            v = partial.length === 0 ? '{}' :
+                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
+                        mind + '}' : '{' + partial.join(',') + '}';
+            gap = mind;
+            return v;
+        }
+    }
+
+// If the JSON object does not yet have a stringify method, give it one.
+
+    if (typeof JSON.stringify !== 'function') {
+        JSON.stringify = function (value, replacer, space) {
+
+// The stringify method takes a value and an optional replacer, and an optional
+// space parameter, and returns a JSON text. The replacer can be a function
+// that can replace values, or an array of strings that will select the keys.
+// A default replacer method can be provided. Use of the space parameter can
+// produce text that is more easily readable.
+
+            var i;
+            gap = '';
+            indent = '';
+
+// If the space parameter is a number, make an indent string containing that
+// many spaces.
+
+            if (typeof space === 'number') {
+                for (i = 0; i < space; i += 1) {
+                    indent += ' ';
+                }
+
+// If the space parameter is a string, it will be used as the indent string.
+
+            } else if (typeof space === 'string') {
+                indent = space;
+            }
+
+// If there is a replacer, it must be a function or an array.
+// Otherwise, throw an error.
+
+            rep = replacer;
+            if (replacer && typeof replacer !== 'function' &&
+                    (typeof replacer !== 'object' ||
+                     typeof replacer.length !== 'number')) {
+                throw new Error('JSON.stringify');
+            }
+
+// Make a fake root object containing our value under the key of ''.
+// Return the result of stringifying the value.
+
+            return str('', {'': value});
+        };
+    }
+
+
+// If the JSON object does not yet have a parse method, give it one.
+
+    if (typeof JSON.parse !== 'function') {
+        JSON.parse = function (text, reviver) {
+
+// The parse method takes a text and an optional reviver function, and returns
+// a JavaScript value if the text is a valid JSON text.
+
+            var j;
+
+            function walk(holder, key) {
+
+// The walk method is used to recursively walk the resulting structure so
+// that modifications can be made.
+
+                var k, v, value = holder[key];
+                if (value && typeof value === 'object') {
+                    for (k in value) {
+                        if (Object.hasOwnProperty.call(value, k)) {
+                            v = walk(value, k);
+                            if (v !== undefined) {
+                                value[k] = v;
+                            } else {
+                                delete value[k];
+                            }
+                        }
+                    }
+                }
+                return reviver.call(holder, key, value);
+            }
+
+
+// Parsing happens in four stages. In the first stage, we replace certain
+// Unicode characters with escape sequences. JavaScript handles many characters
+// incorrectly, either silently deleting them, or treating them as line endings.
+
+            cx.lastIndex = 0;
+            if (cx.test(text)) {
+                text = text.replace(cx, function (a) {
+                    return '\\u' +
+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+                });
+            }
+
+// In the second stage, we run the text against regular expressions that look
+// for non-JSON patterns. We are especially concerned with '()' and 'new'
+// because they can cause invocation, and '=' because it can cause mutation.
+// But just to be safe, we want to reject all unexpected forms.
+
+// We split the second stage into 4 regexp operations in order to work around
+// crippling inefficiencies in IE's and Safari's regexp engines. First we
+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
+// replace all simple value tokens with ']' characters. Third, we delete all
+// open brackets that follow a colon or comma or that begin the text. Finally,
+// we look to see that the remaining characters are only whitespace or ']' or
+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
+
+            if (/^[\],:{}\s]*$/.
+test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
+replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
+replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
+
+// In the third stage we use the eval function to compile the text into a
+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
+// in JavaScript: it can begin a block or an object literal. We wrap the text
+// in parens to eliminate the ambiguity.
+
+                j = eval('(' + text + ')');
+
+// In the optional fourth stage, we recursively walk the new structure, passing
+// each name/value pair to a reviver function for possible transformation.
+
+                return typeof reviver === 'function' ?
+                    walk({'': j}, '') : j;
+            }
+
+// If the text is not JSON parseable, then a SyntaxError is thrown.
+
+            throw new SyntaxError('JSON.parse');
+        };
+    }
+}());
diff --git a/js/wysiwyg-media.js b/js/wysiwyg-media.js
new file mode 100644
index 0000000..156229a
--- /dev/null
+++ b/js/wysiwyg-media.js
@@ -0,0 +1,365 @@
+
+/**
+ *  @file
+ *  Attach Media WYSIWYG behaviors.
+ */
+
+(function ($) {
+
+Drupal.media = Drupal.media || {};
+
+// Define the behavior.
+Drupal.wysiwyg.plugins.media = {
+
+  /**
+   * Initializes the tag map.
+   */
+  initializeTagMap: function () {
+    if (typeof Drupal.settings.tagmap == 'undefined') {
+      Drupal.settings.tagmap = { };
+    }
+  },
+  /**
+   * Execute the button.
+   * @TODO: Debug calls from this are never called. What's its function?
+   */
+  invoke: function (data, settings, instanceId) {
+    if (data.format == 'html') {
+      Drupal.media.popups.mediaBrowser(function (mediaFiles) {
+        Drupal.wysiwyg.plugins.media.mediaBrowserOnSelect(mediaFiles, instanceId);
+      }, settings['global']);
+    }
+  },
+
+  /**
+   * Respond to the mediaBrowser's onSelect event.
+   * @TODO: Debug calls from this are never called. What's its function?
+   */
+  mediaBrowserOnSelect: function (mediaFiles, instanceId) {
+    var mediaFile = mediaFiles[0];
+    var options = {};
+    Drupal.media.popups.mediaStyleSelector(mediaFile, function (formattedMedia) {
+      Drupal.wysiwyg.plugins.media.insertMediaFile(mediaFile, formattedMedia.type, formattedMedia.html, formattedMedia.options, Drupal.wysiwyg.instances[instanceId]);
+    }, options);
+
+    return;
+  },
+
+  insertMediaFile: function (mediaFile, viewMode, formattedMedia, options, wysiwygInstance) {
+
+    this.initializeTagMap();
+    // @TODO: the folks @ ckeditor have told us that there is no way
+    // to reliably add wrapper divs via normal HTML.
+    // There is some method of adding a "fake element"
+    // But until then, we're just going to embed to img.
+    // This is pretty hacked for now.
+    //
+    var imgElement = $(this.stripDivs(formattedMedia));
+    this.addImageAttributes(imgElement, mediaFile.fid, viewMode, options);
+
+    var toInsert = this.outerHTML(imgElement);
+    // Create an inline tag
+    var inlineTag = Drupal.wysiwyg.plugins.media.createTag(imgElement);
+    // Add it to the tag map in case the user switches input formats
+    Drupal.settings.tagmap[inlineTag] = toInsert;
+    wysiwygInstance.insert(toInsert);
+  },
+
+  /**
+   * Gets the HTML content of an element
+   *
+   * @param jQuery element
+   */
+  outerHTML: function (element) {
+    return $('<div>').append( element.eq(0).clone() ).html();
+  },
+
+  addImageAttributes: function (imgElement, fid, view_mode, additional) {
+    //    imgElement.attr('fid', fid);
+    //    imgElement.attr('view_mode', view_mode);
+    // Class so we can find this image later.
+    imgElement.addClass('media-image');
+    this.forceAttributesIntoClass(imgElement, fid, view_mode, additional);
+    if (additional) {
+      for (k in additional) {
+        if (additional.hasOwnProperty(k)) {
+          if (k === 'attr') {
+            imgElement.attr(k, additional[k]);
+          }
+        }
+      }
+    }
+  },
+
+  /**
+   * Due to problems handling wrapping divs in ckeditor, this is needed.
+   *
+   * Going forward, if we don't care about supporting other editors
+   * we can use the fakeobjects plugin to ckeditor to provide cleaner
+   * transparency between what Drupal will output <div class="field..."><img></div>
+   * instead of just <img>, for now though, we're going to remove all the stuff surrounding the images.
+   *
+   * @param String formattedMedia
+   *  Element containing the image
+   *
+   * @return HTML of <img> tag inside formattedMedia
+   */
+  stripDivs: function (formattedMedia) {
+    // Check to see if the image tag has divs to strip
+    var stripped = null;
+    if ($(formattedMedia).is('img')) {
+      stripped = this.outerHTML($(formattedMedia));
+    } else {
+      stripped = this.outerHTML($('img', $(formattedMedia)));
+    }
+    // This will fail if we pass the img tag without anything wrapping it, like we do when re-enabling WYSIWYG
+    return stripped;
+  },
+
+  /**
+   * Attach function, called when a rich text editor loads.
+   * This finds all [[tags]] and replaces them with the html
+   * that needs to show in the editor.
+   *
+   */
+  attach: function (content, settings, instanceId) {
+    var matches = content.match(/\[\[.*?\]\]/g);
+    this.initializeTagMap();
+    var tagmap = Drupal.settings.tagmap;
+    if (matches) {
+      var inlineTag = "";
+      for (i = 0; i < matches.length; i++) {
+        inlineTag = matches[i];
+        if (tagmap[inlineTag]) {
+          // This probably needs some work...
+          // We need to somehow get the fid propogated here.
+          // We really want to
+          var tagContent = tagmap[inlineTag];
+          var mediaMarkup = this.stripDivs(tagContent); // THis is <div>..<img>
+
+          var _tag = inlineTag;
+          _tag = _tag.replace('[[','');
+          _tag = _tag.replace(']]','');
+          try {
+            mediaObj = JSON.parse(_tag);
+          }
+          catch(err) {
+            mediaObj = null;
+          }
+          if(mediaObj) {
+            var imgElement = $(mediaMarkup);
+            this.addImageAttributes(imgElement, mediaObj.fid, mediaObj.view_mode);
+            var toInsert = this.outerHTML(imgElement);
+            content = content.replace(inlineTag, toInsert);
+          }
+        }
+        else {
+          debug.debug("Could not find content for " + inlineTag);
+        }
+      }
+    }
+    return content;
+  },
+
+  /**
+   * Detach function, called when a rich text editor detaches
+   */
+  detach: function (content, settings, instanceId) {
+    var content = $('<div>' + content + '</div>');
+    $('img.media-image',content).each(function (elem) {
+      var tag = Drupal.wysiwyg.plugins.media.createTag($(this));
+      $(this).replaceWith(tag);
+      var newContent = content.html();
+      var tagContent = $('<div></div>').append($(this)).html();
+      Drupal.settings.tagmap[tag] = tagContent;
+    });
+    return content.html();
+  },
+
+  /**
+   * @param jQuery imgNode
+   *  Image node to create tag from
+   */
+  createTag: function (imgNode) {
+    // Currently this is the <img> itself
+    // Collect all attribs to be stashed into tagContent
+    var mediaAttributes = {};
+    var imgElement = imgNode[0];
+    var sorter = [];
+
+    // @todo: this does not work in IE, width and height are always 0.
+    for (i=0; i< imgElement.attributes.length; i++) {
+      var attr = imgElement.attributes[i];
+      if (attr.specified == true) {
+        if (attr.name !== 'class') {
+          sorter.push(attr);
+        }
+        else {
+          // Exctract expando properties from the class field.
+          var attributes = this.getAttributesFromClass(attr.value);
+          for (var name in attributes) {
+            if (attributes.hasOwnProperty(name)) {
+              var value = attributes[name];
+              if (value.type && value.type === 'attr') {
+                sorter.push(value);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    sorter.sort(this.sortAttributes);
+    
+    for (var prop in sorter) {
+      mediaAttributes[sorter[prop].name] = sorter[prop].value;
+    }
+
+    // The following 5 ifs are dedicated to IE7
+    // If the style is null, it is because IE7 can't read values from itself
+    if (jQuery.browser.msie && jQuery.browser.version == '7.0') {
+      if (mediaAttributes.style === "null") {
+        var imgHeight = imgNode.css('height');
+        var imgWidth = imgNode.css('width');
+        mediaAttributes.style = {
+          height: imgHeight,
+          width: imgWidth
+        }
+        if (!mediaAttributes['width']) {
+          mediaAttributes['width'] = imgWidth;
+        }
+        if (!mediaAttributes['height']) {
+          mediaAttributes['height'] = imgHeight;
+        }
+      }
+      // If the attribute width is zero, get the CSS width
+      if (Number(mediaAttributes['width']) === 0) {
+        mediaAttributes['width'] = imgNode.css('width');
+      }
+      // IE7 does support 'auto' as a value of the width attribute. It will not
+      // display the image if this value is allowed to pass through
+      if (mediaAttributes['width'] === 'auto') {
+        delete mediaAttributes['width'];
+      }
+      // If the attribute height is zero, get the CSS height
+      if (Number(mediaAttributes['height']) === 0) {
+        mediaAttributes['height'] = imgNode.css('height');
+      }
+      // IE7 does support 'auto' as a value of the height attribute. It will not
+      // display the image if this value is allowed to pass through
+      if (mediaAttributes['height'] === 'auto') {
+        delete mediaAttributes['height'];
+      }
+    }
+
+    // Remove elements from attribs using the blacklist
+    for (var blackList in Drupal.settings.media.blacklist) {
+      delete mediaAttributes[Drupal.settings.media.blacklist[blackList]];
+    }
+    tagContent = {
+      "type": 'media',
+      // @todo: This will be selected from the format form
+      "view_mode": attributes['view_mode'].value,
+      "fid" : attributes['fid'].value,
+      "attributes": mediaAttributes
+    };
+    return '[[' + JSON.stringify(tagContent) + ']]';
+  },
+
+  /**
+   * Forces custom attributes into the class field of the specified image.
+   *
+   * Due to a bug in some versions of Firefox
+   * (http://forums.mozillazine.org/viewtopic.php?f=9&t=1991855), the
+   * custom attributes used to share information about the image are
+   * being stripped as the image markup is set into the rich text
+   * editor.  Here we encode these attributes into the class field so
+   * the data survives.
+   *
+   * @param imgElement
+   *   The image
+   * @fid
+   *   The file id.
+   * @param view_mode
+   *   The view mode.
+   * @param additional
+   *   Additional attributes to add to the image.
+   */
+  forceAttributesIntoClass: function (imgElement, fid, view_mode, additional) {
+    var wysiwyg = imgElement.attr('wysiwyg');
+    if (wysiwyg) {
+      imgElement.addClass('attr__wysiwyg__' + wysiwyg);
+    }
+    var format = imgElement.attr('format');
+    if (format) {
+      imgElement.addClass('attr__format__' + format);
+    }
+    var typeOf = imgElement.attr('typeof');
+    if (typeOf) {
+      imgElement.addClass('attr__typeof__' + typeOf);
+    }
+    if (fid) {
+      imgElement.addClass('img__fid__' + fid);
+    }
+    if (view_mode) {
+      imgElement.addClass('img__view_mode__' + view_mode);
+    }
+    if (additional) {
+      for (var name in additional) {
+        if (additional.hasOwnProperty(name)) {
+          if (name !== 'alt') {
+            imgElement.addClass('attr__' + name + '__' + additional[name]);
+          }
+        }
+      }
+    }
+  },
+
+  /**
+   * Retrieves encoded attributes from the specified class string.
+   *
+   * @param classString
+   *   A string containing the value of the class attribute.
+   * @return
+   *   An array containing the attribute names as keys, and an object
+   *   with the name, value, and attribute type (either 'attr' or
+   *   'img', depending on whether it is an image attribute or should
+   *   be it the attributes section)
+   */
+  getAttributesFromClass: function (classString) {
+    var actualClasses = [];
+    var otherAttributes = [];
+    var classes = classString.split(' ');
+    var regexp = new RegExp('^(attr|img)__([^\S]*)__([^\S]*)$');
+    for (var index = 0; index < classes.length; index++) {
+      var matches = classes[index].match(regexp);
+      if (matches && matches.length === 4) {
+        otherAttributes[matches[2]] = {name: matches[2], value: matches[3], type: matches[1]};
+      }
+      else {
+        actualClasses.push(classes[index]);
+      }
+    }
+    if (actualClasses.length > 0) {
+      otherAttributes['class'] = {name: 'class', value: actualClasses.join(' '), type: 'attr'};
+    }
+    return otherAttributes;
+  },
+
+  /*
+   * 
+   */
+  sortAttributes: function (a, b) {
+    var nameA = a.name.toLowerCase();
+    var nameB = b.name.toLowerCase();
+    if (nameA < nameB) {
+      return -1;
+    }
+    if (nameA > nameB) {
+      return 1;
+    }
+    return 0;
+  }
+};
+
+})(jQuery);
diff --git a/media.admin.inc b/media.admin.inc
deleted file mode 100644
index 4e968c8..0000000
--- a/media.admin.inc
+++ /dev/null
@@ -1,661 +0,0 @@
-<?php
-
-/**
- * @file
- * This file contains the admin functions for the Media module.
- */
-
-/**
- * Include media.pages.inc since it has some form definitions we will use.
- */
-
-require_once dirname(__FILE__) . '/media.pages.inc';
-
-/**
- * Display the list or thumbnails media admin display.
- */
-function media_admin($form, $form_state) {
-  global $user;
-  
-  $path = drupal_get_path('module', 'media');
-
-  $form['#attached'] = array(
-    'js' => array($path . '/javascript/media-admin.js'),
-    'css' => array($path . '/css/media.css'),
-  );
-
-  if (isset($form_state['values']['operation']) && $form_state['values']['operation'] == 'delete') {
-    $form['#attributes']['class'][] = "media-list-operation";
-    return media_multiple_delete_confirm($form, $form_state, array_filter($form_state['values']['files']), 'admin/content/media', 'admin/content/media');
-  }
-
-  require_once dirname(__FILE__) . '/media.browser.inc';
-  media_attach_browser_js($form);
-  
-  $types = media_display_types();
-
-  if (arg(3)) {
-    $display = arg(3);
-    if (!in_array($display, array_keys($types))) {
-      exit(drupal_not_found());
-    }
-    // Save their preference.
-    db_merge('media_list_type')
-      ->key(array('uid' => $user->uid))
-      ->fields(array(
-          'type' => $display,
-      ))
-      ->execute();
-  }
-  else {
-    $display = db_query("SELECT type FROM {media_list_type} WHERE uid = :uid", array(':uid' => $user->uid))->fetch();
-    if (!$display) {
-      $display = 'list';
-    }
-    else {
-      $display = $display->type;
-    }
-  }
-
-  // Build the display switch.
-  $form['switch'] = media_admin_display_switch(array('active display' => $display));
-
-  // Build the 'Media operations' form.
-  $form['options'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Operations'),
-    '#prefix' => '<div class="container-inline">',
-    '#suffix' => '</div>',
-  );
-  $options = array();
-  foreach (module_invoke_all('media_operations') as $operation => $array) {
-    $options[$operation] = $array['label'];
-  }
-  $form['options']['operation'] = array(
-    '#type' => 'select',
-    '#options' => $options,
-    '#default_value' => 'delete',
-  );
-  $options = array();
-  $form['options']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Submit'),
-    '#submit' => array('media_admin_submit'),
-    '#validate' => array('media_admin_validate'),
-  );
-
-  include_once $types[$display]['file'];
-
-  $form['admin'] = $types[$display]['callback']();
-
-  return $form;
-}
-
-
-/**
- * Form builder: Builds the media list administration overview.
- */
-function media_admin_list() {
-  // @todo Change to media_variable_get('admin_pager_limit') for consistency
-  //   with browser_pager_limit?
-  $limit = variable_get('media_admin_limit', 50);
-
-  // Build the sortable table header.
-  $header = array(
-   'title' => array('data' => t('Title'), 'field' => 'f.filename'),
-   'type' => array('data' => t('Type'), 'field' => 'f.filemime'),
-   'size' => array('data' => t('Size'), 'field' => 'f.filesize'),
-   'author' => array('data' => t('Author'), 'field' => 'u.name'),
-   'timestamp' => array('data' => t('Updated'), 'field' => 'f.timestamp', 'sort' => 'asc'),
-   'operations' => array('data' => t('Operations')),
-  );
-
-  $query = db_select('file_managed', 'f')->extend('PagerDefault')->extend('TableSort');
-  $query->join('users', 'u', 'f.uid = u.uid');
-
-  $result = $query
-   ->fields('f')
-   ->fields('u', array('name'))
-   ->condition('f.status', FILE_STATUS_PERMANENT)
-   ->limit($limit)
-   ->orderByHeader($header)
-   ->execute();
-
-  $destination = drupal_get_destination();
-  $files = array();
-  $options = array();
-
-  foreach ($result as $file) {
-    $options[$file->fid] = array(
-      'title' => theme('media_link', array('file' => $file)),
-      'type' =>  check_plain($file->filemime),
-      'size' => format_size($file->filesize),
-      'author' => theme('username', array('account' => $file)),
-      'timestamp' => format_date($file->timestamp, 'short'),
-    );
-    $options[$file->fid]['operations'] = l(t('edit'), 'media/' . $file->fid . '/edit', array('query' => $destination));
-  }
-
-  $form['files'] = array(
-   '#type' => 'tableselect',
-   '#header' => $header,
-   '#options' => $options,
-   '#empty' => t('No media available.'),
-   '#attributes' => array('class' => array('media-display-table', 'media-clear')),
-  );
-  $form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
-
-  return $form;
-}
-
-/**
- * Form builder: Builds the media thumbnails administration overview.
- */
-function media_admin_thumbnails() {
-  // @todo Change to media_variable_get('admin_pager_limit') for consistency
-  //   with browser_pager_limit?
-  $limit = variable_get('media_admin_limit', 50);
-
-  $query = new EntityFieldQuery();
-
-  $query->pager($limit);
-  $query->entityCondition('entity_type', 'media');
-  $query->propertyCondition('status', FILE_STATUS_PERMANENT);
-
-  $result = $query->execute();
-  $ids = key_exists('media', $result) ? array_keys($result['media']) : FALSE;
-  if ($ids) {
-    $media_entities = entity_load('media', $ids);
-  }
-  else {
-    $media_entities = array();
-  }
-
-  $destination = drupal_get_destination();
-  $rows = array();
-  $options = array();
-  $form['files'] = array(
-   '#tree' => TRUE,
-   '#prefix' => '<div class="media-display-thumbnails media-clear clearfix"><ul class="media-list-thumbnails">',
-   '#suffix' => '</ul></div>',
-  );
-
-  if (count($media_entities)) {
-    foreach ($media_entities as $media) {
-     $preview = media_get_thumbnail_preview($media, TRUE);
-     $form['files'][$media->fid] = array(
-       '#type' => 'checkbox',
-       '#title' => '',
-       '#prefix' => '<li>' . drupal_render($preview),
-       '#suffix' => '</li>',
-     );
-    }
-  }
-
-  $form['pager'] = array('#markup' => theme('pager', array('tags' => NULL)));
-
-  return $form;
-}
-
-/**
- * Build the display switch portion of the file listings form.
- */
-function media_admin_display_switch($options = array()) {
-  $options += array(
-    'form location' => 'admin/content/media',
-    'active display' => 'list',
-  );
-  $display_types = media_display_types();
-
-  // Build the item list.
-  $display_items = array();
-  foreach ($display_types as $delta => $item) {
-    $attributes = array('title' => $item['description']);
-
-    // Set a seperate icon for the active item.
-    if ($delta == $options['active display']) {
-      $icon = $item['icon_active'];
-      $attributes['class'][] = 'active';
-    }
-    else {
-      $icon = $item['icon'];
-    }
-
-    $display_items[] = array(
-      'data' => l(theme('image', array('path' => $icon, 'alt' => $item['title'])),
-        $options['form location'] . '/' . $delta,
-        array(
-          'html' => TRUE,
-          'attributes' => $attributes,
-        )),
-    );
-  }
-
-  return array(
-    '#type' => 'markup',
-    '#markup' => theme('item_list', array(
-       'items' => $display_items,
-       'attributes' => array('class' => 'media-display-switch'),
-      )
-    ),
-  );
-}
-
-/**
- * Validate media_admin_list form submissions.
- *
- * Check if any files have been selected to perform the chosen
- * 'Update option' on.
- */
-function media_admin_validate($form, &$form_state) {
-  $files = array_filter($form_state['values']['files']);
-  if (count($files) == 0) {
-    form_set_error('', t('No items selected.'));
-  }
-}
-
-/**
- * Process media_admin_list form submissions.
- *
- * Execute the chosen 'Update option' on the selected files.
- */
-function media_admin_submit($form, &$form_state) {
-  $operations = module_invoke_all('media_operations');
-  $operation = $operations[$form_state['values']['operation']];
-  // Filter out unchecked nodes
-  $files = array_filter($form_state['values']['files']);
-  if ($function = $operation['callback']) {
-    // Add in callback arguments if present.
-    if (isset($operation['callback arguments'])) {
-      $args = array_merge(array($files), $operation['callback arguments']);
-    }
-    else {
-      $args = array($files);
-    }
-    call_user_func_array($function, $args);
-    cache_clear_all();
-  }
-  elseif (!empty($operation['redirect'])) {
-    $fids = implode(' ', array_keys(array_filter($form_state['values']['files'])));
-    $form_state['redirect'] = array(str_replace('%fids', $fids, $operation['redirect']), array('query' => array('destination' => 'admin/content/media')));
-  }
-  else {
-    // We need to rebuild the form to go to a second step. For example, to
-    // show the confirmation form for the deletion of nodes.
-    $form_state['rebuild'] = TRUE;
-  }
-}
-
-/**
- * Display all media types.
- */
-function media_admin_type_list() {
-  $types = media_type_get_types();
-  $field_ui = module_exists('field_ui');
-  $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => $field_ui ? '4' : '2'));
-  $rows = array();
-
-  foreach ($types as $key => $info) {
-    $type = $types[$key];
-    $type_url_str = str_replace('_', '-', $key);
-    $row = array();
-    $label = $info->label;
-
-    // Set the name column.
-    $row[] = array('data' => t($info->label));
-
-    // Set the edit column.
-    $row[] = array('data' => l(t('settings'), 'admin/config/media/types/manage/' . $type_url_str));
-
-    if ($field_ui) {
-      // Manage fields.
-      $row[] = array('data' => l(t('manage fields'), 'admin/config/media/types/manage/' . $type_url_str . '/fields'));
-
-      // Display fields.
-      $row[] = array('data' => l(t('manage display'), 'admin/config/media/types/manage/' . $type_url_str . '/display'));
-    }
-
-    // Set the delete column.
-    if (empty($type->base)) {
-      $row[] = array('data' => l(t('delete'), 'admin/config/media/types/manage/' . $type_url_str . '/delete'));
-    }
-    else {
-      $row[] = array('data' => '');
-    }
-
-    $rows[] = $row;
-
-  }
-
-  $build['media_type_table'] = array(
-    '#theme' => 'table',
-    '#header' => $header,
-    '#rows' => $rows
-  );
-
-  return $build;
-}
-
-/**
- *  The administration form for managing media types.
- */
-function media_admin_type_manage_form($form, &$form_state, $media_type) {
-  $form = array();
-  $form['media_type'] = array(
-    '#type' => 'value',
-    '#value' => $media_type->name,
-  );
-  
-  // If this Media type is handled by us, then we can put in some default
-  // options. Otherwise, we leave it to the implementing module to form_alter.
-  if ($media_type->type_callback == 'media_is_type') {
-    // Options for match_type.
-    $options = array(
-      'all' => t('All'),
-      'any' => t('Any'),
-      '0' => t('Other'),
-    );
-    if ($media_type->type_callback_args['match_type'] && isset($options[$media_type->type_callback_args['match_type']])) {
-      $default_value = $media_type->type_callback_args['match_type'];
-      $other_default_value = '';
-    }
-    else {
-      $default_value = 0;
-      $other_default_value = $media_type->type_callback_args['match_type'];
-    }
-    $form['match_type'] = array(
-      '#type' => 'radios',
-      '#title' => t('Match type'),
-      '#options' => $options,
-      '#default_value' => $default_value,
-    );
-    $form['match_type_other'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Other match type value'),
-      '#default_value' => $other_default_value,
-      '#attached' => array(
-        'js' => array(drupal_get_path('module', 'media') . '/javascript/media-admin.js'),
-      ),
-    );
-
-    // Options for allowed Streams.
-    $options = array('public' => t('Public files'), 'private' => t('Private files'));
-    foreach(file_get_stream_wrappers() as $stream => $wrapper) {
-      $options[$stream] = $wrapper['name'];
-    }
-    unset($options['temporary']);
-    $default_value = array();
-    if (isset($media_type->type_callback_args['streams'])) {
-      foreach ($media_type->type_callback_args['streams'] as $stream) {
-        $default_value[$stream] = $stream;
-      }
-    }
-    $form['streams'] = array(
-      '#type' => 'checkboxes',
-      '#title' => t('Allowed streams'),
-      '#options' => $options,
-      '#default_value' => $default_value,
-    );
-
-    // Options for allowed mimetypes & extensions.
-    $default_value = isset($media_type->type_callback_args['mimetypes']) ? implode(' ', $media_type->type_callback_args['mimetypes']) : '';
-    $form['mimetypes'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Allowed mimetypes'),
-      '#description' => t('You may optionally enter one or more allowed file mimetypes for this Media type, if appropriate, separating each with a space. You may use a regular expression for matching, such as %image_match (which would match any mimetype beginning with %image) or %any_match, which would match any file mimetype.', array('%image_match' => '/^image/', '%image' => t('image'), '%any_match' => '/.*/')),
-      '#default_value' => check_plain($default_value),
-    );
-    $default_value = isset($media_type->type_callback_args['extensions']) ? implode(' ', $media_type->type_callback_args['extensions']) : '';
-    $form['extensions'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Allowed extensions'),
-      '#description' => t('You may optionally enter one or more allowed file extensions for this Media type, if appropriate, separating each with a space (and no dots).'),
-      '#default_value' => check_plain($default_value),
-    );
-  }
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-    '#weight' => 100,
-  );
-  return $form;
-}
-
-function media_admin_type_manage_form_submit($form, &$form_state) {
-  $media_type = media_type_load($form_state['values']['media_type']);
-  if ($form_state['values']['match_type']) {
-    $media_type->type_callback_args['match_type'] = $form_state['values']['match_type'];
-  }
-  else {
-    $media_type->type_callback_args['match_type'] = $form_state['values']['match_type_other'];
-  }
-  $media_type->type_callback_args['streams'] = array();
-  foreach ($form_state['values']['streams'] as $stream) {
-    if ($stream) {
-      $media_type->type_callback_args['streams'][] = $stream;
-    }
-  }
-  $media_type->type_callback_args['mimetypes'] = explode(' ', $form_state['values']['mimetypes']);
-  $media_type->type_callback_args['extensions'] = explode(' ', $form_state['values']['extensions']);
-
-  media_type_save($media_type);
-  drupal_set_message(t('The @label media type has been saved.', array('@label' => $media_type->label)));
-}
-
-/**
- * Form callback for mass import.
- */
-function media_import($form, &$form_state) {
-  if (!isset($form_state['storage']['files'])) {
-    $form_state['storage']['step'] = 'choose';
-    $form_state['storage']['next_step'] = 'preview';
-    $form['directory'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Directory'),
-      '#required' => TRUE,
-    );
-
-    $form['pattern'] = array(
-      '#type' => 'textfield',
-      '#title' => t('Pattern'),
-      '#description' => 'Only files matching this pattern will be imported.',
-    );
-
-    $form['submit'] = array(
-      '#type' => 'submit',
-      '#value' => t('Preview')
-    );
-  }
-  else {
-    $form['preview'] = array(
-      '#markup' => theme('item_list', array('items' => $form_state['storage']['files'])),
-    );
-
-    $form = confirm_form($form, t('Import these files?'), 'admin/content/media/import');
-  }
-  return $form;
-
-}
-
-/**
- * Validate handler for media_import().
- */
-function media_import_validate($form, &$form_state) {
-  if ($form_state['values']['op'] != t('Confirm')) {
-    $directory = $form_state['values']['directory'];
-    $pattern = $form_state['values']['pattern'];
-    if (!is_dir($directory)) {
-      form_set_error('directory', t('The provided directory does not exist.'));
-    }
-    $pattern = !empty($pattern) ? $pattern :  '*';
-    $files = glob("$directory/$pattern");
-    if (count($files) == 1) {
-      form_set_error('pattern', t('No files were found in %directory matching %pattern', array('%directory' => $directory, '%pattern' => $pattern)));
-    }
-    $form_state['storage']['files'] = $files;
-  }
-}
-
-/**
- * Submit handler for media_import().
- */
-function media_import_submit($form, &$form_state) {
-  if ($form_state['values']['op'] == 'Confirm') {
-    $files = $form_state['storage']['files'];
-    $batch = array(
-      'title' => t('Importing'),
-      'operations' => array(
-        array('media_import_batch_import_files', array($files)),
-      ),
-      'finished' => 'media_import_batch_import_complete',
-      'file' => drupal_get_path('module', 'media') . '/media.admin.inc',
-    );
-    batch_set($batch);
-    return;
-
-  }
-  $form_state['rebuild'] = TRUE;
-}
-
-/**
- * BatchAPI callback op for media import.
- */
-function media_import_batch_import_files($files, &$context) {
-  if (!isset($context['sandbox']['files'])) {
-    // This runs the first time the batch runs.
-    // This is stupid, but otherwise, I don't think it will work...
-    $context['results'] = array('success' => array(), 'errors' => array());
-    $context['sandbox']['max'] = count($files);
-    $context['sandbox']['files'] = $files;
-  }
-  $files =& $context['sandbox']['files'];
-
-  // Take a cut of files.  Let's do 10 at a time.
-  $length = (count($files) > media_variable_get('import_batch_size')) ? media_variable_get('import_batch_size') : count($files);
-  $to_process = array_splice($files, 0, $length);
-  $image_in_message = '';
-
-  foreach ($to_process as $file) {
-    try {
-      $file_obj = media_parse_to_file($file);
-      $context['results']['success'][] = $file;
-      if (!$image_in_message) {
-        $media = media_load($file_obj->fid);
-        $image_in_message = field_view_field('media', $media, 'file', 'media_preview');
-      }
-    }
-    catch(Exception $e) {
-      $context['results']['errors'][] = $file . " Reason: " . $e->getMessage();
-    }
-  }
-
-  $context['message'] = "Importing " . theme('item_list', array('items' => $to_process));
-  $context['message'] .= drupal_render($image_in_message); // Just for kicks, show an image we are importing
-
-  $context['finished'] = ($context['sandbox']['max'] - count($files)) / $context['sandbox']['max'];
-
-}
-
-/**
- * BatchAPI complete callback for media import.
- */
-function media_import_batch_import_complete($success, $results, $operations) {
-  if ($results['errors']) {
-    drupal_set_message(theme('item_list', array('items' => $results['errors'])), 'error');
-  }
-  if ($results['success']) {
-    drupal_set_message(theme('item_list', array('items' => $results['success'])));
-  }
-}
-
-/**
- * Admin configruation form for media browser settings.
- */
-function media_admin_config_browser($form, &$form_state) {
-  $theme_options = array();
-  $theme_options[NULL] = 'Default administration theme';
-  foreach (list_themes() as $key => $theme) {
-    if ($theme->status) {
-      $theme_options[$key] = $theme->info['name'];
-    }
-  }
-
-  $form[media_variable_name('dialog_theme')] = array(
-    '#type' => 'select',
-    '#title' => t('Media browser theme'),
-    '#options' => $theme_options,
-    '#description' => t("This theme will be used for all media related dialogs.  It can be different from your site's theme because many site themes do not work well in the small windows which media uses."),
-    '#default_value' => media_variable_get('dialog_theme'),
-  );
-
-  $form[media_variable_name('file_extensions')] = array(
-    '#type' => 'textfield',
-    '#title' => 'Allowed file extensions',
-    '#default_value' => media_variable_get('file_extensions'),
-    '#description' => t('File extensions which are accepted in the media browser.  Use spaces to separate (e.g. "jpg gif pdf doc"). <br/><em>Note that this can be overriden on a per-field basis when creating multimedia asset fields and files of certain extensions cannot be embedded at this time.</em>'),
-  );
-
-  $form['#submit'][] = 'media_admin_config_browser_pre_submit';
-  return system_settings_form($form);
-}
-
-function media_admin_config_browser_pre_submit(&$form, &$form_state) {
-  if (!$form_state['values'][media_variable_name('dialog_theme')]) {
-    media_variable_del('dialog_theme');
-    unset($form_state['values'][media_variable_name('dialog_theme')]);
-  }
-}
-
-/**
- * Confirmation form for rebuliding the file_managed table to include type
- * in rows where there is no type.
- */
-function media_admin_rebuild_types_form($form, &$form_state) {
-  $total = media_type_invalid_files_count();
-  if ($total == 0) {
-    media_variable_del('show_file_type_rebuild_nag');
-    // @TODO: Make this not sound stupid.
-    drupal_set_message('All files in the system have been assigned types. Media installation complete.');
-    drupal_goto('admin');
-  }
-  $form['total'] = array('#type' => 'value', '#value' => $total);
-  return confirm_form($form, 'Update types for existing files', 'admin/config/media', 'This process is required when installing media on an existing site.  Media needs to scan through existing files and identify the file type. <br/><strong>Update types for ' . $total . ' files?</strogn>');
-}
-
-/**
- * @see media_admin_rebuild_types_form().
- */
-function media_admin_rebuild_types_form_submit(&$form, &$form_state) {
-  $total = $form_state['values']['total'];
-
-  $batch = array(
-    'title' => t('Rebuilding type information for ' . $total . ' files'),
-    'operations' => array(
-      array('media_admin_rebuild_types_batch_op', array($total)),
-    ),
-    'finished' => 'media_admin_rebuild_types_batch_complete',
-    'file' => drupal_get_path('module', 'media') . '/media.admin.inc',
-  );
-  batch_set($batch);
-}
-
-/**
- * Batch operation for fixing the file_managed table for media, adding type values
- * where no value exists.
- */
-function media_admin_rebuild_types_batch_op($total, &$context) {
-  $per_run = media_variable_get('media_type_batch_update_per_run', 100);
-  $context['results'] = array_merge($context['results'], media_type_batch_update(FALSE, $per_run));
-  $context['finished'] = count($context['results']) / $total;
-}
-/**
- * Sets a message informing the user how many file records were updated.
- */
-function media_admin_rebuild_types_batch_complete($success, $results, $operations) {
-  if ($success) {
-    $message = format_plural(count($results), 'One file identified and given a type.', '@count files identified and given a type.');
-    media_variable_del('show_file_type_rebuild_nag');
-  }
-  drupal_set_message($message);
-}
\ No newline at end of file
diff --git a/media.api.php b/media.api.php
index 6bae603..0cf18a3 100644
--- a/media.api.php
+++ b/media.api.php
@@ -17,7 +17,7 @@
  *  '#title' => t('Library'),
  *  '#attached' => array(
  *    'js' => array(
- *       $path . '/javascript/plugins/media.library.js',
+ *       $path . '/js/plugins/media.library.js',
  *     ),
  *   ),
  *   '#settings' => array(
diff --git a/media.browser.inc b/media.browser.inc
deleted file mode 100644
index 7b3df7a..0000000
--- a/media.browser.inc
+++ /dev/null
@@ -1,427 +0,0 @@
-<?php
-
-/**
- * @file
- * Media Browser page callback
- */
-function media_browser($selected = NULL) {
-  $output = array();
-  $output['#attached']['library'][] = array('media', 'media_browser_page');
-  
-  $params = drupal_get_query_parameters();
-  array_walk_recursive($params, '_media_recursive_check_plain');
-  media_set_browser_params($params);
-  
-  // How do we validate these?  I don't know.
-  // I think PDO should protect them, but I'm not 100% certain.
-  if (isset($params['fid'])) {
-    // A form has submitted, selected a piece (or pieces) of media and we refreshed.
-    // So we're just going to send the media entity out as a JS setting so the browser can close itself.
-    $selected = $params['fid'];
-    if (!is_array($params['fid'])) {
-      $selected = array($params['fid']);
-    }
-    if (!is_numeric($selected[0])) {
-      throw new Exception('Error selecting media, fid param is not an fid or an array of fids');
-    }
-    $selectedMedia = entity_load('media', $selected);
-    foreach ($selectedMedia as &$media) {
-      media_browser_build_media_item($media);
-    }
-    $setting = array('media' => array('selectedMedia' => array_values($selectedMedia)));
-    drupal_add_js($setting, 'setting');
-    return $output;
-  }
-
-  // Normal browser operation.
-  foreach (module_implements('media_browser_plugin_info') as $module) {
-    foreach(module_invoke($module, 'media_browser_plugin_info') as $key => $plugin_data) {
-      $plugins[$key] = $plugin_data + array(
-        '#module' => $module,
-        '#weight' => 0,
-      );
-      $plugins[$key]['#weight'] += count($plugins)/1000;
-    }
-  }
-  
-  // Only the plugins in this array are loaded.
-  if (!empty($params['enabledPlugins'])) {
-    $plugins = array_intersect_key($plugins, array_fill_keys($params['enabledPlugins'], 1));
-  }
-  elseif (!empty($params['disabledPlugins'])) {
-    $plugins = array_diff_key($plugins, array_fill_keys($params['disabledPlugins'], 1));
-  }
-
-  foreach ($plugins as $key => &$plugin) {
-    $plugin += module_invoke($plugin['#module'], 'media_browser_plugin_view', $key, $params);
-  }
-  
-  // Allow modules to change the tab names or whatever else they want to change
-  // before we render.  Perhaps this should be an alter on the theming function
-  // that we should write to be making the tabs.
-  drupal_alter('media_browser_plugins', $plugins);
-  
-  $tabs = array(); // List of tabs to render.
-
-  $settings = array('media' => array('browser' => array()));
-  $browser_settings =& $settings['media']['browser'];
-
-  //@todo: replace with Tabs module if it gets upgraded.
-  foreach (element_children($plugins, TRUE) as $key) {
-    $plugin =& $plugins[$key];
-
-    //Add any JS settings
-    $browser_settings[$key] = isset($plugin['#settings']) ? $plugin['#settings'] : array();
-
-    // If this is a "ajax" style tab, add the href, otherwise an id.
-    $href = isset($plugin['#callback']) ? $plugin['#callback'] : "#media-tab-$key";
-    $tabs[] = "<a href='$href'><span>{$plugin['#title']}</span></a>";
-
-    // Create a div for each tab's content.
-    $plugin['#prefix'] = <<<EOS
-    <div class="media-browser-tab" id="media-tab-$key">
-EOS;
-    $plugin['#suffix'] = <<<EOS
-    </div>
-    <!-- End #media-tab-$key -->
-EOS;
-  }
-
-  drupal_add_js($settings, 'setting');
-
-  $output['tabset'] = array(
-    '#prefix' => '<div id="media-browser-tabset">',
-    '#suffix' => '</div>',
-  );
-  $output['tabset']['list'] = array(
-    '#markup' => '<ul><li>' . implode('</li><li>', $tabs) . '</li></ul>'
-  );
-
-  $output['tabset']['plugins'] = $plugins;
-  return $output;
-}
-
-/**
- * Provides a singleton of the params passed to the media browser.
- *
- * This is useful in situations like form alters because callers can pass
- * id="wysiywg_form" or whatever they want, and a form alter could pick this up.
- * We may want to change the hook_media_browser_plugin_view() implementations to
- * use this function instead of being passed params for consistency.
- *
- * It also offers a chance for some meddler to meddle with them.
- *
- * @param array $params
- *  An array of parameters provided when a media_browser is launched.
- *  See media_browser().
- */
-function media_set_browser_params(&$params = NULL) {
-  static $stored_params;
-  
-  if (!$params) {
-    return $stored_params;
-  }
-  $stored_params = $params;
-  drupal_alter('media_browser_params', $stored_params);
-}
-
-/**
- * For sanity in grammar.
- * @see media_set_browser_params().
- */
-function media_get_browser_params() {
-  return media_set_browser_params();
-}
-
-/**
- * AJAX Callback function to return a list of media files
- */
-function media_browser_list() {
-  $params = drupal_get_query_parameters();
-  // How do we validate these?  I don't know.
-  // I think PDO should protect them, but I'm not 100% certain.
-  array_walk_recursive($params, '_media_recursive_check_plain');
-  
-  $types = isset($params['types']) ? $params['types'] : NULL;
-  $url_include_patterns = isset($params['url_include_patterns']) ? $params['url_include_patterns'] : NULL;
-  $url_exclude_patterns = isset($params['url_exclude_patterns']) ? $params['url_exclude_patterns'] : NULL;
-
-  $start = isset($params['start']) ? $params['start'] : 0;
-  $limit = isset($params['limit']) ? $params['limit'] : media_variable_get('browser_pager_limit');
-  
-  $conditions = array();
-
-  $query = new EntityFieldQuery();
-  
-  $query->range($start, $limit);
-  $query->entityCondition('entity_type', 'media');
-  $query->entityOrderBy('entity_id', 'DESC');
-
-  if ($types) {
-    $query->entityCondition('bundle', $types, 'IN');
-  }
-
-  if ($url_include_patterns) {
-    $query->propertyCondition('uri', $v, 'CONTAINS');
-    // Insert stream related restrictions here.
-  }
-  if ($url_exclude_patterns) {
-    $query->propertyCondition('uri', "%$v%", 'NOT LIKE');
-  }
-
-  // @todo Implement granular editorial access: http://drupal.org/node/696970.
-  //   In the meantime, protect information about private files from being
-  //   discovered by unprivileged users. See also media_file_view().
-  if (!user_access('administer media')) {
-    $query->propertyCondition('uri', 'private://%', 'NOT LIKE');
-  }
-
-  $query->propertyCondition('status', FILE_STATUS_PERMANENT);
-
-  $result = $query->execute();
-  $ids = array_keys($result['media']);
-  if ($ids) {
-    $media_entities = entity_load('media', $ids);
-  }
-  else {
-    $media_entities = array();
-  }
-  
-  foreach ($media_entities as &$media) {
-    media_browser_build_media_item($media);
-  }
-  
-  drupal_json_output(array('media' => array_values($media_entities)));
-  exit();
-
-}
-
-/**
- * Implements hook_media_browser_plugin_info().
- */
-function media_media_browser_plugin_info() {
-  $plugins = array();
-
-  // @TODO: Should we add a permission around this?
-  $plugins['library'] = array(
-    '#weight' => 10,
-  );
-
-  if (user_access('administer media') || user_access('edit media')) {
-    $plugins['upload'] = array(
-      '#weight' => -10,
-     );
-  }
-  return $plugins;
-}
-
-/**
- * Implementaion of hook_media_browser_plugin_view().
- */
-function media_media_browser_plugin_view($plugin_name, $params) {
-  $path = drupal_get_path('module', 'media');
-
-  module_load_include('inc', 'media', 'media.admin');
-  module_load_include('inc', 'media', 'media.pages');
-
-  $types = isset($params['types']) ? $params['types'] : array();
-
-  // The multiselect parameter is a string.  So we check to see if it is set and
-  // adjust the local variable accordingly.
-  $multiselect = FALSE;
-  if (isset($params['multiselect']) && $params['multiselect'] != 'false') {
-    $multiselect = TRUE;
-  }
-
-  $redirect = array('media/browser', array('query' => array('render' => 'media-popup')));
-  switch ($plugin_name) {
-    case 'upload':
-      $attached = array();
-      if ($multiselect && module_exists('plupload')) {
-        $upload_form_id = 'media_add_upload_multiple';
-        $attached['js'] = array($path . '/javascript/plugins/media.upload_multiple.js');
-      }
-      else {
-        $upload_form_id = 'media_add_upload';
-      }
-
-      $upload_form = drupal_get_form($upload_form_id, $types);
-      return array(
-        '#title' => t('Upload'),
-        'form' => array($upload_form),
-        '#attached' => $attached,
-      );
-      break;
-    case 'library':
-      return array(
-        '#title' => t('Library'),
-        '#attached' => array(
-          'js' => array(
-            $path . '/javascript/plugins/media.library.js',
-          ),
-          'css' => array(
-            //@todo: should move this.
-            $path . '/javascript/plugins/media.library.css',
-          ),
-        ),
-        '#settings' => array(
-          'viewMode' => 'thumbnails',
-          'getMediaUrl' => url('media/browser/list'),
-          'types' => $types,
-          'multiselect' => $multiselect,
-        // We should probably change this to load dynamically when requested
-        // via the JS file.
-        ),
-        '#markup' => '<div id="container"><div id="scrollbox"><ul id="media-browser-library-list" class="media-list-thumbnails"></ul><div id="status"></div></div></div>',
-      );
-    break;
-  }
-
-  return array();
- }
-
-/**
- * Silly function to recursively run check_plain on an array.
- *
- * There is probably something in core I am not aware of that does this.
- *
- * @param $value
- * @param $key
- */
-function _media_recursive_check_plain(&$value, $key) {
-  $value = check_plain($value);
-}
-
-/**
- * Attaches media browser javascript to an element.
- *
- * @param $element
- *  The element array to attach to.
- */
-function media_attach_browser_js(&$element) {
-  $javascript = media_browser_js();
-  foreach ($javascript as $key => $definitions) {
-    foreach ($definitions as $definition) {
-      $element['#attached'][$key][] = $definition;
-    }
-  }
-}
-
-/**
- * Helper function to define browser javascript.
- */
-function media_browser_js() {
-  $settings = array(
-    'browserUrl' => url('media/browser',
-      array('query' => array('render' => 'media-popup'))),
-    'styleSelectorUrl' => url('media/-media_id-/format-form',
-      array('query' => array('render' => 'media-popup'))),
-  );
-
-  $js = array(
-    'library' => array(
-      array('media', 'media_browser'),
-    ),
-    'js' => array(
-      array(
-       'data' => array('media' => $settings),
-       'type' => 'setting',
-      ),
-    ),
-  );
-  return $js;
-}
-
-/**
- * Menu callback for testing the media browser
- */
-function media_browser_testbed($form) {
-  media_attach_browser_js($form);
-
-  $form['test_element'] = array(
-    '#type' => 'media',
-    '#media_options' => array(
-      'global' => array(
-        'types' => array('video', 'audio'),
-      ),
-    )
-  );
-  
-  $launcher = '<a href="#" id="launcher"> Launch Media Browser</a>';
-
-  $form['options'] = array(
-    '#type' => 'textarea',
-    '#title' => 'Options (JSON)',
-    '#rows' => 10,
-  );
-
-  $form['launcher'] = array(
-    '#markup' => $launcher,
-  );
-
-  $form['result'] = array(
-    '#type' => 'textarea',
-    '#title' => 'Result',
-  );
-
-
-  $js = <<<EOF
-    Drupal.behaviors.mediaTest = {
-    attach: function(context, settings) {
-      var delim = "---";
-      var recentOptions = [];
-      var recentOptionsCookie = jQuery.cookie("recentOptions");
-      if (recentOptionsCookie) {
-        recentOptions = recentOptionsCookie.split("---");
-      }
-
-      var recentSelectBox = jQuery('<select id="recent_options" style="width:100%"></select>').change(function() { jQuery('#edit-options').val(jQuery(this).val())});
-
-      jQuery('.form-item-options').append('<label for="recent_options">Recent</a>');
-      jQuery('.form-item-options').append(recentSelectBox);
-      jQuery('.form-item-options').append(jQuery('<a href="#">Reset</a>').click(function() {alert('reset'); jQuery.cookie("recentOptions", null); window.location.reload(); }));
-
-      jQuery.each(recentOptions, function (idx, val) {
-        recentSelectBox.append(jQuery('<option></option>').val(val).html(val));
-      });
-
-
-      jQuery('#launcher').click(function () {
-        jQuery('#edit-result').val('');
-        var options = {};
-        var optionsTxt = jQuery('#edit-options').val();
-        if (optionsTxt) {
-          // Store it in the recent box
-          recentOptionsCookie += "---" + optionsTxt
-          jQuery.cookie("recentOptions", recentOptionsCookie, { expires: 7 });
-          recentSelectBox.append(jQuery('<option></option>').val(optionsTxt).html(optionsTxt));
-          options = eval('(' + optionsTxt + ')');
-        }
-        Drupal.media.popups.mediaBrowser(Drupal.behaviors.mediaTest.mediaSelected, options);
-        return false;
-      });
-    },
-
-    mediaSelected: function(selectedMedia) {
-      var result = JSON.stringify(selectedMedia);
-        jQuery('#edit-result').val(result);
-    }
-  }
-
-EOF;
-
-  drupal_add_js($js, array('type' => 'inline'));
-  return $form;
-}
-
-/**
- * Adds on properties to the media item which are expected by callers who are
- * going to act on the returned item.
- *
- * @param Object &$media;
- */
-function media_browser_build_media_item(&$media) {
-  $preview = media_get_thumbnail_preview($media);
-  $media->preview = drupal_render($preview);
-  $media->url = file_create_url($media->uri);
-}
diff --git a/media.fields.inc b/media.fields.inc
deleted file mode 100644
index de28823..0000000
--- a/media.fields.inc
+++ /dev/null
@@ -1,456 +0,0 @@
-<?php
-
-/**
- * @file: Provides a "Multimedia asset" field to the fields API
- */
-
-/**
- * Implement hook_field_info().
- */
-function media_field_info() {
-  return array(
-    'media' => array(
-      'label' => t('Multimedia asset'),
-      'description' => t('This field stores a reference to a multimedia asset.'),
-      'settings' => array(),
-      'instance_settings' => array(),
-      'default_widget' => 'media_generic',
-      'default_formatter' => 'media_large',
-    ),
-  );
-}
-
-/**
- * Implements hook_field_is_empty().
- */
-function media_field_is_empty($item, $field) {
-  if (!is_array($item) || (empty($item['fid']))) {
-    return TRUE;
-  }
-  return FALSE;
-}
-
-/**
- * Implement hook_field_widget_info().
- */
-function media_field_widget_info() {
-  return array(
-    'media_generic' => array(
-      'label' => t('Media file selector'),
-      'field types' => array('media'),
-      'settings' => array(
-        'progress_indicator' => 'throbber',
-        'allowed_types' => array('image'),
-        'allowed_schemes' => array('public', 'private'),
-      ),
-      'behaviors' => array(
-        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
-        'default value' => FIELD_BEHAVIOR_NONE,
-      ),
-    ),
-  );
-}
-
-/**
- * Implement hook_field_formatter_info
- */
-function media_field_formatter_info() {
-  $formatters = array();
-
-  // Formatters for media are the same as build modes for media.
-  $build_modes = media_field_view_modes('media');
-  foreach ($build_modes as $key => $mode) {
-    $formatters[$key] = array(
-      'label' => t($mode['label']),
-      'field types' => array('media'),
-    );
-  }
-
-  // Also add a formatter for files to show larger icons based on mimetype
-  $formatters['media_large_icon'] = array(
-    'label' => t('Large filetype icon'),
-    'field types' => array('file'),
-  );
-  
-  return $formatters;
-}
-
-/**
- * Used to Implement hook_field_view_modes().
- */
-function media_field_view_modes($obj_type) {
-  $modes = array();
-  if ($obj_type == 'media') {
-    $modes = array(
-      'media_link' => array('label' => t('Link'), 'custom settings' => FALSE),
-      'media_preview' => array('label' => t('Preview'), 'custom settings' => FALSE),
-      'media_small' => array('label' => t('Small'), 'custom settings' => FALSE),
-      'media_large' => array('label' => t('Large'), 'custom settings' => FALSE),
-      'media_original' => array('label' => t('Original'), 'custom settings' => FALSE),
-    );
-  }
-  return $modes;
-}
-
-function media_field_ui_view_modes_tabs() {
-  $modes = array(
-    'basic' => array(
-      'view modes' => array_keys(media_field_view_modes('media')),
-    ),
-  );
-  return $modes;
-}
-
-/**
- * Implements hook_field_prepare_view().
- *
- * @todo Get resolution on http://drupal.org/node/879034 regarding using this
- *   hook to load referenced entities.
- */
-function media_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
-  // Collect all media IDs that need loading.
-  $fids = array();
-  foreach ($entities as $id => $entity) {
-    foreach ($items[$id] as $delta => $item) {
-      if (is_array($item) && !empty($item['fid'])) {
-        $fids[$item['fid']] = $item['fid'];
-      }
-    }
-  }
-
-  // Load the entities in a single operation.
-  $media_entities = media_load_multiple(array_values($fids));
-
-  // Populate that item data with the loaded entities.
-  foreach ($entities as $id => $entity) {
-    foreach ($items[$id] as $delta => $item) {
-      if (is_array($item) && !empty($item['fid'])) {
-        $items[$id][$delta] = $media_entities[$item['fid']];
-      }
-    }
-  }
-}
-
-/**
- * Implement hook_field_formatter_view
- */
-function media_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
-  
-  $element = array();
-  
-  // This block handles fallback file type icons for media which doesn't have
-  // a thumbnail.  It's kind of a hack, and belongs in the file module.
-  if ($display['type'] == 'media_large_icon') {
-    foreach ($items as $delta => $item) {
-      // Display all values in a single element..
-      $element[$delta] = array(
-        '#theme' => 'media_formatter_large_icon',
-        '#file' => $item,
-      );
-    }
-    return $element;
-  }
-  
-  // The rest of this hook invocation is for rendering media when it is attached
-  // as a field to other entities.  The formatter on the media field is equivilent
-  // to the media view_mode.  So if someone configures a media field on a node
-  // and says that the teaser formatter for the media is "large", it will render
-  // the media entity as per the large view_mode.
-
-  // An array of media items to pass into entity rendering functions.
-  $media_items = array();
-
-  foreach ($items as $delta => $item) {
-    $media_items[$item->fid] = $item;
-    //$media_items[$item['fid']] = media_load($item['fid']);
-  }
-
-  field_attach_prepare_view('media', $media_items, $display['type']);
-  entity_prepare_view('media', $media_items);
-
-
-
-  foreach ($items as $delta => $item) {
-    // @TODO:
-    // This works fine for Media styles, just not the default file types.
-    // Find out why this doesn't automatically already route through file.
-    // We're using a filefield in theory, created during the media_type
-    // instantiation at media_type_configure_fields().
-    // Other fields are routed properly, but not that default field...
-
-    // First check if the default File formatters are sufficient.
-//     $element[$delta] = file_field_formatter_view($obj_type, $object, $field, $instance, $langcode, $items, $display);
-
-    // If this is not handled by the File module, then defer to Styles.
-//     if (!$element[$delta]) {
-      $element[$delta] = field_attach_view('media', $item, $display['type'], $langcode);
-
-      // We'll defer to the Styles module to route formatting for the 'file'.
-      $element[$delta]['file']['#theme'] = 'styles_field_formatter';
-      $element[$delta]['file']['#element'] = $element[$delta]['file'];
-//     }
-  }
-
-  return $element;
-}
-
-/**
- * Implement hook_field_widget_settings_form().
- */
-function media_field_widget_settings_form($field, $instance) {
-
-  $widget = $instance['widget'];
-  $settings = $widget['settings'];
-  $form = array();
-
-  // Setup type selection form
-  $types = media_type_get_types();
-  $options = array();
-  foreach ($types as $key => $definition) {
-    $options[$key] = $definition->label;
-  }
-
-  $form['allowed_types'] = array (
-    '#type' => 'checkboxes',
-    '#title' => t('Allowed media types'),
-    '#options' => $options,
-    '#default_value' => $settings['allowed_types'],
-    '#description' => t('Media types which are allowed for this field'),
-    '#weight' => 1,
-  );
-
-  $streams = file_get_stream_wrappers();
-  $options = array();
-  unset($streams['temporary']);
-  foreach ($streams as $scheme => $data) {
-    $options[$scheme] = t('@scheme (@name)', array('@scheme' => $scheme . '://', '@name' => $data['name']));
-  }
-  $form['allowed_schemes'] = array(
-    '#type' => 'checkboxes',
-    '#title' => t('Allowed URI schemes'),
-    '#options' => $options,
-    '#default_value' => $settings['allowed_schemes'],
-    '#description' => t('URI schemes include public:// and private:// which are the Drupal files directories, and may also refer to remote sites.'),
-    '#weight' => 2,
-  );
-  return $form;
-}
-
-/**
- * Implements hook_field_widget_form().
- */
-function media_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
-  $widget_settings = $instance['widget']['settings'];
-
-  $permission = user_access('edit media');
-  if (!$permission) {
-    // @todo: show the default file upload field.
-    return;
-  }
-
-  // @todo The Field API supports automatic serialization / unserialization, so
-  //   this should no longer be needed. After verifying with a module that uses
-  //   the 'data' column, remove this.
-  // @see media_field_widget_value()
-  $current_value = array();
-  if (isset($items[$delta])) {
-    $current_value = $items[$delta];
-    // @todo $items[$delta] is sometimes a loaded media entity (an object)
-    //   rather than an array. This conflicts with Field API expectations (for
-    //   example, it results in fatal errors when previewing a node with a
-    //   multi-valued media field), so should be fixed. In the meantime, don't
-    //   assume that $current_value is an array.
-    if (is_array($current_value) && isset($current_value['data']) && is_string($current_value['data'])) {
-      $current_value['data'] = unserialize($current_value['data']);
-    }
-  }
-
-  $element += array(
-    '#type' => 'media', // Would like to make this a fieldset, but throws some weird warning about element_children... not sure what it is about yet.
-    '#collapsed' => TRUE,
-    '#default_value' => $current_value,
-    '#required' => $instance['required'],
-    //'#media_file_extensions' => $instance['settings']['file_extensions'],
-    // @TODO: Not implemented yet.
-    '#media_options' => array(
-      'global' => array(
-        'types' => array_filter($instance['widget']['settings']['allowed_types']),
-        // @todo: Not implemented
-        'schemes' => $instance['widget']['settings']['allowed_schemes'],
-      ),
-    ),
-  );
-
-  return $element;
-}
-
-/**
- * Implements hook_field_validate().
- *
- * Possible error codes:
- * - 'media_fid_illegal_value': The value is not part of the list of allowed values.
- */
-function media_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) {
-
-  $allowed_types = array_keys(array_filter($instance['widget']['settings']['allowed_types']));
-
-    // @TODO: merge in stuff from media_uri_value
-  foreach ($items as $delta => $item) {
-    if ($item['fid'] == 0) {
-      return TRUE;
-      //@TODO: make support for submiting with just a URI here?
-    }
-    $result = db_select('file_managed', 'f')
-      ->fields('f')
-      ->condition('fid', $item['fid'])
-      ->condition('type', $allowed_types, 'IN')
-      ->execute()
-      ->fetchField();
-    if (!$result) {
-      $errors[$field['field_name']][$langcode][$delta][] = array(
-        'error' => 'media_fid_illegal_value',
-        'message' => t('%name: illegal value.', array('%name' => t($instance['label']))),
-      );
-    }
-  }
-}
-
-/**
- * @todo Is this function ever called? If not, remove it. The Field API now
- *   supports automatic serialization / unserialization, so this should no
- *   longer be needed. After verifying with a module that uses the 'data'
- *   column, remove this.
- *
- * @see media_field_widget_form()
- */
-function media_field_widget_value($element, $input, $form_state) {
-  $return = $input;
-  
-  if (!is_array($return)) {
-    $return = array();
-  }
-
-  if (isset($return['data'])) {
-    $return['data'] = serialize($return['data']);
-  }
-
-  $return += array(
-    'fid' => 0,
-    'title' => '',
-    'data' => NULL,
-  );
-
-  return $return;
-}
-
-/**
- * @todo The following hook_field_(insert|update|delete|delete_revision)
- *   implementations are nearly identical to the File module implementations of
- *   the same field hooks. The only differences are:
- *   - We pass 'media' rather than 'file' as the module argument to the
- *     file_usage_(add|delete)() functions.
- *   - We do not delete the file / media entity when its usage count goes to 0.
- *   We should submit a core patch to File module to make it flexible with
- *   respect to the above, so that we can reuse its implementation rather than
- *   duplicating it.
- */
-
-/**
- * Implements hook_field_insert().
- */
-function media_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
-  // Add a new usage of each uploaded file.
-  foreach ($items as $item) {
-    $file = (object) $item;
-    file_usage_add($file, 'media', $entity_type, $id);
-  }
-}
-
-/**
- * Implements hook_field_update().
- *
- * Checks for files that have been removed from the object.
- */
-function media_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
-  // On new revisions, all files are considered to be a new usage and no
-  // deletion of previous file usages are necessary.
-  if (!empty($entity->revision)) {
-    foreach ($items as $item) {
-      $file = (object) $item;
-      file_usage_add($file, 'media', $entity_type, $id);
-    }
-    return;
-  }
-  
-  // Build a display of the current FIDs.
-  $current_fids = array();
-  foreach ($items as $item) {
-    $current_fids[] = $item['fid'];
-  }
-  
-  // Create a bare-bones entity so that we can load its previous values.
-  $original = entity_create_stub_entity($entity_type, array($id, $vid, $bundle));
-  field_attach_load($entity_type, array($id => $original), FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
-
-  // Compare the original field values with the ones that are being saved.
-  $original_fids = array();
-  if (!empty($original->{$field['field_name']}[$langcode])) {
-    foreach ($original->{$field['field_name']}[$langcode] as $original_item) {
-      $original_fids[] = $original_item['fid'];
-      if (isset($original_item['fid']) && !in_array($original_item['fid'], $current_fids)) {
-        // Decrement the file usage count by 1.
-        $file = (object)$original_item;
-        file_usage_delete($file, 'media', $entity_type, $id, 1);
-      }
-    }
-  }
-
-  // Add new usage entries for newly added files.
-  foreach ($items as $item) {
-    if (!in_array($item['fid'], $original_fids)) {
-      $file = (object) $item;
-      file_usage_add($file, 'media', $entity_type, $id);
-    }
-  }
-}
-
-/**
- * Implements hook_field_delete().
- */
-function media_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
-  // Delete all file usages within this entity.
-  foreach ($items as $delta => $item) {
-    $file = (object) $item;
-    file_usage_delete($file, 'media', $entity_type, $id, 0);
-  }
-}
-
-/**
- * Implements hook_field_delete_revision().
- */
-function media_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-  foreach ($items as $delta => $item) {
-    // @TODO: Not sure if this is correct
-    $file = (object)$item;
-    if (file_usage_delete($file, 'media', $entity_type, $id, 1)) {
-      $items[$delta] = NULL;
-    }
-  }
-}
-
-/**
- * Implements hook_field_instance_update().
- */
-function media_field_update_instance($instance, $prior_instance) {
-  // Clear the filter cache when updating instance settings for a media entity.
-  if ($instance['entity_type'] == 'media') {
-    media_filter_invalidate_caches();
-  }
-}
diff --git a/media.filter.inc b/media.filter.inc
deleted file mode 100644
index aec8abe..0000000
--- a/media.filter.inc
+++ /dev/null
@@ -1,425 +0,0 @@
-<?php
-
-/**
- * @file
- * Functions related to the WYSIWYG editor and the media input filter.
- *
- * @TODO: Rename this file?
- */
-
-/**
- * Implementation of hook_wysiwyg_include_directory()
- */
-function media_wysiwyg_include_directory($type) {
-  switch($type) {
-    case 'plugins':
-      return 'wysiwyg_plugins';
-        break;
-    }
-}
-
-/**
- * Filter callback for media markup filter.
- *
- * @TODO check for security probably pass text through filter_xss
- * @return unknown_type
- */
-function media_filter($text) {
-  $text = ' ' . $text . ' ';
-  $text = preg_replace_callback("/\[\[.*?]]/s",'media_token_to_markup', $text);
-
-  return $text;
-}
-
-/**
- *  Filter callback for media url filter.
- *  @TODO: There are currently problems with this. For instance, if a file is
- *  to be loaded from a remote location here, it will be recreated multiple
- *  times, each time this filter is called. If we want to continue supporting
- *  this feature, we would need to probably create a new stream or other way
- *  to lookup a remote file w/ its local version. Probably best as a contributed
- *  module because of this difficulty. ~ aaron.
- */
-function media_url_filter($text, $filter) {
-  $text = ' '. $text .' ';
-
-  // Need to attach the variables to the callback after the regex.
-  $callback = _media_url_curry('_media_url_parse_full_links', 1);
-
-  // Match absolute URLs.
-  $text = preg_replace_callback("`(<p>|<li>|<br\s*/?>|[ \n\r\t\(])((http://|https://)([a-zA-Z0-9@:%_+*~#?&=.,/;-]*[a-zA-Z0-9@:%_+*~#&=/;-]))([.,?!]*?)(?=(</p>|</li>|<br\s*/?>|[ \n\r\t\)]))`i", $callback, $text);
-
-  return $text;
-}
-
-/**
- * If one of our allowed providers knows what to do with the url,
- * then let it embed the video.
- *
- * @param int $filter
- *  The filter id.
- * @param array $match
- *  The matched text from our regex.
- *
- * @return string
- *  The replacement text for the url.
- */
-function _media_url_parse_full_links($match) {
-  // Get just the URL.
-  $match[2] = check_url(decode_entities($match[2]));
-  try {
-    $file = media_parse_to_file($match[2]);
-  }
-  catch (Exception $e) {
-    // Ignore errors; pass the original text for other filters to deal with.
-    return $match[0];
-  }
-
-  if ($file->fid) {
-    // Return the embedded media.
-    // Now load the desired media to display.
-    $medias = entity_load('media', array($file->fid));
-
-    if (!empty($medias)) {
-      $media = array_pop($medias);
-
-      // Generate a preview of the file
-      // @TODO: Allow user to change the formatter in the filter settings.
-      $preview = field_view_field('media', $media, 'file', 'media_large');
-      $preview['#show_names'] = TRUE;
-
-      return drupal_render($preview);
-    }
-  }
-
-  // Nothing was parsed; return the original text.
-  return $match[0];
-}
-
-function _media_url_curry($func, $arity) {
-  return create_function('', "
-    \$args = func_get_args();
-    if(count(\$args) >= $arity)
-        return call_user_func_array('$func', \$args);
-    \$args = var_export(\$args, 1);
-    return create_function('','
-        \$a = func_get_args();
-        \$z = ' . \$args . ';
-        \$a = array_merge(\$z,\$a);
-        return call_user_func_array(\'$func\', \$a);
-    ');
-  ");
-}
-
-/**
- * Replace callback to convert tag into markup
- * @param string $match
- * Takes a match of tag code
- * @param boolean $wysiwyg
- *   Set to TRUE if called from within the WYSIWYG text area editor.
- * @return
- * Return the replaced markup
- */
-function media_token_to_markup($match, $wysiwyg = FALSE) {
-  $match = str_replace("[[","",$match);
-  $match = str_replace("]]","",$match);
-  $tag = $match[0];
-
-  try {
-    if (!is_string($tag)) {
-      throw new Exception('Unable to find matching tag');
-    }
-    
-    $media = drupal_json_decode($tag);
-
-    if (!isset($media['fid'])) {
-      throw new Exception('No file Id');
-    }
-    if (!isset($media['view_mode'])) {
-      // Should we log or throw an exception here instead?
-      // Do we need to validate the view mode for fields API?
-      $media['view_mode'] = media_variable_get('wysiwyg_default_view_mode');
-    }
-    $media_obj = media_load($media['fid']);
-    if (!$media_obj) {
-     throw new Exception('Could not load media object');
-    }
-
-    // Track the fid of this media object in the {media_filter_usage} table.
-    media_filter_track_usage($media['fid']);
-
-    $settings = is_array($media['attributes']) ? $media['attributes'] : array();
-    $attribute_whitelist = media_variable_get('wysiwyg_allowed_attributes');
-    $settings = array_intersect_key($settings, array_flip($attribute_whitelist));
-
-    // @TODO: What case does this provide for?  Can we add this logic in JS when we embed it?
-    // This doesn't look great to me.  Also won't work if the style has anything
-    // between width and height (or if they are in reverse order).
-    if (isset($settings['style'])) {
-      if (preg_match('@width: (.+?)px; height: (.+?)px;@i', $settings['style'], $matches)) {
-        $settings['width'] = $matches[1];
-        $settings['height'] = $matches[2];
-      }
-    }
-    if ($wysiwyg) {
-      $settings['wysiwyg'] = $wysiwyg;
-    }
-  }
-  catch (Exception $e) {
-    watchdog('media', 'Unable to render media from %tag. Error: %error', array('%tag' => $tag, '%error' => $e->getMessage()));
-    return '';
-  }
-
-  $file_field = media_get_file_without_label($media_obj, $media['view_mode'], $settings);
-  return drupal_render($file_field);
-}
-
-/**
- * Builds a map of media tags in the element being rendered to their rendered HTML.
- *
- * The map is stored in JS, so we can transform them when the editor is being displayed.
- *
- * @param array $element
- */
-function media_pre_render_text_format($element) {
-  // filter_process_format() copies properties to the expanded 'value' child
-  // element.
-  if (!isset($element['format'])) {
-    return $element;
-  }
-  
-  $field = &$element['value'];
-  $settings = array(
-    'field' => $field['#id'],
-  );
-
-  $tagmap = _media_generate_tagMap($field['#value']);
-
-  if (isset($tagmap)) {
-    drupal_add_js(array('tagmap' => array_unique($tagmap)), 'setting');
-  }
-  return $element;
-}
-
-/**
- * Generates an array of [inline tags] => <html> to be used in filter
- * replacement and to add the mapping to JS.
- * @param
- * The String containing text and html markup of textarea
- * @return
- * An associative array with tag code as key and html markup as the value.
- *
- * @see
- * media_process_form
- * media_token_to_markup
- */
-function _media_generate_tagMap($text) {
-  // Making $tagmap static as this function is called many times and
-  // adds duplicate markup for each tag code in Drupal.settings JS,
-  // so in media_process_form it adds something like tagCode:<markup>,
-  // <markup> and when we replace in attach see two duplicate images
-  // for one tagCode. Making static would make function remember value
-  // between function calls. Since media_process_form is multiple times
-  // with same form, this function is also called multiple times.
-  static $tagmap = array();
-  preg_match_all("/\[\[.*?]]/s", $text, $matches, PREG_SET_ORDER);
-  foreach($matches as $match) {
-    // We see if tagContent is already in $tagMap, if not we add it
-    // to $tagmap.  If we return an empty array, we break embeddings of the same
-    // media multiple times.
-    if(empty($tagmap[$match[0]])) {
-      // @TODO: Total HACK, but better than nothing.
-      // We should find a better way of cleaning this up.
-      if ($markup_for_media = media_token_to_markup($match, TRUE)) {
-        $tagmap[$match[0]] = $markup_for_media;
-      } else {
-        $tagmap[$match[0]] = '<div><img src="/broken.jpg" height="50px" width="100px"/></div>';
-      }
-    }
-  }
-  return $tagmap;
-}
-
-/**
- * Form callback used when embedding media.
- *
- * Allows the user to pick a format for their media file.
- * Can also have additional params depending on the media type.
- */
-function media_format_form($form, $form_state, $media) {
-  // This will vary depending on the media type.
-  $form = array();
-  $form['#media'] = $media;
-
-  $instance_info = field_info_instance('media', 'file', $media->type);
-  $entity_info = entity_get_info('media');
-  $view_modes = $entity_info['view modes'];
-
-  $options = array();
-
-  drupal_alter('media_wysiwyg_allowed_view_modes', $media_type, $view_modes);
-
-  foreach ($view_modes as $key => $title) {
-    $format = field_get_display($instance_info, $key, $media);
-    //If the format is set to hidden, don't offer it
-    if ($format['type'] == 'hidden') {
-      continue;
-    }
-
-    //@TODO: Display more verbose information about which formatter and what it does.
-    $options[$key] = $title['label'];
-    $file_field = media_get_file_without_label($media, $key, array('wysiwyg' => TRUE));
-
-    // Make a pretty name out of this.
-    $formats[$key] = drupal_render($file_field);
-  }
-
-  if (!count($formats)) {
-    throw new Exception('Unable to continue, no available formats for displaying media.');
-    return;
-  }
-
-  $default_view_mode = media_variable_get('wysiwyg_default_view_mode');
-  if (!isset($formats[$default_view_mode])) {
-    $default_view_mode = key($formats);
-  }
-
-
-  // Add JS and settings array of formats.
-  $settings = array();
-  $settings['media'] = array('formatFormFormats' => $formats);
-  drupal_add_js($settings, 'setting');
-
-  drupal_add_library('media', 'media_base');
-  drupal_add_library('system', 'form');
-
-  $path = drupal_get_path('module', 'media');
-  $form['#attached']['js'][] = $path . '/javascript/media-format-form.js';
-  $form['#attached']['css'][] = $path . '/css/media-format-form.css';
-
-  $form['heading'] = array(
-    '#type' => 'markup',
-    '#prefix' => '<h1 class="title">',
-    '#suffix' => '</h1>',
-    '#markup' => t('Embedding %filename', array('%filename' => $media->filename)),
-  );
-
-  $preview = media_get_thumbnail_preview($media);
-
-  $form['preview'] = array(
-    '#type' => 'markup',
-    '#title' => basename($media->uri),
-    '#markup' => drupal_render($preview),
-  );
-
-  // These will get passed on to WYSIWYG
-  $form['options'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('options'),
-  );
-
-  $form['options']['format'] = array(
-    '#type' => 'select',
-    '#title' => t('Current format is'),
-    '#options' => $options,
-    '#default_value' => $default_view_mode
-  );
-
-  // Similar to a form_alter, but we want this to run first so that media.types.inc
-  // can add the fields specific to a given type (like alt tags on media).
-  // If implemented as an alter, this might not happen, making other alters not
-  // be able to work on those fields.
-  // @TODO: We need to pass in existing values for those attributes.
-  drupal_alter('media_format_form_prepare', $form, $form_state, $media);
-
-  if (!element_children($form['options'])) {
-    $form['options']['#attributes'] = array('style' => 'display:none');
-  }
-
-  return $form;
-}
-
-
-
-/**
- * Wrapper around field_view_field, returns the file field w/o a label
- *
- * @param Object $media
- * @param string $view_mode
- * @param array $settings
- *  Any attribute overrides to pass to the style formatter.
- * @return array
- *  drupal_renderable array
- */
-function media_get_file_without_label($media, $view_mode, $settings = array()) {
-  $instance = field_info_instance('media', 'file', $media->type);
-  $format = field_get_display($instance, $view_mode, $media);
-  $format['label'] = 'hidden';
-  $format['settings'] = array_merge($format['settings'], $settings);
-  $media->override = $settings;
-  return field_view_field('media', $media, 'file', $format);
-}
-
-/**
- * Clears caches that may be affected by the media filter.
- *
- * The media filter calls media_load(). This means that if a media object
- * is updated, the check_markup() and field caches could return stale content.
- * There are several possible approaches to deal with this:
- *  - Disable filter caching in media_filter_info(), this was found to cause a
- *    30% performance hit from profiling four node teasers, due to both the
- *    media filter itself, and other filters that can't be cached.
- *  - Clear the filter and field caches whenever any media node is updated, this
- *    would ensure cache coherency but would reduce the effectiveness of those
- *    caches on high traffic sites with lots of media content updates.
- *  - The approach taken here: Record the fid of all media objects that are
- *    referenced by the media filter. Only clear the filter and field caches
- *    when one of these is updated, as opposed to all media objects.
- *  - @todo: consider an EntityFieldQuery to limit cache clearing to only those
- *    entities that use a text format with the media filter, possibly checking
- *    the contents of those fields to further limit this to fields referencing
- *    the media object being updated. This would need to be implemented
- *    carefully to avoid scalability issues with large result sets, and may
- *    not be worth the effort.
- *
- * @param $fid
- *   Optional media fid being updated. If not given, the cache will be cleared
- *   as long as any file is referenced.
- */
-function media_filter_invalidate_caches($fid = FALSE) {
-  // If fid is passed, confirm that it has previously been referenced by the
-  // media filter. If not, clear the cache if the {media_filter_usage} has any
-  // valid records.
-  if (($fid && db_query('SELECT fid FROM {media_filter_usage} WHERE fid = :fid', array(':fid' => $fid))->fetchField()) || (!$fid && media_filter_usage_has_records())) {
-    // @todo: support entity cache, either via a hook, or using module_exists().
-    cache_clear_all('*', 'cache_filter', TRUE);
-    cache_clear_all('*', 'cache_field', TRUE);
-  }
-}
-
-/**
- * Determines if the {media_filter_usage} table has any entries.
- */
-function media_filter_usage_has_records() {
-  return  (bool) db_query_range('SELECT 1 FROM {media_filter_usage} WHERE fid > :fid', 0, 1, array(':fid' => 0))->fetchField();
-}
-
-/**
- * Tracks usage of media fids by the media filter.
- *
- * @param $fid
- *   The media fid.
- */
-function media_filter_track_usage($fid) {
-  // This function only tracks when fids are found by the media filter.
-  // It would be impractical to check when formatted text is edited to remove
-  // references to fids, however by keeping a timestamp, we can implement
-  // rudimentary garbage collection in hook_flush_caches().
-  // However we only need to track that an fid has ever been referenced,
-  // not every time, so avoid updating this table more than once per month,
-  // per fid.
-  $timestamp = db_query('SELECT timestamp FROM {media_filter_usage} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
-  if (!$timestamp || $timestamp <= REQUEST_TIME - 86400 * 30) {
-    db_merge('media_filter_usage')->key(array('fid' => $fid))->fields(array('fid' => $fid, 'timestamp' => REQUEST_TIME))->execute();
-  }
-}
diff --git a/media.info b/media.info
index 5af89ea..782b255 100644
--- a/media.info
+++ b/media.info
@@ -6,10 +6,10 @@ core = 7.x
 php = 5.2
 files[] = media.install
 files[] = media.module
-files[] = media.admin.inc
-files[] = media.theme.inc
-files[] = media.types.inc
-files[] = MediaReadOnlyStreamWrapper.inc
+files[] = includes/media.admin.inc
+files[] = includes/media.theme.inc
+files[] = includes/media.types.inc
+files[] = includes/MediaReadOnlyStreamWrapper.inc
 ; Tests
 files[] = test/media.types.test
 files[] = test/media.entity.test
diff --git a/media.install b/media.install
index eae4621..957add3 100644
--- a/media.install
+++ b/media.install
@@ -511,4 +511,11 @@ function media_update_7014() {
     variable_del('media__dialog_get_theme_name');
     variable_set('media__dialog_theme', $old_value);
   }
+}
+
+/**
+ * Rebuild the registry to locate class files in new location.
+ */
+function media_update_7015() {
+  registry_rebuild();
 }
\ No newline at end of file
diff --git a/media.module b/media.module
index b21f4dd..ff1506c 100644
--- a/media.module
+++ b/media.module
@@ -17,13 +17,13 @@
 require_once ( dirname(__FILE__) . '/includes/media.variables.inc');
 
 // Creating media types, entity definition
-require_once ( dirname(__FILE__) . '/media.types.inc');
+require_once ( dirname(__FILE__) . '/includes/media.types.inc');
 
 // Code relating to using media as a field
-require_once ( dirname(__FILE__) . '/media.fields.inc');
+require_once ( dirname(__FILE__) . '/includes/media.fields.inc');
 
  // Functions for working with [[inline tags]] and wysiwyg editors
-require_once ( dirname(__FILE__) . '/media.filter.inc');
+require_once ( dirname(__FILE__) . '/includes/media.filter.inc');
 
 /* ***************************************** */
 /* Hook Implementations                      */
@@ -64,7 +64,7 @@ function media_menu() {
     'page callback' => 'drupal_get_form',
     'page arguments' => array('media_admin_config_browser'),
     'access arguments' => array('administer media'),
-    'file' => 'media.admin.inc',
+    'file' => 'includes/media.admin.inc',
   );
   // For managing different types of media and the fields associated with them.
   $items['admin/config/media/rebuild_types'] = array(
@@ -73,7 +73,7 @@ function media_menu() {
     'page callback' => 'drupal_get_form',
     'page arguments' => array('media_admin_rebuild_types_form'),
     'access arguments' => array('administer media'),
-    'file' => 'media.admin.inc',
+    'file' => 'includes/media.admin.inc',
   );
   // For managing different types of media and the fields associated with them.
   $items['admin/config/media/types'] = array(
@@ -81,7 +81,7 @@ function media_menu() {
     'description' => 'Manage files used on your site.',
     'page callback' => 'media_admin_type_list',
     'access arguments' => array('administer media'),
-    'file' => 'media.admin.inc',
+    'file' => 'includes/media.admin.inc',
   );
 
   // Settings used for determining the type of media a file is.
@@ -93,7 +93,7 @@ function media_menu() {
     'page callback' => 'drupal_get_form',
     'page arguments' => array('media_admin_type_manage_form', 5),
     'access arguments' => array('administer media'),
-    'file' => 'media.admin.inc',
+    'file' => 'includes/media.admin.inc',
   );
 
   $items['admin/config/media/types/manage/%media_type/settings'] = array(
@@ -110,7 +110,7 @@ function media_menu() {
     'page arguments' => array('media_admin'),
     'access arguments' => array('administer media'),
     'type' => MENU_LOCAL_TASK,
-    'file' => 'media.admin.inc',
+    'file' => 'includes/media.admin.inc',
   );
 
   // Used to import files from a local filesystem into Drupal.
@@ -119,7 +119,7 @@ function media_menu() {
     'description' => 'Import files into your media library.',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('media_import'),
-    'file' => 'media.admin.inc',
+    'file' => 'includes/media.admin.inc',
     'access arguments' => array('import media'),
     'type' => MENU_LOCAL_ACTION,
   );
@@ -131,7 +131,7 @@ function media_menu() {
     'access callback' => 'media_access',
     'access arguments' => array('view'),
     'type' => MENU_CALLBACK,
-    'file' => 'media.browser.inc',
+    'file' => 'includes/media.browser.inc',
     'theme callback' => 'media_dialog_get_theme_name',
   );
 
@@ -141,7 +141,7 @@ function media_menu() {
     'description' => 'Make it easier to test media browser',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('media_browser_testbed'),
-    'file' => 'media.browser.inc',
+    'file' => 'includes/media.browser.inc',
     'access arguments' => array('administer media'),
     'type' => MENU_CALLBACK,
   );
@@ -158,7 +158,7 @@ function media_menu() {
     'access callback' => 'media_access',
     'access arguments' => array('view'),
     'type' => MENU_CALLBACK,
-    'file' => 'media.browser.inc',
+    'file' => 'includes/media.browser.inc',
   );
 
   $items['media/browser/library'] = array(
@@ -168,7 +168,7 @@ function media_menu() {
     'access callback' => 'media_access',
     'access arguments' => array('view'),
     'type' => MENU_CALLBACK,
-    'file' => 'media.browser.inc',
+    'file' => 'includes/media.browser.inc',
   );
 
   $items['media/%media/format-form'] = array(
@@ -178,7 +178,7 @@ function media_menu() {
     'page arguments' => array('media_format_form', 1),
     'access arguments' => array('administer media'),
     'weight' => 0,
-    'file' => 'media.filter.inc',
+    'file' => 'includes/media.filter.inc',
     'theme callback' => 'media_dialog_get_theme_name',
   );
 
@@ -187,7 +187,7 @@ function media_menu() {
     'page arguments' => array(1),
     'access callback' => 'media_access',
     'access arguments' => array('view'),
-    'file' => 'media.pages.inc',
+    'file' => 'includes/media.pages.inc',
   );
 
   $items['media/%media/view'] = array(
@@ -205,7 +205,7 @@ function media_menu() {
     'weight' => 0,
     'type' => MENU_LOCAL_TASK,
     'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
-    'file' => 'media.pages.inc',
+    'file' => 'includes/media.pages.inc',
      // Use the same setting as node module
     'theme callback' => '_node_custom_theme',
   );
@@ -217,7 +217,7 @@ function media_menu() {
     'access callback' => 'media_access',
     'access arguments' => array('edit'),
     'weight' => 0,
-    'file' => 'media.pages.inc',
+    'file' => 'includes/media.pages.inc',
      // Use the same setting as node module
     'theme callback' => '_node_custom_theme',
   );
@@ -231,7 +231,7 @@ function media_menu() {
     'weight' => 1,
     'type' => MENU_LOCAL_TASK,
     'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
-    'file' => 'media.pages.inc',
+    'file' => 'includes/media.pages.inc',
      // Use the same setting as node module
     'theme callback' => '_node_custom_theme',
   );
@@ -244,7 +244,7 @@ function media_menu() {
     'access callback' => 'media_access',
     'access arguments' => array('view'),
     'type' => MENU_CALLBACK,
-    'file' => 'media.pages.inc',
+    'file' => 'includes/media.pages.inc',
   );
 
   return $items;
@@ -301,48 +301,48 @@ function media_theme() {
     // A link for a file w/ an icon to the media/$fid page.
     'media_link' => array(
       'variables' => array('file' => NULL),
-      'file' => 'media.theme.inc',
+      'file' => 'includes/media.theme.inc',
     ),
 
     // A preview of the uploaded file.
     'media_thumbnail' => array(
       'render element' => 'element',
-      'file' => 'media.theme.inc',
+      'file' => 'includes/media.theme.inc',
     ),
 
     // Administrative thumbnail previews.
     'media_admin_thumbnail' => array(
       'variables' => array('file' => array(), 'style_name' => 'thumbnail'),
-      'file' => 'media.theme.inc',
+      'file' => 'includes/media.theme.inc',
     ),
 
     // Administrative thumbnail previews.
     'media_admin_thumbnail_operations' => array(
       'variables' => array('file' => NULL),
-      'file' => 'media.theme.inc',
+      'file' => 'includes/media.theme.inc',
     ),
 
     // Tabs in the media browser.
     'media_browser_tabs' => array(
-      'file' => 'media.theme.inc',
+      'file' => 'includes/media.theme.inc',
     ),
 
     // Dialog page.
     'media_dialog_page' => array(
       'render element' => 'page',
-      'template' => 'theme/media-dialog-page',
-      'file' => 'media.theme.inc',
+      'template' => 'templates/media-dialog-page',
+      'file' => 'includes/media.theme.inc',
     ),
 
     //
     'media_element' => array(
       'render element' => 'element',
-      'file' => 'media.theme.inc',
+      'file' => 'includes/media.theme.inc',
     ),
 
     'media_formatter_large_icon' => array(
       'variables' => array('file' => NULL),
-      'file' => 'media.theme.inc',
+      'file' => 'includes/media.theme.inc',
     ),
   );
 }
@@ -482,7 +482,7 @@ function media_media_display_types() {
     'icon' => $path . '/images/display-list.png',
     'icon_active' => $path . '/images/display-list-active.png',
     'callback' => 'media_admin_list',
-    'file' => drupal_get_path('module', 'media') . '/media.admin.inc',
+    'file' => drupal_get_path('module', 'media') . '/includes/media.admin.inc',
   );
 
   $display_types['thumbnails'] = array(
@@ -491,7 +491,7 @@ function media_media_display_types() {
     'icon' => $path . '/images/display-thumbnails.png',
     'icon_active' => $path . '/images/display-thumbnails-active.png',
     'callback' => 'media_admin_thumbnails',
-    'file' => drupal_get_path('module', 'media') . '/media.admin.inc',
+    'file' => drupal_get_path('module', 'media') . '/includes/media.admin.inc',
   );
 
   return $display_types;
@@ -753,9 +753,9 @@ function media_library() {
   $libraries['media_base'] = array(
     'title' => 'Media base',
     'js' => array(
-      $path . '/javascript/media.core.js' => array('group' => JS_LIBRARY, 'weight' => - 5),
-      $path . '/javascript/util/json2.js' => array('group' => JS_LIBRARY),
-      $path . '/javascript/util/ba-debug.min.js' => array('group' => JS_LIBRARY),
+      $path . '/js/media.core.js' => array('group' => JS_LIBRARY, 'weight' => - 5),
+      $path . '/js/util/json2.js' => array('group' => JS_LIBRARY),
+      $path . '/js/util/ba-debug.min.js' => array('group' => JS_LIBRARY),
     ),
     'css' => array(
       $path . '/css/media.css',
@@ -769,7 +769,7 @@ function media_library() {
   $libraries['media_browser'] = array(
     'title' => 'Media Browser popup libraries',
     'js' => array(
-      $path . '/javascript/media.popups.js' => array('group' => JS_DEFAULT),
+      $path . '/js/media.popups.js' => array('group' => JS_DEFAULT),
     ),
     'dependencies' => array(
       array('media', 'media_base'),
@@ -785,7 +785,7 @@ function media_library() {
   $libraries['media_browser_page'] = array(
     'title' => 'Media browser',
     'js' => array(
-      $path . '/javascript/media-browser.js'  => array('group' => JS_DEFAULT),
+      $path . '/js/media.browser.js'  => array('group' => JS_DEFAULT),
     ),
     'css' => array(
       $path . '/css/media.browser.css' => array('group' => CSS_DEFAULT),
@@ -831,7 +831,7 @@ function media_dialog_get_theme_name() {
  *    A fully populated object, or FALSE on an error.
  */
 function media_retrieve_xml($url, $display_errors = FALSE, $refresh = FALSE) {
-  module_load_include('inc', 'media', 'media.xml');
+  module_load_include('inc', 'media', 'includes/media.xml');
   return _media_retrieve_xml($url, $display_errors, $refresh);
 }
 
@@ -1089,7 +1089,7 @@ function media_element_process(&$element, &$form_state, $form) {
   );
 
   // Media browser attach code.
-  $element['#attached']['js'][] = drupal_get_path('module', 'media') . '/javascript/media.js';
+  $element['#attached']['js'][] = drupal_get_path('module', 'media') . '/js/media.js';
 
   $settings = array();
   $setting['media']['elements'][$element['#id']] = $element['#media_options'];
@@ -1102,7 +1102,7 @@ function media_element_process(&$element, &$form_state, $form) {
   // hmm... Might need to think about this.
   // All settings would likely apply to all media in a multi-value, but what about passing the existing fid?
 
-  module_load_include('inc', 'media', 'media.browser');
+  module_load_include('inc', 'media', 'includes/media.browser');
   media_attach_browser_js($element);
 
   return $element;
diff --git a/media.pages.inc b/media.pages.inc
deleted file mode 100644
index c005a2e..0000000
--- a/media.pages.inc
+++ /dev/null
@@ -1,432 +0,0 @@
-<?php
-
-/**
- * @file
- * Common pages for the Media module.
- */
-
-/**
- * Menu callback; view a single piece of media.
- */
-function media_file_view($media) {
-  // @todo Implement granular editorial access: http://drupal.org/node/696970.
-  //   In the meantime, protect information about private files from being
-  //   discovered by unprivileged users. Media IDs are autoincrement, so one can
-  //   attempt discovery by trying to access different media/ID paths. See also
-  //   media_browser_list(). This logic potentially belongs within
-  //   media_access(), but that would require extending that function's
-  //   signature to accept a $media paramter, and this is temporary code anyway.
-  if (!user_access('administer media') && (file_uri_scheme($media->uri) === 'private')) {
-    return MENU_ACCESS_DENIED;
-  }
-
-  drupal_set_title($media->filename);
-
-  // @TODO maybe something more elegant... ?
-  field_attach_prepare_view('media', array($media->fid => $media), 'media_original');
-  $build['media'][$media->fid] = field_attach_view('media', $media, 'media_original');
-
-  return $build;
-}
-
-/**
- *  Menu callback; display a thumbnail for an AJAX preview.
- */
-function media_preview_ajax() {
-  $url = $_GET['url'];
-  $uri = media_parse_to_uri($url);
-  if ($uri) {
-    $media = file_uri_to_object($uri);
-    drupal_json_output(array(
-      'preview' => theme('media_admin_thumbnail', array('file' => $media)),
-    ));
-  }
-  else {
-    drupal_json_output(array(
-      'error' => t('Invalid URL.'),
-    ));
-  }
-  die();
-}
-
-/**
- * Menu callback; presents the Media editing form.
- */
-function media_page_edit($media) {
-  drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => $media->type, '@title' => $media->filename)), PASS_THROUGH);
-  return drupal_get_form('media_edit', $media);
-}
-
-/**
- * Menu callback; presents the Media editing form for multiple items.
- */
-function media_page_multiedit($media_items) {
-  if (!module_exists('multiform')) {
-    drupal_set_message('To edit multiple media items, you must install the multiform module.');
-  }
-  $i = 0;
-  foreach ($media_items as $item) {
-    // To maintain unique form_ids, increment this counter.
-    // @see media_forms().
-    $i++;
-    $forms[] = array("media_edit_$i", $item);
-  }
-  
-  $form = call_user_func_array('multiform_get_form', $forms);
-  $form['#attributes']['class'][] = 'media-multiedit-form';
-  
-  unset($form['buttons']['Delete']);
-  // Would be nice to add this to show a message, but not working.
-  // Can debug.
-  //$form['buttons']['Save']['#submit'][] = 'media_page_multiedit_submit';
-  
-  drupal_set_title(t('Editing multiple media files'));
-  return $form;
-}
-
-/**
- * Menu callback; shows delete confirmation form.
- */
-function media_page_delete($media) {
-  drupal_set_title(t('<em>Delete @type</em> @title', array('@type' => $media->type, '@title' => $media->filename)), PASS_THROUGH);
-  // Don't bother showing the form if the item is in use, since we won't allow
-  // them to delete it anyway. Note that file_usage_list() requires a stdClass,
-  // so we can't pass the $media object in directly.
-  $file = file_load($media->fid);
-  $references = file_usage_list($file);
-  if (!empty($references)) {
-    return t('The file %title is in use and cannot be deleted.', array('%title' => $media->filename));
-  }
-  else {
-    $files = array($media->fid => $media);
-    return drupal_get_form('media_multiple_delete_confirm', $files, '<front>', 'media/' . $media->fid);
-  }
-}
-
-/**
- * Confirm the request to delete files.
- */
-function media_multiple_delete_confirm($form, &$form_state, $files, $redirect_on_success = NULL, $redirect_on_cancel = NULL) {
-  $form['files'] = array('#tree' => TRUE);
-  $form['file_titles'] = array('#theme' => 'item_list');
-  foreach ($files as $fid => $value) {
-    $title = db_query('SELECT filename FROM {file_managed} WHERE fid = :fid', array(':fid' => $fid))->fetchField();
-    $form['files'][$fid] = array(
-      '#type' => 'value',
-      '#value' => $fid,
-    );
-    $form['file_titles']['#items'][] = check_plain($title);
-  }
-  $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
-  if (isset($redirect_on_success)) {
-    $form['redirect_on_success'] = array(
-      '#type' => 'value',
-      '#value' => $redirect_on_success,
-    );
-  }
-  $form['#submit'][] = 'media_multiple_delete_confirm_submit';
-
-  $confirm_question = format_plural(count($files),
-                                  'Are you sure you want to delete this item?',
-                                  'Are you sure you want to delete these items?');
-
-  return confirm_form($form,
-    $confirm_question,
-    isset($redirect_on_cancel) ? $redirect_on_cancel : current_path(),
-    t('This action cannot be undone.'),
-    t('Delete'),
-    t('Cancel'));
-}
-
-/**
- * Attempt to delete files and notify the user of the result.
- */
-function media_multiple_delete_confirm_submit($form, &$form_state) {
-  if ($form_state['values']['confirm']) {
-    $results = array();
-    $files = array_keys($form_state['values']['files']);
-    foreach ($files as $fid) {
-      $file = file_load($fid);
-      $files[$fid] = $file;
-      $results[$fid] = file_delete($file);
-    }
-    // The result of file_delete can be an array if the file is in use, or TRUE/FALSE.
-    foreach ($results as $fid => $result) {
-      if (is_array($result)) {
-        drupal_set_message(t('The file @title is in use and cannot be deleted.', array('@title' => $files[$fid]->filename)), 'warning');
-      }
-      elseif (!$result) {
-        drupal_set_message(t('The file @title was not deleted due to an error.', array('@title' => $files[$fid]->filename)), 'error');
-      }
-      else {
-        $message = t('File @title was deleted', array('@title' => $files[$fid]->filename));
-        watchdog('media', $message);
-        drupal_set_message($message);
-      }
-    }
-    if (isset($form_state['values']['redirect_on_success'])) {
-      $form_state['redirect'] = $form_state['values']['redirect_on_success'];
-    }
-  }
-}
-
-
-/**
- * Form callback for adding media via an upload form.
- * @todo: should use the AJAX uploader
- */
-function media_add_upload($form, &$form_state, $types = NULL) {
-
-  $validators = array(
-   'file_validate_extensions' => array(media_variable_get('file_extensions')),
-   'file_validate_size' => array(parse_size(media_variable_get('max_filesize'))),
-  );
-
-  //@todo add additional validators for media types allowed.
-  if ($types) {
-    $validators['media_file_validate_types'] = array($types);
-  }
-  
-  // A blank set of allowed file extensions means no need to validate.
-  if (!$validators['file_validate_extensions'][0]) {
-    unset($validators['file_validate_extensions']);
-  }
-  // Use the PHP limit for filesize if no variable was set.
-  if (!$validators['file_validate_size']) {
-    $validators['file_validate_size'] = file_upload_max_size();
-  }
-
-  if ($validators['file_validate_size'][0] == 0) {
-    unset($validators['file_validate_size']);
-  }
-
-  $form['#validators'] = $validators;
-  
-  $form['upload'] = array(
-    '#type' => 'file',
-    '#title' => t('Upload a new file'),
-    '#description' => theme('file_upload_help', array('description' => '', 'upload_validators' => $validators)),
-    '#upload_validators' => $validators,
-  );
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Submit'),
-  );
-
-  return $form;
-}
-
-/**
- * Validate the generic file upload with the global media settings.
- */
-function media_add_upload_validate($form, &$form_state) {
-  // Save the file as a temporary file.
-  $file = file_save_upload('upload', $form['#validators']);
-  
-  if ($file === NULL) {
-    form_set_error('upload', t("No file appears to have been selected."));
-  }
-  elseif ($file === FALSE) {
-    form_set_error('upload', t("Failed to upload the file."));
-  }
-  else {
-    $form_state['values']['upload'] = $file;
-  }
-}
-
-/**
- * Upload a file.
- */
-function media_add_upload_submit($form, &$form_state) {
-  $scheme = variable_get('file_default_scheme', 'public') . '://';
-  $file = $form_state['values']['upload'];
-  _media_save_file_permenently($file);
-
-  $destination = file_stream_wrapper_uri_normalize($scheme . $file->filename);
-
-  $defaults = array (
-    'display' => TRUE,
-  );
-
-  // Save the uploaded file.
-  $file = file_move($file, $destination, FILE_EXISTS_RENAME);
-
-  if ($file) {
-    drupal_set_message(t('The file @name was uploaded', array('@name' => $file->filename)));
-  }
-  else {
-    drupal_set_message(t('An error occurred and no file was uploaded.'), 'error');
-    return;
-  }
-
-  $form_state['redirect'] = array('media/browser', array('query' => array('render' => 'media-popup', 'fid' => $file->fid)));
-}
-
-function media_add_upload_multiple($form, &$form_state, $types) {
-  $form = media_add_upload($form, $form_state, $types);
-  unset($form['upload']['#title']);
-  // The validators will be set from plupload anyway.  This isn't pretty, but don't
-  // it to show up twice.
-  unset($form['upload']['#description']);
-  
-  $form['upload']['#type'] = 'plupload';
-  $form['submit']['#value'] = t('Start upload');
-  return $form;
-}
-
-function media_add_upload_multiple_submit($form, &$form_state) {
-  $scheme = variable_get('file_default_scheme', 'public') . '://';
-  $saved_files = array();
-  // We can't use file_save_upload() because of http://www.jacobsingh.name/content/tight-coupling-no-not
-  foreach ($form_state['values']['upload'] as $uploaded_file) {
-    if ($uploaded_file['status'] == 'done') {
-      $source = $uploaded_file['tmppath'];
-      $destination = file_stream_wrapper_uri_normalize($scheme . $uploaded_file['name']);
-      // Rename it to its original name, and put it in its final home.
-      // Note - not using file_move here because if we call file_get_mime
-      // (in file_uri_to_object) while it has a .tmp extension, it horks.
-      
-      $destination = file_unmanaged_move($source, $destination, FILE_EXISTS_RENAME);
-      
-      $file = file_uri_to_object($destination);
-      file_save($file);
-      _media_save_file_permenently($file);
-      
-      $saved_files[] = $file;
-    }
-    else {
-      // @todo: move this to element validate or something and clean up t().
-      form_set_error('pud', "Upload of {$uploaded_file['name']} failed");
-    }
-  }
-  // Get a list of fids to pass back.
-  $fids = array();
-  foreach ($saved_files as $file) {
-    $fids[] = $file->fid;
-  }
-  $form_state['redirect'] = array('media/browser', array('query' => array('render' => 'media-popup', 'fid' => $fids)));
-}
-
-/**
- * Form builder: Builds the edit file form.
- */
-function media_edit($form, $form_state, $media) {
-  field_attach_form('media', $media, $form, $form_state);
-  unset($form['file']);
-
-  $form['#attached'] = array(
-    'css' => array(drupal_get_path('module', 'media') . '/css/media.css'),
-  );
-
-  // Not sure about this class name, seems to indicate the style.
-  $form['#attributes']['class'][] = 'media-image-left';
-  $form['#attributes']['class'][] = 'media-edit-form';
-  
-  $form['preview'] = field_view_field('media', $media, 'file', 'media_preview');
-  $form['preview']['#weight'] = -10;
-  $form['preview']['#suffix'] = '<div class="no-overflow">';
-
-  // Add the buttons.
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['#prefix'] = '</div>';
-  $form['actions']['delete'] = array(
-    '#type' => 'submit',
-    '#value' => t('Delete'),
-    '#weight' => 15,
-    '#submit' => array('media_edit_delete_submit'),
-  );
-  
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-    '#weight' => 5,
-    '#submit' => array('media_edit_submit'),
-  );
-
-  $form['fid'] = array(
-    '#type' => 'hidden',
-    '#value' => $media->fid,
-  );
-
-  return $form;
-}
-
-function media_edit_validate($form, &$form_state) {
-  $media = media_load($form_state['values']['fid']);
-  field_attach_form_validate('media', $media, $form, $form_state);
-}
-
-/**
- * Process media_edit form submissions.
- */
-function media_edit_submit($form, &$form_state) {
-  $media = media_load($form_state['values']['fid']);
-  field_attach_submit('media', $media, $form, $form_state);
-  media_save($media);
-  $form_state['redirect'] = 'media/' . $media->fid;
-}
-
-function media_edit_delete_submit($form, &$form_state) {
-  $fid = $form_state['values']['fid'];
-  $destination = array();
-  if (isset($_GET['destination'])) {
-    $destination = drupal_get_destination();
-    unset($_GET['destination']);
-  }
-  $form_state['redirect'] = array('media/' . $fid . '/delete', array('query' => $destination));
-}
-
-function media_add_remote($form, &$form_state) {
-  // Totally prototyping code to show designs.
-  $form['sources'] = array(
-    '#type' => 'vertical_tabs',
-    '#title' => 'Sources',
-  );
-
-  $form['sources']['paste'] = array(
-    '#type' => 'fieldset',
-    '#title' => 'Paste URL or embed code',
-  );
-
-
-  $providers = '';
-  $providers .= '<img style="height:50px; margin:20px" src="http://www.macuser.com/2008/10/09/top_youtube_logo_31_Dec_06.jpg">';
-  $providers .= '<img style="height:50px; margin:20px" src="http://jasonhilldesign.com/FlikrLogo.jpg">';
-  $form['sources']['paste']['code'] = array(
-    '#type' => 'textarea',
-    '#title' => t('URL or embed code'),
-    '#description' => t('The following providers are supported: <br/>' . $providers),
-  );
-
-  $form['sources']['youtube'] = array(
-    '#type' => 'fieldset',
-    '#title' => 'YouTube',
-    '#description' => t(''),
-    '#attributes' => array('style' => 'height: 300px; overflow:auto'),
-  );
-
-  $form['sources']['flikr'] = array(
-    '#type' => 'fieldset',
-    '#title' => 'Flikr',
-  );
-
-  $box = '<div style="width:100px; height:100px; border:1px solid blue; padding:10px; float:left; margin:5px;"> Video </div>';
-  $boxes = '';
-  for ($i = 0; $i < 10; $i++) {
-    $boxes .= $box;
-  }
-  $form['sources']['youtube']['stupid'] = array(
-    '#markup' => $boxes,
-  );
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Import'),
-    '#attributes' => array('style' => 'float:right'),
-    '#suffix' => '<br style="clear:both" />',
-  );
-
-  return $form;
-}
-
diff --git a/media.theme.inc b/media.theme.inc
deleted file mode 100644
index 1b5f373..0000000
--- a/media.theme.inc
+++ /dev/null
@@ -1,324 +0,0 @@
-<?php
-
-/**
- * @file
- * Media Theming
- *
- * Theming functions for the Media module.
- */
-
-/**
- * Display the media file browser.
- * @TODO this is depreciated I think
- * @param array $element
- *   The form element.
- * @return string
- */
-function theme_media_file_browser($element) {
-  // Add the CSS for our display.
-  $output = '<div class="media browser">' . $element . '</div>';
-
-  return $output;
-}
-
-/**
- * Display a media file list.
- * @TODO this is depreciated I think
- * @param array $element
- *   The form element.
- * @return string
- */
-function theme_media_file_list($element) {
-  // Add the CSS for our display.
-  return '<div class="media-file-list">' . theme('form_element', $element, $element['#children']) . '</div>';
-}
-
-/**
- * Display a browser pane.
- * @TODO this is depreciated I think
- *
- * @param array $form
- *   The form element.
- * @return string
- */
-function theme_media_browser_pane($form) {
-  return;
-  $output   = array();
-
-  // render the drawers
-  $output[] = '<div' . drupal_attributes($form['#attributes']) . '>';
-
-  // render the drawer list
-  $output[] = '  <div class="browser drawers">';
-  $output[] = drupal_render_form(null, $form['drawers']);
-  $output[] = ' </div>';
-
-  // render the drawer displays
-  $output[] = drupal_render_form(null, $form);
-  $output[] = '</div>';
-
-  return implode("\n", $output);
-}
-
-
-/**
- * Default theming function for creating the browser frame.
- * Assumes an array of file objects as $files and an
- * array of $parameters
- * @param $variables
- *   array of variables
- * @return unknown_type
- */
-function theme_media_browser_content_frame($variables) {
-  // Pull out all the variables into a usable form
-  extract($variables);
-  // Did we get any files back?
-  if (! count($files)) {
-    // @TODO display no files found
-  }
-
-  $html = array();
-
-  // On the first invocation, load javascript and build the browser frame
-   if ($invoke) {
-
-
-  }
-   // Render the results limiter
-  $html[] = theme('media_browser_control_result_limit', array('parameters' => $parameters));
-  // Render the actual content
-  $form = drupal_get_form('media_file_listing_form', $files, $parameters);
-  $html[] = drupal_render($form);
-
-  // Make sure to close the wrapping div
-  if ($invoke) {
-    $html[] = '</div>';
-  }
-  return implode("\n", $html);
-}
-
-
-
-/**
- * Display a item list of files as thumbnails. Implements
- * the admin thumbnail theme for now- serves as a wrapper
- *
- *  @param $files
- *    An array of file objects to display.
- *  @return
- */
-function theme_media_browser_thumbnails($variables) {
-  $files = $variables['files'];
-  $style_name = $variables['style_name'];
-  $thumbnails = array();
-  foreach ($files as $file) {
-    $thumbnails[] = theme('media_admin_thumbnail', array('file' => $file, 'style_name' => $style_name));
-  }
-  return theme('item_list', array('items' => $thumbnails, 'attributes' => array('class' => 'media_content_navigator results')));
-}
-
-
-/**
- * Theme a thumbnail.
- * @param $variables
- *   array items being passed in
- */
-function theme_media_admin_thumbnail($variables) {
-  $path = drupal_get_path('module', 'media');
-  $file = $variables['file'];
-  $style_name = $variables['style_name'];
-  if (isset($file)) {
-    $file_url = file_create_url($file->uri);
-  }
-  else {
-    return '';
-  }
-  $output = '';
-  if (module_exists('styles')) {
-    $thumbnail = theme('styles',
-      array(
-        'field_type' => 'file',
-        'style_name' => $style_name,
-        'uri' => $file->uri,
-        'description' => t('Thumbnail for !filename.', array('!filename' => $file->filename)),
-        'object' => $variables['file'],
-    ));
-  }
-  else {
-    // Display a thumbnail for images.
-    if (strstr($file->filemime, 'image')) {
-      $thumbnail = theme('image_style',
-        array(
-          'style_name' => 'thumbnail',
-          'path' => $file->uri,
-          'alt' => t('Thumbnail for !filename.', array('!filename' => $file->filename)),
-        )
-      );
-    }
-    // Display the 'unknown' icon for other file types.
-    else {
-      $thumbnail = theme('image',
-        array(
-          'path' => $path . '/images/file-unknown.png',
-          'alt' => t('Thumbnail for !filename.', array('!filename' => $file->filename)),
-          'attributes' => array('class' => 'file-unknown'),
-        ));
-    }
-  }
-  $output .= l($thumbnail,
-    $file_url,
-    array(
-      'html' => TRUE,
-      'attributes' => array('class' => 'media-thumbnail'),
-    ));
-  return $output;
-}
-
-
-/**
- * Theme operations for a thumbnail.
- */
-function theme_media_admin_thumbnail_operations($variables) {
-  $destination = drupal_get_destination();
-  $file = $variables['file'];
-  $output = l(t('edit'), 'media/' . $file->fid . '/edit', array('query' => $destination));
-  return $output;
-}
-
-/**
- * Add messages to the page.
- */
-function template_preprocess_media_dialog_page(&$variables) {
-  $variables['messages'] = theme('status_messages');
-}
-
-/* ******************************************** */
-/* Content navigation controls                  */
-/* ******************************************** */
-
-
-/**
- * Theme function to display the results limiting- 10, 30, 50 results
- * per page.
- *
- * @param $variables
- *   array parameters
- * @return unknown
- */
-function theme_media_browser_control_result_limit($variables) {
-  // Pull out all the variables into a usable form
-  extract($variables);
-
-  if (! isset($limits)) {
-    $limits = array(10, 30, 50);
-  }
-  // @NOTE these do not need to be aware of the current
-  //       page because clicking them will reset the
-  //       display to 1 -> $limit
-  $parameters['page'] = 0;
-  // save the active limit
-  $current_limit = $parameters['limit'];
-  foreach ($limits as $limit) {
-    if ($limit == $current_limit) {
-      $class = 'active';
-    }
-    else {
-      $class = '';
-    }
-    // set the value of this limit parameter to this limit value
-    $parameters['limit'] = $limit;
-    $per_display[] = l($limit, $limit, array('query' => $parameters, 'attributes' => array('class' => $class)));
-  }
-
-  return theme('item_list', array('items'  => $per_display, 'attributes' => array('class' => 'result_limit')));
-}
-
-
-/**
- * Stolen from file.module theme_file_link
- *
- * @param $variables
- *   An associative array containing:
- *   - file: A file object to which the link will be created.
- */
-function theme_media_link($variables) {
-  $file = $variables['file'];
-
-  $url = 'media/' . $file->fid;
-  $icon = theme('file_icon', array('file' => $file));
-
-  // Set options as per anchor format described at
-  // http://microformats.org/wiki/file-format-examples
-  $options = array(
-    'attributes' => array(
-      'type' => $file->filemime . '; length=' . $file->filesize,
-    ),
-  );
-
-  // Use the description as the link text if available.
-  if (empty($file->description)) {
-    $link_text = check_plain($file->filename);
-  }
-  else {
-    $link_text = check_plain($file->description);
-    $options['attributes']['title'] = check_plain($file->filename);
-  }
-
-  return '<span class="file">' . $icon . ' ' . l($link_text, $url, $options) . '</span>';
-}
-
-/**
- * Adds a wrapper around a preview of a media file.
- * @param unknown_type $element
- * @return unknown_type
- */
-function theme_media_thumbnail($variables) {
-  $label = '';
-  $element = $variables['element'];
-  $destination = drupal_get_destination();
-
-  // Wrappers to go around the thumbnail
-  $prefix = '<div class="media-item"><div class="media-thumbnail">';
-  $suffix = '</div></div>';
-
-  // Arguments for the thumbnail link
-  $thumb = $element['#children'];
-  $target = 'media/' . $element['#object']->fid . '/edit';
-  $options = array('query' => $destination, 'html' => true, 'attributes' => array('title' => t('Click to edit details')));
-
-  // Element should be a field renderable array... This is a little wonky - admitted.
-  if (!empty($element['#show_names']) && $element['#name']) {
-    $label = '<div class="label-wrapper"><label class="media-filename">' . $element['#name'] . '</label></div>';
-  }
-
-  // How can I attach CSS here?
-  //$element['#attached']['css'][] = drupal_get_path('module', 'media') . '/css/media.css';
-  drupal_add_css(drupal_get_path('module', 'media') . '/css/media.css');
-
-  $output = $prefix;
-  if (!empty($element['#add_link'])) {
-    $output .= l($thumb, $target, $options);
-  }
-  else {
-    $output .= $thumb;
-  }
-  $output .= $label . $suffix;
-  return $output;
-}
-
-function template_preprocess_media_thumbnail(&$variables) {
-  // Set the name for the thumbnail to be the filename.  This is done here so
-  // that other modules can hijack the name displayed if it should not be the
-  // filename.
-  $variables['element']['#name'] = isset($variables['element']['#object']->filename) ? $variables['element']['#object']->filename : NULL;
-}
-
-/**
- * Field formatter for displaying a file as a large icon.
- */
-function theme_media_formatter_large_icon($variables) {
-  $file = $variables['file'];
-  $icon_dir = media_variable_get('icon_base_directory') . '/' . media_variable_get('icon_set');
-  $icon = file_icon_path((object)$file, $icon_dir);
-  return theme('image', array('path' => $icon));
-}
diff --git a/media.types.inc b/media.types.inc
deleted file mode 100644
index 8f7a813..0000000
--- a/media.types.inc
+++ /dev/null
@@ -1,323 +0,0 @@
-<?php
-
-/**
- *  @file
- *  Helper functions related to media types.  CRUD for saving their settings mainly.
- *
- *  Also contains the media entity class definition.
- *  @see media.install for a list of the base types.
- */
-
-/**
- *  Update an existing media type or create a new one.
- *
- *  The default media types are currently 'Audio', 'Image', 'Video', and
- *  'Other', which are defined in media_install().
- *
- *  @param object &$type
- *    $type is an object with the following fields:
- *      ->name => The name of the media asset type, such as 'video';
- *      ->label => The human readable name;
- *      ->base => boolean: If the media type cannot be removed.
- *      ->type_callback => Function call to filter an instance to its bundle.
- *      ->type_callback_args => An array of args to be passed to type_callback.
- *  @return void;
- */
-function media_type_save(&$type) {
-  if (empty($type->name)) {
-    throw new Exception('Enable to add type, name not provided');
-  }
-
-  $type = media_type_set_defaults($type);
-  if (!is_array($type->type_callback_args)) {
-    throw new Exception('type_callback_args should be an array');
-  }
-
-  $type->type_callback_args = serialize($type->type_callback_args);
-
-  $ret = db_merge('media_type')
-    ->key(array('name' => $type->name))
-    ->fields((array)$type)
-  ->execute();
-
-  media_type_configure_fields($type);
-
-  // Clear the caches
-  drupal_static_reset('media_type_get_types');
-  drupal_static_reset('media_type_get_mime_map');
-  return;
-}
-
-/**
- * Assigns formatters to view_modes for the file field of a media type.
- *
- * Each media type contains a field called "file" and certain stock view modes.
- * This function will assign a format to each view mode. For instance, images
- * would want to use a thumbnail format for files when in the "preview"
- * view_mode.  The called would pass in 'image' for $name and
- * array('media_preview' => 'styles_file_square_thumbnail') to $view_modes_to_formatters.
- *
- * This will also set the "custom_settings" bit to make sure it actually takes effect.
- *
- * @param string $name
- * @param array $view_modes_to_formatters
- * @throws Exception
- * @return void
- */
-function media_type_configure_formatters($name, $view_modes_to_formatters) {
-  $instance = field_info_instance('media', 'file', $name);
-  $bundle_settings = field_bundle_settings('media', $name);
-
-  if (!$instance) {
-    throw new Exception('Unable to set formatter preferences for '. $name);
-  }
-  foreach ($view_modes_to_formatters as $view_mode => $formatter) {
-    $bundle_settings['view_modes'][$view_mode]['custom_settings'] = 1;
-    if (!isset($instance['display'][$view_mode])) {
-      $instance['display'][$view_mode] = $instance['display']['default'];
-    }
-    $instance['display'][$view_mode]['type'] = $formatter;
-  }
-  field_bundle_settings('media', $name, $bundle_settings);
-  field_update_instance($instance);
-}
-
-/**
- * Loads a media type based on its machine name.
- *
- * @param string $name
- * @return StdClass
- */
-function media_type_load($name) {
-  $types = media_type_get_types();
-  if (isset($types[$name])) {
-    return $types[$name];
-  }
-}
-
-/**
- * Sets up the default fields which a media type needs.
- *
- * Currently, this is just a "file" field which contains the reference to the actual file.
- * @TODO Do we also want to make the file 'description/caption' a field?
- *
- * @param StdClass $media_type
- * @return void
- */
-function media_type_configure_fields($media_type) {
-  $field = field_info_field('file');
-  $instance = field_info_instance('media', 'file', $media_type->name);
-
-  if (empty($field)) {
-    $field = array(
-      'field_name' => 'file',
-      'type' => 'file',
-    );
-    $field = field_create_field($field);
-  }
-
-  // Perhaps the file field exists already, if so skip.
-  if (empty($instance)) {
-    $weight = -5;
-    $instance = array(
-      'field_name' => 'file',
-      'entity_type' => 'media',
-      'bundle' => $media_type->name,
-      'label' => 'File',
-      'widget_type' => 'file_file',
-      'widget' => array(
-        'weight' => $weight,
-      ),
-      'required' => TRUE,
-      'locked' => TRUE,
-      'display' => array(
-        'media_preview' => array(
-          'label' => 'hidden',
-          'type' => 'file_default',
-          'weight' => $weight,
-        ),
-        'media_original' => array(
-          'label' => 'hidden',
-          'type' => 'file_default',
-          'weight' => $weight,
-        ),
-      ),
-    );
-    field_create_instance($instance);
-  }
-}
-
-/**
- *  Loads all media types into an array keyed by machine name and sorted
- *  and weighted lexographically.
- *
- * @return array
- *  Media types keyed by machine name.
- */
-function media_type_get_types() {
-  $types =& drupal_static(__FUNCTION__);
-  if (!$types) {
-    $types = db_select('media_type', 'mt')
-      ->orderBy('weight')
-      ->fields('mt')
-      ->execute()
-      ->fetchAllAssoc('name'); // Will key by the name field.
-    foreach ($types as &$type) {
-      // I really hate this.
-      $type->type_callback_args = unserialize($type->type_callback_args);
-    }
-  }
-
-  return $types;
-}
-
-/**
- *  Create the basic class and defaults for a media entity bundle type.
- */
-function media_type_set_defaults($info) {
-  $type = new StdClass();
-
-  // This is used to filter a file to the proper bundle.
-  $type->type_callback = 'media_is_type';
-  $type->type_callback_args = array();
-  $type->weight = 0;
-
-  foreach ($info as $k => $v) {
-    $type->{$k} = $v;
-  }
-
-  return $type;
-}
-
-/**
- * Determines the type of media a passed in $file is.
- *
- * @todo: integrate this properly with other APIs in media when fields is done
- * @param unknown_type $file
- * @return unknown_type
- */
-function media_get_type($file) {
-  $types = media_type_get_types();
-  foreach ($types as $name => $type) {
-    if (call_user_func_array($type->type_callback, array($file, $type->type_callback_args))) {
-      return $name;
-    }
-  }
-  throw new Exception('Unable to determine type of media from ' . var_export($file, 1));
-}
-
-/**
- * Default callback used to determine if a piece of media is of a given type.
- *
- * @TODO: document 'any' and 'all' matching.
- *
- * @param $media
- *   The media file asset object.
- * @param $args
- *
- * @return unknown_type
- */
-function media_is_type($media, $args) {
-  $match_type = !empty($args['match_type']) ? 'any' : $args['match_type'];
-  $match_all = $match_type == 'all';
-  if (!empty($args['mimetypes'])) {
-    foreach ($args['mimetypes'] as $expression) {
-      if (preg_match($expression, $media->filemime)) {
-        if (!$match_all) {
-          return TRUE;
-        }
-      }
-    }
-    // Was not matched, so return
-    if ($match_all) {
-      return FALSE;
-    }
-  }
-
-  if (!empty($args['extensions'])) {
-    if (in_array(pathinfo($media->uri, PATHINFO_EXTENSION), $args['extensions'])) {
-      if (!$match_all) {
-        return TRUE;
-      }
-    }
-    // Was not matched, so return
-    if ($match_all) {
-      return FALSE;
-    }
-  }
-
-  if (!empty($args['streams'])) {
-  }
-}
-
-/**
- * Implement hook_media_format_form_prepare_alter
- */
-function media_media_format_form_prepare_alter(&$form, &$form_state, $media) {
-  switch($media->type) {
-    case 'image':
-      // @TODO: Isn't there a $file->description?
-      $description = $media->filename;
-      $form['options']['alt'] = array(
-        '#type' => 'textfield',
-        '#title' => t('Description'),
-        '#default_value' => $description,
-        '#description' => t('Alternate text a user will see if the image is not available'),
-      );
-      break;
-  }
-}
-
-/**
- * Returns the number of files that need to be converted to media.
- */
-function media_type_invalid_files_count() {
-  return db_select('file_managed', 'fm')
-    ->condition('type', NULL)
-    ->countQuery()
-    ->execute()
-    ->fetchField();
-}
-
-/**
- * Adds a value for the type column in files_managed.
- *
- * If $update_existing is TRUE, will update the type of files with an existing type value.
- *
- * @param boolean $update_existing
- * @param integer $no_to_update
- * @param integer $offset
- *
- * @return array
- *  A list of updated file ids
- */
-function media_type_batch_update($update_existing = FALSE, $no_to_update = NULL, $offset = 0) {
-  $results = array();
-  
-  $query = db_select('file_managed', 'fm')
-    ->fields('fm', array('fid'));
-
-  if (!$update_existing) {
-    $query->condition('type', NULL);
-  }
-
-  if ($no_to_update) {
-    $query->range($offset, $no_to_update);
-  }
-  elseif ($offset) {
-    $query->range($offset);
-  }
-
-  $fids = $query->execute()->fetchCol();
-  foreach ($fids as $fid) {
-    $file = file_load($fid);
-    if (!$file->fid) {
-      throw new Exception('Unable to continue, file was not loaded.');
-    }
-    $file->type = media_get_type($file);
-    file_save($file);
-    $results[] = $fid;
-  }
-
-  return $results;
-}
diff --git a/media.xml.inc b/media.xml.inc
deleted file mode 100644
index a237a2b..0000000
--- a/media.xml.inc
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-/**
- *  @file
- *  XML data retrieval and storage API for Media.
- */
-
-/**
- *  A wrapper around simplexml to retrieve a given XML file.
- *
- *  @param $url
- *    The URL to the XML to retrieve.
- *  @param $display_errors
- *    Optional; if TRUE, then we'll display errors to the end user. They'll be
- *    logged to the watchdog in any case.
- *  @param $refresh
- *    Optional; if TRUE, then we'll force a new load of the XML. Otherwise,
- *    a cached version will be retrieved if possible.
- *  @return
- *    A fully populated object, or FALSE on an error.
- */
-function _media_retrieve_xml($url, $display_errors = FALSE, $refresh = FALSE) {
-  $xmls = &drupal_static(__FUNCTION__, array());
-
-  // Return our cached XML if allowed, and it exists.
-  if (!$refresh && isset($xmls[$url])) {
-    return $xmls[$url];
-  }
-  else if (!$refresh && $cache = cache_get('media:xml:'. $url, 'cache_media_xml')) {
-    $xmls[$url] = $cache->data;
-    return $xmls[$url];
-  }
-
-  // Enable user error handling.
-  libxml_use_internal_errors(TRUE);
-
-  // Load the document
-  $xml = simplexml_load_file($url);
-
-  if (!$xml) {
-    foreach (libxml_get_errors() as $error) {
-      $message = 'Error retrieving XML from %url: %error';
-      $params = array('%url' => $url, '%error' => $error->message);
-      // Handle errors here.
-      if ($display_errors) {
-        drupal_set_message(t($message, $params), 'error');
-      }
-      watchdog('media', $message, $params, WATCHDOG_WARNING);
-    }
-
-    // Clear our error cache.
-    libxml_clear_errors();
-
-    // Set the static cache, but not Drupal's cache, so we can attempt to
-    // retrieve the file another time if possible.
-    $xmls[$url] = FALSE;
-  }
-  else {
-    $xmls[$url] = _media_unserialize_xml($xml);
-    cache_set('media:xml:'. $url, $xmls[$url], 'cache_media_xml', media_variable_get('xml_cache_expire', 3600));
-  }
-
-  return $xmls[$url];
-}
-
-/**
- *  Recursively converts a SimpleXMLElement object into an array.
- *  @param $xml
- *    The original XML object.
- */
-function _media_unserialize_xml($xml) {
-  if ($xml instanceof SimpleXMLElement) {
-    $xml = (array) $xml;
-  }
-  if (is_array($xml)) {
-    foreach ($xml as $key => $item) {
-      $xml[$key] = _media_unserialize_xml($item);
-    }
-  }
-  return $xml;
-}
diff --git a/modules/media_internet/media_internet.module b/modules/media_internet/media_internet.module
index 7aaaab9..7374d98 100644
--- a/modules/media_internet/media_internet.module
+++ b/modules/media_internet/media_internet.module
@@ -47,7 +47,7 @@ function media_internet_media_browser_plugin_view($plugin_name, $params) {
         '#title' => t('Web'),
         'form' => array($from_web_form),
         '#attached' => array(
-          //'js' => array($path . '/javascript/plugins/media.fromurl.js'),
+          //'js' => array($path . '/js/plugins/media.fromurl.js'),
         ),
       );
       break;
diff --git a/templates/media-dialog-page.tpl.php b/templates/media-dialog-page.tpl.php
new file mode 100644
index 0000000..d426686
--- /dev/null
+++ b/templates/media-dialog-page.tpl.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Default theme implementation to display a single Drupal page.
+ *
+ * Available variables:
+ *
+ * General utility variables:
+ * - $base_path: The base URL path of the Drupal installation. At the very
+ *   least, this will always default to /.
+ * - $directory: The directory the template is located in, e.g. modules/system
+ *   or themes/garland.
+ * - $is_front: TRUE if the current page is the front page.
+ * - $logged_in: TRUE if the user is registered and signed in.
+ * - $is_admin: TRUE if the user has permission to access administration pages.
+ *
+ * Site identity:
+ * - $front_page: The URL of the front page. Use this instead of $base_path,
+ *   when linking to the front page. This includes the language domain or
+ *   prefix.
+ * - $logo: The path to the logo image, as defined in theme configuration.
+ * - $site_name: The name of the site, empty when display has been disabled
+ *   in theme settings.
+ * - $site_slogan: The slogan of the site, empty when display has been disabled
+ *   in theme settings.
+ *
+ * Navigation:
+ * - $main_menu (array): An array containing the Main menu links for the
+ *   site, if they have been configured.
+ * - $secondary_menu (array): An array containing the Secondary menu links for
+ *   the site, if they have been configured.
+ * - $breadcrumb: The breadcrumb trail for the current page.
+ *
+ * Page content (in order of occurrence in the default page.tpl.php):
+ * - $title_prefix (array): An array containing additional output populated by
+ *   modules, intended to be displayed in front of the main title tag that
+ *   appears in the template.
+ * - $title: The page title, for use in the actual HTML content.
+ * - $title_suffix (array): An array containing additional output populated by
+ *   modules, intended to be displayed after the main title tag that appears in
+ *   the template.
+ * - $messages: HTML for status and error messages. Should be displayed
+ *   prominently.
+ * - $tabs (array): Tabs linking to any sub-pages beneath the current page
+ *   (e.g., the view and edit tabs when displaying a node).
+ * - $action_links (array): Actions local to the page, such as 'Add menu' on the
+ *   menu administration interface.
+ * - $feed_icons: A string of all feed icons for the current page.
+ * - $node: The node object, if there is an automatically-loaded node
+ *   associated with the page, and the node ID is the second argument
+ *   in the page's path (e.g. node/12345 and node/12345/revisions, but not
+ *   comment/reply/12345).
+ *
+ * Regions:
+ * - $page['help']: Dynamic help text, mostly for admin pages.
+ * - $page['highlight']: Items for the highlighted content region.
+ * - $page['content']: The main content of the current page.
+ * - $page['sidebar_first']: Items for the first sidebar.
+ * - $page['sidebar_second']: Items for the second sidebar.
+ * - $page['header']: Items for the header region.
+ * - $page['footer']: Items for the footer region.
+ *
+ * @see template_preprocess()
+ * @see template_preprocess_page()
+ * @see template_process()
+ */
+?>
+
+<div id="media-browser-page-wrapper"><div id="media-browser-page">
+  <?php if (isset($messages)) { print $messages; } ?>
+  <?php print render($page['content']); ?>
+</div></div> <!-- /#page, /#page-wrapper -->
diff --git a/theme/media-dialog-page.tpl.php b/theme/media-dialog-page.tpl.php
deleted file mode 100644
index d426686..0000000
--- a/theme/media-dialog-page.tpl.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * @file
- * Default theme implementation to display a single Drupal page.
- *
- * Available variables:
- *
- * General utility variables:
- * - $base_path: The base URL path of the Drupal installation. At the very
- *   least, this will always default to /.
- * - $directory: The directory the template is located in, e.g. modules/system
- *   or themes/garland.
- * - $is_front: TRUE if the current page is the front page.
- * - $logged_in: TRUE if the user is registered and signed in.
- * - $is_admin: TRUE if the user has permission to access administration pages.
- *
- * Site identity:
- * - $front_page: The URL of the front page. Use this instead of $base_path,
- *   when linking to the front page. This includes the language domain or
- *   prefix.
- * - $logo: The path to the logo image, as defined in theme configuration.
- * - $site_name: The name of the site, empty when display has been disabled
- *   in theme settings.
- * - $site_slogan: The slogan of the site, empty when display has been disabled
- *   in theme settings.
- *
- * Navigation:
- * - $main_menu (array): An array containing the Main menu links for the
- *   site, if they have been configured.
- * - $secondary_menu (array): An array containing the Secondary menu links for
- *   the site, if they have been configured.
- * - $breadcrumb: The breadcrumb trail for the current page.
- *
- * Page content (in order of occurrence in the default page.tpl.php):
- * - $title_prefix (array): An array containing additional output populated by
- *   modules, intended to be displayed in front of the main title tag that
- *   appears in the template.
- * - $title: The page title, for use in the actual HTML content.
- * - $title_suffix (array): An array containing additional output populated by
- *   modules, intended to be displayed after the main title tag that appears in
- *   the template.
- * - $messages: HTML for status and error messages. Should be displayed
- *   prominently.
- * - $tabs (array): Tabs linking to any sub-pages beneath the current page
- *   (e.g., the view and edit tabs when displaying a node).
- * - $action_links (array): Actions local to the page, such as 'Add menu' on the
- *   menu administration interface.
- * - $feed_icons: A string of all feed icons for the current page.
- * - $node: The node object, if there is an automatically-loaded node
- *   associated with the page, and the node ID is the second argument
- *   in the page's path (e.g. node/12345 and node/12345/revisions, but not
- *   comment/reply/12345).
- *
- * Regions:
- * - $page['help']: Dynamic help text, mostly for admin pages.
- * - $page['highlight']: Items for the highlighted content region.
- * - $page['content']: The main content of the current page.
- * - $page['sidebar_first']: Items for the first sidebar.
- * - $page['sidebar_second']: Items for the second sidebar.
- * - $page['header']: Items for the header region.
- * - $page['footer']: Items for the footer region.
- *
- * @see template_preprocess()
- * @see template_preprocess_page()
- * @see template_process()
- */
-?>
-
-<div id="media-browser-page-wrapper"><div id="media-browser-page">
-  <?php if (isset($messages)) { print $messages; } ?>
-  <?php print render($page['content']); ?>
-</div></div> <!-- /#page, /#page-wrapper -->
diff --git a/wysiwyg_plugins/media.inc b/wysiwyg_plugins/media.inc
index acba0b9..908e315 100644
--- a/wysiwyg_plugins/media.inc
+++ b/wysiwyg_plugins/media.inc
@@ -23,7 +23,7 @@ function media_media_plugin() {
     'icon file' => 'wysiwyg-media.gif',
     'icon title' => t(media_variable_get('wysiwyg_icon_title')),
     // @todo: move this to the plugin directory for the wysiwyg plugin.
-    'js path' => drupal_get_path('module','media').'/javascript/',
+    'js path' => drupal_get_path('module','media').'/js/',
     'js file' => 'wysiwyg-media.js',
     'css file' => NULL,
     'css path' => NULL,
@@ -49,7 +49,7 @@ function media_include_browser_js() {
     return;
   }
   $included = TRUE;
-  module_load_include('inc', 'media', 'media.browser');
+  module_load_include('inc', 'media', 'includes/media.browser');
   $javascript = media_browser_js();
   foreach ($javascript as $key => $definitions) {
     foreach ($definitions as $definition) {
-- 
1.7.2

