diff --git a/hover_preview.css b/hover_preview.css
new file mode 100644
index 0000000..25031db
--- /dev/null
+++ b/hover_preview.css
@@ -0,0 +1,15 @@
+
+#hover-preview {
+	background: #fff;
+	border: 1px solid #fff;
+	color: #000;
+	display: none;
+	padding: 5px;
+	position: absolute;
+	z-index: 1000;
+}
+
+#ruler {
+  visibility: hidden;
+	white-space: nowrap;
+}
diff --git a/hover_preview.js b/hover_preview.js
index b814279..f582289 100644
--- a/hover_preview.js
+++ b/hover_preview.js
@@ -1,53 +1,107 @@
-/**
- * @file
- * JavaScript for the Hover Preview module.
- */
 (function ($) {
 
-/**
- * Drupal Hover Preview behavior.
+/*
+ * Hover preview javascript
+ *
+ * Inspired by http://cssglobe.com/post/1695/easiest-tooltip-and-image-preview-using-jquery
+ *   by Alen Grakalic (http://cssglobe.com)
+ *
  */
-Drupal.behaviors.hoverPreview = {
-  attach: function (context, settings) {
-    // Scan through each of the IDs provided by Hover Preview.
-    $.each(settings.hoverPreview || {}, function(id, images) {
-      // Only act on the family of hover images once.
-      $("#" + id).once('hover-preview', function() {
-        // Preload all the images in this family of hover states.
-        $(images).each(function(index, value) {
-          $('<img/>').attr('src', value);
-        });
-
-        // Setup the hover states for each of the images.
-        $('img', this).once('hover-preview').each(function(index, value) {
-          // Save the Hover Preview image from the settings.
-          $(this).data('hoverPreview', images[index]);
-          $(this).hover(function() {
-            // Swap the images whenever the user hovers their mouse.
-            var backup = $(this).data('hoverPreview');
-            $(this).data('hoverPreview', $(this).attr('src'));
-            $(this).attr('src', backup);
-          });
-        });
-      });
-    });
-
-    // Scan through hover-preview-items.
-    $('img.hover-preview-item').once('hover-preview', function() {
-      // Preload the hover preview image.
-      var hover = $(this).attr('data-hover-preview');
-      $('<img/>').attr('src', hover);
-      $(this).data('hoverPreview', hover);
-
-      // Invoke the hover action on the image.
-      $(this).hover(function() {
-        // Swap the images whenever the user hovers their mouse.
-        var backup = $(this).data('hoverPreview');
-        $(this).data('hoverPreview', $(this).attr('src'));
-        $(this).attr('src', backup);
-      });
-    });
+
+Drupal.behaviors.HoverPreview = { attach : function (context, setings) {
+  /* CONFIG */
+    
+    yOffset = 10;
+    xOffset = 30;
+    docHeight = $(document).height();
+		//get here because document height could change during DOM manipulation 
+    // these 2 variable determine popup's distance from the cursor
+    // you might want to adjust to get the right result
+    
+  /* END CONFIG */
+
+  //determins the length of the title in pixels.
+  String.prototype.visualLength = function()
+  {
+      var ruler = document.getElementById("ruler");
+      ruler.innerHTML = this;
+	    return ruler.offsetWidth;
+  }  
+
+  $("img.hover-preview").hover(function(e){
+
+    this.t = this.title;
+
+    //this.title = "";  
+    var c = (this.t != "") ? "<br/>" + this.t : "";
+    var preview_link = $('#' + this.id + '-url')[0]; //why [0] ?
+    
+    img_width = $(preview_link).width();
+    img_height = $(preview_link).height();
+    
+    //Output of the preview element
+    $("body").append("<span id='ruler'></span><p id='hover-preview'><img src='" + preview_link.src + "' alt='Loading Image Preview' />" + c + "</p>");
+    $("#hover-preview")
+      .css("top",(e.pageY - yOffset) + "px")
+      .css("left",(e.pageX + xOffset) + "px")
+      .fadeIn("fast");
+    },
+    
+  function(){
+   // this.title = this.t;  
+    $("#ruler").remove();
+    $("#hover-preview").remove();
+    });   
+
+  
+  //keep track of mouse movement in an image and move preview element
+  $("img.hover-preview").mousemove(function(e){
+  
+  var elementHeight = $("p#hover-preview").outerHeight(true);
+  var elementWidth = $("p#hover-preview").outerWidth(true);
+  var winHeight = $(window).height();
+  var winWidth = $(window).width();
+	var scrolledPixel = $(window).scrollTop();
+  var scrolledPixelLeft = $(window).scrollLeft();
+
+  //Check if item title is too long.
+  var tlen = this.title;
+	var len = tlen.visualLength();
+
+  if(len > elementWidth){
+    elementWidth = len;
+  }
+  //Checks if the bottom was hit.
+  if( ((winHeight - e.pageY + 20 +  scrolledPixel) <= elementHeight) ) {
+		   var yPosition = winHeight - elementHeight + scrolledPixel;
+  }
+	//Checks if the right side was hit.
+  if( ((winWidth - e.pageX - xOffset + scrolledPixelLeft) <= elementWidth) ) {
+	     var xPosition = winWidth - elementWidth + scrolledPixelLeft;
   }
-};
 
+  if (yPosition != null || xPosition != null){
+    if (yPosition != null && xPosition == null){
+      $("#hover-preview")
+        .css("top",(yPosition + yOffset) + "px")
+	      .css("left",(e.pageX + xOffset) + "px");
+		}
+    if (yPosition == null && xPosition != null){
+      $("#hover-preview")
+		    .css("top",(e.pageY - yOffset) + "px")
+		    .css("left",(e.pageX - elementWidth - xOffset) + "px");
+    }
+    if (xPosition != null && yPosition != null){
+      $("#hover-preview")
+       .css("top",(yPosition + yOffset) + "px")
+       .css("left",(e.pageX - elementWidth - xOffset) + "px");
+    }
+  } else {
+    $("#hover-preview")
+      .css("top",(e.pageY - yOffset) + "px")
+      .css("left",(e.pageX + xOffset) + "px");
+  }
+  });  
+}};
 })(jQuery);
+
diff --git a/hover_preview.module b/hover_preview.module
index 7bf5d90..e2b8071 100644
--- a/hover_preview.module
+++ b/hover_preview.module
@@ -63,50 +63,93 @@ function hover_preview_field_formatter_settings_summary($field, $instance, $view
  * Implements hook_field_formatter_view().
  */
 function hover_preview_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
-  $image_style = $display['settings']['image_style'];
-  $hover_style = $display['settings']['hover_preview_style'];
-
-  $id = drupal_html_id('hover-preview');
-  $settings = array();
-
   // Build the view from the displayed image.
   $element = image_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display);
+  $hover_preview_style = $display['settings']['hover_preview_style'];
+  $path = drupal_get_path('module', 'hover_preview');
 
-  foreach ($items as $delta => $item) {
-    $hover_uri = image_style_path($hover_style, $item['uri']);
-    $settings[$id][$delta] = file_create_url($hover_uri);
-  }
-
-  // TODO: Send the data in as attributes instead:
-  //   http://drupal.org/node/1025796
-  // Could even pass the hover image in as a data attribute:
-  //   http://ejohn.org/blog/html-5-data-attributes/
   foreach ($element as $delta => $item) {
-    $element[$delta]['#item']['attributes']['class'] = 'hover-preview-item';
-    $element[$delta]['#item']['attributes']['data-hover-preview'] = $settings[$id][$delta];
-    $element[$delta]['#attached']['js'][] = array(
-      'data' => drupal_get_path('module', 'hover_preview') . '/hover_preview.js',
-      'type' => 'file',
+    $element[$delta]['#theme'] = 'hover_preview_image_formatter';
+    $element[$delta]['#hover_image_style'] = $hover_preview_style;
+    $element[$delta]['#item']['attributes'] = array(
+      'class' => 'hover-preview',
+      'id' => drupal_html_id('hover-preview-img'),
     );
+    foreach (array('js', 'css') as $key) {
+      $element[$delta]['#attached'][$key][] = array(
+        'data' => "$path/hover_preview.$key",
+        'type' => 'file',
+      );
+    }
   }
 
-  // If there are images, provide the JavaScript required to have the hover.
-  if (!empty($element)) {
-    $element['#type'] = 'container';
-    $element['#attributes']['class'][] = 'hover-preview';
-    $element['#attributes']['id'] = $id;
-    $element['#attached']['js'][] = array(
-      'data' => drupal_get_path('module', 'hover_preview') . '/hover_preview.js',
-      'type' => 'file',
-    );
-    $element['#attached']['js'][] = array(
-      'data' => array(
-        'hoverPreview' => $settings,
-      ),
-      'type' => 'setting',
-    );
+  return $element;
+}
+
+/**
+ * Implements hook_theme().
+ */
+function hover_preview_theme() {
+  return array(
+    'hover_preview_image_formatter' => array(
+      'variables' => array('item' => NULL, 'path' => NULL, 'image_style' => NULL, 'hover_image_style' => NULL),
+    ),
+  );
+}
+
+/**
+ * Returns HTML for a hover image field formatter.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - item: An array of image data.
+ *   - image_style: An optional image style.
+ *   - hover_image_style: An optional image style for the hover image.
+ *   - path: An array containing the link 'path' and link 'options'.
+ *
+ * @ingroup themeable
+ */
+function theme_hover_preview_image_formatter($variables) {
+  $item = $variables['item'];
+  $image = array(
+    'path' => $item['uri'],
+    'alt' => $item['alt'],
+  );
+  // Do not output an empty 'title' attribute.
+  if (drupal_strlen($item['title']) > 0) {
+    $image['title'] = $item['title'];
   }
+  // This is not supported by core.
+  if (isset($item['attributes'])) {
+    $image['attributes'] = $item['attributes'];
+  }
+  $output = _hover_preview_image_helper($image, $variables['image_style']);
 
-  return $element;
+  if ($variables['path']) {
+    $path = $variables['path']['path'];
+    $options = $variables['path']['options'];
+    // When displaying an image inside a link, the html option must be TRUE.
+    $options['html'] = TRUE;
+    $output = l($output['small'], $path, $options);
+  }
+  // This adds the hover image.
+  $image['attributes']['style'] = 'display: none';
+  $image['attributes']['id'] .= '-url';
+  unset($image['attributes']['class']);
+  $output .= _hover_preview_image_helper($image, $variables['hover_image_style']);
+
+  return $output;
 }
 
+/**
+ * Helper calling theme('image_style') / theme('image') as appropriate.
+ */
+function _hover_preview_image_helper($image, $image_style) {
+  if ($image_style) {
+    $image['style_name'] = $image_style;
+    return theme('image_style', $image);
+  }
+  else {
+    return theme('image', $image);
+  }
+}
