diff --git a/js/media.filter.js b/js/media.filter.js
index e69de29..5e60007 100644
--- a/js/media.filter.js
+++ b/js/media.filter.js
@@ -0,0 +1,228 @@
+/**
+ *  @file
+ *  File with utilities to handle media in html editing.
+ */
+(function ($) {
+
+  Drupal.media = Drupal.media || {};
+  /**
+   * Utility to deal with media tokens / placeholders.
+   */
+  Drupal.media.filter = {
+    /**
+     * Replaces media tokens with the placeholders for html editing.
+     * @param content
+     */
+    replaceTokenWithPlaceholder: function(content) {
+      var tagmap = Drupal.media.filter.tagmap(),
+        matches = content.match(/\[\[.*?\]\]/g),
+        media_definition;
+
+      if (matches) {
+        for (var index in matches) {
+          var macro = matches[index];
+
+          if (tagmap[macro]) {
+            var media_json = macro.replace('[[', '').replace(']]', '');
+
+            // Make sure that the media JSON is valid.
+            try {
+              media_definition = JSON.parse(media_json);
+            }
+            catch (err) {
+              media_definition = null;
+            }
+            if (media_definition) {
+              // Apply attributes.
+              var element = Drupal.media.filter.create_element(tagmap[macro], media_definition);
+              var markup = Drupal.media.filter.outerHTML(element);
+
+              content = content.replace(macro, markup);
+            }
+          }
+          else {
+            debug.debug("Could not find content for " + macro);
+          }
+        }
+      }
+      return content;
+    },
+
+    /**
+     * Replaces the placeholders for html editing with the media tokens to store.
+     * @param content
+     */
+    replacePlaceholderWithToken: function(content) {
+      var tagmap = Drupal.media.filter.tagmap(),
+        i = 0,
+        markup,
+        macro;
+
+      // Replace all media placeholders with their JSON macro representations.
+      //
+      // There are issues with using jQuery to parse the WYSIWYG content (see
+      // http://drupal.org/node/1280758), and parsing HTML with regular
+      // expressions is a terrible idea (see http://stackoverflow.com/a/1732454/854985)
+      //
+      // WYSIWYG editors act wacky with complex placeholder markup anyway, so an
+      // image is the most reliable and most usable anyway: images can be moved by
+      // dragging and dropping, and can be resized using interactive handles.
+      //
+      // Media requests a WYSIWYG place holder rendering of the file by passing
+      // the wysiwyg => 1 flag in the settings array when calling
+      // media_get_file_without_label().
+      var matches = content.match(/<img[^>]+class=[\'"]([^"']+ )?media-element[^>]*>/gi);
+      if (matches) {
+        for (i = 0; i < matches.length; i++) {
+          markup = matches[i];
+          macro = Drupal.media.filter.create_macro($(markup));
+          tagmap[macro] = markup;
+          content = content.replace(markup, macro);
+        }
+      }
+      return content;
+    },
+
+    /**
+     * Register new element and returns the placeholder markup.
+     *
+     * @param formattedMedia a formatted media object as given by the onSubmit
+     * function of the media Style popup.
+     * @param fid the file id.
+     *
+     * @return The registered element.
+     */
+    registerNewElement: function (formattedMedia, fid) {
+      var element = Drupal.media.filter.create_element(formattedMedia.html, {
+        fid: fid,
+        view_mode: formattedMedia.type,
+        attributes: formattedMedia.options
+      });
+
+      var markup = Drupal.media.filter.outerHTML(element),
+        macro = Drupal.media.filter.create_macro(element);
+
+      // Store macro/markup pair in the tagmap.
+      Drupal.media.filter.tagmap();
+      Drupal.settings.tagmap[macro] = markup;
+
+      return element;
+    },
+
+    /**
+     * Serializes file information as a url-encoded JSON object and stores it as a
+     * data attribute on the html element.
+     *
+     * @param html (string)
+     *    A html element to be used to represent the inserted media element.
+     * @param info (object)
+     *    A object containing the media file information (fid, view_mode, etc).
+     */
+    create_element: function (html, info) {
+      if ($('<div></div>').append(html).text().length === html.length) {
+        // Element is not an html tag. Surround it in a span element
+        // so we can pass the file attributes.
+        html = '<span>' + html + '</span>';
+      }
+      var element = $(html);
+
+      // Move attributes from the file info array to the placeholder element.
+      if (info.attributes) {
+        $.each(Drupal.media.filter.allowed_attributes(), function(i, a) {
+          if (info.attributes[a]) {
+            element.attr(a, info.attributes[a]);
+          }
+        });
+        delete(info.attributes);
+      }
+
+      // Important to url-encode the file information as it is being stored in an
+      // html data attribute.
+      info.type = info.type || "media";
+      element.attr('data-file_info', encodeURI(JSON.stringify(info)));
+
+      // Adding media-element class so we can find markup element later.
+      var classes = ['media-element'];
+
+      if(info.view_mode){
+        classes.push('file-' + info.view_mode.replace(/_/g, '-'));
+      }
+      element.addClass(classes.join(' '));
+
+      return element;
+    },
+
+    /**
+     * Create a macro representation of the inserted media element.
+     *
+     * @param element (jQuery object)
+     *    A media element with attached serialized file info.
+     */
+    create_macro: function (element) {
+      var file_info = Drupal.media.filter.extract_file_info(element);
+      if (file_info) {
+        return '[[' + JSON.stringify(file_info) + ']]';
+      }
+      return false;
+    },
+
+    /**
+     * Extract the file info from a WYSIWYG placeholder element as JSON.
+     *
+     * @param element (jQuery object)
+     *    A media element with attached serialized file info.
+     */
+    extract_file_info: function (element) {
+      var file_json = $.data(element, 'file_info') || element.data('file_info'),
+        file_info,
+        value;
+
+      try {
+        file_info = JSON.parse(decodeURIComponent(file_json));
+      }
+      catch (err) {
+        file_info = null;
+      }
+
+      if (file_info) {
+        file_info.attributes = {};
+
+        // Extract whitelisted attributes.
+        $.each(Drupal.media.filter.allowed_attributes, function(i, a) {
+          if (value = element.attr(a)) {
+            file_info.attributes[a] = value;
+          }
+        });
+        delete(file_info.attributes['data-file_info']);
+      }
+
+      return file_info;
+    },
+
+    /**
+     * Gets the HTML content of an element.
+     *
+     * @param element (jQuery object)
+     */
+    outerHTML: function (element) {
+      return $('<div>').append(element.eq(0).clone()).html();
+    },
+
+    /**
+     * Ensures the tag map has been initialized and returns it.
+     */
+    tagmap: function () {
+      Drupal.settings.tagmap = Drupal.settings.tagmap || {};
+      return Drupal.settings.tagmap;
+    },
+
+    /**
+     * Ensures the wysiwyg_allowed_attributes and returns it.
+     * In case of an error the default settings are returned.
+     */
+    allowed_attributes: function () {
+      Drupal.settings.wysiwyg_allowed_attributes = Drupal.settings.wysiwyg_allowed_attributes || ['height', 'width', 'hspace', 'vspace', 'border', 'align', 'style', 'alt', 'title', 'class', 'id', 'usemap'];
+      return Drupal.settings.wysiwyg_allowed_attributes;
+    }
+  }
+})(jQuery);
diff --git a/js/wysiwyg-media.js b/js/wysiwyg-media.js
index cdf5ab2..e1b0890 100644
--- a/js/wysiwyg-media.js
+++ b/js/wysiwyg-media.js
@@ -42,7 +42,7 @@ Drupal.wysiwyg.plugins.media = {
       var insert = new InsertMedia(instanceId);
       if (this.isNode(data.node)) {
         // Change the view mode for already-inserted media.
-        var media_file = extract_file_info($(data.node));
+        var media_file = Drupal.media.filter.extract_file_info($(data.node));
         insert.onSelect([media_file]);
       }
       else {
@@ -61,39 +61,7 @@ Drupal.wysiwyg.plugins.media = {
    * that will show in the editor.
    */
   attach: function (content, settings, instanceId) {
-    ensure_tagmap();
-
-    var tagmap = Drupal.settings.tagmap,
-        matches = content.match(/\[\[.*?\]\]/g),
-        media_definition;
-
-    if (matches) {
-      for (var index in matches) {
-        var macro = matches[index];
-
-        if (tagmap[macro]) {
-          var media_json = macro.replace('[[', '').replace(']]', '');
-
-          // Make sure that the media JSON is valid.
-          try {
-            media_definition = JSON.parse(media_json);
-          }
-          catch (err) {
-            media_definition = null;
-          }
-          if (media_definition) {
-            // Apply attributes.
-            var element = create_element(tagmap[macro], media_definition);
-            var markup = outerHTML(element);
-
-            content = content.replace(macro, markup);
-          }
-        }
-        else {
-          debug.debug("Could not find content for " + macro);
-        }
-      }
-    }
+    content = Drupal.media.filter.replaceTokenWithPlaceholder(content);
     return content;
   },
 
@@ -101,44 +69,7 @@ Drupal.wysiwyg.plugins.media = {
    * Detach function, called when a rich text editor detaches
    */
   detach: function (content, settings, instanceId) {
-    ensure_tagmap();
-    var tagmap = Drupal.settings.tagmap,
-        i = 0,
-        markup,
-        macro;
-
-    // Replace all media placeholders with their JSON macro representations.
-    //
-    // There are issues with using jQuery to parse the WYSIWYG content (see
-    // http://drupal.org/node/1280758), and parsing HTML with regular
-    // expressions is a terrible idea (see http://stackoverflow.com/a/1732454/854985)
-    //
-    // WYSIWYG editors act wacky with complex placeholder markup anyway, so an
-    // image is the most reliable and most usable anyway: images can be moved by
-    // dragging and dropping, and can be resized using interactive handles.
-    //
-    // Media requests a WYSIWYG place holder rendering of the file by passing
-    // the wysiwyg => 1 flag in the settings array when calling
-    // media_get_file_without_label().
-    //
-    // Finds the media-element class.
-    var classRegex = 'class=[\'"][^\'"]*?media-element';
-    // Image tag with the media-element class.
-    var regex = '<img[^>]+' + classRegex + '[^>]*?>';
-    // Or a span with the media-element class (used for documents).
-    // \S\s catches any character, including a linebreak; JavaScript does not
-    // have a dotall flag.
-    regex += '|<span[^>]+' + classRegex + '[^>]*?>[\\S\\s]+?</span>';
-    var matches = content.match(RegExp(regex, 'gi'));
-    if (matches) {
-      for (i = 0; i < matches.length; i++) {
-        markup = matches[i];
-        macro = create_macro($(markup));
-        tagmap[macro] = markup;
-        content = content.replace(markup, macro);
-      }
-    }
-
+    content = Drupal.media.filter.replacePlaceholderWithToken(content);
     return content;
   }
 };
@@ -178,20 +109,16 @@ InsertMedia.prototype = {
    * tagmap.
    */
   insert: function (formatted_media) {
-    var element = create_element(formatted_media.html, {
-          fid: this.mediaFile.fid,
-          view_mode: formatted_media.type,
-          attributes: formatted_media.options
-        });
-
-    var markup = outerHTML(element),
-        macro = create_macro(element);
+    var element = Drupal.media.filter.registerNewElement(
+      formatted_media.html,
+      this.mediaFile.fid,
+      formatted_media.type,
+      formatted_media.options
+    );
+    var placeholder = Drupal.media.filter.outerHTML(element);
 
     // Insert placeholder markup into wysiwyg.
-    Drupal.wysiwyg.instances[this.instanceId].insert(markup);
-    // Store macro/markup pair in the tagmap.
-    ensure_tagmap();
-    Drupal.settings.tagmap[macro] = markup;
+    Drupal.wysiwyg.instances[this.instanceId].insert(placeholder);
   }
 };
 
diff --git a/wysiwyg_plugins/media.inc b/wysiwyg_plugins/media.inc
index 9d0c07c..56ad769 100644
--- a/wysiwyg_plugins/media.inc
+++ b/wysiwyg_plugins/media.inc
@@ -15,6 +15,9 @@ function media_media_plugin() {
   // @see http://drupal.org/node/1039076
   media_include_browser_js();
 
+  // Add the filter handling.
+  drupal_add_js(drupal_get_path('module', 'media') . '/js/media.filter.js');
+
   // Plugin definition.
   $plugins['media'] = array(
     'title' => media_variable_get('wysiwyg_title'),
